forked from bchanot/claude
feat(agents): wire contract + verify + security into feat/bugfix/hotfix (verify-loops lot 4)
lib/verify-secure-loop.md: shared main-loop include. GATE 1 fresh verifier (blind, contract from disk) → CONFORME straight to GATE 2, ECARTS loop max 3; GATE 2 fresh security-auditor (MODE gate) → PASS to commit, BLOCK loop max 3 with re-verify-request-FIRST order invariant. Mute agent never a PASS. feater.md: STEP 0.7 CONTRACT (proportional, silent on a clear feature) + STEP 3 VERIFY+SECURE via the include. Nominal = one verifier + one security dispatch; the loop only costs when it loops. bugfixer.md: STEP 3.5 CONTRACT fed by the DIAGNOSIS (bug report verbatim + reproduced-then-gone + regression test criteria) + STEP 5 fresh gates via the include. Renumbered STEP 5 sub-steps (gates before the commit gate). hotfixer.md: STEP 1.7 CONTRACT (silent autofill, zero questions) + STEP 3 security gate whose FAILURE REVERTS (git restore to pre-flight SHA + escalate to /bugfix), never loops — the 1-attempt model preserved. No fresh verifier at hotfix weight (the smoke-check verifies the trivial contract). Adds the Agent tool to hotfixer.md + hotfix/SKILL.md for the security dispatch. lib/tests/loops-light.test.sh: 27 structure locks green, shellcheck clean. Behavioral pipeline dogfood on a fixture (feat adding a feature WITH a SQLi): GATE1 CONFORME (feature present, SQLi not a conformity gap — orthogonal gates) → GATE2 BLOCK(1) (checklist caught the %-interp SQLi semgrep's taint rules missed) → [fix to parameterized] → re-verify CONFORME (order invariant, feature intact) → re-scan PASS. Loop converges to green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5aa4216409
commit
0f0162dcae
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# Structure locks — light-flow wiring (verify-loops lot 4)
|
||||
# feat/bugfix get contract + fresh verifier + security gate
|
||||
# (bounded 3x); hotfix gets contract + security gate whose
|
||||
# FAILURE REVERTS (never loops). Locks the load-bearing clauses.
|
||||
# ============================================================
|
||||
set -u
|
||||
|
||||
REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
INC="$REPO/lib/verify-secure-loop.md"
|
||||
FEA="$REPO/agents/feater.md"
|
||||
BUG="$REPO/agents/bugfixer.md"
|
||||
HOT="$REPO/agents/hotfixer.md"
|
||||
HSK="$REPO/skills/hotfix/SKILL.md"
|
||||
PASS=0; FAIL=0
|
||||
|
||||
tf() { # tf <label> <file> <fixed-string>
|
||||
if grep -qF -- "$3" "$2" 2>/dev/null; then
|
||||
echo " PASS $1"; PASS=$((PASS+1))
|
||||
else
|
||||
echo " FAIL $1 — missing: $3"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
tr_() { # tr_ <label> <file> <ERE>
|
||||
if grep -qE -- "$3" "$2" 2>/dev/null; then
|
||||
echo " PASS $1"; PASS=$((PASS+1))
|
||||
else
|
||||
echo " FAIL $1 — no match: $3"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "── verify-secure-loop.md (shared include) ──"
|
||||
if [ -f "$INC" ]; then echo " PASS include exists"; PASS=$((PASS+1)); else echo " FAIL include missing"; FAIL=$((FAIL+1)); fi
|
||||
tf "gate1 fresh verifier" "$INC" "GATE 1 — REQUEST CONFORMITY (fresh verifier)"
|
||||
tf "gate2 fresh auditor" "$INC" "GATE 2 — SECURITY (fresh security-auditor)"
|
||||
tf "blind — no dev summary" "$INC" "Never pass the dev's summary"
|
||||
tf "conforme first pass no loop" "$INC" "First-pass conforme = no loop"
|
||||
tf "conformity max 3" "$INC" "Max 3 conformity iterations"
|
||||
tf "security max 3" "$INC" "Max 3 security iterations"
|
||||
tf "reverify request first" "$INC" "re-verify the REQUEST first"
|
||||
tf "order invariant" "$INC" "always re-checked BEFORE security"
|
||||
tf "mute never a pass (verify)" "$INC" "NEVER a PASS"
|
||||
tf "nominal cheap stated" "$INC" "one verifier dispatch + one security dispatch"
|
||||
|
||||
echo "── feater.md (feat wiring) ──"
|
||||
tf "feat contract step" "$FEA" "STEP 0.7 — CONTRACT"
|
||||
tf "feat contract-interview" "$FEA" "lib/contract-interview.md"
|
||||
tf "feat verify+secure step" "$FEA" "STEP 3 — VERIFY + SECURE"
|
||||
tf "feat uses shared include" "$FEA" "lib/verify-secure-loop.md"
|
||||
tf "feat nominal 1+1 dispatch" "$FEA" "verifier + one security dispatch"
|
||||
|
||||
echo "── bugfixer.md (bugfix wiring) ──"
|
||||
tf "bug contract step" "$BUG" "STEP 3.5 — CONTRACT"
|
||||
tf "bug diagnosis feeds it" "$BUG" "feeds it: REQUEST verbatim"
|
||||
tf "bug fresh gates" "$BUG" "Fresh gates (verify + secure)"
|
||||
tf "bug uses shared include" "$BUG" "lib/verify-secure-loop.md"
|
||||
|
||||
echo "── hotfixer.md (hotfix wiring — revert, not loop) ──"
|
||||
tr_ "hotfix has Agent tool" "$HOT" "^tools:.*Agent"
|
||||
tf "hotfix silent contract" "$HOT" "STEP 1.7 — CONTRACT (silent autofill)"
|
||||
tf "hotfix zero questions" "$HOT" "questions ever"
|
||||
tf "hotfix security gate" "$HOT" "Security gate (fresh auditor)"
|
||||
tf "hotfix block reverts" "$HOT" "failure REVERTS, never loops"
|
||||
tf "hotfix no verifier" "$HOT" "No verifier is dispatched at hotfix weight"
|
||||
tf "hotfix skill has Agent" "$HSK" " - Agent"
|
||||
|
||||
echo ""
|
||||
echo "loops-light structure locks: $PASS pass, $FAIL fail"
|
||||
[ "$FAIL" -eq 0 ]
|
||||
@@ -0,0 +1,67 @@
|
||||
# Verify + secure loop — shared orchestrator include (feat, bugfix)
|
||||
|
||||
Runs in the ORCHESTRATOR MAIN LOOP after the dev step completes. Turns a
|
||||
finished diff into a verified, security-cleared change through two fresh
|
||||
gates and bounded loops. The dev stays inline (LRN-083: subagents =
|
||||
execution + report; loop decisions live here, in the main loop).
|
||||
|
||||
Inputs the caller must have ready:
|
||||
- `CONTRACT`: path to the contract file written by `contract-interview.md`.
|
||||
- `DIFF`: the range/file-list the dev just produced (e.g. `HEAD` vs the
|
||||
pre-dev SHA, or the working-tree diff before commit).
|
||||
- `TEST`: the project test command, if known.
|
||||
|
||||
Nominal path is cheap: one verifier dispatch + one security dispatch, done.
|
||||
The loop only costs more when it actually loops.
|
||||
|
||||
## GATE 1 — REQUEST CONFORMITY (fresh verifier)
|
||||
|
||||
Dispatch a FRESH verifier subagent (`subagent_type: verifier`, or load
|
||||
`agents/verifier.md`). Pass ONLY: the `CONTRACT` path, the `DIFF` range, the
|
||||
`TEST` command. Never pass the dev's summary, never pass a prior iteration's
|
||||
gaps — the verifier reads the contract from disk and judges blind.
|
||||
|
||||
Parse its single `VERIFY — VERDICT:` line:
|
||||
|
||||
- `CONFORME` → go to GATE 2. (First-pass conforme = no loop.)
|
||||
- `ECARTS(n)` → hand the dev the CONTRACT path + the exact `CRITERIA` gap
|
||||
lines (NOT-MET / out-of-scope), nothing else. Dev fixes inline, then
|
||||
re-dispatch a FRESH verifier. Repeat. **Max 3 conformity iterations** →
|
||||
STOP + human escalation with the CRITERIA table (the contract-vs-realized
|
||||
diff).
|
||||
- Remaining `UNVERIFIABLE` while all else MET → direct human gate (a dev
|
||||
cannot fix unverifiability); do not spend a loop on it.
|
||||
- Out-of-scope files: a dev justification is accepted ONLY through the human
|
||||
micro-gate that appends `[gated <date>]` to the contract's FILE SCOPE;
|
||||
otherwise the dev removes the file.
|
||||
- Structural failure (`ERROR(…)`, missing/duplicated VERDICT line,
|
||||
unparsable, crash, `CONFORME` without `PROOF`) → retry ONCE with a fresh
|
||||
verifier; a 2nd structural failure → human escalation. A mute verifier is
|
||||
NEVER a PASS.
|
||||
|
||||
## GATE 2 — SECURITY (fresh security-auditor)
|
||||
|
||||
Only after GATE 1 is `CONFORME`. Dispatch a FRESH security-auditor
|
||||
(`subagent_type: security-auditor`, or load `agents/security-auditor.md`)
|
||||
with `MODE: gate`, `SCOPE: <DIFF>`. No report path (gate mode is
|
||||
stdout-only, no Write).
|
||||
|
||||
Parse its single `SECURITY — VERDICT:` line:
|
||||
|
||||
- `PASS` → done, proceed to commit.
|
||||
- `BLOCK(n)` → hand the dev the `BLOCKING` list + the CONTRACT path. Dev
|
||||
fixes inline. Then **re-verify the REQUEST first** (GATE 1, fresh
|
||||
verifier) — a security fix can drift the behavior — **then re-run GATE 2**
|
||||
(fresh auditor), in that order. **Max 3 security iterations** → STOP +
|
||||
human escalation with the BLOCKING table.
|
||||
- `DEGRADED` (semgrep absent) → does NOT block on the tool's absence; surface
|
||||
the checklist result + recommend `make plugin`. A DEGRADED run that still
|
||||
BLOCKs (grep-caught secret/injection) blocks like any other.
|
||||
- Structural failure → retry ONCE fresh; 2nd → human escalation. A mute
|
||||
auditor is NEVER a PASS.
|
||||
|
||||
## Order invariant
|
||||
|
||||
REQUEST conformity is always re-checked BEFORE security on any re-loop — a
|
||||
security fix that breaks the feature must not slip through because only the
|
||||
security gate re-ran. Never the reverse order.
|
||||
Reference in New Issue
Block a user