Skip to content

pm-dispatch: reconcile the quota rows with the identity-bound rate limit; merged-write rule; account-suspension recovery readings - #18242

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-17374-rate-limit-identity-second-half
Sep 15, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-17374-rate-limit-identity-second-half

Conversation

@claude

@claude claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

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 in references/rest-channel.md and 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/** and scripts/pm/** sit outside every package's files[]).

What changed — 4 files, +60 / −7, head 694b1d2 on base b3b43b6

references/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):

  • 「限流、403、传输失败都要试过另一侧才说得出我没手段。」 → 「403 与传输失败要试过另一侧才说得出我没手段;限流先比身份,同 ID 的他侧不是手段。」 (114 B)
  • 「⇒ MCP 限流先探 REST 再定退避,⛔ 不据一侧限流把整个平台的写都停掉。」 → 「⇒ MCP 限流先 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:

  1. 「停用报文 account was suspended 遍及 /rate_limit 与 git,不给理由;非会话门 403、非限流。」 (108 B) — the recognition reading: the third 403 shape beside the session gate and the rate limit, which is what tells a seat the four steps below now apply.
  2. 「账号停用销毁其名下 PR、卡与评论;分支与 commit 属仓库照留远端 ⇒ 代码从未真丢。」 (112 B) — F3 step 1.
  3. 「被销毁的 PR 仍占分支名:API 答 404,同名开新 PR 仍被拒 ⇒ 同批 commit 推新分支名再开。」 (114 B) — F3 step 2.
  4. 「本地对象库是最后备份:复核时 fetch 过的每条分支,其 head 在停用后仍在本地可推。」 (110 B) — F3 step 3.
  5. 「重建 PR 正文自报四件:head 逐字节同、无 rebase/amend/squash、数字出自旧基底、CI 为准。」 (113 B) — F3 step 4.

Dedup table for the exception (候选/落地/已有/拒收): candidates 5 / landed 5 / already present 0 / refused 0. The family grep suspended, 停用, 销毁, 幽灵, 重推, 重建, 对象库, 分支名 on origin/main at b3b43b6 hits 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 on origin/main.

Ratchet: ceiling 454 → 459 in scripts/pm/check-skill-line-ratchet.mjs, with a FIFTEENTH ruledRaises record 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_limit read:

  • 「写少而大:同卡同轮结论合成一条评论,⛔ 不放慢单笔、不攒着一次性发;写量按身份计。」 (117 B)

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.md counts 0 lines in the write-merging sense — every 合并 hit is merge-queue / merged-PR vocabulary — against control 限流 3 lines. The quota rows in platform-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)

  • 「复核项证据已销毁(评论、卡或 PR 答 404)⇒ 记 NOT MEASURED 并写因,⛔ 不记通过或无旗。」 (113 B) — directly under the INCONCLUSIVE row, so the reviewer's verdict vocabulary now has all three: INCONCLUSIVE (the positive control failed), NOT MEASURED (the evidence is destroyed), 不入账 (a dead-tree reading).

A5 measured: grep -c 'NOT MEASURED\|销毁\|404' on review-checklist.md and on contract-review.md at b3b43b6 = 0 and 0 (control INCONCLUSIVE = 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.md and core-rules.md; the dispatch word's A5 asks for exactly this one reviewer line in review-checklist.md or contract-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

  1. Positive (client A refused, client B has quota): the quota section itself now answers 停 for the same user ID — 「限流先比身份,同 ID 的他侧不是手段」 and 「同 ID 的 REST 满额不是退路,写排队到重置」 — and 「异 ID 才是」 only when GET /user answers a different id. Measured on this container: GET /user on the REST credential answers os-zhuang id 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.
  2. Negative control (normal-quota writes unaffected): every changed rule fires only on a refusal, a suspension or a destroyed record; the new SKILL.md line says 「⛔ 不放慢单笔、不攒着一次性发」 in so many words, so it can neither slow a write nor push a seat toward the burst shape. No transport is banned, no 「下次注意」 is written, no green-widening exemption is introduced — the card's three 「不要走的路」 hold.
  3. Rehearsed recovery: on record rather than staged. The incident's own recovery is the rehearsal — PRs fix(security): OAuth-connected MCP agents run at the delegator's recorded scope, and a narrowed delegated read says so #17332, fix(triggers,spec,service-automation)!: a time-triggered flow declares its acting organization and the run executes as it #17334, fix(service-analytics): a row-scope refusal carries a declared envelope, so queryDataset stops classifying refusals by their wording #17336 and fix(service-automation): a notify node reports the recipients it addressed, so a zero-delivery run stops reading like a successful one #17339 were rebuilt on new branch names from the same commits with byte-identical heads, each self-reporting the four items row 5 now spells. A staged suspension is not something this PR can or should perform; the five rows are the steps that recovery took, written so the next seat does not rediscover them.
  4. Ablation (grep line counts, origin/main at b3b43b6 versus head 694b1d2, 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; control INCONCLUSIVE 2 → 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

  • A1 — confirmed: :123 and :126 on b3b43b6 prescribed the switch; both re-conditioned in place as above.
  • A2 — measured as above; a rule line was owed, and it lands in 〈平台读数纪律〉.
  • A3 — the family grep found no suspension rows on origin/main; all four F3 steps plus the recognition reading were absent and land.
  • A4 — recorded under acceptance 3.
  • A5 — measured as above; the reviewer line lands in review-checklist.md.

Gates — head 694b1d2

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set taken from git off merge base b3b43b6ea, 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). --ran on 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 with tail --pid; verdict line ✓ dispatch-gates self-test: 1723 cases pass.
  • The three lint-package families ran after pnpm --filter "@objectstack/lint..." build under os-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.
  • Three roster families the derivation marked as sitting under 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.
  • Not measured locally, CI's: the 3 workflow-value families, the Test Core job, the 11 wide-population families, the 14 changeset-pending families (skip-changeset), the remaining artifact rosters; repo-wide pnpm lint not run.
  • Control-byte scan on the four files: 0 hits. No changed line over 120 B in the three markdown files. No card number in any added operative line.

Acceptance notes

  • noted, not filed (承接者: the skills seat, holder of the same-batch tension ruling): 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's GET /user comparison 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.
  • noted, not filed (承接者: none): 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

…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>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 15, 2026
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 15, 2026
@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 694b1d2841faf626fd3c0567153b3eefe50c594b

Reviewing seat: domain:skills, in-seat review at the contract-review tier (get_session at 2026-09-15T02:54Z: configured = session = last served, the constant's tier). Card #17374 (second half), claim 5673628965, report 5674013532; Clause-②: no on the claim and in the PR body.

① Derived judgments

  • Accept set / public surface: none moves — .claude/skills/pm-dispatch/SKILL.md (+2/−2), references/platform-readings.md (+7/−2), references/review-checklist.md (+2/−2), scripts/pm/check-skill-line-ratchet.mjs (+49/−1 — the ceiling 454 → 459 and the FIFTEENTH ruledRaises record quoting the standing exception verbatim). Nothing published; skip-changeset on the PR (read back with size/s, documentation).
  • Folds checked survivor by survivor on the head: SKILL.md 「维护者中止只在有显式信号时成立…」 + 「⛔ 不据推断立一道没有重启条件的门;判据是信号不是症状」 → one 108-B line keeping both rules (only the rationale clause 「判据是信号不是症状」 goes); review-checklist 「以死代码…核一次引用面」 + 「核过再 ACCEPT:…这一查只花十秒」 → one 113-B line keeping the rule and its ordering (the rationale goes). No rule is homeless.
  • The new rule (SKILL.md 〈平台读数纪律〉, 117 B): 「写少而大:同卡同轮结论合成一条评论,⛔ 不放慢单笔、不攒着一次性发;写量按身份计。」 — the card's expectation 2 with its acceptance 2 (no per-write slowing) stated in the line itself.
  • platform-readings :123 / :126 re-conditioned in place on the GET /user identity test (114 B / 109 B), consistent with rest-channel :12–:13; five new rows (108–114 B each, one event per row): the suspension signal, branches and commits survive, a destroyed PR holds its branch name, the local object store is the backup, a rebuilt PR self-reports four things. Dedup re-run by the seat on origin/main b3b43b6 (停用|销毁|幽灵|重推|重建|对象库|分支名|suspended → only the Routine-rebuild, transfer and branch-count rows; control 限流 → 10 lines): candidates 5 / landed 5 / already present 0 / refused 0 — the standing exception's conditions hold, per row.
  • Seat-run on the PR head in a detached worktree at 694b1d2: check-skill-line-ratchet.mjs ✓ (812/812, 459/459, 77/77), check-skill-frame-sync ✓, check-skill-id-lint 27 files clean; frame block :734–:755 md5 3327d02c56f8a0eca88569dad2270f32 unchanged; every added line ≤ 117 B by the seat's own awk length.
  • Gates: 40 derived / 40 run / 0 NOT-MEASURED / 0 UNRUN per the report, exits captured by redirect; the check:pm-dispatch-gates family ran detached and was waited on with tail --pid (its exit 0 captured through a wrapper on a second run) — the cap it exceeds is class (a): pnpm check:pm-dispatch-gates cannot complete inside the container's ~10-minute foreground cap — measured 9m0s then exit 124, twice; the wall-clock twin of #17708 #18201's card, not this PR's.

② Semver level

Not applicable — nothing published moves; skip-changeset measured by the fast track (.claude/**, scripts/pm/**).

③ Boundary flags

open_questions: none. Deviations (4), each answered: (1) review-checklist.md outside the claim's declared surface — the dispatch's own A5 asked for exactly that one line; the claim's file surface is amended on the card's ACCEPT; (2) the detached gate run — accepted as reported; (3) platform-readings :143 「报文里的 user ID 只是报文」 left as written beside the identity binding — recorded on the card as an equal-byte candidate for a later density pass, not a defect; (4) the model-free trailer pair — the repository's hook is the authority, accepted. Governed (SKILL.md = rules layer) ⇒ four-piece: needs-user-decision on the PR, 速读终稿 below, reviews requested; ⛔ not flipped ready by the seat before an authorized approval.

Implemented-by: claude/issue-17374-rate-limit-identity-second-half
Reviewed-by: session_01HZfg2AwVX191qCizp88gQr

VERDICT: PASS


Generated by Claude Code

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

改了什么:三份席位规则/事实文件加一份门禁台账。① 事实表 platform-readings.md 配额段:把两行还在教席位「限流了就换另一条通道继续写」的指引改成「先比身份,同一身份就停写排队到重置」;新增五行记下账号被停用时的识别信号与恢复四步(分支和提交不丢;被销毁的 PR 仍占着分支名,要换名重推;本地对象库是备份;重建的 PR 要自报四件)。② 席位规则 SKILL.md 加一行「写少而大」——同一张卡同一轮的结论合成一条评论,既不放慢单笔也不攒着突发,写入量按身份合计;用折叠两行既有规则付账。③ 复核清单加一行:复核项的证据被销毁时记 NOT MEASURED,不记通过;同样折两行付账。④ 棘轮台账:platform-readings.md 上限 454 → 459,第十五条常设例外记录。

为什么改:这是 2026-09-10 整个 fleet 被停用事故复盘卡的后半。前半已把「限流绑定身份」写进通道表;事实表里却还留着相反的指引,席位照读就会重演事故里的那个动作。恢复流程当时是现场摸索出来的,没写下来下次还要摸一遍;写入合并的口径此前完全空白。

风险与代价(含回滚):纯文本规则与事实,不碰任何发布包,不改 CI 行为。代价是 platform-readings.md 多 5 行(每行 ≤ 120 字节、一事一行,席位逐行核实,去重计数 候选 5 / 落地 5 / 已有 0 / 拒收 0),其余三份文件行数不变。回滚 = revert 本 PR 的一个 commit,无迁移。误读风险:「写排队到重置」不是「所有写都变慢」,新行已明写不放慢单笔。

席位意见:建议批准。本席核过:两处折叠的规则都幸存(砍掉的只是理由句);五行去重在 origin/main 上亲跑 grep 复核;四轴框架块 md5 不变;棘轮、frame-sync、id-lint 在 head 上全绿;--pair 0;CI 23 绿 / 11 跳过、Lint & Repo Gates 仍在跑(绿后才由本席按裁决 C 落地)。一处保留的张力不阻批:事实表 :143「报文里的 user ID 只是报文」与身份绑定并存,dev 建议等字节改为「只标身份」,留待下次密度轮。

你要做的(一个动作):approve 本 PR。approve 后本席翻 ready + 入队,你不必再点合并。


Generated by Claude Code

@claude
claude Bot requested review from hotlong and os-zhuang September 15, 2026 03:05
This was referenced Sep 15, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review September 15, 2026 06:05
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 15, 2026
@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Ruling-C landing provenance — skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T06:08Z. Authorized approval: os-zhuang's APPROVED review 5206118837 at 2026-09-15T06:05Z on head 694b1d2841faf626fd3c0567153b3eefe50c594b, the same head the review of record 5674046970 (## Contract review PASS) names and the ACCEPT 5674047468 on #17374 cites; not dismissed. The approver also flipped the PR ready (2026-09-15T06:05Z) and enqueued it (added_to_merge_queue 2026-09-15T06:05Z, actor os-zhuang; queue ref gh-readonly-queue/main/pr-18242-* present on origin at 2026-09-15T06:07Z), so the seat's remaining acts under ruling C are the readings and the label: check-clause2-carriers --pair 18242 exit 0 re-read at 2026-09-15T06:07Z with the C6-RECORD note naming that record; on that head Lint & Repo Gates and TypeScript Type Check both success and every other check is success or skipped; mergeable_state clean, 15 commits behind origin/main with no main commit touching any of the four files since the merge-base, so no branch update was made (the queue builds the merge); needs-user-decision cleared through label-write.mjs and read back. Serial note: PR #18250 (:645–:646) and PR #18256 (:306–:415) share SKILL.md in disjoint regions and merge origin/main at their own landing. MERGED is confirmed later by two readings (the queue ref gone + git log origin/main carrying (#18242) with one parent), then #17374's residue is stripped and the platform-readings family fold (#18158 · #18195 · #16762 · #18219 · #18147 · #18258 · #18262) is unblocked on platform-readings.md (ceiling 454 → 459 by this PR's ruledRaises entry).


Generated by Claude Code

Merged via the queue into main with commit 1a02ef1 Sep 15, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants