commit-check — link every governed change to its decision
decree commit-check gates the commit→decision link itself. git guarantees what
changed; the Implements:/Refs:/Fixes: trailer that says which decision a change
implements is an unenforced convention. commit-check measures whether that link is
present — and lets CI require it.
Scenario A change to src/auth/tokens.py (governed by an in-flight SPEC) is about to merge.
Without decree
The commit lands with no trailer. git records the diff, but nothing links it to
the decision it implements. The “why” is lost — decree refs shows the SPEC with
zero implementing commits, and health can’t even flag it (it’s “unobserved, not
dead”). The provenance silently rots.
With decree
CI runs decree commit-check --diff-base origin/main --strict on the net diff and
fails when a governed change has no matching trailer:
$ decree commit-check --diff-base HEAD~1 --strict[commit-check] exit 1: trailer coverage below threshold.Commit check — diff-baseTrailer coverage (0/1, 0%)
Uncovered (1): • src/auth/tokens.py → SPEC-00000000000000000000000001 (JWT token storage) Consider `decree commit --implements SPEC-00000000000000000000000001` so the commit links to the SPEC.→ exit 1VALUE The weakest link in decree’s provenance — the commit→decision trailer — becomes a check CI can require, at the one moment the decision is knowable.
HONESTY It is coverage you can gate, not a guarantee: git commit --no-verify and CI overrides exist, so it measures and enforces where you run it — it cannot make the link true. It reads only declared governs:; a ticket is not a decision and decree never maps one.
How it works
Section titled “How it works”commit-check intersects a diff’s changed paths with the declared governs: map (via
why, the authoritative layer — never git history as truth), keeping only in-flight
decisions. For each governed change it checks whether the commit message(s) carry a
matching Implements:/Refs:/Fixes: trailer. The primary mode is --diff-base origin/main:
it gathers trailers across the whole commit range, so it survives squash-merge. It is
advisory by default — --strict (require 100%) or --min-coverage N (ratchet up over
time on a legacy repo) turn an uncovered change into a finding.
Run it
Section titled “Run it”Under --strict, a governed change with no linking trailer fails:
$ decree commit-check --diff-base HEAD~1 --strict[commit-check] exit 1: trailer coverage below threshold.Commit check — diff-baseTrailer coverage (0/1, 0%)
Uncovered (1): • src/auth/tokens.py → SPEC-00000000000000000000000001 (JWT token storage) Consider `decree commit --implements SPEC-00000000000000000000000001` so the commit links to the SPEC.→ exit 1Commit through decree commit (or add the trailer) and it passes:
$ decree commit-check --diff-base HEAD~1 --strictCommit check — diff-baseTrailer coverage (1/1, 100%)→ exit 0The same check is exposed over MCP as the commit_check tool, with the identical JSON
payload, so an agent gates its own loop.
When you’d reach for this
Section titled “When you’d reach for this”- As a CI gate on every PR — require that governed changes name the decision they implement (
--strict), so provenance can’t silently rot. - In an agent loop — the actor most likely to drop a trailer; gate it deterministically.
- Adopting on a legacy repo — start with
--min-coverageat today’s number and ratchet up, no flag day.
Flags & exit codes
Section titled “Flags & exit codes”Advisory by default: it exits exit 0 clean · advisory-only and reports unless you opt into a
gate. With --strict or --min-coverage, an uncovered governed change exits
exit 1 a finding you can gate CI on . A config error (missing index, no trailer source) exits
exit 2 config error .
All flags
| Flag | What it does |
|------|--------------|
| --diff-base REF | CI mode: paths from git diff REF...HEAD, trailers unioned across the commit range (squash-safe). |
| --diff PATH | A unified diff file (- = stdin); pair with --message. |
| --message PATH | The candidate commit message (for a commit-msg hook). |
| --strict | Require 100% coverage; any uncovered governed change exits 1. |
| --min-coverage N | Require ≥ N% coverage (ratchet for gradual adoption). |
| --json | Stable machine payload (same as the MCP tool). |
| --project PATH | Operate on the project at this path. |
decree does not install a git hook (that’s the harness’s job). To enforce locally, opt
in with a one-line commit-msg hook calling decree commit-check --message "$1" --strict.
Next: health — has a decision’s declared scope rotted over
time? Or see how decree relates to git and session-capture tools.