Read-only inspection of 8 files (prompt-builder.js, schema.js, utils.js,
apply-proposal.js, orchestrator.js, experiment-60b1.md, experiment-60b2.md,
current-handoff.md). No code changes.
Key findings:
- Prompt has no independent materiality/sufficiency rule (Rule 20 says null
selectedQuestion when 'no consequential unresolved unknown' but doesn't define
what makes an unknown non-consequential)
- Validator performs structural checks only, no evidence sufficiency evaluation
- No cross-option comparison logic in propagateResolvedChildEvidence
- Schema has no materiality or couldChangeDecision field
- 60B.1 resolved WITH 'no other material differences' cue; 60B.2 continued
WITHOUT it, despite internally computing ~3.6 month payback
Classification: C — NO SUFFICIENCY RULE + CONTINUATION BIAS
Missing distinction: MATERIALITY / DECISION-RELEVANCE RULE
Implementation of Candidate B (unknown+option) from decision architecture
design in 60A.2. Adds two new primitives to the situation graph:
Schema (lib/graph/schema.js):
- SituationKind.option — a choice available within a decision context
- SituationRelationship.contained_in — links option → its parent unknown context
Prompt rules (lib/graph/prompt-builder.js):
- Section added: Decision Option Structure Rules with 5 numbered instructions
governing when/how to create option nodes, link them via contained_in,
attach consequences to specific options, and handle do-nothing alternatives.
Explicitly forbids alternative_to edges and is_baseline/is_default flags.
Tests (446 new lines):
- schema.test.js: +300 — enum completeness updates, option kind validation,
contained_in edge validation, native two-option graph fixture (~25 new tests)
- prompt-builder.test.js: +133 — focused rules verification for all 5 rule points,
negative checks (no relocation/savings/example-specific wording, no alternative_to
requirement, baseline flag prohibition context)
No production code paths affected beyond the two enum additions; existing node and
edge kinds remain unchanged. No Ollama calls, no live API calls.
Diagnoses the root cause of the persistent pattern from 59B.2-59B.4
where explicit dual-option input collapsed into a single undifferentiated
unknown node. Concludes the graph vocabulary lacks first-class primitives
for options/decisions (not primarily a prompt issue). Identifies three
missing primitives: option node kind, decision node kind, alternative_of
edge type. Recommends ~25-line schema addition for 60A.2 implementation.
Add FIXTURE_MODE=updateOnly support that bypasses Start and sends the
committed fixture (tests/fixtures/pre-anchored-update-savings-realism.json)
directly as an Update request body through production HTTP route.
scripts/reproduce-multi-turn-investigation.mjs:
- Added ESM imports for deterministic fixture loading (fs, fileURLToPath, path)
- Added FIXTURE_PATH constant pointing to committed fixture
- Added fixtureMode env-var selector and runUpdateOnlyMode() function
- Validates ANSWER_2 before any live call (zero calls if missing)
- Verifies single savings-realism anchor invariant on load
- Preserves all hardened capture fields in pre-anchored mode
- Normal-mode Start→Update chain preserved under guard clause
tests/reproduce-multi-turn-investigation.harness.test.js:
- Added 7 new harness tests for pre-anchored scenarios (46 total, all pass)
- Updated runPreAnchoredSimulation to persist rejectedProposalSnapshot on rejection
- Added runPreAnchoredSimulationWithBlock() helper
docs/:
- New docs/experiment-57j78.md with full apparatus description
- Updated docs/current-handoff.md with 57J.78 section
- Experiment 57J.49: read-only architecture diagnosis showing EXISTING STRUCTURE IS PARTIAL
- All classification enums (supportCategory) and resolution enums (resolutionGuidance) already exist in production schema
- Gap is population (prompt says leave null if unsure) + enforcement (no enum constraint on Zod fields)
- Corrected 57J.48 overstatement of SUFFICIENT → PARTIAL sufficiency in handoff
Add one explicit action-order rule in Additional Guidance for when
rule #6 applies to explicitly unresolved uncertainty:
1. First check whether an existing unresolved node already represents
the same uncertainty.
2. If so, update/refine that existing structure rather than creating
a duplicate.
3. If no such node exists, add a new unknown that directly represents
the unresolved uncertainty.
4. Do not use an edge alone to represent a previously unrepresented
uncertainty.
14 focused prompt tests verify: existing-first ordering, reuse path,
fallback-to-add, related-node-insufficient, edge-only-prohibited,
possibleInference separation, resolution path preserved, duplicate
contract preserved, scope uncertainty-only, fidelity/traceability
preserved, noop validator untouched, no semantic classifier added.
Adds rejectedProposalSnapshot to orchestrator diagnostics for
proposal_compatibility rejections — exposing answerMeaning (userSupportedMeaning,
possibleInference), addedNodes structural fields, addedEdges structural fields,
updatedNodes summaries, and resolvedUnknownNodeIds. Diagnostic evidence only;
does not alter validation, mutation, or error messages. Stage-gated to
proposal_compatibility only.