From 55e935066c216e957445cfbee9e83a755566c33f Mon Sep 17 00:00:00 2001 From: robbond Date: Sat, 5 Sep 2026 13:15:11 +0100 Subject: [PATCH] fix(confidence-engine): project unexplained transitions --- docs/current-handoff.md | 6 +++++ lib/graph/builder.js | 18 +++++++++++++++ tests/graph/builder.test.js | 45 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/docs/current-handoff.md b/docs/current-handoff.md index 9c73f38..c819e34 100644 --- a/docs/current-handoff.md +++ b/docs/current-handoff.md @@ -33,6 +33,12 @@ - The read-back rule requires relationship type, endpoint direction, and description to agree. Provenance and interpretation-separation rules remain unchanged. - Deterministic tests prove the instruction exists, not that the model obeys it. No live call has tested this refinement; next restart point is live directional-integrity validation before broader repeatability testing. +## Unexplained-transition projection coverage + +- Starting checkpoint: `e183914`. The latest one-call v0.5 validation emitted two reconstruction relationships: one directionally clean relationship projected, while the other (`r1`) was semantically/directionally incoherent and lost because it referenced an `unexplainedTransition` without graph projection. +- `unexplainedTransitions` now project as unresolved transition nodes and register their source IDs for existing relationship projection; the relationship architecture itself was not redesigned. +- The semantic coherence of live `r1` remains a separate unresolved reasoning issue; intervention-fit E was absent in that run and remains unresolved. No live validation has occurred after this deterministic fix. + ## Current product architecture Three distinct routes, not a single page: diff --git a/lib/graph/builder.js b/lib/graph/builder.js index f3c5000..61dd8c7 100644 --- a/lib/graph/builder.js +++ b/lib/graph/builder.js @@ -204,6 +204,24 @@ export function buildInitialGraph(analysisData) { } } + // Unexplained transitions remain unresolved transition nodes + if (reconstruction.unexplainedTransitions) { + for (const trans of reconstruction.unexplainedTransitions) { + const label = + trans.description || + `${trans.entity ?? "Unexplained transition"}: ${trans.previousState ?? "unknown"} → ${trans.currentState ?? "unknown"}`; + mapSourceId(trans.id, ensureNode( + label, + "transition", + "unknown", + trans.description || label, + null, + null, + trans.confidence || "low", + )); + } + } + // Known transitions if (reconstruction.knownTransitions) { for (const trans of reconstruction.knownTransitions) { diff --git a/tests/graph/builder.test.js b/tests/graph/builder.test.js index 6fed4a6..068e038 100644 --- a/tests/graph/builder.test.js +++ b/tests/graph/builder.test.js @@ -269,6 +269,51 @@ describe("buildInitialGraph", () => { })); }); + it("projects relationships referencing unexplained transition source IDs", () => { + const reconstruction = { + ...makeReconstructionFixture(), + observedStates: [ + { id: "obs-rate", description: "Complaint rate is not yet known", confidence: "high" }, + ], + unexplainedTransitions: [ + { + id: "ut-1", + description: "Support wait time increased without an explanation", + entity: "Support", + previousState: "2 hours", + currentState: "8 hours", + confidence: "medium", + }, + ], + relationships: [{ + id: "r-transition", + fromId: "obs-rate", + toId: "ut-1", + relationship: "depends_on", + description: "Understanding the rate depends on the unexplained transition.", + confidence: "medium", + }], + }; + + const result = buildInitialGraph({ reconstruction, evidence: [] }); + const rateNode = result.nodes.find((node) => node.label === "Complaint rate is not yet known"); + const transitionNode = result.nodes.find( + (node) => node.description === "Support wait time increased without an explanation", + ); + + expect(transitionNode).toMatchObject({ + kind: "transition", + status: "unknown", + description: "Support wait time increased without an explanation", + }); + expect(result.edges).toContainEqual(expect.objectContaining({ + id: "e-rel-r-transition", + fromNodeId: rateNode.id, + toNodeId: transitionNode.id, + relationship: "depends_on", + })); + }); + it("skips relationships with invalid source IDs without creating substitute nodes", () => { const reconstruction = { ...makeReconstructionFixture(),