Commit 90b23ab
docs(pm-dispatch): the detach reading names where its pid comes from (#18405)
Fixes #18362
One row of `.claude/skills/pm-dispatch/references/platform-readings.md`,
re-keyed in place. No other row is touched.
## The row, before and after
before — 119 bytes:
```
- `check:pm-dispatch-gates` 单机 430–450 秒贴调用方容器上限 ⇒ detach 加 `tail --pid`;超时非读数。
```
after — 113 bytes:
```
- `check:pm-dispatch-gates` 单机 430–450 秒贴容器上限 ⇒ detach 加 `tail --pid=$!`;超时非读数。
```
The pid's source is now IN the prescription, in the spelling a reader
copies: `tail --pid=$!`. The three parts the card protects are
byte-identical — `430–450 秒`, `detach`, `超时非读数`. The one word paid to
the byte cap is `调用方` in `贴调用方容器上限`; `容器上限` still names what the seconds
bump against. That trim is the route the dispatching seat proposed, and
it is what buys the `=$!`.
Located by content, never by line number (the card's own correction,
comment 5692424694): `grep -n 'tail --pid'` matched exactly one row on
`origin/main` at `9a566305e` before the edit and matches exactly one
after.
## Line budget — the whole edit had to fit an existing line
`pnpm check:pm-skill-ratchet` holds this file at **466 / 466 lines,
headroom 0**, and caps every line at **120 bytes**
(`scripts/pm/check-skill-line-ratchet.mjs`, section "Why 120 bytes"). So
the edit is an in-place re-key: **466 → 466 lines**, the row **119 → 113
bytes**, 7 bytes under the cap. Nothing was re-wrapped to buy room, and
no line was added.
## Acceptance item 2 (the starred half) is NOT written here — the
measurement, then the reason
The card's item 1 (name the pid source) is a hard requirement and is
landed. Item 2 (⭐ explain why `pgrep -f` self-matches) is starred, and
every spelling of it was measured against the 120-byte cap:
| candidate row | bytes | verdict |
|---|---|---|
| landed, item 1 only | 113 | fits |
| item 1 + `⛔ pgrep 自匹配`, qualifiers kept | 133 | 13 over |
| item 1 + `⛔ 不用 pgrep`, qualifiers kept | 130 | 10 over |
| item 1 + `⛔ pgrep 自匹配`, minus `单机` | 126 | 6 over |
| item 1 + `⛔ pgrep 自匹配`, minus `单机` and `容器` | 120 | fits, zero margin
|
| item 1 + terse `⛔ 非 pgrep`, minus `单机` | 120 | fits, zero margin |
So both items fit on one row only by deleting measured qualifiers out of
the half the card forbids disturbing (`单机` says the 430–450 seconds is a
single-box reading, `容器` says which limit it bumps against), and the two
that do fit sit exactly at the cap with no margin for the next author.
Against that, the starred half is already written down twice in this
repo, both times with more room than a 120-byte row has:
- `AGENTS.md:534` (Multi-agent discipline §8) — "⛔ One process table per
container: **kill only a PID you recorded, and wait only on one, never a
name** — `pgrep -f` matches the asking shell (`guard-process-kill.sh`)."
- `.claude/hooks/guard-process-kill.sh` header, lines 72–76 — the
measured mechanism itself, including the deadlock it produced on this
container.
And the re-derivation the card names as the natural wrong completion has
no occasion left: a reader who copies `tail --pid=$!` has the pid
already. Trading a measured qualifier for a duplicate of a rule two
other files carry is the trade this PR declines; the seat can overturn
it with one number from the table above.
## Verification
Gate families derived from the real change set, not from a hand-written
list — `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (1 path vs merge base `f04be62aa`), then
reconciled with `--ran`:
```
Run reconciliation — 16 derived, 16 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 16 derived famil(ies) accounted for — 16 run, 0 NOT-MEASURED
(a DERIVED zero — all 16 recorded an exit code and none of them is 3).
```
All 16 exited 0 on the merged head `7674f7ce7`: `check:pm-skill-ratchet`
· `check:pm-skill-id-lint` · `check:skill-frame-sync` ·
`check:required-contexts` · `check-governed-queue-guard.mjs --self-test`
· `check-closing-keyword-parity.mjs` (+ `--self-test`) ·
`check-comment-mask-corpus.mjs` · `@objectstack/lint
check:doc-formula-expressions` · `check:agent-test-spelling` ·
`check:doc-authoring` · `check:driver-memory-census` · `check:nul-bytes`
· `check:pm-governed-merges` · `check:refd-timer-probe` ·
`check:watch-hint-literal`.
The ratchet's own verdict line for this file:
```
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md
is 466 lines (ceiling 466; headroom 0).
```
`check:doc-formula-expressions` exited **3 on its first run** —
`PREREQUISITE NOT MET`, `@objectstack/formula` and `@objectstack/lint`
not built — which is not a finding and not a measurement. Cleared by the
fix the gate itself prints (`turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint`, run through
`scripts/pm/os-verify-lock.sh`), then re-run to exit 0.
Three gates run beyond the derived 16, each for a stated reason:
- `pnpm check:pm-settings-deny-roster` — exit 0. The derivation flags
its roster as living under `.claude/`, where one of my paths is, so its
silence is evidence in neither direction and it was read rather than
assumed.
- `node scripts/pm/check-governed-merges.mjs --test
.claude/skills/pm-dispatch/references/platform-readings.md` — exit **3,
GOVERNED**, as expected: `.claude/** ×1`. This PR stays draft; no seat
flips it ready, enqueues it or arms auto-merge.
- `node scripts/check-skills-token-ratchet.mjs` — exit 0. It ratchets
the published `skills/` catalog, which this diff does not touch; run to
prove that, not because it covers this file.
**Repo-wide lint is CI's, and the local narrowing is declared with its
three readings.** ① The population comes from eslint's own config, not a
guess: every `files:` glob in `eslint.config.mjs` is
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` or a TypeScript subset of it —
markdown is in no configured population. ② Counted from `--format json`:
eslint over the one changed file returns `errorCount: 0` with `"File
ignored because no matching configuration was supplied."`, i.e. 0 linted
files in this diff. ③ Invariance: there is no `parserOptions.project`
and no typed `@typescript-eslint` rule anywhere in that config, so
nothing in this diff can move the verdict of a file it does not contain
— and this diff contains no JS or TS at all.
Not run and not owed: no workspace package is in the change set, so
there is no dependency closure to build and no package test or typecheck
to run. `@objectstack/formula` and `@objectstack/lint` were built only
as the named prerequisite above.
## Acceptance notes
- `.claude/agents/os-dev.md:342` also names `tail --pid` without a pid
source — noted, not filed, and deliberately not edited: it is one hop
from `AGENTS.md:534`, which bans the `pgrep -f` derivation by name, and
the card's item 4 keeps a sweep of other prescriptions out of this PR as
a separate card with its own evidence. Successor: whoever files that
sweep.
- Region-parallel on this file with PR #18396 — that PR's three rows are
a different card's and are untouched here; #18396 is not addressed by
this PR. `git merge origin/main` was run once before opening this PR;
`origin/main` at `f04be62aa` did not carry #18396 yet, and the merge
touched no `.claude/` path.
- `skip-changeset`: the diff publishes nothing. `.claude/**` ships in no
package's `files[]` — it is repo-internal agent instruction text.
## 维护者速读(草稿)
**改了什么** —— `platform-readings.md` 里那条讲 `check:pm-dispatch-gates` 太慢要
detach 的读数,原来只说了「detach 加 `tail --pid`」,没说这个 pid 从哪来。现在它自己带上了可照抄的写法
`tail --pid=$!`。全文只动这一行,行数 466 → 466。
**为什么改** —— 处方缺了一半,而最自然的补法是错的:用 `pgrep -f 模式` 反推 pid 时,跑 pgrep 的那层 shell
自己的命令行里就带着那个模式,于是监视器等的是它自己,永远等不到。代价量过:一个 dev 在一张卡上踩了三次,每次都把一次干净跑完、exit
0、日志完整的测量,报成了挂死的后台任务。
**风险与代价(含回滚)** —— 风险极低:改的是给 agent 读的一行说明文字,不进任何产物、不发布、无
changeset。代价是一个词:`调用方容器上限` 压成 `容器上限`,为 `=$!` 腾出字节(该文件行数上限 466 已零余量,每行上限
120 字节)。卡面 ⭐ 的第二件(解释 `pgrep -f` 为什么自匹配)没写进这一行 —— 量过:要写下它,就得删掉 `单机` 或
`容器` 这些量过的限定词,而这条规则 `AGENTS.md:534` 与 `guard-process-kill.sh`
文件头已经各写了一遍。回滚 = revert 本 PR 的单行 diff,无后续依赖。
**席位意见** ——
**你要做的** —— 这是受管面(`.claude/**`),合并权不在任何 agent 席位手里。按 references 层的分级,它走
skills 席的 contract-tier 复核后由本席落地;若你想自己看一眼,只需看上面 before/after 那两行,和「要不要为
`pgrep` 那句话删掉 `单机`」这一个取舍。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 588475c commit 90b23ab
1 file changed
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
424 | 424 | | |
425 | 425 | | |
426 | 426 | | |
427 | | - | |
| 427 | + | |
428 | 428 | | |
429 | 429 | | |
430 | 430 | | |
| |||
0 commit comments