Files
ersatztv/docs/decisions/archive/release-ci-governance.md
T
timothyandClaude Opus 4.8 b1e7e08884 feat(521): rework decisions guard — lifecycle validator + narrow [decisions-edit]; header + CI rewrite [decisions-edit]
Retire the line-level append-only mechanic (ersatztv#303 H9) in favor of the lifecycle validator
built in Tasks 1-5. .claude/hooks/decisions-guard.sh is now a thin fail-open shim around
scripts/decisions_validate.py; .husky/pre-commit calls it for the structural (working-tree) checks,
.husky/commit-msg drops the old staged/[decisions-edit]-deletion block and keeps only the
Co-Authored-By check. The Gitea decisions-guard job is renamed "decisions lifecycle" and now runs
decisions_validate.py --base/--head (structural + body-diff + no-vanish) and
build_decisions_catalog.py --check (active catalog drift), with actions/setup-python@v5 added since
the bare `small` lane doesn't guarantee python3; the old 1800-line consolidation-floor step is
removed (replaced by the validator's aggregate active-corpus budget). docs/decisions.md's header is
rewritten from append-only to lifecycle framing (metadata schema, statuses, generated catalog,
archive, same-PR supersession); [decisions-edit] is re-scoped (not removed) to rationale-prose
edits/factual corrections only. docs/ci-cd.md's release ritual and hook/job descriptions are
rewritten to match.

Also fixes a pre-existing validator false-positive surfaced while sanity-checking against
origin/main: Task 6's #303 H9/H3 split (commit d09be57e) renamed the archived record's heading
away from the pre-split original, which the validator's heading-based relocation check reads as
"removed without an archive copy." Restored the archived heading to match the original text
(functionally unchanged — still status: superseded, same key) and updated the two prose
cross-references (migration-map.md, release-ci-governance.md) that pointed at the old anchor.

PR1 scope only (per brief): does NOT wire the kickoff-guard CI step or touch the
kickoff/README/select-queue docs — that's Task 8/PR2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 03:00:15 +02:00

3.4 KiB

Archived — release/CI/merge-governance records superseded by the #521 lifecycle schema

Records relocated verbatim (rationale prose unchanged) from docs/decisions/release-ci-governance.md during the #521 decision-lifecycle migration. See docs/decisions/archive/README.md for what this directory is and docs/decisions/migration-map.md for the full mapping.

  • docs.append-only-guard (below) — the docs/decisions.md-is-append-only-by-construction half of #303 H9/H3. Superseded by docs.decision-lifecycle (active, in docs/decisions.md) — the lifecycle validator (scripts/decisions_validate.py) that this very PR introduces replaces line-level append-only enforcement with record-level lifecycle checks. The companion H3 root-screenshot guard was split out (#521) into its own still-active record, ci.root-screenshot-guard, in docs/decisions/release-ci-governance.md — it is not covered by this supersession and was never archived.

2026-07-12 — decisions.md is append-only, enforced; root-screenshot guard (#303 H9/H3)

key: docs.append-only-guard · 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.mddocs.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.