94 lines
4.1 KiB
Markdown
94 lines
4.1 KiB
Markdown
# Experiment 57J.70 — structuralActionRequired as Authoritative No-Op Contract
|
|
|
|
**Branch:** `feature/semantic-action-contract-v0.23`
|
|
**Starting HEAD:** `bd3c7d5` (fix(graph): make structural action contract authoritative)
|
|
|
|
## Objective
|
|
|
|
Answer and fix exactly:
|
|
|
|
> Can the validator emit only the authoritative `structuralActionRequired` contract diagnostic on the new-contract path, while preserving the old no-op behaviour only for legacy proposals that do not use the new field?
|
|
|
|
## Defect (from 57J.69)
|
|
|
|
When `structuralActionRequired=true` + zero-mutation proposal:
|
|
|
|
```
|
|
- structuralActionRequired is true but proposal contains no graph mutation
|
|
- answerMeaning.userSupportedMeaning is populated, but the proposal contains no graph mutation. answerMeaning alone does not constitute graph progress.
|
|
```
|
|
|
|
Both errors fired for the same proposal. Under the v0.23 design, only the first (new contract) error should fire when `structuralActionRequired` is present.
|
|
|
|
## Root Cause
|
|
|
|
The legacy semantic-only no-op guard at line 905 of `lib/graph/utils.js` used the condition:
|
|
|
|
```javascript
|
|
if (!hasMeaningfulChange && update.structuralActionRequired !== false) {
|
|
```
|
|
|
|
This meant the guard still fired when `structuralActionRequired === true`, because `true !== false`. The guard then checked `meaningPopulated` (which was true) and added a second, duplicate error message about userSupportedMeaning.
|
|
|
|
## Fix
|
|
|
|
Changed the guard condition to only fire when `structuralActionRequired` is **absent** (null/undefined):
|
|
|
|
```javascript
|
|
const fieldAbsent =
|
|
update.structuralActionRequired === null ||
|
|
update.structuralActionRequired === undefined;
|
|
|
|
if (!hasMeaningfulChange && fieldAbsent) {
|
|
if (meaningPopulated) {
|
|
// structuralActionRequired was missing while userSupportedMeaning exists.
|
|
// Missing-field rejection already added above; skip semantic-only guard.
|
|
} else if (!meaningPopulated) {
|
|
errors.push("Update contains no meaningful change");
|
|
}
|
|
}
|
|
```
|
|
|
|
This ensures:
|
|
- `true` / `false` → new contract owns no-op/mutation consistency; legacy guard is silent
|
|
- `null` / `undefined` → transition rule fires first (missing-field rejection), then legacy no-op for meaning-less proposals
|
|
|
|
## Contract Matrix After Fix
|
|
|
|
| structuralActionRequired | meaningful mutation | Result | Errors |
|
|
|---|---|---|---|
|
|
| true | absent | REJECT | 1: "structuralActionRequired is true but proposal contains no graph mutation" |
|
|
| true | present | PASS | 0 |
|
|
| false | absent | PASS (intentional no-op) | 0 |
|
|
| false | present | REJECT | 1: "structuralActionRequired is false but proposal contains meaningful mutations" |
|
|
| null/missing | populated meaning | REJECT | 1: "structuralActionRequired must be present when userSupportedMeaning is populated" |
|
|
| null/missing | no meaning + zero mutation | REJECT | 1: "Update contains no meaningful change" |
|
|
|
|
## Tests Added (structural-action-contract-v0.23 block)
|
|
|
|
1. `true + zero mutation + populated meaning` → exactly one contract error, no legacy duplicate ✓
|
|
2. `false + zero mutation + populated meaning` → pass (intentional no-op) ✓
|
|
3. `true + meaningful mutation` → pass ✓
|
|
4. `false + meaningful mutation` → exactly one contradiction error ✓
|
|
5. `null + populated meaning` → transition rule rejection preserved ✓
|
|
6. `null + no meaning + zero mutation` → legacy no-op rejection preserved ✓
|
|
7. `hasMeaningfulChange` definition unchanged (status change = meaningful) ✓
|
|
8. Schema shape unchanged (makeNode/makeEdge resolve correctly) ✓
|
|
9. Validator-level fix does not affect apply path ✓
|
|
10. Pure boolean + structural gating, no semantic keyword logic added ✓
|
|
|
|
## Tests Updated in Existing "semantic-to-mutation contract" Block
|
|
|
|
- Test 1: Changed to verify missing-field rejection fires (since `structuralActionRequired` was previously absent)
|
|
- Tests 4-8: Added `structuralActionRequired: true` where meaningful mutation is present (required by v0.23 transition rule)
|
|
|
|
## Files Changed
|
|
|
|
- `lib/graph/utils.js` — guard condition (line ~905)
|
|
- `tests/graph/utils.test.js` — updated 6 existing tests, added 10 new regression tests
|
|
|
|
## Ollama calls: 0
|
|
## Live API calls: 0
|
|
## Schema changed: NO
|
|
## Prompt changed: NO
|