Skip to content

docs(pm-dispatch): 增补 2026-08-03 全天运行沉淀的八条 PM 操作经验 (#4882) - #4885

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4882-pm-dispatch-lessons
Aug 3, 2026
Merged

docs(pm-dispatch): 增补 2026-08-03 全天运行沉淀的八条 PM 操作经验 (#4882)#4885
os-zhuang merged 1 commit into
mainfrom
claude/issue-4882-pm-dispatch-lessons

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #4882

把 2026-08-03 主 backlog PM 会话跑完 12 单合并 + 8 单新立后沉淀的八条操作经验写进 .claude/skills/pm-dispatch/SKILL.md,让下一个 PM 会话不用重新踩。纯插入,不重写既有章节:git diff --stat134 insertions(+),既有行删除数为 0。

落点与内容

step 2 分诊 — 新增「无生产者」形状的识别规则:声明了、消费端有读取、gate 全绿,却没有任何生产者写值;类型系统与 lint 只验消费方,所以这类洞在全绿下长期存活。当日五次命中(#4704 / #4837 / #4839 / #4862 / #4867)。遇到 declared ≠ enforced 类 issue,分诊时先问「生产者在哪」,答案通常就是根因,并会在派发前改掉 issue 的作用域与 domain:* 标签。

step 3 选批 — 同文件 issue 严格跨轮串行,并明确推迟 ≠ 搁置:#4820 的 dev 查出 #4821 正文建议的 JSON.stringify 键法会改掉类型强制语义、引入静默新缺陷,这条警告与 Blocked-by:#4820 复核完成的同一轮就钉到了 #4821 上 —— 被推迟的 issue 在每次 sweep(包括别的 PM 的)里看起来都是可派发的,你知道的坑不写上去就等于不存在。

step 5 派发 — 两条:

  1. 「main 当天已动过同一文件」要写进派发词(现有 Stale-premise check 防的是 issue 过期,同日变体是另一回事):phoneOtp.cooldownSeconds 配成大于 1 小时会被静默截断 —— OtpSendGuard 的历史 TTL 硬编码 1 小时 #4808 派发前 fix(plugin-auth): 每号码 OTP 发送预算改用惰性解析的共享计数存储 —— 多节点下不再按节点数倍增 (#4790) #4806 刚改过同一 guard,compareTo drops every measure-scoped filter, so <measure>__compare silently reports a different measure than the column beside it #4820 派发前 fix(service-analytics): 按聚合种类填充「查询从未报告过的分组」,并补上 compareTo 这一道接缝 (#4708) #4822 刚改过同一文件,两单都因派发词点名而避开返工。
  2. 新增 #### Handing off an interrupted dev(worktree 接手协议):/compact 与宿主中断会把运行中的子 agent 连同挂起的工具调用一并杀死(@objectstack/verify 的 bootStack 与 qa dogfood 同样用裸 import 解析 cloud 私有包 —— 与 cloud#1013 同一缺陷类 #4700 / hook 的 condition 求不出值时:全局 fail loud —— 抛错并中断该次操作(方案 B 已拍板;Blocked-by #4770) #4775 停在同一秒),agent 不可恢复但 worktree/分支/提交完好。接手 = 派新 agent + 明确「worktree 已存在,⛔ 不要新建」+ 先通读既有改动再逐块决定取舍 + 补跑从未跑过的验证 + 认领与分支不变。两单按此接手后均一次通过复核。

step 7 复核 — 两条:

  1. dev 证伪 issue 或派发词是好报告的标志,照单全收的报告反而要多看两眼(phoneOtp.cooldownSeconds 配成大于 1 小时会被静默截断 —— OtpSendGuard 的历史 TTL 硬编码 1 小时 #4808 / 内核的插件 init/start 超时守卫定时器从不清除也不 unref —— 每个进程在工作结束后还要空转 startupTimeout(CLI 挂 ~120s 才退出) #4813 / [metadata] nextEventSeq() 把驱动读失败也当成「表还没建」,静默从 1 重新发号 —— #4632 同形,机械检查覆盖不到 #4825 / 每号码 OTP 发送预算(#2780)也只在进程内计数 —— 与 #4772 的限流洞同类,多节点下可按节点数倍增 #4790);PM 被纠正时公开认账,错误归因另立 issue(os migrate 成功时退出码是随机非零值(208/171/176/163/62…),--version / --help 却干净退出 0 #4873)而不是静默丢弃。
  2. PR diff 里的 +0/-0 未必是空文件 —— 文件含 NUL 字节时 git 判为二进制。fix(service-analytics): compareTo 带上 measure 自己的 filter,__compare 列不再是另一个 measure (#4820) #4870 的 347 行测试文件即如此,PM 一度误判为占位空文件。判据落在 blob 不落在 diff;修法写转义 \\u0000(反斜杠 + u0000),不写裸字节。

新增 ## Operational notes(实测坑位)(接在 State model 之后,中文,与 Domain lanes 节先例一致) — 四条队列/平台层结论,共同点是「判据取命令输出,不取 API 字段字面值,也不取本地工作树现状」:

验收自检

验收项 结果
六个关键词可检索 生产者 ×2、worktree 接手 ×1、gh-readonly-queue ×2、rate_limit ×2、origin/main ×9、nul ×5(不区分大小写)
八条(A1 A2 B1 B2 C1 C2 D1 D2 D3 D4)全部落地 全部当日证据编号均在文中命中
不破坏既有结构与内容 134 insertions(+), 0 deletions,既有行删除数 grep 得 0
changeset 与 #4824 同形 空 frontmatter(--- / ---),不发布任何包 —— 与 .changeset/pm-dispatch-domain-lanes.md 首 12 字节逐字节一致

门禁

node scripts/check-nul-bytes.mjscheck-nul-bytes: OK (2986 tracked source file(s), no raw NUL bytes).

值得记一笔:写 C2 那条时编辑工具真的把转义写成了裸 NUL 字节(SKILL.md 第 626 行),先被控制字符扫描(grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]')抓住、按字节改写为转义文本后才提交 —— 该条经验在自己身上原样复现了一次;本 PR 正文首版的同一处也被 GitHub 吃掉过一次,现已修正。eslint 不覆盖 md(配置仅 **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} 等 JS/TS glob),check:doc-authoring 的 ROOTS 是 skills / content,不含 .claude/,故无其他适用门禁。

改动面严格限于 .claude/skills/pm-dispatch/SKILL.md + 一个 changeset,未碰 .claude/agents/os-dev.mdpackages/**、顶层 skills/**content/docs/**

🤖 Generated with Claude Code

https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny

按落点融入既有结构,纯插入不重写:

- step 2 分诊:「无生产者」形状(声明了、消费端有读取、gate 全绿,却无人写值)
- step 3 选批:同文件跨轮串行,推迟时把已知的坑当场钉到 issue 上
- step 5 派发:同日 main 变更写进派发词;新增 worktree 接手协议(宿主中断的 dev
  不可恢复但 worktree 完好)
- step 7 复核:dev 证伪前提是好报告的标志;`+0/-0` 先怀疑 NUL 字节
- 新增 Operational notes(实测坑位):gh-readonly-queue 判入队、队列踢出先认签名、
  GraphQL 配额与 rate_limit 轮询、核验 main 一律用 origin/main

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 1:11pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling size/m labels Aug 3, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 3, 2026 13:17
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit e2086fe Aug 3, 2026
19 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4882-pm-dispatch-lessons branch August 3, 2026 13:28
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 4, 2026
…) (objectstack-ai#4893)

2026-08-03 的 v17 协议变更派发里,有四类已经付出过代价的经验,现行 skill 里
没有或只写了半截。同日另一车道的 objectstack-ai#4885 已沉淀八条,本单只补它覆盖边界之外的。

pm-dispatch —— Operational notes 由四条扩到八条:

- 5:rerun_failed_jobs 复用原 run 的提交/合并 ref,不重算。红的原因若是「基上
  缺一个已合并的修复」,重跑无效,只有推新提交才拿得到新的合并 ref(objectstack-ai#4852 因此
  在队列外空转 100 分钟)。与 rerun-safety-nightly.yml 无关,后者查的是测试污染。
- 6:读数纪律。cd X && cmd 短路会在错的仓里执行(跨仓一律 git -C);
  git grep -c | wc -l 数的是文件数不是命中数;裸名 grep 会被幸存家族当子串命中,
  退役核验要带引号精确名、更硬的判据是查声明式而非提及。零命中必须用确定存在的
  邻近词反查。
- 7:CI 红了先取完整日志归档。completeness check 绿只说明没有 worker 静默死掉;
  turbo 并发输出相邻不等于因果(test 的 dependsOn 只有 ^build,spec 无 pretest);
  不要只看 tail。据错误结论开的 PR 要撤回 draft 并解绑 Fixes。
- 8:共享基础设施类修复按症状复查 main。duplicate-fix-guard.yml 只覆盖「同仓 +
  同一个 Fixes #N」,objectstack-ai#4864objectstack-ai#4856 挂在不同 issue 号下,门禁看不到,而后者先合的
  60s 会被前者降回 30s。

另在 note 1 上补:「不在 main 上」是二义读数(兼容「排队中」与「没入队」);队列
分支 base sha 串成链,可读出排第几;转 draft 会同时掉 auto-merge 与队列成员资格。

step 7 之后新增「入队与落地」小节:merge=os-regen 的七条路径(含两条文档产物)、
四步同步协议、以及跟到 MERGED 而不是跟到入队为止。

spec-property-retirement:

- 新增「四张 ratchet 的可见性按路线相反」—— 枚举值收窄不可见(objectstack-ai#4391),整 def
  删除必须变化(objectstack-ai#4834:-12/-23/-5);拿错对照会双向判错。
- 修好第 2 节指向 plugin-runtime.zod.ts:243-248 的先例引用,该文件已被 objectstack-ai#4878
  整体删除。

Fixes objectstack-ai#4892


Claude-Session: https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 4, 2026
…bjectstack-ai#4907)

这道门禁自己的报错文案写着它拦的是什么:一个裸 NUL 让 grep/ripgrep 把整个
文件当成二进制、静默返回零匹配。那是 grep 的行为,与文件是什么语言无关 ——
但扫描面此前是一份 JS/TS 扩展名清单,范围(用途)与理由(载体)对不上。

代价是 .claude/ 下的全部 markdown 同时落在三道门禁之外。objectstack-ai#4890 就是这么暴露
的:PR objectstack-ai#4885 要写的规则正是「不要写裸 NUL」,而写的过程中一个真的裸 NUL 落进
了 .claude/skills/pm-dispatch/SKILL.md,这道门禁报 OK。

改为扫描所有被 git 跟踪的文本文件。二进制判据是内容判断而非扩展名清单:
非常规文件(symlink / gitlink)跳过;UTF-16/32 BOM 开头跳过(那种编码里 NUL
是结构性的);其余先剔除 NUL 字节、再整文件按 UTF-8 严格解码,解不通才算
二进制。先剔除 NUL 是为了打破「有 NUL 所以是二进制所以不查 NUL」这个 git 掉
进去的循环;整文件解码而非只看前缀,是因为 git 只嗅前 8000 字节正是本文案里
记着的盲区。

同时按仓内惯例把 --self-test 接进 check:nul-bytes 脚本,自检在临时 git 仓库
里跑真实的 scan() 路径。


Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pm-dispatch SKILL.md 增补:2026-08-03 全天运行沉淀的八条 PM 操作经验(全部零覆盖)

2 participants