Skip to content

Commit e3a86e3

Browse files
os-zhuangclaude
andauthored
docs(pm-skill): land the quota/credential incident facts and reconcile the body-entity read/write rows in platform-readings (#9485)
Merges the measured facts of the 2026-08-16 quota/credential incident and the 2026-08-18 pool-exhaustion readings into the API-quota section: the REST fallback is not executable from an MCP-only session, field slimming does not avoid the server-side field_values fan-out, WebFetch and git transport are the famine-degraded read paths, mid-session credential rotation kills the GitHub MCP server, and gradual auth propagation can fake a zero-issue lane. Reconciles the read-layer entity artifact with the write-layer angle-bracket loss as scoped, non-contradictory rows. Pays for the new rows by compressing existing rows in place; the file stays at its 134-line ratchet ceiling. Claude-Session: https://claude.ai/code/session_01Rn7aaamsR99FXRqLcpL99q Co-authored-by: Claude <noreply@anthropic.com>
1 parent af2a989 commit e3a86e3

1 file changed

Lines changed: 67 additions & 66 deletions

File tree

.claude/skills/pm-dispatch/references/platform-readings.md

Lines changed: 67 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -22,112 +22,113 @@
2222
- **状态核验用最小字段**(search/list + `fields`)或等事件,整对象 `get` 留给入队
2323
决策点;门禁放行判据 = 承载门禁族 job 的 conclusion,聚合(`blocked`/`dirty`)
2424
只作阴性筛查再定位,放行按名定向读单条 job,⛔ 不拉全表(按名定位失败才拉)。
25-
- **PR 转回 draft 会同时掉 auto-merge 与队列成员资格,且不自动恢复**;转正后必须
26-
重新挂。反方向同理:要真踢出队列,只有转 draft —— `disable_pr_auto_merge` 单独
27-
调用**不解除队列成员资格**,PR 照样落地。
28-
- **`enable_pr_auto_merge` 一律显式传 `mergeMethod: "SQUASH"`**:不传时静默退回被
29-
禁的 merge-commit 方式,等于无操作。**回显两向不可靠**(实测:队列路径回显空而
30-
入队照发;显式传 SQUASH 回显 `MERGE`,队列侧改写,落地仍每 PR 一提交)⇒ 权威信
31-
号只有 timeline 入队事件与最终 MERGED,⛔ 不拿回显当任何方向的证据。
25+
- **PR 转回 draft 同时掉 auto-merge 与队列成员资格,均不自动恢复**(转正后必须重
26+
新挂);反方向同理:要真踢出队列只有转 draft —— `disable_pr_auto_merge` 单独调用**不解除队列成员资格**,PR 照样落地。
27+
- **`enable_pr_auto_merge` 一律显式传 `mergeMethod: "SQUASH"`**(不传时静默退回被
28+
禁的 merge-commit 方式 = 无操作);**回显两向不可靠**(实测:队列路径回显空而入
29+
队照发;显式传 SQUASH 回显 `MERGE`,落地仍每 PR 一提交)⇒ 权威信号只有 timeline 入队事件与最终 MERGED,⛔ 不拿回显当任何方向的证据。
3230
- enable 后的验证序列:① 先验队列分支(给条目 ~20–30s 建出);② 分支在 ⇒ 结束,
3331
⛔ 不翻转;③ 等待后仍缺席**且队列已见 churn**(更新的条目建出了分支而你的没有 ——
34-
截断下单纯缺席不充分)⇒ 翻转一次(`disable``enable`),翻转后仍以 timeline
35-
事件验证;④ ⛔ enable 与它的队列验证之间永不插 `disable` ——「入队」webhook 可能
36-
乱序迟到,armed 窗口里补的 disable 会把已发生的真实入队撤掉。
32+
截断下单纯缺席不充分;首挂静默不入队实测存在,churn 后翻转即愈)⇒ 翻转一次
33+
(`disable``enable`),翻转后仍以 timeline 事件验证;④ ⛔ enable 与它的队列验
34+
证之间永不插 `disable` ——「入队」webhook 可能乱序迟到,armed 窗口里补的 disable 会把已发生的真实入队撤掉。
3735
- **队列踢出先认签名再决定重投**:已知 flaky 核对失败签名一致 ⇒ 原样重投;止血修
38-
复合入后**同一签名再现就不再是那条 flaky**,是新问题,必须重新诊断,⛔ 禁止条件
39-
反射式重投。第三种签名:本 PR 名下**没有任何** `merge_group` run 且批次同伴的
40-
run 全部 `success` = 队列重建的连带取消,不是红 —— 带签名读数收据重投一次(收据
41-
留在 PR 上),⛔ 无收据不重投;同一 PR 第二次被踢 ⇒ 停止重投,按签名四分支重判。
36+
复合入后**同一签名再现就不再是那条 flaky**,是新问题必须重新诊断,⛔ 禁止条件反
37+
射式重投;第三种签名:本 PR 名下**没有任何** `merge_group` run 且批次同伴的 run
38+
全部 `success` = 队列重建的连带取消不是红 —— 带签名读数收据重投一次(收据留在
39+
PR 上),⛔ 无收据不重投;同一 PR 第二次被踢 ⇒ 停止重投,按签名四分支重判。
4240
- **实测吞吐参数两则**:合并队列落地延迟 ≈ 每 PR 15–30 分钟且串行(⛔ 不据「还没
4341
落」提前判异常);单容器重验证(build+test)并发甜点 ≈3,排批按它定并发上限。
4442

4543
## API 配额
4644

47-
- GraphQL 配额(5000/时)极易打满,**MCP list 家族(`list_issues` 等)整个走
48-
GraphQL 池** —— 反复撞上的限流墙就是它;读与评论一律走 REST(core 15000/时,独
49-
立计);只有无 REST 对应物的写才花 GraphQL,`issue_write` 连查找半边都吃 —— 配额
50-
红时认领类动作整体排队,评论(REST)先行把结论发出去。
51-
- **git 先行**:能从本地检出 / `git log` / `ls-remote` 读到的状态不花配额;开轮先
52-
读配额(免装 gh CLI:`curl -H "Authorization: Bearer $GH_TOKEN"
45+
- GraphQL 配额(5000/时)极易打满,**MCP list/search 家族整个走 GraphQL 池** ——
46+
反复撞上的限流墙就是它;「读与评论走 REST(core 15000/时,独立计)」预设会话真有
47+
REST 通道:MCP 读工具无 REST 替身,直连 REST 受会话级授权门(会话起点快照,403
48+
`GitHub access is not enabled for this session`)与出口代理(只放 repo-scoped
49+
路径,`/search/*` 被拒、`/rate_limit` 例外)钳制 —— **纯 MCP 会话撞上枯竭池 =
50+
重置前没有任何 list 通道**,降级读法 = 下文 git 先行与 WebFetch 两行;只有无
51+
REST 对应物的写才花 GraphQL,`issue_write` 连查找半边都吃 —— 配额红时认领类动作排队,评论(REST 池)先行把结论发出去。
52+
- **`fields` 瘦身省载荷不省池**:MCP list/search 服务器端无条件抓 Project
53+
field_values —— 池枯竭时**最小字段请求同样全体失败**,报错串
54+
`failed to fetch issue field values: API rate limit already exceeded`
55+
- **git 先行**:本地检出 / `git log` / `ls-remote` 不花配额,断粮期分支存在性检查
56+
照常可用,PR 文件读取同走 git(REST PR files 端点实测可瞬态 404);开轮先读配额
57+
(免装 gh CLI:`curl -H "Authorization: Bearer $GH_TOKEN"
5358
https://api.github.com/rate_limit`),graphql remaining < 1000 ⇒ 本轮降级为 git
5459
先行 + 只做必要写。打满时:待执行写**排成有序清单挂进巡逻词**(不靠记忆),恢复
5560
窗口按序连清;重试对齐整点(REST core 整点重置)优于指数退避,⛔ 绝不忙轮询;
56-
search 与 core 独立计,一侧打满另一侧可作退路;REST core 共享身份下同样会打满
57-
——「走 REST」≠「不限量」。
58-
- **MCP 参数两陷阱**:`list_issues` 多标签过滤是 **OR(并集)**不是 AND —— 双标签
59-
查询混入别车道同状态卡与本车道全状态卡,结果良构、规模合理,失效全静默;正确读
60-
法 = **整车道单标签一次读全 + 本地对 labels 求交**(正确性要求,不是风格偏好)。
61-
`issue_write``labels`**整组替换**不是追加 —— 写标签前**同一动作内**重读现
62-
值合并再写,隔轮/隔小时的旧读数视为无效快照(按其回写会静默剥掉别的标签);真追加走 REST `POST /issues/{n}/labels`;写后照标签纪律回读。
63-
- **`list_issues` 永不返回 assignees**(`fields` 枚举无此成员;不传 `fields` 也没
64-
有)—— 已认领卡与空闲卡在响应里逐字节相同,车道清单因此回答不了「哪张能认领」,
65-
失效完全静默。清单只是**候选名单**:每一条在认领前必须过一次完整 `issue_read`
66-
(它才返回 `assignees`),⛔ 不把 `list_issues` 结果当候选集直接认领。
67-
- **MCP `issue_read` 的 body 实体转义是纯读侧伪影**(撇号/引号/尖括号成实体;
68-
comments 原样),存储体未变;**先解码实体再写回**的往返实测安全(无双重转义),
69-
腐蚀 body 的是把转义读数原样回写。写侧真损耗:HTML 注释写入时被**静默剥除**,要存活的内容一律写成可见 markdown。
61+
search 与 core 独立计,一侧打满另一侧可作退路;REST core 共享身份下同样会打满;文档载明、未实测:条件请求答 `304` 不计 core 池(仅当直连 REST 获准才相关)。
62+
- **公开仓降级读法:WebFetch github.com 网页零 API 配额**(带 label 过滤的 issue
63+
列表、issue 全文含评论、PR 页含 checks,实测撑得起整轮盘点);边界:~15 分钟缓存、列表行不含 assignee、内容是渲染层。
64+
- **会话中途轮换凭据把 GitHub MCP 服务器杀到不可恢复**:此后一切 `mcp__github__*`
65+
`Streamable HTTP error: invalid session`(含几分钟前还好的工具),只有新会话
66+
重绑 —— 轮换前提醒维护者:在飞席位丢的是整条 GitHub 通道;配额池按身份计,换身份即清零燃烧,共享身份结构不变。
67+
- **组织侧授权变更后仓库访问逐步传播**(同一端点数分钟内 403→200);403 错误对象存
68+
盘仍是合法 JSON,期待列表的脚本会静默报假「0 issues」—— 零命中纪律覆盖 list 读:空车道先对仓库 `open_issues_count` 反查再信。
69+
- **MCP 参数两陷阱**:`list_issues` 多标签过滤是 **OR(并集)**不是 AND —— 混入别
70+
车道同状态卡与本车道全状态卡,结果良构、失效全静默;正确读法 = **整车道单标签一
71+
次读全 + 本地对 labels 求交**`issue_write``labels`**整组替换**不是追加
72+
—— 同一动作内重读现值合并再写(隔轮旧读数 = 无效快照,按其回写静默剥别的标签);真追加走 REST `POST /issues/{n}/labels`;写后照标签纪律回读。
73+
- **`list_issues` 永不返回 assignees**(`fields` 枚举无此成员,不传也没有)—— 已
74+
认领卡与空闲卡响应逐字节相同,清单只是**候选名单**:每条认领前必须过完整
75+
`issue_read`(它才返回 `assignees`),⛔ 不把清单当候选集直接认领。
76+
- **MCP `issue_read` 的 body 实体转义是纯读侧伪影**(撇号/引号/尖括号成数字实体;
77+
comments 原样),存储体是明文,**先解码实体再写回**往返实测安全(无双重转义)——
78+
腐蚀 body 的恰是把转义读数原样回写;可逆的只有读侧,写侧剥除(HTML 注释、短
79+
`<…>` 片段 —— 细则见「读数陷阱」截断行)是**真实存储损耗**,⛔ 两类不并成一条「API 会改 body」,写后回读因此必做;实体归属(MCP 还是 GitHub API)与 `&amp;` 类未实测。
7080
- **`Blocked-by:` 行归 BODY(单通道反向索引)**:追加按上条「解码后写回」执行;历
7181
史上寄放在评论里的行按同程序**增量**回填(⛔ 不搞批量突击 —— 限流压力);解锁扫
72-
描只 grep body,⛔ 不加常设评论读;旧「连评论一起扫(`in:comments`)」提示作废,
73-
扫描走直读(`list_issues` + `issue_read` 读 body)。
82+
描只 grep body,⛔ 不加常设评论读;旧「连评论一起扫(`in:comments`)」提示作废,扫描走直读(`list_issues` + `issue_read` 读 body)。
7483
- **`list_issue_types` 对本集成 403,而 `issue_write type:` 正常**(读权限缺口):
7584
直接写已知好值(`Bug`/`Feature`/`Task`),写侧报错才是真信号,⛔ 不先探列表定可
7685
用性(列表 403 ≠「类型不可用」);非法值是响错还是静默丢弃未实测,写非已知值前先小样验证。
7786

7887
## 读数陷阱
7988

8089
- **读数四坑**:`cd X && cmd` 会短路(路径不存在时命令在当前仓继续执行,产出假读
81-
数)—— 跨仓一律 `git -C <path>`;`git grep -c <pat> | wc -l` 数的是文件数不是命
82-
中数;裸名 grep 被幸存家族当子串命中 —— 退役核验带引号精确名,更硬的判据是查声
83-
明式(`^(export )?(const|type|interface) <Name>\b`)而不是查提及;浅检出上的历
84-
史读数不可信(`merge-base --is-ancestor` 假「非祖先」、`rev-list --count`
85-
断、`branch -r --contains` 零输出)—— 先 `--deepen` 再判,或走 REST `compare`
90+
数)—— 跨仓一律 `git -C <path>`;`git grep -c <pat> | wc -l` 数文件数不是命中数;
91+
裸名 grep 被幸存家族当子串命中 —— 退役核验带引号精确名,更硬判据是查声明式
92+
(`^(export )?(const|type|interface) <Name>\b`)而非查提及;浅检出上的历史读数不可信
93+
(`merge-base --is-ancestor` 假「非祖先」、`rev-list --count` 截断、`branch -r --contains` 零输出)—— 先 `--deepen` 再判,或走 REST `compare`
8694
- `rerun_failed_jobs` 复用原 run 的提交与合并 ref,不拿新 main 重算 —— 红因是基上
8795
缺一个已合修复时重跑无效,只能推提交(`git merge origin/main`);判别:修复的合
8896
并时间晚于 run 创建时间即是。
8997
- **同一 head 上轻量兄弟 workflow `success` + 重量级载体 `cancelled` 是普通取代的
9098
预期签名,不是选择性失败**(cancel-in-progress 窗口只罩得住跑得慢的载体):先
9199
比对 run `head_sha` 与 PR 当前 head(取代必有新 head),不开「为何只取消它」调查。
92100
- **CI 红了先取完整日志归档再下结论**:「completeness check 绿」只断言没有 worker
93-
静默死,≠ 测试通过;并发输出的「相邻」≠「因果」(先查 `turbo.json` 依赖边);
94-
⛔ 不只看 tail。公开发出的诊断被推翻时,更正发在同样公开的位置,据它开的 PR 撤回
95-
draft、解绑 `Fixes`
101+
静默死 ≠ 测试通过;并发输出的「相邻」≠「因果」(先查 `turbo.json` 依赖边),⛔ 不
102+
只看 tail;公开发出的诊断被推翻时,更正发在同样公开的位置,据它开的 PR 撤回 draft、解绑 `Fixes`
96103
- **判「正文被截断」必须双读取**:`.body` 原文 + `Accept:
97-
application/vnd.github.full+json``.body_html`,两者在同一处断掉才算 issue 端
98-
截断;任何单一读法的尾部缺失先算读取端截断(工具输出上限、分页、切片)。写侧另
99-
一半:sanitizer 会在**写入时就地删除**`<…>` 片段(HTML 注释标记、`<n>` 类占
100-
位符、泛型),反引号与围栏**不提供保护** —— 要保留字面尖括号一律写 HTML 实体
101-
`&lt;` / `&gt;`;含这类片段的正文,写后回读逐个确认仍在(失效完全静默)。
102-
- **并行 spec PR 同动 pin 计数断言**(被踢不是事故,按 os-regen 序再解一轮):解
103-
冲突两侧收据都保留、按合并顺序堆叠,新计数**从合并后源码重数**(操作数是文件本
104-
身,不是历史),⛔ 不从两侧收据做算术;双方占同一编号是常态(各取
105-
当时 max+1),重编号后进侧。
104+
application/vnd.github.full+json``.body_html`,两者同一处断掉才算 issue 端截
105+
断,单一读法的尾部缺失先算读取端截断(工具输出上限、分页、切片)。写侧另一半:
106+
sanitizer 会在**写入时就地删除**`<…>` 片段(HTML 注释标记、`<n>` 类占位符、泛
107+
型),反引号与围栏**不提供保护** —— 要保留字面尖括号一律写 HTML 实体 `&lt;` / `&gt;`,要存活的注释类标记改写成可见 markdown;含这类片段的正文,写后回读逐个确认仍在(失效完全静默)。
108+
- **并行 spec PR 同动 pin 计数断言**(被踢不是事故,按 os-regen 序再解一轮):解冲
109+
突两侧收据都保留、按合并顺序堆叠,新计数**从合并后源码重数**(操作数是文件本身不
110+
是历史),⛔ 不从两侧收据做算术;双方占同一编号是常态(各取当时 max+1),重编号后进侧。
106111
- **容器重启杀死在飞 dev,现场三态判读**:① 分支已推 + PR 已开 ⇒ 只欠验收(CI 重
107112
跑 + 复核,不动代码);② 死在 regen 中途(未提交全是生成物、merge commit 已在)
108113
⇒ PM 直接续作 —— build → 整链 regen → 生成物门禁全绿 → 提交推送,恢复 commit 带
109114
`Recovery commit:` 前缀留审计;⚠️ 有的现场 regen 一件没跑,推送前先跑生成物门禁
110115
别赌;③ 死在源码编辑中途 ⇒ 先读 diff 判完整性 —— docblock 写全动机/失效模式/判
111-
据的,PM 可代跑终验后提交;写一半意图不明的 ⛔ 不代提交,记进交接。dev 临时目录
112-
(`.os-scratch/` 一类)是工作物不是交付物,清掉,⛔ 不进 feature PR。
116+
据的,PM 可代跑终验后提交,写一半意图不明的 ⛔ 不代提交、记进交接;dev 临时目录(`.os-scratch/` 一类)是工作物不是交付物,清掉,⛔ 不进 feature PR。
113117

114118
## 闭合关键词解析(PR 正文写侧)
115119

116-
- **PR 正文里「不修某卡」的否定句会关掉那张卡**:GitHub 的闭合关键词解析器匹配
120+
- **PR 正文里「不修某卡」的否定句照样关卡**:闭合关键词解析器匹配
117121
`fix/fixes/fixed/close/closes/closed/resolve/resolves/resolved` + `#N`,**不理会
118-
前面的否定词** —— 声明不修的那句话恰恰在合并时关卡。安全写法:把号码放在没有关
119-
键词打头的位置 —— `#N is not addressed here` / `out of scope: #N` /
120-
`#N remains open`。实测解析边界三条:关键词只绑**同一行**`#N`;动名词
121-
(closing/fixing)不是关键词;行内反引号里的关键词不触发(code span 实测不建闭
122-
合链接;围栏块未独立实测,按同规则对待但留待复测)。
123-
- **PR body 与 squash commit message 是两个独立解析源**:commit message 干净不代
124-
表 body 干净 —— 只查 commit 会漏。误关的卡以 `completed` 状态对一切「只看
125-
open」的过滤与巡检隐身,没有任何机械守卫覆盖这条路径;消费侧检查 = 合并后读
126-
`closed_by_pull_requests`(在复核清单)。
122+
前面的否定词** —— 声明不修的那句话恰在合并时关卡;安全写法 = 号码不被关键词打头
123+
(`#N is not addressed here` / `out of scope: #N` / `#N remains open`)。实测边
124+
界三条:关键词只绑**同一行**`#N`;动名词(closing/fixing)不是关键词;行内反引号里的关键词不触发(code span 实测不建闭合链接;围栏块未独立实测,按同规则对待但留待复测)。
125+
- **PR body 与 squash commit message 是两个独立解析源**(commit 干净 ≠ body 干净,
126+
只查 commit 会漏);误关的卡以 `completed` 状态对一切「只看 open」的过滤与巡检隐
127+
身,无任何机械守卫覆盖这条路径 —— 消费侧检查 = 合并后读 `closed_by_pull_requests`(在复核清单)。
127128

128129
## 断粮检测与跨墙恢复细则(5 小时用量墙)
129130

130131
原则、定时器选型(⛔ 不用 send_later 链)与恢复 playbook 在主文件;事实补遗:
131-
`npx ccusage blocks` 容器内可用(读本地会话记录),给当前 5 小时窗口边界/剩余时间
132+
`npx ccusage blocks` 容器内可用(读本地会话记录),报当前 5 小时窗口边界/剩余时间
132133
与燃烧率(预警);第三盲区:窗口起点是本地推断的近似值;撞墙时 API 调用失败、宿主
133134
报「limit reached, resets at HH:MM」(重置时刻主文件已述:那一刻可得、记下来)。

0 commit comments

Comments
 (0)