Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions .claude/skills/pm-dispatch/references/platform-readings.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,28 +42,25 @@
## API 配额

- **配额按账户计,不跨席共享;按查询复杂度计费,不按调用次数**:各席位跑在**不同 GitHub 账户**下,「所有 agent 共用一个身份」只在**席位内部**成立(一个席位派出的每个 dev 都以该席身份发言 —— 这正是认领必须在评论里写 session ID 的理由)⇒ ⛔ 不据限流报文里的 user ID 推「池子跨席共用、优化自己没用」(实测推翻),本席额度**完全由本席做法决定**,优化有效且是唯一有效手段;计费按复杂度/节点数 ⇒ 优化方向是**每次少拿**,不是少调用。实测(`/rate_limit` 前后差量,该端点自身不计费):上限 5000/时;单个 dev 子代理 ~15 分钟烧 ~5658 点;一次 `list_issues`(34 张卡、perPage=100)= 107 点;耗尽时刻 GraphQL used 10461(超上限一倍)而 REST core used 7 ⇒ **最大消耗方是派出去的 dev 子代理,PM 巡检相比之下是噪声**(2026-08-22 实测)。
- GraphQL 配额(5000/时)极易打满,**MCP list/search 家族整个走 GraphQL 池** ——
反复撞上的限流墙就是它;「读与评论走 REST(core 15000/时,独立计)」预设会话真有
REST 通道:MCP 读工具无 REST 替身,直连 REST 受会话级授权门(会话起点快照,403
`GitHub access is not enabled for this session`)与出口代理(只放 repo-scoped
路径,`/search/*` 被拒、`/rate_limit` 例外)钳制 —— **纯 MCP 会话撞上枯竭池 =
重置前没有任何 list 通道**,降级读法 = 下文 git 先行与 WebFetch 两行;只有无
REST 对应物的写才花 GraphQL,`issue_write` 连查找半边都吃 —— 配额红时认领类动作排队,评论(REST 池)先行把结论发出去。
- **perPage 按预期 population 取,⛔ 不按习惯取 100**:上条计费规则(点数 ≈ 请求节点数/100)使 perPage 成为唯一杠杆 —— 那次 107 点的读数里 34 张卡付的是 100 张的钱,一次只要 3 条的 perPage=100 读法多付约 33 倍点数;只要 `totalCount` 的健康指标取 perPage=1,只要最近 N 张就取 N(2026-08-23)。
- **批量写 ~1 秒一发**:小时池之外还有**分钟级二级限流** —— GraphQL 端点 2,000 点/分、并发 ≤100,官方指引是**变更类请求之间停 ~1 秒**(mutation 在二级计算里按 5× 计)。双载体清标、批量重分诊这类把写挤在同一秒的扫动,会在小时池仍绿时撞上分钟墙(官方文档 2026-08-23 复核)。
- **默认读序 git → REST → MCP/GraphQL**(2026-08-23 策略翻转:REST 通道是**默认**读路径,⛔ 不再是「降级退路」)。list/查重/卡与 PR 读/标签回读**默认走容器 curl 的 REST 通道** —— App installation token,core 15,000/时,与 GraphQL 池**独立计**(实测同一天本席 GraphQL 池两次耗尽时 REST core 余 14,938);GraphQL 池(5000/时)只留给**没有 REST 对应物**的那几件:draft 翻转、auto-merge/入队挂载、语义 `/search/*`、Projects field_values、`issue transfer`。逐操作通道归属(每条实调 ✓ 带日期)、写侧配方与队列路由三读法见 `references/rest-channel.md`,⛔ 不在本表复述。
- **MCP list/search 家族整个走 GraphQL 稀缺池**,且服务器端无条件抓 Projects field_values —— 反复撞上的限流墙就是它;`issue_write` 连查找半边都吃。边界:直连 REST 受会话级授权门钳制(会话起点快照,403 `GitHub access is not enabled for this session`),**该门关着的纯 MCP 会话撞上枯竭池 = 重置前只剩 git 先行与 WebFetch 两行**;配额红时认领类动作排队,评论(REST 桶)先行把结论发出去。
- **`gh` CLI 的动词按传输分两桶,池枯竭时只死一半**(2026-08-24 同一分钟实测:GraphQL remaining 0 / REST core remaining 4987):porcelain 读家族全走 GraphQL —— `gh issue view` / `gh pr list` / `gh pr checks` 当场回 `API rate limit already exceeded`(`GH_DEBUG=api` 回显 `POST /graphql`);同一批事实改走 `gh api` 的 REST 路径全部照常返回(`repos/{o}/{r}/issues/{n}` 读正文、`.../issues/{n}/labels` 读标签、`.../commits/{sha}/check-runs` 读门禁结论、`.../pulls` 列 PR)⇒ **配额红的一小时里,认领/打标/评论/读门禁这条复核链整条跑得完**,⛔ 不据一次 porcelain 限流就宣布「GitHub 通道断了」而停轮。写侧同一分法:`gh pr create` 也走 GraphQL(同分钟被拒),而开 PR 有 REST 端点 —— `gh api -X POST repos/{o}/{r}/pulls -F draft=true` 同分钟成功 ⇒ 池子为 0 时 draft PR 照开得出,交付不必等重置。边界:本条取自装有 `gh` 的本机席位;容器里没有 `gh`(见「读数陷阱」),那边的分流按 MCP 通道另判。
- **真需要等重置的只有 merge 家族**:合并本身有 REST 端点(`PUT /repos/{o}/{r}/pulls/{n}/merge`,`gh api -X PUT` 可达);**draft 翻转与 issue transfer 没有** —— REST 的 update-a-pull-request 只收 `title`/`body`/`state`/`base`/`maintainer_can_modify`(无 `draft`),issues 端点表里没有 transfer 路由,auto-merge 挂载亦无 REST 对应物(2026-08-24 核对官方 REST 文档;未逐个实调端点)⇒ `pr ready`、`issue transfer`、auto-merge 挂载是 GraphQL-only,`until remaining > 阈值` 的守候留给这三个就够。走合并队列的仓落地必经 auto-merge ⇒ 红窗里**无退路**;直合仓有。
- **红窗调度:等重置的只有上面那几件 GraphQL-only 的**(逐件判据与官方文档核对日期住 `references/rest-channel.md`,⛔ 不在本表复述)⇒ `until remaining > 阈值` 的守候只给它们,⛔ 其余一切不为配额空等。走合并队列的仓落地必经 auto-merge ⇒ 红窗里**无退路**;直合仓有(合并本身有 REST 端点)
- **`issue transfer` 因配额或权限拿不到 ⇒ 当轮改走多仓协调条款已载明的「在目的仓重建」配方**(出处头 + 裸 `#N` 改全名 + 关源单为 moved):该配方纯 REST、配额免疫,⛔ 不为一次转移空等重置,更不因此把跨仓卡搁成半状态。
- **两个「瘦身参数」都不省池**:`fields` 省载荷不省池 —— MCP list/search 服务器端无条件抓 Project field_values,池枯竭时**最小字段请求同样全体失败**(报错串 `failed to fetch issue field values: API rate limit already exceeded`);
⛔ **`minimal_output: true` 不裁 `list_issues` 的 `body`**(2026-08-22 实测:返回字段仍含 `body`,首条 3258 字符、整体 122,685 字符仍超单次工具输出上限被落盘)—— 工具描述的反向暗示是假的,**永不当省额度手段写进任何 skill**;只要 number/labels/title 时也没有任何参数能关掉 body:要么接受整表 107 点,要么换更窄接口(`search_issues` 点数未实测)。⚠️ 前后体积对比不作证据(两次调用相隔数小时、population 已变),站得住的是直接观察 `body` 在。
- **git 先行**:本地检出 / `git log` / `ls-remote` 不花配额,断粮期分支存在性检查照常可用,PR 文件读取同走 git(REST PR files 端点实测可瞬态 404);
**零成本等价物四条**(API 两次挂掉期间实测全程可用):合并队列 `git ls-remote origin 'refs/heads/gh-readonly-queue/*'`;是否落地 `git log --format='%H %s' -40 origin/main` 按 PR 号 grep;squash 验证 `git rev-list --parents -n1`(父提交数);分支存在性 `git ls-remote origin 'refs/heads/*<key>*'`。
开轮先读配额(`curl` 带 Bearer `$GH_TOKEN` 打 `/rate_limit`,该端点免费;容器内**没有** `gh`,见「读数陷阱」),graphql remaining < 1000 ⇒ 本轮降级为 git 先行 + 只做必要写;
开轮先读配额(`curl` 带 Bearer `$GH_TOKEN` 打 `/rate_limit`,该端点免费;容器内**没有** `gh`,见「读数陷阱」),graphql remaining < 1000 ⇒ 本轮读全部按默认读序走 git + REST(独立桶,不受影响),GraphQL 只花在没有 REST 对应物的那几件写上;
**派 dev 之前同样先读一次**:额度不足先等重置再派 —— 中途撞限流的 dev **完不成强制查重**,只能把发现交回 PM 代为归档;限流窗口里「必须查重才能归档」的动作等待,⛔ 不盲目开卡。
打满时:待执行写**排成有序清单挂进巡逻词**(不靠记忆),恢复窗口按序连清;重试对齐整点(REST core 整点重置)优于指数退避,⛔ 绝不忙轮询;
search 与 core 独立计,一侧打满另一侧可作退路;REST core 共享身份下同样会打满;文档载明、未实测:条件请求答 `304` 不计 core 池(仅当直连 REST 获准才相关)。
- **公开仓降级读法:WebFetch github.com 网页零 API 配额**(带 label 过滤的 issue
列表、issue 全文含评论、PR 页含 checks,实测撑得起整轮盘点);边界:~15 分钟缓存、列表行不含 assignee、内容是渲染层。
- **查重先 `search_issues`**(2026-08-18 23:3xZ 实测:单次调用按 issue body 内文本命中且 `total_count` 精确 ——「search API 对本会话不可用/回错误对象」的继承说法实测为**假**;继承说法不是读数,复述必须带实测日期):body 文本匹配是 repo-scoped `list` 做不到的(全量抓取再 grep 才等价),`list` + 对照组降为回退。
- **`search_issues` 可整会话静默归零 —— 控制词一并归零**(2026-08-23 实测:某会话对**每个**查询回 `total_count: 0`,含已知必中的控制词;同时刻另一会话同工具正常 ⇒ 故障是**会话级**,不是工具/平台级)。诊断:结果可疑时先跑一个带 `repo:` 限定、已知必中的控制词;回 0 ⇒ 本会话 search 已坏,**立刻换通道,⛔ 不重试**(重试只烧配额)。正确退路 = **REST 列表端点** `GET /repos/{o}/{r}/issues?state=open&labels=a,b&per_page=100&page=N`(走 core 桶、结果**完整**;`GET /search/issues` **不是**退路 —— 出口代理按设计只放 repo-scoped 路径);⛔ **不要用 MCP `list_issues` 手扫**:它走 GraphQL 稀缺桶,且分页手扫极易半途而废(实测 226 张 open 只扫了 100 张)—— **不完整枚举比零结果更危险,它读作「搜过了,没有」**。⛔ 已推翻的候选机理,别再追:「查询串里带 GitHub 限定符(`repo:`/`is:open`)把语义 search 打成零」—— 两次实测反证:带 `repo:` 的控制词回 `total_count: 5`;另一席同工具两腿对照,`repo:… is:open …` 回 1(精确命中)而裸词回 13(语义扩散),限定符在那儿**收窄**结果而非破坏。归零机理仍未定(候选:scope 过滤层静默清空 / search 桶 403 被 MCP 层吞成空结果),要定它必须在复现会话里抓原始响应。
- **`search_issues` 可整会话静默归零 —— 控制词一并归零**(2026-08-23 实测:某会话对**每个**查询回 `total_count: 0`,含已知必中的控制词;同时刻另一会话同工具正常 ⇒ 故障是**会话级**,不是工具/平台级)。诊断:结果可疑时先跑一个带 `repo:` 限定、已知必中的控制词;回 0 ⇒ 本会话 search 已坏,**立刻换通道,⛔ 不重试**(重试只烧配额)。换到的就是默认读序那一档 = **REST 列表端点** `GET /repos/{o}/{r}/issues?state=open&labels=a,b&per_page=N`(走 core 桶、结果**完整**,perPage 按上面的右尺寸规则取;`GET /search/issues` **不是**退路 —— 出口代理按设计只放 repo-scoped 路径);⛔ **不要用 MCP `list_issues` 手扫**:它走 GraphQL 稀缺桶,且分页手扫极易半途而废(实测 226 张 open 只扫了 100 张)—— **不完整枚举比零结果更危险,它读作「搜过了,没有」**。⛔ 已推翻的候选机理,别再追:「查询串里带 GitHub 限定符(`repo:`/`is:open`)把语义 search 打成零」—— 两次实测反证:带 `repo:` 的控制词回 `total_count: 5`;另一席同工具两腿对照,`repo:… is:open …` 回 1(精确命中)而裸词回 13(语义扩散),限定符在那儿**收窄**结果而非破坏。归零机理仍未定(候选:scope 过滤层静默清空 / search 桶 403 被 MCP 层吞成空结果),要定它必须在复现会话里抓原始响应。
- **`search_issues` 不可靠地返回分钟级新卡**:同轮发现的东西查重,搜索之外必须按创建时间列近期 issue(`list_issues` + `orderBy: CREATED_AT`)—— 实测一张 ~7 分钟大的同实例卡被关键词与语义搜索双双漏掉,靠按日期列表才逮到;边界:两次观察、索引延迟未实测,断言只到「search 可能漏掉分钟级 issue」,更硬的窗口要另测(2026-08-20 实测)。
- **会话中途轮换凭据把 GitHub MCP 服务器杀到不可恢复**:此后一切 `mcp__github__*`
回 `Streamable HTTP error: invalid session`(含几分钟前还好的工具),只有新会话
Expand Down
46 changes: 46 additions & 0 deletions .claude/skills/pm-dispatch/references/rest-channel.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
# REST 通道操作对照表(references —— 按需加载)

出处:`platform-readings.md` API 配额段(**默认读序 git → REST → MCP/GraphQL** 的策略住那里,本表是逐操作的通道归属)。做一件事之前查一行:它有没有 REST 对应物。⛔ 不引用 issue 编号 —— 每行自含边界与日期。

**通道边界**:容器 curl = App installation token,REST core 15,000/时、与 GraphQL 池独立计;出口代理按设计只放 **repo-scoped 路径**(`/repos/{o}/{r}/...`)加 `/rate_limit`,org 级端点未实测。下文每条 ✓ = **2026-08-23 在真实会话里实调通过**(未另标日期者同此日);⛔ 未带 ✓ 的形状不当已验证事实复述。

## 读侧 —— 全部可迁移(MCP list/search 家族才是 GraphQL 燃烧源)

- ✓ 按标签/状态列卡:`GET /repos/{o}/{r}/issues?state=open&labels=a,b&per_page=N` —— `labels=` 是**真 AND**(MCP `list_issues` 的 labels 数组是 OR;语义相反那条住 `platform-readings.md`)。
- ✓ 卡 / PR 元数据:`GET .../issues/{n}` · `GET .../pulls/{n}`(assignees、labels、body 齐全 —— MCP `list_issues` 永不返回 assignees,这条差别本身就是走 REST 的理由)。
- ✓ 整条评论线:`GET .../issues/{n}/comments?per_page=100`。
- ✓ Timeline 事件:`GET .../issues/{n}/timeline`(cross-ref、`added_to_merge_queue`、ready_for_review)。
- ✓ PR diff / 文件清单:`GET .../pulls/{n}` 带 Accept `application/vnd.github.diff` · `.../pulls/{n}/files`(后者实测可瞬态 404 ⇒ PR 文件读取优先走 git)。
- ✓ 取某 ref 上的文件:`GET .../contents/{path}?ref=...`(raw accept)。
- ✓ 祖先 / 对比:`GET .../compare/{base}...{head}` —— 浅检出上本地祖先判据不可信时的正解。
- ✓ 门禁 / workflow:`GET .../commits/{sha}/check-runs` · `GET .../actions/runs`。
- ✓ 配额自读:`GET /rate_limit`(端点自身零计费,开轮先读的就是它)。

## 写侧 —— 全部可迁移

- ✓ 评论:`POST .../issues/{n}/comments`;改评论 `PATCH .../issues/comments/{id}`。
- ✓ 标签:**加法** `POST .../issues/{n}/labels` + **定向删** `DELETE .../issues/{n}/labels/{name}` —— 比 MCP `issue_write` 的整组替换安全:加法写剥不掉并发席位刚挂的标签(整组替换按隔轮旧读数回写会静默剥标,纪律住 `platform-readings.md`)。
- ✓ 建卡带标签 `POST .../issues` · 改正文/状态 `PATCH .../issues/{n}` · 认领 `POST .../issues/{n}/assignees`。
- ✓ 请求复审 `POST .../pulls/{n}/requested_reviewers` · 开 PR `POST .../pulls`(带 `draft=true`;GraphQL 池为 0 的同一分钟里实测开得出 draft PR ⇒ 交付不必等重置)。

## 不可迁移 —— 只有这几件,围着它们排计划

1. **draft → ready 翻转**:GraphQL-only mutation;出口代理只放钉住的 PR-review GraphQL 集(实测拒绝)。判据 = REST 的 update-a-pull-request 只收 `title`/`body`/`state`/`base`/`maintainer_can_modify`,**无 `draft`**(下面 2 与 5 同批核对:2026-08-24 核对官方 REST 文档,未逐个实调端点)。断粮出路:等 MCP 恢复,或人工点一下。
2. **auto-merge / 入队挂载**:GraphQL mutation(MCP `enable_pr_auto_merge`)。走合并队列的仓落地必经它 ⇒ 配额红窗**无退路**;直合仓有退路(合并本身有 REST 端点 `PUT .../pulls/{n}/merge`)。
3. **语义搜索**:`/search/*` 被出口代理按设计拒绝。退路 = REST 列表端点 + 本地 grep(既有纪律)。
4. **Projects field_values**:GraphQL-only —— 舰队并不需要它;MCP 服务器端**无条件**抓它才是漏点,不是需求。
5. **`issue transfer`**(2026-08-24 官方 REST 文档核对补入;原表只列前四件):issues 端点表里没有 transfer 路由 ⇒ 同为 GraphQL-only。拿不到时当轮改走「在目的仓重建」配方(纯 REST、配额免疫),配方住 `platform-readings.md`。

`until remaining > 阈值` 的守候只留给这几件,⛔ 其余一切不为配额空等。

## 第三桶 —— git 零配额等价物(先问 git,再问 REST)

分支存在性、合并队列分支、按内容判落地、squash 验证:四条 `ls-remote` / `git log` 拼写与各自的失效边界是 `platform-readings.md` 的既有正典行,本表只指路 —— ⛔ 不在两处各存一份。

## 队列路由的读法(2026-08-24 实测)

- **`merged_by` 是入队者,⛔ 不是绕队证据**:GitHub 把队列合并归属给**入队的那个账户**,该字段对「队列合并 vs 直接合并」零分辨力。一周内三席各自把人形 `merged_by` 读成「我们绕过了队列」,实测三次全为假 —— 都是队列合并。
- **问「本仓 auto-merge 是否经队列」,答案来自尝试动作,不来自属性字段**:① 直接合并 `PUT .../pulls/{n}/merge`,强制队列 ruleset 下回 **405 `Changes must be made through the merge queue`**;② PR 上的 `added_to_merge_queue` timeline 事件;③ 对已入队 PR 调 update-branch 回「已入合并队列的分支不能更新,要改先出队」。①② 的拼写与边界是 `platform-readings.md` 队列段的既有行,本条只把三读法归拢成一个判据。
- ⚠️ **计数不是机理读数(本行是一次被推翻的推断的墓碑)**:「repo 级 `GET .../actions/runs?event=merge_group` 计数为 0 ⇒ required 集为空、队列什么都不校验」这条推断**提出当天即被自身推翻** —— 同一姊妹仓 2026-08-24 11:04Z 首次产出 merge_group run(0 → 8),同日再测 224(阳性对照 `event=pull_request` 全程非零)。计数答的是「至今发生过没有」,不是「机制在不在」:零计数只作**弱先验**,判 required 集为空要读 ruleset 的 required 集本身、或看队列合并是否真在等检查。⛔ 别处写下的计数值一律先复测再用。
- **required job 名与分片矩阵的改名耦合(现行,自 2026-08-24)**:队列 required 集按 **job / check-run 名**匹配,**workflow 名从不作为 check context 出现**(所以拿 workflow 名在选择器里搜什么也搜不到);改其中任一 job 名**或 test 分片矩阵的形状**,必须**同一笔**更新队列的 required 集,否则队列静默挂起。姊妹仓 objectui 当日配置为 **9 个**:`Lint` · `Type Check` · `Test (shard 1/4 … 4/4)` · `Build & E2E` · `Build Docs` · `Changeset Declaration`。⛔ required 选择只 gate**等待**、不 gate**触发** —— 未列入的检查照跑、算力相同,红了不再挡队列(维护者当日裁定,原话:「我觉得够了」)。
- **配 required 集时先排掉 push-only job**:`if: github.event_name == 'push'` 的 job 在 `merge_group` 构建上永不报到,列为 required 即挂死队列;未展开的矩阵名(带字面 `${{ }}` 的串)是被跳过的占位符,不是真 context。
Loading
Loading