Repository navigation
Commit 728c73e
feat(pm): a ruled decision card still in the box is a patrol row, and the director's ruling lands with its knock (#29515)
domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J
<!-- claude-projects-attribution: {"github_login":"huangyiirene"} -->
_Requested by **Irene** · [project
thread](https://claude.ai/code/project/chan_01TJXtQ1tVD26ecLDZxFS67x?thread=cmsg_01TJXtQ1tVD26ecLDZxFS67xX16uEpSy2CFTwhKrzT6nhy)_
os-dev · domain:skills#1 dispatch · session_01J8VVAm7suYn3TrFHkbu4N3
Fixes #29386
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:总监章程写明,录裁时裁决、正文 `Ruled:` 行和给车道席的 `Notify: ruling`
敲门是同一笔,漏敲是录裁者欠的半状态;半状态巡查新增 H73 行,列出正文已有 `Ruled:` 却仍挂
`needs-user-decision` 超过 6 小时的卡,附裁决 id、已过时长和欠退箱的 `domain:*` 车道席。
- 为什么改:11 月 2 日录裁的 12 张卡,录裁时一次敲门都没有,11 小时后仍全部挂在决策箱里;巡查没有一行读这种情况。
- 风险与回滚:只读巡查,不写任何标签或评论;章程只改一条录裁规则,新增一行由合并 职责一 里两行同义条款付账,仍为 86 行上限。回滚即
revert 本 PR。
- 先看:`scripts/pm/check-half-states.mjs` 的
`h73RuledCardStillInBox`(围栏剥离、取最新时间戳、`Prior rulings on this card:`
重入例外),再看 `lanes/director.md` 第 13 行与第 39 行。
- 两分钟验证:`node scripts/pm/check-half-states.mjs --self-test`,预期结尾的
battery 列表里有 `H73 ruled card in the decision box 28/25`,且全部通过。
- 需要你做的:本 PR 触及 `.claude/**`(Tier S),由 skills 席复核落地,你无需动作。
## What changes
- **`.claude/skills/pm-dispatch/references/lanes/director.md`** (86 → 86
lines, ceiling unchanged)
- New line under the `Ruled:` recording line: the `Notify: ruling` knock
to the card's lane seat lands in the same act, its shape is the one
`../project-forms.md` spells, and a ruling recorded without its knock is
a half state the recorder owes. The existing `⛔ 不新增标签与状态` stays, so the
director still writes no label on the card.
- Paid in the same file: the two 职责一 lines 「处理三分支之一,收尾 PASS 且工已完 ⇒
跟到合并为止」 and 「到合并 = 推进到就绪并点名等待,⛔ 不代维护者合并」 say one thing, and are merged
into one line with the same terms. No ceiling raise.
- **`scripts/pm/check-half-states.mjs`**: new report-only row **H73**,
the body-record half of the runbook's declared 「旗一」.
- Population: open issues carrying `needs-user-decision`, read on the
unscoped open listing beside H62. `needs-user-decision` is not a label
page, so this is where those cards are first visible. It is one label
read and one body read on rows the sweep already holds, so no new
request.
- Record: a line-leading `Ruled:` (the shared directive reader, so
bullet/bold/backtick decoration reads) whose value opens with a numeric
comment id. Fenced blocks are stripped first (`stripMarkdownCode`,
inline spans kept). A mid-sentence mention and a line-leading `Ruled:`
followed by prose are not records.
- Age: from the newest readable UTC stamp on the card's `Ruled:` lines.
A record with no readable stamp reports with its age unreadable, never
as fresh. Threshold `RULED_IN_BOX_STALE_HOURS` = 6, one standing patrol
cycle (`PATROL_CADENCE_HOURS`).
- Re-entry: a card whose governing ruling id is named on its `Prior
rulings on this card:` line (or the list under it) has lawfully
re-entered the box with a new question and is clean.
- Row text: the ruling comment id, the age against the threshold, the
owing `domain:*` lane seat (or "no lane yet, the triage seat routes
it"), the one-act rule and the bell shape's printout. No angle brackets,
no tracker number.
- Band `stall`, registered in `HALF_STATE_FAMILY_BAND` with its reasons.
- Self-test battery `H73 ruled card in the decision box` on the frozen
roster (28 cases, pin 25).
## Why
Card #29386, letter A. The director records a ruling as a `Ruling:`
comment plus a `Ruled:` body line and writes no label; the exit out of
`needs-user-decision` is the lane seat's, and the lane learns of the
ruling from a `Notify: ruling` bell. Measured on 2026-11-02, twelve
cards ruled 01:14Z–10:06Z drew zero bells and all twelve still carried
the label at 12:08Z, the oldest 10 h 54 min after its ruling. No patrol
row read a `Ruled:` line against the label.
## Verification
At 4bf6e86 in objectstack-ai/objectstack:
<details><summary>Self-test and ablations</summary>
- `node scripts/pm/check-half-states.mjs --self-test`: exit 0, 5213
cases pass; battery `H73 ruled card in the decision box 28/25`, every
other battery at or above its pin.
- The controls, each beside its positive twin: the same body without the
label is clean; a `Ruled:` line inside a backtick fence and inside a
tilde fence is clean, and the identical line unfenced fires; a
mid-sentence mention and a table cell are clean; a line-leading `Ruled:`
followed by prose (the H55 fixture's body) is clean; 5h59m is silent and
6h01m fires; a missing stamp reports; a fresh re-ruling silences an old
one, and two old ones report the newer id; the `Prior rulings on this
card:` line naming this id (inline or as a list) is clean, naming
another id fires, and an id after a blank line is not that line's;
closed card, PR row, unreadable labels, missing row and null body are
silent.
- Ablations on a copy of the committed file
(`scripts/pm/zz-ablate-h73.mjs`, deleted afterwards; `git status`
clean): fence stripping removed → 3 cases red; label gate removed → 2
red; id requirement removed → 2 red; re-entry exception removed → 2 red;
threshold removed → 3 red; the sweep push removed → 21 red, the
band/coverage case among them.
</details>
<details><summary>Live sweep</summary>
- `node scripts/pm/check-half-states.mjs` against the live board
(objectstack-ai/objectstack, main 86d36e9 plus this commit), exit 0:
two H73 rows, #28888 (`domain:spec`, ruling 6568787922, ~13h) and #29029
(`domain:server`, ruling 6573561091, ~8h). #29425 (ruled 14:56Z, ~2h) is
under the threshold and silent. That matches the premise re-measured at
16:52Z: three open `needs-user-decision` cards with a line-leading
`Ruled:`.
</details>
<details><summary>Gates</summary>
- `node scripts/pm/dispatch-gates.mjs --commands` (no paths, from the
merge base) derives 54 commands for the two paths. All 54 exited 0, each
exit captured before any pipe. `pnpm --filter @objectstack/lint run
check:doc-formula-expressions` first answered exit 3 (prerequisite: no
built `@objectstack/formula` / `@objectstack/lint`); after `turbo run
build --filter=@objectstack/formula --filter=@objectstack/lint` it
exited 0 on the same head.
- `dispatch-gates --ran`: 54 derived, 54 run, 0 NOT-MEASURED, 0 UNRUN.
- Among them: `pnpm check:pm-half-states` 0, `pnpm
check:pm-skill-ratchet` 0 (`lanes/director.md` 86 against ceiling 86),
`pnpm check:pm-skill-id-lint` 0, `pnpm check:skill-frame-sync` 0, `pnpm
check:doc-authoring` 0, `pnpm check:commit-card-trailers` 0, `pnpm
check:nul-bytes` 0.
- `pnpm exec eslint --no-inline-config
scripts/pm/check-half-states.mjs`: exit 0.
</details>
- No changeset: only `.claude/**` and `scripts/pm/**` change, which
publish nothing, so the PR carries `skip-changeset`.
## Acceptance notes
- **The runbook still says the wiring is another card's.**
`dispatch-runbook.md` 「机械两旗 report-only,归半状态巡查;接线是另一张卡的事」 is now
half-true for 旗一: its body-record half is H73. The runbook is outside
this card's surface and at its own ceiling (245), so it is left for the
seat.
- **旗一's title half stays where it was.** A `[Decision]` title is read
from the queue side by H54; this row reads only the body record, as the
card scopes it.
- **Out of scope as filed:** option B and the reverse direction (a card
moved out of the box with no retrievable ruling id), which the runbook
assigns to the dispatch-time 决策复读.
- **A live shape this row does not read:** a scoped record such as
`Ruled (plugin-ai): ID · letter A · UTC` (one open card carries one,
outside the box) is not a line-leading `Ruled:` and is not read. The
card specifies `Ruled:`; widening the key is a seat call.
## Human Minimum
- [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.
- [x] 3. No formatter ran; eslint over the changed script passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. Nothing publishes, so no changeset; both declaration lines are
filled in.
- [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, except the 维护者速读
section, which the lane asks for in Chinese.
- [ ] The *Before Submitting* list in CONTRIBUTING.md is done. (Left
unchecked: the workspace-wide `pnpm lint` and `pnpm test` were not run;
the changed script has no package tests, and eslint over it was run.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J8VVAm7suYn3TrFHkbu4N3
---
_Generated by [Claude
Code](https://claude.ai/code/session_01J8VVAm7suYn3TrFHkbu4N3)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3d6f230 commit 728c73e
2 files changed
Lines changed: 217 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
38 | | - | |
| 39 | + | |
39 | 40 | | |
40 | | - | |
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| |||
0 commit comments