---
key: docs.append-only-guard
title: 2026-07-12 — decisions.md is append-only, enforced; root-screenshot guard (#303 H9/H3)
status: superseded
since: '2026-07-12'
supersedes: none
superseded-by: docs.decision-lifecycle@2026-07-21
rule: (superseded) `docs/decisions.md` is append-only, enforced by hook + CI.
signals: 'decisions-guard hook · paths: `.claude/hooks/decisions-guard.sh`, `.husky/commit-msg` · issues: #303 (H9)'
mechanics: superseded by `scripts/decisions_validate.py` (ersatztv#521); see `docs/decisions.md` → `docs.decision-lifecycle`
---

**This log is append-only by construction, not just by convention.** A commit or PR that deletes or
modifies an existing line of `docs/decisions.md` is blocked — by the Husky `commit-msg` hook
(`.claude/hooks/decisions-guard.sh staged`) locally and the blocking `decisions-guard` CI job (same
script, `range` mode) on PRs. Shared detection, deliberately different granularity: the Husky hook
gates **each commit** (its own message must carry the token); CI gates the **PR-wide** net diff
(token in any commit of the range suffices), so the local hook is the stricter primary gate and CI the
push/bypass backstop. Insertions anywhere are always allowed, so a normal new entry (TOC line
near the top + a block appended at the bottom, both pure insertions) passes untouched. Detection is
`git diff --numstat` deleted-count > 0, which is robust to markdown `-` list markers (a byte-level `-`
prefix would false-match). The block is lifted only by the literal **`[decisions-edit]`** token in the
commit message, reserved for two cases: fixing a factual error, and superseding a reversed decision
(add the new entry, prepend a `> **Superseded …**` banner to the old one, tag its Index line
`(superseded)` — keep the old rationale, never silently rewrite). **Consolidation** of superseded
entries is a release-checklist step (`docs/ci-cd.md` → Versioning & releases), backstopped by a
non-blocking 1800-line **size floor** in the `decisions-guard` job (the read-cost point past which the
log no longer fits one default agent Read), so append-only doesn't accrete contradictory *or
unreadably-large* history between releases (Timothy's call, 2026-07-12: mark-and-keep on reversal,
consolidate at each milestone, size-floor backstop).

The companion H3 root-screenshot guard was split out during the #521 migration to the active record
`ci.root-screenshot-guard` in `docs/decisions/release-ci-governance.md`.
