Skip to content

docs(pm-dispatch): a scene table of when a seat must ring another seat's mailbox, the missing keys admitted, the mailbox as a seating step - #28716

Merged
os-zhuang merged 10 commits into
mainfrom
claude/issue-28675-mailbox-doorbells
Nov 1, 2026
Merged

os-zhuang merged 10 commits into
mainfrom
claude/issue-28675-mailbox-doorbells

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread

Fixes #28675
Clause-②: no
Metadata-protocol: none

Stacked on #28687 — its commits show in this diff until it lands; the seat merges origin/main after it lands.

维护者速读

  • 改了什么:把〈信箱 PR〉一节改成「哪种场景必须敲谁的信箱、用哪个键」的场景表,补上 main-red 通知、发版阻塞、解锁、裁决、串行、先落地等门铃;坐席即开信箱并发带巡查编号的上线通知,巡查每轮先读信箱;半态巡查新增 H72,只报告信箱里读不懂的门铃行。
  • 为什么改:维护者原话「感觉车道之间还是不太知道使用 邮箱沟通,综合评估一下哪些场景应该沟通」;卡片引用了这句原话,这正是新增半态行 H72 所需的授权(instrument-discipline.md L9)。
  • 风险与回滚:只动席位规则文本和 PM 工具,不碰产品代码;没有任何门禁、workflow 或队列守卫读信箱,H72 只报告不拦截;四个规则文件净增 0 行。已知过渡噪音:旧格式的上线通知(无巡查编号)在落地后约一天内会被 H72 列出。回滚 = revert 本 PR。
  • 先看:.claude/skills/pm-dispatch/references/project-forms.md〈信箱 PR〉· scripts/pm/record-recognisers.mjs 的 MAILBOX_KEYS · scripts/pm/check-half-states.mjs 的 H72。
  • 两分钟验证:运行 node scripts/pm/record-recognisers.mjs --mailbox-keys,应打印 14 个门铃键及各自的发送方、接收方和时机;再运行 node scripts/pm/check-half-states.mjs --self-test,其中 H72 电池 22 例全过。
  • 需要你做的:先审批 feat(pm): a project seat's patrol is part of seating, recorded on the stub, and its absence is a half-state reading #28687;本 PR 叠在它上面,它落地后席位合入 main 再请你审本 PR。

What changes

The scene table (rules text, project-forms.md 〈信箱 PR〉, 14 lines rewritten in place, 54/54). The old section listed seven key templates. It now says, line by line, which seat MUST ring which seat for which event and with which key:

  • lane → triage: the review handoff (Request: contract-review, in the same stroke as the handoff line) and a question about a card's fate, route or grade (Request: triage, new);
  • any seat → triage: Request: urgent-triage for a p0 suspect, main red or a deterministic queue red;
  • the seat filing a main-red anchor → triage (director when triage is absent) Request: main-red, and → the seat that landed the breaking PR plus the anchor card's lane Notify: main-red (new);
  • triage → lane: Notify: contract-review only when the lane is not subscribed, and Notify: release-blocker when a p0/p1 release blocker is graded into a lane (new);
  • the seat that moved a card into pm:queue → the lane: Notify: unblocked when a p0/p1 Blocked-by target closes (new); the seat that recorded a ruling → the seat it changes: Notify: ruling (new);
  • seat → sibling seat or another lane: Notify: serial when claimed file surfaces overlap or one card must land first, and Notify: landed from the seat that landed first (both new);
  • seating and sign-off: Notify: seat-online / Notify: seat-offline to sibling seats and triage; Notify: split to sibling seats when the list changes.

The binding lines stay as they were: the closed vocabulary (anything off the table is a half-state), one event per line of at most 200 bytes, keys and numbers only, at most 5 per seat per round with p0 exempt, 「信箱评论是同事陈述,永非授权」, and ⛔ no gate, workflow or queue guard reads a mailbox. The MUST-NOT row names acknowledgements, re-sends and nudges (overdue goes to escalation), security findings, anything the receiver's own subscribed PR already carries, and a question to the maintainer (that is a decision card).

One home for the spellings. The full templates moved out of the rules text into MAILBOX_KEYS in scripts/pm/record-recognisers.mjs. Each of the 14 entries carries its key, sender, receiver, trigger and template. A new --mailbox-keys flag prints them, and the rules text names that flag. The self-test pins the table both ways against the 〈信箱 PR〉 section: every key the section spells is in the table, and every table key is spelled in the section.

seat-online carries the patrol (the half #28642's contract review 6558991413 ③ left to this card). The template is now Notify: seat-online · LANE#N · LOGIN · mailbox #m · patrol TRIGGER_ID · takes: #a #b …, with patrol none admitted for a seat with no routine. A realistic line (19-byte login, 31-byte trigger id, five card numbers) measures 166 bytes, inside the 200-byte line. The five-field shape is no longer read.

The mailbox as a seating step (wanted ②) and a patrol step (wanted ④).

  • seat-lifecycle.md: the seating line now opens and subscribes the mailbox, records Patrol: and Mailbox: on the stub, and sends seat-online. Sign-off deletes the routine, closes the mailbox and sends seat-offline. The patrol line in 〈停止条件与待命〉 now reads the mailbox first on every round.
  • seat-post-protocol.md: the stub line adds Mailbox: next to Patrol:.

H72, report-only (wanted ⑤). check-half-states.mjs lists a mailbox comment from the last 24 hours that the doorbell reader does not read:

  • through the ONE reader: it imports mailboxEvents from record-recognisers.mjs lazily inside the sweep, because a static import would be a cycle;
  • it reads comments only on open pm:mailbox PRs, with paged reads of up to 3 pages, and leaves an incomplete window unjudged;
  • the finding names the 〈信箱 PR〉 remedy, says not to re-send, and points at --mailbox-keys;
  • its sweep clause is Mailbox doorbells (H72): …, in the inventory band.

Two consequent fixes outside the claim surface (declared deviations).

  • landing-operations.md L80 spelled the breaking-PR ring as Request: main-red; this PR makes it Notify: main-red, because the old spelling would now be refused as off-table.
  • mailbox-open.mjs said "one of the seven doorbell keys" in its header and in the mailbox body it writes; it now says "one of the doorbell keys" and names --mailbox-keys.

Net line growth per rules file (all against the stacked base 8eb102f).

File Before After Net
references/project-forms.md 54 54 0
references/seat-lifecycle.md 96 96 0
references/seat-post-protocol.md 88 88 0
references/landing-operations.md 101 101 0

The table fits at zero net lines because the templates moved to code. Dropped from the rules text: the verbatim templates, which the tool now prints; the 「本席复审档不可用,一 head 一次」 clause, now carried by "in the same stroke as the handoff line" plus the no-re-send row; the 「开轮时」 qualifier on split; and 「Routine 席唤不醒,读信箱靠巡查」, now carried by the patrol line. Every rules line is at most 120 bytes.

Tests

  • record-recognisers self-test: 350 cases pass. The doorbell battery has 44 cases, against a floor raised 27 → 40. It adds:
    • each new key's reading, plus patrol none;
    • a refusal for the old five-field seat-online;
    • the both-ways table pin, and a check that the section names --mailbox-keys;
    • every template sent verbatim is unreadable, so placeholders cannot pass as values;
    • --mailbox-keys output pinned exactly;
    • invented keys (surface, take) and a main-red cleared line refused;
    • an end-to-end case through check-half-states.mjs's H72 functions, proving there is one reader.
  • check-half-states self-test: 5185 cases pass. The new battery H72 mailbox off-table bells has 22 cases, floor 20. The seat-window pin "every other comment read names its page" goes 3 → 4 for the new paged read.
  • mailbox-open self-test: 71 cases pass.

Acceptance notes

Verification

Gates were derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 901e99e: 55 commands, all run, every exit code recorded before any pipe. The --ran reconciliation reports 55 derived, 55 run, 0 NOT-MEASURED, 0 UNRUN.

check:doc-formula-expressions first exited 3 (prerequisite not met: @objectstack/formula was not built in this worktree). After pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint (exit 0) it exited 0, and that is the exit recorded. The rules-text ratchets passed:

  • check:pm-skill-ratchet, check:pm-skill-id-lint and check:skill-frame-sync, all in the derived list;
  • check:pm-governed-prose, run separately: exit 0.
Recorded exit codes (55)
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-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 :: 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/ci/plan-test-core-shards.mjs --self-test :: 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 :: exit 0
node scripts/pm/check-harness-current.mjs --self-test :: exit 0
node scripts/pm/sweep-closed-cards.mjs --self-test :: exit 0
node scripts/render-release-coverage-anchor.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:closing-target-claim :: exit 0
pnpm check:commit-card-trailers :: 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:issue-citations :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:partof-closing-keyword :: exit 0
pnpm check:platform-checklist :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-half-states :: exit 0
pnpm check:pm-post-stamped :: exit 0
pnpm check:pm-record-recognisers :: 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:pnpm-flag-spellings :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:scripts-symbol-anchors :: exit 0
pnpm check:single-claim-paths :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
--ran reconciliation tail
  The 55 is THIS tree's derivation, recomputed in this process from the same expression --commands prints — never read back from your record. A family you never wrote down is still counted, which is what an arithmetic over your own list cannot do.
  EXIT CODES — all 55 accounted famil(ies) carry one, so the NOT-MEASURED count above is DERIVED from them. ⛔ This tool ran none of them; it read the codes you recorded.
  ⛔ This answers ONE link: what this card DERIVES against what you RAN. It is not a complete account of what CI runs on the PR — the 43 artifact-roster famil(ies), the 11 declared WIDE-population famil(ies), the 11 pending-changeset famil(ies), the unreachable listing, the 1 path-scheduled CI job(s), the type-check lanes and the always-runs tail are each outside the derived total, each printed under its own heading by a run without --ran.
✓ dispatch-gates --ran: 55 derived famil(ies) accounted for — 55 run, 0 NOT-MEASURED (a DERIVED zero — all 55 recorded an exit code and none of them is 3).
node scripts/pm/record-recognisers.mjs --mailbox-keys (exit 0)
(header line, which names the To: line, omitted)
one event, one line of at most 200 bytes, keys and numbers only; anything else is a half-state.

Request: contract-review · PR #n · head SHA · REASON
    lane → triage; in the same stroke as the PR's `Contract-review-handoff:` line (the seat's own review tier is unavailable)
Request: triage · #n · ONE-LINE-QUESTION
    lane → triage; a question about a card's fate, route or grade
Request: urgent-triage · #n · ONE-LINE-CRITERION
    any seat → triage; a p0 suspect, main red, a deterministic queue red
Request: main-red · CHECK-NAME · anchor #n · suggested lane X
    the seat that files the main-red anchor card → triage, or the director when triage is absent; the anchor card is filed
Notify: main-red · anchor #n · breaking PR #m
    the seat that files the main-red anchor card → the seat that landed the breaking PR, and the anchor card's lane; the anchor card is filed
Notify: contract-review PASS or FAIL · PR #n · record ID
    triage → lane; with the record, only when the lane is not subscribed to the PR
Notify: release-blocker · #n
    triage → the lane it is graded into; a p0/p1 release blocker is graded into a lane
Notify: unblocked · #n · p0 or p1
    the seat that moved the card to `pm:queue` (triage or an epic seat) → the card's lane seats; a p0/p1 card's `Blocked-by` target closed
Notify: ruling · #n · LETTER · record ID
    the seat that recorded a maintainer ruling → the seat whose card or draft it changes; the ruling is recorded
Notify: serial · #a behind #b
    a seat → a sibling seat or another lane; claimed file surfaces overlap, or one card must land first
Notify: landed · #b
    the seat whose card landed first → the seat a `serial` held behind it; the card a `serial` named lands
Notify: seat-online · LANE#N · LOGIN · mailbox #m · patrol TRIGGER_ID · takes: #a #b …
    the seating session (or a forced takeover, for the walled account) → the lane's sibling seats and triage; at seating
Notify: seat-offline · LANE#N · in-flight: … · stay-behind: … · brief ID
    the signing-off session → the lane's sibling seats and triage; at sign-off
Notify: split · LANE#N · takes: … · leaves: …
    a seat of a multi-seat lane → its sibling seats; at round open, only when the list changed

Protocol text (who MUST ring, and what is never rung): pm-dispatch references/project-forms.md 〈信箱 PR〉.

🤖 Generated with Claude Code

…b records it, an empty queue is standby

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
…unclaimed queue card on an on-duty lane

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
… matches the director seat anywhere in the seat name

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
…seat must ring, with seven admitted keys and seat-online carrying the patrol

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
… not read, through the one reader

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
…receivers on its own

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 901e99ed1317f46b05411bc3fc65db7c8e77921f
Local-runs: none
Scope: .claude/skills/pm-dispatch/references/landing-operations.md, .claude/skills/pm-dispatch/references/project-forms.md, .claude/skills/pm-dispatch/references/seat-lifecycle.md, .claude/skills/pm-dispatch/references/seat-post-protocol.md + scripts/pm/record-recognisers.mjs (MAILBOX_KEYS, the field readers, the both-ways self-test pin against 〈信箱 PR〉, --mailbox-keys), scripts/pm/check-half-states.mjs (H72, its sweep wiring, summary clause and battery), scripts/pm/mailbox-open.mjs (MAILBOX_LABEL, seatKey, the mailbox body text) — the rule and the reader that enforces it, read as one change

Written 2026-11-01T15:22Z by the at-tier subagent of domain:skills#2. Inputs = card #28675 (body + 4 comments), PR #28716 (body, file list, the net diff of 901e99ed over its stacked base claude/issue-28642-seat-patrol@8eb102ff, i.e. PR #28687 whose records 6558991413 + 6559169512 stand on their own), and the check-runs on 901e99ed. The dev's report (6559765410) and the PR body were read as claims and checked against the tree, never adopted. ⛔ #28687's own hunks are not re-judged here.

① Derived judgments

  • Published accept set / public surface: unchanged — right. The delta touches four .claude/skills/pm-dispatch/references/** files (governed rules text) and three scripts/pm/*.mjs PM tools (not a package, not in GOVERNED_SURFACES). No packages/**, no exports map, no .d.ts, no error code. Clause-②: no on the PR body and on the claim (6559235791) is the correct declaration.
  • Review face: rules, CONTRACT_REVIEW_TIER — right. reviewFace --paths on the PR's file list returns the four references files as hits; the three scripts are cited code and are listed on Scope: as such.
  • Landing tier by the register: Tier S (SKILL.md:204, AGENTS.md ¶14: .claude/** whole tree; scripts/pm/** is not a governed path), exactly as 6558991413 ① read the base PR. Triage's "Tier H" on the card is a card-level condition — the maintainer's approval of the PR is the ruling for the rule change — and the claim and PR body accept it; see ③.
  • Rules text: four files, net 0 lines each (101/54/96/88 at the base and at head); every edited line measured ≤ 120 bytes (L80 108; project-forms L37–L49 90–120; seat-lifecycle L41/L42/L89 119/117/117; seat-post-protocol L18 116); SKILL.md untouched, as the claim's line budget stated. check:pm-skill-ratchet, pm-skill-id-lint, skill-frame-sync and pm-governed-prose ride Lint · PM dispatch-gates, green on this head.
  • The key table moved from text to code — right, and pinned both ways. 〈信箱 PR〉 now names 14 keys as Kind: key (L40–L46) and points at record-recognisers.mjs --mailbox-keys (L49); MAILBOX_KEYS holds the 14 full templates with from/to/when; the self-test reads the 〈信箱 PR〉 section and asserts the named set equals the table set and that the section names --mailbox-keys. Verified by hand: the section names exactly Request: contract-review, triage, urgent-triage, main-red and Notify: main-red, contract-review, release-blocker, unblocked, ruling, serial, landed, seat-online, seat-offline, split = the 14 ids in code; MAILBOX_FIELD_READERS has a reader per id (the roster case pins Object.keys(...).length === 14). The // dispatch-gates: self-test-reads marker stays on L77 with the same two paths, which dispatch-gates.self-test.mjs pins by line (7573) — the prose after -- is not pinned, only that it ends on its own line, so the reworded reason is safe.
  • Reader contract: Notify: seat-online gains a required patrol TRIGGER_ID | none field; the five-field shape is now unreadable — right by the rule and disclosed. PATROL_REF hard-codes trig_…; that spelling is the live Routine trigger id in fleet-roster.fixture.json (6015: trig_01Ncn…) and the feat(pm): a project seat's patrol is part of seating, recorded on the stub, and its absence is a half-state reading #28687 battery's Patrol: fixtures, so a real create_trigger id reads. grep over scripts/, .claude/ and .github/: no consumer of readMailboxLine/mailboxEvents/MAILBOX_KEYS outside the two files, so no tool composes the old shape.
  • Half-state registry: H72 → inventory, emitted from sweepInto, report-only — right. No label, title, body, assignee or comment write anywhere in the added code. Request cost traced: population = seenPrs filled from /pulls?state=open (mailbox drafts are in that listing; LEDGER_EXCLUSION is a search-query string and does not touch it), cut by h72NeedsThread to pm:mailbox rows with updated_at inside 24 h; then one since-bounded comment page per touched mailbox, ≤ 3 pages, incomplete window left UNJUDGED; a failed lazy import renders NOT MEASURED, never 0 of N. The H72 block sits after the listOpenPullRequests loop, so seenPrs is full when it runs; the dynamic import is reached only after awaited REST calls, so the module has finished evaluating (the banner's deadlock arm). The seatWindowAudit pin "every other comment read names its page" moves 3 → 4 because the new path carries &page= — raised, not weakened.
  • Self-test rosters: record-recognisers doorbell battery pin 27 → 40 under 44 cases; check-half-states gains H72 mailbox off-table bells pinned 20 under 22; mailbox-open battery updated to the reworded body. SELF_TEST_BATTERY_FLOOR untouched.
  • Check-runs on this head at write time: every completed non-skipped run is success — Lint · PM dispatch-gates (the ratchet family and check:pm-record-recognisers / check:pm-half-states), Lint · scoped · tooling self-tests, Lint · docs & code guards, Lint · CI/release/engine, Lint · repo & tooling, ESLint, Type Check ×6, Governed Surface Queue Guard, PR-body guards, Check Changeset, Check PR Size, Live required-set sweep, Platform checklist, Release section-coverage, Test UI, Dogfood, Test Core (2/8), and Live half-state sweep — the standing caller of check-half-states.mjs, the one run that exercises H72 against the live board, concluded success at 2026-11-01T15:21Z (a 12-minute run with the new row in it). Still in_progress: Test Core (1/8) only, a product shard no path here feeds. No red, no merge-base signature to discount.

② Semver level

No publishable package is touched; no changeset exists and the PR carries skip-changeset — matches what the diff publishes (nothing). Clause-②: no is consistent on the PR body and the claim; no (narrowing) arm, so no narrowing face is owed. Correct.

③ Boundary flags

Author: objectstack-fleet[bot] (relay) — no off-relay login to name.

Dev deviations (report 6559765410 / PR body "Acceptance notes"):

  • landing-operations.md L80 Request: main-red → Notify: main-red (outside the claim surface) — accepted, and necessary: the old spelling at that line (anchor filer → owning lane's mailbox) is the direction the table now spells as Notify: main-red; left as Request: it would read as a triage-bound key sent to a lane. One line, in the same file family, declared.
  • mailbox-open.mjs "seven doorbell keys" → "doorbell keys" + --mailbox-keys (outside the claim surface) — accepted: the text the opener writes into every new mailbox body would have been false the moment this lands; the body battery is updated in step.
  • Templates moved out of the rules text into MAILBOX_KEYS — accepted; this is how the table fits at net 0 lines, and the both-ways pin keeps the text and the reader coupled (see ①).
  • Wanted ③ (epic seats get mailboxes) not built — accepted as an out-of-scope finding: triage's own evidence on the card (6559611600) shows seatKey refuses epic: spellings and adds mailbox-open.mjs to the card's files; the seat filed pm-dispatch(mailbox): epic seats cannot be rung — mailbox-open refuses epic: seat keys and the fleet roster has no epic rows #28718 (open, finding) and answered on the card (6559792075).
  • Sender of Notify: main-red is "立锚卡者", not "triage" as the card's proposed row said — accepted: by main-red 约定① (landing-operations L76) the seat that sees the red files the anchor, triage or not, so the filer is the one who knows the breaking PR at that moment. Not listed as a deviation by the dev; noted here so the maintainer sees it.

out_of_scope_findings:

  • Epic seats cannot be rung — pm-dispatch(mailbox): epic seats cannot be rung — mailbox-open refuses epic: seat keys and the fleet roster has no epic rows #28718, see above.
  • Frozen dispatch-gates.data.mjs L505 / dispatch-gates.self-test.mjs L7570 comments still say "seven" — accepted: comment-only, nothing reads them (the census pins record-recognisers.mjs:77 self-test-reads by line and that the reason ends on its line, not the word "seven"); instrument-discipline L10 freezes that file.
  • Transition noise: pre-landing five-field seat-online lines read unreadable for ~24 h after landing — accepted as a report-only reading (H72 writes nothing; the rows age out). Seats on the old text re-read on the next round's touch-sha check (seat-lifecycle L16–17).

seat_decides:

Open escalations to the maintainer (the approval is the ruling):

  • Instrument-discipline L9 (a new patrol row needs the maintainer's words on the card): the card quotes the maintainer verbatim (「感觉车道之间还是不太知道使用 邮箱沟通,综合评估一下哪些场景应该沟通,需要的话立skills 卡」) and its Wanted ⑤ asks for the half-state row in so many words; whether that sentence authorises H72 is the maintainer's call, exactly as 6558991413 escalated H70/H71. H72 costs ≤ 3 GETs per touched mailbox and writes nothing. Until approved the PR stays draft, as it is.
  • Tier S by the register with triage's card-level condition: this record is the Tier S record of record; the owning seat does not flip ready or enqueue before (a) the maintainer's approval is on the PR, (b) feat(pm): a project seat's patrol is part of seating, recorded on the stub, and its absence is a half-state reading #28687 has landed and origin/main is merged in (the PR body says so), and (c) Live half-state sweep on the then-current head is green.

④ Design judgment

Scenario accepted against: the card's measured day — seven invented off-table keys (surface ×5, serial, landed, ruling, take, reverse main-red, main-red cleared), zero uses ever of the three Request: keys, a p1 card (#28560) waiting 4.5 h after its blocker closed, a main-red anchor (#28622) whose breaking lane was never rung, release blockers (#28546, #28657) learned on patrol, the first free-form question to triage on #28646, and a hand-off line (#28650) with no doorbell.

project-forms.md 〈信箱 PR〉 L37–L49 — holds. Each measured event now has a row and a key, every invented spelling either gets admitted (serial, landed, ruling) or has a table home (surface → serial, take → split, reverse main-red → Notify: main-red), and main-red cleared is deliberately not admitted (the anchor's close is on the card the receiver was rung about) — the doorbell battery pins that those three stay refused/unreadable. The closed-vocabulary line, 200-byte cap, keys-and-numbers-only, ≤ 5/round with p0 exempt, 「永非授权」 and the no-gate line all survive; the dropped 「本席复审档不可用,一 head 一次」 is carried by contract-review.md L21–22 (the hand-off line exists only when the tier was refused) plus the no-re-send row. Read against the reader: Request: triage reads card + free question (comma allowed); Notify: unblocked admits p0|p1 only and the template's own p0 or p1 is unreadable; Notify: ruling admits a primed or CJK letter and refuses a sentence; Notify: serial · #a behind #b / Notify: landed · #b pair; every template sent verbatim is unreadable, so a placeholder can never pass as an event. I tried to break the both-ways pin: it reads only the 〈信箱 PR〉 section (split on \n## ), so L80 of landing-operations naming Notify: main-red neither adds nor is required; a key named only in prose outside the section would go unpinned, but no such key exists at head.

  • should-fix (cap vs. MUST): L39 makes every listed event 必敲 and L48 keeps 「每席每轮 ≤5 条,p0 豁免」. A triage round that grades three p1 release blockers and unblocks three p1 cards owes six mandatory bells and may send five. Say which wins in one clause (e.g. 「超额归下轮」, or exempt p0/p1 both); today the text contradicts itself at p1.
  • should-fix (Notify: main-red with no breaking PR): the template requires breaking PR #m, yet landing-operations L79 admits a maintainer's direct one-line fix and 约定① a revert of "the commit between last green and first red" — a red introduced without a PR has no #m, and the mandatory row then cannot be spelled at all. Either give the field a none arm in the reader or scope the row to "when the breaking PR is known".

seat-lifecycle.md L41–L42, L89 — holds. Seating is one stroke: routine + mailbox open/subscribe + Patrol:/Mailbox: on the stub + seat-online; sign-off is the mirror (delete routine, close mailbox, seat-offline), consistent with seat-post-protocol L54 step 9 and SKILL.md:75 (換账号重开该席与信箱). The seat-online key now carries the patrol trigger, which closes the half 6558991413 ③ left to this card. L89 makes the mailbox read a per-round patrol step (Wanted ④), so a lost delivery is seen within a round.

  • should-fix (order against SKILL.md:73): L89 reads 「巡查每轮先读信箱、认领到 batch 上限」 while SKILL.md:73 says the fire's FIRST action is the cheapest lane-inventory probe and nothing is read before it. The mailbox page is small, but "先读信箱" as written contradicts 探针先行. One word fixes it: 「探针后先读信箱」 (or 「认领前读信箱」).

seat-post-protocol.md L18 — holds, with one gap. The stub gains Mailbox: beside Patrol:; L66 spells Patrol: TRIGGER_ID · CRON as the one home of that value.

landing-operations.md L80 — holds. The anchor filer rings the owning lane with Notify: main-red, the direction the table spells; the same line's 「根因卡恒归该车道」 is unchanged. (The no-PR arm is the should-fix above, owed on the key, not on this line.)

Cited reader record-recognisers.mjs (read, not reviewed): readMailboxLine still strips one signature line, demands a To: SEAT line via seatKey, refuses an off-table word before the byte cap, and routes the field reading through MAILBOX_FIELD_READERS[entry.id]; the new readers are strict on part count and regex per field, and numbers lists every card/PR a line names. mailboxKeyLines() is pure and the CLI arm prints it at exit 0. The doorbell battery drives every new key, the patrol none arm, the five-field refusal, the invented keys and the end-to-end H72 leg through a real mailboxEvents reading.

Cited gate check-half-states.mjs H72 (read, not reviewed): scope traced in ①; the predicate lists refused and unreadable events younger than 24 h (an unreadable at is kept, not aged out), names ≤ 5 comments with id/author/state/reason, sanitises angle-bracket placeholders out of the body, and the sentence carries the 〈信箱 PR〉 remedy, the --mailbox-keys pointer, the no-re-send and never-an-authorization lines and the report-only boundary. I tried to break it: a mailbox whose comment page fails is skipped and counted short, not read clean; a reader that fails to import renders NOT MEASURED; a quiet mailbox costs no request; GitHub's since filters by comment updated_at, so an old comment edited today is fetched but then aged out by its created_at — correct. One accepted limitation: any non-doorbell comment a bot might leave on a mailbox PR would be listed; the dev's live probe found only seat-written lines.

No must-fix: the table holds against every event the card measured, the reader and the text cannot drift (the pin is bidirectional and gated), the new row writes nothing and costs what it says, and every rule line is at or under 120 bytes at net zero.

Implemented-by: claude/issue-28675-mailbox-doorbells
Reviewed-by: session_017ihL2g7RyXa8ErM6GAkHyc

VERDICT: PASS

…he next round, main-red rings with no breaking PR, the mailbox is read after the probe, the stub spells Mailbox: PR #n

Contract review 6559915023 ④ should-fixes:
- project-forms 〈信箱 PR〉: the ≤5-per-round cap now says mandatory bells over it
  roll to the next round (p0 still exempt); the closing line keeps the
  colleague-statement and no-gate clauses.
- record-recognisers: `Notify: main-red` admits `breaking PR none`, so a red that
  came in with no PR can still be rung; the doorbell battery pins it.
- seat-lifecycle: the patrol reads the mailbox after the probe (SKILL.md 探针先行).
- seat-post-protocol: the stub spells `Mailbox: PR #n` beside `Patrol:`.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 37661b885bac64fd4d09ed0c922d7504fc0d1577
Local-runs: none
Scope: .claude/skills/pm-dispatch/references/landing-operations.md, .claude/skills/pm-dispatch/references/project-forms.md, .claude/skills/pm-dispatch/references/seat-lifecycle.md, .claude/skills/pm-dispatch/references/seat-post-protocol.md + scripts/pm/record-recognisers.mjs (MAILBOX_KEYS, BREAKING_REF, the notify:main-red reader and its new case), scripts/pm/check-half-states.mjs and scripts/pm/mailbox-open.mjs (unchanged on this head; their readings in 6559915023 carry over)

Delta review, written 2026-11-01T15:40Z by the same at-tier subagent that rendered record 6559915023 on the previous head 901e99ed. The head moved by ONE commit (37661b88, "mailbox patch round"): 12+/8− across project-forms.md L48–L49, seat-lifecycle.md L89, seat-post-protocol.md L66 and record-recognisers.mjs; landing-operations.md, check-half-states.mjs and mailbox-open.mjs are byte-identical to the previous head. The ① ② ③ ④ judgments of 6559915023 carry over unchanged except where this record says otherwise. Inputs = that diff, the card thread (no new comment since), the PR body, and the check-runs on 37661b88.

① Derived judgments

  • Accept set / public surface: still unchanged — right. Three governed rules files and one PM tool; no package, no changeset, skip-changeset still on the PR, Clause-②: no unchanged.
  • The delta applies the four should-fixes of 6559915023 and nothing else — right, verified against the diff: (1) project-forms L48 gains 「超额归下轮」 beside 「≤5 条,p0 豁免」 — the cap and the MUST rows no longer contradict at p1; the two closing lines were re-cut so L48 carries cap + --mailbox-keys and L49 carries 「永非授权」 + the no-gate line, every clause of the old L48/L49 surviving. (2) Notify: main-red template becomes … · breaking PR #m or none; BREAKING_REF admits #digits or the literal none; the reader returns pr: null and numbers: [anchor] on none. (3) seat-lifecycle L89 now reads 「巡查每轮探针后读信箱、认领满 batch」 — ordered after SKILL.md:73's probe. (4) seat-post-protocol L66 spells Mailbox: PR #n beside Patrol: TRIGGER_ID · CRON as the stub's one home for both values; 「放宽须写理由」 survives as 「放宽注由」.
  • Rules text: net 0 lines each (54/96/88/101 unchanged); edited lines measured 115 / 119 / 116 / 120 bytes — at or under 120. The 〈信箱 PR〉 section still names exactly 14 distinct Kind: key spans and still names --mailbox-keys, so the both-ways pin holds on this head; the // dispatch-gates: self-test-reads marker is untouched.
  • Reader contract: the new template sent verbatim (breaking PR #m or none) matches neither arm of BREAKING_REF, so the "every template is not an event" pin still holds; breaking none, breaking PR #0 and breaking PR stay unreadable — the new case pins all four. Doorbell battery 44 → 45 cases under the pin of 40.
  • Check-runs on this head: COMPLETE, every non-skipped run success, none in progress — Lint · PM dispatch-gates (ratchet / id-lint / frame-sync / check:pm-record-recognisers / check:pm-half-states), Lint · scoped · tooling self-tests, Lint · docs & code guards, Lint · CI/release/engine, Lint · repo & tooling, ESLint, Type Check ×6, Governed Surface Queue Guard, PR-body guards, Check PR Size, Live required-set sweep, Live half-state sweep (H72 against the live board, done 2026-11-01T15:35Z), Platform checklist, Release section-coverage, Test UI, Dogfood, Test Core (1/8), (2/8) and the aggregate. No red, no merge-base signature to discount.

② Semver level

Unchanged from 6559915023: no publishable package touched, no changeset, skip-changeset on the PR, Clause-②: no on body and claim, no narrowing arm. Correct.

③ Boundary flags

④ Design judgment

The four hits files on this head hold as 6559915023 ④ judged them, with the four should-fixes closed:

project-forms.md L48 — holds. 「超额归下轮」 resolves cap-versus-MUST without widening the exemption: a p1 bell the cap defers is still owed, next round, and the no-re-send row is untouched because deferral is not a re-send. I tried to break it: a seat could read 「归下轮」 as permission to never ring — but L39's 必敲 still binds the event and H72 does not read silence, so the only reading that satisfies both lines is "ring it next round"; good enough for a rules line at 115 bytes.

Notify: main-red … breaking PR #m or none — holds. The red with no PR (landing-operations L79's maintainer direct fix, 约定①'s revert window) can now be rung; pr: null keeps numbers honest (the anchor alone), so a reader that fans out on numbers never chases a PR #0. none is the same spelling the list fields already use (takes: none), one vocabulary.

seat-lifecycle.md L89 — holds. 「探针后读信箱」 orders the mailbox read after SKILL.md:73's cheapest-probe-first and before claiming, which is where a delivered bell (an unblocked, a serial) can still change what the seat claims this round.

seat-post-protocol.md L66 — holds. Mailbox: PR #n is now spelled where Patrol: is, matching the #28687 H70 fixture (- **Mailbox:** PR #28676) and the mailbox #m field of seat-online; a future roster reader has one spelling to read.

Cited reader (read, not reviewed): the notify:main-red reader still demands exactly three parts, and the none arm cannot be reached through the #m capture, so a well-formed numeric line reads exactly as before (the existing DB.nm case still passes). No must-fix, no new should-fix.

Implemented-by: claude/issue-28675-mailbox-doorbells
Reviewed-by: session_017ihL2g7RyXa8ErM6GAkHyc

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc

维护者速读(席位终稿 · head 37661b885bac64fd4d09ed0c922d7504fc0d1577)

席位意见:ACCEPT. 场景表覆盖卡上实测的每个事件,读器与规则文本双向钉住不会漂移;新键只是收编已有的表外半态,不开新口子。

席位核对

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc

Regen-provenance: 6560123525 · 37661b885bac64fd4d09ed0c922d7504fc0d1577 → 08370eb6fe1ff1beea57435ce60f959c89eb7f75 · git diff --name-only 37661b88 08370eb6 -- .claude scripts/pm → (empty)

Base sync after PR #28687 merged (fa494d0538): one merge of origin/main; the three conflicted files (seat-lifecycle.md, seat-post-protocol.md, check-half-states.mjs) were resolved to this branch's side, because main's copy of each equals the stacked base 8eb102ff byte for byte. The PR diff is now the 7 reviewed files only. Self-tests on the merged tree: record-recognisers 351, check-half-states 5185, mailbox-open 71, all exit 0; check:pm-skill-ratchet exit 0.

@os-zhuang
os-zhuang marked this pull request as ready for review November 1, 2026 16:17
@os-zhuang
os-zhuang enabled auto-merge November 1, 2026 16:17
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit db357f6 Nov 1, 2026
47 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-28675-mailbox-doorbells branch November 1, 2026 16:31
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/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants