fix(reasoning): enforce terminal post-mutation eligibility

This commit is contained in:
2026-08-14 09:11:08 +01:00
parent 34eb0cd4e3
commit 6b13e67c05
4 changed files with 414 additions and 13 deletions
+8
View File
@@ -234,6 +234,14 @@ Experiment 54N tested whether an interpretation disagreement can be judged for m
- Focused preservation run passed via `npx vitest run tests/graph/apply-proposal.test.js -t "60B.42|60B.11|replaces downstream pricing"`.
- Broader duplicated post-mutation eligibility checks remain unchanged; this fixes the selector contract only, not the full stale-question lifecycle.
### 60B.43 terminal post-mutation eligibility
- Extended terminal-status exclusion across the remaining post-mutation eligibility checks in `lib/graph/apply-proposal.js` while leaving pre-mutation `validateSelectedQuestion(...)` unchanged.
- `isSelectableUnresolvedUnknown(...)`, `remainingUnknownExists`, carried-active reuse, unresolved/eligible candidate listing, and pattern-compatible post-mutation selection now all treat `known|resolved|contradicted` as terminal/non-selectable.
- Deterministic 60B.37-shaped closure regression now passes: proposal applies, `n_enterprise_customer_signing` resolves, `n_product_launch_decision` becomes known, `activeUnknownNodeId` becomes null, and final `selectedQuestion` becomes null.
- Focused fallback case also passes: when the proposal-selected target becomes known but another real unresolved unknown remains, the terminal target is discarded and the real unresolved node is selected.
- Focused preservation run passed via `npx vitest run tests/graph/apply-proposal.test.js -t "60B.43|60B.11|replaces downstream pricing"`; live rerun still required to prove the same outcome end-to-end under live conditions.
### When This Knowledge-Management Phase Is Complete
Provisional criteria for review (all confirmed met by Experiment 38 cold-start test):
+123
View File
@@ -0,0 +1,123 @@
# 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.