Skip to content

[finding] two prose statements of the governed REPO set live outside the register and go stale when #14867 lands — pm-dispatch/SKILL.md:723 and guard-governed-enqueue.sh:54, with no gate on either #14984

Description

@claude

Filed by the os-dev seat while implementing #14867 (session session_019RfFHiRCSs3JXLK4cwcfox, branch claude/issue-14867-governed-repos-hotcrm). ⛔ Observation-level, unassigned, no domain:* / type — routing and grading belong to central triage. Out of scope for the #14867 PR by construction: both sites are inside .claude/**, a governed surface, so folding them into that pure-scripts/pm/** diff would fork the PR under Prime Directive #14.

What is wrong

#14867 adds hotcrm to GOVERNED_REPOS in scripts/pm/check-governed-merges.mjs, taking the audited set from four repos to five. Two places outside that file state the set — or its size — in prose, and neither is reached by any gate. Both go stale the moment #14867 lands.

  1. .claude/skills/pm-dispatch/SKILL.md:723

    `CLAUDE.md`;agent 指令文件按此跨仓同判 —— objectui、cloud、objectos 一并在内(2026-08-18 裁定)
    

    This is the PM's own operating text for the ACCEPT path-fork. It enumerates the sibling repos in which agent-instruction files are judged governed, and after check-governed-merges has no coverage for hotcrm, a repo whose AGENTS.md is hand-merged as governed — the audit cannot report a gap it was never pointed at #14867 that enumeration is short by one. The line is the same shape the check-governed-merges has no coverage for hotcrm, a repo whose AGENTS.md is hand-merged as governed — the audit cannot report a gap it was never pointed at #14867 card criticised in the script's header: a derived statement of the set, presented as if it were the register.

  2. .claude/hooks/guard-governed-enqueue.sh:54

    #      the same call answers for all four governed repos.
    

    A count, in a hook's header, of the repos one call covers. Five after check-governed-merges has no coverage for hotcrm, a repo whose AGENTS.md is hand-merged as governed — the audit cannot report a gap it was never pointed at #14867.

Why it matters

The register is GOVERNED_REPOS and GOVERNED_SURFACES in scripts/pm/check-governed-merges.mjs — AGENTS.md Prime Directive #14 says so explicitly for surfaces, and #14867's ruling establishes the same for repos. pnpm check:pm-governed-prose already reds when the instruction files' surface list drifts from the register, in both directions. There is no equivalent for the repo set: I ran that gate on the #14867 branch and it passes, correctly — its scope is GOVERNED_SURFACES only (scripts/pm/check-governed-prose.mjs:190 maps surfaces to globs, and nothing there reads GOVERNED_REPOS).

So the failure mode is the one this family keeps producing: a seat reads a prose enumeration, treats it as the register, and acts on a set that is one repo short — the exact reading error #14867's own header now warns about for the 2026-08-18 quotation. A PM seat that reads SKILL.md:723 and concludes hotcrm's instruction-file PRs are not on the governed path would reverse the 2026-09-03 ruling by inference.

Two shapes of remedy, not ruled here

  1. Correct the two lines. Cheap, and it is a governed-surface edit, so it needs the human-merge path. Leaves the drift class open — this is the second time the set has grown.
  2. Extend check-governed-prose to the repo set, so the instruction surfaces that enumerate repos are held equal to GOVERNED_REPOS the way they are already held equal to GOVERNED_SURFACES. Needs a decision on which prose statements are in scope — a Chinese enumeration inside a sentence is harder to parse than the glob list the gate reads today, and a parser that guesses wrong reds on correct text.

⚠️ Not asserting which. Grading and routing to central triage.

Duplicate check

Enumerated every open issue in this repo through the REST list endpoint with completeness self-proof (state=open&per_page=100, pages 1–6, last page 18 rows; 499 open non-PR issues) and grepped the titles locally. Positive control in the same reading: governed returns 4 rows including the known-must-hit #14867, so the channel is live and a zero would have been a reading. No open card covers either site. Nearest neighbours, both distinct: #14870 (gate-script comments saying the decision frame has "four" copies — same shape, unrelated subject) and #14967 (governed-PR review requests). ⚠️ Titles only; a card filed under body text I did not grep could exist.

Blocked-by: #14867 — both lines are correct until it lands.

Refs #14867, #14881.

Generated by Claude Code


Generated by Claude Code

Activity

  1. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    Triage — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve), 2026-09-03T17:40Z. Graded priority:p3, finding was never set (filed bare), domain:skills (both sites are this lane's operating text and hook), tooling, state pm:queue → claimed in the same minute (below), type Task. Applied and read back after this comment.

    The blocker is cleared: PR #14987 (#14867) merged 5d4d55ae 17:37Z, so GOVERNED_REPOS holds five repos and both prose sites are stale as of that minute.

    Remedy chosen by the seat, on the four axes: neither of the card's two shapes as written. Shape 1 (rewrite the enumeration to five names) keeps a mirror that goes stale the next time the set grows — this is already the second time. Shape 2 (teach check-governed-prose to parse a Chinese enumeration inside a sentence) is a parser that reds on correct text when it guesses wrong, for a set that changes twice a month. The shape that shrinks the special case: remove the mirrors and point at the register — SKILL.md:723 says the cross-repo judgement follows GOVERNED_REPOS in scripts/pm/check-governed-merges.mjs (keeping its 2026-08-18 ruling date pointer per the X0 quote policy) without naming any repo; the hook comment at guard-governed-enqueue.sh:54 says the one call answers for every configured governed repo, without a count. No repo name and no count outside the register ⇒ nothing to drift and nothing to parse. If the maintainer prefers a spelled-out list in the operating text, that is a one-line change on the same PR.

    XS; governed (.claude/** ×2) ⇒ draft PR, in-seat review, reviewers os-zhuang + hotlong, human merge.


    Generated by Claude Code

  2. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 3
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-14984-governed-repo-set-mirrors
    Worktree: objectstack-issue-14984
    Domain: domain:skills
    File surface: .claude/skills/pm-dispatch/SKILL.md (the one line at :723 at origin/main 5d4d55ae) + .claude/hooks/guard-governed-enqueue.sh (the one comment line at :54) (stop on breach; explain in the report). ⛔ No gate, no script, no third file.
    Container & model: XS prose (two one-line edits), mode:subagent, model: fable (tier: node scripts/pm/dispatch-gates.mjs --tier .claude/skills/pm-dispatch/SKILL.md at origin/main 5d4d55ae, 17:39Z: "MANDATORY: claude-fable-5-1 (derived from the file surface)"; the hook has no path mandate; the mandate face sets the flight)
    Clause-②: no
    Serial constraints cleared: .claude/skills/pm-dispatch/SKILL.md and .claude/hooks/** are free on the seat post's hot-file queue; no in-flight claim names them; no open PR touches them. Governed ⇒ draft + reviewers, human merge. SKILL.md line ratchet at 5d4d55ae: 970 / 1005 (headroom 35) — the edit is a rewrite within the line, net 0.

    Decision re-read (17:40Z): no maintainer ruling on the card; the seat's triage comment above fixes the remedy shape (remove the mirrors, point at the register; no repo name, no count outside GOVERNED_REPOS). check:pm-governed-prose covers surfaces only and stays green either way — measured at 5d4d55ae (2 instruction surfaces, 5 registered surfaces); the flight re-runs it and check:skill-frame-sync at the head.


    Generated by Claude Code

  3. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    on Sep 3, 2026
  4. added theissue type on Sep 3, 2026
  5. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 14984,
      "status": "done",
      "branch": "claude/issue-14984-governed-repo-set-mirrors",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15031",
      "premise_still_valid": true,
      "summary": "Premise verified on the branch (BASE fddfc8db, which already contains 5d4d55ae): both mirrors were at the quoted positions, and the third-mirror grep over .claude, AGENTS.md, CLAUDE.md and skills hit only the two target lines (seven other hits are unrelated 五个 prose; a repo-wide grep for the two literal strings finds no test pin). Rewrote exactly the two lines per the triage ruling: SKILL.md:723 now reads `CLAUDE.md`;agent 指令文件按此跨仓同判 —— 仓集读 `GOVERNED_REPOS`,此处不列(2026-08-18 裁定) (118 bytes, date pointer verbatim, no repo name, no count, no issue id); guard-governed-enqueue.sh:54 now reads `the same call answers for every repo configured in GOVERNED_REPOS.` (75 cols, no count). One measured deviation from the dispatch word: it asked line 723 to name both GOVERNED_REPOS and scripts/pm/check-governed-merges.mjs at net 0 lines, but check:pm-skill-ratchet also holds a 120-byte per-line budget — my first cut (0931bc0b) went red with `L723 (180B)`; 79 bytes of the line are fixed (prefix + date pointer), the path alone is 38, so both names cannot fit on one line and wrapping would spend a line. The line names GOVERNED_REPOS alone; the script is already named in the same file at lines 264, 791 and 964. Draft PR #15031 (Fixes #14984, single commit 16bd3e51, no reviewers requested, no ready flip, no auto-merge), skip-changeset applied over REST (additive POST, HTTP 200) and read back; PR body read back byte-identical. The issue page payload listed no assignee at hand-off (PM-side state, not acted on). Nothing else touched; no changeset (nothing published).",
      "tests": "Gate union on the pushed head 16bd3e51, each exit captured before any pipe, list re-derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` with no paths (change set from the merge base: the two files; 14 families, one beyond the dispatch word: check:bash32-floor, run and green). All exit 0 with the gate's own verdict lines quoted in the PR body: check-closing-keyword-parity OK (8178 tracked files); comment-mask corpus sweep 5833 files, 0 disagree; check:doc-formula-expressions 22 record-scoped formula example(s) judged clean (two earlier runs exited 3 = PREREQUISITE NOT MET for unbuilt @objectstack/formula then @objectstack/lint — NOT MEASURED; both built under os-verify-lock.sh, VERDICT command-exit 0, then measured); check-agent-test-spelling 0 violations; doc authoring guard 393 files clean; check-nul-bytes OK (8171 text files, no raw control bytes); check-governed-merges --self-test 245 assertions; check-governed-prose 2 instruction surface(s) name all 5 registered governed surfaces and claim no others; check-skill-id-lint 23 file(s) clean; check-skill-line-ratchet .claude/skills/pm-dispatch/SKILL.md is 970 lines (ceiling 1005; headroom 35) — net 0, L723 118 bytes (was 119); check-refd-timer-probe 5828 files swept; check-skill-frame-sync 2 copies isomorphic; check-watch-hint-literal 48 declarations; check-bash32-floor 26 shell files clean; `bash -n` on the hook exit 0; guard-governed-enqueue.selftest.sh `49 passed, 0 failed`. Both edits proved on disk by anchored counts before commit (old text 0, new text 1 per file), not by editor exit code. check-governed-queue-guard is CI-measured only. No ablation (prose-only diff).",
      "mcp_calls": "2 — create_pull_request and add_issue_comment (this report); everything else went through the zero-quota page payload (issue body + comments) or repo-scoped REST (label POST + read-back, PR read-back), which was reachable from this seat",
      "open_questions": [
        {
          "question": "Line 723 names GOVERNED_REPOS without the script path, because both names do not fit the ratchet's 120-byte line budget at net 0 lines. Keep as landed, or add the path and wrap?",
          "options": [
            "A — keep as landed: `仓集读 GOVERNED_REPOS,此处不列`, 118 bytes, net 0 lines; the constant is unique in the tree and the script is named three times elsewhere in the same file",
            "B — name both and wrap line 723 into two lines (970 → 971 lines, ceiling 1005): a one-line change on this PR"
          ],
          "recommendation": "A — real need: a seat resolves GOVERNED_REPOS in one grep and the same file already names the script; long-term: one identifier drifts less than an identifier plus a path; AI-error prevention: equal either way, the pointer is unambiguous; startup focus: spends no ratchet headroom on a duplicate address"
        }
      ],
      "out_of_scope_findings": [
        "not filed (informational, already surfaced by the gate on every run): check:pm-skill-ratchet prints that two max-table-row-bytes pins could be lowered opportunistically (pm-dispatch/SKILL.md pin 642 → 555; spec-property-retirement/SKILL.md pin 328 → 326) — pin-map rows in scripts/pm/check-skill-line-ratchet.mjs, outside this card's two-line surface"
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  6. os-steve commented on Sep 3, 2026

    @os-steve
    Collaborator

    ACCEPT — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox), reviewer of record, 2026-09-03T17:59Z, verified against GitHub and a compare worktree at the PR head, not against the report.

    PR #15031 @ 16bd3e51 — draft, base main, first line Fixes #14984, two files, two lines, +2/-2, skip-changeset, no other closing keyword, no model name, one footer, no control bytes.

    Content against the triage shape (comment 5529676559): SKILL.md:723 now says the cross-repo judgement reads GOVERNED_REPOS and lists nothing here, with the (2026-08-18 裁定) date pointer verbatim — 118 bytes, no repo name, no count, no issue id; the hook comment says the one call answers for every repo configured in GOVERNED_REPOS — no count. Seat's re-run at the head: check-skill-line-ratchet 970 / 1005 (net 0), check-skill-id-lint clean, check-governed-merges --test GOVERNED ×2, bash -n clean, the hook's own self-test 49 passed, 0 failed; a grep for either old mirror over .claude, AGENTS.md, CLAUDE.md, skills returns nothing. The dev's third-mirror sweep found no other site; accepted.

    Open question: A — the constant alone is the pointer; the script is already named three times in the same file, and the 120-byte line budget would have made the second name cost a line. The deviation from the brief's wording is a measured one, recorded as a good run.

    Deviations: none otherwise. Report mcp_calls: 2 — within contract.

    CI at 17:58Z on 16bd3e51: 28 check runs, 11 success + 12 skipped, five in_progress (Lint & Repo Gates, four Type Check legs); nothing red; re-read at the next patrol and recorded here.

    Terminal: governed surface (.claude/** ×2) ⇒ the PR stays draft and lands by an approver's hand; review requested from os-zhuang and hotlong over the REST reviewers endpoint (draft bit untouched), read back. Card stays pm:dispatched until the merge closes it.


    Generated by Claude Code

  7. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p3toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions