Files
confidence-engine/docs/experiment-60b43.md
T

123 lines
3.5 KiB
Markdown

# 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.