feat: advance reasoning after comparability is resolved

This commit is contained in:
2026-08-02 17:06:34 +01:00
parent 7d408701b5
commit 25a989450c
9 changed files with 649 additions and 5 deletions
+92 -2
View File
@@ -1,5 +1,7 @@
import { describeGraph } from "./builder.js";
import {
buildReasoningState,
COMPARABILITY_REASONING_NODE_ID,
formulateQuestion,
formulateTieResolutionQuestion,
} from "./question-formulator.js";
@@ -426,7 +428,67 @@ function buildChangesApplied(proposal, affectedNodeIds) {
};
}
export function applyValidatedProposal({ situationGraph, proposal }) {
function isComparabilityQuestion(question) {
const text = String(question || "").toLowerCase();
return (
text.includes("same basis") ||
text.includes("same scale") ||
text.includes("same period")
);
}
function answerConfirmsComparability(answer) {
const text = String(answer || "").toLowerCase();
return (
/\byes\b/.test(text) &&
(text.includes("same accounting period") ||
text.includes("same management accounts") ||
text.includes("same basis") ||
text.includes("same scale") ||
text.includes("both figures cover the same"))
);
}
function deriveReasoningStateOverride({
graph,
previousQuestion,
answer,
resolvedUnknownNodeIds,
}) {
const previousReasoningState = buildReasoningState(graph);
const previousComparabilityStatus =
previousReasoningState.comparabilityStatus ?? null;
if (
previousComparabilityStatus === "uncertain" &&
isComparabilityQuestion(previousQuestion) &&
answerConfirmsComparability(answer)
) {
return {
reasoningStateOverride: {
comparabilityStatus: "confirmed",
comparabilityReason:
"Comparability was confirmed by the user answer covering the same period and source basis.",
comparabilityEvidence: resolvedUnknownNodeIds,
},
resolvedReasoningNodeIds: [COMPARABILITY_REASONING_NODE_ID],
previousReasoningState,
};
}
return {
reasoningStateOverride: {},
resolvedReasoningNodeIds: [],
previousReasoningState,
};
}
export function applyValidatedProposal({
situationGraph,
proposal,
previousQuestion = null,
answer = null,
}) {
const graphValidation = situationGraphSchema.safeParse(situationGraph);
const proposalValidation = graphUpdateSchema.safeParse(proposal);
@@ -574,6 +636,12 @@ export function applyValidatedProposal({ situationGraph, proposal }) {
const proposalSnapshot = cloneJsonSafe(validatedProposal);
const previousActiveUnknownNodeId = graphSnapshot.activeUnknownNodeId ?? null;
const affectedNodeIds = buildAffectedNodeIds(graphSnapshot, proposalSnapshot);
const reasoningResolution = deriveReasoningStateOverride({
graph: graphSnapshot,
previousQuestion,
answer,
resolvedUnknownNodeIds: validatedProposal.resolvedUnknownNodeIds,
});
const applied = applyGraphUpdate(graphSnapshot, proposalSnapshot);
if (!applied.success) {
@@ -590,6 +658,11 @@ export function applyValidatedProposal({ situationGraph, proposal }) {
edges: applied.edges,
resolvedNodeIds: applied.resolvedNodeIds,
};
const nextReasoningState = buildReasoningState(
updatedSituationGraph,
reasoningResolution.reasoningStateOverride,
);
updatedSituationGraph.reasoningState = nextReasoningState;
const activeUnknownWasResolved =
previousActiveUnknownNodeId != null &&
@@ -676,7 +749,21 @@ export function applyValidatedProposal({ situationGraph, proposal }) {
strategy: formulatedQuestion?.strategy,
investigationStrategy: formulatedQuestion?.investigationStrategy,
}
: null;
: (() => {
const relationshipFallback = formulateTieResolutionQuestion({
graph: updatedSituationGraph,
});
return relationshipFallback?.question
? {
nodeId: null,
question: relationshipFallback.question,
reason: relationshipFallback.reason,
strategy: relationshipFallback.strategy,
investigationStrategy:
relationshipFallback.investigationStrategy,
}
: null;
})();
const resultGraphValidation = situationGraphSchema.safeParse(
updatedSituationGraph,
@@ -725,10 +812,13 @@ export function applyValidatedProposal({ situationGraph, proposal }) {
graphUpdate: validatedProposal,
affectedNodeIds,
resolvedUnknownNodeIds: validatedProposal.resolvedUnknownNodeIds,
resolvedReasoningNodeIds: reasoningResolution.resolvedReasoningNodeIds,
previousActiveUnknownNodeId,
newActiveUnknownNodeId,
selectedQuestion: finalSelectedQuestion,
changesApplied: buildChangesApplied(validatedProposal, affectedNodeIds),
graphReferenceValidation: resultReferenceValidation,
previousReasoningState: reasoningResolution.previousReasoningState,
reasoningState: nextReasoningState,
};
}