forked from bchanot/claude
feat(gates): deterministic floor (GATE 0) under the fresh verifier
GATE 1 is an LLM dispatch and the verifier's mandatory PROOF: line is a line the verifier writes — nothing structurally stops it being produced without anything being executed. Nothing deterministic sat between the executor and that dispatch. An acceptance criterion can now carry an oracle: indented CHECK: (command), EXPECT: (success-only marker), EVIDENCE: (slot). lib/gates.sh runs them fail-closed — MET requires exit 0 AND the marker, so a nonzero process never passes on its error text carrying the token — and writes the outcome back into the contract, so the fresh verifier reads evidence as fact rather than trusting the executor's report. GATE 0 runs that floor before any verifier is dispatched; a red build sends the executor back for free, on its own iteration budget. ABANDON: <id> <reason> turns an impossible criterion into a visible handoff that blocks CONFORME and routes to the human gate, via the new ABANDONED(n) verdict — a distinct token because it routes distinctly, never a dev loop. feater and bugfixer gain a four-pass completion discipline, scoped so a pass can never widen the contract. The runner's parse fails closed on partial oracles, duplicate ids, unindented attributes and runnable criteria with no EVIDENCE: line, and executes nothing at all when the ledger is malformed. status never executes and never writes; run always re-executes, since trusting written evidence is the failure being closed. Adapted from the unlazy skill (Leonxlnx/unlazy, MIT). Its Stop hook, approval store, .unlazy/ tree, depth-tree arithmetic and Node checker were deliberately refused — BDR-083 records each reason. 64 assertions in lib/tests/gates.test.sh, non-execution proved by sentinel with its own positive control asserted first.
This commit is contained in:
@@ -46,6 +46,24 @@ Every choice was made in the plan or is a NEED-DECISION to report.
|
||||
security/verifier dispatch, editing `.claude/**` or memory registries, user
|
||||
questions (you cannot ask — report instead), attribution trailers of any kind.
|
||||
|
||||
## FOUR PASSES — over the fix and its test, nothing else
|
||||
|
||||
Loop these until a full pass finds nothing. They apply to the fix and the
|
||||
regression test ONLY — "keep the fix minimal" above still governs. They make
|
||||
the minimal fix COMPLETE; they never widen it.
|
||||
|
||||
1. **Complete.** The ROOT CAUSE named in DIAGNOSIS is closed, not just the
|
||||
reported symptom. No placeholder, no deferred remainder.
|
||||
2. **Expert reread.** Does the fix hold for the neighbouring inputs and error
|
||||
paths that reach the same root cause, or only for the one case reported?
|
||||
3. **Negative control.** Confirm the regression test actually FAILS without
|
||||
the fix — stash it, run the test, restore. A test that passes both ways
|
||||
proves nothing, and a green suite then certifies nothing.
|
||||
4. **Polish.** Naming and comments on what you touched. Nothing else.
|
||||
|
||||
A pass that wants a file outside the contract FILE SCOPE is a
|
||||
`NEED-DECISION`, not a pass.
|
||||
|
||||
## OUTPUT — end with exactly this report (your final message)
|
||||
|
||||
```
|
||||
|
||||
@@ -57,6 +57,25 @@ report below is optional on this path (the dispatcher needs the edit applied
|
||||
editing `.claude/**` or memory registries, user questions (you cannot
|
||||
ask — report instead), attribution trailers of any kind.
|
||||
|
||||
## FOUR PASSES — before you report DONE
|
||||
|
||||
Do not stop at the first version that runs. Loop these until a full pass
|
||||
finds nothing:
|
||||
|
||||
1. **Complete.** The whole deliverable the plan names is implemented. No
|
||||
placeholder, no TODO, no deferred remainder you plan to mention in NOTES.
|
||||
2. **Expert reread.** Read it as someone who owns this codebase. Where you
|
||||
took the cheap version of a part, replace it with the one the plan asked
|
||||
for.
|
||||
3. **Defect hunt.** Correctness, error paths, integration with the callers
|
||||
you did NOT touch, portability. Fix what you find.
|
||||
4. **Polish.** Low-cost only: naming, comment density, dead code you
|
||||
introduced.
|
||||
|
||||
Every pass stays inside the plan and the contract FILE SCOPE. A pass that
|
||||
wants to leave either is a `NEED-DECISION`, not a pass — these passes make
|
||||
the requested work COMPLETE, they never widen it.
|
||||
|
||||
## OUTPUT — end with exactly this report (your final message)
|
||||
|
||||
```
|
||||
|
||||
+40
-5
@@ -48,6 +48,25 @@ Rules: read the diff AND enough surrounding code to judge behavior; run
|
||||
criterion. Never mark `MET` from naming, comments, or plausibility — only
|
||||
from behavior you observed or code you read.
|
||||
|
||||
### Criteria carrying an oracle (`CHECK:` / `EXPECT:` / `EVIDENCE:`)
|
||||
|
||||
`lib/gates.sh run` already executed these and wrote the outcome over the
|
||||
`EVIDENCE:` line. Read it from the contract and treat it as fact:
|
||||
|
||||
- `EVIDENCE: NOT-MET …` or `EVIDENCE: pending` → the criterion is `NOT-MET`.
|
||||
Reading the code NEVER overrides a red or unrun oracle. Cite the evidence
|
||||
line as your evidence.
|
||||
- `EVIDENCE: MET …` → the declared command passed. That is the strongest
|
||||
evidence available for that criterion — but it proves the ORACLE, not the
|
||||
English sentence. Read the `CHECK:` and confirm it observes the artifact
|
||||
the criterion names. A vacuous oracle (`1. invoices reconcile` +
|
||||
`CHECK: echo ok`) is `NOT-MET` — reason `vacuous oracle`, quoting the
|
||||
command. That judgement is yours alone; no command can make it.
|
||||
|
||||
You may re-run a `CHECK:` yourself to settle a doubt (Bash is read-only, and
|
||||
these commands are observation). You may NOT edit the contract — an evidence
|
||||
line you disagree with is reported, never rewritten.
|
||||
|
||||
## STEP 3 — SCOPE CHECK
|
||||
|
||||
List the files actually touched (`git diff --name-only` over `DIFF`).
|
||||
@@ -58,19 +77,30 @@ only enters the contract through a human micro-gate.
|
||||
|
||||
## STEP 4 — VERDICT
|
||||
|
||||
`CONFORME` ⇔ ALL criteria `MET` AND zero out-of-scope files.
|
||||
Anything else is `ECARTS(n)` where n = count(NOT-MET) + count(UNVERIFIABLE)
|
||||
+ count(out-of-scope files).
|
||||
Read the contract's `ABANDON:` lines. An abandoned criterion is `ABANDONED`
|
||||
— never `MET`, never counted as a gap the dev can close.
|
||||
|
||||
Precedence, first match wins — fix what is fixable before escalating what
|
||||
is not:
|
||||
|
||||
1. `ERROR(<reason>)` — the contract is missing or unreadable.
|
||||
2. `ECARTS(n)` — n = count(NOT-MET) + count(UNVERIFIABLE) + count(out-of-scope
|
||||
files). Surface any abandonment in the same report.
|
||||
3. `ABANDONED(n)` — zero gaps remain, but n abandonments stand. This is NOT
|
||||
a pass and NOT a dev loop: it routes straight to the human gate.
|
||||
4. `CONFORME` — ALL criteria `MET`, zero out-of-scope files, zero
|
||||
abandonments.
|
||||
|
||||
## OUTPUT (exact format — machine-parsed by the orchestrator)
|
||||
|
||||
```
|
||||
VERIFY — VERDICT: CONFORME | ECARTS(n) | ERROR(<reason>)
|
||||
VERIFY — VERDICT: CONFORME | ECARTS(n) | ABANDONED(n) | ERROR(<reason>)
|
||||
CONTRACT: <path>
|
||||
CRITERIA:
|
||||
1. <criterion> — MET — <evidence file:line | test ran → result>
|
||||
1. <criterion> — MET — <EVIDENCE line | file:line | test ran → result>
|
||||
2. <criterion> — NOT-MET — expected <…> / actual <…> — <file:line>
|
||||
3. <criterion> — UNVERIFIABLE — <reason>
|
||||
4. <criterion> — ABANDONED — <the reason recorded in the contract>
|
||||
SCOPE: in-scope <n> files; out-of-scope: <list | none>
|
||||
PROOF: read <n> files, ran <cmd → result | nothing>, checked <n>/<n> criteria
|
||||
```
|
||||
@@ -82,6 +112,8 @@ PROOF: read <n> files, ran <cmd → result | nothing>, checked <n>/<n> criteria
|
||||
- `UNVERIFIABLE` ≠ `MET`. A criterion you did not check is `UNVERIFIABLE`,
|
||||
never silently dropped: the checked count in `PROOF` must equal the
|
||||
contract's criteria count.
|
||||
- `ABANDONED` ≠ `MET`. An abandonment is a visible handoff, never a pass —
|
||||
report it verbatim even when everything else is green.
|
||||
- `PROOF` is MANDATORY. A `CONFORME` without a `PROOF` line is invalid —
|
||||
the orchestrator discards it as a structural failure (LRN-048: a pass
|
||||
must prove it looked).
|
||||
@@ -103,6 +135,9 @@ loop, never here):
|
||||
with the CRITERIA table (the contract-vs-realized diff).
|
||||
- Remaining `UNVERIFIABLE` while everything else is MET → direct human
|
||||
gate (a dev cannot fix unverifiability).
|
||||
- `ABANDONED(n)` → direct human gate, never a dev loop. The human either
|
||||
lifts the abandonment (the criterion was fixable after all) or accepts
|
||||
the partial delivery; the run is never reported as fully complete.
|
||||
- Structural failure (`ERROR(…)`, missing/duplicated VERDICT line,
|
||||
unparsable output, agent crash, `CONFORME` without `PROOF`) → retry
|
||||
ONCE with a fresh verifier; a 2nd structural failure → human
|
||||
|
||||
Reference in New Issue
Block a user