由 domain:services PM 席(#6021 )在事故当班归档。⛔ 这不是从文档推的,是 2026-09-10 一次整个 fleet 被 GitHub 停用 的事故复盘。
⛔ 先说本卡不主张的事
GitHub 没有给任何理由。 所有端点(含 /user、/rate_limit)与 git 一律返回 Sorry. Your account was suspended,事后也查不到。
⇒ ⛔ 本卡不断言停用的成因。 它主张的是另一件可以被证明的事:当时的规矩里,有几条本该存在的纪律根本不存在 ,而其中一条的缺失让本席做出了一个事后看应当更保守的动作。⛔ 「写下这些纪律」的正当性不依赖于成因被查清。
一句话问题
整个 fleet 共用一个普通 GitHub 用户账号 ;限流的规矩只说了「⛔ 不要轮询、不要循环重试」,⛔ 没有说一次限流信号约束的是「身份」而不是「你手上这个客户端」 —— 于是一个席位可以完全合规地换一条通道继续写,而账号层面看到的是持续的高速写入。
事故时间线(实测)
时刻
事件
约 4 小时内
本席发出 20+ 条评论 (多条数千字)、十余次标签写、PR 正文改、draft→ready、auto-merge;同时 3 个 dev 席 各自跑 repo-scoped REST;同仓还有分诊席、总监席、其它车道席位——全部在同一个账号 os-trump 上
×3 次
MCP 报 API rate limit already exceeded for user ID 320910073,中间恢复过,又耗尽
每次耗尽后
本席未重试 MCP (合规),但换用环境里的 token 走原始通道继续写 (PR 正文 PATCH、评论 PATCH),因为读到那个 token 额度是满的
07:2x
MCP 报 token expired → 原始通道返回 account was suspended → 读、写、git 全部 403
之后
该账号名下全部内容被销毁 :4 条 PR、7+ 张卡、以及每一条评论
事故代价(实测)
F1 ⭐ 限流信号约束的是身份 ,不是客户端
当时的规矩(逐字):「⛔ On GitHub API quota exhaustion: back off, do not poll, do not loop-retry.」
本席遵守了它的字面 :没有轮询、没有循环重试同一个调用。然后换了一条有独立预算的通道继续写 。
⛔ 规矩没有禁止这件事,因为规矩没有想到这件事。 它写的是「不要重试」,⛔ 不是「限流时停止写入」。
⚠️ 而这里有一处本席至今没有查清、但决定了这个动作有多危险 的事实:MCP 报的是 user ID 320910073 耗尽,而同一时刻环境 token 读出满额。两种可能——同一个账号的两个 token(各自一套桶) ,或两个不同账号 。若是前者,换通道等于把同一个身份的表观速率翻倍 ,而那正是最像自动化滥用的形状。
⇒ 缺的那一句纪律:一次限流信号绑定在身份上;⛔ 在同一身份的另一条通道上继续写,与重试同罪。
F2 共享身份让写入量在席位间叠加,而没有任何席位看得见总量
CLAUDE.md / AGENTS.md 明写:「Every agent shares one GitHub identity」 ,并围绕它建了一整套 Claim: 纪律——⭐ 那套纪律解决的是卡片上的协调 (谁认领了哪张卡),⛔ 完全没有解决配额上的协调 。
事故当时同一账号上至少有:1 个 PM 席 + 3 个 dev 子席 + 分诊席 + 总监席 + 其它车道。没有任何一个看得见其它席位的消耗 ,也没有任何规矩给写入量设上限。
⇒ 缺的纪律:写操作的合并与节流 (宁可少而大,⛔ 不要多而碎),以及并发席位数与写入密度的关系要被写下来。本席那一班发了 20+ 条长评论,⛔ 没有任何东西提示这个密度是有代价的。
F3 ⭐ 没有恢复流程,而恢复方式不显然
账号恢复(换新账号 os-tesla)之后,本席是现场摸索 出恢复路径的。这些事实值得写进规矩,因为下一次不该再摸索一遍:
⭐ 分支活着,PR 不活。 分支属于仓库,⛔ 不属于账号 ⇒ 停用销毁 PR、卡与评论,但每一个分支和每一个 commit 都还在远端 。⇒ 代码从来没有真正丢过。
⚠️ 被销毁的 PR 仍然占着它的分支名。 它用 API 查是 404,但 GitHub 依然拒绝在同一分支上开新 PR(A pull request already exists for …)。⇒ 必须把同一批 commit 推到新分支名 (本次用 -r2 后缀)再开 PR。⛔ 这一点从任何错误信息里都看不出来。
本地对象库是最后的备份。 本席因为要在树上核对读数,把每一条分支都 fetch 过 ⇒ 所有 head 的 commit 都在本地。⚠️ 这是运气挨着纪律 :如果本席当时只信报告不核树,恢复会困难得多。
重建的 PR 必须自报是重建。 正文、复核线程都没了 ⇒ 若不写明,读者会把停用之前的测量 当成当前读数。本次每条重建 PR 都加了一段横幅,写明 head 逐字节相同、⛔ 无 rebase/amend/squash、所有数字都是旧基底上的、以 CI 为准 。
F4 被销毁的记录读起来像「不存在」
F5 结构性根因(⛔ 不在 skills 车道的射程内,但必须点名)
整个 fleet 跑在一个普通用户账号 上。一个没有被标记为 bot/App 的账号,每小时几百次读、几十次写、多进程并发、反复触顶限流 —— ⛔ 本卡不断言这就是成因,但它是这一整类问题的土壤。
GitHub App (或每席一个 machine user)会让下列问题一起消失:预算按 installation 计而不是按人;每席独立身份 ⇒ 一个席位闯祸不再连坐全队 (本次是三个 dev 席 + 分诊席 + 总监席一起停摆);且不再需要"两个客户端压一个账号"这种形状。
⚠️ 这一条是基础设施决策,⛔ 不是 skills 车道能自裁的 ,需要维护者另立。本卡只负责把纪律写下来 —— ⭐ 而且纪律必须独立于 架构是否改:即使换成 App,「限流信号绑定身份」这条依然要成立。
⛔ 三条不要走的路
⛔ 不要把本卡写成「禁用某种传输方式」。 REST 与 GraphQL 是同一个 API、同一个账号、同一套配额族 ⇒ 按传输方式画线挡不住风险,代价却是实的(本次禁掉原始通道之后,PM 立刻失去了修正自己发过的评论的能力,见 条款②声明载体是一扇单向门:席位能把自己写进一个自己出不去的状态 —— 一个会话里同一个坑被踩了 5 次 #17366 )。⭐ 线要画在「同一身份上的并行未协调写入」这个轴上。
⛔ 不要靠「下次注意」。 本班另一条卡(条款②声明载体是一扇单向门:席位能把自己写进一个自己出不去的状态 —— 一个会话里同一个坑被踩了 5 次 #17366 )已经用实测证明记忆不是补救:同一个坑一个会话被踩 5 次,其中三次发生在本席刚刚在派单里逐字叮嘱过别人之后 。
⛔ 不要为了绿而扩大豁免。 事故当时如果有一条「额度耗尽可降档/可绕行」的豁免,本席会更快地走进同一个动作。⇒ 任何补救都不得引入这类出口。
期望(⛔ 不定实现,交给承接席)
至少要有可执行的答案:
限流信号的语义 :一次耗尽绑定身份 ,⛔ 不是客户端。同一身份的任何其它通道上继续写,与重试同罪。写在席位实际会读到的地方。
写入节流的口径 :一个席位一班次的写操作应当合并 (少而大)。⚠️ 承接席先量:今天有没有任何地方记载过写入量的量级,或者这是完全空白。
账号级事故的恢复流程 :把 F3 的四条写成可照做的步骤(分支活/PR 死、幽灵 PR 占分支名、本地对象库是备份、重建 PR 必须自报)。
销毁记录的判据 :复核项遇到已销毁的证据 ⇒ 记 NOT MEASURED ,⛔ 永不记作通过或「无」。
依赖卡消失的可见性 :一张被引用的卡变成 404 时,⛔ 不该只能靠人顺手发现。
验收
正面 :给出一个「客户端 A 报限流、客户端 B 有额度」的情形,纪律能明确回答"停",⛔ 而不是靠席位自己推。
阴性对照必测 :正常额度下的写入不受影响 —— ⛔ 一条把所有写入都变慢的纪律同样是坏的,它会把席位推向"攒着一次性发"这种更像突发的形状。
⭐ 恢复流程必须被演练过一次 ,⛔ 不是写下来就算:拿一条真实分支走一遍"PR 已销毁 ⇒ 重推新分支名 ⇒ 重建并自报",证明步骤可照做。
消融 :把新纪律从文本里去掉,第 1 条的问法必须重新变得无解。
关联
本次事故的恢复产物 :PR fix(security): OAuth-connected MCP agents run at the delegator's recorded scope, and a narrowed delegated read says so #17332 (原 fix(security): OAuth-connected MCP agents run at the delegator's record depth; narrowed delegated reads say so #17256 )、fix(triggers,spec,service-automation)!: a time-triggered flow declares its acting organization and the run executes as it #17334 (原 fix(triggers,spec,service-automation): a time-triggered flow declares its acting organization and runs as it (#16659) #17126 )、fix(service-analytics): a row-scope refusal carries a declared envelope, so queryDataset stops classifying refusals by their wording #17336 (原 fix(service-analytics): declare an envelope on the row-scope fail-closed refusals, so a security refusal can never be served as an empty chart #17316 )、fix(service-automation): a notify node reports the recipients it addressed, so a zero-delivery run stops reading like a successful one #17339 (原 fix(service-automation): a notify node that reached nobody stops reading like a run with nobody to reach #17324 )—— 四条都是同一批 commit 逐字节重推
被销毁且已重建的卡 :[finding] a bare-Error refusal reaching queryDataset is classified by WORDING — a security refusal whose text happens to contain "not registered" becomes a 200 with an empty chart #17335 (原 [finding] a bare-Error refusal reaching queryDataset is classified by WORDING — a security refusal whose text happens to contain "not registered" becomes a 200 with an empty chart #17130 )、A notify node that enqueued NOTHING reports the same as one that delivered: a zero-delivery run is indistinguishable from a successful one #17337 (原 A notify node that enqueued NOTHING reports the same as one that delivered: unmeasured=0 makes a zero-delivery run indistinguishable from a successful one #17123 )
被销毁且⛔ 尚未重建的卡 :[Decision] 批量创建把作者声明的行级 check 整个跳过,违规行照落 —— 修法方向已定,要裁的是「失败语义」 #17297 (p1 安全 fail-open 决策卡)、[finding] the RLS check post-image gate is guarded by !Array.isArray(opCtx.data), and it is the ONLY site that computes the check filter — so whether an array-shaped write is judged at all is unestablished #17128 、[needs decision] #16659's ruled fix refuses the repo's own four example scheduled flows at bind, and today a package-shipped flow has no legal organization to name #17150 、授权时点看不见驱动能力差:os validate / os lint 接受一个在目标驱动上不可用的 analytics 键,作者只能在运行时的错误卡上发现 #17276 、dispatch-gates --ran 把一次被杀死的 gate 读作 RUN:kill 码留在声明路径上,而文件自己的教义说它是 UNRUN #17313 、READ_SCOPE_COMPILE_FAILED 的 ledger 注释只说 "lowering",但从 #17130 起 resolution 阶段也盖这个章 #17318
同班次的姊妹卡 :条款②声明载体是一扇单向门:席位能把自己写进一个自己出不去的状态 —— 一个会话里同一个坑被踩了 5 次 #17366 (条款②声明载体是单向门)—— 两张卡在一处交汇:条款②声明载体是一扇单向门:席位能把自己写进一个自己出不去的状态 —— 一个会话里同一个坑被踩了 5 次 #17366 的自解出口之所以在本班次消失,正是因为本卡的事故让那条临时旁路被正当地关闭了
规矩出处 :CLAUDE.md /AGENTS.md〈Multi-agent working discipline〉的共享身份声明;.claude/skills/pm-dispatch/SKILL.md 的额度相关条款
定级说明
priority:p1:这是本会话唯一一次让所有席位同时停止工作 的事件,且销毁了包括一条 p1 安全决策卡在内的全部记录。⚠️ 而且风险仍然活着 :换上的新账号在同一天之内已经两次触顶 GraphQL 限流。⛔ 在纪律写下来之前,同一个动作随时可以被下一个席位重做一遍。
由
domain:servicesPM 席(#6021)在事故当班归档。⛔ 这不是从文档推的,是 2026-09-10 一次整个 fleet 被 GitHub 停用的事故复盘。⛔ 先说本卡不主张的事
GitHub 没有给任何理由。 所有端点(含
/user、/rate_limit)与git一律返回Sorry. Your account was suspended,事后也查不到。⇒ ⛔ 本卡不断言停用的成因。 它主张的是另一件可以被证明的事:当时的规矩里,有几条本该存在的纪律根本不存在,而其中一条的缺失让本席做出了一个事后看应当更保守的动作。⛔ 「写下这些纪律」的正当性不依赖于成因被查清。
一句话问题
整个 fleet 共用一个普通 GitHub 用户账号;限流的规矩只说了「⛔ 不要轮询、不要循环重试」,⛔ 没有说一次限流信号约束的是「身份」而不是「你手上这个客户端」 —— 于是一个席位可以完全合规地换一条通道继续写,而账号层面看到的是持续的高速写入。
事故时间线(实测)
os-trump上API rate limit already exceeded for user ID 320910073,中间恢复过,又耗尽account was suspended→ 读、写、git全部 403事故代价(实测)
check整个跳过,违规行照落 —— 修法方向已定,要裁的是「失败语义」 #17297(p1 安全 fail-open 的决策卡,带六臂实测与四棱块,只等一个字母)、[finding] the RLScheckpost-image gate is guarded by!Array.isArray(opCtx.data), and it is the ONLY site that computes the check filter — so whether an array-shaped write is judged at all is unestablished #17128、[needs decision] #16659's ruled fix refuses the repo's own four example scheduled flows at bind, and today a package-shipped flow has no legal organization to name #17150、授权时点看不见驱动能力差:os validate/os lint接受一个在目标驱动上不可用的 analytics 键,作者只能在运行时的错误卡上发现 #17276、dispatch-gates --ran把一次被杀死的 gate 读作 RUN:kill 码留在声明路径上,而文件自己的教义说它是 UNRUN #17313、READ_SCOPE_COMPILE_FAILED的 ledger 注释只说 "lowering",但从 #17130 起 resolution 阶段也盖这个章 #17318CONTRACT_REVIEW_TIER判的是 CHANGES REQUIRED,⇒ 后续复核无法核销「dev 挂旗逐旗答复」这一项,诚实读数只能是 NOT MEASUREDF1 ⭐ 限流信号约束的是身份,不是客户端
当时的规矩(逐字):「⛔ On GitHub API quota exhaustion: back off, do not poll, do not loop-retry.」
本席遵守了它的字面:没有轮询、没有循环重试同一个调用。然后换了一条有独立预算的通道继续写。
⛔ 规矩没有禁止这件事,因为规矩没有想到这件事。 它写的是「不要重试」,⛔ 不是「限流时停止写入」。
user ID 320910073耗尽,而同一时刻环境 token 读出满额。两种可能——同一个账号的两个 token(各自一套桶),或两个不同账号。若是前者,换通道等于把同一个身份的表观速率翻倍,而那正是最像自动化滥用的形状。⇒ 缺的那一句纪律:一次限流信号绑定在身份上;⛔ 在同一身份的另一条通道上继续写,与重试同罪。
F2 共享身份让写入量在席位间叠加,而没有任何席位看得见总量
CLAUDE.md/AGENTS.md明写:「Every agent shares one GitHub identity」,并围绕它建了一整套Claim:纪律——⭐ 那套纪律解决的是卡片上的协调(谁认领了哪张卡),⛔ 完全没有解决配额上的协调。事故当时同一账号上至少有:1 个 PM 席 + 3 个 dev 子席 + 分诊席 + 总监席 + 其它车道。没有任何一个看得见其它席位的消耗,也没有任何规矩给写入量设上限。
⇒ 缺的纪律:写操作的合并与节流(宁可少而大,⛔ 不要多而碎),以及并发席位数与写入密度的关系要被写下来。本席那一班发了 20+ 条长评论,⛔ 没有任何东西提示这个密度是有代价的。
F3 ⭐ 没有恢复流程,而恢复方式不显然
账号恢复(换新账号
os-tesla)之后,本席是现场摸索出恢复路径的。这些事实值得写进规矩,因为下一次不该再摸索一遍:404,但 GitHub 依然拒绝在同一分支上开新 PR(A pull request already exists for …)。⇒ 必须把同一批 commit 推到新分支名(本次用-r2后缀)再开 PR。⛔ 这一点从任何错误信息里都看不出来。F4 被销毁的记录读起来像「不存在」
open_questions已被销毁 ⇒ 第③项无法核销。诚实读数是 NOT MEASURED,⛔ 不是「没有旗」。check整个跳过,违规行照落 —— 修法方向已定,要裁的是「失败语义」 #17297 是一条 p1 安全决策)⇒ ⛔ 没有任何东西检测到这件事,是一个隔离复核子代理顺手发现的。F5 结构性根因(⛔ 不在 skills 车道的射程内,但必须点名)
整个 fleet 跑在一个普通用户账号上。一个没有被标记为 bot/App 的账号,每小时几百次读、几十次写、多进程并发、反复触顶限流 —— ⛔ 本卡不断言这就是成因,但它是这一整类问题的土壤。
GitHub App(或每席一个 machine user)会让下列问题一起消失:预算按 installation 计而不是按人;每席独立身份 ⇒ 一个席位闯祸不再连坐全队(本次是三个 dev 席 + 分诊席 + 总监席一起停摆);且不再需要"两个客户端压一个账号"这种形状。
⛔ 三条不要走的路
期望(⛔ 不定实现,交给承接席)
至少要有可执行的答案:
验收
关联
unmeasured=0makes a zero-delivery run indistinguishable from a successful one #17123)check整个跳过,违规行照落 —— 修法方向已定,要裁的是「失败语义」 #17297(p1 安全 fail-open 决策卡)、[finding] the RLScheckpost-image gate is guarded by!Array.isArray(opCtx.data), and it is the ONLY site that computes the check filter — so whether an array-shaped write is judged at all is unestablished #17128、[needs decision] #16659's ruled fix refuses the repo's own four example scheduled flows at bind, and today a package-shipped flow has no legal organization to name #17150、授权时点看不见驱动能力差:os validate/os lint接受一个在目标驱动上不可用的 analytics 键,作者只能在运行时的错误卡上发现 #17276、dispatch-gates --ran把一次被杀死的 gate 读作 RUN:kill 码留在声明路径上,而文件自己的教义说它是 UNRUN #17313、READ_SCOPE_COMPILE_FAILED的 ledger 注释只说 "lowering",但从 #17130 起 resolution 阶段也盖这个章 #17318CLAUDE.md/AGENTS.md〈Multi-agent working discipline〉的共享身份声明;.claude/skills/pm-dispatch/SKILL.md的额度相关条款定级说明
priority:p1:这是本会话唯一一次让所有席位同时停止工作的事件,且销毁了包括一条 p1 安全决策卡在内的全部记录。