diff --git a/docs/loop/LOOP-RULES.md b/docs/loop/LOOP-RULES.md index 105a6e5f7d..061121e903 100644 --- a/docs/loop/LOOP-RULES.md +++ b/docs/loop/LOOP-RULES.md @@ -232,6 +232,16 @@ in `f5be7dc1c` (#2298) precisely because one file per PR is what stops every concurrent PR colliding on its first line, and this rule went on naming it for sixteen days. +`docs/NOW.md` is now ENFORCED, not merely labelled: a range that edits it is +refused unless a commit in it carries + + Archive-Repair: + +Repairing the archive is legitimate -- one of the two post-freeze edits is a +repair of destroyed bodies -- and no textual rule separates a repair from a new +entry, since the repair adds headings too. So the exception is DECLARED rather +than detected. The gate does not judge the reason; it requires one to exist. + Reference the issue in the PR body. `Refs #N` DOES satisfy `check-linked-issue`: the matcher is `(Closes?|Fixes?|Resolves?|Refs?|Updates?)\s*#[0-9]+` at `.github/workflows/issue-gate.yml:69`, and seven readers in this tree carry that diff --git a/docs/loop/LOOP-RULES.sha256 b/docs/loop/LOOP-RULES.sha256 index 34798256a0..14f7a65167 100644 --- a/docs/loop/LOOP-RULES.sha256 +++ b/docs/loop/LOOP-RULES.sha256 @@ -1 +1 @@ -f4c3c52eb779142edd011d3ce39faf71f12a4a69a677ecb5b42ca8b8e7b6fbd9 docs/loop/LOOP-RULES.md +6d68cc1592b9078f888081b8d8cb8bc0f4ea1b9ce1705d38adbff8e130c6a466 docs/loop/LOOP-RULES.md diff --git a/docs/now/2026-09-07-a-frozen-file-that-nothing-refused-to-open.md b/docs/now/2026-09-07-a-frozen-file-that-nothing-refused-to-open.md new file mode 100644 index 0000000000..42016c54d5 --- /dev/null +++ b/docs/now/2026-09-07-a-frozen-file-that-nothing-refused-to-open.md @@ -0,0 +1,8 @@ +# NOW -- A frozen file that nothing refused to open (2026-09-07) + +## A frozen file that nothing refused to open (Closes #3368) + +- `docs/NOW.md` says FROZEN ARCHIVE on line 1, and every mention of it under `.github/workflows/`, `scripts/` and `.githooks/` was a comment -- measured, zero lines rejected an edit. An author following a stale instruction could reopen it and pass all four required checks. +- Both detection rules were measured and BOTH fail: 'refuse any diff touching it' blocks the one legitimate repair in the population, and so does 'refuse an added `## ` heading' -- that repair adds three, because it restored headings whose bodies were destroyed. +- So the exception is DECLARED rather than detected: `Archive-Repair: ` in the commit message, the same shape as `# tri:no-dispatch` and `# tri:cause-removed`. The gate does not judge the reason; it requires one to exist. +- Six controls on a scratch repository -- PR and push, with and without the trailer, a bare trailer, and an untouched file. Historical control: `458ec0bd6` carries no trailer and would be refused, correctly. Blast radius: 2 commits in 600. diff --git a/scripts/ci/now-sync-gate-diff.sh b/scripts/ci/now-sync-gate-diff.sh index 675c4c38b4..30d06c1a76 100755 --- a/scripts/ci/now-sync-gate-diff.sh +++ b/scripts/ci/now-sync-gate-diff.sh @@ -79,21 +79,79 @@ if [ "$event" = "pull_request" ]; then require_rev "$BASE" PR_BASE_SHA require_rev "$HEAD" PR_HEAD_SHA ADDED=$(git diff --diff-filter=A --name-only "$BASE" "$HEAD" | grep -E "$ENTRY_RE" || true) + RANGE_FROM="$BASE"; RANGE_TO="$HEAD" elif [ "$event" = "push" ]; then BEFORE="${PUSH_BEFORE:?}" AFTER="${PUSH_AFTER:?}" require_rev "$AFTER" PUSH_AFTER if [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then ADDED=$(git show --diff-filter=A --name-only --pretty=format: "$AFTER" | grep -E "$ENTRY_RE" || true) + RANGE_FROM=""; RANGE_TO="$AFTER" else require_rev "$BEFORE" PUSH_BEFORE ADDED=$(git diff --diff-filter=A --name-only "$BEFORE" "$AFTER" | grep -E "$ENTRY_RE" || true) + RANGE_FROM="$BEFORE"; RANGE_TO="$AFTER" fi else echo "::error::now-sync-gate-diff.sh: unsupported GITHUB_EVENT_NAME=$event" exit 1 fi +# --- THE FROZEN ARCHIVE --------------------------------------------------- +# docs/NOW.md carries "FROZEN ARCHIVE -- do not add entries here." on its first +# line, and until now NOTHING enforced it: every mention of that path under +# .github/workflows/, scripts/ and .githooks/ is a comment -- measured, zero +# lines reject an edit. An author following one of the stale instructions that +# still pointed there could reopen the archive and pass all four required checks. +# +# A blanket "refuse any diff touching it" is WRONG, and that was measured too: +# of the 600 commits since the freeze, 2 touched the file and one (458ec0bd6) +# REPAIRS damaged entries. So is the narrower "refuse an ADDED `## ` heading" -- +# that same repair adds three of them, because it restored headings whose bodies +# had been destroyed. No textual rule separates adding an entry from repairing +# one: the only difference is POSITION, which is the coupling the one-file-per- +# entry layout exists to remove. +# +# So the exception is DECLARED, where the tool looks, the way `# tri:no-dispatch` +# and `# tri:cause-removed` are. A commit that edits the archive on purpose says +# so in its own message: +# +# Archive-Repair: +# +# The gate does not judge the reason. It requires one to exist, so that reopening +# a frozen file is a decision somebody signed rather than an accident. +if [ -n "$RANGE_TO" ]; then + if [ -n "$RANGE_FROM" ]; then + TOUCHED=$(git diff --name-only "$RANGE_FROM" "$RANGE_TO" -- docs/NOW.md || true) + MSGS=$(git log --format=%B "$RANGE_FROM".."$RANGE_TO" || true) + else + TOUCHED=$(git show --name-only --pretty=format: "$RANGE_TO" -- docs/NOW.md || true) + MSGS=$(git log -1 --format=%B "$RANGE_TO" || true) + fi + if [ -n "$TOUCHED" ]; then + if printf '%s\n' "$MSGS" | grep -qE '^Archive-Repair:[[:space:]]*[^[:space:]]'; then + echo "docs/NOW.md edited under an Archive-Repair trailer -- allowed." + else + echo "::error::docs/NOW.md is a FROZEN ARCHIVE and this range edits it." + echo "" + echo "Its first line says so. Entries go in one file each:" + echo " docs/now/-.md" + echo "" + echo "If you are REPAIRING the archive rather than adding to it -- restoring a" + echo "body the tooling destroyed, say -- that is allowed, and it has to be said" + echo "out loud. Put a trailer in the commit message:" + echo "" + echo " Archive-Repair: " + echo "" + echo "The gate does not judge the reason; it requires one to exist. A blanket" + echo "refusal was measured and rejected: 1 of the 2 post-freeze edits is a" + echo "legitimate repair, and it adds headings, so no textual rule tells the two" + echo "apart." + exit 1 + fi + fi +fi + if [ -z "$ADDED" ]; then echo "::error::SYNC REQUIRED: this PR/push adds no docs/now/ entry." echo ""