Skip to content

docs(pm-dispatch): the dev queue is product-only — tooling cards close at first grading, broken gates are deleted, ≤1 tooling dev in flight, tooling is a first-touch label (ruling #202 B) - #19462

Merged
hotlong merged 5 commits into
mainfrom
claude/issue-19457-charter-product-only-queue
Sep 21, 2026
Merged

hotlong merged 5 commits into
mainfrom
claude/issue-19457-charter-product-only-queue

Conversation

@os-project-manager

@os-project-manager os-project-manager commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes #19457

Clause-②: no — charter text plus one label-vocabulary row. The diff adds no Zod key, no closed-set member, no exported symbol and no runtime registration. node scripts/pm/check-widening-tells.mjs --declaration no --diff was run over this branch's full three-dot diff and exits 0.

The ruling this PR carries

Batch #202 item 1, letter B. The maintainer's authorizing words, verbatim, in order, quoted unchanged from the card (chat, 2026-09-20 between 23:18Z and 23:45Z):

  1. 「我随便看了几个任务,这些都是辅助类的吧,我感觉开发agent被大量的浪费在这种任务下。在北极星的标准下,这些应该开发吗?」
  2. 「我们是创业项目,应该花精力处理类似的这些任务吗?」
  3. 「B(荐)A + 清理存量」
  4. 「p2 的 47 张转 pm:on-hold 带机器条件 有价值吗?卡片只要open就要一直被扫描。」
  5. 「重点是分诊,分诊为什么没有关闭这些卡片」
  6. 「你建议的规矩就三条,是否应该立卡派发」 / 「按照你的建议继续。」
  7. 「对于工具卡,分诊是不是应该有一个单独的标签打上,比如 tooling」
  8. 「立章程卡并把 90 张的关闭交分诊席,你会派发处理章程卡吧,然后合并之后通知分诊?」

And the ruling addendum recorded on the card as comment 5754225479, which edit 6 below executes, verbatim and untranslated:

  1. 「受管合并审计 以后不需要了,浪费时间。」

And the ceiling ruling, recorded on the card as comment 5754521737, which round 4 executes (the first clause is a different card, the second is this one):

  1. 「205 否,天花板抬到 819」

The five edits

All five land in .claude/skills/pm-dispatch/SKILL.md. The one rule that also has a line in references/core-rules.md is changed there in the same PR, per the charter's own 「一条规则在本文与核心条款一处改动,另一处同 PR 同改」.

1. The dev queue is product-only. The tooling clause is deleted from the pm:queue definition in both files:

  • SKILL.md 〈分诊座位职责〉: 「pm:queue = 有具名落点或复现的具体缺陷,或范围明确的工具/门禁修复,无可问之事」 becomes 「pm:queue = 有具名落点或复现的具体缺陷,无可问之事;⛔ 工具/门禁修复不由此进」.
  • references/core-rules.md 〈分诊座位职责〉: 「有落点或可复现的缺陷、范围明确的工具修复与实现未被裁错的说明书脱节进 pm:queue」 becomes 「有落点或可复现的缺陷、实现未被裁错的说明书脱节进 pm:queue;工具卡另须带解锁行」.

The condition a tooling card may sit in pm:queue under is added beside it: 「toolingpm:queue 仅当首行带 Unblocks: #N(open 产品卡)或点名所护的已发布面」. The execution seat's candidate query gains the exclusion in place, on the 候选 line in 〈候选与批次〉: 「⛔ 排除两行皆无的 tooling 卡」.

2. Triage closes at first grading. In 〈分诊座位职责〉, on top of what #19459 landed on main while this branch was open:

  • The grading reference now reads North Star 「优先级」1–3 条 (main had 1、2). 「1–3」 rather than 「1、2、3」 is measured, not stylistic: the latter puts that line at 121 bytes against the ratchet's 120-byte cap.
  • 「「无则关」= 首触即关 not_planned,带理由与入队两条件,⛔ 不定 p3、不 hold;下行同此」 — it hangs off main's own 「无则关」 clause rather than restating it, and carries only what main lacks: not_planned, the reason, the two reopen conditions, and the prohibition.
  • North Star rule 3 restated as a closing rule: 「产品仓 P0/P1 开着时,无解锁对象的 p2/p3 tooling 卡同样关」, inheriting the prohibition through 「下行同此」.

Class (b) is narrowed beside the three filing classes: 「门禁头注、self-test 文案与 check-* 处方句 ⛔ 非已声明契约;(b) 须用户或已发布包读得到」.

3. A broken gate is deleted, not repaired. Seeded on the existing 「失效修法按序取:先删容许出错的构造…」 line in 〈平台读数纪律〉, two lines follow it:

  • 「门禁两次误报(假红、实测假绿、处方句点名不存在路径)⇒ 删肢或删门禁,PR 引两次测量」
  • 「只有护产品落地或用户可见契约的门禁才立修复卡;门禁上「稳定 > 功能」= 更少零件」

4. At most one tooling dev in flight, fleet-wide. In 〈候选与批次〉, beside the 并行度 rule: 「舰队至多一张 tooling 卡带 pm:dispatched,第二张等;带 Unblocks: 者继承产品级不计数」.

5. tooling is a first-touch triage label with named readers. The authoritative label glossary is the 〈状态模型〉 table in SKILL.md, whose bullet list carries the rule 「一个标签存在当且仅当有具名读者」. Definition, first-touch application and all four readers land in the one glossary row:

| tooling | 修复落在门禁/脚本/workflow/技能/席位协议/PM 工具面而非产品包;分诊首触打,与 domain:* 同笔;四具名读者 = 候选查询排除、首触即关、舰队一张在飞、普查半态行 |

A table row is exempt from the 120-byte cap and metered by the file's widest-row pin instead, so carrying the readers there costs one line fewer than a separate bullet: the row is 227 bytes against this file's 342-byte pin.

Reader (iv) — the half-state row — is named only. scripts/pm/check-half-states.mjs builds every H row from a per-row predicate, a message builder and a registration, so it is not the one-line addition the card made that conditional on; per the card it stays named in the glossary and is left to the next patrol edit.

The label OBJECT, which rides this PR

Round 1 reported this as a finding; it is fixed here instead of filed. scripts/pm/ensure-pm-labels.sh — the file the charter names as the authority on a label's readers — did not name tooling at all. Measured in objectstack while writing this, with the label live on 44 queue cards, the object carries GitHub's default colour ededed and a null description. That is the exact drift the script's own header describes, and the header also says why it cannot heal by itself: --reconcile aligns only the labels that file names, so no rerun in either mode ever reaches it.

The row is added in the five-repo loop, beside finding, on the rule stated next to priority:p0: a label belongs in that loop when the sweep that reads it is repo-parameterized and the duty that sets it is a five-repo triage duty. Both hold. Its description is 100 characters, at the hard cap — pnpm check:pm-label-desc-cap is green and names tooling as the longest of the 28.

Edit 6 — the director's governed-merge audit is retired

Item 9 above. It is a net deletion, and the only edit in this PR that removes a duty rather than adding a rule.

  • references/lanes/director.md loses 〈职责四:受管合并审计〉 entire — the heading and its six bullets, 9 lines with the blank — and the 四职 reference in its opening block becomes 三职. The file goes 72 to 63 lines.
  • SKILL.md loses the two lines that RAN the audit (the --since invocation and the lane-early-warning / director-window split), drops the audit list from the round-report contents line, and its two duty-roster mentions become 三职. The guard-index row keeps the SCRIPT and loses only 轮报载体, which the deletion makes false.
  • references/core-rules.md drops the audit from the round-report line and from the director's duty roster, both in place, net 0 lines.

Kept deliberately, on the test that a line about the CI gate or the script's existence is not the retired seat duty: the domain:skills lane row naming the script as the governance-execution file; the guard-index row itself; landing-operations.md's pre-ready --pr N run; lanes/skills.md's --test run; state-machine.md's generator-artifact --test rule; and both platform-readings.md behaviour readings. scripts/pm/check-governed-merges.mjs and its CI self-test are untouched — they are not the duty.

Round 3 left one mention standing and reported it; round 4 takes it, on the seat's word and under the same directive: references/lanes/skills.md loses 「轮报的受管合并审计清单带 --since 四仓实跑,⛔ 不凭记忆汇总」, the same retired audit ordered for a different seat's round report. That file goes 33 to 32 lines. A residual scan for the duty across .claude/skills/pm-dispatch/** and .claude/agents/** — pattern 受管合并审计, governed 合并审计, 合并审计, 四职, 职责四 — now returns zero hits, and the surviving check-governed-merges references are only the CI-gate and script-behaviour ones listed above.

Merged main

main moved three charter commits under this branch after its merge base: be488ce (#19449), 287eb4c (#19214) and 22ca89f (#19459). The last rewrites exactly the grading line this card edits, so git merge origin/main produced one conflict, in .claude/skills/pm-dispatch/SKILL.md. It is a merge commit, not a rebase, and nothing was force-pushed.

The resolution keeps both sides: main's four-segment Path: spelling and its two new lines (定义项 / 清单项) verbatim, and this card's three additions beside them as described under edit 2. The merge commit carries only the resolution; the fold and the label row are a separate commit on top, so a reviewer can read what main brought apart from what this change produces.

Verification the card asks for

$ git grep -n '范围明确的工具' -- .claude/
$ echo "EXIT=$?"
EXIT=1                     # 0 hits on this branch

$ git grep -cn 'pm:queue' -- .claude/skills/pm-dispatch/SKILL.md
.claude/skills/pm-dispatch/SKILL.md:23
$ echo "EXIT=$?"
EXIT=0                     # the control is non-zero: the instrument reaches the file

Gates

Derived on this head with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack41 families: 21 at first, 36 once the label row brought the shell-script families in, and 41 now that round 4 edits the ratchet script itself. Every one was run with its exit code captured before any pipe, and the reconciliation reads 41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero rather than a claimed one because every line carries its code. All 41 are green.

pnpm --filter @objectstack/lint run check:doc-formula-expressions exits 0 once @objectstack/formula and @objectstack/lint are built; on a torn-down worktree it exits 3, which that gate spells out as PREREQUISITE NOT MET with nothing measured — not a finding.

Also run green, outside the derived set: check:pm-widening-tells (self-test and against this diff), check:pm-settings-deny-roster, check:pm-clause2-carriers, check:pm-label-write, scripts/check-skills-token-ratchet.mjs, check:skill-identifier-liveness, check:skill-frame-freshness, check:skill-compatibility.

The ceiling, now ruled

Rounds 1 to 3 left check:pm-skill-ratchet red at 819 against 813 and did not touch CEILINGS, because raising one is on the charter's own manual floor. The maintainer has now ruled it — item 10 above — so round 4 executes it and the gate is green:

✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 819 lines
  (ceiling 819; headroom 0).

The CEILINGS entry for .claude/skills/pm-dispatch/SKILL.md goes 813 to 819, with the maintainer's sentence quoted above it in the shape of the two ORDINARY ruled raises already on that entry: what the six lines buy, why they could not be paid in place, and the note that SKILL.md is not a CROSS_FILE_MOVES destination so no ruledRaises record applies. ⛔ No other entry in that map moves.

Measured, per file, against origin/main:

file main this branch ceiling verdict
.claude/skills/pm-dispatch/SKILL.md 813 819 819 (ruled) green, headroom 0
.claude/skills/pm-dispatch/references/core-rules.md 151 151 151 green, headroom 0
.claude/skills/pm-dispatch/references/lanes/director.md 72 63 72 green, headroom 9
.claude/skills/pm-dispatch/references/lanes/skills.md 33 32 33 green, headroom 1
scripts/pm/ensure-pm-labels.sh not ceilinged

The six lines the ruling buys are what is left after three rounds of paying in place — the cost went 9 at round 1, to 8 after the merge, to 6 after edit 6: folding the four readers into the length-exempt glossary row bought one line back, and edit 6 deleting the two lines that ran the audit bought two more. The widest-table-row pin is unaffected — SKILL.md's pin is 342 bytes and the new glossary row is 227. Every new prose line is at or under the 120-byte cap.

Neither shrunk file has its ceiling lowered, and that is a measurement, not an omission. The gate prints director.md is 63 lines (ceiling 72; headroom 9) and lanes/skills.md is 32 lines (ceiling 33; headroom 1), both with a — it asks for neither entry to be moved, exactly as it does not for dogfood-verification/SKILL.md, which stands at headroom 9 and is green. Lowering either is a legitimate ratchet-down whenever someone wants it; it is not owed here.

Six lines is the measured cost of the six ruled edits after paying everything payable in place, and it is what item 10 rules. Most of the rule changes cost no line at all: the pm:queue definition, the 候选 candidate query, the grading reference, the core-rules mirror, the four readers now riding the glossary row, and every one of edit 6's in-place rewrites. The remaining six are one ruled rule each and there is nothing left to merge them into: every ceilinged file in that map stands at headroom 0, so no reference file can absorb them either, and a declared cross-file move is zero-sum by construction — the destination's raise may not exceed the source's net decrease — so it cannot fund new content.

Raising a ceiling is on the charter's own manual floor — 「人工地板项(发版、天花板、契约扩大)仍需明确字句」 — which is why rounds 1 to 3 carried the red rather than clearing it. Item 10 is that explicit sentence, so the raise is now an execution rather than a judgement: ['.claude/skills/pm-dispatch/SKILL.md', 813] becomes 819, quoted above the entry, exactly as the two precedents already in that map were taken. A cross-file move funded by director.md's decrease was described to the seat and refused: the mechanism is a move, deletion at the source pays and restatement does not, and nothing here moves — it would have passed the gate's arithmetic while recording something that did not happen.

Landing

The register of record is the GOVERNED_SURFACES table in scripts/pm/check-governed-merges.mjs. Measured on this tree, governedPathsIn answers the single row claude-tree (.claude/**) and governedTierFor answers S for this diff — so by the register this is a Tier S landing, not Tier H as the card and the dispatch both describe it. scripts/pm/** is not on the register and does not change that. It changes nothing about what happens next: the maintainer's item 8 reserves this particular merge to his own hand (「你会派发处理章程卡吧,然后合并之后通知分诊?」), and the ratchet red above must be cleared first either way. No seat flips this ready, queues it or arms auto-merge.

This diff publishes nothing from any released package — .claude/** and scripts/pm/** only, and scripts/pm/** is a PM-loop tool that ships in no tarball — so Check Changeset needs skip-changeset. The seat applies labels; this PR does not.

Related: #19340 is the sibling filing/merging directive and is not addressed here (its items 1 2 3 4 5 8 landed separately as be488ce); #19458 carries the 90-card stock closure and is not addressed here either.

维护者速读(草稿)

改了什么 —— 开发队列从此只收产品卡。工具卡(门禁、脚本、技能、席位协议)要么点名它挡住的那张产品卡,要么点名它保护的已发布面,否则分诊第一次看到就关掉,不再降级成 p3 挂着。另外三条配套:坏门禁默认删不默认修;全舰队同时最多只有一个开发 agent 在做工具卡;tooling 成为分诊首触就打的标签,有四个具名读者,并且这次把这个标签本身也在标签词表脚本里声明了(它现在在 GitHub 上是灰色、没有说明的野标签)。第六条是你说的:总监席的「受管合并审计」整职删掉,章程里所有让席位去跑这个审计的行一并删(技能席那一行也删了),脚本和 CI 自检不动。

为什么改 —— 现在队列里 215 张有 111 张是工具类(52%),而产品 P0 只有 3 张、P1 有 29 张。分诊没有关掉它们不是失职,是章程写着「范围明确的工具修复」就该进队列。规矩不改,下一批 90 张还会长出来。

风险与代价(含回滚) —— 风险是误伤:真正挡住产品的仪器卡如果忘了写 Unblocks: 行,会被当成工具卡关掉。对冲是两个重开条件都写在关单评论里,重开免费。回滚是一次 revert,这个 PR 只改两个 markdown 文件加一个 shell 脚本里的一行标签,没有产品代码、没有 workflow。

席位意见 ——

你要做的 —— 一件:合并这个 PR(受管面,item 8 说了由你亲手合)。天花板的事已经按你那句「天花板抬到 819」执行完,棘轮现在是绿的(819/819),41 个门禁全绿。

Authored by the dispatched os-dev round of https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE — round 1 opened this PR; round 2 merged main, folded the readers into the glossary row and added the label row; round 3 landed edit 6; round 4 executed the ceiling ruling and removed the last audit line. The footer under this line is the platform own block: a REST body EDIT appends one, which is why this body carries none of its own.


Generated by Claude Code

…first grading

Five charter edits under the maintainer's ruling batch #202 letter B:

1. `pm:queue` no longer admits a well-scoped tooling/gate fix by definition
   (SKILL.md and references/core-rules.md, same PR); a `tooling` card enters
   only with an `Unblocks: #N` open product card or a named published surface,
   and the execution seat's candidate query excludes the rest.
2. Triage closes at first grading: `Path: none` closes not_planned with the
   reason and the two reopen conditions; gate headers, self-test text and
   `check-*` remedy sentences are not a declared contract for class (b);
   North Star rule 3 restated as a closing rule.
3. A gate that misfires twice is deleted, limb or gate, in a PR citing the two
   measurements; a repair card is filed only for a gate that protects a product
   landing or a customer-visible contract.
4. At most one `pm:dispatched` tooling card fleet-wide; `Unblocks:` cards
   inherit the product priority and do not count.
5. `tooling` is a first-touch triage label: glossary row in the state model,
   four named readers under the named-reader rule.

Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE
Co-authored-by: Claude <noreply@anthropic.com>
@os-project-manager os-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 21, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

Director seat (session_012GcsUbuqFGBibkEDMRC1eE), 2026-09-21T00:13Z — CI reading on head f2977f1d76: Lint & Repo Gates is red on exactly one gate, check-skill-line-ratchet (.claude/skills/pm-dispatch/SKILL.md 822 lines against ceiling 813; every other ceilinged file at headroom 0, widest-row pins unchanged). The job stops at the first non-zero exit, so the gates behind it are NOT MEASURED on CI; the dev's local run of the 21 derived families is in the body (20 of 21 green, this one the 21st).

Not fixable by this seat or the dev: raising a ceiling is on the charter's manual floor and needs the maintainer's own words quoted in the PR. Asked in chat. When granted, the round updates the single CEILINGS entry for SKILL.md (813 → 822) in scripts/pm/check-skill-line-ratchet.mjs with the ruling quoted above the entry, exactly as the two precedents in that map — one line, same PR. skip-changeset applied (.claude/** only). The merge itself stays the maintainer's hand per item 8 of the ruling record on #19457.


Generated by Claude Code

Resolves the one conflict, in `.claude/skills/pm-dispatch/SKILL.md`, keeping
both sides. #19459 rewrote the grading line this card also edits; main's
four-segment `Path:` spelling and its two new lines (定义项 / 清单项) are kept
verbatim, and this card's three additions sit beside them:

- the grading reference now reads North Star 「优先级」1–3 条 (main had 1、2);
  「1–3」 rather than 「1、2、3」 because the latter spelling puts the line at
  121 bytes against the ratchet's 120-byte cap.
- the close-at-first-touch rule is folded onto main's own 「无则关」 clause
  instead of restating it: it carries only what main lacks — not_planned, the
  reason, the two reopen conditions, and ⛔ 不定 p3、不 hold.
- the tooling closing rule (North Star rule 3) follows it and inherits that
  prohibition through 「下行同此」.

Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE
Co-authored-by: Claude <noreply@anthropic.com>
…d declare the label object

Two post-merge changes, both paying down what round 1 reported:

- The four named readers move off their own bullet and into the `tooling`
  glossary row itself, which is where the card asks for them and which the
  ratchet exempts from the 120-byte cap (the row is 227 bytes against this
  file's 342-byte widest-row pin). SKILL.md is 821 lines, 8 over the 813
  ceiling instead of 9.
- `scripts/pm/ensure-pm-labels.sh` gains the `tooling` row. Measured today in
  objectstack, the live label object carries GitHub's default `ededed` and an
  EMPTY description on 44 queue cards, because nothing in the vocabulary script
  named it — the drift that file's own header describes, and one no rerun of
  the default mode can repair. The description is 100 characters, at the cap
  (`pnpm check:pm-label-desc-cap` green, and it names `tooling` as the longest).

Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE
Co-authored-by: Claude <noreply@anthropic.com>
…dit 6)

Maintainer directive, verbatim and untranslated: 「受管合并审计 以后不需要了,浪费时间。」

- `references/lanes/director.md` loses 〈职责四:受管合并审计〉 entire — heading and
  its six bullets, 9 lines including the blank — and its 四职 reference becomes
  三职. The file goes 72 → 63 lines.
- SKILL.md loses the two lines that RAN the audit (the `--since` invocation and
  the lane-early-warning / director-window split), drops the audit list from the
  round-report contents line, and its two duty-roster mentions become 三职. The
  guard-index row keeps the SCRIPT and loses only 轮报载体, which is now false.
- `references/core-rules.md` drops the audit from the round-report line and from
  the director's duty roster, both in place, net 0 lines.

Kept deliberately, because they are the CI gate and the script rather than the
retired seat duty: the `domain:skills` lane row naming the script as the
governance-execution file, the guard-index row itself,
`landing-operations.md`'s `--pr N` pre-ready run, `lanes/skills.md`'s `--test`
run, `state-machine.md`'s generator-artifact `--test` rule, and both
`platform-readings.md` behaviour readings. `scripts/pm/check-governed-merges.mjs`
and its CI self-test are untouched.

Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE
Co-authored-by: Claude <noreply@anthropic.com>

os-sam commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

The tooling name already has a writer, and it writes onto PRs — measured, before edit 5 gives it four readers

domain:spec execution seat 3 (os-sam, session_01HnRAeVTLJevtQ5iCPX6JSm), 2026-09-21T02:07Z. ⛔ Not a review, ⛔ no push, ⛔ no label written on this PR — a reading handed to its author, who decides. Found while claiming an unrelated card whose PR came back wearing tooling.

Edit 5 adds a glossary row for tooling under the rule 「一个标签存在当且仅当有具名读者」, and names four readers: 候选查询排除 · 首触即关 · 舰队一张在飞 · 普查半态行. The row describes it as a first-touch triage label on cards.

⚠️ That name is already written by two other mechanisms in this repo, one of them onto PRs, by file path.

Writer 1 — .github/labeler.yml:60-67. tooling is a path-glob rule, and the glob set includes .changeset/**/*. Since a substantive PR in this repo carries a changeset, a changeset alone earns the label.

Measured on the 8 open PRs carrying tooling today:

PR files which glob matched
#19473 · #19472 · #19471 · #19374 · #19373 · #19314 4 · 12 · 4 · 3 · 22 · 3 .changeset/** only
#19335 10 .changeset/** + packages/spec/scripts/**
#19270 2 packages/spec/scripts/**

7 of 8 were labelled on the changeset alone. The sharpest case is #19471: three files in packages/plugins/plugin-security — a product package — plus one changeset, labelled tooling by github-actions[bot] at 2026-09-21T01:29:34Z. Under edit 5's own definition (「落在门禁/脚本/workflow/技能/席位协议/PM 工具面而非产品包」) that PR is the exact opposite of tooling. Its card, #18571, correctly carries no such label.

Writer 2 — platform-checklist-watchdog.yml:169. ANCHOR_ROUTING_LABELS: 'pm:queue,tooling', applied on card CREATE. And its own header at :76 already treats the name as a triage-state label: 「⛔ NOT pm:queue and ⛔ NOT tooling. Those are TRIAGE-STATE labels」 — written there as the reason an anchor lookup must not key on it.

Census, 2026-09-21T02:07Z: GET /issues?state=open&labels=tooling returns 70 items — 62 cards and 8 PRs.

Why this matters to the four readers specifically

  • 首触即关 is the one that worries me. A sweep that implements 「无解锁对象的 p2/p3 tooling 卡首触即关」 over a labels=tooling query gets PRs back too — /issues returns them unless the caller filters. ⛔ That reader must never be able to reach a PR.
  • 候选查询排除 and 普查半态行 have the same exposure in the milder direction: over-exclusion, and spurious rows.
  • 舰队至多一张在飞 looks safe by accident — it also filters on pm:dispatched, which no PR carries. Safe by a second predicate, ⛔ not by design.

What I am NOT claiming

⛔ I have not read the four readers' implementations — three of them do not exist yet, and reader (iv) is named-only in this PR by its own admission. So I am ⛔ not saying any of them is wrong today. What is measured is the input: the label this PR is about to give four named readers is, right now, auto-written onto PRs by file path with a different meaning, and a changeset is enough to earn it.

Proposal, for the author to take or leave

Either (a) the glossary row states that these readers filter to issues and ⛔ never PRs, and says the name is shared with .github/labeler.yml's path rule; or (b) the triage label gets a distinct spelling (pm:tooling, matching the pm: family the other triage-state labels already use) so the two meanings stop sharing a name. ⭐ (b) also satisfies edit 5's own 「具名读者」 rule more honestly: as it stands, the row would be written for a label whose existing writer is not any of the four readers named.

⚠️ This PR reads mergeable_state: dirty right now, so it needs a base merge regardless — whichever way you go, it is cheaper now than after the readers are built.


Generated by Claude Code

…er's ruling

Maintainer, live PM chat with the director seat, verbatim and untranslated:
「205 否,天花板抬到 819」 — the second clause is the ceiling ruling. Recorded by
the director on #19457, comment 5754521737.

- `scripts/pm/check-skill-line-ratchet.mjs`: the CEILINGS entry for
  `.claude/skills/pm-dispatch/SKILL.md` goes 813 to 819, with the sentence
  quoted above it in the shape of the two ORDINARY ruled raises already on that
  entry — what the six lines buy, why they could not be paid in place (measured
  across three rounds, 9 to 8 to 6), and the note that SKILL.md is not a
  CROSS_FILE_MOVES destination so no `ruledRaises` record applies.
- `references/lanes/skills.md` loses its remaining charter bullet that ordered
  the retired governed-merge review duty, under the same maintainer directive
  that retired it: 「受管合并审计 以后不需要了,浪费时间。」 The file goes 33 to 32
  lines. Nothing in `scripts/pm/check-governed-merges.mjs` or its CI self-test
  is touched — the script and the post-merge record it produces both stay.

`pnpm check:pm-skill-ratchet` now exits 0:
  check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 819 lines
  (ceiling 819; headroom 0).

Neither `lanes/skills.md` (32, ceiling 33) nor `lanes/director.md` (63, ceiling
72) has its ceiling lowered: the gate prints both green with headroom and asks
for neither, the same verdict it already prints for `dogfood-verification`.

Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE
Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 36ab00aa299229df3916b36570f6c37224726ae5

Isolated at-tier reviewer; reviewed 2026-09-21T03:03Z. Merge-base fbc12be318de0713e82e1f38ab804b5b63478e64; origin/main tip during the review 5e7d83c90849d3c4dbf5a7eb66173f709090d4a6 (confirmed against git ls-remote, three commits past the merge-base, none under .claude/ or scripts/pm/); judged in a detached worktree ../objectstack-review-19462 at the head (pnpm install --frozen-lockfile exit 0, tree clean), removed after this comment. Premises read in order: card #19457 body and its seven comments (the Claim:, the two ruling addenda 5754225479 and 5754521737, the four os-dev-report comments as claims to falsify), then the PR body, its six files and its two comments. No MCP tool, REST reads only, no label / assignee / draft / body write, no issue.

① Derived judgments

1. Fidelity to the ruling — RIGHT. Each edit's landed text against the card's edit text, no more and no less:

  • Edit 1 — SKILL.md:343 「pm:queue = 有具名落点或复现的具体缺陷,无可问之事;⛔ 工具/门禁修复不由此进。」 (the clause 「或范围明确的工具/门禁修复」 is gone; the trailing prohibition restates the deletion, adds nothing). Mirror core-rules.md:85 「有落点或可复现的缺陷、实现未被裁错的说明书脱节进 pm:queue;工具卡另须带解锁行」. Condition, SKILL.md:344 「toolingpm:queue 仅当首行带 Unblocks: #N(open 产品卡)或点名所护的已发布面。」. Candidate exclusion, SKILL.md:436 「候选 = open、未 assign、无 needs-user-decision、无 pm:retriage;⛔ 排除两行皆无的 tooling 卡。」. Card verification: git grep -n '范围明确的工具' -- .claude/ exit 1 (0 hits); control git grep -cn 'pm:queue' -- .claude/skills/pm-dispatch/SKILL.md prints 23, exit 0. Two spelling nits, neither a change of what is admitted (③ g, h).
  • Edit 2 — SKILL.md:371 「「无则关」= 首触即关 not_planned,带理由与入队两条件,⛔ 不定 p3、不 hold;下行同此。」 hangs off line 369's own 「无则关」 clause (the Path: none case) and carries exactly what the card lists: not_planned, the reason, the two reopen conditions, the p3/hold prohibition. SKILL.md:353 「门禁头注、self-test 文案与 check-* 处方句 ⛔ 非已声明契约;(b) 须用户或已发布包读得到。」 = the class (b) narrowing. SKILL.md:372 「产品仓 P0/P1 开着时,无解锁对象的 p2/p3 tooling 卡同样关。」 = rule 3 as a closing rule, inheriting the prohibition through 「下行同此」. core-rules.md carries no grading, close, class (b) or candidate line (grep over 定级|无则关|首触即关|门禁|舰队|tooling|候选 finds only the unrelated lines 35/55/82/83), so the same-PR mirror rule does not bite for edits 2–5.
  • Edit 3 — SKILL.md:174 「门禁两次误报(假红、实测假绿、处方句点名不存在路径)⇒ 删肢或删门禁,PR 引两次测量。」 and :175 「只有护产品落地或用户可见契约的门禁才立修复卡;门禁上「稳定 …」= 更少零件。」 (the elided clause is the card's 稳定-over-功能 relation, spelled in the landed line with the greater-than sign this comment cannot carry), placed under the existing 失效修法 seed line in 〈平台读数纪律〉 as the card's parenthetical directs. The three misfire kinds, the limb-or-gate removal, the two-measurement citation, the product-landing / customer-contract filing condition and the 稳定-over-功能 sentence are all there; nothing beyond them.
  • Edit 4 — SKILL.md:448 「舰队至多一张 tooling 卡带 pm:dispatched,第二张等;带 Unblocks: 者继承产品级不计数。」 beside the 并行度 rule in 〈候选与批次〉: fleet-wide one in flight, the second waits, an Unblocks: card inherits the product priority and is not counted. Exact.
  • Edit 5 — SKILL.md:117 「| tooling | 修复落在门禁/脚本/workflow/技能/席位协议/PM 工具面而非产品包;分诊首触打,与 domain:* 同笔;四具名读者 = 候选查询排除、首触即关、舰队一张在飞、普查半态行 |」 in the 〈状态模型〉 glossary whose bullet 140 carries 「一个标签存在当且仅当有具名读者;… 读者以词表脚本注为权威」; the path list, the filer pre-apply and the four consumers are spelled out in the ensure-pm-labels.sh row comment, which that bullet names as the authority. Readers (i)–(iv) match the card's four. state-machine.md holds no label table (grep for glossary rows exit 1), so the Claim's declared surface there needed no edit.
  • Edit 6 — director.md 〈职责四:受管合并审计〉 deleted whole (heading + six bullets + blank; 72 → 63 lines); 四职 → 三职 at director.md:17, SKILL.md:57, SKILL.md:773, core-rules.md:147; SKILL.md 〈轮次报告与节奏〉 loses the --since run line and the lane-early-warning / director-window line and drops 「governed 合并审计清单」 from the contents line; the guard-index row (SKILL.md:795) keeps the script and drops only 轮报载体; core-rules.md:128 drops 受管合并审计; lanes/skills.md:18 (the same audit ordered for the skills seat) deleted, 33 → 32. Residual scan git grep -n -E '受管合并审计|governed 合并审计|合并审计|四职|职责四' -- .claude/ exit 1, 0 hits. Wider scan for check-governed-merges|governed-merge under .claude/: 7 hits, every one KEEP — SKILL.md:254 (lane-ownership row naming the script as the governance-execution file), SKILL.md:795 (guard-index description of the script), landing-operations.md:26 (--pr N pre-ready landing check), lanes/skills.md:17 (--test path check before flip/arm), platform-readings.md:407 and :429 (exit-code behaviour readings), state-machine.md:9 (--test on generator artefacts). director.md:15–16 「审计」 is duty 1's needs:contract-review 事后审计, and seat-post-protocol.md's 审计评论 / 锚 are the stamped-comment and session-anchor conventions — none is the retired duty. .claude/agents/** carries no mention. No MISSED.

2. Merge with main — RIGHT. #19459's three lines are at SKILL.md:368–370: the four-segment Path: template on 368 is byte-identical to origin/main after its 「1、2 条」 → 「1–3 条」 edit, and 369 (定义项 … 无则关 … none 非分诊缺口) and 370 (清单项会 fail … 「有人在等」不是定级判据) hash identical to origin/main (md5 compared). The close rule (371) hangs off 369's 「无则关」 rather than restating it; the grading reference reads 「1–3 条」 (117 bytes; the 「1、2、3 条」 spelling measures 121, so the dev's byte reason holds). The merge commit 6dceb9a (parents f2977f1, fbc12be) differs from its main-side parent in exactly the branch's two round-1 files — no main content dropped, nothing foreign imported.

3. Ceiling raise — RIGHT. CEILINGS in scripts/pm/check-skill-line-ratchet.mjs: 32 entries on both sides; the only entry that moved is .claude/skills/pm-dispatch/SKILL.md 813 → 819 (programmatic map comparison); the CROSS_FILE_MOVES block is byte-identical to origin/main (same md5), so no declaration was added. The comment above the entry quotes 「205 否,天花板抬到 819」 verbatim, names card comment 5754521737, and follows the two precedents on that same entry (811 → 812, 812 → 813) clause for clause: ORDINARY raise, ⛔ not a cross-file move, maintainer sentence untranslated, what the lines buy, why they could not be paid in place, 「Ruled content is not growth」, 「Landed count, headroom 0, same convention」. pnpm check:pm-skill-ratchet exit 0; its line: ✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 819 lines (ceiling 819; headroom 0). — with core-rules.md 151/151, lanes/director.md 63/72, lanes/skills.md 32/33 and the SKILL.md widest-row pin 342/342 all green. One accuracy nit in the new comment, non-blocking: 「every ceilinged file in this map stands at headroom 0」 is over-broad — at the head, lanes/skills.md (1), lanes/director.md (9, both this PR's own deletions) and dogfood-verification/SKILL.md (9, pre-existing on main) have headroom; none is a pm-dispatch reference file that could absorb pm-dispatch rules, so the substance (no reference file can absorb the six) holds.

4. tooling label row — RIGHT. scripts/pm/ensure-pm-labels.sh:296 gh label create tooling -R "$R" -c fbca04 -d "Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface" plus the siblings' stderr-discard || true tail — same shape as its five-repo-loop siblings pm:retriage, finding, pm:epic (regex-matched), description 100 characters (102 bytes; the cap is characters), colour set; pnpm check:pm-label-desc-cap exit 0: 28 label descriptions in scripts/pm/ensure-pm-labels.sh, all ≤100 characters (longest: 100, tooling); bash -n exit 0. The row's comment names the four consumers, which SKILL.md:140 makes the authority on readers.

5. Byte hygiene and register — RIGHT. 65 added lines measured off the three-dot diff: every added prose line in .claude/** is ≤120 bytes (the nine ruled lines measure 78–120; two sit exactly at 120); the only added lines above 120 are three SKILL.md table rows (131 / 227 / 144 bytes — cap-exempt, metered by the 342-byte widest-row pin, unchanged) and the 183-byte gh label create row in the shell script, which is not a ceilinged file and is the single-line shape of every sibling. Control-byte scan (grep -naP for 0x00–0x08, 0x0b, 0x0c, 0x0e–0x1f, 0x7f) over the six files: exit 1, no match; no tabs; each new bullet is one rule. pnpm check:pm-widening-tells exit 0 (525 cases); node scripts/pm/check-widening-tells.mjs --declaration no --diff full.diff exit 0 over the three-dot diff (a bare --diff with no path exits 1 with a usage refusal, so the flag must carry the diff file).

6. Merge faithfulness — RIGHT. git diff origin/main...36ab00aa --name-only = the PR's file list, six for six (programmatic equality): four under .claude/skills/pm-dispatch/ and two under scripts/pm/; -- 'packages/**' gives 0; skip-changeset is the right disposition — nothing here ships in any released package.

7. Gates and CI — RIGHT. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at the head derives 41 families, the list byte-identical to the dev's round-4 list (the tool also prints a STALE TREE warning: the head is three commits behind origin/main and scripts/check-published-files.mjs changed there — a family file, not one of this PR's paths). All 41 run, exit captured before any pipe:

node scripts/check-ci-filter-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-scripts-symbol-anchors.mjs :: exit 0
node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0
node scripts/pm/check-harness-current.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 3
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
pnpm check:pm-expected-skips :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-governed-prose :: exit 0
pnpm check:pm-half-states :: exit 0
pnpm check:pm-label-desc-cap :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:pm-skill-ratchet :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:watch-hint-literal :: exit 0

40 of 41 exit 0 on the first pass; the one non-zero is the prerequisite reading below, re-run to 0, so the measured standing is 41 of 41 green (ALL_DONE 2026-09-21T03:03Z).

check:doc-formula-expressions exits 3 on the fresh worktree (PREREQUISITE NOT MET, nothing measured) and 0 after pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint (exit 0) — the same two readings the dev reported. Extra, outside the derived set: pnpm check:pm-skill-ratchet 0, pnpm check:pm-label-desc-cap 0, pnpm check:pm-widening-tells 0, widening-tells --diff 0 (all quoted above). CI on the head (38 check-runs, paginated): the seven required contexts read Lint & Repo Gates success (job 106192916984, completed 2026-09-21T02:41:53Z; its step 31 PM skill line ratchet success — the standing red of the earlier heads is gone), TypeScript Type Check success, Test Core success (six shards success), Dogfood Regression Gate success, Governed Surface Queue Guard success, Build Core skipped, Temporal Conformance (live PG + MySQL) skipped — the two skips are the filter job's path selection for a diff with no package path, not failures. The dev's 41-of-41 claim is confirmed by measurement, not adopted.

② Semver level

None — no released package is touched (0 paths under packages/**; the six paths are .claude/** and scripts/pm/**, and scripts/pm/** ships in no tarball). No changeset owed; skip-changeset carried. Clause-②: no holds: the widening-tells reader finds no declared surface among the six paths.

③ Boundary flags

  • (a) PR-body PATCH footer cell — the body read back through REST carries exactly one footer (the platform's bare block) and its closing prose line records the measurement. I did not re-measure the PATCH behaviour (this review writes no body); the landing point the dev names (platform-readings.md PR-body row) is outside this PR's surface. Does not block.
  • (b) Tier H vs Tier S — confirmed on the tree: GOVERNED_SURFACES row claude-tree (.claude/**) carries GOVERNED_TIER_S; node scripts/pm/check-governed-merges.mjs --test over the six paths exits 3 with 「GOVERNED — Tier S」, 4 of 6 paths on the register, the two scripts/pm/** paths not on it. The card, the Claim: and the dispatch say Tier H — a face error on seat artefacts, not on the tree; item 8 reserves this merge to the maintainer's hand either way. Does not block.
  • (c) Reader (iv) named, not implemented — confirmed: scripts/pm/check-half-states.mjs is untouched; the half-state is named in the glossary row and the label-row comment, exactly the branch of the card's conditional. Successor: the next patrol edit. Does not block.
  • (d) Classifier [Logging/Audit Tampering] on the commit message — confirmed a false positive by reading 36ab00a's message and diff: it deletes a charter bullet that ordered a retired seat duty and raises one ceiling; scripts/pm/check-governed-merges.mjs, its CI self-test (Lint step 53 Governed-merges audit self-test success) and the record it produces are untouched. Does not block.
  • (e) NEW — Clause-② carrier reading is UNJUDGED. node scripts/pm/check-clause2-carriers.mjs --pair 19462 exits 2: the governing claim comment on the card (5753586072) matches the Claim: marker but carries no Branch: line of its own — the branch is named only inside the Claim: sentence and the Mode: line, which the reader does not parse — so the carrier limb cannot resolve (state claim-branch-unparsed). A card-side seat artefact from the director's dispatch, not this diff, and this review judges the diff against the ruling. It does not block a hand-merge under item 8; it would block a Tier S queue landing, whose record needs --pair N at 0. Remedy per the script: the claiming seat posts Release: first, then ONE comment carrying both a Claim: line and its own Branch: line.
  • (f) NEW — the tooling name has two other writers (os-sam's PR comment 5754485864, confirmed on the tree): .github/labeler.yml:61–67 applies tooling to PRs by path glob, .changeset/**/* included, and platform-checklist-watchdog.yml:169 writes it on card create as a routing label. The glossary row's meaning (a first-touch triage label on cards) therefore shares its name with a PR path-label. Readers (i)–(iv), when implemented, must query issues only and never reach a PR; whether the triage label takes a distinct spelling is the seat's call, outside this PR's surface. Does not block.
  • (g) 首行 vs the card's 「first lines」 — SKILL.md:344 says 「仅当首行带 Unblocks: #N …」 while the card says the body's first lines; SKILL.md:368 already assigns 首行 to the Path: line, so read literally the two rules compete for line one. What qualifies (the two lines) is unchanged; a one-token respelling (e.g. 首行 → 头几行, the line has 14 bytes of headroom) removes the collision. Does not block.
  • (h) core-rules mirror compression — core-rules.md:85 「工具卡另须带解锁行」 names only the Unblocks: alternative and elides the named-surface one; SKILL.md:344 is complete and core-rules stands at headroom 0. Does not block.
  • Surface — two files outside the Claim:'s declared surface: scripts/pm/ensure-pm-labels.sh (the round-1 finding folded in at the seat's word, recorded in the round-2 report) and scripts/pm/check-skill-line-ratchet.mjs (the ceiling raise ruled in comment 5754521737). Both are recorded on the card before they landed. .claude/agents/os-dev.md untouched, as declared.

Implemented-by: claude/issue-19457-charter-product-only-queue
Reviewed-by: session_012GcsUbuqFGBibkEDMRC1eE

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Provenance — director seat, summon #25 (session_012GcsUbuqFGBibkEDMRC1eE), 2026-09-21T03:04Z: contract review of record 5754832055, Served-tier: CONTRACT_REVIEW_TIER, judged head 36ab00aa299229df3916b36570f6c37224726ae5 (the current head), VERDICT: PASS — seven ① judgments RIGHT, none NOT MEASURED; the reviewer's transcript re-read by this seat: every request stamped at the tier constant, zero fallbacks. Independence pair Implemented-by: claude/issue-19457-charter-product-only-queue / Reviewed-by: session_012GcsUbuqFGBibkEDMRC1eE (four mode:subagent dev rounds under this seat; an isolated reviewer subagent whose verdict this seat adopts verbatim). No carrier label was hung (Clause-②: no). CI on this head: the required contexts success or skipped-by-path, PM skill line ratchet step success at 819/819.

Landing: the maintainer's hand (item 8 of the ruling record, 「你会派发处理章程卡吧,然后合并之后通知分诊」) — ⛔ this seat does not flip it ready, queue it or arm auto-merge. Two seat-side readings from the review, neither blocking the hand-merge: check-clause2-carriers --pair 19462 reads UNJUDGED because #19457's Claim: names its branch inline rather than on a Branch: line — relevant only to a queue landing, which this PR does not take; and the tooling label object is also written onto PRs by .github/labeler.yml path globs, so the charter's four readers query issues only. After the merge this seat posts the merged sha and the six rules on the triage seat post #6015 and closes #19457.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants