Appendix A - 6. Pre-commit hook (3-stanza, tree-sha markers)

The Structure of Pre-commit hook (3-stanza, tree-sha markers) — its shape at a glance:

Three stanzas fire in order at commit time, then a downstream merge-check reads the tree-keyed marker: a pass on this tree becomes an auditable fact, not a trust assumption.

flowchart LR
  Commit[/Commit attempt/] --> Lint[Changed-file lints]
  Lint --> Test[Unit-tier tests]
  Test --> Marker[(Write tree-sha marker)]
  Marker --> Check{merge-check: valid marker?}
  Check -->|yes| Advance([Advance to merge])
  Check -->|no| Reject([Reject commit])

Accessible description: a commit attempt runs changed-file lints, then unit-tier tests, then writes a marker keyed to the tree's sha; a downstream merge-check advances the commit only if a valid marker exists for its tree and rejects it otherwise.

Projected from the catalogue entry gates-and-merge-train / Pre-commit hook (3-stanza, tree-sha markers).

On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns

Intent

Intent — A three-stanza pre-commit hook that runs changed-file lints and unit-tier tests and writes tree-sha-keyed marker files, so an agent's commit cannot advance to merge unless the cheap checks actually passed on exactly this tree.

Motivation

Agents commit fast and often. Without a cheap gate at commit time, a broken change flows downstream to the expensive gates (merge-train, deploy) where the context of what the agent was doing is gone and the cost of diagnosis is highest. Worse, because the merge-train batches many agents' work, one un-checked broken commit can poison an entire batch. The failure recurs on every commit and compounds with fleet size: the later the break is found, the more work is entangled with it.

Applicability

Structure

The Structure diagram appears at the top of this page.

Sample Code

The gate keys its pass-marker to the tree sha, so a marker from one tree can never vouch for another. The downstream verifier re-derives the tree sha and refuses any commit lacking a matching marker — the early check becomes an independently auditable fact.

import hashlib, os, sys

MARKER_DIR = ".gate-markers"

def tree_sha(files: list[str]) -> str:
    h = hashlib.sha256()
    for f in sorted(files):
        h.update(f.encode()); h.update(open(f, "rb").read())
    return h.hexdigest()

def pre_commit(files: list[str], run_lints, run_unit_tests) -> int:
    if run_lints(files) or run_unit_tests():          # each returns findings; non-empty = fail
        return 1                                       # block the commit; no marker written
    os.makedirs(MARKER_DIR, exist_ok=True)
    open(os.path.join(MARKER_DIR, tree_sha(files)), "w").close()   # marker keyed to THIS tree
    return 0

def merge_check(files: list[str]) -> int:
    ok = os.path.exists(os.path.join(MARKER_DIR, tree_sha(files)))
    return 0 if ok else 1   # refuse to advance a commit whose tree was never gated

Consequences

Example use within DocAble

Contents
© James C. Davis, 2026–present