# 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-skills `constraint-driven-development` (floor guard) to this repo's gate model. Usage `bash ~/.claude/lib/floor-guard.sh [-- ...]` over `git diff ` (working tree included). Kinds: SUPPRESS (added checker silencing: `@ts-ignore`, `@ts-expect-error` without 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 (added `not implemented`, `NotImplementedError`, empty `catch` block, `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 carrying `floor-guard: allow ` is printed as WAIVED and not counted. Output: one `FLOOR : ` per finding, then `FLOOR GUARD: clean` (rc 0) or `FLOOR GUARD: finding(s), 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 suite `lib/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 table` so the guard stays clean on itself (this also exercises the waiver path for real). - Verifier step: `bash ~/.claude/lib/floor-guard.sh ` 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 test` exports GIT_CONFIG_GLOBAL=/dev/null); one RED fixture per kind, one WAIVED fixture, one CLEAN fixture, each printed as `PASS `; summary `PASS=n FAIL=m` like 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 1. 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 2. 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 3. 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… 4. 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 5. 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 1. Script: arg parsing (base, optional `--` pathspec), `git diff --unified=0 -- ` plus `git diff --diff-filter=D --name-only -- `, rc contract, header comment stating WHY (BDR-100 class: deterministic floor under an LLM gate). 2. 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. 3. Output lines + summary + rc. 4. Suite: helper `mk_repo` (git init -q, base commit with a test file holding 3 assertions, a `vitest.config.ts` with `lines: 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. 5. verifier.md step + verify-secure-loop.md paragraph + CHANGELOG; run criteria 1-5.