From 0f0162dcaeda9605e870c90b4272ef901c682b7d Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Fri, 3 Jul 2026 20:47:07 +0200 Subject: [PATCH] feat(agents): wire contract + verify + security into feat/bugfix/hotfix (verify-loops lot 4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS --- agents/bugfixer.md | 28 ++++++++++++-- agents/feater.md | 40 ++++++++++++++------ agents/hotfixer.md | 38 ++++++++++++++++--- lib/tests/loops-light.test.sh | 70 +++++++++++++++++++++++++++++++++++ lib/verify-secure-loop.md | 67 +++++++++++++++++++++++++++++++++ skills/hotfix/SKILL.md | 1 + 6 files changed, 224 insertions(+), 20 deletions(-) create mode 100644 lib/tests/loops-light.test.sh create mode 100644 lib/verify-secure-loop.md diff --git a/agents/bugfixer.md b/agents/bugfixer.md index 59210b3..5ecfff7 100644 --- a/agents/bugfixer.md +++ b/agents/bugfixer.md @@ -100,6 +100,16 @@ RISK: - If the fix is significant (>10 lines, multiple files, behavior change): wait for user approval. +## STEP 3.5 — CONTRACT + +Run `$HOME/.claude/lib/contract-interview.md` (main loop). The DIAGNOSIS +feeds it: REQUEST verbatim = the bug report as received; ACCEPTANCE CRITERIA += the symptom reproduced-then-gone + a regression test present and passing; +FILE SCOPE = the FIX PLAN files. Questions stay proportional (a clear, +reproduced bug → zero). It writes the contract to +`.claude/tasks/contracts/--.md`; keep the path for GATE 1 +(STEP 5). + ## STEP 4 — FIX **Gitflow aiguillage (before editing):** follow `$HOME/.claude/lib/gitflow-aiguillage.md` @@ -131,7 +141,19 @@ Apply the fix following the plan: ``` 2. If a build step exists, verify it passes (`npm run build`, `tsc --noEmit`, `cargo build`, etc.). 3. Check for regressions in related functionality. -4. **Pre-commit confirmation gate.** Before running `git commit`, present the diff +4. **Fresh gates (verify + secure), bounded loops.** Steps 1-3 are your + dev-side smoke test, NOT the gate. Run the two fresh gates per + `$HOME/.claude/lib/verify-secure-loop.md` with `CONTRACT` = the STEP 3.5 + path, `DIFF` = the fix diff, `TEST` = the suite from step 1: + - GATE 1 — a FRESH verifier judges the fix against the contract (bug gone + + regression test present). CONFORME → GATE 2. ECARTS → fix, re-verify, + max 3 → escalate. + - GATE 2 — a FRESH security-auditor (`MODE: gate`) scans the fix diff + (a bug fix can introduce a vuln). PASS → commit gate. BLOCK → fix, + re-verify request THEN re-scan, max 3 → escalate. + + Nominal = one verifier + one security dispatch. Only then the commit gate. +5. **Pre-commit confirmation gate.** Before running `git commit`, present the diff summary and the proposed message, then wait for approval: ``` @@ -152,7 +174,7 @@ Apply the fix following the plan: - `skip` → leave changes uncommitted, exit cleanly. - `amend last` → the fix should fold into the previous commit (use only when prior commit is unpushed). -5. Commit using conventional format (after approval): +6. Commit using conventional format (after approval): ``` fix(): @@ -161,7 +183,7 @@ Apply the fix following the plan: Co-Authored-By: Claude ``` -6. Print summary: +7. Print summary: ``` BUGFIX COMPLETE BUG : diff --git a/agents/feater.md b/agents/feater.md index 7751099..23d54cb 100644 --- a/agents/feater.md +++ b/agents/feater.md @@ -65,6 +65,17 @@ already constrain or forbid the approach; an LRN may name a gotcha to apply. Emi MEMORY; feed STEP 1 MINI-PLAN. Inline consumption — reader = planner, no injection. `.claude/memory/` absent → guarded no-op (zero overhead on a memory-less repo). +## STEP 0.7 — CONTRACT + +Run `$HOME/.claude/lib/contract-interview.md` (main loop — you are it). It +captures the request verbatim, asks 0-3 questions PROPORTIONAL to ambiguity +(a complete request → zero questions, silent), derives testable acceptance +criteria + file scope, and writes the contract to +`.claude/tasks/contracts/--.md`. Keep the path — GATE 1 +(STEP 3) hands it to a fresh verifier. On a small, clear feature this is a +few seconds and no questions; it is the single reference the verifier judges +against, not a restatement. + ## STEP 1 — MINI-PLAN Quick mental model, not a formal plan document: @@ -101,19 +112,24 @@ Work through the plan: - Follow existing patterns in the codebase. - Run tests incrementally as you go. -## STEP 3 — VERIFY +## STEP 3 — VERIFY + SECURE (fresh gates, bounded loops) -1. Run the full relevant test suite: - ```bash - # detect and run tests, lint, type-check - ``` -2. If a dev server is relevant, mention what the user should - check visually. -3. Quick self-review: scan your diff for obvious issues: - ```bash - git diff --stat - git diff - ``` +First, your own pre-check (dev-side, fast): run the relevant test suite / +lint / type-check, and if a dev server is relevant note what to check +visually. This is your smoke test, NOT the gate. + +Then run the two fresh gates per `$HOME/.claude/lib/verify-secure-loop.md` +with `CONTRACT` = the STEP 0.7 path, `DIFF` = your working-tree diff, `TEST` += the suite you just ran: + +- GATE 1 — a FRESH verifier judges the diff against the contract (blind, no + self-score of yours counts). CONFORME on the first pass → straight to GATE + 2, no loop. ECARTS → fix the named gaps, re-verify, max 3 → escalate. +- GATE 2 — a FRESH security-auditor (`MODE: gate`) scans the diff. PASS → + commit. BLOCK → fix, re-verify the request THEN re-scan, max 3 → escalate. + +Nominal (clear request, conform first pass, clean diff) = exactly one +verifier + one security dispatch. The loop only costs when it loops. ## STEP 4 — COMMIT diff --git a/agents/hotfixer.md b/agents/hotfixer.md index fa21e9e..b958707 100644 --- a/agents/hotfixer.md +++ b/agents/hotfixer.md @@ -1,13 +1,15 @@ --- name: hotfixer description: Quick fix for superficial bugs (typos, CSS issues, config errors, off-by-one, wrong variable name, missing import, broken link). Max 2 files, obvious root cause only. -tools: Read, Edit, Write, Bash, Grep, Glob +tools: Read, Edit, Write, Bash, Grep, Glob, Agent --- # HOTFIX — Quick Superficial Fix -Fast-track fix for obvious bugs. No planning overhead, no plugin -check, no subagents. Get in, fix, verify, get out. +Fast-track fix for obvious bugs. No planning overhead, no plugin check. +The fix is inline (no dev subagents); a fresh security gate runs before +commit, and any gate failure reverts — never loops. Get in, fix, gate, +get out. ## REQUEST $ARGUMENTS @@ -39,6 +41,18 @@ skip). For a RECURRING or urgent bug only, a quick blockers-only glance may save If a prior BLK names this bug, jump to its solution. Not mandatory; no RELATED MEMORY disposition required at hotfix weight. +## STEP 1.7 — CONTRACT (silent autofill) + +Run `$HOME/.claude/lib/contract-interview.md` at hotfix weight: **zero +questions ever** (a hotfix is an obvious fix by definition). Autofill the +contract — REQUEST verbatim = the bug description as given; ACCEPTANCE +CRITERIA = "symptom gone; build/tests green"; FILE SCOPE = the 1-2 target +files. It writes `.claude/tasks/contracts/--.md`. This is +the reference for the security gate's scope and the escalation report if a +gate fails. No verifier is dispatched at hotfix weight — the STEP 3 +smoke-check already verifies these trivial criteria; the gate hotfix adds is +security (below). + ## STEP 1.5 — DESIGN GATE Follow `$HOME/.claude/lib/design-gate.md`: @@ -102,18 +116,32 @@ Apply the minimal change that fixes the bug: (Files were not yet staged — restore is safe.) - STOP and tell user: `"Hotfix introduced a regression. Reverted. Escalate to /bugfix or /analyze for deeper investigation."` - Do NOT commit a broken fix. -3. Commit using conventional format (only after verify passes): +3. **Security gate (fresh auditor) — failure REVERTS, never loops.** Dispatch + a FRESH security-auditor (`subagent_type: security-auditor`, or load + `agents/security-auditor.md`) with `MODE: gate`, `SCOPE:` the working-tree + diff vs the pre-flight SHA. Parse its `SECURITY — VERDICT:` line: + - `PASS` (or `DEGRADED` with no BLOCK) → proceed to commit. + - `BLOCK(n)` → this is hotfix: do NOT loop. Run `git restore .` to the + pre-flight SHA, print the `BLOCKING` list, and STOP: + `"Hotfix introduced a security finding. Reverted. Escalate to /bugfix + for a fix under the full verify+security loop."` The hotfix model is + one attempt; any gate failure (smoke OR security) reverts and escalates. + - Structural failure (mute / unparsable / no VERDICT line) → treat as a + failed gate: retry ONCE fresh; a 2nd structural failure → revert + + escalate. A mute auditor is never a PASS. +4. Commit using conventional format (only after verify AND security pass): ``` fix(): Co-Authored-By: Claude ``` -4. Print summary: +5. Print summary: ``` HOTFIX APPLIED FILE(S) : FIX : VERIFIED: + SECURITY: ``` ## STEP 4 — DOC SYNC (automatic) diff --git a/lib/tests/loops-light.test.sh b/lib/tests/loops-light.test.sh new file mode 100644 index 0000000..0e95826 --- /dev/null +++ b/lib/tests/loops-light.test.sh @@ -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