5.9 KiB
CONTRACT — floor-guard
- date: 2026-09-27 | flow: feat (ad-hoc dispatch, /feat gates replayed by the orchestrator) | branch: feature/agent-skills-borrow
- status: active
REQUEST (verbatim — IMMUTABLE)
Build
lib/floor-guard.sh, a diff-scoped deterministic detector of a quietly weakened quality bar, adapted from addyosmani/agent-skillsconstraint-driven-development(floor guard) to this repo's gate model. Usagebash ~/.claude/lib/floor-guard.sh <base-ref> [-- <pathspec>...]overgit diff <base-ref>(working tree included). Kinds: SUPPRESS (added checker silencing:@ts-ignore,@ts-expect-errorwithout a trailing reason,eslint-disable*,# noqa,# type: ignore,nosemgrep,nosec,shellcheck disable), SKIP (added.skip(,.only(,xit(,xdescribe(,fit(,fdescribe(,it.todo(,@pytest.mark.skip,@unittest.skip,t.Skip(in test files), DELETED_TEST (deleted file whose path matches a test pattern), ASSERT_DROP (a test file whose assertion-line count decreases:expect(,assert,should,.toBe), STUB (addednot implemented,NotImplementedError, emptycatchblock,except: pass), THRESHOLD_DOWN (a numeric value decreased on the same key in coverage/quality config files:jest.config*,vitest.config*,.nycrc*,codecov*,sonar-project.properties,lighthouserc*,CONSTRAINTS.md). Waiver: an added line carryingfloor-guard: allow <reason>is printed as WAIVED and not counted. Output: oneFLOOR <KIND> <file>:<line> <snippet>per finding, thenFLOOR GUARD: clean(rc 0) orFLOOR GUARD: <n> finding(s), <m> waived(rc 2); rc 3 on usage error. Wire it as a mandatory verifier step (agents/verifier.md) and document it in lib/verify-secure-loop.md GATE 1; hermetic suitelib/tests/floor-guard.test.sh. User go 2026-09-27 ("ok pour les 4", case 2 item 2).
CLARIFICATIONS
- Language: bash entry point; the diff parsing may live in an embedded python3 heredoc (precedent
lib/tests/run-review-guards.sh). Functions <= 25 logic lines, 80-char lines. - File classes: test file = path contains
test,spec,__tests__, or matches*.test.*,*.spec.*,*_test.go,*_test.py,test_*.py; config file = the names listed under THRESHOLD_DOWN. SKIP and ASSERT_DROP apply to test files only; SUPPRESS and STUB to any file; THRESHOLD_DOWN to config files only. - The guard's own pattern table contains the trigger strings: those source lines carry
# floor-guard: allow pattern tableso the guard stays clean on itself (this also exercises the waiver path for real). - Verifier step:
bash ~/.claude/lib/floor-guard.sh <base>where base = the branch's gitflow base (develop; main for hotfix/release); rc 2 → verdict ECARTS listing each FLOOR line, unless the contract's CLARIFICATIONS explicitly authorize that exact weakening (quote it in the verdict). - Suite: throwaway repos (
make testexports GIT_CONFIG_GLOBAL=/dev/null); one RED fixture per kind, one WAIVED fixture, one CLEAN fixture, each printed asPASS <KIND>; summaryPASS=n FAIL=mlike the other suites. - Self-detection: the suite file itself contains the trigger strings; the self-run criterion excludes it by pathspec.
- Out of scope: a pre-commit hook, running it on the repo's own diff in
make test, parsers beyond the regexes above.
ACCEPTANCE CRITERIA
- Suite green, every kind flip-tested. CHECK: out=$(make test suite=lib/tests/floor-guard.test.sh 2>&1); for k in SUPPRESS SKIP DELETED_TEST ASSERT_DROP STUB THRESHOLD_DOWN WAIVED CLEAN; do echo "$out" | grep -q "PASS $k" || { echo "missing PASS $k"; echo "$out" | tail -15; exit 1; }; done; echo "$out" | grep -qE "FAIL=[1-9]" && exit 1; echo KINDS_GREEN EXPECT: KINDS_GREEN EVIDENCE: MET exit=0 marker-found :: KINDS_GREEN
- Usage error is rc 3. CHECK: bash lib/floor-guard.sh >/dev/null 2>&1; [ $? -eq 3 ] && echo RC_USAGE EXPECT: RC_USAGE EVIDENCE: MET exit=0 marker-found :: RC_USAGE
- Self-run clean on this branch (suite file excluded by pathspec), waivers visible.
CHECK: out=
(bash lib/floor-guard.sh develop -- . ':!lib/tests/floor-guard.test.sh' 2>&1); rc=?; echo "$out" | tail -3; [ $rc -eq 0 ] && echo "$out" | grep -q WAIVED && echo SELF_CLEAN EXPECT: SELF_CLEAN EVIDENCE: MET exit=0 marker-found :: WAIVED STUB lib/floor-guard.sh:105 'not implemented', # floor-guard: allow pattern table WAIVED STUB lib/floor-guard.sh:106 'NotImplementedE… - Verifier wired, loop documented. CHECK: grep -q "floor-guard.sh" agents/verifier.md && grep -q "floor-guard" lib/verify-secure-loop.md && echo WIRED EXPECT: WIRED EVIDENCE: MET exit=0 marker-found :: WIRED
- shellcheck and doctrine-citers clean. CHECK: shellcheck lib/floor-guard.sh lib/tests/floor-guard.test.sh && out=$(make test suite=lib/tests/doctrine-citers.test.sh 2>&1) && ! echo "$out" | grep -qE "FAIL=[1-9]" && echo LINT_OK EXPECT: LINT_OK EVIDENCE: MET exit=0 marker-found :: LINT_OK
FILE SCOPE
- lib/floor-guard.sh (new), lib/tests/floor-guard.test.sh (new)
- agents/verifier.md (one mandatory step), lib/verify-secure-loop.md (one paragraph under GATE 1)
- CHANGELOG.md (Unreleased entry)
PLAN
- Script: arg parsing (base, optional
--pathspec),git diff --unified=0 <base> -- <pathspec>plusgit diff --diff-filter=D --name-only <base> -- <pathspec>, rc contract, header comment stating WHY (BDR-100 class: deterministic floor under an LLM gate). - Python parser on stdin: walk hunks, classify added lines by kind and file class, count assertion lines removed vs added per test file, compare numeric values on identical keys in config files (
key: 80→key: 60, JSON or YAML-ish), detect waivers. - Output lines + summary + rc.
- Suite: helper
mk_repo(git init -q, base commit with a test file holding 3 assertions, avitest.config.tswithlines: 80, a source file), one fixture per kind → assert rc 2 and the FLOOR line; WAIVED → rc 0 and a WAIVED line; CLEAN → rc 0. - verifier.md step + verify-secure-loop.md paragraph + CHANGELOG; run criteria 1-5.