Repository navigation
Commit e9ae8ef
docs(pm-dispatch): mailbox progress knocks —
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBUyArWn6eHL33NNgvdpkwiW)_
Fixes #29754
Clause-②: no
Metadata-protocol: none
**Rules text plus the mailbox reader.** Two mailbox keys join
`MAILBOX_KEYS` in `scripts/pm/record-recognisers.mjs`, three pm-dispatch
reference files carry the ruled text, and three `CEILINGS` rows rise by
the measured deltas. Nothing published moves (`skip-changeset`). No new
gate, no new label, no new state.
## 维护者速读
- **改了什么**:信箱新增一对键:分诊向认领席敲 `Request: progress · #n · idle since
TIME`,认领席回 `Notify: progress · #n · record ID`。「⛔ 不敲:催」改成频率规则:同一项 60
分钟内只敲一次,答了 ETA 到期前不敲,已停车或等决策卡的项不敲。分诊每轮按「进展」(head 提交、非分诊评论、其席改标,不读
`updated_at`)扫闲置 ≥3h 的在飞卡和 open PR;两轮不答视为掉线,其卡与 PR
归同车道在线兄弟席,分诊只敲兄弟席一次、不代为释放。下班席的在飞同样交兄弟席。
- **为什么改**:您 2026-11-03 裁定 29775 A(「同意29775
A」)并补充「同意:下班席位的卡交给兄弟席」。此前技能禁止用信箱催,催只能写在 PR 或卡上:PR
上三分钟有人答,卡上三条催无人读;分诊也听不到回复。信箱是唯一可靠叫醒席位的通道。
- **风险与回滚**:只动 `.claude/**` 规则文本与 `scripts/pm/`
的读键表,无发布面、无门禁读信箱(信箱永不是合并条件)。回滚即还原本 PR。三个文件各抬 ratchet
上限(56→58、17→20、96→98),每一行都是裁决点名的文字,PR 正文逐条引记录号。
- **先看**:`project-forms.md` 第 48–50 行(新键行、频率行、改写的不敲行);`lanes/triage.md`
第 16–18 行(轮次步骤);`seat-lifecycle.md` 第 38、42–43 行(收班
`Release:`+`Resume-from:` 回队,兄弟席再认领)。
- **两分钟验证**:`node scripts/pm/record-recognisers.mjs
--mailbox-keys`,预期表头写 17 keys 且含两条 `progress` 模板;`pnpm
check:pm-skill-ratchet`,预期 ✓(三个文件各在新上限,行宽 ≤120 字节)。
- **需要你做的**:无,席位落地(Tier S,`.claude/**` + `scripts/pm/**`)。
## What changes
### `scripts/pm/record-recognisers.mjs` — the two keys and their
self-test rows
- `MAILBOX_KEYS` gains `request:progress` (template `Request: progress ·
#n · idle since TIME`; from triage to the claiming seat, or to the
online sibling an off-shift seat's items went to) and `notify:progress`
(template `Notify: progress · #n · record ID`; from the claiming seat to
triage). The `when` rows carry the rate (one per item per 60 min), the
holds (an answered ETA; parked `Blocked-by:` / `Resume-from:`; a
decision-card wait), the idle reading (progress, never `updated_at`) and
the escalation (two unanswered ⇒ round record, seat read as possibly
down), so `--mailbox-keys` prints the whole rule beside the spelling.
- `MAILBOX_FIELD_READERS`: `request:progress` reads the item (`#n`,
bare, as a card OR a PR — `PR #n` does not read) and `idle since TIME`
with `TIME` a `Z` time in `CLAIM_TIME`'s spelling (`2026-11-03T00:06Z`,
seconds optional); `notify:progress` reads the item and `record ID`
through the existing `RECORD_REF`. Both return `item`, so a reader can
tell a card number from a PR number only by looking it up — which is the
card's definition of "item".
- Self-test: a `⭐` row per key (the fixture reads; the placeholder
`TIME`, a duration, a date without a time, a local-offset time, `PR #n`,
a missing field and a fourth field are `unreadable` with `does not
spell`; `record none`, `record ID`, an ETA in place of a record and a
trailing field are `unreadable`). The roster pin moves 15 → 17 keys / 17
readers. The pins already in place cover the rest: every template sent
verbatim is not an event, `--mailbox-keys` prints exactly
`mailboxKeyLines()`, and every key of the table is named as a code span
in 〈信箱 PR〉 and that section names no other key.
- The `MAILBOX_KEYS` doc comment records why the pair was admitted
(ruling 「同意29775 A」, record 6595514258, amendments 6595244387 and
6595840476).
### `.claude/skills/pm-dispatch/references/project-forms.md` 〈信箱 PR〉 (56
→ 58 lines)
```diff
-- ⛔ 不敲:回执、重发、催(逾期归升级);收方订阅 PR 已载者;问维护者走决策卡。
+- 分诊→认领席:认领卡或 open PR 闲置 ≥3h `Request: progress`;反向随记录 `Notify: progress`。
+- 记录 = 项上写下一步与 ETA 的评论、§C 停车或释放;同项 60 分钟一敲,答 ETA 到期前不敲。
+- ⛔ 不敲:回执、重发;收方订阅 PR 已载者;停车或待决策卡的项的进度;问维护者走决策卡。
```
The key row names both keys as `Kind: key` code spans (the self-test
pins the section against the table in both directions). The record/rate
line is amendment 1's rule: what a `Notify: progress` record is (a
comment on the item with the next act and an ETA, the §C park, or a
release), one knock per item per 60 min, an answered ETA holds the clock
until it passes. The 不敲 line loses 催(逾期归升级) — a nudge is now the key
above, at that rate — and gains the two never-knocked cases (parked,
decision-card wait) in their place.
### `.claude/skills/pm-dispatch/references/lanes/triage.md` (17 → 20
lines)
```diff
+- 每轮扫 `pm:dispatched` 卡与非信箱 open PR,进展=head 提交/非分诊评论/其席改标,非 `updated_at`。
+- 进展逾 3h 即敲认领席 `Request: progress`,下轮读答复,无答列轮次记录;两轮无答视为掉线。
+- 掉线或下班席的项归在线兄弟席(`seat-lifecycle.md`),下轮敲兄弟席一次;⛔ 分诊永不释放。
```
The round step of Wanted item 3 as amended: the sweep population
(dispatched cards and non-mailbox open PRs), the idle reading of
amendment 2 item 1 (head commit, any non-triage comment, a label change
by its seat — triage's own nudge bumps `updated_at` and would hide the
item from a naive filter), the knock, the next-round read, the
unanswered list, and the escalation: two silent rounds read the seat as
down, its items go to an online sibling of the lane (the mechanics live
in `seat-lifecycle.md`, which the third line points at), triage knocks
the sibling once and never releases a claim. Push case ⑥ (the 6 h
stale-PR push to the maintainer) is untouched: the knock is what happens
before it.
### `.claude/skills/pm-dispatch/references/seat-lifecycle.md` (96 → 98
lines)
```diff
-- 收班 = 状态 flush + 在飞默认交回队列、不等落地;派发口径失效,简报 ⛔ 不列作继承裁决。
+- 收班 = 状态 flush + 在飞 `Release:`+`Resume-from:` 回队待兄弟席、不等落地;简报非继承裁决。
…
+- 下班席(`seat-offline`,或两敲 `Request: progress` 无答走接管四件)的在飞归同车道在线兄弟席。
+- 兄弟席 `Resume-from:` 引简报或轮次记录认领;待裁先等字;无兄弟席「待席」;安全归安全席。
```
Amendment 2 item 2 (「同意:下班席位的卡交给兄弟席」): the sign-off line keeps the
transit `check-handover.mjs` (a) and `seat-post-protocol.md` 收尾 step 2
read — the departing seat releases its in-flight with `Release:` +
`Resume-from:` back to `pm:queue` — and names the online sibling as the
next claimant (round 1, seat should-fix 1); the two new lines say when
the hand-over applies (sign-off, or two unanswered knocks, the latter
through `execution-duties.md`'s existing 接管四件 shape), what the sibling
cites in its `Resume-from:` claim (the sign-off brief, or triage's round
record where no brief exists — round 1, should-fix 2) and the three
exceptions (a decision-card wait waits for the letter first; no online
sibling ⇒ 「待席」 on the round record; security items stay with the
security seat). 「派发口径失效」 left line 38 to make room: it is implied by the
brief not being an inherited ruling.
### `scripts/pm/check-skill-line-ratchet.mjs` — three ceilings raised by
the measured deltas
The header authorises a raise only by the ruling of record that orders
text into the file, quoted in the raising PR with the measured delta,
the row moved by at most that delta. Every target file sat at headroom 0
with every neighbouring line at 104–120 bytes, so no new rule could be
folded into an existing line; the lines rewritten in place (the 不敲 line,
the sign-off line, and in round 1 lines 42–43) were paid in place.
| file | ceiling | delta | lines the ruling orders | ruling of record |
|---|---|---|---|---|
| `references/project-forms.md` | 56 → 58 | +2 | the key row (Wanted
item 1, 「新增一对键」); the record/rate line (amendment 1) | #29775 record
6595514258 「同意29775 A」; amendment 6595244387 |
| `references/lanes/triage.md` | 17 → 20 | +3 | the sweep + idle reading
(amendment 2 item 1); the knock/escalation (Wanted item 3 + amendment
1); the sibling hand-over (amendment 2 item 2) | record 6595514258;
amendments 6595244387, 6595840476 「同意:下班席位的卡交给兄弟席」 |
| `references/seat-lifecycle.md` | 96 → 98 | +2 | when the hand-over
applies; what the sibling cites and the exceptions (amendment 2 item 2,
which names this file) | amendment 6595840476 under record 6595514258 |
None of the three is a `CROSS_FILE_MOVES` destination, so no
`ruledRaises` record applies; each row's comment quotes the ids and the
maintainer's words. Map total +7.
## Why
The mailbox is the one channel that reliably wakes a seat (a comment on
the subscribed PR), and the text forbade using it to ask for progress.
Measured on 2026-11-03: a nudge on a PR was answered in three minutes;
three nudges on cards woke nobody; triage, subscribed only to its own
mailbox, heard no reply either way. Ruling A on #29775 allows the knock
at one per item per hour; amendment 2 fixes how "idle" is read (triage's
own nudge bumps `updated_at`) and where an off-shift seat's items go.
## Tests
- `node scripts/pm/record-recognisers.mjs --self-test` on the final
head: ✓ 362 cases pass (360 before; the two new `⭐ progress` rows). Run
again through `pnpm check:pm-record-recognisers` (this file's own suite
plus `check-owed-reviews`, `mailbox-open`, `fleet-roster` self-tests),
which is also in the derived gate list below.
- No ablation: the diff adds readers and rules text; no tested behaviour
of an existing key changed (the existing rows all still pass unchanged).
## Acceptance notes
- **Wanted item 4 (the per-seat cap ≤5).** Already the rule on the cap
line (「每席每轮 ≤5 条,p0 豁免,超额归下轮,计入写节奏」), which the knock inherits as any
key does; no byte was spent restating it.
- **Wording choices a reviewer may push back on.** `Request: progress`
reads a bare `#n` for a PR too (the card's "item"), matching
`serial`/`landed` rather than `contract-review`'s `PR #n`; `idle since`
takes a `Z` time only (the spelling every `Claim:`/record stamp here
already uses), never a duration; seat-lifecycle.md line 43 spells the
security seat as 安全席 and the no-sibling case as 「待席」 to fit 120 bytes;
「待裁先等字」 stands for "items waiting on a decision card wait for the letter
first".
- **Round 1 minor item not taken.** Narrowing `lanes/triage.md` line
16's sweep population to owned, non-Version PRs (as push ⑥ spells it)
does not fit the line's 120 bytes without dropping the idle reading;
push ⑥'s own exclusion list (two lines above) still governs what triage
pushes, and the knock line (17) only fires on items with a claiming seat
to address.
- **`check-skill-id-lint`.** The record and comment ids live in the PR
body and in the ratchet's `CEILINGS` comments (a script header, outside
the lint's corpus); the three rule files cite none.
- **Attribution thread link.** The order file spells the skills-lane
seat thread in the attribution line, as this body carries it; the card
thread is `cmsg_01BVUyyPie8gHwoPM18iABwBC6GLUUzuNXWcdKZZCu8ixr`.
- Out-of-scope findings: none.
## Verification
Round 0 readings on **32a1b5f5** (= 7447169, the one change commit,
merged with `origin/main` 592845b; the merge brought in no pm-dispatch
or gate-script file). Round 1 head **d9e8647f** (seat-lifecycle.md lines
38, 42–43 and the ratchet comment rewritten in place): `pnpm
check:pm-skill-ratchet` ✓, `pnpm check:pm-skill-id-lint` ✓, `node
scripts/pm/check-handover.mjs --self-test` ✓ 66 cases, `node
scripts/pm/record-recognisers.mjs --self-test` ✓ 362, `pnpm
check:skill-frame-sync` ✓, `pnpm check:ratchet-remedy-authority` ✓ — all
exit 0.
- `node scripts/pm/record-recognisers.mjs --self-test`: ✓ 362 cases
pass. `--mailbox-keys` prints the 17-key table with both `progress`
templates and their rows.
- `pnpm check:pm-skill-ratchet`: ✓ (self-test, then the gate:
project-forms.md 58/58, lanes/triage.md 20/20, seat-lifecycle.md 98/98;
every edited line ≤120 bytes; the three declared cross-file moves
unchanged, map total down 496).
- `pnpm check:pm-skill-id-lint`: ✓ 109 file(s) clean.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 38 commands for the five changed
paths (merge base 592845b, three-dot); each run serially from a runner
script after `turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` under `os-verify-lock.sh` (VERDICT
command-exit 0, 101 s held) for `check:doc-formula-expressions`'s dist;
exit codes captured before any pipe into `CMD :: exit N` lines; then
`--ran`: **38 derived, 38 run, 0 NOT-MEASURED, 0 UNRUN**, exit 0. Every
one of the 38 exited 0, among them `pnpm check:pm-record-recognisers`,
`pnpm check:pm-dispatch-gates`, `pnpm check:pm-governed-merges`, `pnpm
check:skill-frame-sync`, `pnpm check:ratchet-remedy-authority`, `pnpm
check:scripts-symbol-anchors`, `pnpm check:nul-bytes`.
- Repo-level scans (`pnpm lint`, the type-check lanes, Test Core) are
CI's run on this PR; the seat reads the check-runs at ACCEPT.
## Human Minimum
Each item is one rule from
[CONTRIBUTING.md › Human
Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum),
which links the AGENTS.md text that states it.
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
(`IDLE_SINCE_REF`, `request:progress`, `notify:progress` follow their
neighbours)
- [x] 3. No formatter ran; `pnpm lint` is CI's run (repo-level scan).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (`.claude/**`
and `scripts/pm/**` are not published: `skip-changeset`)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English. (the 维护者速读 block and
the rule text itself are Chinese by the dispatch contract; the quoted
rulings stay verbatim)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzf7EDaUFBmJTyLuKbj
<!-- signed: domain:skills#3 · os-support-ai ·
session_01WGufzhd2Ad9ANGTyAMW7pH -->
---------
Co-authored-by: Claude <noreply@anthropic.com>Request: progress / Notify: progress, one per item per hour, idle read off progress (#29887)1 parent 5f99582 commit e9ae8ef
5 files changed
Lines changed: 72 additions & 7 deletions
File tree
- .claude/skills/pm-dispatch/references
- lanes
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
16 | 19 | | |
17 | 20 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
49 | 51 | | |
50 | 52 | | |
51 | 53 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
38 | | - | |
| 38 | + | |
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1289 | 1289 | | |
1290 | 1290 | | |
1291 | 1291 | | |
1292 | | - | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
1293 | 1305 | | |
1294 | 1306 | | |
1295 | 1307 | | |
| |||
1443 | 1455 | | |
1444 | 1456 | | |
1445 | 1457 | | |
1446 | | - | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
1447 | 1468 | | |
1448 | 1469 | | |
1449 | 1470 | | |
| |||
1589 | 1610 | | |
1590 | 1611 | | |
1591 | 1612 | | |
1592 | | - | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
| 1619 | + | |
| 1620 | + | |
| 1621 | + | |
| 1622 | + | |
| 1623 | + | |
| 1624 | + | |
1593 | 1625 | | |
1594 | 1626 | | |
1595 | 1627 | | |
| |||
0 commit comments