Skip to content

Ratchet AGENTS.md's published spellings list to the detector's constant - #11908

Draft
os-steve wants to merge 2 commits into
mainfrom
claude/issue-10855-spellings-mirror-ratchet
Draft

Ratchet AGENTS.md's published spellings list to the detector's constant#11908
os-steve wants to merge 2 commits into
mainfrom
claude/issue-10855-spellings-mirror-ratchet

Conversation

@os-steve

@os-steveos-steve commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes#10855

GOVERNED SURFACE — draft only. This PR edits AGENTS.md, which is human-merge-only
(scripts/pm/check-governed-merges.mjs). It stays a draft, it is never flipped ready,
never armed for auto-merge, never queued. @os-zhuang merges it.

⚠️It carries one open decision that only the maintainer can settle — the AGENTS.md
line ceiling. See "The ask" below. check:pm-skill-ratchet is red until that is answered.

What was wrong

scripts/check-cross-package-test-inputs.mjs is a source scan: a path spelling it does
not know produces no flag, so a test whose reads escape its package goes undeclared
silently. That is why its recognised set is published in AGENTS.md rather than left
in the implementation — and nothing compared the two copies. Measured on 1a47a5368:

readingresult
findUp occurrences in the detector22
findUp mentions in AGENTS.md0
control — __dirname in AGENTS.md (a spelling that IS published)2

The control makes that zero a reading rather than a dead grep. The published block was
short by the two findUp anchor seeds and the ⛔ prohibition that qualifies them.

The card's line reference had already rotted: it names AGENTS.md:96-106; the block sat
at :92-104 three days later. The gate therefore locates the block structurally — by
its heading and fence — and never by line number.

The gate

scripts/check-published-list-mirrors.mjs, wired into lint.yml beside its closest
sibling, --self-test first (37 cases).

  • Equality, not containment. A line the doc publishes that the constant does not hold
    is a spelling authors are told is recognised while the scanner is blind to it — the
    original defect with a published byline, and the "wrong on arrival" case the card names.
    Containment cannot see it, and cannot see a comment-only drift either.
  • It judges a projection, and the projection is why one mechanism serves two rulings.
    The constant is the text of a CLI failure box: spellings (code, each with the comment
    annotating it) plus free-standing NOTE PARAGRAPHS set off by blank entries. An entry is
    PUBLISHED unless it is blank, or it is a comment line that no published entry directly
    precedes. Every spelling is code, so no spelling can hide from it.
    ⭐ This is not a convenience. Publishing all 24 entries verbatim put the note paragraphs'
    issue-ID citations into AGENTS.md and check:pm-skill-id-lint went red — the gate
    meant to keep the document honest would have broken the document's own prose standard
    (maintainer ruling 2026-08-12). A self-test case now pins that property against the live
    constant, so an issue number added to a spelling line goes red in this gate, naming the
    ruling, instead of landing in the instruction surface.
  • Every unreadable state REFUSES. Renamed heading, duplicated heading, re-tagged
    fence, unterminated fence, empty block, two candidate fences, and — on the code side — a
    constant that is missing, renamed, empty, or not a string list.
  • It can only ever go RED.AGENTS.md is governed, so the gate never repairs; it
    prints the exact block to paste. Stated in its own header, and in AGENTS.md itself, so
    the next reader does not discover it at review time.

Non-vacuity — 8 ablations, each proven on disk and restored under a trap

Every leg: mutate → prove it landed (sha change + anchor count) → node --check the
mutant where it is JS → run the gate → restore → prove the sha is back. Run against the
final gate, not an earlier draft.

legmutationverdict
A0none (control)OK
A1doc short by a spelling (the card's own case)FAIL line 14
A2doc publishes a spelling the detector cannot seeFAIL line 14
A3⛔ prohibition reworded in the doc (prose-only drift)FAIL line 16
A4heading renamedREFUSE "heading not found"
A5fence re-tagged ts → jsREFUSE — the ts-tagged fence is gone, 1 fence of another kind
A6constant renamed in the detectorREFUSE "is not exported"
A7new spelling added to the constantFAIL line 1
A8note-paragraph line added to the constantOK (projection holds; A7 proves red is reachable on the same file)

Two earlier legs were voided by their own proof step rather than reported: one whose
mutation did not land (sha unchanged) and one whose marker was a substring of the mutant.
Both were re-run with corrected anchors.

The ask — the AGENTS.md line ceiling, 961 → 969

AGENTS.md sits at its ceiling with zero headroom, so any honest correction of this
mirror crosses it. This PR takes it to 969 (+8):

costlines
block 11 → 17: the two findUp anchor seeds (one broken across two lines)+3
the ⛔ manifest-name prohibition that qualifies them+2
the -> repo root annotation+1
prose paragraph 4 → 6: names the ratchet and that it can only go red+2

Per the ratchet's own doctrine the cost should be paid in place. Measured, the way the
map's only previous raise was measured — greedy rewrap at 90 columns, per paragraph, over
the whole section:

lines 80-85: current 6, chars 506, lossless rewrap 6, headroom 0
lines 87-90: current 4, chars 319, lossless rewrap 4, headroom 0
lines 106-109: current 4, chars 353, lossless rewrap 4, headroom 0
lines 111-116: current 6, chars 471, lossless rewrap 6, headroom 0
lines 118-119: current 2, chars 164, lossless rewrap 2, headroom 0
TOTAL lossless rewrap headroom: 0 lines

So it cannot be paid in place without deleting content. The CEILINGS map records that
raising needs a maintainer ruling quoted in the raising PR; its one previous raise
(958 → 961) is recorded that way. ⛔ I have no such ruling and have not touched the map.

Options, for @os-zhuang to pick:

  • A (recommended). Raise to 969, recorded in the map's comment the way the previous
    raise is. Rationale: the mirror is now mechanically enforced, so this is a one-time
    truthful re-sync of a copy that had drifted three times, not the start of a trend — the
    gate makes every later line cost visible at the moment it is incurred.
  • B. Raise to 967 and drop the two prose lines that name the ratchet; authors then meet
    it as a CI red instead of reading about it in the file.
  • C. Compress 8 lines elsewhere in the section. Headroom is measured at 0, so this
    means deleting content, and the surrounding prose is load-bearing.
  • D. Stop publishing the copy at all and point at the tool (net −8 lines, and it
    retires the drift class outright). ⚠️ It contradicts the section's own stated reason for
    publishing — an agent reading AGENTS.md would have to run a tool to learn the
    spellings — so I do not recommend it without a deliberate reversal of that sentence.

The one-line follow-up once a ruling exists: ['AGENTS.md', 961]['AGENTS.md', 969]
in scripts/pm/check-skill-line-ratchet.mjs, with the ruling quoted verbatim beside it.

Verification

22312aa9e. 27 of 29 derived gate families green, derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (never a hand-built
path list). pnpm lint — the whole repo, eslint . --no-inline-config — green in 51s.

Two reds, both named:

  • check:pm-skill-ratchet — the ceiling above. This is the ask, not an oversight.
  • check:type-check-debt — environmental, not from this diff: it refuses to --re-measure
    without the workspace build closure on disk ("56 workspace dependenc(ies) ... have no
    built type entry point"), which lint.yml builds before that step. This diff contains no
    TypeScript at all.

check:pm-skill-id-lint: 22 file(s) clean. check:cross-package-test-inputs: 109
self-test cases, 16 packages read outside themselves, all declared.

Co-tenancy

PR #11829 also edits AGENTS.md and is also a governed draft in the same reviewer's
queue. Regions are disjoint — theirs is a single line at :191, mine is the section at
:78-135 — so both can land in either order. If this branch goes dirty I will merge
origin/main in and keep both edits; ⛔ never a rebase or a force-push on a governed file.

No changeset: this diff publishes nothing (scripts, workflow, and the instruction file).


Generated by Claude Code


Generated by Claude Code

os-steveand others added 2 commits August 24, 2026 23:44
…tant
`scripts/check-cross-package-test-inputs.mjs` is a source scan: a path spelling
it does not know produces no flag, so a test whose reads escape its package goes
undeclared silently. That is why its recognised set is published in AGENTS.md
rather than left in the implementation -- and nothing compared the two copies.
The mirror drifted three times (#10163, #10854, and this one). Twice the stale
line was the stated REASON FOR A PROHIBITION, so a rotting mirror does not
merely misinform: it launders an obsolete rule into a live one. And one claim
was already false BEFORE the PR that supposedly staled it, so a lag-only check
would not have caught it either.
Adds `scripts/check-published-list-mirrors.mjs`, which asserts line-for-line
EQUALITY between a declared constant and the block a document publishes:
- equality, not containment -- a comment-only drift is invisible to containment,
and the comments are where the prohibitions live;
- the block is located by heading + fence, never by line number (this card was
filed against `AGENTS.md:96-106`; the block sat at `:92-104` three days later);
- every unreadable state REFUSES: renamed heading, duplicate heading, re-tagged
fence, unterminated fence, empty block, two candidate fences, and a constant
that is missing, renamed, empty or not a string list;
- it can only ever go RED. AGENTS.md is governed, human-merge-only, so the gate
never repairs -- it prints the exact block to paste. Stated in its header.
Repairs the published block in the same change: it was short by 13 of the
constant's 24 lines, including the two `findUp` ANCHOR seeds PR #10852 added,
and the lead-in prose named only two of the three seed kinds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
Two standing rulings on AGENTS.md govern what a mirror may put there, and a
byte-identical copy of the whole constant breaks one of them outright:
- the shrink-only line ceiling (`check-skill-line-ratchet`), and
- "operative text carries lessons self-contained ... numbers go"
(maintainer ruling 2026-08-12, `check-skill-id-lint`).
The constant is the text of a CLI failure box: spellings (code, each with the
comment that annotates it) plus free-standing NOTE PARAGRAPHS set off by blank
entries, two of which carry issue-ID citations. Publishing all 24 lines put
those citations into the instruction surface and check:pm-skill-id-lint went
red -- the gate meant to keep the document honest would have broken the
document's own prose standard.
So the mirror judges a projection with no judgement in it: an entry is
PUBLISHED unless it is blank, or it is a comment line that no published entry
directly precedes. A comment continuing a spelling travels with that spelling;
a note paragraph standing alone stays in the failure box. Every spelling is
code, so no spelling can hide from it.
24 entries project to 17 published lines, zero issue-ID citations, and the
published block grows 11 -> 17: the two `findUp` anchor seeds, the ⛔ manifest
prohibition that qualifies them, and the `-> repo root` annotation.
A self-test case now pins the citation property against the live constant, so
an issue number added to a spelling line goes red HERE, naming the ruling,
instead of landing in AGENTS.md.
check:pm-skill-id-lint: 22 file(s) clean. check:pm-skill-ratchet is red by 8
lines (969 vs 961) and needs a maintainer ruling -- see the PR body.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026
@github-actionsgithub-actionsBot added ci/cd documentation Improvements or additions to documentation labels Aug 25, 2026
@os-steveClaude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPT on the implementation; the ceiling is escalated, not decided here. Verified against the branch and current origin/main, not read back from the report.

What I confirmed

Governed discipline held.draft: true, requested_reviewers: [os-zhuang], not merged, never armed. ⛔ It stays that way.

The CEILINGS map is untouched — structurally, not by assertion. The diff is 4 files (lint.yml, AGENTS.md, check-cross-package-test-inputs.mjs, check-published-list-mirrors.mjs); scripts/pm/ has no changed lines at all. AGENTS.md 961 → 969 on the branch, matching the +8 claimed.

"It can only ever go RED" — I checked this rather than taking it. A raw grep for writeFileSync|writeFile|--fix|--write|repair returns 3 hits, which looks like a contradiction. It is not: all three are prose in sentences saying it never repairs (:89, :93, and the :344 failure text). The decisive reading is structural — the module imports readFileSync, existsSync and no write API at all. That is a stronger proof than the sentence in the header.

Structural location, confirmed in code (:147:150, :174:181): the block is found by heading + fence, and a renamed heading returns a refusal, explicitly "never a pass: the block cannot be located, so nothing was compared."

The drift premise re-measured on current origin/main, not the 1a47a5368 in the body:

readingdev (1a47a5368)me (current main)
findUp in the detector2223
findUp in AGENTS.md00
control — __dirname in AGENTS.md22

The detector moved by one while this sat in review, which sharpens the card rather than weakening it. The control makes the zero a reading. On your branch AGENTS.md carries findUp3 times.

The zero-headroom measurement, re-derived independently. I did not re-run your greedy rewrap; I measured the raw line widths of the five paragraphs against the 90-column target: 86 75 84 90 86 82 / 78 86 82 72 / 86 87 90 89 / 77 89 87 87 86 42 / 90 75. Lines already sitting at 72–90 against a 90-column target cannot be rewrapped shorter. Option C genuinely means deleting prose, not reflowing it — I agree with how you characterised it.

The projection is pinned against the live constant, not against a fixture (:432, :434), so an issue number added to a spelling line reddens here instead of landing in the instruction surface. That is the right shape for a rule whose whole point is that the document's prose standard and the mirror can conflict.

⚠️ One co-tenant your PR body does not name — and it is the one that matters

Your Co-tenancy section names #11829 (AGENTS.md:191, disjoint — correct). But #11905 rewrites lint.yml by 95 insertions / 76 deletions across 171 lines, and it is already armed and sitting in the merge queue. Your PR adds 21 lines to that same file. #11905 will land first. So expect this branch to go dirty on lint.yml, not on AGENTS.md.

Your stated plan is right and applies unchanged — merge origin/main in, keep both edits, ⛔ never a rebase or force-push on a governed branch. Just aim it at the right file.

The ceiling is not mine to rule on

scripts/pm/check-skill-line-ratchet.mjs:66 is explicit — "RAISING one requires a maintainer ruling quoted in the raising PR's body" — and :58 adds that it "needs its own card and a maintainer's ruling, not a CEILINGS row added in passing." You were right not to touch the map, and a PM seat cannot authorise past that line.

Filed as its own card per :58, labelled needs-user-decision so it reaches the maintainer's decision box: #11910. ⛔ Do not touch the CEILINGS map until a ruling lands there.

check:pm-skill-ratchet staying red is the ask, correctly declared. check:type-check-debt refusing without the build closure is environmental and this diff contains no TypeScript — I accept that as declared-not-skipped.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cddocumentationImprovements or additions to documentationsize/lskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AGENTS.md's RECOGNISED_PATH_SPELLINGS mirror has drifted three times — nothing checks that the published list matches the detector

1 participant

@os-steve