fix(reasoning): enforce confirmation-gated decision closure

- reconcileDecisionClosureOwnership normaliser between reconciliation and validation (Boundary B)
- Strips terminal parent updates without explicit user confirmation; preserves all other proposal work
- Strips parent from resolvedUnknownNodeIds bookkeeping on no-confirmation strip
- Restores reconciler-forced resolved→unknown for synthetic updates too
- Prevents hybrid unknown+value states by nulling newValue in all stripping paths
- No-op update created when reconciler synthesized the entry to prevent downstream errors

Prompt:
- Rule #143 rewritten from evidence-sufficiency to explicit-confirmation gate
- Directs model to use possibleInference for directional conclusions when confirmation absent

Regression preservation:
- 60B.43 lifecycle invariant restored via explicit confirmation phrases in fixture answers
- 60B.49 reconciliation auto-add invariant restored under confirmed closure flow
- Test apparatus fixed: structuralActionRequired required with userSupportedMeaning (validator constraint)

New coverage:
- 10 tests for all 60B.79/80 coverage requirements
- 5 prompt alignment tests for Rule #143
This commit is contained in:
2026-08-15 11:34:39 +01:00
parent 4988159986
commit b181c3ea75
4 changed files with 862 additions and 12 deletions
+58
View File
@@ -863,6 +863,64 @@ describe("60B.4 decision materiality rule", () => {
});
});
// ============================================
// 60B.80 — explicit confirmation requirement in prompt
// ============================================
describe("60B.80 — prompt alignment for confirmation-gated closure", () => {
let sufficiencySection;
beforeAll(() => {
const testNode = makeNode({
id: "n-test-decision",
label: "Test Decision",
kind: "state",
status: "supported",
});
const prompt = buildGraphUpdatePrompt({
situationGraph: makeGraph({
centralStatement: "test",
nodes: [testNode],
edges: [],
activeUnknownNodeId: null,
resolvedNodeIds: [],
currentSummary: "Test.",
}),
situationContext: "Test context",
});
sufficiencySection = prompt.split("## Decision Sufficiency Rule")[1].split("## Decision Option Structure Rules")[0];
});
it("explicit confirmation requirement is present in the Decision Sufficiency Rule", () => {
expect(sufficiencySection).toContain("explicitly confirms");
expect(sufficiencySection).toContain("no other material uncertainty remains");
});
it("evidence-only terminal closure rule is replaced by confirmation-gated rule", () => {
// The OLD rule that permitted evidence-only closure is replaced:
expect(sufficiencySection).not.toContain(
"If the currently supported evidence is sufficient to distinguish the options and no such material unresolved factor remains, resolve the existing decision context",
);
// The NEW confirmation requirement is present:
expect(sufficiencySection).toContain("explicit confirmation");
});
it("model must not close parent merely because evidence appears sufficient", () => {
expect(sufficiencySection).toContain("You may not resolve the decision context unless");
expect(sufficiencySection).toContain("explicitly confirms (using their own words)");
});
it("preserves directional conclusion guidance when confirmation absent", () => {
expect(sufficiencySection).toContain("preserve your directional conclusion in possibleInference");
});
it("does NOT still permit evidence-only terminal closure", () => {
// The old "resolve" permission based on evidence sufficiency alone is gone:
const text = sufficiencySection;
expect(text).not.toContain("evidence is sufficient to distinguish the options and no such material unresolved factor remains, resolve");
});
});
// ============================================
// 60B.11 — prompt prerequisite-aware targeting clarity
// ============================================