fix(confidence-engine): scope episode closure authority
This commit is contained in:
+68
-13
@@ -2,6 +2,7 @@ import { describeGraph } from "./builder.js";
|
||||
import {
|
||||
countRemainingMaterialFactors,
|
||||
hasRemainingMaterialFactors,
|
||||
isDecisionClosureAuthorityQuestion,
|
||||
isUserConfirmationOfNoRemainingUncertainty,
|
||||
shouldCloseDecision,
|
||||
} from "./decision-sufficiency.js";
|
||||
@@ -4032,15 +4033,39 @@ export async function applyValidatedProposal({
|
||||
);
|
||||
|
||||
// ── 60B.80 — normalise terminal parent closure without explicit confirmation
|
||||
// legacy only: episode evidence does not supply a single-answer confirmation string;
|
||||
// Done and Finding agree are NOT closure authority (settled architecture rule).
|
||||
const ownershipChanges = isLegacySingleTurn
|
||||
? reconcileDecisionClosureOwnership(
|
||||
situationGraph,
|
||||
reconciledProposal.proposal,
|
||||
answer,
|
||||
)
|
||||
: { strippedUpdates: [], strippedResolvedIds: [] };
|
||||
// legacy: use raw answer for explicit confirmation detection.
|
||||
// episode: scan preserved paired turns for a QUALIFIED authority pair;
|
||||
// question must grant decision-level closure authority AND
|
||||
// answer must confirm no remaining uncertainty, SAME TURN.
|
||||
let ownershipAnswer = answer;
|
||||
if (!isLegacySingleTurn) {
|
||||
const epTurns = evidenceContext?.isCompletedEpisode
|
||||
? evidenceContext.episodeEvidence?.turns
|
||||
: [];
|
||||
if (Array.isArray(epTurns) && epTurns.length > 0) {
|
||||
// Scan all turns — paired check on SAME turn only.
|
||||
let explicitConfirmationAnswer = null;
|
||||
for (const turn of epTurns) {
|
||||
const tQuestion = turn?.question;
|
||||
const tAnswer = turn?.answer;
|
||||
if (
|
||||
typeof tQuestion === "string" &&
|
||||
typeof tAnswer === "string" &&
|
||||
isDecisionClosureAuthorityQuestion(tQuestion) &&
|
||||
isUserConfirmationOfNoRemainingUncertainty(tAnswer)
|
||||
) {
|
||||
explicitConfirmationAnswer = tAnswer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ownershipAnswer = explicitConfirmationAnswer ?? null;
|
||||
}
|
||||
}
|
||||
const ownershipChanges = reconcileDecisionClosureOwnership(
|
||||
situationGraph,
|
||||
reconciledProposal.proposal,
|
||||
ownershipAnswer,
|
||||
);
|
||||
if (ownershipChanges.strippedResolvedIds.length > 0) {
|
||||
// Reconciler may have added selectedQuestion = null because the parent
|
||||
// appeared resolved during reconciliation. If we stripped it, restore
|
||||
@@ -4175,10 +4200,40 @@ export async function applyValidatedProposal({
|
||||
? findNodeById(graphSnapshot, previousActiveUnknownNodeId)
|
||||
: null;
|
||||
const affectedNodeIds = buildAffectedNodeIds(graphSnapshot, proposalSnapshot);
|
||||
// For episode evidence: extract turn text from the ordered Q/A sequence.
|
||||
const _epTurns = evidenceContext?.isCompletedEpisode ? evidenceContext.episodeEvidence.turns : null;
|
||||
const _derivedQuestion = isLegacySingleTurn ? previousQuestion : (_epTurns?.[0]?.question ?? "");
|
||||
const _derivedAnswer = isLegacySingleTurn ? answer : (_epTurns?.[0]?.answer ?? "");
|
||||
// For episode evidence: derive comparability override from the relevant Q/A pair,
|
||||
// not from turns[0]. Scan preserved paired turns for a matching comparability question
|
||||
// whose answer confirms comparability.
|
||||
let _derivedQuestion = isLegacySingleTurn ? previousQuestion : "";
|
||||
let _derivedAnswer = isLegacySingleTurn ? answer : "";
|
||||
if (!isLegacySingleTurn) {
|
||||
const epTurns = evidenceContext?.isCompletedEpisode
|
||||
? evidenceContext.episodeEvidence?.turns
|
||||
: [];
|
||||
if (Array.isArray(epTurns)) {
|
||||
// Scan all turns for a matching comparability Q/A pair.
|
||||
let found = false;
|
||||
for (const turn of epTurns) {
|
||||
const tq = turn?.question;
|
||||
const ta = turn?.answer;
|
||||
if (
|
||||
typeof tq === "string" &&
|
||||
typeof ta === "string" &&
|
||||
isComparabilityQuestion(tq) &&
|
||||
answerConfirmsComparability(ta)
|
||||
) {
|
||||
_derivedQuestion = tq;
|
||||
_derivedAnswer = ta;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// No qualifying comparability pair — do not fabricate confirmation.
|
||||
_derivedQuestion = "";
|
||||
_derivedAnswer = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
const reasoningResolution = deriveReasoningStateOverride({
|
||||
graph: graphSnapshot,
|
||||
previousQuestion: _derivedQuestion,
|
||||
|
||||
Reference in New Issue
Block a user