Skip to content

pm-dispatch: an open PR names its owner — pr_create assigns the claim's Account, a hand-over re-assigns it, and H76 reports an unowned PR - #30796

Draft
objectstack-fleet[bot] wants to merge 2 commits into
mainfrom
claude/project-thread-jhzb1f
Draft

objectstack-fleet[bot] wants to merge 2 commits into
mainfrom
claude/project-thread-jhzb1f

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

domain:skills#1 · os-elon-musk · session_016WV4d1MNyKiUFDBccFX8Cc

Fixes #29828

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:按裁决 A 落地「PR 挂处理人」。开发经中继 pr_create 开 PR 时就把处理人挂成认领评论里的 Account:(即卡的处理人);换手(兄弟席接管、释放、改派)与认领变更同笔改 PR 处理人;交棒请审时仍 ⛔ 不请席位自己的账号。半状态巡查新增 H76 行:开着的、非信箱 PR 没有处理人就列出来(只报告,不是门禁)。
  • 为什么改:原规则禁止 PR 挂处理人,实测今天开着的 52 个 PR 里 0 个有处理人,GitHub 列表上看不出谁负责,排查闲置 PR 只能逐个读评论。
  • 风险与回滚:只动 .claude/** 规则文本(Tier S)与 scripts/pm/ 巡查工具,不发布(skip-changeset)。中继本来就支持 pr_create 带 assignees,不改代码。H76 不写任何标签、处理人或评论,不挡合并。各规则文件行数不变、每行仍在 120 字节内,未抬任何上限。回滚即 revert。
  • 先看:① .claude/skills/pm-dispatch/references/execution-duties.md 认领节第 69 行(规则本体与换手同笔);② scripts/pm/check-half-states.mjs 的 h76OpenPrWithoutAssignee 与 sweep 里的接线;③ .claude/skills/pm-dispatch/SKILL.md 第 198–199 行(交棒请审只留「不请席位自身账号」)。
  • 两分钟验证:node scripts/pm/check-half-states.mjs --self-test,末行电池清单里应见 H76 unowned open PR 12/11,并以 0 退出。
  • 需要你做的:无,席位落地(Tier S,席位达档复核 PASS 且全绿后落地;存量 PR 的一次性补挂由席位在规则落地后经中继执行)。

What changes

Ruling record 6674390671 (letter A, maintainer 「按推荐裁」) on #29828, encoded in the three texts it names plus every restatement found under .claude/**:

  • .claude/agents/os-dev.md rule 2 (L38): the PR's assignee is the claim's Account:, carried by pr_create; a hand-over is the seat's. Was 「PR ⛔ 不带 assignees(中继虽收),席位交棒才派」.
  • .claude/agents/os-dev.md write budget (L62): pr_create carries labels and assignees. Was 「⛔ 无 assignees」.
  • .claude/skills/pm-dispatch/references/execution-duties.md 认领 (L69; L67 at 1371b9d8): the PR is assigned to the claim's account and a hand-over re-assigns it in the same stroke. Was 「PR ⛔ 不派席位账号」.
  • .claude/skills/pm-dispatch/SKILL.md hand-off step ③ (L198-199): 「⛔ 永不派/请席位自身账号」 keeps only the request half (the ruling replaces the assign half alone; a review request to the seat's own account stays forbidden); 「pr_create 不请不派」 becomes 「pr_create 派不请」.
  • .claude/skills/pm-dispatch/references/dispatch-runbook.md (L140): a card thread does not write the CARD's assignee — the PR's is now written by its pr_create.
  • scripts/pm/check-half-states.mjs: row H76, report-only, band state (H1's): an open PR with no assignee, a pm:mailbox PR excepted. The mailbox label is mailbox-open.mjs's MAILBOX_LABEL, imported lazily as H72 does; when it cannot be loaded every unowned PR fires UNJUDGED, never clean. Drafts are included. Free: it reads the open listing the sweep already holds. ⛔ No gate wiring.

Every file keeps its line count (each edit rewrites its own line in place) and every touched line stays within the 120-byte line budget; no ceiling moved.

Tests

  • node scripts/pm/check-half-states.mjs --self-test: new battery H76 unowned open PR (12 cases, pin 11): the firing, a ready PR firing like a draft, the assignee exemption (list field and legacy single field), the mailbox exemption beside its unlabelled twin, closed and missing rows, the unread-label UNJUDGED leg for null / undefined / empty, an assigned PR staying clean with the label unread, not-loud, band + emitted, the lazy-import wiring, the roster floor.
  • Live probe (read-only, offline predicate over a REST listing, 2026-11-06 ~03:05Z): 52 open PRs, 0 assigned, 13 unassigned pm:mailbox PRs exempt, 39 would fire H76.

Acceptance notes

Verification

Local subset, recorded as COMMAND :: exit CODE and reconciled with node scripts/pm/dispatch-gates.mjs --ran FILE --repo objectstack-ai/objectstack:

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No schema touched.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (Publishes nothing: skip-changeset.)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (Not touched.)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

…'s Account, a hand-over re-assigns it, and H76 reports an unowned PR

Ruling record 6674390671 (letter A) on #29828. The dev's pr_create now
carries the claim's Account as the PR's assignee (os-dev.md rule 2 and the
write budget), the 认领 line in execution-duties.md says a hand-over
re-assigns it in the same stroke as the claim change, SKILL.md's hand-off
step keeps only "never request the seat's own account", and the runbook's
thread rule names the card's assignee. check-half-states gains H76, a
report-only state row: an open non-mailbox PR with no assignee.

Claude-Session: https://claude.ai/code/session_016WV4d1MNyKiUFDBccFX8Cc
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 6, 2026
@github-actions github-actions Bot added the size/s label Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-elon-musk · session_016WV4d1mkgEris4MHHLjtnk

Contract-review-claim: session_016WV4d1mkgEris4MHHLjtnk · head bcc6eae · 2026-11-06T03:29Z

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-elon-musk · session_016WV4d1mkgEris4MHHLjtnk

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: bcc6eae49a5b2cc2f77bda6357918830f0210e67
Local-runs: probe — scratch worktree at the head sha ran node scripts/pm/check-half-states.mjs --self-test, check-skill-line-ratchet.mjs (self-test + run), check-governed-prose.mjs and check-skill-id-lint.mjs to verify the H76 battery pin and the ceilings offline; worktree removed afterwards
Scope: hits = .claude/agents/os-dev.md, .claude/skills/pm-dispatch/SKILL.md, .claude/skills/pm-dispatch/references/dispatch-runbook.md, .claude/skills/pm-dispatch/references/execution-duties.md, scripts/pm/check-half-states.mjs + cited = scripts/pm/mailbox-open.mjs, scripts/pm/check-skill-line-ratchet.mjs, scripts/pm/fleet-write/ops.mjs, AGENTS.md, .github/workflows/half-state-patrol.yml, .claude/skills/pm-dispatch/references/state-machine.md

① Derived judgments

  1. Assign at pr_create = the claim's Account — CORRECT. os-dev.md:38 「PR 派认领 Account:(pr_create 带)」 and os-dev.md:63 「pr_create(draft,含 labels 与 assignees)」 replace 「⛔ 不带 assignees」/「⛔ 无 assignees」; execution-duties.md:69 「PR 派认领账号」; SKILL.md:199 「pr_create 派不请」. The relay already accepts it: ops.mjs:2091 lists assignees as optional on pr_create, and the second-leg mechanism is ops.mjs:58-70.
  2. Hand-over re-assigns in the same stroke — CORRECT. execution-duties.md:69 「交棒同笔改」; os-dev.md:38 「交棒归席位」 (the dev's side: the hand-over is the seat's, not the dev's). The card hand-over step execution-duties.md:92 (② assignee 同笔换人) is unchanged and now covered by L69's PR clause.
  3. Reviewer request to the seat's own account still forbidden — CORRECT. SKILL.md:198 keeps 「⛔ 永不请席位自身账号」, dropping only the 派 half; the author-is-approver assign fallback stays.
  4. Tier S/H landing rules unchanged — CORRECT. SKILL.md:197 (③ 请审 = 席位交棒) and SKILL.md:203 (Tier S = .claude/**, 余皆 Tier H) are untouched; the diff touches only L198-199.
  5. "dev never writes the CARD assignee" preserved — CORRECT. os-dev.md:36-38 「卡 assignee 归 PM … 恒不写卡的 assignee」, execution-duties.md:69 「卡 assignee 归 PM,dev 恒不写」, dispatch-runbook.md:140 now says 「不写卡 assignee」 (narrowed from a bare 「不写 assignee」 that would have contradicted the ruling). SKILL.md:29/104-106, state-machine.md:41 card-assignee rules unchanged and consistent.
  6. No in-surface leftover — CORRECT. git grep of .claude/** at head for assignee|不派|派席位|不带 \assignees`|无 assignees|交棒才|席位自身finds no line saying a PR carries no assignee; every remainingassigneehit is a card/seat-post rule (e.g.SKILL.md:29,92-110, seat-post-protocol.md:11, platform-readings.md:170-171,236which are read-channel caveats, not the rule).dispatch-runbook.md:138(线程 ⛔ 不做 harness 开 PR 后指派/请审) forbids the harness's post-open assign, which the relay-carriedassignees` does not use — not a contradiction.
  7. Out-of-surface restatements still carry the old rule, as the PR declares: AGENTS.md:403-404 ("nor the PR's: pr_create carries none; the seat sets it at hand-over") and ops.mjs:103-105, ops.mjs:2093-2094 ("a dev's create carries no assignees"). Not edited here — carrier items, correctly left out of a Tier S diff.
  8. H76 report-only — CORRECT. check-half-states.mjs H76 predicate (the new block after h72OffTableMailboxLines) returns a string or null and writes nothing; the sweep wiring only does findings.push([pr, 'H76', unowned]) over seenPrs (populated from listOpenPullRequests at L23790, the REST /pulls?state=open listing at L21563, which carries assignees/assignee); band H76: 'state' (H1's band, L19617). half-state-patrol.yml:84: "Findings never fail anything. A completed sweep exits 0 whether it found 0 or 40"; lint.yml:1275 runs only --self-test. No gate reads it.
  9. Mailbox exemption — CORRECT: MAILBOX_LABEL = 'pm:mailbox' (mailbox-open.mjs:153) imported lazily the same way H72 does (L23811 vs L23853); unread label fires UNJUDGED_MARKER (L18073) rather than clean; an assigned PR is clean regardless.
  10. Battery meaningful — CORRECT: 12 cases, pin 11 (house "pin just under the count", as L25259-25375 neighbours), covering firing, draft/ready parity, both assignee fields, mailbox vs unlabelled twin, closed/missing rows, three unread-label UNJUDGED legs, not-loud, band + familyRegistryCoverage().emitted (L20038-20055 regex matches the push), lazy-import wiring, roster floor. Probe: ✓ check-half-states self-test: 5252 cases pass … H76 unowned open PR 12/11, exit 0.
  11. Ceilings/byte caps hold — CORRECT: line counts main→head os-dev 405→405, SKILL.md 319→319, dispatch-runbook 245→245, execution-duties 188→188 (zero headroom each, ratchet run ✓ exit 0); changed lines measure 117/105/116/116/120/119 bytes against MAX_LINE_BYTES = 120 inclusive (check-skill-line-ratchet.mjs:2714,2935). Governed-prose and skill-id-lint runs exit 0.
  12. Head delta: git patch-id of 561d532c4..7c76c755 equals the three-dot diff at head (66332495cd…), so the merge commit added nothing.

② Semver level

None — no package touched (paths are .claude/** and scripts/pm/**); skip-changeset label present, Check Changeset check-run success. Clause-②: no and Metadata-protocol: none re-derived as correct: no packages/spec/**, no published schema, no metadata protocol surface in the file list.

③ Boundary flags

④ Design judgment

  • .claude/agents/os-dev.md (L38, L63): the two dev-side lines encode arms 1 and 2 of the ruling in the dev's voice (what pr_create carries; the hand-over is not the dev's), keep the card-assignee prohibition, and stay inside the write budget line. Holds.
  • .claude/skills/pm-dispatch/SKILL.md (L198-199): the minimal edit — 「派/请」 → 「请」, 「不请不派」 → 「派不请」 — keeps the reviewer-request prohibition and the author-is-approver fallback intact, and reads with ③ (L197) unchanged. Holds.
  • .claude/skills/pm-dispatch/references/execution-duties.md (L69): the rule body — assign the claim's account, re-assign at hand-over in the same stroke — sits in the 认领 section beside L68's Account: derivation and L92's card hand-over step, so the PR re-assign is the same stroke as the claim change. Holds.
  • .claude/skills/pm-dispatch/references/dispatch-runbook.md (L140): narrowing 「不写 assignee」 to 「不写卡 assignee」 is required, since a thread's pr_create now writes the PR's. Holds.
  • scripts/pm/check-half-states.mjs: predicate, band, sweep wiring and battery follow the H72/H75 pattern (lazy import, UNJUDGED on unread input, not loud, no write, no exit effect); the row names WHO/HOW and the mailbox out-of-scope. Holds.

No must-fix. No should-fix.

Implemented-by: claude/project-thread-jhzb1f
Reviewed-by: session_016WV4d1mkgEris4MHHLjtnk

VERDICT: PASS


Generated by Claude Code

This branch has not been deployed

No deployments
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

2 participants