# Experiment 60B.43 — Terminal post-mutation eligibility **Date:** 2026-08-14 **Branch:** `feature/post-mutation-terminal-eligibility-v0.38` ## Purpose Extend the 60B.42 selector guard to the remaining post-mutation question-target eligibility checks so terminal-status unknown nodes cannot remain active or become the final selected question after mutation. ## Starting point from 60B.42 60B.42 fixed `selectActiveUnknownCandidate(...)` so it no longer scores or returns unknown-kind nodes whose status is terminal: - `known` - `resolved` - `contradicted` That closed one fallback source, but several independent post-mutation checks in `apply-proposal.js` still used weaker eligibility rules and could keep terminal nodes alive through other paths. ## Remaining post-mutation eligibility changes This experiment changed post-mutation eligibility only in `lib/graph/apply-proposal.js`. Updated paths: - `isSelectableUnresolvedUnknown(...)` - `remainingUnknownExists` - `carriedActiveUnknownStillUnresolved` - `listUnresolvedUnknownCandidates(...)` - `listEligibleUnknownCandidates(...)` - `selectPatternCompatibleUnknownCandidate(...)` ### Terminal-status rule applied post-mutation A selectable unresolved post-mutation target now requires: ```text kind === unknown status NOT IN [known, resolved, contradicted] not in resolvedNodeIds ``` ### Important boundary preserved `validateSelectedQuestion(...)` was **not** changed. The closure proposal remains valid pre-mutation even when: - `selectedQuestion.nodeId = n_product_launch_decision` - the same proposal updates `n_product_launch_decision -> known` Only after mutation is that now-terminal target discarded. ## 60B.37 deterministic regression Added focused regression: - `60B.43 — terminal post-mutation target is cleared after valid decision closure` Reproduced the 60B.37-shaped closure: - existing active unknown: `n_enterprise_customer_signing` - proposal resolves `n_enterprise_customer_signing` - proposal updates `n_product_launch_decision -> known` - proposal still selects `n_product_launch_decision` - no added nodes or edges ### Result - proposal applied successfully - customer factor resolved in place - decision became known in place - both options preserved unchanged - `activeUnknownNodeId = null` - final `selectedQuestion = null` ## Fallback-to-real-unknown result Added a second focused case where: - proposal-selected target becomes `known` - another genuine unresolved unknown remains after mutation Result: - terminal known target discarded - remaining genuine unresolved candidate selected ## Known-only post-mutation result Added a valid mutated case where the final remaining unknown becomes `known` in the same mutation. Result: - `activeUnknownNodeId = null` - `selectedQuestion = null` ## Preservation checks Focused run also preserved: - 60B.11 preferred-target behaviour - pricing prerequisite-first behaviour - resolved exclusion - contradicted exclusion ## Validation Command run: ```bash npx vitest run tests/graph/apply-proposal.test.js -t "60B.43|60B.11|replaces downstream pricing" ``` Result: - PASS — `14 passed | 76 skipped` ## What remains unproven until live rerun This deterministic bounded fix now clears the exact 60B.37-shaped stale target through the post-mutation production path under test. What remains unproven until a live rerun is whether the full runtime/orchestration path with the real customer-signing closure answer produces the same null-question closure end state under live conditions.