You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
LOOP-RULES R12 denies a spelling the gate accepts, and points authors at a frozen archive #3368
Two sentences in docs/loop/LOOP-RULES.md R12 contradict machine-readable truths in the same repository, and both were wrong on the day they were written.
1. Refs #N DOES satisfy check-linked-issue
LOOP-RULES.md:230-231 says:
put a literal Closes #N line in the PR body -- Refs #N does NOT satisfy check-linked-issue.
The gate's matcher, .github/workflows/issue-gate.yml:69:
Refs? has been in it since 5850c302e (2026-07-07). The R12 sentence was written 2026-08-20 — 44 days late on arrival, not stale. Seven readers in this tree carry the same dictionary (issue-gate.yml, l1-traceability.yml, auto-merge-ready-prs.yml, both check-l1-traceability.sh, gates.rs, hooks.rs). LOOP-RULES.md:230 is the sole dissenter.
It also fights R11 nineteen lines above — "AUTOCLOSING ISSUES IS FORBIDDEN" — by demanding the one spelling that autocloses.
The sharpest evidence is the file's own history: the most recent commit to LOOP-RULES.md is bb60ddc03, "docs(loop): R17 … (Refs #3357) (#3358)". The PR that last edited the rules file disproved line 230 while merging, and left it standing.
2. R12 still tells authors to write to a frozen archive
LOOP-RULES.md:228 says to add a section to the top of docs/NOW.md. That file's first line:
FROZEN ARCHIVE -- do not add entries here.
Frozen in f5be7dc1c (#2298, 2026-08-21) precisely because one file per PR is what stops concurrent PRs colliding. .claude/skills/spec-first-ternary-nn.md:62 carries the same instruction.
The guard, and why the obvious one is wrong
Every mention of docs/NOW.md under .github/workflows/, scripts/ and .githooks/ is a comment — measured, 0 lines that reject an edit. The freeze is prose only, so an author can follow the stale instruction, reopen the archive, and pass all four required checks.
But "fail any diff containing docs/NOW.md" is too broad. Measured over the 600 commits since the freeze: 2 touched the file, and one of them — 458ec0bd6 — REPAIRS damaged entries in the archive. The banner forbids adding entries, not touching the file. A guard that blocks a legitimate repair is the "gate that lands red on something legitimate gets muted" shape this repository already documents.
The narrow rule that would work: fail a diff that adds a ## heading to docs/NOW.md, not one that touches it. That is still a change to a required context (check-now-freshness), so it is filed here rather than done.
Found by the adversarial sweep for #3355. These two are the mirror of that class: the truth is machine-readable and the prose is the stale transcription.
Correction: the narrow rule I proposed also fails its control
In the issue body above I wrote:
The narrow rule that would work: fail a diff that adds a ## heading to docs/NOW.md, not one that touches it.
Measured, and it does not work. The legitimate repair in the population — 458ec0bd6, which restores damaged entries in the archive — adds 3 ## heading lines:
It re-adds headings whose bodies had been destroyed. So the "narrow" rule blocks exactly the edit the broad rule blocks, and my recommendation was wrong in the same direction twice.
The other post-freeze commit, 505785011, adds 0 headings — so on the measured population of 2 the rule would fire 1 of 2, and the one it fires on is the legitimate one.
No cheap textual rule separates "adds an entry" from "repairs an entry" here: both add headings, both add body lines, and the only difference is position (a prepend sits at the top) which is exactly the fragile coupling the one-file-per-entry layout was adopted to remove.
So: no guard is shipped, and this is a measurement rather than caution. What is shipped instead is #3369, which removes the two stale instructions that could send an author to the archive in the first place. An author who ignores the banner on line 1 is not a population a grep can separate from a repairer.
If the owner wants a guard anyway, the honest version is a human-readable allowlist — a commit trailer such as Archive-Repair: <reason> that the gate looks for — which puts the exception where the tool looks, the same shape as # tri:no-dispatch and # tri:cause-removed. That is a convention change, so it is not made unasked.
Implemented as the declared-exception form named at the end of my correction above: Archive-Repair: <reason> in the commit message, read by scripts/ci/now-sync-gate-diff.sh — the required check-now-freshness job.
Six controls, all run against a scratch repository rather than reasoned about:
case
expected
got
PR edits docs/NOW.md, no trailer
refuse
exit 1, with the archive message
PR edits it, trailer present
allow
exit 0, "under an Archive-Repair trailer"
PR does not touch it
unchanged
exit 0
trailer present but with no reason
refuse
exit 1
push edits it, no trailer
refuse
exit 1
push edits it, trailer present
allow
exit 0
Historical control: 458ec0bd6, the legitimate repair, carries 0Archive-Repair: trailers — so the guard would have refused it, which is correct. The point is not to permit that commit retroactively but to make reopening a frozen file a decision somebody signed.
Blast radius: 2 commits in the 600 since the freeze touch docs/NOW.md. Every other PR is unaffected.
My own probe was the broken instrument first: git reset --hard removed docs/now/, the entry file could not be written, and two cases failed on the presence check rather than on the guard. Fixed the harness, not the reading.
A boundary drafted from the paths this issue already names, so a bee can take it. Nothing is reserved until this is in the issue body - the Queen reads the body, not comments. Check the paths first: a wrong boundary reserves files the work does not own.
An attempt claimed this issue on 2026-09-24 and left an empty branch. A dispatch row that has spent its retry ceiling is released once and then keeps its issue for good, which is the right rule for a third identical failure and the wrong outcome for an issue nobody ever attempted twice. The new issue carries the same work, the same boundary, and today's brief.
Closing this one so the swarm can take the work again.
Activity
gHashTag commentedon Sep 6, 2026
Correction: the narrow rule I proposed also fails its control
In the issue body above I wrote:
Measured, and it does not work. The legitimate repair in the population —
458ec0bd6, which restores damaged entries in the archive — adds 3##heading lines:It re-adds headings whose bodies had been destroyed. So the "narrow" rule blocks exactly the edit the broad rule blocks, and my recommendation was wrong in the same direction twice.
The other post-freeze commit,
505785011, adds 0 headings — so on the measured population of 2 the rule would fire 1 of 2, and the one it fires on is the legitimate one.No cheap textual rule separates "adds an entry" from "repairs an entry" here: both add headings, both add body lines, and the only difference is position (a prepend sits at the top) which is exactly the fragile coupling the one-file-per-entry layout was adopted to remove.
So: no guard is shipped, and this is a measurement rather than caution. What is shipped instead is #3369, which removes the two stale instructions that could send an author to the archive in the first place. An author who ignores the banner on line 1 is not a population a
grepcan separate from a repairer.If the owner wants a guard anyway, the honest version is a human-readable allowlist — a commit trailer such as
Archive-Repair: <reason>that the gate looks for — which puts the exception where the tool looks, the same shape as# tri:no-dispatchand# tri:cause-removed. That is a convention change, so it is not made unasked.docs(loop): R12 denied a spelling the gate accepts (Refs #3368) (#3369)
gHashTag commentedon Sep 6, 2026
Implemented as the declared-exception form named at the end of my correction above:
Archive-Repair: <reason>in the commit message, read byscripts/ci/now-sync-gate-diff.sh— the requiredcheck-now-freshnessjob.Six controls, all run against a scratch repository rather than reasoned about:
docs/NOW.md, no trailerHistorical control:
458ec0bd6, the legitimate repair, carries 0Archive-Repair:trailers — so the guard would have refused it, which is correct. The point is not to permit that commit retroactively but to make reopening a frozen file a decision somebody signed.Blast radius: 2 commits in the 600 since the freeze touch
docs/NOW.md. Every other PR is unaffected.My own probe was the broken instrument first:
git reset --hardremoveddocs/now/, the entry file could not be written, and two cases failed on the presence check rather than on the guard. Fixed the harness, not the reading.gHashTag commentedon Sep 24, 2026
A boundary drafted from the paths this issue already names, so a bee can take it. Nothing is reserved until this is in the issue body - the Queen reads the body, not comments. Check the paths first: a wrong boundary reserves files the work does not own.
Boundary
.claude/skills/spec-first-ternary-nn.md.github/workflows/issue-gate.ymldocs/NOW.mddocs/loop/LOOP-RULES.mdgithub-actions commentedon Oct 3, 2026
Re-filed as #5708.
An attempt claimed this issue on 2026-09-24 and left an empty branch. A dispatch row that has spent its retry ceiling is released once and then keeps its issue for good, which is the right rule for a third identical failure and the wrong outcome for an issue nobody ever attempted twice. The new issue carries the same work, the same boundary, and today's brief.
Closing this one so the swarm can take the work again.
salvage(queen-3368): commit what the turn left uncommitted