From c91582c68872342f093043064fdea98910fd6be7 Mon Sep 17 00:00:00 2001 From: Jonny Spicer Date: Tue, 18 Aug 2026 20:49:36 +0000 Subject: [PATCH] docs: record the tag-immutability decision and scope the autoupdate advice (86cb4jjbv) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nine repos pin this repository's hooks by tag, and a git tag is mutable, so `hooks-v1.0` could be force-moved and would reach every consumer on its next cache-cold CI run with no diff anywhere to show for it. Fixed at source with an `Immutable hook version tags` ruleset covering refs/tags/hooks-v* and refs/tags/dead-code-v*: no force-push, no deletion, no bypass actors. Verified by pushing a throwaway tag matching the pattern and confirming both a force-move and a delete are rejected. Creating new tags is unaffected. Pinning consumers by commit SHA was the other option and is rejected here: `pre-commit autoupdate` rewrites SHA pins back to tags, so the protection would disappear the first time anyone ran the bump command this README recommends. The ruleset needs no change in any consumer and cannot be undone by one. Also scopes that bump command with --repo. A bare autoupdate updates every repo in a consumer's config, so bumping the ops hook in the six consumers that run ruff through pre-commit would silently bump ruff too — turning a deliberate version pin into a side effect of an unrelated change. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4995514..3e600f5 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,46 @@ repository at one `rev` — two hooks cannot be versioned independently from a single repo. The older `dead-code-v1.0` and `dead-code-v1.1` tags remain valid for consumers that have not moved. -To change a hook: edit it here, run both suites in `tests/`, merge, tag, then -bump `rev` in the consumers (`pre-commit autoupdate` does the bump for you). +### Tags here are immutable, and that is enforced + +A git tag is mutable by default: `git push --force` moves it, and consumers +that pinned it fetch whatever it points at *now*. CI runners are always +cache-cold, so a moved tag would reach every consumer on its next run with no +diff in any consuming repo to show for it. Nine repos pin `hooks-v1.0`. + +The `Immutable hook version tags` ruleset closes that off at source. It targets +`refs/tags/hooks-v*` and `refs/tags/dead-code-v*`, and blocks force-pushes and +deletions with no bypass actors — **not even admins**, deliberately. Creating a +new tag is unaffected, so cutting `hooks-v1.1` works exactly as before. + +The trade is that a tag pushed to the wrong commit cannot be quietly corrected. +Cut a new tag instead; that is the honest fix anyway, since anyone who already +consumed the bad one would never see a correction. If a tag genuinely must be +removed, add it to the ruleset's `exclude` list, delete it, and take it back +out — which leaves an audit trail, unlike a force-push. + +Consumers therefore do not need to pin by commit SHA. That was considered and +rejected: `pre-commit autoupdate` silently rewrites SHA pins back to tags, so +the protection would evaporate the first time anyone ran the documented bump +command. See ClickUp 86cb4jjbv for the full decision. + +### Changing a hook + +Edit it here, run both suites in `tests/`, merge, tag, then bump `rev` in the +consumers: + + pre-commit autoupdate --repo https://github.com/offworldlabs/ops + +**Use `--repo`.** A bare `pre-commit autoupdate` updates *every* repo in the +consumer's config. In the six consumers that also run ruff through pre-commit +(everything except node-infra, retina-gui and retina-node, which pip-install +ruff in CI instead), that silently bumps `astral-sh/ruff-pre-commit` as a side +effect of bumping the ops hook. Those repos' CI runs `pre-commit run +--all-files`, so the new ruff takes effect in CI on the same commit — the exact +"an unpinned linter picks up new rules on release and turns a green branch red +without anything in this repo changing" outcome their neighbouring +`# Pinned deliberately` comments exist to prevent. Bump ruff on purpose, in its +own commit, or not at all. The "Adding a script" conventions below are about scheduled chores on the VPS and do not apply to hooks — a hook takes no env config and nothing schedules it.