feat(confidence-engine): accept episode evidence at graph application

This commit is contained in:
2026-09-01 08:30:26 +01:00
parent 6b77e32771
commit 0752c53a25
2 changed files with 585 additions and 13 deletions
+27 -13
View File
@@ -3971,9 +3971,11 @@ export async function applyValidatedProposal({
proposal,
previousQuestion = null,
answer = null,
evidenceContext = null,
provider = null,
modelName = null,
}) {
const isLegacySingleTurn = !evidenceContext;
const graphValidation = situationGraphSchema.safeParse(situationGraph);
const proposalValidation = graphUpdateSchema.safeParse(proposal);
@@ -4030,11 +4032,15 @@ export async function applyValidatedProposal({
);
// ── 60B.80 — normalise terminal parent closure without explicit confirmation
const ownershipChanges = reconcileDecisionClosureOwnership(
situationGraph,
reconciledProposal.proposal,
answer,
);
// 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: [] };
if (ownershipChanges.strippedResolvedIds.length > 0) {
// Reconciler may have added selectedQuestion = null because the parent
// appeared resolved during reconciliation. If we stripped it, restore
@@ -4130,12 +4136,16 @@ export async function applyValidatedProposal({
validatedProposal,
);
proposalCompatibilityErrors.push(...selectedQuestionValidation.errors);
proposalCompatibilityErrors.push(
...validateAnswerMeaningCompatibilityWithRawAnswer({
answer,
proposal: validatedProposal,
}),
);
// legacy only: raw-answer fidelity is a single-turn safeguard.
// Episode evidence carries structured turns — no synthetic answer is constructed.
if (isLegacySingleTurn) {
proposalCompatibilityErrors.push(
...validateAnswerMeaningCompatibilityWithRawAnswer({
answer,
proposal: validatedProposal,
}),
);
}
proposalCompatibilityErrors.push(
...validateAnswerMeaningAlignment(validatedProposal),
);
@@ -4165,10 +4175,14 @@ 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 ?? "");
const reasoningResolution = deriveReasoningStateOverride({
graph: graphSnapshot,
previousQuestion,
answer,
previousQuestion: _derivedQuestion,
answer: _derivedAnswer,
resolvedUnknownNodeIds: validatedProposal.resolvedUnknownNodeIds,
});