CI & git integration
CI gate: the enforcement backstop
Agent integration covers the in-session touch hook and commit advisor, but those only run inside a hooked agent session — a human-authored commit, a session with hooks disabled, or (on Codex) a permissionDecision: 'deny' that doesn't actually block can all still land span debt. git span drift's default exit code is the backstop, checked before merge:
git span driftgit span drift exits 1 when it finds drift and 0 on a clean scan, so it drops into any CI pipeline as a plain step:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # spans compare against real git history
- run: npm install -g git-span
- run: git span driftFor any other CI system:
git span drift || { echo "drifted spans found — run 'git span drift' locally to see them"; exit 1; }For a report-only job that should never fail the build, add --no-exit-code:
git span drift --no-exit-code--no-exit-code only suppresses the exit code for ordinary drift. An interior-anchor integrity violation still exits 1 even with --no-exit-code set.
For machine consumption, use --format json or --format porcelain:
git span drift --format json
git span drift --format porcelain--format json always emits a document — a clean scan carries "clean": true (schema_version 3) rather than empty stdout, so a parser can rely on an envelope on every run and read clean for the verdict; informational RESOLVED_PENDING_COMMIT findings may still be listed, so clean does not imply findings: []. --format porcelain still prints nothing on a clean scan (no header row); empty stdout plus exit 0 is the clean signal there. The human format (the default) prints a summary line on a clean scan.
See Command reference for the full set of drift flags, and Concepts for what counts as drift.
Wiring into a validate script
Add git span drift as its own step alongside lint/typecheck/test, run after them — span debt is a documentation-coupling concern, not a correctness one, so it should never mask an earlier, more actionable failure:
echo "Checking spans..."
git span drift || { echo "git span drift found drifted anchors — see above"; exit 1; }Local pre-commit hook
For a workflow without agent hooks at all (a human editor, or a harness git-span doesn't yet plug into), add a plain git span drift call to .git/hooks/pre-commit:
#!/bin/sh
git span driftchmod +x .git/hooks/pre-commitWith no drift, the hook exits 0 and the commit proceeds. With drift, git span drift exits 1, which fails the hook and blocks the commit — fail-closed by default. A developer can still bypass it with git commit --no-verify, which is standard git behavior for any hook, not something git-span adds or removes.
Merge driver
Registering git span merge-driver lets .span/ files merge structurally instead of falling back to a textual diff3 merge. Add the attribute:
.span/** merge=spanAnd the driver in .git/config:
[merge "span"]
name = git-span structural span merge driver
driver = git span merge-driver %O %A %B %LThe driver resolves whatever is structurally derivable from the three sides without trusting the worktree. Genuine same-anchor divergence — both sides changed the same anchor's range or hash, or the why text diverged with no common merge base — it can't resolve on its own; it writes minimal conflict markers into the span file and exits non-zero so that a later git span drift --fix can finish the resolution. Anything still unresolved surfaces in git span drift with a — conflict suffix in human output (CONFLICT in the machine formats).
See the reconcile drifted spans guide for what to do with a span left in that state.