test(confidence-engine): establish longitudinal synthesis checkpoints

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-10-08 10:33:59 +01:00
co-authored by Claude Code
parent 8d9df8c939
commit 57c88b713a
3 changed files with 356 additions and 1 deletions
@@ -103,4 +103,63 @@ Contents:
- `situationGraph`: 11 nodes (1 state, 5 observation, 2 metric, 3 unknown), 10 edges, full `reasoningState`, `resolvedNodeIds`, `centralStatement`, `activeUnknownNodeId`
- `findings`: 9 findings total; all 7 previously verified IDs present (`hciikc`, `ybfsm3`, `k45vyt`, `vhftyr`, `9bgkmw`, `23192q`, `kkh39k`)
Verified: helper parses without invoking live synthesis; fixture immutability confirmed via deep-clone roundtrip.
Verified: helper parses without invoking live synthesis; fixture immutability confirmed via deep-clone roundtrip.
## Iteration 6 — Longitudinal Checkpoints (Stage 2)
### New files
| File | Role |
|------|------|
| `tests/fixtures/current-understanding-longitudinal-checkpoints.json` | Compact checkpoint manifest referencing original fixture; lists included contribution IDs and finding IDs per checkpoint |
| `tests/graph/current-understanding-experiment-helper.test.js` (extended) | 15 new longitudinal assertions in `Longitudinal checkpoints — manifest integrity` describe block |
### Checkpoint manifest structure
The manifest contains:
- `manifestVersion`, `description`, `sourceFixture`, `contaminationAssessment`
- `contributions[]` — ordered list with timestamps and ordering integers
- `findingsById` — verbatim copy of each original finding (by ID)
- `checkpoints[]` — four cumulative states: `{ id, includedContributions[], includedFindings[] }`
### Checkpoint state summary
| Checkpoint | Description | Contribs | Findings | Count |
|---|---|---|---|---|
| checkpoint-0 | Baseline (no findings) | none | none | 0 |
| checkpoint-1 | contrib-0001 | contrib-0001 | 3bw5f0, i5zdcq | 2 |
| checkpoint-2 | contrib-0001 + contrib-0002 | contrib-0001, contrib-0002 | 7 findings | 7 |
| checkpoint-3 | All contributions | all three | all 9 original findings | 9 |
### Graph-state contamination analysis
`synthesizeCurrentUnderstanding()` and `buildGraphEvidenceProjection()` project **only**:
- Nodes with `status: "known"` or `"supported"` (filtered)
- `centralStatement` (framing only)
- Findings passed as separate parameter
**Explicitly excluded from prompt:** `resolvedNodeIds`, `activeUnknownNodeId`, `reasoningState`, edges, `currentSummary`, nodes outside known/supported statuses.
**Conclusion: NONE — final-state graph fields do not leak into synthesis input.**
### Compatibility gap
The existing helper consumes a single `{ situationGraph, findings }` fixture via `--file`. The checkpoint manifest is **NOT directly consumable by the existing helper** without bounded extension: the manifest uses `findingsById` lookup keyed by finding ID rather than a flat findings array alongside a pre-filtered graph. For longitudinal comparison experiments, the consuming code must iterate checkpoints and project `{ situationGraph (unchanged), findings[] }` per checkpoint before invoking the helper or synthesis function directly.
### Test assertions added (15 new tests)
1. Manifest has exactly 4 checkpoints
2. Checkpoint counts are 0, 2, 7, 9
3. checkpoint-0 has no included findings
4. checkpoint-1 includes exactly contrib-0001 findings
5. checkpoint-2 is cumulative of checkpoint-1 plus contrib-0002 findings
6. checkpoint-3 includes all 9 original findings
7. All checkpoint findings are verbatim from the original fixture
8. Contribution ordering matches recorded timestamps
9. Manifest ordering matches fixture finding order
10. checkpoint-3 is cumulative of all checkpoints before it
11. No findings appear before their contribution boundary
12. Original fixture unchanged (spot check)
13. Production synthesis contract accepts all checkpoint inputs
14. Manifest references the source fixture path
15. Manifest does not fabricate historical graph state
@@ -0,0 +1,167 @@
{
"manifestVersion": "1.0.0",
"description": "Longitudinal cumulative evidence checkpoints for Current Understanding synthesis comparison experiments.",
"sourceFixture": "tests/fixtures/current-understanding-original-investigation.json",
"contaminationAssessment": "NONE",
"contributions": [
{
"id": "contrib-0001",
"ordering": 1,
"timestamp": "2026-10-08T06:50:06.355Z"
},
{
"id": "contrib-0002",
"ordering": 2,
"timestamp": "2026-10-08T06:55:45.665Z"
},
{
"id": "contrib-0003",
"ordering": 3,
"timestamp": "2026-10-08T07:01:23.359Z"
}
],
"checkpoints": [
{
"id": "checkpoint-0",
"includedContributions": [],
"includedFindings": []
},
{
"id": "checkpoint-1",
"includedContributions": [
"contrib-0001"
],
"includedFindings": [
"finding-3bw5f0",
"finding-i5zdcq"
]
},
{
"id": "checkpoint-2",
"includedContributions": [
"contrib-0001",
"contrib-0002"
],
"includedFindings": [
"finding-3bw5f0",
"finding-i5zdcq",
"finding-hciikc",
"finding-ybfsm3",
"finding-k45vyt",
"finding-vhftyr",
"finding-9bgkmw"
]
},
{
"id": "checkpoint-3",
"includedContributions": [
"contrib-0001",
"contrib-0002",
"contrib-0003"
],
"includedFindings": [
"finding-3bw5f0",
"finding-i5zdcq",
"finding-hciikc",
"finding-ybfsm3",
"finding-k45vyt",
"finding-vhftyr",
"finding-9bgkmw",
"finding-23192q",
"finding-kkh39k"
]
}
],
"findingsById": {
"finding-3bw5f0": {
"id": "finding-3bw5f0",
"status": "provisional",
"createdAt": "2026-10-08T06:50:06.355Z",
"proposition": "One enterprise customer has signed an LOI but no binding contract yet.",
"contributionId": "contrib-0001",
"userDisposition": null,
"sourceObservation": "One enterprise customer has signed an LOI but no binding contract yet.",
"originatingTargetNodeId": "nw0dwkz"
},
"finding-i5zdcq": {
"id": "finding-i5zdcq",
"status": "provisional",
"createdAt": "2026-10-08T06:50:06.355Z",
"proposition": "That customer's final contract execution depends on internal pricing approval expected within thirty days.",
"contributionId": "contrib-0001",
"userDisposition": null,
"sourceObservation": "That customer's final contract execution depends on internal pricing approval expected within thirty days.",
"originatingTargetNodeId": "nw0dwkz"
},
"finding-hciikc": {
"id": "finding-hciikc",
"status": "provisional",
"createdAt": "2026-10-08T06:55:45.665Z",
"proposition": "Absence of reliable evidence linking a twelve-month delay to changes in customer retention",
"contributionId": "contrib-0002",
"userDisposition": null,
"sourceObservation": "Absence of reliable evidence linking a twelve-month delay to changes in customer retention",
"originatingTargetNodeId": "nfl7ee3"
},
"finding-ybfsm3": {
"id": "finding-ybfsm3",
"status": "provisional",
"createdAt": "2026-10-08T06:55:45.665Z",
"proposition": "Failure to identify which prospective customers would remain interested after a delay",
"contributionId": "contrib-0002",
"userDisposition": null,
"sourceObservation": "Failure to identify which prospective customers would remain interested after a delay",
"originatingTargetNodeId": "nfl7ee3"
},
"finding-k45vyt": {
"id": "finding-k45vyt",
"status": "provisional",
"createdAt": "2026-10-08T06:55:45.665Z",
"proposition": "Failure to assess whether customer purchasing decisions would shift due to the delay",
"contributionId": "contrib-0002",
"userDisposition": null,
"sourceObservation": "Failure to assess whether customer purchasing decisions would shift due to the delay",
"originatingTargetNodeId": "nfl7ee3"
},
"finding-vhftyr": {
"id": "finding-vhftyr",
"status": "provisional",
"createdAt": "2026-10-08T06:55:45.665Z",
"proposition": "Unestablished impact of competitor activity on customer willingness to wait",
"contributionId": "contrib-0002",
"userDisposition": null,
"sourceObservation": "Unestablished impact of competitor activity on customer willingness to wait",
"originatingTargetNodeId": "nfl7ee3"
},
"finding-9bgkmw": {
"id": "finding-9bgkmw",
"status": "provisional",
"createdAt": "2026-10-08T06:55:45.665Z",
"proposition": "Unknown financial consequences tied to potential customer loss during the delay",
"contributionId": "contrib-0002",
"userDisposition": null,
"sourceObservation": "Unknown financial consequences tied to potential customer loss during the delay",
"originatingTargetNodeId": "nfl7ee3"
},
"finding-23192q": {
"id": "finding-23192q",
"status": "provisional",
"createdAt": "2026-10-08T07:01:23.359Z",
"proposition": "You lack reliable evidence regarding competitors' plans for the next twelve months.",
"contributionId": "contrib-0003",
"userDisposition": null,
"sourceObservation": "You lack reliable evidence regarding competitors' plans for the next twelve months.",
"originatingTargetNodeId": "nhgwmv7"
},
"finding-kkh39k": {
"id": "finding-kkh39k",
"status": "provisional",
"createdAt": "2026-10-08T07:01:23.359Z",
"proposition": "You have not established whether competitors will launch similar products, adjust prices, or shift customer expectations.",
"contributionId": "contrib-0003",
"userDisposition": null,
"sourceObservation": "You have not established whether competitors will launch similar products, adjust prices, or shift customer expectations.",
"originatingTargetNodeId": "nhgwmv7"
}
}
}
@@ -611,4 +611,133 @@ describe("CU synthesis experiment helper — --include-output flag", function ()
expect(JSON.stringify(situationGraph)).toBe(JSON.stringify(graphBefore));
expect(JSON.stringify(findings)).toBe(JSON.stringify(fixBefore));
});
});
// ── Longitudinal checkpoint deterministic verification ──
import longitudinalManifest from "@/tests/fixtures/current-understanding-longitudinal-checkpoints.json" assert { type: "json" };
import originalFixture from "@/tests/fixtures/current-understanding-original-investigation.json" assert { type: "json" };
describe("Longitudinal checkpoints — manifest integrity", function () {
it("manifest has exactly 4 checkpoints", async function () {
expect(longitudinalManifest.checkpoints.length).toBe(4);
});
it("checkpoint counts are 0, 2, 7, 9", async function () {
const expectedCounts = [0, 2, 7, 9];
longitudinalManifest.checkpoints.forEach((cp, i) => {
expect(cp.includedFindings.length).toBe(expectedCounts[i]);
});
});
it("checkpoint-0 has no included findings", async function () {
expect(longitudinalManifest.checkpoints[0].includedFindings).toEqual([]);
});
it("checkpoint-1 includes exactly contrib-0001 findings", async function () {
const expected = ["finding-3bw5f0", "finding-i5zdcq"];
expect(longitudinalManifest.checkpoints[1].includedFindings).toEqual(expected);
});
it("checkpoint-2 is cumulative of checkpoint-1 plus contrib-0002 findings", async function () {
const cp2 = new Set(longitudinalManifest.checkpoints[2].includedFindings);
const cp1 = longitudinalManifest.checkpoints[1].includedFindings;
cp1.forEach(f => expect(cp2.has(f)).toBe(true)); // all checkpoint-1 findings present in cp2
expect(cp2.size).toBe(7);
});
it("checkpoint-3 includes all 9 original findings", async function () {
const expected = [
"finding-3bw5f0", "finding-i5zdcq",
"finding-hciikc", "finding-ybfsm3", "finding-k45vyt",
"finding-vhftyr", "finding-9bgkmw",
"finding-23192q", "finding-kkh39k"
];
expect(longitudinalManifest.checkpoints[3].includedFindings).toEqual(expected);
});
it("all checkpoint findings are verbatim from the original fixture", async function () {
const originalMap = {};
originalFixture.findings.forEach(f => { originalMap[f.id] = f; });
longitudinalManifest.checkpoints.forEach(cp => {
cp.includedFindings.forEach(fid => {
expect(originalMap[fid]).toBeDefined();
const manifestFinding = longitudinalManifest.findingsById[fid];
expect(manifestFinding).toEqual(originalMap[fid]);
});
});
});
it("contribution ordering matches recorded timestamps", async function () {
const contribs = longitudinalManifest.contributions;
for (let i = 1; i < contribs.length; i++) {
expect(new Date(contribs[i].timestamp).getTime()).toBeGreaterThan(
new Date(contribs[i - 1].timestamp).getTime()
);
}
});
it("manifest ordering matches fixture finding order", async function () {
const originalIds = originalFixture.findings.map(f => f.id);
const manifestIds = Object.keys(longitudinalManifest.findingsById);
expect(manifestIds).toEqual(originalIds);
});
it("checkpoint-3 is cumulative of all checkpoints before it", async function () {
for (let i = 0; i < 3; i++) {
const cpSet = new Set(longitudinalManifest.checkpoints[i].includedFindings);
const cp3 = longitudinalManifest.checkpoints[3].includedFindings;
cpSet.forEach(f => expect(cp3.includes(f)).toBe(true));
}
});
it("no findings appear before their contribution boundary", async function () {
const contribOrder = {};
longitudinalManifest.contributions.forEach(c => { contribOrder[c.id] = c.ordering; });
Object.entries(longitudinalManifest.findingsById).forEach(([fid, f]) => {
const cpIndex = longitudinalManifest.checkpoints.findIndex(cp =>
cp.includedFindings.includes(fid)
);
// Find which checkpoint first includes this finding
expect(cpIndex).toBeGreaterThanOrEqual(contribOrder[f.contributionId] - 1);
});
});
it("original fixture is unchanged (spot check)", async function () {
expect(originalFixture.findings.length).toBe(9);
expect(longitudinalManifest.checkpoints[3].includedFindings.length).toBe(9);
});
it("production synthesis contract accepts checkpoint inputs", async function () {
// Verify that the filtered findings from each checkpoint can reach synthesizeCurrentUnderstanding
const fake = makeFakeProvider();
for (const cp of longitudinalManifest.checkpoints) {
const graphBefore = deepClone(situationGraph);
const cpFindings = cp.includedFindings.map(fid => originalFixture.findings.find(f => f.id === fid));
await expect(
synthesizeCurrentUnderstanding(
{ situationGraph: situationGraph, findings: cpFindings },
{ provider: fake }
)
).resolves.toBeDefined();
// Immutability check per checkpoint
expect(JSON.stringify(situationGraph)).toBe(JSON.stringify(graphBefore));
}
});
it("manifest references the source fixture path", async function () {
expect(longitudinalManifest.sourceFixture).toBe(
"tests/fixtures/current-understanding-original-investigation.json"
);
});
it("manifest does not fabricate historical graph state", async function () {
// Manifest contains findings data only, not resolvedNodeIds, activeUnknownNodeId, or reasoningState
expect(longitudinalManifest.checkpoints).not.toHaveProperty([0, "resolvedNodeIds"]);
expect(longitudinalManifest.contributions).not.toHaveProperty([0, "reasoningState"]);
});
});