Skip to content

docs(pm): state the skill line-ratchet's coverage boundary in its header - #9962

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-9923-ratchet-boundary-honesty
Aug 19, 2026
Merged

docs(pm): state the skill line-ratchet's coverage boundary in its header#9962
os-warren merged 1 commit into
mainfrom
claude/issue-9923-ratchet-boundary-honesty

Conversation

@os-warren

@os-warrenos-warren commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Fixes#9923

Header honesty only. No CEILINGS entry added; no enforcement behaviour changed.

The gap

scripts/pm/check-skill-line-ratchet.mjs ends its "Why ceilings" section with "the ceiling now covers the whole surface, per file" — which reads as a claim about the instruction surface as a category. Re-measured on origin/main at merge base 5989b0de9:

CEILINGS entries18
under .claude/17
otherAGENTS.md
under the published skills/ root0

The published catalog is eleven SKILL.md totalling 10,443 lines, against 3,664 lines of ceiling covered here. A reader could not see the published root was outside the boundary without diffing the map against the filesystem.

Note the card said 16 entries; its own enumeration (6 + 6 + 4 + 1 + 1) sums to 18, and 18 is what the map holds. The load-bearing measurement — zero published-root entries — is unaffected.

The fix, per the ruling on the card

Deliberately uncovered stays uncovered. The header now states:

  1. The covered set is an enumeration, never a root glob, and names it. It also names what else carries no ceiling (.claude/hooks/, .claude/settings.json, CLAUDE.md) so absence reads as a fact to check rather than an oversight to infer.
  2. The published skills/ catalog is deliberately outside, with both reasons: the ratchet prices a full-file token read paid per seat session and per Routine fire, and the published catalog is read by customer projects rather than this repo's seats — a different cost curve; and the published catalog is already a governed, human-merge-only surface (Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14), so growth there passes a human eye by construction.
  3. Extending coverage to the published root is a policy change needing its own card and a maintainer's ruling, not a CEILINGS row added in passing.

One precision added while writing reason 2, because the header must not itself state something false: the governed-surface reason does not separate the two roots on its own — the covered files are human-merge-only too (Prime Directive #14 names .claude/** and AGENTS.md), so taken alone that reason would argue against the AGENTS.md ceiling that already exists. The header now says so and points at the recurring per-session read as the property that actually does the separating.

The boundary is pinned, because enforcement cannot hold it

One self-test case in the file's existing idiom asserts no CEILINGS key starts with the published root. This is not decoration — see the reverse verification below: a ceiling on a real published SKILL.md runs perfectly green through the enforcement path, so without the pin the header paragraph could drift from the map silently.

Reverse verification (direction predicted in writing before running)

RV-1 — the pin catches a published-root ceiling. Mutation: add ['skills/objectstack-formula/SKILL.md', 556], a real path at its true line count, chosen so enforcement stays green. Predicted: enforcement GREEN, self-test RED. Observed exactly that:

RUN_EXIT=0
check-skill-line-ratchet: skills/objectstack-formula/SKILL.md is 556 lines (ceiling 556; headroom 0).
SELFTEST_EXIT=1
the published skills/ catalog is deliberately uncovered [failing case]
check-skill-line-ratchet self-test: 1 of 15 case(s) failed.

Restored with git checkout HEAD -- ... from the committed state; git status --porcelain clean afterwards (no MM), mutation absent.

RV-2 — header prose must not change derived watch hints.scripts/pm/dispatch-gates.mjs machine-reads this file via extractWatchHints, which masks comments first. Predicted: hint set byte-identical before and after, since any change would silently widen the ratchet family's watch set — an enforcement/derivation change this card forbids.

This leg falsified its prediction on the first run, and the failure was real. The initial header spelled a glob whose asterisk and slash form a comment terminator, closing the docblock early. Node reported SyntaxError: Unexpected identifier 'AGENTS', and the orphaned prose — now parsed as module body — leaked seven phantom hints (.claude, .claude/hooks, .claude/settings.json, .claude/skills, origin/main, references, skills). Rewritten without the terminator; node --check passes and the hint set is identical to the base (17 hints, unchanged) at final head. The three dispatch-gates self-test cases that pin this file's hints stay green.

The generalisable trap: a path glob containing an asterisk followed by a slash cannot be written literally inside a block comment.

Gates

node scripts/pm/dispatch-gates.mjs with no args, deriving from the real diff, re-run on final commit 021dfbcbb; scripts/** adds check:nul-bytes per the dispatch. Each gate's own verdict line, from that head:

  • pnpm check:pm-skill-ratchetcheck-skill-line-ratchet self-test: 15 cases pass. and check-skill-line-ratchet: AGENTS.md is 958 lines (ceiling 958; headroom 0). (whole tree still passes)
  • pnpm check:cross-package-test-inputsOK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
  • pnpm check:nul-bytescheck-nul-bytes: OK (scanned 6314 text file(s) -- 6314 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).
  • pnpm check:pm-dispatch-gates (not derived; run because it pins this file's hints) — dispatch-gates self-test: 310 cases pass.

skip-changeset: internal PM tooling, nothing user-visible ships.


Generated by Claude Code

@os-warrenos-warren added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 19, 2026 — with Claude
The CEILINGS map covers 18 files — the .claude/ instruction surfaces plus root
AGENTS.md — and zero files under the published skills/ catalog, while the header
read as though the ceiling covered the instruction surface as a category. A
reader could not see the published root was outside it without diffing the map
against the filesystem.
State the covered set as an enumeration, state the published root as
deliberately outside with the two reasons (the ratchet prices per-seat/
per-Routine full-file reads, which the published catalog is not on; and the
published catalog is already a governed human-merge-only surface), and name
coverage extension as a policy change needing its own card and a maintainer.
Pin the boundary in the self-test: enforcement cannot hold it, because a ceiling
on a real published SKILL.md would run green like any other row.
No CEILINGS entry added; no enforcement behaviour changed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA3nU1B5Q2pgxqxgUrexd
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] The PUBLISHED skills/ catalog carries no line-ratchet ceiling — every .claude/ instruction surface does

2 participants

@os-warren@claude