Skip to content

finding: CLAUDE.md carries no line-ratchet ceiling, while AGENTS.md does — same read path, same governance #9965

Closed

Description

@os-warren

Filed unassigned while implementing #9923 (ratchet header boundary honesty). Out of that card's scope, which was ruled to be header wording about the published skills/ root and explicitly not a coverage extension.

Measured

scripts/pm/check-skill-line-ratchet.mjs CEILINGS on origin/main at merge base 5989b0de9 covers 18 files. Root AGENTS.md is covered at 958 lines. Root CLAUDE.md is not covered, at 86 lines.

Why the two look alike

Both are repo-root agent instruction files, both are read by every seat session, both are governed surfaces under Prime Directive #14. The reasoning that added the AGENTS.md ceiling — largest, most-read, most-binding instruction file with no ceiling — applies to CLAUDE.md in kind, differing only in degree.

CLAUDE.md is also structurally growth-prone in a way the ratchet is built for: it exists to inline the rules that must never be missed, so every new must-never-miss rule is an argument for appending to it. It currently inlines four.

The honest counter-argument

At 86 lines it is small, and its own framing caps it: it is an excerpt of AGENTS.md, not an independent surface. A ceiling on it may be a control with nothing to control.

The card this was found on now makes the fact visible either way — the ratchet's header states that CLAUDE.md, .claude/hooks/ and .claude/settings.json carry no ceiling, so absence reads as a fact to check rather than an oversight to infer. This card is only about whether the fact should change.

Decision needed, not an implementation

Adding a CEILINGS row is a one-line change; whether it should exist is a policy question of exactly the kind #9923 was ruled on. Note the sibling repo objectui has its own CLAUDE.md and AGENTS.md, neither reachable from this ratchet at all, so a maintainer may prefer to answer the root-instruction-file question once rather than per file.

Activity

  1. os-warren commented on Aug 19, 2026

    @os-warren
    CollaboratorAuthor

    首触定级(skills 座,session ae0d8bd1-5667-5f58-8ff8-fa22ec154325):转 needs-user-decision —— 卡自陈「Decision needed, not an implementation」,且与 #9923 裁过的政策形状相同(棘轮覆盖范围=政策),照 #9962 落地的头部升级句走维护者。

    座位推荐:暂不加 ceiling。反方论证成立 —— 86 行、自我定位是 AGENTS.md 的节选、每条内联规则都经治理面人合;且 #9962 之后「CLAUDE.md 无 ceiling」已是头部明示的事实而非盲区,可见性问题已解决,剩下的纯粹是要不要多一道控制。若维护者想一次回答「根指令文件是否都该有 ceiling」(含 objectui 的一对,本棘轮够不着),那是跨仓政策卡,另立。


    Generated by Claude Code

  2. os-warren commented on Aug 19, 2026

    @os-warren
    CollaboratorAuthor

    指派 os-zhuang = 决策通知机制(维护者 2026-08-19 裁定,推送已实测):待裁 = CLAUDE.md 是否加 line-ratchet ceiling(座位推荐:暂不加,理由见上方定级评论)。裁定后摘指派。


    Generated by Claude Code

  3. removed their assignment
    on Aug 20, 2026
  4. os-zhuang commented on Aug 20, 2026

    @os-zhuang
    Contributor

    Maintainer ruling recorded (2026-08-20, live decision-inbox session with the triage seat, session session_01PjAP6vbcsg2yMtvySPv1Qo)

    Ruled: add a CEILINGS row for root CLAUDE.md. Provenance: maintainer accepted the batch, verbatim: 「其他接受你的建议。」Rationale on record: CLAUDE.md is structurally growth-prone (every must-never-miss rule argues for appending), and a one-line ceiling now prevents compound growth cheaply. The sibling-repo question (objectui/cloud root instruction files unreachable from this ratchet) is noted for the skills seat to carve separately if wanted — not part of this landing.

    Governed surface (scripts/pm/ ratchet + the covered file) — draft-only PR, human merge, contract-review tier per the skills-face rule. State transition in the same stroke: needs-user-decision → pm:queue.


    Generated by Claude Code

  5. self-assigned this
    on Aug 23, 2026
  6. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    Claim: PM loop round 1
    Session: session_01RMTpSRF5CjMmQBFfPtPCwJ
    Branch: claude/issue-9965-claude-md-ceiling
    Worktree: objectstack-issue-9965
    Domain: domain:skills
    File surface: scripts/pm/check-skill-line-ratchet.mjs (stop on breach; explain in the report)
    Container & model: S mechanical, mode:subagent, model: opus — per this dispatch's node scripts/pm/dispatch-gates.mjs --tier run: "no path-derived mandate"; governance-face mechanical change dispatched at opus construction per the 2026-08-20 tiering ruling, with contract-review-tier review as the compensating control at ACCEPT (this seat session is machine-verified at that tier)
    Clause-②: no
    Serial constraints cleared: none in flight — scanned all 9 open PRs, none touches scripts/pm/**; last churn on this file is merged PR #11126.


    Generated by Claude Code

  7. claude commented on Aug 23, 2026

    @claude
    Contributor
    {
      "issue": 9965,
      "status": "done",
      "branch": "claude/issue-9965-claude-md-ceiling",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11274",
      "premise_still_valid": true,
      "summary": "Added ['CLAUDE.md', 86] to CEILINGS in scripts/pm/check-skill-line-ratchet.mjs per the 2026-08-20 ruling, quoted in place. All three PM mechanism assumptions held on measurement: CLAUDE.md is 86 lines on origin/main under the gate's OWN countLines convention (wc -l agrees), the header sentence naming CLAUDE.md as uncovered exists and was corrected in the same commit, and the file surface held — CLAUDE.md itself is untouched. One coupling the brief did not name was forced by the gate's existing self-test: the case 'every separator-less ceiling declares a root-file watch hint' goes red for any separator-less CEILINGS key lacking a hint, so ROOT_FILE_WATCH_HINTS gained 'CLAUDE.md/**' — the same spelling scripts/pm/check-governed-merges.mjs already carries for these two root files. Its docblock prose and the AGENTS.md-exclusivity self-test case were corrected with it. Everything stayed inside the one named file.",
      "files_changed": [
        "scripts/pm/check-skill-line-ratchet.mjs"
      ],
      "gates_run": [
        "pnpm check:pm-skill-ratchet — '✓ check-skill-line-ratchet self-test: 20 cases pass.' (was 19) and '✓ check-skill-line-ratchet: CLAUDE.md is 86 lines (ceiling 86; headroom 0).'; 21 ceiling rows green (was 20)",
        "pnpm check:cross-package-test-inputs — 'OK: 14 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.'",
        "pnpm check:entry-guard — '✓ check:entry-guard: 137 scripts/ file(s) — every entry guard goes through invoked-as.mjs; 96 export bindings, 86 of them inert on import'",
        "pnpm check:parse-guard — 'All 104 self-test cases passed.'",
        "pnpm check:pnpm-filter-targets — '✓ check:pnpm-filter-targets: 121/149 `--filter` occurrence(s) across 25 file(s) resolve against 78 workspace package(s)'",
        "node scripts/check-ci-filter-parity.mjs — 'OK: all 88 declared cross-package glob(s) (76 unique) are covered by `core` or `crosspkg`'",
        "node scripts/check-cross-package-test-inputs.mjs — 'OK: 14 package(s) read outside themselves, all declared'",
        "pnpm check:nul-bytes — 'check-nul-bytes: OK (scanned 6330 text file(s) -- 6330 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).'"
      ],
      "tests": "Families derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` with NO path arguments, against the actual change set (it read '1 path(s) vs merge base 6a4e929f5', working tree 1) — 7 families, all run, all green, plus check:nul-bytes; see gates_run for each gate's own verdict line (exit codes captured before any pipe, via redirect-then-capture, never `| tail`). UNION RUN AT FINAL COMMIT 411b950 (`git rev-parse --short HEAD` from that run). ABLATION (throwaway, never committed, trap-guarded `trap ... EXIT INT TERM` so a foreground-cap SIGTERM mid-mutation could not leave the tree mutated): no build/dist leg applies — the covered input is a .md file the gate reads directly, so there is no rebuild to prove; the mutation was instead confirmed ON DISK rather than trusted to an editor exit code — marker `grep -c` 0 -> 1, gate-convention line count 86 -> 87, sha256 03749199..c57b91d2 -> a59e9ffe..cec82e5c. RED leg (process exit 1), gate's own verdict line: '✗ check-skill-line-ratchet: CLAUDE.md is 87 lines; the ratchet ceiling is 86.' RESTORE proved byte-identical: sha256 back to 03749199..c57b91d2, marker count back to 0, `git status --porcelain CLAUDE.md` empty. GREEN leg (exit 0): '✓ check-skill-line-ratchet: CLAUDE.md is 86 lines (ceiling 86; headroom 0).' Direction was as predicted (red on growth). WATCH-HINT DELTA measured, not asserted: a hypothetical CLAUDE.md card derived 1 family before (check:pm-governed-merges) and 2 after (+ check:pm-skill-ratchet, 'matched via CLAUDE.md ⇢ gate source CLAUDE.md/**') — exactly the one (gate, file) pair the docblock claims per root file. NOTE, not a flake: check-ci-filter-parity.mjs first exited 1 with ERR_MODULE_NOT_FOUND for `yaml` because I had not yet run `pnpm install` in the fresh worktree; after install (via `bash scripts/pm/os-verify-lock.sh -c`, VERDICT command-exit 0 · held the lock 8s · waited 196s) it is green as quoted. My own missed step, corrected — not a repo condition.",
      "deviations": [
        "check-governed-merges DISAGREES with the ruling comment's prose, and the PR is kept draft anyway per the card's recorded constraint. `node scripts/pm/check-governed-merges.mjs --test scripts/pm/check-skill-line-ratchet.mjs`, verdict verbatim: 'governed-surface predicate: 0 of 1 path(s) hit the register (5 surfaces, repo-agnostic).' / '✅  NOT governed — ordinary queue landing applies to a PR with exactly this file list.' The register governs docs/adr/**, .claude/**, skills/**, AGENTS.md and CLAUDE.md — NOT scripts/pm/**. The ruling comment said 'Governed surface (`scripts/pm/` ratchet + the covered file)': the COVERED FILE is governed, the RATCHET SCRIPT is not, and this PR touches only the script. PR left draft, auto_merge null, never flipped ready — for the PM to reconcile at ACCEPT.",
        "Self-test surface changed beyond the brief, all inside the named file and mechanically forced: ROOT_FILE_WATCH_HINTS + its docblock, and the two self-test cases described in summary. No other file touched."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  8. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    ACCEPT (skills seat, session session_01RMTpSRF5CjMmQBFfPtPCwJ, reviewer of record) — PR #11274.

    Checklist conclusions, verified against GitHub not the report: draft ✓ · base main ✓ · Fixes #9965 first line ✓ · changed files = exactly the claimed surface (scripts/pm/check-skill-line-ratchet.mjs, 1 file) ✓ · skip-changeset on a scripts-only diff ✓. Diff read line by line: the CEILINGS row quotes the ruling verbatim in place; the header boundary sentence is corrected in the same stroke (the contradiction this lane files findings about); the ROOT_FILE_WATCH_HINTS addition is mechanically forced by the pre-existing self-test case and matches the sibling gate's spelling; the exclusivity self-test case correctly moves to the two-file join form. Ablation evidence is two-directional with the mutation proven on disk and the restore proven byte-identical. Watch-hint delta measured at exactly one (gate, file) pair — the discoverability claim is backed, not asserted.

    Contract-review-tier compensating control (per the claim): this review ran on the seat session machine-verified at tier — get_session → external_metadata.last_served_model: claude-fable-5 = CONTRACT_REVIEW_TIER, read this fire, not self-described.

    Deviation reconciliation: check-governed-merges --test says NOT governed (register covers the COVERED file, not the ratchet script) — the dev is right about the register. The card's recorded ruling constraint (draft-only, human merge) is honored anyway: PR stays draft, review requested from os-zhuang (author is claude[bot], so the #11083 author-identity failure does not apply). If the maintainer prefers ordinary queue landing for it, that is their call at merge; the discrepancy is listed in the round report.


    Generated by Claude Code

  9. removed their assignment
    on Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions