Repository navigation
Commit 82f60eb
docs(pm-dispatch): a queue card with no triage audit is knocked by the lane and derived by the sweep (#29940)
<!-- 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 #29682
## 维护者速读
- **改了什么:** 车道席遇到「有标签、无分诊审计评论」的排队卡,当轮用既有门铃键 `Request: triage · #n ·
first-touch audit` 敲分诊信箱,分诊空席改敲总监(`execution-duties.md` 候选节);分诊席章程加一条:每轮
sweep 推导这类卡并照首触补审计、报数(`lanes/triage.md`);半状态巡查 `check-half-states.mjs`
加一行只报告的 H74,从正文与评论两个通道推导同一集合,豁免 SKILL.md 的三类免审计卡(非车队卡、main-red
锚卡、承父卡域/级的子卡)与机器锚卡的身份标签。
- **为什么改:** 您 11-03 的原话「他在等,为什么没有用邮箱提醒你,是否应该立卡改进技能」。立卡席把域、级与 `pm:queue`
一笔挂齐,分诊 sweep 的五个析取都按「缺标签」找卡,看不见它;车道一侧读到「无分诊审计评论 ⛔
不得认领」就停着等。两边互等,没人被提醒;当日实测 15 张,含两张 p1。
- **风险与回滚:** 文本两处各净零行(新增一行以删一行重复内容付账,两文件仍在棘轮上限,行宽 ≤120 字节);H74
只报告、不写标签不拦落地,每轮对正文无记录的排队/阻塞卡各买一次评论首页,对两个通道都干净的卡再买一次父卡读(当前 open
`pm:queue` 33 张 + `pm:blocked` 23 张为上界)。回滚 = revert 本 PR,无数据与门禁副作用。
- **先看:** ① `scripts/pm/check-half-states.mjs` 的 H74
节(`isTriageAuditRecord`
两种记录形状、`isFleetCard`、`H74_MAIN_RED_ANCHOR_TITLE`、`h74InheritsFromParent`
与 `H74_EXEMPT_LABELS`);② `execution-duties.md` 第 63 行门铃句、第 121
行降档理由并入与删掉的旧第 120 行;③ `lanes/triage.md` 第 16 行 sweep 句与删掉的末行。
- **两分钟验证:** `node scripts/pm/check-half-states.mjs --self-test`,预期末行
`5222 cases pass`,电池清单含 `H74 first-touch audit 60/46`。
- **需要你做的:** 无,席位落地(`.claude/**` 为 Tier S,席内达档复核后由席位入队)。
Clause-②: no
Metadata-protocol: none
## What changes
**Lane side — `references/execution-duties.md`, 候选与批次 section (net 0
lines, 188/188).** One line added directly under the generic doorbell
line 「⛔ 不静默等分诊」: a candidate with no triage audit comment is knocked in
the same round with the existing key `Request: triage · #n · first-touch
audit`; when the triage seat is vacant the knock goes to the director
(`project-forms.md` routes a vacant triage to the director only on its
`Request: main-red` row, so the route is spelled here for this key); and
⛔ the gap is never reported to the maintainer in the mailbox's place.
The rule sits here rather than beside SKILL.md line 171 (where 「无分诊审计评论
⛔ 不得认领」 lives) because this file is where the execution seat's candidate
definition and its generic knock already live, SKILL.md is at its
ceiling with no restated line to pay with, and the card asked for one
place only. Paid by deleting the line 「降档的档位与理由记入认领评论 Container & model
行;档位逐次派发显式传参,永不省略」 — and because its first half (the downgraded tier AND
its reason go on the claim comment) was stated nowhere else, that half
is folded into the effort line below (now 「effort 按卡难度定,与档位、降档理由写进
Container & model 行与派发令,显式传参,⛔ 不留空」); its second half (the tier passed
explicitly at every dispatch) was already carried by that same line.
One thing the card said that the text does not spell, on purpose: "one
doorbell per card or one listing several" — the `request:triage` reader
(`record-recognisers.mjs`) takes exactly one `#n`, so the line says one
knock per card.
**Triage side — `references/lanes/triage.md` (net 0 lines, 20/20).** One
line added under the push list: the sweep also derives `pm:queue` /
`pm:blocked` cards with no audit comment (exemptions as the H74 row
lists them), audits each as an ungraded first touch and reports the
count. Paid by deleting the last line 「`security#1` 空缺时沿用
`security-findings.md` 第 7 节处置,⛔ 不代席、不追加细节」, which restates
`security-findings.md` §7 (no details, hand to the maintainer) and §9 (a
vacant `security#1` seat hands to the maintainer) — and §8 of that file
forbids a second copy outright. The line above it still points at
`security.md`.
**Tooling — `scripts/pm/check-half-states.mjs`, H74 (report-only).** The
triage sweep's disjunct derivations do live in this script (H13 mirrors
disjunct ③), so the card's "if a script owns the sweep" arm applies. H74
speaks about open issues carrying `pm:queue` or `pm:blocked`, minus:
- the card's exempt labels (`tracking`, `pm:epic`, `pm:seat`, `qa-run`,
`status:parked`, `queue-flake-anchor` — the label
`merge-queue-triage.yml` stamps on its anchors) plus
`platform-checklist-watchdog`, the identity label of the one in-tree
anchor template `fleet-patrols.yml` files AS `pm:queue,tooling` (title
`check:platform-checklist is red on main`, which the main-red title
reading below does not open; the identity label is the machine's and is
never graded off, so it is the right key). A census of every
`.github/workflows/**` template that files a card found no other that
applies `pm:queue` on create;
- SKILL.md's three audit-exempt classes: a **non-fleet card** (the
card's "`[maintainer]` cards", read as 「非车队卡:作者非 `Bot` 且首行非席位签名」 through
one shared `isFleetCard` — an App author or a seat-signed body is the
fleet's, an unreadable author is never promoted to a human's — which
H67's read order now uses in place of its inline copy); a **main-red
anchor card**, by its measured title openings (`main red:` / `main red
(…)` / `main red since …` as seats file them, and `: red on main` as the
scheduled-run cards file them; a card *about* main-red such as
`pm-dispatch(main-red): …` stays in); and a **sub-issue inheriting its
parent's domain or grade**, read through the sub-issues parent fetch
class H45 already takes (`subIssueParentPath`, same documented-404
reading), bought only for a card whose thread was READ and found clean —
a non-404 failure fires the card's own UNJUDGED row through a sentinel,
never silence.
A triage audit record is either a text whose first line is a `triage#N`
signature and which carries a `## Triage` heading (fences stripped), or
one of the older openings `isTriageRulingComment` already reads for H33
(`Triage:`, `Grading`, …, signature-tolerant). The body is read first; a
body-clean member buys one comment-page read off the shared cache (H55's
gated-fetch pattern, `needsTriageAuditComments`), so a card H2 or H4
already fetched costs nothing. Three thread states: unconsulted ⇒ silent
(never a fabricated row); unreadable ⇒ an UNJUDGED row; read ⇒ fires
when neither channel carries a record. Band `state` (H13's), a coverage
pair in the summary sentence (`First-touch audits (H74): comment threads
read on X of Y …`), two count keys on the forwarding contract, no label,
no exit code, no gate.
**Not changed:** `SKILL.md` (untouched; line 171 stays the lane's ⛔
line), `triage-duties.md` (held by #29445's dispatch),
`record-recognisers.mjs` (the `request:triage` key is reused as is), no
new key, label, gate or ceiling raise. The line ratchet's raise rule
asks for a ruling comment id; the maintainer's word on this card is a
session sentence with no comment id, so every added line is paid by
deletion.
## Why
Both sides had a rule that read 「not mine」: triage's sweep derived cards
by missing labels and this shape has every label; the lane read the
audit-less card as non-candidate and waited. The maintainer's own word
is on the card. The fix is the smallest pair that converges: the lane
rings the existing bell in the round it notices, and the sweep lists the
same set so the first triage round after landing finds nothing a lane
has not also knocked about.
## Tests
- `node scripts/pm/check-half-states.mjs --self-test` — `✓
check-half-states self-test: 5222 cases pass`, battery `H74 first-touch
audit 60/46` (frozen roster, pin under the count). The battery drives: a
card with an audit comment and one without (the card's acceptance pin),
each record shape as its own positive control (signed `## Triage`, `###
Triage`, the older `Triage:` opening, a lane-signed self-triage
`Triage:` record), the controls that must still fire (a triage-signed
comment with no heading, a lane-signed heading below prose, a fenced `##
Triage` citation), the body channel (clean and buys no thread), the
three thread states, every exempt label beside a non-exempt twin, the
watchdog anchor as `fleet-patrols.yml` files it beside the same card
without its identity label (fires — the title alone does not exempt it),
the non-fleet card beside its three fleet twins (seat-signed body under
the same human account, App author, unreadable author) and the
`isFleetCard` leg of H67's order, six measured main-red anchor titles
beside a card about main-red and a mid-title `red on main`, the
sub-issue exemption by domain and by grade beside a parent sharing
neither, a parentless 404, an unread parent, the unreadable-parent
UNJUDGED row, the parent-read gating policy, the population edges
(closed, PR row, unreadable labels, missing row, null body), the row
text (no angle bracket, no tracker number, both remedies named), the
summary pair and its shortfall clause, the two count keys on the
contract, the band and the emitted-code coverage.
- `npx eslint scripts/pm/check-half-states.mjs` — exit 0.
- `pnpm check:pm-half-states`, `pnpm check:pm-skill-ratchet` (both
edited files at their ceilings, widest line 120 bytes), `pnpm
check:pm-skill-id-lint` (109 files clean) — exit 0 on the final head.
## Acceptance notes
- The audit-record reader is deliberately the union of the card's two
shapes, not a new vocabulary: `isTriageRulingComment` is reused
unchanged, so a `## Triage` heading as a comment's FIRST line reads as a
record under the older anchor whatever the signature (H33's reading,
pinned as kept); the signed shape only adds the case where the heading
sits below prose.
- A `[maintainer]` title prefix has no spelling in the protocol text or
scripts on `origin/main`, so the card's "`[maintainer]` cards" are read
as SKILL.md's 非车队卡 (a human author with no seat signature) rather than
as a title test that would never match.
- Cost: at most one comment-page request per body-clean member per
sweep, plus one sub-issues parent read per card whose thread was read
and found clean (the set that would otherwise fire — fifteen the day the
word was given); measured population at dispatch 33 open `pm:queue` + 23
open `pm:blocked` cards, less exemptions and cards H2/H4 already fetch.
No cap was added because H2, H4 and H45 buy the same way without one;
the summary pair reports any thread shortfall, and an unreadable parent
is its own UNJUDGED row.
- Deviation from the card's letter, declared: the lane line spells one
knock per card (reason under *What changes*).
- `Writes:` `git push` ×4 (WIP, final, patch round 1, patch round 2),
relay `pr_create` ×1 (draft, `labels: ["skip-changeset"]`, no
assignees), `post-stamped.mjs --comment` ×1 for the `os-dev-report`.
`mcp_calls`: GitHub MCP reads only (`issue_read` ×6, `list_issues` ×2);
no MCP write.
## Verification
Final head `1281c79a` (three commits on the branch: the change, contract
review r0's four should-fixes, contract review r1's one). On the first
head `197f87e0`, derived with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` against merge base
`21510bba` (3 paths), 54 commands run serially in the worktree with each
exit code captured before any pipe, then reconciled: `✓ dispatch-gates
--ran: 54 derived famil(ies) accounted for — 53 run, 1 NOT-MEASURED (1
DERIVED from a recorded exit 3)`. The two later heads touch the same
three paths and re-ran the gates the diff can move: `pnpm
check:pm-half-states`, `pnpm check:pm-skill-ratchet`, `pnpm
check:pm-skill-id-lint` and `npx eslint` on the script, all exit 0.
- 52 of 54 exit 0 on `197f87e0`, among them `pnpm check:pm-half-states`
(the H74 battery), `pnpm check:pm-skill-ratchet`, `pnpm
check:pm-skill-id-lint`, `pnpm check:pm-dispatch-gates`, `pnpm
check:pm-governed-merges`, `pnpm check:pm-post-stamped`, `pnpm
check:skill-frame-sync`, `pnpm check:single-claim-paths`, `pnpm
check:nul-bytes`, `pnpm check:doc-authoring`, `pnpm
check:platform-checklist`.
- `node scripts/check-release-section-coverage.mjs --strict :: exit 1` —
the same finding, byte for byte, on the primary checkout at
`origin/main` `21510bba` (`content/docs/releases/v18/` has no heading
for the published spec 18.3 series). A release-owned page this diff does
not touch; out of scope, not fixed here.
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions ::
exit 3` — PREREQUISITE NOT MET (the lint package's built output is
absent in a fresh worktree; `--ran` classifies the recorded exit 3 as
NOT MEASURED). CI builds it; read the PR's `Lint & Repo Gates` check-run
for the verdict.
- Not run locally, by the derivation's own account: the 43
artifact-roster families, 11 wide-population families, 10
changeset-pending families (no changeset: the diff publishes nothing),
the Type Check lanes and the `Plan Test Core shards` step — CI's runs on
this PR are the readings for those. No package test or typecheck is
owed: the diff touches no package.
- `git merge-tree --write-tree HEAD origin/main` clean at every push;
`origin/main` has not moved any of the three paths since the branch
point.
Repeat the two-minute check: `node scripts/pm/check-half-states.mjs
--self-test` → `✓ check-half-states self-test: 5222 cases pass … H74
first-touch audit 60/46`.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(no spec change)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; eslint passes on the edited script.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset — this diff
publishes nothing (`.claude/**`, `scripts/pm/**`), so `skip-changeset`;
both declaration lines are above.
- [x] 6. `packages/spec` 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 maintainer's
ruling is quoted verbatim, untranslated).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzgjcGBFsSmg6tHbbJq
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WGufzgjcGBFsSmg6tHbbJq)_
<!-- signed: domain:skills#3 · os-support-ai ·
session_01WGufzhd2Ad9ANGTyAMW7pH -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 40fa433 commit 82f60eb
3 files changed
Lines changed: 374 additions & 9 deletions
File tree
- .claude/skills/pm-dispatch/references
- lanes
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| 63 | + | |
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
| |||
117 | 118 | | |
118 | 119 | | |
119 | 120 | | |
120 | | - | |
121 | | - | |
| 121 | + | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | | - | |
0 commit comments