ADR-021: Halt-On-Violation Quality Gate
Status
Section titled “Status”Accepted
2026-06-11
Context
Section titled “Context”“Run the tests before you call it done” is only meaningful if there is one command that defines “done” and it fails loudly. Previously the repo had separate task test, task lint, task test:binary-size, and task scan:vuln with no single gate, and no rule binding agents to run them. Agents would declare work complete on a green happy-path build while lint, race, budget, or vulnerability checks were never run.
Decision
Section titled “Decision”Define a single halt-on-violation gate, task ci, that an agent must run before claiming any change complete (Constitution rule 9). It chains:
fmt:check → lint → go test -race -covermode=atomic ./... → agents:check → versions:check → check:adr → check:generated → test:binary-size → test:asset-budgets → scan:vuln(Chain as of 2026-08-19 — versions:check (ADR-030), check:adr (ADR-033) and check:generated (#90, warn-only regeneration-drift check for ADR-003/ADR-017) joined the gate after acceptance; Taskfile.yml is authoritative.)
If it exits non-zero, the agent halts and fixes the failure. It must not lower a threshold, exclude files, or bypass git hooks with --no-verify.
2026-07 Amendment: the GitHub Actions workflow originally re-implemented these checks as parallel jobs, and they drifted (fmt:check ran locally but never in CI). The workflow now installs the environment (Postgres, toolchain, Task) and invokes task ci itself — the same single-source-of-truth fix ADR-022 applied to AGENTS.md. If the gate needs a new check, add it to the ci task; the workflow inherits it.
Consequences
Section titled “Consequences”- “Done” has one definition, locally and in CI.
- Formatting, race conditions, binary-size budget, agent-spine drift, and known vulnerabilities are all caught before review.
- Slightly slower inner loop — mitigated by running
task test/task lintindividually during iteration and reservingtask cifor the final gate.
Alternatives Considered
Section titled “Alternatives Considered”- Rely on CI only. Rejected — pushes failures to the slowest feedback point and lets agents claim completion prematurely.
- A pre-push hook running everything. Complementary, not a replacement — the constitution rule makes the agent run it before claiming done, not just before pushing.
References
Section titled “References”Enforcement
Section titled “Enforcement”- Testable consequences:
- TC-1:
task cichains the full gate (fmt, lint, race+coverage tests, agents:check, versions:check, check:adr, binary/asset budgets, vuln scan). - TC-2:
.github/workflows/ci.ymlinvokestask cirather than re-implementing its steps.
- TC-1:
- Checks:
- TC-1 → the Taskfile definition itself; any removal is a public diff (status: block, pre-existing)
- TC-2 →
adr021-ci-invokes-gateinscripts/adrcheck(status: block, promoted 2026-09-30)
- Not machine-checkable: That an agent actually ran the gate before claiming done — approximated by the Stop-gate hook (ADR-033), which runs tests and the check suite when an agent tries to finish.
- Graduation log:
- 2026-09-30 — promoted warn → block (ADR-033 §4): clean since 2026-07-12 (80 days, zero false positives; the rule asks for 7).
checks/enforcement.config.jsoncarriesgraduated: 2026-09-30. Demotion back to warn is allowed with the same trail.
- 2026-09-30 — promoted warn → block (ADR-033 §4): clean since 2026-07-12 (80 days, zero false positives; the rule asks for 7).