---
description: "git span drift as the CI enforcement backstop, the local pre-commit hook, and the optional merge driver."
---

# CI & git integration (/docs/ci)

## CI gate: the enforcement backstop

[Agent integration](/docs/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:

```bash
git span drift
```

`git 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:

```yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0 # spans compare against real git history
- run: npm install -g git-span
- run: git span drift
```

For any other CI system:

```bash
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`:

```bash
git span drift --no-exit-code
```

> [!WARNING]
> `--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`:

```bash
git span drift --format json
git span drift --format porcelain
```

> [!NOTE]
> `--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](/docs/commands) for the full set of `drift` flags, and [Concepts](/docs/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:

```bash
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`:

```bash
#!/bin/sh
git span drift
```

```bash
chmod +x .git/hooks/pre-commit
```

With 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:

```txt
.span/** merge=span
```

And the driver in `.git/config`:

```ini
[merge "span"]
	name = git-span structural span merge driver
	driver = git span merge-driver %O %A %B %L
```

The 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](/docs/guides/reconcile-drifted-spans) for what to do with a span left in that state.
