diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 4bdfd5b493..4173d8b960 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -41,12 +41,12 @@ model: opus 按 issue 隔离**:在 scratchpad 目录下建 `issue-/` 子目录,所有临时文件写进去——同批 agents 共用一个 scratchpad 目录,自然命名(`pr-body.md`)会被彼此的成功回执静默覆盖;靠结构隔 离,不靠记性。 -2. **issue 已由 PM 认领**(大家共享同一 GitHub 身份)。不要动 assignee;若发现与他人在途 - 工作重复,停下报 `blocked`。**PR 上不是你设置的状态属于另一个 actor——去问,永不去 - 「纠正」**:共享身份让所有人的写入都像你写的;被改写的 body 只是关于 body 的证据,不证 - 明别的;回退他人的操作——尤其是 ready 翻转——永远轮不到你(转回 draft 实测两向: - 掉 auto-merge 与队列成员资格,或踢不掉、照样合入 —— 没有一向是好的)。把意外写进 - `summary`,交给 PM 裁决。 +2. **assignee 归 PM**:派发原子对已把它设好(共享身份下该字段答不了「谁」,你的身份位是认 + 领评论里的分支);仓 CLAUDE.md 的 claim-first 已由 PM 的认领满足 —— 你恒不写 assignee,到手 + 时它为空也一样(那是 PM 侧半状态,报进 `summary`,照常开工)。发现与他人在途工作重复,停 + 下报 `blocked`。**PR 上不是你设置的状态属于另一个 actor——去问,永不去「纠正」**:共享 + 身份让所有人的写入都像你写的;被改写的 body 只是关于 body 的证据,不证明别的;回退他人 + 的操作——尤其 ready 翻转(转 draft 两向实测都坏)——永不轮到你。把意外写进 `summary`。 3. **范围 = 这张 issue,别无其它。** 顺路撞见的无关缺陷立成新的**无 assignee** issue,列进 `out_of_scope_findings` —— 永不在本 PR 里修。立单纪律:**先搜再立**(关键词 + 文件路径扫 open issues;并行 dev 看不见彼此同一小时立的卡,这一搜只能靠你)—— 通道**先探后选**: @@ -332,10 +332,10 @@ dispatch prompt 只携带每单增量(裁决引文、裁决 / PM-机制假设分 签是真实机制,打标签是你的步骤、不是 CI 的,PR 一建立就打。写入首选**加法端点**(REST `POST .../issues//labels`——不碰已有标签、无读写窗口),⚠️ 但它只在会话门开着的席位可 达(2026-08-25 实测:容器 dev 席整类 repo-scoped REST 全 403);被拒 ⇒ 走实测过的回退:MCP 读 - 现值→并集→整组写→**必做读回**,并申报换道——整组写会剥并发席位刚挂的标签,读回 - 是回退安全的全部理由。加法写同样必要不充分:实测 size-labeler 的整组 PUT 曾在 ~1 秒内抹 - 掉一次正确的加法写——收尾一律读回、把清单引进报告;标签消失读作被抹,**重新加 - 上**,不是你的错。关此步骤的是读回,不是写入;⚠️ 读回验的是「写落了」,验不出「有门 + 现值→并集→整组写→**必做对比式读回**(diff 现集对 union(读集, 目标) —— union 有而回读 + 缺 = 被你的整组写剥掉的并发标签,重挂并写进报告),并申报换道。加法写同样必要不充分: + 实测 size-labeler 的整组 PUT 曾在 ~1 秒内抹掉一次正确的加法写——收尾一律读回、把清单 + 引进报告;标签消失读作被抹,**重新加上**,不是你的错。关此步骤的是读回,不是写入;⚠️ 读回验的是「写落了」,验不出「有门 在读」——幻影门标签读回同样成功。**objectui:同名标签对象在(历史误挂铸出的),但零 workflow/脚本读它、豁免不了任何东西,pin 测试钉着这条**——那边的声明方式是空 frontmatter 的 changeset,门禁自己的判定行是权威;⛔ 永不在那边施加该标签(挂上去只是给 PR 挂一个假 @@ -457,14 +457,14 @@ skill 文件)。原始 NUL 让 grep 把整文件当二进制;其它控制字节 字符,就在门禁之外自扫(`grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' `)。 GitHub 正文 sanitizer 是同一纪律的另一半 —— 形状、触发条件与作者纪律都归 AGENTS.md 的 -「GitHub mutates body BYTES」条款(一条规则一个家;此处不再复制窄版)。署名页脚必须用 -**session-URL** 形式: +「GitHub mutates body BYTES」条款(一条规则一个家;此处不再复制窄版)。署名页脚两形: ```text -_Generated by [Claude Code](https://claude.ai/code)_ ← stripped on every edit -_Generated by [Claude Code](https://claude.ai/code/session_)_ ← survives both paths +_Generated by [Claude Code](https://claude.ai/code)_ ← bare:评论用;PR 正文编辑后的落形 +_Generated by [Claude Code](https://claude.ai/code/session_)_ ← session-URL:创建 PR 正文用 ``` -以裸形式收尾的正文,之后每次编辑都丢掉整个页脚;创建时的写入可能把裸形式静默改写成 -session 形式 —— 那是平台行为,不是别的 agent 在编辑你的 PR,也不构成任何其它证据(基本 -规则 2)。评论是另一条通路:裸形式在那里原样存活。 +实测(2026-08-26,三写逐写回读):create 保 session 形,并把裸形静默改写成它(平台行为,基本 +规则 2);**每次 PATCH 编辑把 session 形降回裸形,裸形其后原样存活** ⇒ session 页脚只按 +create-only 对待:归属要跨正文编辑存活,把 session URL 写进正文散文或评论作耐久副本,⛔ 不 +循环重贴页脚。评论是另一条通路:裸形在那里原样存活。 diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index 33af0b111c..cdafa97fbc 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -136,12 +136,12 @@ fire 一轮烟测,判据取**GitHub 上的产出**;③ 每 fire 一轮:读座位 闭/作废,评论带出处三件(谁的指令、原话、在哪说的)** —— 无出处的关闭与误操作在证 据上不可区分,会被兄弟席当误扫重开;同理适用于摘标签、回收认领等不可反推理由的动 作。 -- **写后回读按风险定向;标签写恒为硬步骤 read-modify-write + 回读**(维护者 2026-08-18 裁定):① - 先取现集 → ② 只增删目标标签 → ③ 写合并集 → ④ 写后回读核验 —— 整组 PUT 写的是 - 本席快照,并发席落在你读与写之间的标签被静默剥掉,受害者常是你没打算碰的那枚;**承 - 载闸门语义的标签**(如 `needs:contract-review`)挂与清两向同此四步,闸门被剥不是红灯是放 - 行,与「从未挂过」在证据上不可区分,回读是唯一察觉手段;多席可写面恒读回(防竞态覆 - 盖,不只防 sanitizer);「API 返回 200」≠「落地内容正确」。 +- **写后回读按风险定向;标签写恒为硬步骤 read-modify-write + 对比式回读**(维护者 2026-08-18 + 裁定):① 取现集 → ② 只增删目标标签 → ③ 写合并集 → ④ 回读并 diff 对 union(现集, + 目标增删) —— 读与写之间落地的并发标签被你的整组 PUT 静默剥掉,常剥你没打算碰的那 + 枚;union 有而回读缺 = 被剥,重挂并报告(对比只察觉、不预防);**承载闸门语义的标签**(如 + `needs:contract-review`)挂与清两向同此四步,闸门被剥不是红灯是放行,与「从未挂过」在证据 + 上不可区分;多席可写面恒读回(防竞态覆盖,不只防 sanitizer);「API 200」≠「落地正确」。 - **PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符**,改写成「后跟显式路径」的说法 —— 被 sanitizer 吃掉后剩下的裸命令读起来完整而其实不是(实测:一条派发令就 在「永不用裸形式」之前展示了它要禁的那条裸形式);backtick、围栏块与标题同吃,都不是 @@ -543,8 +543,8 @@ PR 前合一次 main;③ 兄弟卡落地后再合一次;④ 冲突交合并队 ### 认领(先认领后动工) 同账号内的多会话共享 GitHub 身份,assignee 只回答「有 agent 认领了」,回答不了「哪个」—— -认领评论承载身份(`mode:subagent` 派发下其身份位是分支,见规则 2);跨账号只需一条:assignee -不是你 ⇒ 永不碰。选中的单,派发前按序执行**一个原子对**: +认领评论承载身份;**assignee 字段归 PM**:原子对 step 1 设,dev 席恒不写它(os-dev 规则 2 同 +句);跨账号只需一条:assignee 不是你 ⇒ 永不碰。选中的单,派发前按序执行**一个原子对**: 1. **Assign @me + 状态标签对调**(`pm:dispatched` 与摘 `pm:queue` 同一次标签写入 ——只摘不换留 下的在飞卡会被分诊析取每轮重付全评论读);step 1 之后获得 assignee 的直接弃出本批。 @@ -583,10 +583,11 @@ epic 领 数(复现命令、依赖路径、已确认的坑、在飞同文件PR、区域申报、串行约束),赢家不必重 扫。 -dev 侧推分支要早 —— 远程分支是在飞工作最硬的证据。**死认领回收(stale-claim reclaim)**:认 -领 >~24h、承诺分支 -不存在、无 PR ⇒ 死认领 —— 评论询问,静默一窗后摘 assignee(注明原因)回队;有带提交活分 -支的认领永不回收。 +dev 侧早推分支 —— 远程分支是在飞工作最硬的证据。**死认领回收(stale-claim reclaim)**:认领 +>~24h、承诺分支不存在、无 PR ⇒ 死认领 —— **回收前先救工作树**:派发 worktree 的未提交改 +动先 WIP commit 到派发分支并 push,sha 记进回收评论、标 INCOMPLETE AND UNREVIEWED(续派者 diff +它,⛔ 不无审续建;死在编辑中途的 dev 没来得及自己 WIP 推,这一步是最后防线)—— 再评论 +询问,静默一窗后摘 assignee(注明原因)回队;有带提交活分支的认领永不回收。 ### 派发 @@ -984,9 +985,8 @@ grep `),复升级时逐条**跑**一遍,零命中/变形的就地改写 `premise_still_valid: false` + `pr: null` 是合法终报 —— 当再分诊输入复核,永不当失败派发。 `status: needs_decision` 时 `open_questions` 必须非空。`out_of_scope_findings` 应已由 dev 立成无 assignee -的卡 -(查重先行、归挂判据、`finding` 标注,立在修复落地仓并带回链);PM 核验它们存在,**并把同轮 -并行报告互相 +的卡(查重先行、归挂判据、标签按种分 —— 观察类打 `finding`、具体缺陷不打标留分诊,与 +os-dev 规则 3 同句;立在修复落地仓并带回链);PM 核验它们存在,**并把同轮并行报告互相 对读** —— 两个 dev 同一小时审相邻代码会立出孪生卡,只有 PM 同时看得见两份报告。 ## 机械守卫索引(原则在此,细节以脚本头为权威) diff --git a/AGENTS.md b/AGENTS.md index 77193ad844..66ee34e6f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -476,11 +476,11 @@ _Generated by [Claude Code](https://claude.ai/code)_ ← bare: u _Generated by [Claude Code](https://claude.ai/code/session_)_ ← session-URL: use in PR BODIES ``` -**PR body:** bare loses the **whole** footer, `---` included, on every `update_pull_request` edit; -session-URL survives both write paths, and `create_pull_request` *rewrites* bare into it — how a body -comes back in a shape nobody typed. An edit also **APPENDS a fresh platform bare footer** under yours, -and the next edit strips that one and appends another — the floor is your session-URL footer plus one -platform bare, so ⛔ don't loop trying to normalize it. **Issue comment:** the platform APPENDS a bare +**PR body:** `create_pull_request` keeps the session-URL form and *rewrites* bare into it — how a body +comes back in a shape nobody typed. On EDIT the promise inverts (measured 2026-08-26, three REST +writes, per-write read-back): every `update_pull_request`/PATCH edit normalises the session-URL footer +DOWN to bare, and that bare survives later edits verbatim. Treat the session id as create-only (⛔ no +re-append loop); durable attribution lives in body prose or a comment. **Issue comment:** the platform APPENDS a bare footer to every one (measured 2026-08-20 — MCP `add_issue_comment` and direct REST agree: per-surface, not per-tool). It is idempotent if yours is already bare; if yours is the session-URL form *it survives verbatim* and a bare one lands under it, leaving two. ⛔ A tail bare footer on a comment is the