Uh oh!
There was an error while loading. Please reload this page.
pm-ratchet: meter table-row bytes per file, close the Han+ASCII-punct soft breaks, cover compile-surfaces.md - #12263
Merged
Conversation
… soft breaks, cover compile-surfaces.md Three cards against one serial unit, scripts/pm/check-skill-line-ratchet.mjs. #11947 — per-file max-table-row-bytes pins, shrink-only, seeded at each file's own widest table row read from the script's own scanner. Closes the largest unmetered channel the 120-byte line rule leaves open: a table row grows by widening a cell, which costs zero lines and is exempt by shape. #12098 — a CEILINGS row for references/compile-surfaces.md, the last uncovered pm-dispatch references file. Count read from the ratchet's own run (26). #12081 — the 34 measured soft breaks sitting after an ASCII , ; : between Han characters are merged (each rendered as a stray mid-sentence space), and wrapLine no longer offers that break at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMTpSRF5CjMmQBFfPtPCwJ
The case used an atom pair with no space after the mark, where the break was already illegal for an unrelated reason — it passed under ablation of the very guard it names. Pinned both shapes instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RMTpSRF5CjMmQBFfPtPCwJ
This was referenced Aug 25, 2026
hotlong
marked this pull request as ready for review
August 25, 2026 16:02
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes#11947, closes#12098, closes#12081
Three cards against one serial unit —
scripts/pm/check-skill-line-ratchet.mjs— which is why they are one PR. Final commit for every reading below:
acf9bedfc.#11947 — per-file max-table-row-bytes pins
Maintainer ruling 2026-08-25 (issue #11947, comment 5406811814), verbatim and
untranslated:
accepting the skills seat's option 1 (A-ratchet): a per-file
max-table-row-bytes pin over the
CEILINGScorpus, shrink-only, seeded attoday's widest table row per file, no invented constants. Options 2 (whole-file
byte ceiling on the two table-heavy files) and 3 (nothing) are not taken. The
other five structural exemption classes — quotation / blockquote / unbreakable
/ anchored / fence — are untouched.
Seeds, measured with the script's own
scanTableRows, not by hand. Five ofthe 27 ceilinged files carry a table row today:
AGENTS.md.claude/skills/pm-dispatch/SKILL.md.claude/skills/pm-dispatch/references/compile-surfaces.md.claude/skills/spec-property-retirement/SKILL.md.claude/skills/checklist-test/SKILL.mdRe-measured on this branch rather than copied from the grading comment: the
AGENTS.mdrow is at L703, not L702 — it shifted by one line when #11819landed. The byte figure (1081) is unchanged.
The remaining 22 files are pinned at 0, which is a measurement (their
widest table row is 0 bytes), not a disabled row. An uncovered file is the one
place a row could widen unmetered again, which is the whole defect, so every
CEILINGSkey carries a pin and the self-test holds the two maps in step —enforcement cannot, because a missing pin is simply never consulted.
Ablation — the card demonstrated in one run. Widening the corpus's widest
cell by 11 bytes (a marker injected into
AGENTS.mdL703, mutation confirmed ondisk by grep count 0 → 1 and by the line growing 1081 → 1092 bytes):
The line ceiling stays green — that is exactly the unmetered channel the card
measured, and the new pin is what sees it. The mutation script carried a
trap … EXIT INT TERMrestore; restoration verified (marker count back to 0,gate exit 0).
#12098 — a
CEILINGSrow forreferences/compile-surfaces.mdAdded at 26, read from the ratchet's own run per the card's convention (row
inserted at a deliberately low ceiling of 1, gate printed
compile-surfaces.md is 26 lines, then set), headroom 0.the file carries no line over the 120-byte rule. It carries seven — and all
seven are
tablerows, structurally exempt and not re-wrappable (a wrapped|row is a different table). The card's remedy for that case ("re-wrap beforethe row, never raise") does not apply, because there is no legal wrap to
perform. They are instead metered by this PR's other half: the file's 352-byte
pin above. No ceiling was raised.
#12081 — closing the Han+ASCII-punct soft breaks, and no longer offering them
⭐ Read this before reading the diff — the card's self-concealment mechanism,
quoted verbatim:
Measured on this diff: it did not fire. Literal Han+mark+space occurrences
across the 15 touched files went 11 → 11, delta 0 — because this PR merges
the break (deleting the stray space, which is the defect) rather than re-wrapping
it faithfully (which would have preserved it). So no reviewer should read a
migrated
", "here; the mechanism is quoted because the next re-wrap of thiscorpus will hit it, and it is now recorded in the script header too.
⭐ It has, however, already fired once — measured, and filed as #12265.
platform-readings.mdwas re-wrapped by #12091 earlier the same day #12081 wasfiled, and running both predicates across that rewrite shows all seven of its
breaks converting into literal
", ":That is why #12081's own file table no longer reproduces for that file, and it
is the reason its count is 34 here rather than 45. The literal population (14
across 3 files) is out of scope for this PR — the dispatch scoped #12081 to "the
measured lines" — and is recorded in #12265 with its triage caveat.
The population was re-derived on this tree, and it is 34, not the card's 45.
Two reasons, both worth stating:
platform-readings.mdwas rewritten by refs(pm-dispatch): qualify the REST prescriptions as per-session, and bank four platform readings #12091 since, taking its 7 with it (it now measures 0).
rulings — the
quotationexemption class the 2026-08-25 ruling explicitlyleaves untouched, and which AGENTS.md 语言规则 requires be carried verbatim and
unrewritten. They are excluded, not fixed. A blunt pass over the card's raw
predicate would have edited six maintainer rulings.
The 34 are closed in place across 15 files. Line counts move only downward:
decision-analysis.md48 → 46, lowered in the map (shrink-only, alwayslegitimate); every other file's count is unchanged, so no other row moved and no
ceiling was raised. ⛔ No corpus-wide re-wrap: the re-wrap window is the smallest
run of lines starting at the measured break and growing downward inside the same
paragraph that does not cost a line. Paragraphs with no measured break are not
touched.
Render-level before/after, two of the samples (the whole point of the card is
that this is invisible in the source and visible only in the render):
② The wrapper no longer offers the break.
hanAsciiPunctTailrefuses a breakafter an ASCII
,;:that follows a Han character. It is a separate testrather than three more characters in
NO_BREAK_AFTER, because those marks muststay legal break points in ASCII prose — the self-test pins both directions, and
the ASCII twin case exists so the Han case cannot pass for being over-broad.
Why only one shape ever leaked, now pinned in the self-test: with no space
after the mark there was never a legal break there anyway (neither side is wide),
so the defect could only enter where the author had written
", "and thewrapper spent that space on a newline.
Reverse-verification, and it changed a case. Ablating the guard
(
hanAsciiPunctTailcall removed frombreakLegal; mutation confirmed on disk,guard count 1 → 0, marker count 0 → 1) first turned only one case red. The
breakLegalcase was passing for an unrelated reason — its atom pair had nospace after the mark, so the break was already illegal. It was rewritten to use a
spaced pair; the ablation now turns 2 of 111 red, and the fix is a separate
commit so the sequence is auditable. Restore trapped and verified.
Inertness on today's corpus, measured. Comparing
classifyLineandwrapLineold-vs-new over all 4,953 lines of the corpus: 0 classificationdiffs, 0 wrap-output diffs. The rule removes a break option, which could in
principle turn a RED over-long line into an exempt
unbreakableone — a realloosening. It does not, on any line in this corpus.
Verification
Self-test 71 → 111 cases, all green. (The dispatch brief said "currently
1336+ cases"; the measured baseline on
origin/mainis 71 — recorded herebecause the figure will otherwise be quoted again.)
Gate families derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(16 paths vs merge baseeeec62a98; 19 families,re-derived on the final tree with no new family appearing). Each ran at
acf9bedfcwith its exit code captured before any pipe; every one green:check:agent-model-declared·check:agent-test-spelling·check:cross-package-test-inputs·check:doc-authoring·check:doc-formula-expressions·check:entry-guard·check:nul-bytes·check:parse-guard·check:pm-governed-merges·check:pm-governed-prose·check:pm-skill-id-lint·check:pm-skill-ratchet·check:pnpm-filter-targets·check:required-contexts·check:skill-frame-sync·check-ci-filter-parityVerdict lines from the two that speak to this change:
check-governed-queue-guardis the one family that cannot run locally — it readsGITHUB_EVENT_PATHand correctly refuses to exit 0 without it. CI owns it.ESLint — a narrowed run, declared. Repo-wide
pnpm lintis CI's; this raneslint --no-inline-configover the 16 changed paths, 0 errors. Thenarrowing excludes nothing, on three readings: (1) the population comes from
eslint's own config, which reports 15 of the 16 paths as "File ignored because
no matching configuration was supplied" — the linted population of this diff is
exactly
scripts/pm/check-skill-line-ratchet.mjs; (2) the count is read from--format json(16 entries, 0 errors, 15 unconfigured); (3) this repo'seslint.config.mjsnever enables type-aware linting for ANY file (noparserOptions.project, no typed@typescript-eslintrules — stated andmeasured with a positive control at
eslint.config.mjs:324-334), so this diffcannot move the verdict of any file it does not touch.
No changeset: internal agent tooling plus
.claude/instruction files. Thispublishes nothing.
skip-changesetapplied.⛔ Draft only — the diff touches
.claude/**, a governed surface. Not flippedready, no auto-merge armed. Review requested from @os-zhuang.
Generated by Claude Code
Generated by Claude Code