Forced mechanical quality gates for Claude Code agents — the Turnstile.
Agents do not end a turn with mechanically failing code. Every verdict is a deterministic CLI result (exit codes + JSON), never an LLM judgment.
L0 PostToolUse ≤150ms format-in-place · syntax floor · secret scan → auto-fix / feedback
L1 PostToolBatch ≤2s lint · typecheck · cycles on the batch → block error-severity
L2 Stop ≤30s scoped tests · dep audit · diff security → decision:"block" (TURNSTILE)
L3 /gate · CI minutes full suite · SBOM/license → same policy as CI
claude plugin marketplace add davila7/agentic-hooks # or your fork's path/URL
claude plugin install gates-essential@agentic-gates
# optional content packs (declarative tool-chain profiles):
claude plugin install gates-polyglot@agentic-gates
claude plugin install gates-security@agentic-gates
claude plugin install gates-strict@agentic-gatesThen in each project: /gate-init (creates .gate/ policy), /gate-doctor
(checks tool availability).
| Pack | Role | Enforcement hooks |
|---|---|---|
gates-essential |
Engine: dispatcher + all hooks + guard + ratchet + commands | yes (only here) |
gates-polyglot |
Python + TS/JS tool chains (ruff, ty, biome, oxlint, tsc) | registration only |
gates-security |
secrets, gitleaks, osv-scanner, semgrep/ast-grep profiles | registration only |
gates-strict |
scoped tests, dep audits, cycles, expired-waiver enforcement | registration only |
Enforcement hooks live in the engine pack alone — plugin hooks run in parallel
across packs, so two packs carrying a Stop handler would double-fire the turnstile.
- Mechanical only. Deterministic tool exit codes. LLM-as-judge is banned as a verdict.
- Missing tool ≠ pass. A tool that is not installed produces an explicit DEGRADED verdict.
- Guard-the-guard. The agent cannot edit policy, waivers, baseline, hooks or
settings —
PreToolUsedenies it,ConfigChangeblocks it, and CI re-checks it. - Ratchet.
.gate/baseline.jsonregisters existing debt once; only NEW findings block. Fingerprints are content-based, so line drift never looks like new debt. - Waivers expire. Every suppression has an owner and an expiry date; expired waivers re-open their findings.
- Style is silent, semantics are strict. Formatting is auto-fixed with zero feedback; only error-severity semantic findings block.
- Feedback is compact. Findings are capped (~20 / 8k chars) — the platform's
10k
additionalContextcap spills to a file with only a 2k preview. - The loop guard releases. After
loopguard.max_blocksidentical blocks the turn is released with asystemMessageto the human — never wedged against the platform's 8-continuation cap.
| Concern | Python | TS/JS |
|---|---|---|
| format | ruff format |
biome format → prettier |
| syntax | in-process ast.parse |
node --check (JS) |
| lint | ruff check |
oxlint → biome |
| types | ty → pyright → mypy |
tsc --noEmit |
| secrets | in-process pattern scan → gitleaks |
same |
Chains are declared in .gate/policy.toml with explicit fallback order and budgets.
python3 core/gate.py init # .gate/ skeleton
python3 core/gate.py doctor # tool availability / degraded chains / lock skew
python3 core/gate.py check # CI parity — exit 2 on blocking findings
python3 core/gate.py baseline # register current debt (ratchet)
python3 core/gate.py waive --rule ruff/E501 --path 'legacy/**' --owner you@example.com --expires 2026-12-31
python3 core/gate.py report # SARIF-lite dumpcore/gate/ dispatcher source of truth (stdlib-only, Python 3.11+)
packs/essential/ engine pack (vendored core/ + hooks + commands)
packs/{polyglot,security,strict}/ declarative content packs
templates/ CI / pre-commit / gitignore parity templates
tools/ vendor_core, validate_manifests, build_packs
tests/ unit + hook-contract integration + fixtures + spikes
PYTHONPATH=core python3 -m pytest tests/ -q
python3 tools/validate_manifests.py
python3 tools/vendor_core.py # before release
python3 tests/spikes/h2_latency/bench_l0.py
python3 tests/spikes/h4_ratchet/run.pySpike results live in tests/spikes/*/RESULTS.md (honest verdicts, including
partial falsifications — see H2's unchanged-hash budget).
v0.1.0 — engine, packs, guard, ratchet, turnstile, contracts all under test. H1/H3 spike runs (live agent sessions) are packaged as taskpacks ready to execute.