Repository navigation
pm-dispatch(mailbox): one mailbox PR per seat with a closed doorbell vocabulary; seat-online/offline notices; alternate pick for multi-seat lanes - #27143
Conversation
…rbell vocabulary, seat-online/offline notices, alternate pick for multi-seat lanes project-forms.md 〈信箱 PR〉 carries the per-seat shape (pinned + pm:mailbox, body first line To: LOGIN, opened at seating and closed at hand-over or account switch), the seven first-line keys and the four anti-chatter rules; seat-lifecycle.md 〈座位贴协议〉 the seat-online / seat-offline acts; execution-duties.md 〈候选与批次〉 the alternate pick; SKILL.md 入口与角色 the maintainer's form of the seats (resident, an account out of tokens is replaced by one that reopens the seat and its mailbox). Every ceilinged file stays at its ceiling: new lines are paid by deleting lines whose content has a home elsewhere. record-recognisers.mjs reads the seven keys (readMailboxLine, mailboxEvents, MAILBOX_KEYS) beside the records it already reads, with a 21-case battery that pins each template verbatim in the protocol text; mailbox-open.mjs sends pinned, writes To: LOGIN as the body's first line and names the closed vocabulary. No gate, workflow or queue guard reads a mailbox. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg
…ilbox-per-seat-doorbell
…d and register it in the dispatch-gates census The doorbell battery reads project-forms.md to pin the seven key templates verbatim — a population claim on a governed file. check:pm-dispatch-gates reds a new governed read until it is classified: the read is declared (self-test-reads marker in record-recognisers.mjs), GOVERNED_READ_FLOOR carries its row (declared: true) and the live-declaration roster of the self-test gains its ninth line. Measured: a change set holding project-forms.md alone now derives pnpm check:pm-record-recognisers, keyed on the declaration. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg
Contract reviewServed-tier: Read at 2026-10-28T08:18Z by the ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
维护者速读 — 席位终稿 · PR #27143 · head
|
Fixes #27077
Clause-②: no
Metadata-protocol: none
维护者速读
pm:mailbox+pinned,正文首行To: LOGIN),信箱上只允许七种一行式门铃(3 种 Request、4 种 Notify),配四条防刷屏硬规则;坐席发 seat-online、收班发 seat-offline;多席车道按优先序交替取卡;record-recognisers.mjs能读这七键,mailbox-open.mjs开信箱时带pinned并把To:写在正文首行。PACKAGE#*, D9 amendment) #26766 的契约复审等了 9 小时)。改成每席一个信箱后,席位之间可以互相按门铃「推」,而且铃声只带七种键与编号,不会演变成 AI 互聊。pinned标签在本仓并不存在(404),首次开信箱时需要看标签是否落上(见 Acceptance notes)。references/project-forms.md〈信箱 PR〉(七键与四规则,每行 ≤120 字节);②scripts/pm/record-recognisers.mjs的readMailboxLine(拒收面:表外键、第二行、超 200 字节);③scripts/pm/mailbox-open.mjs的PR_LABELS与mailboxBody。node scripts/pm/record-recognisers.mjs --self-test预期末行✓ record-recognisers self-test: 254 cases pass (…);pnpm check:pm-skill-ratchet预期四个受管文件各自headroom 0且绿。What changes
One PR, six files, the four ruled sections A–E of the card:
A. One mailbox PR per seat —
references/project-forms.md〈信箱 PR〉: one draft PR per seat onmailbox/LOGIN, labelspm:mailbox+pinned, body first lineTo: LOGIN, opened at seating bymailbox-open.mjsand subscribed by the seated session, the seat post's stub pointer line carries its number, never merged, closed at hand-over or account switch with the successor opening a new one.B. The closed doorbell vocabulary — the same section: the first line of a mailbox comment is one of the seven keys (three
Request:, fourNotify:), each key one line with its from → to and when; the four hard rules: one event one line ≤200 bytes keys-and-numbers only, anything outside the table is a half-state; no acknowledgements (the action is the receipt); no re-send and no nagging (the existing escalation rules take over); ≤5 per seat per round, p0 exempt, every send counts against the write pace; a mailbox comment is a colleague's statement, never an authorisation.C. Alternate pick for multi-seat lanes —
references/execution-duties.md〈候选与批次〉, one line beside the take order: seat 1 takes the odd positions, seat 2 the even ones over the lane's total order;splitis sent only to break the alternation; the claim timestamp stays the tie-breaker. Hot-file serial is untouched (its rule stands at L26 of the same section).D. Online / offline —
references/seat-lifecycle.md〈座位贴协议〉, two lines: seating sendsseat-online, the 收班简报 act sendsseat-offline; a forced takeover's newcomer sendsseat-onlinefor the walled account (which can write nothing); a Routine seat cannot be woken, so its patrol reads the mailbox (the card's P4, said in the text).SKILL.mdL74 is corrected to the maintainer's form: seats including triage are resident; an account out of tokens is replaced by one that reopens the seat and its mailbox; the triage seat's default tier and 「裁决归总监席」 stay.E. Recogniser —
scripts/pm/record-recognisers.mjs:MAILBOX_KEYS(the seven templates, spelled exactly as the protocol text prints them),readMailboxLine(body)(pure: the first line in, aread/refused/unreadablereading out — kind, key, numbers, head sha, login, lists) andmailboxEvents(rows)(each comment stamped with its id,created_atand author, plus the half-states by comment id). Exported besidecontractReviewHandoff; no CLI flag; nothing fetched. ⛔ No gate, workflow or queue guard reads a mailbox — the battery pins that a doorbell comment beside a## Contract reviewrecord leaves the locator's reading untouched.Tooling —
scripts/pm/mailbox-open.mjs: sendspinnedwithpm:mailboxandskip-changeset; writesTo: LOGINas the body's first line (the protocol text has said正文首行 To:since the mailbox was born, while the tool wroteMailbox-of:— one spelling now, the ruled one) and reads it back for refusal ①; the body names the seven-key rule; the close comment says hand-over or account switch. Refusal ① (one mailbox per login per repo) is unchanged: measured on the seat posts, a login holds one seat at a time and a lane's second seat is a second login, so per-login already equals per-seat — the card's open question answered by measurement, no seat name needed.Companion registration (outside the claim's declared file surface — the gate's own remedy, named here as the bounded in-place rule requires) — the doorbell battery reads
project-forms.mdto pin the templates, which is a population claim on a governed file, andcheck:pm-dispatch-gatesreds a new governed read until it is classified. Three mechanical edits, each the shape the gate prints: theself-test-readsdeclaration line inrecord-recognisers.mjs(line 77); thedeclared: truerow for it inGOVERNED_READ_FLOOR(scripts/pm/dispatch-gates.data.mjs, with a docblock paragraph); and the ninth line of the live-declaration roster inscripts/pm/dispatch-gates.self-test.mjs(eight → nine). Measured both ways: before,check:pm-dispatch-gatesfailed 2 of 1935 (unpinned: record-recognisers.mjs::project-forms.mdandcheck:pm-record-recognisers … (silent)); after, 1935 pass, anddispatch-gates --commands … project-forms.mdalone derivespnpm check:pm-record-recognisers, keyeddeclared self-test read by scripts/pm/record-recognisers.mjs. No open PR touches either dispatch-gates file (file scan of the 32 open PRs, 2026-10-28).Angle-bracket placeholders are spelled
LOGIN/Nin this body (the platform strips tag-shaped fragments); the files keep the corpus'smailbox/<login>spelling.Line budget (every ceilinged file stays at its ceiling;
check:pm-skill-ratchetgreen, headroom 0 on all four).claude/skills/pm-dispatch/SKILL.md.claude/skills/pm-dispatch/references/project-forms.md.claude/skills/pm-dispatch/references/seat-lifecycle.md.claude/skills/pm-dispatch/references/execution-duties.mdEvery new line is paid by deleting a line whose content has a home elsewhere — never by re-wrap. Prose lines are ≤120 bytes (
wc -c), measured per line below.project-forms.md — 〈信箱 PR〉 grows from 6 lines (heading, blank, 4 bullets) to 16 (heading, blank, 14 bullets): +10, paid by 10 deletions elsewhere in the file. The three old bullets of the section are rewritten in place (their facts survive in the new lines); the fourth (
pm:mailbox PR 不欠 CI…, 67 B) is kept verbatim.车道席:认领、派发、PR、跨账号复审;总监席:决策箱与决裁批、发版、日报、总表。(112 B)lanes/director.md职责一–四 headings分诊席:只写标签与审计评论;分诊与总监可召进任一项目,⛔ 不绑定项目。(99 B)lanes/triage.mdL4 (「由维护者一句召唤到任一项目…只写标签与审计评论」) + SKILL.md L55 (director: 任一车队项目可召)唯一不变量在账号不在项目:批准账号永不跑席位(SKILL.md 全体座位的不变量);本文不另加。(120 B)一张也没有 ⇒ 项目闲置;每席恰一条席位线程、恰一车道,一席一贴。(93 B)seat-post-protocol.mdL9 (一席一贴) +coordinator.md§一 (每条车道开一个席位线程)协调者每次开场把指令行与座位贴对照:逐席按 seat-post-protocol.md 坐席或接管。(106 B)coordinator.md§一 L10–L13 (lanes declared by the instruction line, seat the empty post, takeover per project-forms)总监席只被维护者召唤与 PR 订阅唤醒;摘要与收件箱扫描在召唤内做;见 lanes/director.md。(118 B)lanes/director.md授权与形态 (L7 人工召唤, L24 纯召唤制 ⛔ 无任何 routine, L12 入口); same file L31 keeps 「总监席不建」并发上限是维护者在该项目定的姿态;⛔ 不是协议常量,本文不写数字。(95 B)coordinator.md§三 L23 (卡线程上限全项目一个数,由维护者按项目言明)撞墙账号写不出任何东西,接管永不等它的注记或简报。(75 B)seat-lifecycle.mdL29 (强制接管令…⛔ 不等原认领者注记) +execution-duties.mdL83 (认领人不可达 ⇒ 接管…⛔ 不判死活); the section heading 「接管不等撞墙账号」 keeps the thesis跨账号 Release: 点名旧会话与出处三件;新 Claim: 带 Resume-from: BRANCH@SHA,新 dev 续旧分支。(120 B)execution-duties.md认领 ①–④ (L84–L86), which L40's pointer 「四件形状见 execution-duties.md 认领节」 already names读自座位贴、标签、assignee 与 PR 查询;生成视图 ⛔ 不手改(cross-repo-coordination.md 规则 5)。(120 B)lanes/director.mdL82 (车队总表随本摘要重生成…一席一行,⛔ 不手改) + SKILL.md L92 (状态只经标签、assignee、正文行与座位贴读写)New 〈信箱 PR〉 lines, bytes: 107 · 115 · (67 kept) · 115 · 119 · 118 · 120 · 119 · 118 · 116 · 119 · 115 · 118 · 118. The seven key lines carry the card's spellings verbatim inside the code span; the from → to and when after the colon are compressed to fit the cap, and the seat-online / seat-offline rows carry only from → to because their when lives in seat-lifecycle.md (section D).
seat-lifecycle.md — +2 (115 B, 101 B) after the 收班 line of 〈座位贴协议〉, paid by: L47
接管/移交 = 改正文 + 审计评论(只作审计);收尾清单等细则见 references/seat-post-protocol.md。(119 B) → homeseat-post-protocol.mdL27 and L33 (回收 / 当场 = 改正文 + 审计评论) and its 交接收尾清单 section; L48epic 委托不入座位贴体系;新 packages/spec 工作恒归 spec 座位。(82 B) → home same file L61 (新立 spec/公共契约 sub-issue 照旧转 spec 座位队列) +cross-repo-coordination.md(协议恒归 domain:spec).execution-duties.md — +1 (115 B) after the 全序 lines of 〈候选与批次〉, paid by L37
文件面不相交只保证文本可合并;跨文件语义耦合由队列 CI 逮住,⛔ 不读作不可能冲突。(117 B) → home AGENTS.md Multi-agent discipline §10 (「A clean merge is not a working merge … that second CI round is where joint breakage surfaces」).SKILL.md — L74 replaced in place, 114 B → 116 B:
分诊席跑直连会话、默认判断档;直连会话按召唤跑,⛔ 无自醒不常驻;裁决归总监席。→分诊席与车道席常驻,断粮即换账号重开该席与信箱;分诊席默认判断档;裁决归总监席。. The line sits outside the regionscheck:pm-governed-prose(路径分叉) andcheck:skill-frame-sync(升级与决策) anchor; both green.Tests
node scripts/pm/record-recognisers.mjs --self-test→ exit 0,✓ record-recognisers self-test: 254 cases pass(233 before + the new battery of 21;SELF_TEST_BATTERY_FLOOR13 → 14). The battery reads one example of each of the seven keys (kind, numbers, head sha, login, lists, verdict), pins every template verbatim inproject-forms.mdand that the text lists exactly seven, tolerates the platform's appended footer and key decoration, and refuses: free text, an acknowledgement (收到,谢谢!), an empty comment, an eighth key of either kind, a lowercase kind, a line over 200 bytes, a second line of content, and a known key whose line does not spell its template (no head, 6-hex head, bad login, missing mailbox, missing lane, non-numeric record).mailboxEventscarries comment id / time / author and lists the half-states by comment id.node scripts/pm/mailbox-open.mjs --self-test→ exit 0,✓ mailbox-open self-test: 45 cases in 8 batteries pass(two new pins:pinnedis read offstale.yml'sexempt-pr-labelsandpm:mailboxis not in it; the body opens withTo:and names the closed vocabulary; the open-plan battery floor 8 → 9).node scripts/pm/check-governed-queue-guard.mjs --self-test→ exit 0, 401 cases (it loads the recogniser through the real lazy import).pnpm testowed: the diff touches no package (turbo lshas no root package, so this is 「不在图上」, not 「动不了」).Acceptance notes
pinneddoes not exist as a label on objectstack-ai/objectstack —GET /repos/objectstack-ai/objectstack/labels/pinned→ 404 on 2026-10-28, and so doeswork-in-progress; ofstale.yml's threeexempt-pr-labelsonlysecurityexists (200). The text makespinnedmandatory and the tool sends it, as ruled; whether the relay's labels leg (a POST to the pull's issue-labels path) creates a missing label is a platform reading the first mailbox open takes, not one this PR asserts (a write).scripts/pm/ensure-pm-labels.shis where a label the fleet relies on is created — out of this card's file surface, left for the seat (carrier: seat). Dedupe words: pinned, exempt-pr-labels, stale.yml, work-in-progress.scripts/pm/check-half-states.mjshas no H-row reading a mailbox, and E forbids a gate reading one. The reading a patrol takes ismailboxEvents(rows).halfStates; wiring it into the report-only patrol is a follow-up, not a merge condition.To:vsMailbox-of:— the tool'sMailbox-of:body line is retired for the protocol'sTo:spelling: 0pm:mailboxPRs are open (measured 2026-10-28), so there is no stored instance to migrate and no dual spelling to keep (创业阶段不渐进).SKILL.md(L161/L162/L183 and L166) — region-disjoint from L74; neither had landed at07d18e69(this branch mergedorigin/mainat that sha before opening; the merge brought nothing on this file surface and did not move the lockfile).首次坐席自建本项目的 routine…(project-forms L31) was kept because its 「分诊席加每小时裸卡轮」 clause has no other home; the 总表 section pays only one line because itsobjectstack 置顶 issue「车队总表」fact has no other home.Verification
Local subset = the derivation
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon the final head (no paths passed; change set from the merge base07d18e69: 8 paths, 0 uncommitted), 41 families, every command run in the foreground with its exit captured before any pipe and reconciled with--ran(ran-file lines carry:: exit N). Every family CI runs rides the PR's check-runs (Lint & Repo Gatescarries thecheck:*set); they are not re-run here beyond that subset and readin_progressat report time.Head
d82ca533.--ranverdict:✓ dispatch-gates --ran: 41 derived famil(ies) accounted for — 40 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 claimed)..41 derived families on head d82ca53 — command · exit (captured before any pipe) · wall
node scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/check-declaration-mirrors.mjsnode scripts/check-declaration-mirrors.mjs --self-testnode scripts/check-self-test-wired.mjsnode scripts/check-self-test-wired.mjs --self-testnode scripts/check-self-test-workflow-commands.mjsnode scripts/check-self-test-workflow-commands.mjs --self-testnode scripts/check-whole-set-label-write.mjsnode scripts/check-whole-set-label-write.mjs --self-testnode scripts/ci/plan-test-core-shards.mjs --self-testnode scripts/pm/bare-root-worklist.mjs --self-testnode scripts/pm/check-governed-queue-guard.mjs --self-testnode scripts/pm/check-harness-current.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressions@objectstack/lint/formulaunbuilt — nothing measured, NOT MEASURED; CI builds them)pnpm check:agent-test-spellingpnpm check:bash32-floorpnpm check:ci-filter-paritypnpm check:cli-command-idspnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:entry-guardpnpm check:gitlink-declaredpnpm check:nul-bytespnpm check:parse-guardpnpm check:pm-dispatch-gatespnpm check:pm-expected-skipspnpm check:pm-governed-mergespnpm check:pm-governed-prosepnpm check:pm-record-recogniserspnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:pnpm-filter-targetspnpm check:pnpm-flag-spellingspnpm check:ratchet-remedy-authoritypnpm check:refd-timer-probepnpm check:scripts-symbol-anchorspnpm check:skill-frame-syncpnpm check:watch-hint-literalLocal-only readings (CI runs their
--self-testonly):node scripts/check-skill-frame-freshness.mjs --no-fetch→ exit 0 (0/1 framework files are byte-identical to the ref (wording differences are not drift)— the frame itself is unchanged; L74 is outside it).node scripts/pm/mailbox-open.mjs --self-test→ exit 0, 45 cases (not a derived family: nocheck:script names it; run because the diff edits it).node scripts/pm/check-governed-queue-guard.mjs --self-test→ exit 0, 401 cases.No package is touched, so there is no build closure (①), no package test (②) and no changeset:
.claude/**andscripts/pm/**publish nothing (skip-changesetdeclared on the create). No dogfood: no!in the title and no(narrowing)arm.Generated by Claude Code