pm-dispatch: reconcile the quota rows with the identity-bound rate limit; merged-write rule; account-suspension recovery readings - #18242
Conversation
…account-suspension recovery readings Second half of the shared-identity rate-limit card. - platform-readings.md quota block: the two rows that still told a seat to try the other channel on a rate limit are re-conditioned in place on the GET /user identity test (403 and transport failures stay per-side readings); five new rows record the account-suspension signal and the measured recovery (branches and commits survive, a destroyed PR holds its branch name, the local object store is the backup, a rebuilt PR self-reports). +5 under the standing one-file exception, recorded as a ruledRaises record in the ratchet. - SKILL.md 平台读数纪律: one rule line for merged writes (fewer and larger, no per-write slowing, no burst; write volume counts per identity), paid by folding the two maintainer-abort lines into one. - review-checklist.md: the reviewing seat's NOT MEASURED line for destroyed evidence, paid by folding the dead-code deletion pair into one line. Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Reviewing seat: ① Derived judgments
② Semver levelNot applicable — nothing published moves; ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)改了什么:三份席位规则/事实文件加一份门禁台账。① 事实表 为什么改:这是 2026-09-10 整个 fleet 被停用事故复盘卡的后半。前半已把「限流绑定身份」写进通道表;事实表里却还留着相反的指引,席位照读就会重演事故里的那个动作。恢复流程当时是现场摸索出来的,没写下来下次还要摸一遍;写入合并的口径此前完全空白。 风险与代价(含回滚):纯文本规则与事实,不碰任何发布包,不改 CI 行为。代价是 席位意见:建议批准。本席核过:两处折叠的规则都幸存(砍掉的只是理由句);五行去重在 你要做的(一个动作):approve 本 PR。approve 后本席翻 ready + 入队,你不必再点合并。 Generated by Claude Code |
|
Ruling-C landing provenance — skills seat, session Generated by Claude Code |
…air PR-NUMBER` is the only spelling (objectstack-ai#18250) Fixes objectstack-ai#18181 ## What changed Three lines across two governed rules-layer files; line counts unchanged (os-dev.md 403/403, SKILL.md 812/812), every edited line at or under the 120-byte cap, no re-wrap anywhere, and the decision-frame block SKILL.md :734–:755 untouched (md5 `3327d02c56f8a0eca88569dad2270f32` before and after). **`.claude/agents/os-dev.md` :287** — the line the card names, re-derived on `b3b43b6` (the card body's `:288` had drifted; the filer's correction comment 5664488072 gives :287): ```diff -- 认领写 `Clause-②: yes` ⇒ 开 PR 同笔挂 `needs:contract-review`,报告附 `--pair N` 退出码。 +- `needs:contract-review` 归席位,⛔ 不挂不摘不等;报 PR 上有无与 `--pair PR-NUMBER` 退出码作读数。 ``` The dev no longer hangs the carrier. It never hangs, removes or waits for `needs:contract-review`; it reports whether the label is present on the PR and the `--pair PR-NUMBER` exit code — as a reading, not as a gate it must clear. **`.claude/agents/os-dev.md` :301** — the neighbouring `skip-changeset` label-write rule (mechanism assumption A2, same file surface, same class — an additive label write the container may refuse): ```diff -- 被拒 ⇒ 停下报 `blocked` 点名端点与状态码,⛔ 不换 MCP 写道;写后必做对比式读回。 +- 被拒 ⇒ 报端点与状态码、席位代挂,⛔ 不报 `blocked`、不走 MCP;写后必做对比式读回。 ``` One rule now covers both carriers: a refused additive label write is reported with endpoint and status and the seat hangs the label; the dev never reports `blocked` for it and never switches to an MCP write. **`.claude/skills/pm-dispatch/SKILL.md` :646** — the one `--pair N` token (REGION claim: this line only): ```diff -- `Clause-②: yes` 认领同笔在卡上挂标;PR 开出即读 `check-clause2-carriers --pair N` 为 0 再请审。 +- `Clause-②: yes` 认领同笔卡上挂标;PR 开出读 `check-clause2-carriers --pair PR-NUMBER` 为 0 再请审。 ``` `--pair PR-NUMBER` is now the only spelling in the three governed files (contract-review.md :42 already spelled it so). ## Why Direction A of the card's grading (comment 5673619664), executed as ruled: the `needs:contract-review` carrier is the PM seat's to hang — on the card at the claim and on the PR when the PR appears — which is what every seat already does; the dev never blocks on a label write; `--pair PR-NUMBER` becomes the only spelling. Option B (granting the dev container the label write) is a permissions widening and was ruled not this card. The two defects the card and its correction measured: a dev that obeyed :287 literally was refused the additive label write by its container's write classifier on two independent objectui runs, while an objectstack dev could write it — per container, unknowable in advance; and `--pair N` bound `N` to the nearest referent, the card, while the script takes a PR number and answers a card number with exit 2 (UNJUDGED), which a naive capture reads as a red gate. ## Mechanism readings - **A1** — :287 is the line on `b3b43b6` (108 B; the card's :288 is the `skills/**` line). Replaced as above. Under the byte cap the `Clause-②: yes` trigger did not fit, so the rule is now unconditional: on every PR the dev hangs nothing, removes nothing, waits for nothing, and reports carrier presence plus the `--pair PR-NUMBER` exit code. That superset is deliberate — `--pair` also carries the widening-tell row, which only fires on a `Clause-②: no` claim, so a reading on every PR is worth more than one on `yes` claims alone. - **A2** — measured on the text: :301's `被拒 ⇒ 停下报 blocked` is the same container-refusal class one line over, and nothing about `skip-changeset` requires the dev to block on it: CI's changeset gate reads the label, the dev's report says whether it landed, and the seat can hang it on that report. So one rule covers both carriers and :301 changed as above; :299 (the label is your step, hang it as soon as the PR opens) and :300 (additive endpoint) stay — the dev still attempts the write. The live half of the measurement is this PR's own `skip-changeset` write; its outcome (landed, or the endpoint and status of the refusal) is in the `os-dev-report` comment on the card, not here, because the PR body is written before the label write and is not re-sent. - **A3** — `git grep -n -- '--pair N\b' -- '.claude/**' 'scripts/pm/**'`: before 6 hits (os-dev.md :287, SKILL.md :646, and 4 in `scripts/pm/**`); after 4 hits, all outside the claimed surface: `check-clause2-carriers.mjs` :24 and :490 (docblocks), :4947 (a self-test title), and `check-widening-tells.mjs` :492 (docblock). In the three governed files the count is 0. Lit control `--pair PR-NUMBER`: 1 before (contract-review.md :42), 3 after (:42, os-dev.md :287, SKILL.md :646). The script docblocks bind `N` to a PR number in their own text (:11 spells `--pair 13910 # ONE PR`), so the ambiguity is not live there; noted below, not edited. - **A4** — measured from the sibling checkout on this container: `cd /home/user/objectui && PM_SWEEP_REPO=objectstack-ai/objectui node ../objectstack/scripts/pm/check-clause2-carriers.mjs --pair 9552` exits 0 with the provenance line `every row below is read from objectstack-ai/objectui (source: PM_SWEEP_REPO)` and the verdict `PR objectstack-ai#9552 / card objectstack-ai#6910 — the clause-② declaration is readable in the fixed spelling and both carriers agree` (token present, 3 reads served; the proxy re-exec happened by itself). A control on objectui#9495 (no longer open) exits 2 — UNJUDGED, with the board still retargeted. So the invocation works from a sibling checkout. The byte cap leaves no room on :287 for the invocation (the line stands at 119 B), so the line carries only the flag spelling; the script header (:20–:24) already spells the sibling invocation and every run prints the board it read, which is the remedy text the ruling asks to keep in the script. ## Gates Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths; change set from the merge base): 18 families on `a2cbbef0`, re-derived on the merged head `0c934967` (merge base `68fea8bc`) — identical list. All 18 run in the foreground on `0c934967`, exit codes captured by redirect before any pipe, all 0: `check-closing-keyword-parity` (+ `--self-test`) · `check-comment-mask-corpus` · `check-governed-queue-guard --self-test` · `check:doc-formula-expressions` (lint pkg) · `check:agent-model-declared` · `check:agent-test-spelling` · `check:commit-card-trailers` · `check:doc-authoring` · `check:driver-memory-census` · `check:nul-bytes` · `check:pm-governed-merges` · `check:pm-governed-prose` · `check:pm-skill-id-lint` · `check:pm-skill-ratchet` · `check:refd-timer-probe` · `check:skill-frame-sync` · `check:watch-hint-literal` Reconciliation on `0c934967`: `✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3)`. Verdict lines: `✓ check-skill-line-ratchet: .claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0)` · `✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 812 lines (ceiling 812; headroom 0)` · `✓ check-agent-model-declared: 1 agent definition(s) under .claude/agents/ all declare a model` · `✓ check-skill-id-lint: 27 file(s) clean` · `✓ check-skill-frame-sync: the one declared copy of the decision frame is internally coherent`. `check:doc-formula-expressions` first answered exit 3 (PREREQUISITE NOT MET — `@objectstack/formula` and `@objectstack/lint` unbuilt in a fresh worktree); prerequisite built under the verify lock (`turbo run build --filter=@objectstack/formula --filter=@objectstack/lint`, VERDICT command-exit 0), rebuilt again after the merge, then the gate exits 0. The exit 3 is not a measurement and is not counted as one. Not run locally, by design: the 11 wide-population families and the whole-repo `pnpm lint` sweep belong to CI. Control-byte self-scan on both edited files: no hits. ## Line budget | file | before | after | ceiling | edited lines (bytes) | | --- | --- | --- | --- | --- | | `.claude/agents/os-dev.md` | 403 | 403 | 403 | :287 108→119 · :301 110→114 | | `.claude/skills/pm-dispatch/SKILL.md` | 812 | 812 | 812 | :646 116→118 | Paid by density in place; no line merged or split. ## Deviations, declared 1. SKILL.md :646: the token swap alone (`N` → `PR-NUMBER`, +8 B) puts the line at 124 B, over the 120-byte cap. Two characters were dropped on the same line (`在`, `即`), content unchanged, landing at 118 B. Still inside the one-line REGION claim. 2. os-dev.md :287: the `Clause-②: yes` trigger is gone for the byte cap; the rule is unconditional (see A1). If the seat wants the trigger back, the price is the `作读数` tail or the `不等` limb. 3. os-dev.md :287 does not spell the objectui invocation (A4); the line names the flag, the script header names the sibling form. 4. os-dev.md :301 edited under A2 (inside the file surface and the card's class). objectui's copy and the dispatch-order template are untouched. 5. `skip-changeset`: nothing published moves — `.claude/**` is on the fast path (no package `files[]` ships it). 6. `origin/main` merged at `68fea8bc` (one spec commit, nothing on this surface) before opening; objectstack-ai#18242 remains open on SKILL.md :172–:185 — disjoint from :646. ## Acceptance notes - **To file (class b, for the seat — dedupe words: `needs:contract-review`, `PR 创建者`, `同笔挂`, SKILL.md, direction A):** SKILL.md :645 still reads 「`needs:contract-review`(恒英文)由 PR 创建者随可复审契约增量同笔挂:draft PR,或先到的报告。」 For a dev-created PR the creator is the dev, which direction A says never hangs it. Outside this card's REGION claim (:646 only), so not edited here; it sits on the SKILL.md chain the seat owns. - noted, not filed: 4 `--pair N` spellings remain in `scripts/pm/**` docblocks and one self-test title (listed under A3); each is next to text that binds N to a PR number. 承接者:无. - noted, not filed: os-dev.md :300's 「可达性按会话探,先探后用」 is redundant with the new :301 — the write attempt is the probe. Left as is; deleting it frees bytes, not a line. 承接者:无. - Observed on this PR after opening: the labels present are reported in the `os-dev-report` comment with a read-back; under one shared identity the author field says nothing about who hung what. ## 维护者速读(草稿) **改了什么**:改了两个受管规则文件里的三行,行数不变。开发 agent 定义(os-dev.md)一行:`needs:contract-review` 这个标签归席位挂,开发 agent 不挂、不摘、不等,只在报告里写 PR 上有没有它、以及 `--pair PR-NUMBER` 的退出码,作读数。另一行:加法写标签被容器拒绝时,报端点与状态码、由席位代挂,不再报 `blocked`、不走 MCP。PM 技能(SKILL.md)一个 token:`--pair N` 改成 `--pair PR-NUMBER`。 **为什么改**:两次独立实测里,开发 agent 的容器拒绝写标签,而规则要求它开 PR 同笔挂——照做就失败、如实报告又像抗命。分诊裁定 A 向:载体由席位挂(认领时挂卡、PR 出现时挂 PR),开发 agent 只报所见。同一行的 `N` 让人绑到卡号,脚本要的是 PR 号,给卡号返回 exit 2(未判),会被当红灯读;`PR-NUMBER` 是唯一拼写。 **风险与代价(含回滚)**:行数与上限不变,每行 ≤120 字节,决策框架块未动。风险是开发 agent 不再自己挂载体,若席位漏挂,条款② PR 的载体会缺失——但席位侧已经是现行做法,且开发 agent 报告的 `--pair` 读数会暴露缺失。回滚 = revert 本 PR,无其他副作用。 **席位意见**: **你要做的**:审阅上面三行措辞(尤其 :287 去掉了 `Clause-②: yes` 触发词、改为对每个 PR 都成立);同意即给授权批准,由席位落地;不同意请指出要保留的措辞。 --- _Generated by [Claude Code](https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…quotes the governing line (objectstack-ai#18256) Fixes objectstack-ai#17497 One rule line in 〈分诊座位职责〉 of `.claude/skills/pm-dispatch/SKILL.md`: a triage grading whose direction lands inside an adjudicated area (an ADR, a ruled card, a not-re-adjudicable rule) pastes the governing line — the `Prior rulings read:` line that `check-prior-rulings.mjs` prints — or names no target. Triage keeps writing directions (「写方向」 is charter text); the line binds the direction's evidence, never removes the direction. Governed surface (`.claude/**`, rules layer): draft, no reviewer requested, no ready flip — the skills seat hangs the four-piece and the maintainer lands it. REGION claim :306–:415 only; PR objectstack-ai#18242 holds :172–:185 and PR objectstack-ai#18250 holds :645–:646 on the same file and neither range is touched here (the seat merges `origin/main` at landing). ## The added line (SKILL.md :339 on the head, 120 bytes) ```text - 方向落在 ADR/已裁卡/不可重裁规则:贴 `check-prior-rulings.mjs` 的 `Prior rulings read:` 行,或不点。 ``` Vocabulary is the corpus's own: 「已裁卡」 (SKILL.md 〈家族派发〉), 「不可重裁」 (〈派发词三分区〉), 「落点」 = the named fix target (〈分诊座位职责〉's `pm:queue` definition). The tool is cited by file name and the paste line by its literal prefix — no issue or PR number (`check:pm-skill-id-lint` green). **Placement (P2, chosen by reading the section's order):** directly under the line that mandates the direction — 「裁定写方向、给六态之一」 (:338 on the head, :339 on `origin/main`). That is where 「写方向」 actually lives on this tree: `references/lanes/triage.md` is 7 lines and its :7 is the half-state rule, so the grading's citation of 「写方向」 at `triage.md :7` is stale as a location while its substance holds. The English audit comment line (`Triage: lands in …; rationale: …`, :364 on the head) was the alternative; it names the artifact the direction is written into, but the obligation reads best beside the mandate it qualifies, and 「方向」 is anchored by the preceding line there. **P1 confirmed by a live run** (`node scripts/pm/check-prior-rulings.mjs --card 17497`, exit 0) — the tool printed exactly the shape the line cites: ```text Prior rulings read: triage,gradings,named,target,governing,itself,rejects,grading,quotes,line,adjudication,routes → 61 hits; ADR-0129 D4, ADR-0067 D3, ADR-0076 D11, ADR-0006 D2, ADR-0020 D2, ADR-0021 D1 ``` `formatPasteLine` (`check-prior-rulings.mjs`) prints `Prior rulings read: TERMS → N hits; ADR-xxxx Dn|none` and the whole-line form `Prior rulings read: unresolved` when the search did not run; `check-half-states.mjs` H62 reads the same key (`PRIOR_RULINGS_KEY`), and `references/decision-analysis.md` already binds that line for decision cards. ## Payment (P3, measured) — one adjacent pair inside the region rewritten tighter Before (`origin/main` :311 + :312, 89 B + 50 B): ```text - fire 开局只按名加载互斥检查所需工具,`ToolSearch` 用 `select:` 形式。 - 判定本轮有活之后才加载其余工具。 ``` After (head :311, 116 B): ```text - fire 开局只按名加载互斥检查所需工具,`ToolSearch` 用 `select:`;判定本轮有活才加载其余。 ``` Nothing operative is lost: every instruction token survives — `fire 开局`, `只按名加载`, `互斥检查所需工具`, `ToolSearch`, `select:`, `判定本轮有活`, the ordering `才加载其余` — and the three dropped words are filler (「形式」 names the code span it follows; 「之后才」 → 「才」 keeps the only-after ordering; 「其余工具」 → 「其余」 in a line whose subject is tool loading). The PM's measured pairs (:312+:314, :363+:364, :397+:398) all rewrite past 120 B; this is the pair that fits. Not a re-wrap (no line-joining bought the budget), no rule deleted, ceiling untouched. ## Reverse verification (on head `c96b507d`) - `git diff origin/main -- .claude/skills/pm-dispatch/SKILL.md`: two hunks, `@@ -308,8 +308,7 @@` (the fold) and `@@ -337,6 +336,7 @@` (the insert) — both inside :306–:415; no other line moves. - `wc -l` = 812 (ceiling 812, headroom 0 — the ratchet's own line: `.claude/skills/pm-dispatch/SKILL.md is 812 lines (ceiling 812; headroom 0)`). - `sed -n '734,755p' | md5sum` = `3327d02c56f8a0eca88569dad2270f32` (frame block byte-identical). - an `awk` length scan for lines longer than 120 bytes over :306–:415 prints nothing; touched lines measure 116 B (fold) and 120 B (new). - Positive anchors: `grep -c 'Prior rulings read:'` = 1; the removed :312 text = 0 hits; the fold text = 1 hit. ## Gates (derived, run in the foreground, exits captured before any pipe, reconciled with `--ran`) `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths; change set derived off merge base `cb648cb1`) → 16 commands; `--ran` verdict: `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)`, exit 0. | command | exit | |---|---| | `node scripts/check-closing-keyword-parity.mjs` | 0 | | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | | `node scripts/check-comment-mask-corpus.mjs` | 0 | | `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | first run exit 3 `PREREQUISITE NOT MET` (unbuilt `@objectstack/formula` / `@objectstack/lint`) — built both under `os-verify-lock.sh` (`VERDICT command-exit 0`), re-ran: 0 | | `pnpm check:agent-test-spelling` | 0 | | `pnpm check:doc-authoring` | 0 | | `pnpm check:driver-memory-census` | 0 | | `pnpm check:nul-bytes` | 0 | | `pnpm check:pm-governed-merges` | 0 | | `pnpm check:pm-governed-prose` | 0 (`2 instruction surface(s) name all 5 registered governed surfaces`) | | `pnpm check:pm-skill-id-lint` | 0 (`27 file(s) clean`) | | `pnpm check:pm-skill-ratchet` | 0 (812/812) | | `pnpm check:refd-timer-probe` | 0 | | `pnpm check:skill-frame-sync` | 0 (`the one declared copy of the decision frame is internally coherent`) | | `pnpm check:watch-hint-literal` | 0 | Extra, outside the roster: `node scripts/check-skill-frame-freshness.mjs --self-test` exit 0 and `--no-fetch` exit 0 (`the decision frame in this tree is current with refs/remotes/origin/main`). The roster did not derive `check:pm-dispatch-gates`, so no detached run was owed. Full repo-level scans (`pnpm lint`) belong to CI. `skip-changeset`: `.claude/**` is published by no package's `files[]` (fast track) — nothing released moves. ## Acceptance notes - noted, not filed: the grading's location citation for 「写方向」 (`lanes/triage.md :7`) is stale — that file's :7 is the half-state rule and the charter text lives at SKILL.md :339 on `origin/main`; the ruling's substance is unaffected. 承接者: the skills seat, at landing of this PR. - noted, not filed: the dispatch names a gate `check:pm-skill-frame-sync`; the script's real name is `check:skill-frame-sync` (`scripts/check-skill-frame-sync.mjs`), and it ran green. 承接者: the dispatching PM seat, via this report. - noted, not filed: the ratchet header's reading 「of the file's 601 adjacent bullet pairs ZERO merge under the 120-byte cap」 was a pure-join measurement on an earlier tree; on this tree one pair folds at 116 B as a tighter rewrite. Not a contradiction of that reading and not a defect. 承接者:无. ## 维护者速读(草稿) **改了什么**:分诊座位职责里加一行:分诊的方向若落在 ADR、已裁卡或不可重裁规则内,须贴 `check-prior-rulings.mjs` 印出的 `Prior rulings read:` 行,否则不点落点。为了不突破 812 行上限,把开局工具加载的两行合成一行(116 字节),每条指令原样保留。 **为什么改**:两次分诊给出的落点恰是治理文本已经拒绝的拼写或形状(一次是字段 schema 拒收的扁平拼写,一次是标记为不可重裁的通则),都在执行席的围栏才被拦下,其中一次派发令已写出。分诊的推理没错,错在落点没有引治理行,所以看不见。决策卡那边已有同一行的约束,这次把它也绑到分诊评级上。 **风险与代价(含回滚)**:一行规则、一次同义压缩,不动上限、不删规则、不改工具。风险是新行 120 字节写得紧,读者需要认得 `check-prior-rulings.mjs` 这个工具(它已在仓内、有自检)。回滚 = revert 本 PR 一次提交。 **席位意见**:(留空,席位定稿成评论) **你要做的**:确认这条规则的措辞与落位(放在「裁定写方向」那一行之下);同意即批准本 PR,由席位落地。 --- _Generated by [Claude Code](https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #17374
Second half of the shared-identity rate-limit card. The first half (PR #17860, landed as
ed8dea17b) placed expectations 1 and 4 — the identity-bound rate-limit rows inreferences/rest-channel.mdand the destroyed-evidence NOT MEASURED line in.claude/agents/os-dev.md; expectation 5 is met by the half-state patrol's H40 row. This PR lands what was deferred behind same-file serial: expectation 2 (merged writes), expectation 3 (the account-suspension recovery steps as readings), the reconciliation of the two quota rows that still prescribed the same-identity channel switch, and the reviewing seat's NOT MEASURED line. With this the card's five expectations are all placed. F5 (GitHub App / machine users) is the maintainer's infrastructure decision and not this card's; the disciplines here hold independently of it.Clause-②: no — no published
skills/**file moves; no operator or contract semantics.skip-changeset — nothing published moves (
.claude/**andscripts/pm/**sit outside every package'sfiles[]).What changed — 4 files, +60 / −7, head
694b1d2on baseb3b43b6references/platform-readings.md— 454 → 459 (+5 under the standing one-file exception)Two quota-block rows re-conditioned in place (line-neutral; they pay nothing and buy nothing):
GET /user比 ID:同 ID 的 REST 满额不是退路,写排队到重置;异 ID 才是。」 (109 B)Until this PR both rows told a seat to do the exact same-identity channel switch that
rest-channel.md's identity-bound rows (landed by the first half) forbid. The 403 and transport halves stay as they were: those are per-side readings, and the row above them (「任一侧的拒绝只是那一侧的读数」) is untouched. Reads are untouched too — the row below (「MCP 的读限流与写限流彼此独立」) still governs them, and the identity test is itself a read on the other side. 「写排队到重置」 points at the standing queue-into-the-patrol-word row (:159) rather than restating it.Five new rows after the retry-alignment row (the end of the quota-exhaustion prescriptions), one measured event per row, each under 120 B:
Dedup table for the exception (候选/落地/已有/拒收): candidates 5 / landed 5 / already present 0 / refused 0. The family grep
suspended,停用,销毁,幽灵,重推,重建,对象库,分支名onorigin/mainatb3b43b6hits only the Routine-rebuild rows (:445–:446, :453), the issue-transfer rebuild row (:144) and the landing criterion (:366); none carries any of the five, and the later suspension-row family the dispatch word anticipated does not exist onorigin/main.Ratchet: ceiling 454 → 459 in
scripts/pm/check-skill-line-ratchet.mjs, with a FIFTEENTHruledRaisesrecord citing the standing exception verbatim — 「唯一例外:platform-readings.md增量抬上限到落地行数,免决策卡,记ruledRaises引常设裁决。条件:席位验收评论逐条核实、去重计数(候选/落地/已有/拒收)、一事一行、不计重排」 — and the increment accounted for line by line beside the ceiling. The cross-file-move arithmetic re-derives unchanged (+11 against a net source decrease of 20).SKILL.md〈平台读数纪律〉 — 812 → 812 (equal-line, paid by density)Added as the section's last line, beside the pre-dispatch
rate_limitread:Paid by folding the two maintainer-abort lines into one: 「维护者中止只在有显式信号时成立:原话,或宿主回报 stopped by the user。」 + 「⛔ 不据推断立一道没有重启条件的门;判据是信号不是症状。」 → 「维护者中止只认原话或宿主回报 stopped by the user,⛔ 不据推断立无重启条件的门。」 (108 B). Every operative clause survives (explicit signal only; no inference-built gate without a restart condition); 「判据是信号不是症状」 is the first clause said twice.
references/core-rules.md:46 already digests this pair in one line, so the mirror is unchanged (151 / 151).A2 measured on
b3b43b6:grep -c '合并\|少而大\|节流' SKILL.mdcounts 0 lines in the write-merging sense — every 合并 hit is merge-queue / merged-PR vocabulary — against control限流3 lines. The quota rows inplatform-readings.md(:103–:105: the per-minute secondary limit, the ~1 s spacing between mutations, the same-second burst that hits the minute wall) answer the BURST half of the card's shape and not the MERGE half: nothing on the seat surface said fewer-and-larger, and nothing said not-slower. The dev side already carries a merged budget (os-dev.md写预算四笔); the seat side now carries this line.references/review-checklist.md〈测试与门禁证据〉 — 77 → 77 (equal-line, paid by density)A5 measured:
grep -c 'NOT MEASURED\|销毁\|404'onreview-checklist.mdand oncontract-review.mdatb3b43b6= 0 and 0 (controlINCONCLUSIVE= 2 in review-checklist), so the reviewer had no line to read; the first half's line lives in the dev's file. Paid by folding the dead-code deletion pair (:75–:76) into 「以死代码或不可达为由的删除,PM 先在origin/main用带引号精确名核引用面,再 ACCEPT。」 (113 B): the rule survives whole and the deleted half (「这是断言不是 diff 里的事实,而这一查只花十秒」) is rationale.Face note: the claim comment's file surface names
platform-readings.md,SKILL.mdandcore-rules.md; the dispatch word's A5 asks for exactly this one reviewer line inreview-checklist.mdorcontract-review.md, inside the ratchets, and this is the one deviation from the claim's list. All 11 open PRs' file lists were read at 2026-09-15T02:46Z, immediately before this PR was opened; none touches any file in this diff.Acceptance against the card
GET /useranswers a different id. Measured on this container:GET /useron the REST credential answersos-zhuangid 277994282, the same login as the card's assignee, so PM and dev are one identity in this seat — exactly the case the rule is for.origin/mainatb3b43b6versus head694b1d2, with a lit control in the same file):platform-readings.md, pattern 停用 / 销毁 / 新分支名 / 对象库: 0 → 4 (row 5's term 自报 is excluded because :220 already carries it in another sense;自报四件alone reads 0 → 1); control限流10 → 11.platform-readings.md, pattern 比 ID / 比身份: 0 → 2; the deleted instruction 先探 REST / 整个平台的写: 1 → 0.SKILL.md, pattern 少而大 / 攒着 / 按身份计: 0 → 1; control限流3 → 3.review-checklist.md, pattern NOT MEASURED / 销毁: 0 → 1; controlINCONCLUSIVE2 → 2.Remove the new text and the positive case is again answered only by
rest-channel.md, with the quota section arguing the other way.Mechanism assumptions from the dispatch word, measured
b3b43b6prescribed the switch; both re-conditioned in place as above.origin/main; all four F3 steps plus the recognition reading were absent and land.review-checklist.md.Gates — head
694b1d2node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; change set taken from git off merge baseb3b43b6ea, 4 committed paths) derived 40 commands. 39 of the 40 ran in the foreground with the exit captured by redirect before any pipe, and every one exits 0; the 40th is the tool's own self-test, run detached (below).--ranon the exit-carrying record reconciles 40 derived / 40 run / 0 unrun.check:pm-skill-ratchet(self-test + gate):cross-file move into platform-readings.md: +11 (314→459, less 134 lines of ordinary ruled raise) against a net source decrease of 20·declared cross-file moves: 1, total ceilings down 9 lines.check:pm-dispatch-gates(the tool's own self-test, 1723 cases): detached per its header's foreground-cap warning, waited on in the foreground withtail --pid; verdict line✓ dispatch-gates self-test: 1723 cases pass.pnpm --filter "@objectstack/lint..." buildunderos-verify-lock.sh(VERDICT command-exit 0, held 220 s, waited 0 s).check:pm-skill-id-lint,check:skill-frame-sync,check:nul-bytes,check:ratchet-remedy-authority,check:pm-governed-prose,check:required-contexts: exit 0.scripts/beside this diff were run in addition:check-published-list-mirrors,check:pm-label-desc-cap,check-skills-token-ratchet— all three exit 0.pnpm lintnot run.Acceptance notes
platform-readings.md:97 「⛔ 不据限流报文里的 user ID 推池子跨席共用」 and :143 「报文里的 user ID 只是报文」 stay as written — the ratchet's own record says both hold pending a discriminating read, and the first half'sGET /usercomparison is that read (the id in the refusal text names the identity and says nothing about cross-seat pools), so :143's 「只是报文」 could become 「只标身份」 at equal bytes in a later density pass. Not edited here because the ruling says neither is.core-rules.md〈平台读数纪律〉 carries no digest of the new write-merging line; the mirror's own header says it adds no rules, and the claim conditions a mirror edit on a core clause moving, which none did.维护者速读(草稿)
改了什么:三份 PM 席位的规则/事实文件加一份门禁台账。① 事实表
platform-readings.md配额段:把两条还在教席位「限流了就换另一条通道继续写」的行改成「先比身份,同一身份就停写排队」,并新增五行记下账号被停用时的识别信号与恢复步骤(分支和提交不丢、被销毁的 PR 仍占着分支名要换名重推、本地对象库是备份、重建的 PR 要自报)。② 席位规则SKILL.md:加一行「写少而大」—— 同一张卡同一轮的结论合成一条评论,既不放慢单笔也不攒着突发,写入量按身份合计;用合并两行既有规则付账。③ 复核清单:复核项的证据被销毁时记 NOT MEASURED,不记通过;同样以合并两行付账。④ 棘轮台账:platform-readings.md上限 454 → 459,按常设例外记录。为什么改:这是 2026-09-10 整个 fleet 被停用的事故复盘卡的后半。前半已把「限流绑定身份」写进通道表;但事实表里还留着相反的指引,席位照读就会重演事故里的那个动作。恢复流程当时是现场摸索出来的,没写下来下次还要摸一遍。写入合并的口径此前完全空白。
风险与代价(含回滚):纯文本规则与事实,不碰任何发布包,不改 CI 行为。代价是
platform-readings.md多 5 行(每行 ≤120 字节,一事一行),其余三份文件行数不变。回滚 = revert 本 PR 的一个 commit,无迁移。误读风险:「写排队到重置」不是「所有写变慢」,新行已明写不放慢单笔。席位意见:
你要做的:审批本 PR(受管面,需要你的 approve 后由席位入队);若认为「写量按身份计」或恢复五行的措辞有误,直接改文字或留言,席位按你的话修。
Generated by Claude Code