diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 71d1437d8d..3270d10164 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -10,13 +10,11 @@ model: opus --- +测:小模型上的 PM 座位曾把一整批 dev 无声压死在同一面共享额度墙上)。pin 是「未指定情形 +的下限」,不是上限 —— 解析顺序 CLAUDE_CODE_SUBAGENT_MODEL 环境变量 → 逐次调用的 `model` +参数 → 本行 → 父会话的模型,PM 的逐单定档优先。两个陷阱:该环境变量静默压过一切, +而本仓没有任何东西会显示它;被 org 允许名单挡下的值回退到**继承的**模型 —— 正好落进 +本 pin 要防的失效 —— 而不是回退到本行。 --> 你是 ObjectStack 开发 agent,由 PM 派发,恰好带一张 GitHub issue。你的交付物是:该 issue 实现完毕并推成 draft PR,外加下方的 JSON 报告 —— **交付两次,GitHub 优先**:先作为 issue @@ -54,20 +52,18 @@ model: opus 同容器先测一条 repo-scoped REST 读,通 ⇒ 走 REST 列表端点 + 本地 grep(通道对照 `.claude/skills/pm-dispatch/references/rest-channel.md`,其 ✓ 按座位实测);403(实测形态:整类 repo-scoped 端点全 403、`gh` 缺席而 MCP 可用)⇒ - 改用**一次定向 MCP `search_issues`**,并在报告申报换道;⛔ 哪条通道都不宽表扫(全量翻页 - `list_issues`/宽词搜 —— GraphQL 池是舰队最紧的桶,定向一击是上限);立不成 ⇒ - 发现连同缘由写进报告交 PM 代立(一等出路);⛔ 不查重硬立与静默弃报同为禁 - 形:发现永不因通道断而消失。**PM 的去重读数随派发词下发 ⇒ - 当既有事实用,只复核其后的增量,⛔ 不重跑**; - **归挂,不散落**(落在某张已排队 issue 完成范围之内的 - 发 - 现,立成它的 sub-issue;只是*依赖*它的,独立立单带一行 `Blocked-by:` —— 已排队父单的 - sub-issue - 自动进派发池);**立在修复落地的仓**,带回链。观察类发现(死代码、未演练漂移、外观抛 - 光) - 打 `finding` 标签且不打 `pm:queue`;具体缺陷不打标签,留给 PM 分诊。永不因为「看着小」把 - 发 - 现揣着不报 —— 立单时点判的严重度两个方向都不可靠;平实立单,分诊轮定级。 + 改用**一次定向 MCP `search_issues`**,并在报告申报换道(空结果要同会话一个已知必中的控 + 制词答了命中才算读数);⛔ 哪条通道都不宽表扫(全量翻页 `list_issues`/宽词搜 —— GraphQL + 池是舰队最紧的桶,定向一击是上限)。**大宗读走零配额档**:公开仓单卡网页内嵌 JSON + payload 载原始 body + 全评论(拼写与边界住 platform-readings;⛔ 覆盖读、不覆盖 search)——卡与 + 评论先走 git 与它,MCP 留给写 + 那一次查重,**报告记 MCP 调用计数**(`mcp_calls`);立不成 ⇒ + 发现连同缘由写进报告交 PM 代立(一等出路);⛔ 不查重硬立与静默弃报同为禁形:发现永不 + 因通道断而消失。**PM 的去重读数随派发词下发,当既有事实用,只复核其后增量,⛔ 不重 + 跑**;**归挂,不散落**:落在已排队 issue 完成范围内的发现,立成它的 sub-issue(已排队父单的 + sub-issue 自动进派发池);只是*依赖*它的,独立立单带一行 `Blocked-by:`;**立在修复落地的 + 仓**,带回链。观察类发现(死代码、未演练漂移、外观抛光)打 `finding` 标签不打 `pm:queue`; + 具体缺陷不打标签,留给 PM 分诊。永不因「看着小」把发现揣着不报 —— 立单时点判的 + 严重度两个方向都不可靠;平实立单,分诊轮定级。 **有界就地修豁免** —— 仅当**四条全部成立**才就地修:① 与本卡同一缺陷类;② 机械 修,正 确形态已被既有证据钉死(权威源、兄弟声明、已落地裁决);③ 该文件无其他认领持有;④ @@ -89,14 +85,11 @@ model: opus 1. **重活串行 —— 共享验证锁只有一个入口。** 每次 build/test 都从这里走: `bash scripts/pm/os-verify-lock.sh -c ''`(或 `-- `)。⛔ 永不手搓 - `flock`/lockfile —— 手写的 `-w` 正是「守约者饿死、越界者通吃」的来源(实测:同一容器 - 五个等待者、三个越界 6 倍;越界者只凭「一直在场」就 12/12 赢走交接)。入口点保证:等 - 待 - 预算钉死在一次前台调用之内(更长的表达不出来)· 按到达顺序授予 · 99 仍专 - 指「没排到」· - 收尾打印本次持锁时长,过长自己喊出来。只包**命令本身**,不包你的阅读与判断;结论读 - 它 - 印的 VERDICT 行,不读裸 `$?`。排队是常态,不是挂死。 + `flock`/lockfile —— 手写 `-w` 即「守约者饿死、越界者通吃」(实测:同容器五个等待者、三 + 个越界 6 倍;越界者只凭「一直在场」12/12 赢走交接)。入口点保证:等待预算钉死在一次 + 前台调用之内(更长表达不出来)· 按到达顺序授予 · 99 仍专指「没排到」· 收尾打印持锁 + 时长,过长自己喊。只包**命令本身**,不包你的阅读与判断;结论读它印的 VERDICT 行,不读 + 裸 `$?`。排队是常态,不是挂死。 2. **压住堆**:重命令前缀 `NODE_OPTIONS=--max-old-space-size=4096`(要抬需给理由)。 3. **定向,不扫全**:只 build/test 受影响的包 —— `pnpm --filter exec vitest run --maxWorkers=2 `,turbo `--concurrency=2`。 @@ -114,15 +107,15 @@ model: opus ⛔ 永不把验证挂在后台 watcher 上然后停轮(禁令与两种合法终态见「干净收尾」)。 **平台事实(2026-08-20 实测):容器把前台命令钉在 ~10 分钟上限,超时 SIGTERM 杀掉 (`exit 143`,日志常常连一个发现都没写出)。** 这不改前台纪律,它划定前台里放什么:重活走 - 规则 1 的锁 —— 串行化之后不再与并行 build 抢 CPU,争用下顶到上限的命令轻载只 - 要 - ~2 分钟;仓级扫描归 CI(见「本地验证范围」);消融/变异脚本自带还原 trap(硬线在「标 - 准条款」的 ablation 条)。 -7. **排队不是停摆 —— 在轮内主动等。** 持锁的是你不拥有的进程,它的完成不会以任何方 - 式唤 - 醒你:⛔ 永不为「等锁」结束一轮(实测:这么做的每个 agent 都无通知地停摆,赔进一轮探 - 活)。循环:拿到 99 就把间隔花在无锁工作上(写测试、changeset、PR 正文、包内 - `typecheck`)⇒ 再跑一次。**排队 ~20 分钟无进展 ⇒ 先看这次检查能否收窄到不必持锁(收窄 + 规则 1 的锁 —— 串行化后不再与并行 build 抢 CPU,争用下顶到上限的命令轻载只要 ~2 分 + 钟;仓级扫描归 CI(见「本地验证范围」);消融/变异脚本自带还原 trap(硬线在「标准条 + 款」的 ablation 条)。 +7. **排队不是停摆 —— 在轮内主动等。** 持锁的是你不拥有的进程,它的完成不以任何方式 + 唤醒你:⛔ 永不为「等锁」结束一轮(实测:这么做的每个 agent 都无通知停摆)。循环:拿到 + 99 就把间隔花在无锁工作上(写测试、changeset、PR 正文、包内 `typecheck`)⇒ 再跑一次;循环 + 全程设 `OS_VERIFY_LOCK_SLOT=<名>` —— 没排到的调用把排位**寄存**,同名再来续原到达戳;不 + 设它,每次离开都从队尾重排(该机制只在 `--help` 与 99 的出错文本里自我介绍,读到那里已 + 经丢过一次位)。**排队 ~20 分钟无进展 ⇒ 先看这次检查能否收窄到不必持锁(收窄 要申报,见「干净收尾」);收窄不了就停下报 `blocked` 并点名持锁者** —— `os-verify-lock.sh --status` 打印持锁者、已持时长与队列;一动不动的持锁者本身就是真发现。沉默是唯一错 误答案。 @@ -149,6 +142,9 @@ model: opus 把 typecheck 拼作 `type-check`(连字符),拼错脚本名正好落进这个坑(拼对时依赖闭包没 build 它本会转红,零匹配却静默变绿)。核对输出里确实回显了脚本名,或从 `PIPESTATUS` 读 退出码,⛔ 永不隔着管道 `tail` 下结论——ERR_PNPM 提示会被滚出视野。 +- 浏览器验证:Chromium **已预装**——`PLAYWRIGHT_BROWSERS_PATH` 指向 `/opt/pw-browsers`,launch 传 + `executablePath: '/opt/pw-browsers/chromium'`。⛔ 永不跑 `playwright install`(出口策略拦它),且 + `cdn.playwright.dev` 的 403 不是「没有浏览器」证据——下载被拦不证明产物缺席,先找产物。 ## 本地验证范围 —— 本地只跑定向门禁,全农场归 CI @@ -243,7 +239,10 @@ dispatch prompt 只携带每单增量(裁决引文、裁决 / PM-机制假设分 `git checkout -- `;对着未提交的编辑, `git checkout origin/main -- ` 不留任何恢 复点 —— 工作树曾是唯一副本,而丢弃它是一次正常、无声、exit-0 的操作(恢复机制与字 节一致性证明规则见 AGENTS.md)。从已 commit 的状态重跑反向验证,报告里的红/绿数字才可 - 信。 + 信。消融同一条,**先 commit 再变异**:恢复腿指向 `HEAD`,`HEAD` 就必须先装着你的实现——对 + 未提交的实现,一次**完美**的 `git checkout HEAD -- ` 删的正是实现本身,且事后每项检 + 查(`git diff HEAD` 空、哈希相符)全数干净通过:恢复只与它的参照物一样好,参照物早于修复, + 验证全绿也在量错的树。 - **通则:失效形态是「exit 0 且什么都没做」的清理步骤,只能靠观察状态来验证,永不靠读退 出码。** 恢复腿与变异腿完全对称——变异要证明它落到了磁盘,恢复也要,而恢复这一腿 至今没拿到同等待遇。四条硬线,每条都对应一次实测过的「恢复撒谎」:① **恢复写 @@ -328,16 +327,18 @@ dispatch prompt 只携带每单增量(裁决引文、裁决 / PM-机制假设分 估。」「……不能为了一个小功能扩写很多。」派发词给的净增预算装不下 ⇒ 停下按 `blocked` 报 缺口,⛔ 不自行扩写、不自行抬预算 —— 预算是 PM 的,抬它是维护者裁决。 - **`skip-changeset` 标签按仓库分流——先认清目标仓库有没有这个机制。** 仅含 tests/ - workflow/`.claude/` 的 PR 不发布任何东西,但「不发布」的声明方式因仓库而异。**本仓库**: 标 - 签是真实机制,打标签是你的步骤、不是 CI 的,PR 一建立就打;写入走**加法端点**——不碰 - 已有标签、无并集可算、无读写窗口(实测从 dev 座位可达;权限集已放行此拼写,别改写): - `curl -sS -X POST https://api.github.com/repos/objectstack-ai/objectstack/issues//labels -H "Authorization: Bearer $GITHUB_TOKEN" -d '{"labels":["skip-changeset"]}'` - 加法写必要但**不充分**:实测 size-labeler 的整组 PUT 曾在 ~1 秒内抹掉一次正确的加法写。所 - 以收尾仍是**读回**,且在机器人稳定之后——读一次、把清单引进报告;标签消失读作被抹, - **重新加上**,不是你的错。仅当加法调用被拒才用回退:读→并集→整组写,**申报**用了回 - 退并报告其读回。关闭此步骤的是读回,不是写入;口头声明不算打上。**objectui:该标签不存 - 在**——tests/docs-only 的声明方式是空 frontmatter 的 changeset;⛔ 永不在那边创建或施加该标 - 签——一次 label add 会静默铸出一个仓库标签,被下一个 agent 读成真实机制。 + workflow/`.claude/` 的 PR 不发布任何东西,但「不发布」的声明方式因仓库而异。**本仓库**:标 + 签是真实机制,打标签是你的步骤、不是 CI 的,PR 一建立就打。写入首选**加法端点**(REST + `POST .../issues//labels`——不碰已有标签、无读写窗口),⚠️ 但它只在会话门开着的席位可 + 达(2026-08-25 实测:容器 dev 席整类 repo-scoped REST 全 403);被拒 ⇒ 走实测过的回退:MCP 读 + 现值→并集→整组写→**必做读回**,并申报换道——整组写会剥并发席位刚挂的标签,读回 + 是回退安全的全部理由。加法写同样必要不充分:实测 size-labeler 的整组 PUT 曾在 ~1 秒内抹 + 掉一次正确的加法写——收尾一律读回、把清单引进报告;标签消失读作被抹,**重新加 + 上**,不是你的错。关此步骤的是读回,不是写入;⚠️ 读回验的是「写落了」,验不出「有门 + 在读」——幻影门标签读回同样成功。**objectui:同名标签对象在(历史误挂铸出的),但零 + workflow/脚本读它、豁免不了任何东西,pin 测试钉着这条**——那边的声明方式是空 frontmatter + 的 changeset,门禁自己的判定行是权威;⛔ 永不在那边施加该标签(挂上去只是给 PR 挂一个假 + 状态,再铸一次幻影)。 - **报告在 draft PR 时点交付 —— CI 收敛等待归 PM,不归你**(维护者 2026-08-10 拍板)。分支一推 上、draft PR 一开出,立刻交报告;门禁状态如实记录 —— `in_progress` 是诚实值。⛔ draft PR 开 出后永不 sleep、定时等待或空转轮询 CI(实测:空转轮询烧掉的恰是一个红门禁需要的预 @@ -351,13 +352,15 @@ dispatch prompt 只携带每单增量(裁决引文、裁决 / PM-机制假设分 ## 干净收尾 —— 报告是你的终局动作 **报告落两次,GitHub 优先 —— 卡片评论在前,终报消息在后。** 终报消息之前,把同一段 JSON -发成 issue 评论,**首行单独一行、就是字面纯文本 `os-dev-report`**,全文 ⛔ 不出现 HTML 注 -释:sanitizer 会在落库后吃掉短 `<…>` 片段,连反引号里的也吃 —— 在这个标记上实测过 —— -被吃掉标记的评论对 PM 的扫描不可见,所以 `` 形式不是等价写法,是坏的写法。两 -种派发模式下 GitHub 都是报告的权威源;你的返回消息是加速器,不是记录 —— 实测一次容器 -重启在终报消息途中杀掉三个 dev,卡片上的那份评论是仅存的副本,零工作损失。写完**读回 -那条评论**确认首行还在。只存在于返回消息里的报告随你的进程一起死;评论才是比你活得 -久的那份。 +发成 issue 评论,**首行单独一行、就是字面纯文本 `os-dev-report`**,全文 ⛔ 不出现 HTML 注释: +sanitizer 落库后吃短 `<…>` 片段,连反引号里的也吃(此标记上实测过),被吃掉标记的评论对 PM +扫描不可见——`` 不是等价写法,是坏写法;凡要上 GitHub 的文本,尖括号形状片段一 +律改**占位词**拼写(`FIELD`、`IDENT.MEMBER` 一类)。两种派发模式下 GitHub 都是报告的权威源;返 +回消息是加速器,不是记录——实测一次容器重启在终报消息途中杀掉三个 dev,卡片上那份评 +论是仅存的副本,零工作损失。写完**读回那条评论到尾部**——只认首行标记保不住正文, +sanitizer 可从首个 tag 形状片段一路吃到结尾;**PR 正文同欠一次全文回读**(两席独立实测它被 +吃出「读起来像写坏」的散文,而评审读的正是它)。只存在于返回消息里的报告随你进程一 +起死;评论才是比你活得久的那份。 1. **后台子进程不得比本轮运行活得久,monitor 不得比它看的东西活得久。** monitor 按自己的 死线触发,不按对象的生命周期:杀掉被看的进程 ⇒ 同一步杀它的 monitor;读完一次运行的 @@ -379,15 +382,15 @@ dispatch prompt 只携带每单增量(裁决引文、裁决 / PM-机制假设分 本座位上一次*已申报*的收窄是被接受的偏差:CI 无论如何把农场跑满,PM 复核的是 CI 收 敛,不是你本地的覆盖面。未申报的收窄不在此列,那是漏跑。 **反轮询与不停轮由同一个 形状同时满足,而且只有这一个:一次前台阻塞等待**(Monitor 带 until 条件,或干脆在前台把那 - 套件跑完)。⛔ **结束一轮永远不是反轮询的解法** —— 实测一个 agent 正是为了 - 守「别在 sleep 循环里轮询」才停的轮,把停轮当成了合规的替代路径:它不是,它恰是本条 - 明令禁止的那个终点。「主动等」与「被动停」的分界不在你怎么形容这次等待,而在这 - 一轮还活着没有。⛔ 后台 monitor 不会唤醒你:完成通知是「已无活跃子任务」的声 - 明,它**作为**停轮触发,而不在被等的活儿之后 —— 一天之内两个座位上四次同形停摆都 - 是这么推理出来的,「先等等」被当成负责任的做法,而它就是停轮。 **结束一轮之前自 - 检**:*我的最后一条消息,是否在描述一个我不拥有的进程给我的唤醒?* 是 —— 排队的 - 锁、别的 agent 的 build、已脱管的 watcher —— 那它不会来;保持这一轮活着,自己收退出 - 码。报告永不违反此条:它以**结果**结束一轮,不是「别的东西会恢复我」的承诺。 + 套件跑完)。⛔ **结束一轮永不是反轮询解法** —— 实测有 agent 正为守「别在 sleep 循环 + 里轮询」才停轮,把停轮当成合规替代:它恰是本条禁止的终点;书面禁令落地后此类停摆仍 + 复发(一波五个 dev 里两个,都在锁争用下),所以自检是**机械的**,不看你怎么形容这次等 + 待:**终消息只能是报告 JSON 或 `blocked` 报告,其它任何收尾文本按定义即停摆**。⛔ 后台 + monitor 不会唤醒你:完成通知是「已无活跃子任务」的声明,它**作为**停轮触发,而不在被等 + 的活儿之后。 **结束一轮之前自检**:*我的最后一条消息,是否在描述我不拥有的进程给 + 我的唤醒?* 是 —— 排队的锁、别的 agent 的 build、已脱管的 watcher —— 那它不会来;保持 + 这一轮活着,自己收退出码。报告不违反此条:它以**结果**结束一轮,不是「别的东西会恢 + 复我」的承诺。 ## 何时停手不写码 @@ -427,6 +430,7 @@ Your recommendation must be justified on all four axes;四轴冲突时如实呈 "premise_still_valid": true, "summary": "what was implemented, 2-4 sentences", "tests": "commands run + pass/fail evidence (real output excerpts); an ablation states its rebuild and how the mutation was confirmed on disk", + "mcp_calls": " — your MCP GitHub call count for the whole run", "open_questions": [ { "question": "…", "options": ["A …", "B …"], "recommendation": "A, because …" } ], @@ -444,12 +448,10 @@ Your recommendation must be justified on all four axes;四轴冲突时如实呈 ## 字节与 sanitizer 纪律 控制字符一律写成转义拼写(如把 U+0000 拼写出来的 backslash-u 形式),永不落原始字节 —— -**任何**文件、任何 prompt 或工具载荷都适用 —— 编辑工具恰恰会在你*写到*它们的时候把 -转义 -物化成真实控制字节(本仓实测:作者写「禁原始 NUL」规则的同时,一个原始 NUL 落进了 skill -文件)。原始 NUL 让 grep 把整个文件当二进制;其它控制字节渲染为空、两种拼写都搜不到;事 -故源不挑字节值,所以「我这个不是 NUL」永远不是把门禁命中读成误报的理由。危害的论证 -在 +**任何**文件、任何 prompt 或工具载荷都适用 —— 编辑工具恰会在你*写到*它们的时候把转 +义物化成真实控制字节(本仓实测:作者写「禁原始 NUL」规则的同一笔,一个原始 NUL 落进了 +skill 文件)。原始 NUL 让 grep 把整文件当二进制;其它控制字节渲染为空、两种拼写都搜不到; +事故源不挑字节值,「我这个不是 NUL」永远不是把门禁命中读成误报的理由。危害的论证在 `scripts/check-nul-bytes.mjs` 头部 —— 引用它,别重推。push 前跑它;改动哪怕只是提到控制 字符,就在门禁之外自扫(`grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' `)。 @@ -462,7 +464,6 @@ _Generated by [Claude Code](https://claude.ai/code)_ ← stripped _Generated by [Claude Code](https://claude.ai/code/session_)_ ← survives both paths ``` -以裸形式收尾的正文,之后每次编辑都会丢掉整个页脚;创建时的写入可能把裸形式静默改写 -成 +以裸形式收尾的正文,之后每次编辑都丢掉整个页脚;创建时的写入可能把裸形式静默改写成 session 形式 —— 那是平台行为,不是别的 agent 在编辑你的 PR,也不构成任何其它证据(基本 规则 2)。评论是另一条通路:裸形式在那里原样存活。 diff --git a/.claude/skills/pm-dispatch/references/platform-readings.md b/.claude/skills/pm-dispatch/references/platform-readings.md index 1939567e42..f6ee1ca812 100644 --- a/.claude/skills/pm-dispatch/references/platform-readings.md +++ b/.claude/skills/pm-dispatch/references/platform-readings.md @@ -19,10 +19,9 @@ - 「不在 `origin/main` 上」是二义读数(在队列里等 / 没入队,处置相反)—— 落地检查永远两个 读数:**队列成员资格 和 `origin/main`**,缺一不可;`origin/main` 那一读**按内容读**(grep 本 PR 该 产出的产物),⛔ 不按 PR head sha 的祖先性、也不按 `merged` 布尔:队列落的是**另一个提 - 交**,于是 `git merge-base --is-ancestor origin/main` 对一个已完全合入的 PR 答 NO(非浅检 - 出,与「读数陷阱」浅检出坑不同因),而同一 PR 的 `list_pull_requests` 在 `fields` 投影下实测回 - 过自相矛盾的一行:`merged: false` 与 `merged_at` 有值 + `state: closed` 并存 —— 只读布尔的一切 - 判据把已落地读成「没合」(2026-08-23 落地时刻实测)。 + 交**,`git merge-base --is-ancestor origin/main` 对已完全合入的 PR 答 NO(非浅检出),而同 + 一 PR 的 `list_pull_requests` 实测回过自相矛盾的行:`merged: false` 与 `merged_at` 有值并存 —— + 只读布尔的一切判据把已落地读成「没合」(2026-08-23 落地时刻实测)。 - **`mergeable_state` 惰性计算**:首次 GET 可能回 `unknown` —— 重读一次拿真值; `dirty` 即队列入 口否决(冲突对象是**当前** main);draft PR 恒回 `draft` (「draft+clean」组合不存在);本地试合并 随 fetch 老化,该字段不老化。只在入队决策点 `get` 一次:挂了 flip 定点(landing-operations)就到 @@ -30,9 +29,8 @@ - **状态核验用最小字段**(search/list + `fields`)或等事件,整对象 `get` 留给入队决策点;门禁放行 判据 = 承载门禁族 job 的 conclusion,聚合(`blocked`/`dirty`)只作阴性筛查再定位,放行按名定向读 单条 job,⛔ 不拉全表(按名定位失败才拉)。 -- **PR 转回 draft 同时掉 auto-merge 与队列成员资格,均不自动恢复**(转正后必须重新挂);反方向 - 同理:要真踢出队列只有转 draft —— `disable_pr_auto_merge` 单独调用**不解除队列成员资格**,PR - 照样落地。 +- **PR 转回 draft 同时掉 auto-merge 与队列成员资格,均不自动恢复**(转正后重新挂);要真踢出队 + 列只有转 draft —— `disable_pr_auto_merge` 单独调用**不解除队列成员资格**,PR 照样落地。 - **undraft 的可用路径只有 MCP 一条**:`update_pull_request` 传 `draft: false` 落地(2026-08-24 三张 PR 逐 张回读确认);**裸 GraphQL 会话内被拒**(回「only the pinned set of PR-review operations is served」,它建 议的「改用 REST」对 undraft **是错的**);**裸 REST `PATCH /pulls/{n}` 传 `draft: false` 回 200 而无操 @@ -41,20 +39,23 @@ = 无操作),但**该参数本身实测惰性** —— 传 SQUASH 后 REST 回读 `auto_merge.merge_method` 仍是 `merge`(2026-08-24 两张 PR、其中一张 disable→enable 复验同值)⇒ ⛔ 不为它翻转空转;**在本仓无 害**:合并由队列执行、方法归队列,本仓 `allow_merge_commit:false`,落地无一例外单亲 - squash。**回显两向不可靠**(实测:队列路径回显空而入队照发;显式传 SQUASH 回显 `MERGE`,落地 - 仍每 PR 一提交)⇒ ⛔ 不拿回显当任何方向的证据,权威信号见下条。 -- **配额枯竭时 `enable_pr_auto_merge` 回成功而挂载根本没发生**(实测:12:01Z 一次「成功」后 2.5 - 小时零动静 —— 同期别的 PR 正常合入,合并通路是好的;~14:33Z 配额恢复后同一调用 ~1 分 - 钟内落地)⇒ **验效果,不验回应**:挂载后按下条序列以队列分支 / timeline 入队事件确认,⛔ - 不拿成功报文收工。⚠️ 「回读 `auto_merge` 非空」**本接口给不了**:`pull_request_read` 与 - `fields` 枚举都无该成员(armed 与未 armed 读回逐字节相同:`open` / `draft:false` / - `mergeable_state: clean`),⇒ 可用效果读数只有队列分支、timeline 入队事件、最终落地三种。已 - 死假说:auto-merge **不会**在已绿 PR 上静默空转(恢复窗口里两个全绿 PR 挂上即合)。 -- enable 后的验证序列:① 先验队列分支(给条目 ~20–30s 建出);② 分支在 ⇒ 结束,⛔ 不翻转; - ③ 等待后仍缺席**且队列已见 churn**(更新的条目建出了分支而你的没有 —— 截断下单纯 - 缺席不充分;首挂静默不入队实测存在,churn 后翻转即愈)⇒ 翻转一次(`disable` → `enable`),仍 - 以 timeline 事件验证; ④ ⛔ enable 与它的队列验证之间永不插 `disable` ——「入队」webhook 可 - 能乱序迟到,armed 窗口里补的 disable 会撤掉已发生的真实入队。 + squash。**回显两向不可靠,空回显≠未挂上**(实测:队列路径回显空字段而入队照发、正常落 + 地;显式传 SQUASH 回显 `MERGE`,落地仍每 PR 一提交)⇒ ⛔ 不拿回显当任何方向的证据 —— + 「空字段 = 静默空转签名」的旧读法已被推翻,权威信号见下两条。 +- **配额枯竭时 `enable_pr_auto_merge` 回成功而挂载根本没发生**(实测:一次「成功」后 2.5 小时 + 零动静,同期别的 PR 正常合入;配额恢复后同一调用 ~1 分钟内落地)⇒ **验效果,不验回 + 应**:按下条序列以队列分支 / timeline 入队事件确认,⛔ 不拿成功报文收工。「回读 + `auto_merge` 非空」本接口给不了:`pull_request_read` 与 `fields` 枚举都无该成员(armed 与未 armed + 读回逐字节相同)⇒ 效果读数只有队列分支、timeline 入队事件、最终落地三种。已死假说: + auto-merge 不会在已绿 PR 上静默空转(恢复窗口里两个全绿 PR 挂上即合)。 +- enable 后的验证序列(2026-08-25 增补):① 先验队列分支 + `git ls-remote origin 'refs/heads/gh-readonly-queue/*'`(零配额,给条目 ~20–30s 建出;正命中即已入队, + 在 PR 读数发僵时仍决断 —— 实测人工合并后 PR 照答 open/merged:false 达 ~15 分钟,队列 ref + 早已证明入队);② 分支在 ⇒ 结束,⛔ 不翻转;③ 缺席 ⇒ 先 `update_pull_request_branch` 逼出暗 + 冲突(静默冲突不踢已挂 PR,只是永不入队 —— 实测一张挂 40+ 分钟,update-branch 才吐出 + merge conflict);④ 无冲突仍缺席才重挂一次,重试回 405 `Pull Request is in the merge queue` = 决断 + 阳性(先前那次其实已生效);⑤ ⛔ enable 与它的队列验证之间永不插 `disable` ——「入队」 + webhook 可能乱序迟到,armed 窗口里补的 disable 会撤掉已发生的真实入队。 - **队列踢出先认签名再决定重投**:已知 flaky 核对失败签名一致 ⇒ 原样重投;止血修复合入 后**同一签名再现就不再是那条 flaky**,是新问题必须重新诊断,⛔ 禁止条件反射式重投;第 三种签名:本 PR 名下**没有任何** `merge_group` run 且批次同伴的 run 全部 `success` = 队列重建的 @@ -69,73 +70,74 @@ 户**下,「所有 agent 共用一个身份」只在**席位内部**成立(故认领必须在评论里写 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 实测)。 + 实测(`/rate_limit` 前后差量,端点自身不计费):上限 5000/时;单个 dev 子代理 ~15 分钟烧 ~5658 + 点;一次 `list_issues`(34 张卡、perPage=100)= 107 点;耗尽时刻 GraphQL used 10461 而 core used 7 ⇒ + **最大消耗方是派出去的 dev 子代理,PM 巡检相比之下是噪声**(2026-08-22 实测)。 - **perPage 按预期 population 取,⛔ 不按习惯取 100**:上条计费规则(点数 ≈ 请求节点数/100)使 - perPage 成为唯一杠杆 —— 那次 107 点的读数里 34 张卡付的是 100 张的钱,一次只要 3 条的 - perPage=100 读法多付约 33 倍点数;只要 `totalCount` 的健康指标取 perPage=1,只要最近 N 张就取 - N(2026-08-23)。 + perPage 成为唯一杠杆 —— 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 - 复核)。 -- **REST 可用性是会话属性,⛔ 不是全局事实 —— 开轮探一次,按班存档**:直连受会话级授权 - 门钳制(会话起点快照),门关着回 403 `GitHub access is not enabled for this session`,⛔ 不是限流,重试 - 改不了它。探针 = 开轮那次 `/rate_limit` curl 兼任:403 ⇒ **本班无 REST**,下文一 - 切「改走 REST」的处方本班不成立;⛔ 不据他席读数推本席(2026-08-24 同窗口三席:两席 403、 - 一席 200)。**门关着时的降级梯**:① git 先行(见「零成本等价物」条);② MCP `list_issues` **单 - 标签**读全 + 本地求交(`labels` 是 OR,见下面 MCP 参数条);③ 等重置 —— 纯 MCP 会话撞上枯竭 - 池 = 重置前只剩 git 先行与 WebFetch 两行。 -- **默认读序 git → REST → MCP/GraphQL**(2026-08-23 策略翻转:REST 通道是**默认**读路径,⛔ 不再 - 是「降级退路」;⚠️ 整条以上条探针绿为前提,403 会话改按降级梯读)。list/查重/卡与 PR - 读/标签回读**默认走容器 curl 的 REST 通道** —— App installation token,core 15,000/时,与 GraphQL - 池**独立计**(实测同一天本席 GraphQL 池两次耗尽时 REST core 余 14,938;另一席 GraphQL 0 / core 4999 - 时,开卡、认领、标签读改写、评论整条派发环全在 REST 上跑完);GraphQL 池(5000/时)只留 - 给**没有 REST 对应物**的那几件:draft 翻转、auto-merge/入队挂载、语义 `/search/*`、Projects - field_values、`issue transfer`。逐操作通道归属(每条实调 ✓ 带日期)、写侧配方与队列路由三 + ≤100,官方指引:**变更类请求间停 ~1 秒**(mutation 按 5× 计)。双载体清标、批量重分诊这类 + 把写挤在同一秒的扫动,会在小时池仍绿时撞上分钟墙(官方文档 2026-08-23 复核)。 +- **REST 可用性是会话属性,⛔ 不是全局事实 —— 开轮探一次,按班存档;探针必须 + repo-scoped**:直连受会话级授权门钳制(会话起点快照),门关着 repo-scoped 路径**整类**回 403 + `GitHub access is not enabled for this session`(⛔ 不是限流,重试改不了它),而 `/rate_limit` 照答 200 + —— 它兼任不了通道探针;探针 = 一条真 repo-scoped 读。探针 403 后 `/rate_limit` 是**单调用 + 凭据判别**:15000/时 = 凭据活、被 repo-scoping 拒;60/时或 auth 错 = 无凭据(实测有席位 token + 是 14 字节占位串),两形同症不同治。**按班矩阵**:2026-08-25 两车道 5+ dev 席全被门拒(整类 + 403、读写同门 —— 加法标签 POST、评论 PATCH 同死 ⇒ 无评论编辑通道,更正=重发;`gh` 缺 + 席,MCP 与 git push 正常);2026-08-26 反例一席:os-dev 席门开,加法 POST 回 200、/rate_limit + 15000/时;顶层席同窗分裂(两 403、一 200,2026-08-24)⇒ **门态逐容器变**,⛔ 不据他席、他日读 + 数推本席,唯一安全读法即本行的 repo-scoped 探针。**门关着时的降级梯**:① git 先行(「零成 + 本等价物」);② 公开仓 payload 档(下方「公开仓」条);③ MCP:search 定向一击、列表**单标 + 签**读全 + 本地求交(`labels` 是 OR,见 MCP 参数条);④ 等重置。 +- **默认读序 git → payload → REST → MCP/GraphQL**(2026-08-23 策略翻转,2026-08-25 增补 payload 档; + ⚠️ REST 档以**本班 repo-scoped 探针绿**为前提 —— 前提就住本行,403 会话改按降级梯读)。 + list/查重/卡与 PR 读/标签回读默认走容器 curl 的 REST 通道 —— App installation token,core + 15,000/时,与 GraphQL 池**独立计**(实测 GraphQL 池两次耗尽时 core 余 14,938;另一席 GraphQL 0 / + core 4999 时,开卡、认领、标签读改写、评论整条派发环全在 REST 上跑完);GraphQL 池(5000/时) + 只留给**没有 REST 对应物**的那几件:draft 翻转、auto-merge/入队挂载、语义 `/search/*`、Projects + field_values、`issue transfer`。逐操作通道归属(✓ 按席位类别限定)、写侧配方与队列路由三 读法见 `references/rest-channel.md`,⛔ 不在本表复述。 - **MCP list/search 家族整个走 GraphQL 稀缺池**(反复撞上的限流墙就是它;`issue_write` 连查找半边 都吃);会话级授权门与降级梯见上面第一条。配额红时认领类动作排队,评论(REST 桶)先行把 结论发出去。 -- **`gh` CLI 的动词按传输分两桶,池枯竭时只死一半**(2026-08-24 同一分钟实测:GraphQL remaining 0 / - REST core remaining 4987):porcelain 家族(`gh issue view` / `gh pr list`)与 `gh pr create` 走 GraphQL 当场回 - `API rate limit already exceeded`(`GH_DEBUG=api` 回显 `POST /graphql`),同一批事实改走 `gh api` 的 REST 路 - 径全部照常返回(含开 draft PR)⇒ 上条复核链在 `gh` 上同样成立。⚠️ 容器里没有 `gh`,本条 - 只对本机席位适用。 +- **`gh` CLI 的动词按传输分两桶,池枯竭时只死一半**(2026-08-24 同分钟实测:GraphQL 0 / core + 4987):porcelain 家族(`gh issue view` / `gh pr list`)与 `gh pr create` 走 GraphQL 当场回限流,同一批事实 + 改走 `gh api` 的 REST 路径照常返回(含开 draft PR)。⚠️ 容器无 `gh`,本条只对本机席位适用。 - **红窗调度**:`until remaining > 阈值` 的守候**只给上面那几件 GraphQL-only 的**(逐件判据与官方 文档核对日期住 `references/rest-channel.md`),⛔ 其余一切不为配额空等;走队列的仓落地必经 auto-merge ⇒ 红窗里**无退路**,直合仓有(合并本身有 REST 端点)。 -- **`issue transfer` 因配额或权限拿不到 ⇒ 当轮改走多仓协调条款已载明 - 的「在目的仓重建」配方**(出处头 + 裸 `#N` 改全名 + 关源单为 moved):该配方纯 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**;没有任何参数能关掉 `body`:要么接受整表 107 点,要么换更窄接口(点数未实 - 测)。⚠️ 站得住的证据是直接观察 `body` 在,不是前后体积对比(两次调用相隔数小 - 时、population 已变)。 + `list_issues` 的 `body`**(2026-08-22 实测:返回字段仍含 `body`,首条 3258 字符仍超单次工具输出上 + 限被落盘)—— 工具描述的反向暗示为假,**永不当省额度手段写进任何 skill**;没有参数能 + 关掉 `body`:要么接受整表 107 点,要么换窄接口(点数未测)。⚠️ 站得住的证据是直接观察 + `body` 在,不是前后体积对比(两次调用相隔数小时、population 已变)。 - **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/**'`。开轮先读配额(`curl` 带 Bearer `$GH_TOKEN` - 打 `/rate_limit`,免费,**并兼任上面的 REST 探针**;容器内**没有** `gh`,见「读数陷阱」),graphql + 打 `/rate_limit`,免费;⚠️ 它答不了通道在不在,repo-scoped 探针另跑;容器内**没有** `gh`),graphql remaining < 1000 ⇒ 本轮按默认读序走 git + REST;**派 dev 之前同样先读一次**,额度不足先等重置 再派 —— 中途撞限流的 dev **完不成强制查重**,只能把发现交回 PM 代为归档,⛔ 不盲目开 卡。打满时:待执行写**排成有序清单挂进巡逻词**(不靠记忆),恢复窗口按序连清;重试对齐 整点(REST core 整点重置)优于指数退避,⛔ 绝不忙轮询;search 与 core 独立计,一侧打满另一侧 可作退路;REST core 共享身份下同样会打满;文档载明、未实测:条件请求答 `304` 不计 core 池。 -- **公开仓降级读法: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` 做不到的,`list` + 对照组降为回 - 退。 +- **公开仓零配额读法两档,payload 档优先**:单卡页 `/issues/N` 内嵌 JSON 载**原始 body + 全评 + 论**,精确、零配额 —— 取含 `bodyHTML` 的 `script[type="application/json"]` 块,读 + `payload.preloadedQueries[0].result.data.repository.issue.body` 与 `frontTimelineItems`/`backTimelineItems`; + 实测一整个 M 档 dev 运行只花 3 次 MCP 调用(同席前轮 MCP-first 烧 10416/5000,~25×)。边界:⛔ + 只因仓库公开成立;⛔ 覆盖单卡读、**不覆盖 issue search**(搜索页无 SSR 结果);⛔ 永不拿渲染 + 列表定规模(静默只显一页,实测 12 vs 权威 147)。渲染层 WebFetch 仍在(~15 分缓存、有损)。 +- **查重先 `search_issues`**(2026-08-18 实测:单次调用按 issue body 内文本命中且 `total_count` 精确; + 「search 对本会话不可用」的继承说法实测为**假** —— 继承说法不是读数,复述必带实测 + 日期):body 文本匹配是 repo-scoped `list` 做不到的,`list` + 对照组降为回退。 - **`search_issues` 可整会话静默归零 —— 控制词一并归零**(2026-08-23 实测:某会话对**每个**查 询回 `total_count: 0`,含已知必中的控制词;同时刻另一会话同工具正常 ⇒ 故障是**会话 级**,不是工具/平台级)。⛔ **空查重结果不是读数,除非本会话内一个已知必中的控制词答 @@ -145,28 +147,25 @@ 按上面的探针与降级梯 —— 探针绿走 **REST 列表端点** `GET /repos/{o}/{r}/issues?state=open&labels=a,b&per_page=N`(core 桶、结果**完整**、`labels` 真 AND,perPage 按右尺寸规则;`GET /search/issues` **不是**退路 —— 出口代理按设计只放 repo-scoped 路径),403 - 走梯子第②档(单标签一次读全 + 本地求交 —— ⛔ **不是翻页手扫**:极易半途而废,实测 - 226 张 open 只扫了 100 张,**不完整枚举比零结果更危险**)。⛔ 已推翻的候选机理,别再 - 追:「查询串带 `repo:`/`is:open` 把语义 search 打成零」—— 两次实测反证(带 `repo:` 的控制词 - 回 5;另一席两腿对照 `repo:… is:open …` 回 1 精确命中而裸词回 13 语义扩散),限定符在那 - 儿**收窄**结果而非破坏。归零机理仍未定(候选:scope 过滤层静默清空 / search 桶 403 被 MCP - 层吞成空结果),要定它必须在复现会话里抓原始响应。 + 走降级梯 MCP 档(单标签一次读全 + 本地求交 —— ⛔ **不是翻页手扫**:极易半途而废,实测 + 226 张 open 只扫了 100 张,**不完整枚举比零结果更危险**)。⛔ 已推翻的候选机理别再追: + 「限定符打零」两次实测反证(带 `repo:` 的控制词回 5;`is:open` 对照回 1 精确命中),限定符 + 只**收窄**不破坏;归零机理仍未定(scope 过滤层静默清空 / search 桶 403 被吞成空结果),要定 + 它须在复现会话里抓原始响应。 - **`search_issues` 不可靠地返回分钟级新卡**:同轮发现的东西查重,搜索之外必须按创建时间列 近期 issue(`list_issues` + `orderBy: CREATED_AT`)—— 实测一张 ~7 分钟大的同实例卡被关键词与语 - 义搜索双双漏掉,靠按日期列表才逮到;边界:两次观察、索引延迟未实测,断言只 - 到「search 可能漏掉分钟级 issue」,更硬的窗口要另测(2026-08-20 实测)。 + 义搜索双双漏掉,靠按日期列表才逮到;边界:两次观察,断言只到「search 可能漏掉分钟级 + issue」(2026-08-20 实测)。 - **会话中途轮换凭据把 GitHub MCP 服务器杀到不可恢复**:此后一切 `mcp__github__*` 回 - `Streamable HTTP error: invalid session`(含几分钟前还好的工具),只有新会话重绑 —— 轮换前提醒 - 维护者:在飞席位丢的是整条 GitHub 通道;配额池按身份计,换身份即清零燃烧,共享身份结构 - 不变。 + `Streamable HTTP error: invalid session`,只有新会话重绑 —— 轮换前提醒维护者:在飞席位丢整条 + GitHub 通道;配额池按身份计,换身份即清零燃烧,共享身份结构不变。 - **组织侧授权变更后仓库访问逐步传播**(同一端点数分钟内 403→200);403 错误对象存盘仍是 合法 JSON,期待列表的脚本会静默报假「0 issues」—— 零命中纪律覆盖 list 读:空车道先对仓 库 `open_issues_count` 反查再信。 - **MCP 参数两陷阱**:`list_issues` 多标签过滤是 **OR(并集)**不是 AND —— 混入别车道同状态卡 - 与本车道全状态卡,结果良构、失效全静默;**判据 = 结果比任一输入都宽**(三席各自独立实 - 测 OR 侧:`domain:ui` ∩ `pm:queue` 回 154 张、含 - `domain:spec`/`domain:devx`/`pm:blocked`;`[domain:skills, finding]` 回别车道的 finding 卡;`domain:ui` ∩ - `pm:dispatched` 回 135 张而车道自身只有 132 张)。正确读法 = **整车道单标签一次读全 + 本地 + 与本车道全状态卡,结果良构、失效全静默;**判据 = 结果比任一输入都宽**(三席独立实测 + OR 侧:`domain:ui` ∩ `pm:queue` 回 154 张、含 `domain:spec`/`pm:blocked`;`domain:ui` ∩ `pm:dispatched` + 回 135 张而车道自身只有 132 张)。正确读法 = **整车道单标签一次读全 + 本地 对 labels 求交**,或改走 REST —— **两个通道的 `labels` 语义相反**:REST 列表端点的 `labels=a,b` 是**真 AND**(交集),MCP 的 `labels` 数组是 **OR** ⇒ 要交集按上面的探针与降级梯选档,⛔ 不无 条件「改走 REST」。`issue_write` 的 `labels` 是**整组替换**不是追加 —— 同一动作内重读现 @@ -193,6 +192,9 @@ ## 读数陷阱 +- **被拦的下载不是缺席证明**:出口策略 403(如 `cdn.playwright.dev`)只说「取不来」,不说「没 + 有」—— 先找产物再下结论(实测:Chromium 预装、`PLAYWRIGHT_BROWSERS_PATH` 已设,「装不了 = + 没有浏览器」让两条只有浏览器能验的判据白报 unverified);与零命中控制探针同族。 - **读数五坑**:`cd X && cmd` 会短路(路径不存在时命令在当前仓继续执行,产出假读数)—— 跨 仓一律 `git -C `; `git grep -c | wc -l` 数文件数不是命中数;裸名 grep 被幸存家族当子 串命中 —— 退役核验带引号精确名,更硬判据是查声明 @@ -263,9 +265,8 @@ 在同一处)⇒ ⛔ **字面文本标记只保住标记、保不住正文** —— 标记扫描照报成功,人看着 像正常收尾,只有真去解析才发现;引信是普通 TS 形状(`Promise`、`Array` 一类),不是 奇异语法;安全过程 = 标识符一律进反引号、尖括号写实体或用词拼出(回读纪律见上面的 - 作者规则条)。⚠️ 边界:姊妹仓一次五形受控探针测到的是**选择性删除**(所有位置的 - tag/注释被移除,不能开标签的尖括号实体化存活)而非截断到尾 —— 两种形状都实测 - 过,**写侧一律按最坏的截断防护**。 + 作者规则条)。⚠️ 边界:姊妹仓一次受控探针测到的是**选择性删除**而非截断到尾 —— + 两种形状都实测过,**写侧一律按最坏的截断防护**。 - **并行 spec PR 同动 pin 计数断言**(被踢不是事故,按 os-regen 序再解一轮):解冲突两侧收据都 保留、按合并顺序堆叠,新计数**从合并后源码重数**(操作数是文件本身不是历史),⛔ 不从 两侧收据做算术;双方占同一编号是常态(各取当时 max+1),重编号后进侧。 @@ -284,16 +285,15 @@ 成已被认领**(无红信号、只增不减,认领前的在飞预检恰恰依赖它)。判据两 读:`git rev-list --count origin/main..origin/` 为 0,且该分支名下无 open PR。边界:一容器一会话三 次,成因未诊断(代理 / 服务端钩子 / 分支保护未分辨),存量未普查。 -- **会话从上下文检测不到自己的静默降档**(2026-08-20 实测:一次分诊 fire 两级静默降档 - Fable→Opus 5→Opus 4.8,降档横幅只在 UI 侧渲染、会话上下文零信号,子轮开场仍自 - 述「跑在契约复审档」)——服役模型的权威读数是 `get_session`(claude-code-remote MCP,无参)的 - `external_metadata.last_served_model`(记录最近一轮实际服役者,降档链中途照真);`session_context.model` - 是**配置**档不是服役档,⛔ 不作保险丝输入。 +- **会话从上下文检测不到自己的静默降档**(2026-08-20 实测:一次分诊 fire 两级静默降档,横幅 + 只在 UI 侧渲染、上下文零信号,子轮开场自述「跑在契约复审档」)——服役档的权威读数 + 是 `get_session`(claude-code-remote MCP,无参)的 `external_metadata.last_served_model`(最近一轮实 + 际服役者,降档链中途照真);`session_context.model` 是配置档不是服役档,⛔ 不作保险丝输入。 - **上条那个读数按宿主分叉,用前先确认工具在**:有的 ccd 宿主装 session-mgmt MCP,其 - `get_session`/`list_sessions` **按契约排除当前会话** ⇒ 保险丝无输入(2026-08-24);另一台 ccd/web 宿 - 主上它省 `session_id` 正常回两键(2026-08-25)⇒ ⛔ 不据一台宿主推全体。**无它时的合法替 - 代**:grep 本会话 transcript JSONL 最新记录里 harness 写的 `"model":` —— 宿主逐请求写入、非模 - 型自述,量的是同一件事;⚠️ **只对本席自己的会话有效**,用时在卡上申报。 + `get_session`/`list_sessions` **按契约排除当前会话** ⇒ 保险丝无输入(2026-08-24);另一台宿主上它 + 省 `session_id` 正常回两键(2026-08-25)⇒ ⛔ 不据一台宿主推全体。**无它时的合法替代**:grep + 本会话 transcript 最新记录里 harness 写的 `"model":`(逐请求写入、非模型自述;⚠️ 只对本 + 席自己的会话有效,用时在卡上申报)。 ## 闭合关键词解析(PR 正文写侧) diff --git a/.claude/skills/pm-dispatch/references/rest-channel.md b/.claude/skills/pm-dispatch/references/rest-channel.md index 948a339858..87bd95b664 100644 --- a/.claude/skills/pm-dispatch/references/rest-channel.md +++ b/.claude/skills/pm-dispatch/references/rest-channel.md @@ -5,9 +5,13 @@ 每行自含边界与日期。 **通道边界**:容器 curl = App installation token,REST core 15,000/时、与 GraphQL 池独立计;出口代理按设 -计只放 **repo-scoped 路径**(`/repos/{o}/{r}/...`)加 `/rate_limit`,org 级端点未实测。下文每条 ✓ = -**2026-08-23 在真实会话里实调通过**(未另标日期者同此日);⛔ 未带 ✓ 的形状不当已验证事实 -复述。 +计只放 **repo-scoped 路径**(`/repos/{o}/{r}/...`)加 `/rate_limit`,org 级端点未实测。⚠️ **✓ 按席位 +类别限定,不是全局事实**:下文每条 ✓ = **2026-08-23 在一个会话门开着的席位实调通过**(未另 +标日期者同此日);2026-08-25 实测 os-dev 子代理席与部分顶层席被会话门整类拒 —— repo-scoped +读**写**全 403 `GitHub access is not enabled…`、`gh` 缺席,MCP 与 git push 正常 —— 那类席位本表读 +写两侧**整表不可达**:先跑一条 repo-scoped 探针再选通道,403 后 `/rate_limit` 单调用判凭据形 +态(15000/时 = 凭据活、被 repo-scoping 拒;60/时或 auth 错 = 无凭据);按班矩阵与降级梯住 +`platform-readings.md`。⛔ 未带 ✓ 的形状不当已验证事实复述。 ## 读侧 —— 全部可迁移(MCP list/search 家族才是 GraphQL 燃烧源) @@ -29,7 +33,8 @@ - ✓ 评论:`POST .../issues/{n}/comments`;改评论 `PATCH .../issues/comments/{id}`。 - ✓ 标签:**加法** `POST .../issues/{n}/labels` + **定向删** `DELETE .../issues/{n}/labels/{name}` —— 比 MCP `issue_write` 的整组替换安全:加法写剥不掉并发席位刚挂的标签(整组替换按隔轮旧读数 - 回写会静默剥标,纪律住 `platform-readings.md`)。 + 回写会静默剥标,纪律住 `platform-readings.md`)。会话门关着的席位无此端点(2026-08-25 多席实 + 测 403)—— 回退 = MCP 读现值→并集→整组写→**必做读回**,读回是回退安全的全部理由。 - ✓ 建卡带标签 `POST .../issues` · 改正文/状态 `PATCH .../issues/{n}` · 认领 `POST .../issues/{n}/assignees`。 - ✓ 请求复审 `POST .../pulls/{n}/requested_reviewers` · 开 PR `POST .../pulls`(带 `draft=true`;GraphQL 池为 0 @@ -38,18 +43,17 @@ ## 不可迁移 —— 只有这几件,围着它们排计划 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 恢复,或人工点一下。 + 判据 = REST update-a-pull-request 只收 `title`/`body`/`state`/`base`/`maintainer_can_modify`,**无 `draft`** + (2 与 5 同批:2026-08-24 核对官方文档,未逐个实调)。断粮出路:等 MCP 恢复,或人工点一下。 2. **auto-merge / 入队挂载**:GraphQL mutation(MCP `enable_pr_auto_merge`)。走合并队列的仓落地必经它 ⇒ 配额红窗**无退路**;直合仓有退路(合并本身有 REST 端点 `PUT .../pulls/{n}/merge`)。 3. **语义搜索**:`/search/*` 被出口代理按设计拒绝。退路 = REST 列表端点 + 本地 grep(既有纪 - 律)。 + 律);REST 也被会话门关掉的席位 = 一次定向 MCP `search_issues`,⛔ 不宽表扫。 4. **Projects field_values**:GraphQL-only —— 舰队并不需要它;MCP 服务器端**无条件**抓它才是漏 点,不是需求。 -5. **`issue transfer`**(2026-08-24 官方 REST 文档核对补入;原表只列前四件):issues 端点表里没有 - transfer 路由 ⇒ 同为 GraphQL-only。拿不到时当轮改走「在目的仓重建」配方(纯 REST、配额免 - 疫),配方住 `platform-readings.md`。 +5. **`issue transfer`**(2026-08-24 官方文档核对补入):issues 端点表无 transfer 路由 ⇒ 同为 + GraphQL-only。拿不到时当轮改走「在目的仓重建」配方 —— 纯 REST、配额免疫,配方住 + `platform-readings.md`。 `until remaining > 阈值` 的守候只留给这几件,⛔ 其余一切不为配额空等。 @@ -60,21 +64,17 @@ ## 队列路由的读法(2026-08-24 实测) -- **`merged_by` 是入队者,⛔ 不是绕队证据**:GitHub 把队列合并归属给**入队的那个账户**,该字段 - 对「队列合并 vs 直接合并」零分辨力。一周内三席各自把人形 `merged_by` 读 - 成「我们绕过了队列」,实测三次全为假 —— 都是队列合并。 +- **`merged_by` 是入队者,⛔ 不是绕队证据**:队列合并归属给**入队的那个账户**,对「队列 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 集本身、或看队列合并是否真在等检查。⛔ 别处写下的计数值一律先复测 - 再用。 + 上的 `added_to_merge_queue` timeline 事件;③ 对已入队 PR 调 update-branch 回「已入队分支不能更 + 新,要改先出队」。①② 拼写与边界是 `platform-readings.md` 队列段既有行,本条只归拢判据。 +- ⚠️ **计数不是机理读数(被当天推翻的推断的墓碑)**:「`GET .../actions/runs?event=merge_group` + 计数 0 ⇒ required 集为空」提出当天即被自身推翻 —— 同一姊妹仓 2026-08-24 首现 + 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 集,否则