6.8 KiB
Experiment 57J.74 — Pre-Anchored Update-Only Fixtures and Harness
Branch: feature/semantic-action-contract-v0.23
Starting HEAD: beef434 (tooling: capture structural action declaration in live harness)
Experiment commit: pending (docs: record pre-anchored update apparatus)
Objective
Answer exactly:
Does a deterministic pre-existing graph fixture + update-only harness mode exist that allows direct testing of the
false + no meaningful mutation → acceptedbranch without requiring Update 1 to establish an anchor?
This resolves the self-defeating constraint from 57J.73 (Classification G), where Answer 2 was unreachable because Update 1 failed to produce mutation and triggered contract rejection — making it impossible to test whether the model produces false + no-op on a graph that already contains the savings-realism anchor.
Problem Statement
The v0.23 harness only supports start → update(n) chains. There is no mechanism to inject an arbitrary pre-anchored situationGraph directly into the Update API without first running Start. This means:
- Update 2 can never receive a graph where the savings-realism anchor already exists from Answer 1's perspective, because Answer 1 fails at proposal_compatibility when it declares
structuralActionRequired=truebut produces zero mutation. - Even if Update 1 were to succeed (e.g., with Start producing a pre-populated anchor), there is no harness mechanism to inject that graph for the next update's input without actually sending an answer.
- Testing the
false + no-opbranch requires starting from a known anchored state — but onlystart → updatechains are supported.
Hypothesis
If a deterministic pre-existing graph fixture (with a dedicated savings-realism unknown) can be injected directly into the Update request as an arbitrary situationGraph, and the harness can report structuralActionRequired=false + zero meaningful mutation when updating with Answer 2's input on that anchored graph, then:
false + no meaningful mutation → accepted
can be tested without relying on Update 1's success.
Tooling Changes
Fixture: tests/fixtures/pre-anchored-update-savings-realism.json
A deterministic situationGraph representing the state after Answer 1 has been processed:
{
"centralStatement": "We are considering relocating the engineering team to reduce operating costs.",
"nodes": [
{
"id": "n_relocation_state",
"kind": "state",
"status": "provisional",
"label": "Engineering team relocation consideration"
},
{
"id": "n_savings_realism",
"kind": "unknown",
"status": "unknown",
"label": "Are the projected office savings from relocation realistic?"
}
],
"edges": [
{ "fromNodeId": "n_savings_realism", "toNodeId": "n_relocation_state", "relationship": "depends_on" }
],
"activeUnknownNodeId": "n_savings_realism",
"resolvedNodeIds": []
}
Contains exactly one unresolved savings-realism anchor (kind=unknown, status=unknown).
Harness: runPreAnchoredSimulation() in test file
A new synchronous simulator that mirrors what the harness does when supplied an arbitrary pre-anchored graph:
- No Start call — graph is supplied directly via
initialGraphor defaults to the fixture - Verifies fixture integrity before proceeding (exactly one savings-realism unknown)
- Sends the exact fixture graph into the Update request body
- Reports: node count, edge count,
structuralActionRequired, answerMeaning fields, proposal mutations, selectedQuestion - Call accounting reflects 0 start + 1 update
Inlined fixture constant: PRE_ANCHORED_FIXTURE
The JSON fixture is also inlined as a JS constant in the test file so all tests can access it without filesystem reads.
Harness Tests Added (10)
| # | Test | Asserts |
|---|---|---|
| 1 | pre-anchored fixture contains exactly one savings-realism anchor |
savingsNodes.length === 1, id === "n_savings_realism" |
| 2 | fixture uses valid existing graph shape |
All node/edge fields present with valid enum values |
| 3 | fixture contains a valid relationship into the graph |
Edge exists, from/to nodes exist, relationship === "depends_on" |
| 4 | pre-anchored update-only mode sends exact fixture graph into real update request shape |
node count = 2, edge count = 1, ids match fixture |
| 5 | pre-anchored update-only mode does not call Start |
startCalls === 0, updateCalls === 1 |
| 6 | pre-anchored update-only mode makes exactly one Update call |
type === "all_success", exitCode = 0 |
| 7 | normal Start→Update harness mode remains unchanged |
startCalls === 1, updateCalls === 1 via existing patterns |
| 8 | 57J.62 accepted/rejected capture hardening remains unchanged |
addedNodes/updatedNodes/resolvedUnknownNodeIds captured on acceptance; rejection snapshot intact |
| 9 | 57J.72 structuralActionRequired direct capture remains unchanged |
true/false/null reports work correctly via existing patterns |
| 10 | no retries/additional calls introduced in pre-anchored mode |
totalCalls === 1, zero retry entries |
Results
All 39 tests pass. The pre-anchored fixture is valid, the update-only apparatus makes exactly one Update call with no Start, the exact fixture graph is sent, and all existing harness behaviour (57J.62 capture hardening, 57J.72 structuralActionRequired capture, normal start→update mode) remains unchanged.
Classification: A — HARNESS-ONLY FIX VALIDATED
The pre-anchored update-only apparatus successfully decouples Update testing from the Start pipeline for anchor establishment. The fixture is deterministic and valid per the existing graph schema. The harness helper reports all necessary fields with zero Ollama calls, zero production code changes, and zero API calls beyond the single Update request.
What this enables (but does not prove)
This enables testing false + no meaningful mutation → accepted by injecting a pre-anchored graph as the Update input. It does not itself prove that the live model will produce that outcome — only that the harness can now reach that test scenario without requiring Update 1's success. The next step is a live update-only experiment: inject the fixture, send Answer 2, observe whether the model produces structuralActionRequired = false with zero mutation.
What remains unproven
- Whether the live model, given this pre-anchored graph and Answer 2 input, declares
false + no meaningful mutation - Whether the live update accepts that as an intentional no-op (vs. rejecting it)
- Whether a different pre-anchored graph with additional anchors would produce different results
Production code changed: NO
Harness restored: YES
Scenario, answers, and maxUpdates in scripts/reproduce-multi-turn-investigation.mjs are at canonical defaults.