Skip to content

feat(pm): pm:awaiting-merge — a PR awaiting the maintainer's review and merge no longer shares needs-user-decision with decision cards - #30445

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-29745-pm-awaiting-merge
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-29745-pm-awaiting-merge

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #29745

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:新增 PR 专用标签 pm:awaiting-merge(等您审核并合并的 PR 挂它,永不挂 issue);needs-user-decision 只留给决策卡(issue)。SKILL.md 复核 ② 与状态表改读法;总监呈批、分诊轮次记录、轮次报告健康指标、车队总表都把「决策卡 N」与「待您审合 PR M」分开计。巡查 H48 与交接检查 acceptedPullWait 改读新标签。
  • 为什么改:您的裁决「同意 pm:awaiting-merge」(A):一个标签两种意思,决策箱计数把等您回一个字的卡和等您点合并的 PR 混在一起,呈批分不开。
  • 风险与回滚:不新增门禁、不改六态互斥。风险只在过渡期:本 PR 落地后,巡查 H48 会把仍挂旧标签的受管 PR(现有一张,见 Acceptance notes)报为「缺 pm:awaiting-merge」,席位换标即清。回滚 = revert 本 PR;GitHub 上的标签对象留着无害。
  • 先看:① scripts/pm/check-half-states.mjs:新常量 GOVERNED_PR_AWAITING_MERGE_LABEL 与 DECISION_INBOX_LABEL,及 H48 读标签的 hunk(唯一改行为的 hunk);② .claude/skills/pm-dispatch/SKILL.md:复核 ② 行与状态表 needs-user-decision 行;③ scripts/pm/ensure-pm-labels.sh:新标签行与改写的 needs-user-decision 描述。
  • 两分钟验证:node scripts/pm/fleet-roster.mjs --self-test,预期末行 ✓ fleet-roster self-test: 115 cases in 9 batteries pass.;再 pnpm check:pm-skill-ratchet,预期 SKILL.md 仍 319 行、五个 references 行数不变。
  • 需要你做的:无,席位落地(落地时席位跑 bash scripts/pm/ensure-pm-labels.sh --reconcile 建标签,并给仍挂旧标签的 PR 换标)。

What changes

The maintainer's ruling on the card (verbatim: 「同意 pm:awaiting-merge」, option A). One label, two inboxes: a PR that waits on the maintainer's own review and merge used to carry needs-user-decision, the same label as a decision card, so the decision count mixed "a letter owed" with "a review and a click owed".

  1. The label — scripts/pm/ensure-pm-labels.sh gains pm:awaiting-merge ("PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue", in the per-repo loop, 100-char cap respected) with its named readers in the comment. needs-user-decision's description now says "decision cards only, never on a PR". Kept as is: pm:awaiting-maintainer (an issue, a human act outside GitHub). No new gate.
  2. SKILL.md (net 0 lines, every edited line ≤120 bytes): 复核 ② now says 「同笔挂 pm:awaiting-merge」; the next clause says it is PR-only and outside the six states; the state-table row says needs-user-decision = decision cards (issues) only, never a PR; the preamble names the maintainer's three entry points.
  3. Readers list the two inboxes apart — 「决策卡 N」 and 「待您审合 PR M」:
    • references/lanes/director.md (the director's batch and daily digest), references/triage-duties.md (triage round records; needs-user-decision = issues only), references/seat-lifecycle.md (the round report's health metric), references/state-machine.md (the six states are all issue states). Each reference that grew a line paid for it inside the same file by deleting a duplicated line (details under Acceptance notes); the others were rewritten in place.
    • scripts/pm/check-half-states.mjs: H48 (a governed PR with an ACCEPT verdict must carry the label) now reads GOVERNED_PR_AWAITING_MERGE_LABEL = pm:awaiting-merge. The card readers that had been reusing the PR constant (H52, H62, H73, the ④ dependents index and its renderer) read DECISION_INBOX_LABEL = needs-user-decision. Pinned: the old shape (needs-user-decision on a PR) no longer satisfies H48's label half; the new label is not one of PM_EXCLUSIVE_STATE_LABELS.
    • scripts/pm/check-handover.mjs: acceptedPullWait reads the new label on the stay-behind pull; pinned that the old shape is no longer a wait.
    • scripts/pm/fleet-roster.mjs: a new "awaiting merge" column (pulls carrying the label that close an in-flight card of the seat, attributed like "awaiting landing"; no new read — labels ride on the open-pulls listing) and an inbox line under the table: 「决策卡 N · 待您审合 PR M」, counted apart and never summed. --json carries the same inboxes totals. Fixture: pull 25427 carries the label.
    • The 终局四件套 prose in dispatch-gates.mjs, check-governed-merges.mjs and check-governed-queue-guard.mjs names the new label on the PR.
  4. The open PRs in the old shape — ruling item 4 named PR chore(pm): delete the one-time stale-finding sweep tool; H63 keeps reporting #29698, which merged on 2026-11-03 (moot). The search org:objectstack-ai is:pr is:open label:needs-user-decision at dispatch time returned four: objectstack#30263 (the ADR-0148 docs PR) and three outside the label loop (duly#157, duly#142, www.objectos.ai#327). Relabelling is a label write and belongs to the landing seat, not to this PR's write budget.

Tests

Self-tests of every touched script, run on the final head (readings under Verification):

  • node scripts/pm/fleet-roster.mjs --self-test — 115 cases in 9 batteries pass (110 on origin/main; 5 new: the column, the retired shape counts nowhere, inboxTotals, the inbox line, no inbox line without totals; the import pin now also asserts no second spelling of the label).
  • node scripts/pm/check-handover.mjs --self-test — 67 cases pass (66 on origin/main; 1 new: the retired shape is no longer a wait).
  • pnpm check:pm-half-states (check-half-states.mjs --self-test) — 5224 cases pass (2 new: the old shape no longer satisfies H48's label half; the PR constant is pm:awaiting-merge and not one of the six card states), with H48's existing cases re-pinned on the new label.
  • pnpm check:pm-label-desc-cap — 45 descriptions ≤100 chars (longest 100, the pre-existing tooling).
  • pnpm check:pm-skill-ratchet — SKILL.md 319/319, state-machine 41/41, triage-duties 100/100, seat-lifecycle 98/98, director 86/86; no edited line over 120 bytes.
  • pnpm check:pm-skill-id-lint, pnpm check:skill-frame-sync — green.

No ablation: the behaviour change is label-name plumbing pinned by the self-tests above; nothing under packages/* moved.

Acceptance notes

  • Paying the line ratchets (every ceilinged file at headroom 0; the currency is deleted content, never a re-wrap):
    • lanes/director.md: +1 line (待您审合箱 and the two-count rule) paid by deleting 「本席任何形态都纯召唤制,⛔ 无任何 routine;分诊席默认同项目召唤,职责见 triage.md」 — its first half repeats line 7 (「授权面 = 人工召唤 … 直连 ⛔ 无 Routine/cron」), its second half repeats lanes/triage.md line 4 (「由维护者一句召唤到任一项目,默认同总监席项目」).
    • triage-duties.md: +1 line (round records split the two counts) paid by deleting 「Backlog sweep 是常设职责,每 fire 扫任一析取命中的卡」, which line 19 (「首触定级与五析取 sweep 每 fire 跑到清空 …」) subsumes.
    • seat-lifecycle.md, state-machine.md, SKILL.md: rewritten in place, net 0.
  • The GitHub label object is not created by this PR: no relay op creates labels, so creation stays a landing step — bash scripts/pm/ensure-pm-labels.sh --reconcile on the loop's repos, which also rewrites needs-user-decision's live description (the create-only default would leave the old text). pm:awaiting-merge answered 404 on this repo at dispatch time.
  • Carrier: the landing seat — relabel objectstack#30263 (needs-user-decision → pm:awaiting-merge); the three PRs in duly and www.objectos.ai sit in repos outside ensure-pm-labels.sh's loop, so their shape is a seat decision, noted and not filed.
  • Boundary, not filed (ruling: no new gate): nothing patrols pm:awaiting-merge on an ISSUE, and label-write.mjs cannot tell an issue from a PR without a read; the rule lives in SKILL.md and the label's description.
  • Untouched on purpose: board-snapshot.mjs, references/landing-operations.md, references/contract-review.md (named as clues) do not read the label; fleet-roster.mjs's "awaiting ruling" already excluded pulls; the six-state mutex and PM_EXCLUSIVE_STATE_LABELS are unchanged.
  • skip-changeset: .claude/** and scripts/pm/** publish nothing from any released package.

Verification

Final head 0cf500af (every reading below was taken on this tree; base fdd06415 on origin/main). No package is touched, so no ① dependency-closure build and no ② package test is owed; test:repo and the farm-wide scans are CI's.

Derived families. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 63 commands from the 13 changed files. Every one ran in the worktree with its exit code captured before any pipe (bash -c CMD >> log 2>&1; EXIT=$?). Reconciliation, dispatch-gates --ran over the recorded command :: exit code lines: 「63 derived famil(ies) accounted for — 63 run, 0 NOT-MEASURED (a DERIVED zero — all 63 recorded an exit code and none of them is 3)」.

  • One command, pnpm --filter @objectstack/lint run check:doc-formula-expressions, first exited 3 — PREREQUISITE NOT MET (@objectstack/formula and @objectstack/lint unbuilt; by the gate's own text「Nothing was measured … NOT a finding」). After pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under os-verify-lock.sh (VERDICT command-exit 0 · held the lock 2s · waited 0s) it exits 0; the reconciliation above is the post-rerun one.
  • Local-only families (check:dev-prereqs, check:skill-frame-freshness) are not named by the derivation: neither packages/spec nor the four-axis frame files moved.
  • CI: the same families run in Lint & Repo Gates; the report comment cites the PR's check-runs by name once they exist, not a local re-run.
63 commands, all exit 0 at head 0cf500a (summary line of each, where the gate prints one)
exit 0  node scripts/check-closing-keyword-parity.mjs
exit 0  node scripts/check-closing-keyword-parity.mjs --self-test            40 assertions, 5 mutations each driven to red
exit 0  node scripts/check-comment-mask-corpus.mjs                           20780 files, 0 disagree, 0 unparseable
exit 0  node scripts/check-declaration-mirrors.mjs
exit 0  node scripts/check-declaration-mirrors.mjs --self-test               29 cases
exit 0  node scripts/check-issue-citations.mjs
exit 0  node scripts/check-platform-checklist-watchdog.mjs
exit 0  node scripts/check-platform-checklist-watchdog.mjs --self-test
exit 0  node scripts/check-release-section-coverage.mjs
exit 0  node scripts/check-release-section-coverage.mjs --strict
exit 0  node scripts/check-self-test-wired.mjs                               249 scripts CI runs, every self-test wired
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-skills-token-ratchet.mjs                          57 authored bundle files within ceilings
exit 0  node scripts/check-skills-token-ratchet.mjs --self-test              97 cases
exit 0  node scripts/check-whole-set-label-write.mjs
exit 0  node scripts/check-whole-set-label-write.mjs --self-test             24 fixture trees + 5 refusals + 1 hatch
exit 0  node scripts/ci/plan-test-core-shards.mjs --self-test                23 cases
exit 0  node scripts/ci/scheduled-full-run.mjs --self-test
exit 0  node scripts/pm/bare-root-worklist.mjs --self-test
exit 0  node scripts/pm/board-snapshot.mjs --self-test                       200 cases across 14 batteries
exit 0  node scripts/pm/check-governed-queue-guard.mjs --self-test           403 cases
exit 0  node scripts/pm/check-harness-current.mjs --self-test                26 cases
exit 0  node scripts/pm/sweep-closed-cards.mjs --self-test                   87 cases across 9 batteries
exit 0  node scripts/render-release-coverage-anchor.mjs --self-test          17 assertions
exit 0  pnpm --filter @objectstack/lint run check:doc-formula-expressions    (exit 3 PREREQUISITE before the build, 0 after)
exit 0  pnpm check:agent-test-spelling                                       0 violations, 661 files
exit 0  pnpm check:bash32-floor                                              31 shell files
exit 0  pnpm check:ci-filter-parity                                          139 assertions
exit 0  pnpm check:cli-command-ids                                           89 modules
exit 0  pnpm check:closing-target-claim                                      121 cases
exit 0  pnpm check:commit-card-trailers                                      81 cases
exit 0  pnpm check:cross-package-test-inputs                                 261 cases
exit 0  pnpm check:declared-population-live                                  251 of 312 families reach the tree
exit 0  pnpm check:doc-authoring                                             baseline held, 125895 strings read
exit 0  pnpm check:driver-memory-census
exit 0  pnpm check:entry-guard                                               294 scripts files
exit 0  pnpm check:gitlink-declared                                          36 assertions
exit 0  pnpm check:issue-citations
exit 0  pnpm check:nul-bytes                                                 75 assertions
exit 0  pnpm check:parse-guard                                               293 scripts files
exit 0  pnpm check:partof-closing-keyword                                    45 cases
exit 0  pnpm check:platform-checklist                                        265 assertions
exit 0  pnpm check:pm-dispatch-gates                                         1945 cases (fast tier), 7 slow sections deferred
exit 0  pnpm check:pm-expected-skips                                         206 cases
exit 0  pnpm check:pm-governed-merges                                        537 assertions
exit 0  pnpm check:pm-governed-prose                                         2 surfaces name all 13 governed surfaces
exit 0  pnpm check:pm-half-states                                            5224 cases
exit 0  pnpm check:pm-label-desc-cap                                         45 descriptions, all ≤100 (longest 100, tooling)
exit 0  pnpm check:pm-label-write                                            136 cases across 11 batteries
exit 0  pnpm check:pm-post-stamped                                           753 cases across 26 batteries
exit 0  pnpm check:pm-record-recognisers                                     (also runs fleet-roster: 115 cases in 9 batteries)
exit 0  pnpm check:pm-skill-id-lint                                          110 files clean
exit 0  pnpm check:pm-skill-ratchet                                          every ceiling held; cross-file moves: 3
exit 0  pnpm check:pnpm-filter-targets                                       212/284 filter occurrences resolve
exit 0  pnpm check:pnpm-flag-spellings                                       645 files
exit 0  pnpm check:ratchet-remedy-authority
exit 0  pnpm check:refd-timer-probe                                          11 cases
exit 0  pnpm check:scripts-symbol-anchors
exit 0  pnpm check:single-claim-paths                                        93 cases
exit 0  pnpm check:skill-frame-sync                                          the one declared frame copy is coherent
exit 0  pnpm check:watch-hint-literal                                        76 declarations across 4 rostered names

Generated by Claude Code

…awaiting review and merge

A PR whose landing waits on the maintainer's own review and merge used to
carry `needs-user-decision`, the same label as a decision card, so one inbox
mixed "a letter owed" with "a review and a click owed" and no count could tell
them apart. The maintainer ruled the two apart (verbatim: 「同意 pm:awaiting-merge」).

- `scripts/pm/ensure-pm-labels.sh`: new `pm:awaiting-merge` (PRs only, never an
  issue; named readers in its comment); `needs-user-decision` description now
  says decision cards only, never on a PR.
- SKILL.md 复核 ②: the PR carries `pm:awaiting-merge` in the same stroke; state
  table row: `needs-user-decision` = decision cards (issues) only. Net 0 lines.
- references: director's batch, triage round records, the round-report health
  metrics and the state machine list the two inboxes apart — 决策卡 N · 待您审合 PR M.
  Each file pays within itself (one duplicated line deleted per added line).
- `check-half-states.mjs`: H48 reads `GOVERNED_PR_AWAITING_MERGE_LABEL` on the
  PR; the card readers (H52/H62/H73, the ④ index) read `DECISION_INBOX_LABEL`.
  The old shape on a PR no longer satisfies the label half (pinned).
- `check-handover.mjs` acceptedPullWait, `fleet-roster.mjs` (new "awaiting
  merge" column, inbox line counting both halves apart), and the 终局四件套 prose
  in dispatch-gates / governed-merges / queue-guard follow.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012MmDsR4AZoJZyzKQcBEESh
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 5, 2026
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-lizheng · session_012MmDsR4AZoJZyzKQcBEESh

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0cf500af90e8c6b4288ef5d36bd62275266a93db
Local-runs: none
Scope: .claude/skills/pm-dispatch/SKILL.md, .claude/skills/pm-dispatch/references/lanes/director.md, .claude/skills/pm-dispatch/references/seat-lifecycle.md, .claude/skills/pm-dispatch/references/state-machine.md, .claude/skills/pm-dispatch/references/triage-duties.md + scripts/pm/check-half-states.mjs, scripts/pm/check-handover.mjs, scripts/pm/fleet-roster.mjs, scripts/pm/fleet-roster.fixture.json, scripts/pm/ensure-pm-labels.sh, scripts/pm/check-governed-queue-guard.mjs, scripts/pm/check-governed-merges.mjs, scripts/pm/dispatch-gates.mjs, .claude/skills/pm-dispatch/references/lanes/triage.md, scripts/pm/check-label-desc-cap.mjs, scripts/pm/check-skill-line-ratchet.mjs, scripts/pm/label-write.mjs, scripts/pm/sweep-closed-cards.mjs, scripts/pm/fleet-write/validate.mjs, .github/workflows/half-state-patrol.yml, .github/workflows/stale.yml
Reviewed-at: 2026-11-05T12:02Z · PR #30445 · card #29745 · ruling of record: card comment 6594600712 (letter A, items 1–4, 「No new gate」, pm:awaiting-maintainer kept distinct)

Inputs: the card body and all three comments, the PR body, its 13-file list, the net diff against main at the head, the head's check-runs (31 success, 20 skipped, 0 failed, 0 in progress at the time of this record — Lint & Repo Gates, TypeScript Type Check, Test Core, Governed Surface Queue Guard, Test UI, Live half-state sweep all success; Build Core, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI skipped as expected for a diff with no package path). origin/main and the head tree were read with git grep / gh api only; nothing was built, run or re-run.

① Derived judgments

  1. New vocabulary label pm:awaiting-merge (ensure-pm-labels.sh, the six-repo loop, colour e07b63, description 98 code points; needs-user-decision's description rewritten to 「decision cards only, never on a PR」, 86 code points) — RIGHT. Ruling item 1 verbatim; check-label-desc-cap.mjs measures Unicode code points (CJK and ② included) against the 100 cap, both fit, and the gate is green on the head (Lint · repo & tooling gates).
  2. The PR half of the maintainer's inbox moves to pm:awaiting-merge — SKILL.md 复核 ②, and the three copies of the 终局四件套 sentence in dispatch-gates.mjs (changedLineLines), check-governed-merges.mjs (renderSizeLines + header) and check-governed-queue-guard.mjs (renderSizeVerdict + header) — RIGHT. Ruling item 2; the over-3000-line terminal shares that sentence, so it moves with it, and none of those three reads the label (prose only, checked on origin/main).
  3. needs-user-decision narrowed to issues only (SKILL.md state-table row, state-machine.md L7 「皆 issue 态」, triage-duties.md L43 「只挂 issue」) — RIGHT. The six-state mutex is untouched: SKILL.md L123, PM_EXCLUSIVE_STATE_LABELS, PM_STATE_LABELS, PM_RESIDUE_LABELS, SEEN_LABEL_PAGES, GRADING_STATE_LABELS, INDEFINITE_TARGET_LABELS, H71_NOT_CANDIDATE_LABELS are byte-identical and pm:awaiting-merge is in none of them (pinned by the new H48 case). The only prefix reader of pm: in scripts/** (h18RetriageAged, l.startsWith('pm:')) runs on pm:retriage ISSUES, so a pm:-prefixed PR label reaches no state reader; sweep-closed-cards.mjs skips PR rows and reads the explicit PM_RESIDUE_LABELS; label-write.mjs's one-of refusal reads the imported six.
  4. Module surface of check-half-states.mjs: GOVERNED_PR_DECISION_LABEL removed; GOVERNED_PR_AWAITING_MERGE_LABEL and DECISION_INBOX_LABEL added — RIGHT. Importers on origin/main: check-handover.mjs alone (updated in this diff); fleet-roster.mjs is the one new importer and rides the edge it already had (SEAT_SIGNATURE_LINE), so no new module edge. git grep for the old name outside those two files: zero hits on main, zero on the head.
  5. H48's accept set: an open governed PR on Tier H (drafts in; Tier S excluded by governedLandsOnRecord; merged/closed out) carrying an **ACCEPT** verdict must carry pm:awaiting-merge; the old shape no longer satisfies the label half (pinned) — RIGHT, and report-only: H48 is band state, isLoudFinding fires on the P0 marker alone, and half-state-patrol.yml's job never fails on findings. A Tier S PR (this one included) is outside H48, which is exactly the label's meaning — the seat lands Tier S itself, nothing waits on the maintainer's click.
  6. check-handover.mjs acceptedPullWait: the new label on the stay-behind pull is a wait; the old shape falls through to the timeline reading like an unlabelled pull (pinned) — RIGHT. Transition consequence: an in-flight card whose ACCEPTed Tier H pull still carries only needs-user-decision reads not-waiting ⇒ [claim-unreleased] at a handover until the one label write; that write is the landing step already named (item 4), see ④.
  7. fleet-roster.mjs: the awaiting merge column (pulls carrying the label that close an in-flight card of the seat; mailbox pulls excluded; no new read — labels ride the open-pulls listing), inboxTotals (decision cards = open needs-user-decision ISSUES, a labelled PR row counts nowhere; awaiting-merge = open pulls carrying the label, every repo in), the 「决策卡 N · 待您审合 PR M」 line never summed, --json.inboxes — RIGHT. Ruling item 3 for the roster; the fixture pins one old-shape PR row (25401) counting nowhere and one new-shape pull (25427) in its own column; the import-line pin also forbids a second spelling of the label in that file.
  8. ④ dependents index, H52, H62, H73 read DECISION_INBOX_LABEL — same value, same populations (H62/H73 exclude PR rows; decisionDependentIndex runs over the unscoped open listing as before) — RIGHT, no behavioural change.
  9. Reader sweep (origin/main and the head: .claude/**, scripts/pm/**, .github/workflows/**, AGENTS.md): no workflow names needs-user-decision; stale.yml exempts neither label (exempt-pr-labels: pinned,security,work-in-progress), so the swap changes nothing there; fleet-roster's awaiting ruling already excluded pulls; board-snapshot.mjs, check-owed-reviews.mjs, check-prior-rulings.mjs, close-cards.mjs, post-stamped.mjs read the label on cards or comments only; mailbox-open.mjs's PR_LABELS untouched; the relay (fleet-write/validate.mjs) types labels as a list with no vocabulary, so the seat can write the new label today. No PR-side reader the diff missed. One stale PROSE string found in the cited code, not a reader: H54's row text (check-half-states.mjs L12896, and the comment at L12795) still prints 「needs-user-decision on cards and PRs」 — should-fix S2 below.
  10. Ruling item 4 — PR chore(pm): delete the one-time stale-finding sweep tool; H63 keeps reporting #29698 merged before dispatch (moot as the PM read); the live old-shape PR is objectstack#30263; its relabel and the label-object creation (bash scripts/pm/ensure-pm-labels.sh --reconcile on the loop's repos) are label writes owed by the landing seat, not diff — RIGHT.

② Semver level

skip-changeset is right: 13 files under .claude/** and scripts/pm/**, no released package moved, no authorable key, no published export. PR body carries Clause-②: no and Metadata-protocol: none as line-initial declarations; there is no changeset to mirror them. Check Changeset is success on the head. Clause-②: no.

③ Boundary flags

open_questions: [] — nothing to answer there. The eight deviations and three out-of-scope notes of the os-dev-report (card comment 6657206466), each answered:

  • No changeset — accepted (②).
  • The GitHub label object was not created — accepted: no relay op creates labels. Landing order matters and the body states it: --reconcile FIRST (it both creates pm:awaiting-merge and rewrites needs-user-decision's live description — the create-only default keeps the old text, read in the shim at ensure-pm-labels.sh L110–161), then the relabel of docs(adr): ADR-0148 — the recycle bin is a separate store, restore under the original ids, retention in days (#3146 slice 1) #30263.
  • Ruling item 4 moot for chore(pm): delete the one-time stale-finding sweep tool; H63 keeps reporting #29698; docs(adr): ADR-0148 — the recycle bin is a separate store, restore under the original ids, retention in days (#3146 slice 1) #30263 left to the landing seat — accepted: a label write by the seat that lands this PR, in the same landing; the three PRs in duly / www.objectos.ai sit outside the label loop — a seat decision, noted on the card, not filed.
  • SKILL.md 六态互斥 line left byte-identical — accepted: issues-only is stated in the state-table row, 复核 ②'s next clause and state-machine.md L7; the mutex sentence names the six states and needs no change.
  • Two reference lines deleted to pay the ratchet — judged in ④: both rules survive.
  • check:doc-formula-expressions exit 3 then rerun after a build — a process note; the reading of record is the head's Lint & Repo Gates success.
  • Foreground tail --pid wait crossed the cap — process hygiene, no bearing on the tree.
  • Commit trailers — the model-free pair AGENTS.md requires; conforms.
  • Out of scope: relabel list / reconcile — carrier: the landing seat; accepted as above.
  • Boundary: pm:awaiting-merge on an ISSUE is unpatrolled — accepted under the ruling's 「No new gate」: the rule lives in SKILL.md L107/L194, state-machine.md L7 and the label description; a report-only row is a later card only if that shape is ever measured on the board. Not escalated.
  • Author is objectstack-fleet[bot] through the relay — no login note owed.

④ Design judgment

Judged against ruling comment 6594600712 (option A, items 1–4, 「No new gate」, pm:awaiting-maintainer kept distinct) and the enforcing scripts read in the head tree.

  • SKILL.md (L20 three entry points; L107 row; L189 复核 ②; L194) — HOLDS. H48 reads exactly the label ② writes, on exactly the population ② covers (Tier H, awaiting a human approval; Tier S never carries it, governedLandsOnRecord); acceptedPullWait reads the same constant; L123 and the pm:awaiting-maintainer row are untouched and the two labels stay distinct (issue vs PR, outside-GitHub act vs in-GitHub click). Tried to break it: L194 「不入六态」 against the pm: prefix — the only prefix reader is H18 on pm:retriage issues; the relay's typed labels list lets the seat write it now; 席位落地's 「清标」 (L196) removes it, so no closed-pull residue question arises and sweep-closed-cards skips PRs regardless. Ratchet: 319/319; edited prose lines 104–115 bytes; L107 is a table row (structural exemption class table).
  • references/lanes/director.md (L53–54) — HOLDS: ruling item 3 for 呈批 and 摘要, counted apart. Deleted L25: clause 「本席任何形态都纯召唤制,⛔ 无任何 routine」 survives at L7 「授权面 = 人工召唤,任一项目可召 … 直连 ⛔ 无 Routine/cron」; clause 「分诊席默认同项目召唤」 survives at lanes/triage.md L4 「由维护者一句召唤到任一项目,默认同总监席项目」; the pointer clause 「职责见 triage.md」 is not load-bearing — lanes/triage.md stays reachable from triage-duties.md L100, project-forms.md L15, lanes/security.md L49 and security-findings.md L50, and no script pins the deleted sentence. 86/86; 112/117 bytes.
  • references/seat-lifecycle.md (L74–75) — HOLDS: the health metric splits 「决策卡 N·待您审合 PR M」, 「两数 ⛔ 不合计」, and the open-downstream-dependents clause is kept; it matches fleet-roster's inbox line and the ④ dependents count H14's index feeds. 98/98; 116/108 bytes.
  • references/state-machine.md (L7 「皆 issue 态」) — HOLDS: states what H29/H30's population and PM_EXCLUSIVE_STATE_LABELS already enforce. 41/41; 113 bytes.
  • references/triage-duties.md (L11 round records split; L43 只挂 issue) — HOLDS: 「后者 = open pm:awaiting-merge PR」 is the same filter inboxTotals counts. Deleted L12 「Backlog sweep 是常设职责,每 fire 扫任一析取命中的卡」 survives at L19 「首触定级与五析取 sweep 每 fire 跑到清空,先于旧卡重验」; the only other mention of "Backlog sweep" is a comment in check-half-states.mjs (L3788) pointing at SKILL.md, not a pin. 100/100; 113/114 bytes.
  • The transition (the question of record): once this lands, H48 reports docs(adr): ADR-0148 — the recycle bin is a separate store, restore under the original ids, retention in days (#3146 slice 1) #30263 as 「no pm:awaiting-merge label」 — one report-only row, cleared by the landing seat's one label write, and check-handover carries the same single consequence. That is the ruled outcome (item 4 makes the relabel part of the landing), not noise; nothing patrols the retired shape beyond that, which is right under 「No new gate」.
  • No new gate: nothing new reds. H48, acceptedPullWait and the roster are existing instruments reading a renamed label; the queue guard, the landing pre-check and the post-merge audit read no label.

Must-fix: none. Should-fix (either a delta commit on this PR or a follow-up; neither blocks landing):

  • S1 scripts/pm/check-handover.mjs L178 keeps its own export const DECISION_INBOX_LABEL = 'needs-user-decision' while check-half-states.mjs now exports the same name and value — two declarations of one constant across the pair this PR explicitly binds; import it from check-half-states.mjs beside GOVERNED_PR_AWAITING_MERGE_LABEL so the single-spelling rule holds across files, not only inside each.
  • S2 scripts/pm/check-half-states.mjs H54 row text (L12896) and its comment (L12795) still say 「needs-user-decision on cards and PRs」 — rendered into the patrol anchor; stale under this PR. Drop "and PRs" in both.

Implemented-by: claude/issue-29745-pm-awaiting-merge
Reviewed-by: session_012MmDsR4AZoJZyzKQcBEESh

VERDICT: PASS

… it lives on PRs

Contract-review should-fixes on the awaiting-merge split:
- check-handover.mjs imports DECISION_INBOX_LABEL from check-half-states.mjs
  beside GOVERNED_PR_AWAITING_MERGE_LABEL instead of declaring its own copy
  (nothing imported it from here, so no re-export is kept).
- check-half-states.mjs H54: its header comment and row text said
  `needs-user-decision` is live "on cards and PRs"; since the maintainer split
  the inboxes it lives on cards only, and the text now says so.

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

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-lizheng · session_012MmDsR4AZoJZyzKQcBEESh

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 98ca319efa70fdaccc1863a6cb79869f02019b57
Local-runs: none
Scope: .claude/skills/pm-dispatch/SKILL.md, .claude/skills/pm-dispatch/references/lanes/director.md, .claude/skills/pm-dispatch/references/seat-lifecycle.md, .claude/skills/pm-dispatch/references/state-machine.md, .claude/skills/pm-dispatch/references/triage-duties.md + scripts/pm/check-handover.mjs, scripts/pm/check-half-states.mjs (the two files the delta touches), and every cited file of record 6657482586 unchanged
Reviewed-at: 2026-11-05T15:44Z · DELTA review of PR #30445 on card #29745 — a findings-only commit on top of the head record 6657482586 reviewed (0cf500af), by the same reviewer, per contract-review.md (「只落发现的 commit 由同一复核者 delta 复核;无 must-fix 即止」)

Delta read: gh api …/compare/0cf500af…98ca319e — 1 commit ahead, 0 behind, 2 files, +3 −5, both hunks answering should-fixes S1 and S2 of record 6657482586 and nothing else. The PR's file list is unchanged (13 files, still .claude/** + scripts/pm/**, still Tier S, still skip-changeset). Check-runs on 98ca319e at record time: 30 success, 16 skipped, 0 failed, 0 in progress — Lint & Repo Gates, TypeScript Type Check, Test Core, Governed Surface Queue Guard, Test UI, Live half-state sweep all success; Build Core, Lint UI, Type Check UI, Build & E2E UI, Live E2E UI, Check Changeset skipped as expected (no package path; skip-changeset). Nothing was built, run or re-run; the new head's two files were read via gh api, origin/main via git grep.

① Derived judgments

Carried over unchanged from record 6657482586 (items 1–10). The delta adds exactly one derived change and it is RIGHT: scripts/pm/check-handover.mjs no longer EXPORTS its own DECISION_INBOX_LABEL (the local declaration at the old L177–178 is deleted) and imports it from ./check-half-states.mjs beside GOVERNED_PR_AWAITING_MERGE_LABEL. Importers of that export on origin/main outside the two files: zero (git grep DECISION_INBOX_LABEL excluding check-handover and check-half-states returns nothing), so no caller loses a symbol. Every read site in check-handover (L248, L268, the refusal text at L474–481, the self-test cases incl. the six-state pin at L845 and the old-shape acceptedPullWait case at L824) keeps the same name and value; check-half-states.mjs still exports both constants (L11405, L11412). No accept-set or public-surface change beyond that; ① items 1–10 stand as written.

② Semver level

Unchanged: skip-changeset, Clause-②: no, Metadata-protocol: none — the delta touches two files under scripts/pm/**, publishing nothing. Clause-②: no.

③ Boundary flags

Unchanged from record 6657482586 (every deviation and out-of-scope note answered there; open_questions: []). The delta carries no new flag: the commit message names the two findings it answers and nothing else; author is objectstack-fleet[bot] through the relay.

④ Design judgment

The five hits files are byte-identical between 0cf500af and 98ca319e (the compare lists only the two scripts), so ④ for SKILL.md, lanes/director.md, seat-lifecycle.md, state-machine.md and triage-duties.md carries over unchanged: each HOLDS against ruling comment 6594600712 and the enforcing scripts exactly as recorded. The two delta hunks, judged against the should-fixes they answer:

  • S1 — scripts/pm/check-handover.mjs (+1 −3): DECISION_INBOX_LABEL added to the existing import list from ./check-half-states.mjs, the local export const and its one-line doc removed. RESOLVED. One spelling of the decision-inbox label now lives in check-half-states.mjs and every reader in the pair imports it; the edge check-handover → check-half-states already existed (no new module edge, no cycle). Tried to break it: nothing on main imported the removed export; the self-test's six-state pin (PM_EXCLUSIVE_STATE_LABELS.includes(DECISION_INBOX_LABEL)) now holds the imported constant against the imported list, which is the stronger form; Lint · scoped · tooling self-tests and Lint & Repo Gates are green on the head.
  • S2 — scripts/pm/check-half-states.mjs (+2 −2): the H54 banner comment (L12795) and the rendered H54 row text (L12896) now read 「needs-user-decision is live on cards」 / 「needs-user-decision on cards, pm:awaiting-maintainer on cards」. RESOLVED: the patrol anchor no longer tells a reader the decision label lives on PRs. grep "cards and PRs" over both files on the new head: zero hits. The H54 row is report-only and the sentence's meaning (no new label; 逐卡改判) is unchanged; the row's own self-test pins (h54() includes 「needs-user-decision, carrying its four-facet analysis」) do not depend on the edited clause, and check:pm-half-states is green on the head via Lint & Repo Gates.

No new finding. Must-fix: none. Should-fix: none — S1 and S2 of record 6657482586 are closed on this head.

Implemented-by: claude/issue-29745-pm-awaiting-merge
Reviewed-by: session_012MmDsR4AZoJZyzKQcBEESh

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-lizheng · session_012MmDsR4AZoJZyzKQcBEESh

席位核对 — ACCEPT · domain:skills#2 · 2026-11-05T15:45Z

Head-sha: 98ca319efa70fdaccc1863a6cb79869f02019b57

维护者速读

  • 改了什么:按裁决 A,等你审核合并的 PR 改挂新标签 pm:awaiting-merge,needs-user-decision 只留给决策卡(issue)。SKILL.md、4 份 references 与读这两个收件箱的脚本(H48、check-handover、fleet-roster 等)同步;轮报与总监摘要分写「决策卡 N」「待您审合 PR M」。行数各文件净 0。
  • 为什么改:你问「带我审核的 PR 也挂着待决裁的标签,是否容易产生歧义」并裁定 A;两种待办共用一个标签,计数分不开。
  • 风险与回滚:只动规则文本与 PM 工具,无产品包、无门禁新增;回滚即 revert。
  • 先看:SKILL.md 复核 ② 与状态表 needs-user-decision 行。
  • 两分钟验证:pnpm check:pm-half-states 与 node scripts/pm/fleet-roster.mjs --self-test 绿。
  • 需要你做的:合并后在本地跑一次 bash scripts/pm/ensure-pm-labels.sh --reconcile(中继无建/改标签的操作):创建 pm:awaiting-merge 的颜色与描述,并改写 needs-user-decision 的描述。本 PR 是 Tier S,由席位落地,不需要你批。

席位意见:可落。契约复核 PASS 6657482586(0cf500af)+ delta PASS 6662318244(现 head),两条 should-fix 已在补丁轮收掉。

核对

  1. 形态:draft → main,正文首行 Fixes #29745;skip-changeset(.claude/** + scripts/pm/** 不发运);Clause-②: no · Metadata-protocol: none。文件面在认领申报内(SKILL.md、references、scripts/pm/ 读者、ensure-pm-labels.sh)。
  2. 前提实测:origin/main 上 pm:awaiting-merge 0 命中、标签对象 404;裁决第 4 项:PR chore(pm): delete the one-time stale-finding sweep tool; H63 keeps reporting #29698 与 docs(adr): ADR-0148 — the recycle bin is a separate store, restore under the original ids, retention in days (#3146 slice 1) #30263 均已合并,2026-11-05T15:45Z 无 open PR 仍挂旧形态,无需改标。
  3. 门禁 check-runs(head 98ca319e):30 success、16 skipped、0 失败/挂起;mergeable_state: clean。
  4. 接受的偏差:标签对象未建(无中继操作,交维护者本地 --reconcile);为付棘轮删去两行重复规则(director.md、triage-duties.md),复核确认规则在他处存续。
  5. 范围外去向:duly / www.objectos.ai 三个旧形态 PR 在标签循环之外 → Acceptance notes;pm:awaiting-merge 误挂 issue 无巡查 → 规则住 SKILL.md 与标签描述,不新增门禁(裁决「No new gate」)。
  6. 落地条款:Tier S(check-governed-merges --pr 30445:.claude/** ×5,248 行 ≤ 3000)→ 席位经中继 pr_ready + automerge_enable 落地,跟到 MERGED。

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 15:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 5d0904c Nov 5, 2026
47 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-29745-pm-awaiting-merge branch November 5, 2026 16:24
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/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant