experiment: define decision sufficiency module boundary

This commit is contained in:
2026-08-14 15:00:48 +01:00
parent bce05f779b
commit 983ebcc836
2 changed files with 447 additions and 0 deletions
+94
View File
@@ -3295,3 +3295,97 @@ One unresolved question at implementation layer only: precise phrase family brea
### VITEST RUN: NO
### JEST RUN: NO
### WATCHMAN USED: NO
## Experiment 60B.65 — Decision-sufficiency module boundary audit (read-only structural analysis)
**Type:** Structural audit of decision-sufficiency implementation scale and extractability
**Purpose:** Determine whether the 60B.64 closure implementation added ~210 lines to apply-proposal.js is carrying too many responsibilities, and identify the smallest safe module boundary for future zero-semantic-change extraction.
### LINE FOOTPRINT (lib/graph/apply-proposal.js — 4730 lines)
| Unit | Lines | Approx count |
|---|---|---|
| Confirmation helper (`isUserConfirmationOfNoRemainingUncertainty`) + constants | 61117 | ~57 |
| Remaining-factor helpers (`isUnresolvedUnknown`, `hasRemainingMaterialFactors`, `countRemainingMaterialFactors`) + TERMINAL_STATUSES | 46154730 | ~111 |
| Closure integration block (inside applyValidatedProposal) | 38353984 | ~149 |
| **Total decision-sufficiency production lines** | — | **~317** |
The closure integration block (~149 lines) is the largest single unit. It contains three responsibilities: virtual resolution set construction, decision-sufficiency evaluation (predicate), and graph mutation (status update). The virtual helper `checkRemainingFactorsVirtual` duplicates ~55 lines of traversal logic from `countRemainingMaterialFactors`.
### RESPONSIBILITY CLASSIFICATION
| Unit | Classification | Mixed? |
|---|---|---|
| Confirmation helper | TEXT CONFIRMATION | NO — pure text predicate |
| `isUnresolvedUnknown` | GRAPH QUERY | NO |
| `hasRemainingMaterialFactors` | GRAPH QUERY | NO |
| `countRemainingMaterialFactors` | GRAPH QUERY | NO |
| Closure integration block | ORCHESTRATION + MUTATION | YES — 3 responsibilities in one inline block |
### DATA DEPENDENCIES
- **Confirmation:** needs only `answer` string. Zero accidental coupling. Pure function.
- **Remaining-factor detection:** needs `(decisionNodeId, graph)`. Zero accidental coupling. Pure function.
- **Closure application:** reads `updatedSituationGraph`, `proposalSnapshot`, `answer`. All PASSABLE ARGUMENTS — no deep local-variable dependency. The virtual helper's duplication of graph traversal is latent code smell, not accidental coupling.
### CANDIDATE ASSESSMENT SUMMARY
| Candidate | Semantic risk | Coupling reduction | Testability | Complexity reduction | Schema change? |
|---|---|---|---|---|---|
| A (no extract) | N/A | NONE | NONE | NONE | NO |
| B (query only) | LOW | MEDIUM | MEDIUM | MEDIUM | NO |
| C (query+confirm) | LOW | HIGH | HIGH | MEDIUM | NO |
| **D (pure sufficiency unit)** ★ | **LOW** | **HIGH** | **HIGH** | **HIGH** | **NO** |
| E (query+mutation) | HIGH | MEDIUM | MEDIUM | LOW-MED | YES/NO |
### PREDICATE BOUNDARY
**Pure-function boundary possible: YES**
```js
shouldCloseDecision({ decisionNodeId, graph, answer }) -> boolean
```
Derived from two existing pure functions + one boolean combine. Zero mutation, zero state change. All inputs naturally available at the closure-evaluation point.
### MINIMUM ORCHESTRATION REMAINING AFTER EXTRACTION
~40 lines (reduced from ~150):
```js
iterate parent unknown nodes → call shouldCloseDecision() → apply mutation if true → set closureApplied = true
```
### TEST MIGRATION
- 60B.61 tests (9 cases, 353 lines): **YES** — all test pure `hasRemainingMaterialFactors`
- Confirmation subtests: **PARTIAL** — mixed with integration fixtures
- 60B.64 full integration tests: **STAY in apply-proposal.test.js**
### RUNTIME / TOOLING IMPACT
| Question | Answer |
|---|---|
| Runtime performance improvement? | NEGLIGIBLE |
| Claude/Codex edit reliability? | LIKELY YES |
| Future context reduction? | LIKELY YES |
### CRITICAL DISTINCTION: D — EXTRACT PURE DECISION-SUFFICIENCY UNIT
### MINIMUM REFACTOR BOUNDARY: B — ONE NEW decision-sufficiency.js MODULE
### REFACTOR TIMING: B — RUN LIVE REGRESSION FIRST, THEN REFACTOR
### IMPLEMENTATION READINESS: A — READY FOR ZERO-SEMANTIC-CHANGE REFACTOR
The evaluation logic (confirmation detection + remaining-factor counting + closure predicate) is entirely pure and self-contained. One `decision-sufficiency.js` module with 5 exports achieves all seven criteria: zero semantic change, identical 60B.64 behaviour, visible orchestration (~40 lines), mutation ownership in apply-proposal, independently testable pure logic, no schema/prompt changes, less future context.
**One unresolved question at implementation layer only:** Should `shouldCloseDecision` return just `boolean` or a richer shape `{ hasRemainingFactors, userConfirmedNoRemainingUncertainty, shouldClose }` for diagnostic logging? This does not affect extraction correctness — only post-refactor API surface.
Smallest zero-semantic-change refactor: extract all decision-sufficiency evaluation logic to one module with 5 exports, replace inline closure evaluation in apply-proposal.js with call to `shouldCloseDecision`, keep mutation code in apply-proposal.js.
### PRODUCTION CODE CHANGED: NO
### TESTS CHANGED: NO
### PROMPT CHANGED: NO
### SCHEMA CHANGED: NO
### OLLAMA CALLS: 0
### LIVE API CALLS: 0
### VITEST RUN: NO
### JEST RUN: NO
### WATCHMAN USED: NO