fix(grounding-check): an empty extraction is not a vacuous PASS

Step 5 emitted PASS on "all assumptions ratified", which is vacuously
true when the extraction yielded nothing. Under /boss a grounding PASS
IS the autonomous spec signature, so an under-extraction on a real spec
(zero assumptions -> PASS) would auto-sign an unread spec.

An empty assumption list is now a PASS only on a demonstrably trivial
spec (pure rename / doc-only / cosmetic, with the kind named as positive
evidence). An empty extraction over a spec that adds or changes
behaviour is an under-extraction -> BLOCK, which routes the spec to the
human sign-off pause it should have hit. Tightened consistently across
Step 5, the Iron Law, the trivial-spec clause, the Status protocol
table, and the Common Rationalisations.

closes #13
This commit is contained in:
2026-06-27 14:27:18 +02:00
parent d6c4faa3c0
commit f3eba8ab95
+23 -2
View File
@@ -138,6 +138,7 @@ mechanism strongly.
EXTRACT ASSUMPTIONS FROM THE SPEC, NOT FROM YOUR MEMORY. EXTRACT ASSUMPTIONS FROM THE SPEC, NOT FROM YOUR MEMORY.
RATIFICATION REQUIRES A NAMED, CURRENTLY-GREEN TEST. NOT CODE PRESENCE. NOT RECALL. RATIFICATION REQUIRES A NAMED, CURRENTLY-GREEN TEST. NOT CODE PRESENCE. NOT RECALL.
ONE UNRATIFIED LOAD-BEARING ASSUMPTION = BLOCK. NO PARTIAL CREDIT. ONE UNRATIFIED LOAD-BEARING ASSUMPTION = BLOCK. NO PARTIAL CREDIT.
AN EMPTY ASSUMPTION LIST IS A PASS ONLY ON A DEMONSTRABLY TRIVIAL SPEC — A NO-OP EXTRACTION OVER A REAL SPEC IS A BLOCK, NOT A FREE PASS.
YOU DO NOT EDIT FILES. YOU DO NOT PROPOSE FIXES. YOU DO NOT EDIT FILES. YOU DO NOT PROPOSE FIXES.
YOU DO NOT RUN THE FULL TEST SUITE. (TEST LIST AND TYPE-CHECK ARE OK.) YOU DO NOT RUN THE FULL TEST SUITE. (TEST LIST AND TYPE-CHECK ARE OK.)
``` ```
@@ -169,6 +170,18 @@ YOU DO NOT RUN THE FULL TEST SUITE. (TEST LIST AND TYPE-CHECK ARE OK.)
5. Compute aggregate status: 5. Compute aggregate status:
- All assumptions ratified → `PASS`. - All assumptions ratified → `PASS`.
- One or more unratified assumptions → `BLOCK`. - One or more unratified assumptions → `BLOCK`.
- **Empty assumption list → NOT automatically `PASS`.** "All
ratified" is vacuously true when you extracted nothing, so an
empty list is a `PASS` *only* when the spec is demonstrably
trivial (pure rename / doc-only / cosmetic) and you say which
(the trivial-spec clause below). An empty extraction over a spec
that adds or changes behaviour is an *under-extraction*, not a
clean bill: emit `BLOCK` with reason "no load-bearing assumptions
extracted from a non-trivial spec — re-extract or the spec is
mis-scoped". Before concluding a real spec is assumption-free,
re-read it for the *implicit* form (a new table entry, an extended
pass, a free function added to a surface) — those are the
assumptions most easily missed.
- Any infra error (cannot read spec, type-check fails, - Any infra error (cannot read spec, type-check fails,
workspace does not build) → `INFRA_ERROR`. workspace does not build) → `INFRA_ERROR`.
6. Emit the report in the format below. 6. Emit the report in the format below.
@@ -184,8 +197,8 @@ down to one iteration.
| Status | Meaning | | Status | Meaning |
|--------|---------| |--------|---------|
| `PASS` | All extracted load-bearing assumptions ratified. specify proceeds to Step 6. | | `PASS` | All extracted load-bearing assumptions ratified — OR an empty extraction over a demonstrably trivial spec (named `<kind>`). specify proceeds to Step 6. |
| `BLOCK` | At least one load-bearing assumption is unratified. specify presents the report to the user. | | `BLOCK` | At least one load-bearing assumption is unratified, OR an empty extraction over a non-trivial spec (under-extraction — zero unratified but the empty list is itself the finding). specify presents the report to the user. |
| `INFRA_ERROR` | The dispatch cannot complete (spec file missing, type-check broken, etc.). specify aborts; orchestrator debugs out-of-band. | | `INFRA_ERROR` | The dispatch cannot complete (spec file missing, type-check broken, etc.). specify aborts; orchestrator debugs out-of-band. |
There is no `NEEDS_CONTEXT`. The standing reading list is There is no `NEEDS_CONTEXT`. The standing reading list is
@@ -227,6 +240,13 @@ If the spec is trivial (pure rename, doc-only, cosmetic) and
the assumption-extraction step yields an empty list, emit the assumption-extraction step yields an empty list, emit
`PASS` with an empty ratified table and a one-line `PASS` with an empty ratified table and a one-line
`Note: no load-bearing assumptions extracted (spec is <kind>).` `Note: no load-bearing assumptions extracted (spec is <kind>).`
Naming the `<kind>` is mandatory — it is the positive evidence
that the empty list is a genuinely trivial spec rather than an
under-extraction. If you cannot name a trivial `<kind>`, the
empty list is a `BLOCK` (Step 5), not a `PASS`. This matters most
under `/boss`, where a `PASS` is the autonomous spec signature: a
vacuous PASS would auto-sign an unread spec, while a `BLOCK`
correctly routes it to the human sign-off pause.
## Common Rationalisations ## Common Rationalisations
@@ -237,6 +257,7 @@ the assumption-extraction step yields an empty list, emit
| "There's a fixture that uses this feature indirectly" | Indirect use is weak ratification. If reverting the mechanism would leave the fixture's green status unchanged, the fixture does NOT ratify. | | "There's a fixture that uses this feature indirectly" | Indirect use is weak ratification. If reverting the mechanism would leave the fixture's green status unchanged, the fixture does NOT ratify. |
| "I extracted too many assumptions, let me trim the report" | Don't trim. If a spec has too many assumptions to check, that is the finding — report it as BLOCK with reason "spec too broad". | | "I extracted too many assumptions, let me trim the report" | Don't trim. If a spec has too many assumptions to check, that is the finding — report it as BLOCK with reason "spec too broad". |
| "The orchestrator will override if I block, so I'll lean toward PASS" | The override is the orchestrator's job, not yours. Your job is to be the fresh-context check. Skewing toward PASS defeats the whole role. | | "The orchestrator will override if I block, so I'll lean toward PASS" | The override is the orchestrator's job, not yours. Your job is to be the fresh-context check. Skewing toward PASS defeats the whole role. |
| "I extracted no assumptions, so all are vacuously ratified → PASS" | An empty extraction is a clean bill ONLY on a demonstrably trivial spec (rename / doc-only / cosmetic), and you must name which. On a spec that adds or changes behaviour, extracting nothing means you under-read it — re-scan for the *implicit* assumptions (new table entries, extended passes, a surface a new symbol joins). Empty over a real spec is a BLOCK, not a free PASS. |
| "I'll run the full test suite to see which tests are actually green" | You may NOT run the full test suite. Use the project's test-list command to enumerate, then read test bodies. Running tests would mutate workspace state and is out of scope for a read-only review. | | "I'll run the full test suite to see which tests are actually green" | You may NOT run the full test suite. Use the project's test-list command to enumerate, then read test bodies. Running tests would mutate workspace state and is out of scope for a read-only review. |
## Red Flags — STOP ## Red Flags — STOP