Skip to content

docs(skills): 补齐「跟到合并为止 / CI 诊断纪律 / 生成物同步」四类 PM 经验 (#4892) - #4893

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4892-pm-skill-landing-discipline
Aug 3, 2026
Merged

xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4892-pm-skill-landing-discipline

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4892

2026-08-03 的 v17 协议变更派发里,有四类已经付出过代价的经验,现行 skill 里没有或只写了半截。

⚠️ 先说与 #4885 的关系:同日同文件的第二次沉淀,已逐条去重

本单开出来时 origin/main 刚跳到 e2086fef0 —— PR #4885(issue #4882,另一 PM 会话),「增补 2026-08-03 运行沉淀的八条 PM 操作经验」。同一天、同一份 skill、同一类工作,挂在不同 issue 号下。

这正是本单新增的第 8 条要写的那条规则的现场实例:duplicate-fix-guard.yml 只在两个 PR 声明同一个 Fixes #N 时开火,跨 issue 号的同类工作它看不见。

所以本单先读完 #4885 全文再动笔,重叠的一律不重写:

条目 #4885 已覆盖? 本单处置
队列判据看 gh-readonly-queue/* ✅ 覆盖,且角度更准(auto_merge 字段入队后回落为 off) 不重写,只补三点
rerun_failed_jobs 复用原 ref ❌ 新增 note 5
os-regen 入队前整体重生成 ❌ 完全未覆盖 新增「入队与落地」小节
CI 日志诊断三条 ❌ 新增 note 7
跨 issue 号的基础设施撞车 ❌ 新增 note 8
读数纪律三条 ❌(它的 note 4 讲「在哪棵树上读」,本条讲「命令是否在回答你以为的问题」) 新增 note 6
ratchet 可见性按路线相反 ❌ 进 spec-property-retirement

1. pm-dispatch —— Operational notes 四条 → 八条

note 5:rerun_failed_jobs 复用原 run 的提交与合并 ref,不重算。
note 2 讲的是「同一签名再现要重新诊断」;这条讲另一半 —— 当红的原因是基上缺一个已经合并的修复时,重跑这个动作本身就无效。#4852 的 CI 红在止血 PR #4856 落地之前,重跑仍是同一个 5000ms,直到 git merge origin/main 推了新提交才拿到新的合并 ref。它因此在队列外空转 100 分钟。 判别方法:比对那个修复的合并时间与本 run 的创建时间。

明确写了与 rerun-safety-nightly.yml 无关 —— 那个查的是「同一 checkout 跑两遍是否自洽」的测试污染,名字像但不是一回事。

note 6:读数纪律 —— 三条各自产出过一个「我信了并据此行动」的错读数。

  • cd X && cmd 短路:cd 失败时整条命令继续,在当前仓里执行,产出假的「objectui 零消费方」。⛔ 跨仓一律 git -C。
  • git grep -c | wc -l 数的是文件数不是命中数。
  • 裸名 grep 被幸存家族当子串命中(system/EmailTemplate → EmailTemplateDefinition)。退役核验要带引号精确名;更硬的判据是查声明式而非提及 —— 注释 / pin 断言词 / 迁移散文里出现该名是正常且应当的。
  • 统一原则:零命中必须用确定存在的邻近词反查,否则不成立。

note 7:CI 红了先取完整日志归档 —— 三条读日志的纪律。
当天最贵的错误:公开断定四次 CI 红是内核 OOM-killer 杀掉 DTS 构建,据此开了 PR #4853,被 #4853 自己的 CI 推翻(挂着新参数跑,红得一模一样)。更正见 #4845。三个叠加错误各成一条:completeness check 绿 ≠ 测试通过;turbo 并发输出相邻 ≠ 因果;不要只看 tail(那次 ~10 KB 尾巴被 gen:schema 的 1675 行清单吃光)。并补:据错误结论开的 PR 要撤回 draft、解绑 Fixes。

note 8:共享基础设施类修复按症状复查 main,不按 issue 号。
写明 duplicate-fix-guard.yml 的覆盖边界(同仓 + 同一 Fixes #N),以及边界外的这一例:#4864 与 #4856 修同一个基础设施问题却挂不同 issue 号,而且 #4864 若合进去会把 #4856 已合的 60s 降回 30s —— 一次静默回退。

note 1 的三点补充(不重写它的主判据):「不在 main 上」是二义读数(同时兼容「排队中」和「没入队」,处置相反);队列分支 base sha 串成链,可读出排第几;转 draft 会同时掉 auto-merge 与队列成员资格。

2. pm-dispatch —— step 7 之后新增「入队与落地」小节

A. 碰生成物的 PR 入队前必须同步 + 整体重生成。 第 3 步只保证同一批内 file-disjoint,管不到先后两单都碰 packages/spec 生成物 —— 而协议变更几乎必然如此。merge=os-regen 会让 merge exit 0、零冲突标记却静默丢掉一侧改动,当天在 #4809 / #4846 / #4841 各复现一次。四步协议 + 一条更硬的旁证(去查上一单的实现体是否完好,条目是索引、实现体才是重灾区)。

B. 跟到 MERGED 为止,不是跟到「已入队」为止 —— 维护者对此有过明确纠正。落地之后再核一次落地判据本身,因为队列的合并同样走 os-regen 驱动。

3. spec-property-retirement

新增「四张 ratchet 的可见性按路线相反」:

形态 四张 ratchet 实例
枚举值收窄 字节完全相同,仪器上不可见 #4391(PR #4871)
整 def 删除 必须变化 #4834(PR #4878):−12 / −23 / −5

后果双向且都贵:前者的零变化判成异常 → 白折腾;后者的零变化判成正常 → 放过一个没真正删掉的 def。所以是先定路线再定期待读数,不能反过来。

顺手修掉一个悬空引用:第 2 节「nothing parses it」路线的先例指针写的是 packages/spec/src/kernel/plugin-runtime.zod.ts:243-248,该文件今天刚被 #4878 整体删除。改为指 #3896 / #4834(PR #4878)两个编号 + 幸存的 kernel/index.ts 说明块 —— 指 issue 比指行号耐放。


写进去的事实,逐条实跑核过

断言 核实来源
test 的 dependsOn 只有 ["^build"] turbo.json 实读
packages/spec 无 pretest package.json 实读
merge=os-regen 路径清单 .gitattributes 实读 —— 是 7 条不是 5 条,初稿漏了 docs/protocol-upgrade-guide.md 与 content/docs/references/**,已补,并注明权威清单是文件本身
「completeness check 绿 ≠ 测试通过」 原文在 .github/workflows/ci.yml:234
duplicate-fix-guard.yml 覆盖边界 workflow 实读

其他

  • ⛔ content/docs/releases/ 一个字未碰。
  • 空 frontmatter changeset —— 只改 .claude/skills/ 下两份内部 agent 说明,不含任何 workspace 包的代码或产物。
  • 纯文档改动,无代码路径变化。

🤖 Generated with Claude Code

https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9


Generated by Claude Code

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

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

- 5:rerun_failed_jobs 复用原 run 的提交/合并 ref,不重算。红的原因若是「基上
  缺一个已合并的修复」,重跑无效,只有推新提交才拿得到新的合并 ref(#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」,#4864 与 #4856 挂在不同 issue 号下,门禁看不到,而后者先合的
  60s 会被前者降回 30s。

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

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

spec-property-retirement:

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

Fixes #4892

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9
@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:39pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling size/m labels Aug 3, 2026
@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 13:59
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit db6581a Aug 3, 2026
18 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-4892-pm-skill-landing-discipline branch August 3, 2026 14:08
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 4, 2026
…ing a release (objectstack-ai#4898) (objectstack-ai#4899)

17.0.0-rc.2 is versioned in main (3cfd9f0) and absent from npm. The Release run
that should have published it reported success:

  Create Release Pull Request or Publish to npm ... 0s
  All changesets are empty; not creating PR

changesets/action reaches its publish branch only when there are ZERO pending
changesets. An EMPTY changeset still counts as pending, so a main carrying only
empty ones takes neither path — no version PR, no publish, published=false, and
a green run. `docker` skipped with it, so no runtime image shipped either.

Two pending changesets were on main, both empty-frontmatter: objectstack-ai#4893's and
objectstack-ai#4896's. Either one alone is enough. This is not an exotic state — `Check
Changeset` sanctions the empty changeset as the "this PR releases nothing"
declaration, so any docs/ci PR landing during the version-PR window produces it.

Unstick: delete both. An empty-frontmatter changeset declares no releases, so it
contributes no entry to any package's CHANGELOG — deleting it discards nothing.
Its only job was to satisfy `Check Changeset` for PRs that merged hours ago.
With the pending set empty, the next push to main takes the action's normal
publish path, which ships the GitHub Releases and the ADR-0087 D4 spec-changes
attachment along with npm and Docker.

This PR therefore adds no changeset of its own — it carries the `skip-changeset`
label instead. Adding an empty one would re-create the exact condition being
removed and force the release down the degraded fallback path below.

Prevent the silent recurrence: a recovery step after the changesets action,
gated on the only fact that matters — the version in the repo is not on the
registry. `changeset publish` skips versions already published, so it is a
no-op on the normal path (main's version IS the last released one) and a repair
on this one. It never succeeds silently: if the version is still missing after
publishing, the job fails. The job's `published` / `cli-version` outputs now
accept either publish path, so a rescued release still ships its Docker image
instead of losing it to the same hole.

The fallback cannot create GitHub Releases or the spec-changes attachment —
those live inside the action's own publish path — so it says so in a warning
rather than leaving the gap to be discovered later.

Verified: YAML parses, both job outputs resolve on each path, the step's shell
passes `bash -n`, and all three branches were exercised against stubbed
npm/pnpm/git — already-published (no-op, exit 0), missing (publishes, sets
published/version), and missing-after-publish (::error::, exit 1).


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

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
… — no queued PR left the queue on a draft conversion (objectstack-ai#20845)

Fixes objectstack-ai#20764

Clause-②: no

## What changes

`.claude/skills/pm-dispatch/references/platform-readings.md`, lines
41-43 only. The rewrite is net 0 lines, and the three new lines are 120
/ 118 / 120 bytes (cap 120).

| line | before | after |
|:--|:--|:--|
| `:41` | 转 draft 不是可靠的踢队手段:两向相反读数并存,处置按最坏走。 | 转 draft
不是可靠的踢队手段:本仓与姊妹仓均见已入队转 draft 仍保位照合,处置按最坏走。 |
| `:42` | 本仓转 draft 同时掉 auto-merge 与队列成员资格,不自动恢复,转正后重挂;姊妹仓曾保位照合。 | 本仓转
draft 同秒掉 auto-merge 仅见于未入队时,转正后重挂;已入队者 2026-08-08、09-29 照合。 |
| `:43` | 补救:转 draft 与卸载 auto-merge 都做 —— 本仓卸载 auto-merge 单独不踢队。 | 补救:转
draft 与卸载 auto-merge 都做,本仓卸载单独不踢队;两手齐做亦未见踢队(08-08、09-28)。 |

The remedy on `:43` is unchanged: do both acts. No rule is added. The
in-file citations are dates, because `pnpm check:pm-skill-id-lint` goes
red on a `#`-number anywhere under `.claude/skills/pm-dispatch/`, and
this file cites no PR number today. The PR numbers and timeline events
are in this body. That is where the id-lint header puts a reading's
provenance.

## The readings (re-readable: `GET
/repos/objectstack-ai/objectstack/issues/N/timeline`)

I ran a read-only timeline scan over objectstack-ai#4700-objectstack-ai#4900, objectstack-ai#6650-objectstack-ai#6850 and
objectstack-ai#20400-objectstack-ai#20840. It looked for two patterns: a `convert_to_draft` between
`added_to_merge_queue` and the next `removed_from_merge_queue`, and a
`convert_to_draft` on a PR that was armed but not queued. The scan found
every row below. The table is complete for those windows only, not for
the whole repo.

**Queued, then converted to draft.** None of the four left the queue
because of the conversion.

| PR | queued | draft | disable sent? | what followed |
|:--|:--|:--|:--|:--|
| objectstack-ai#6732 | 2026-08-08T14:01:50Z | 14:04:32Z | claimed in objectstack-ai#6799's text; no
`auto_merge_disabled` event | the queue merged it at 14:38:56Z, 34 min
later, still draft |
| objectstack-ai#20420 | 2026-09-28T13:43:01Z | 14:02:28Z | claimed in the seat's
14:04Z comment; no event | its own group `pr-20420-3cf6449` had `Lint &
Type Check` = failure at 14:01:49Z, before the draft;
`github-merge-queue[bot]` removed it at 14:11:24Z, unmerged |
| objectstack-ai#20442 | 2026-09-28T13:45:42Z | 14:02:50Z | claimed in the seat's
14:04Z comment; no event | the queue built a new group
`pr-20442-b285508` for it at 14:11:29Z, 9 min after the draft; that
group's `CI` = failure at 14:30:14Z; removed at 14:35:44Z, unmerged |
| objectstack-ai#20695 | 2026-09-29T23:32:51Z | 23:57:02Z | no (the card discloses:
draft alone) | the queue merged it at 00:04:21Z, 7 min later, still
draft |

**Armed, not queued, then converted to draft.** Each one dropped
auto-merge.

| PR | armed | draft | `auto_merge_disabled` | after |
|:--|:--|:--|:--|:--|
| objectstack-ai#4745 | 2026-08-03T01:28:46Z | 01:29:30Z | 01:29:30Z (same second) |
ready, re-armed 01:30:52Z, queued, merged |
| objectstack-ai#6727 | 2026-08-08T13:42:58Z | 13:44:36Z | 13:44:36Z (same second) |
ready, re-armed 13:46:02Z |
| objectstack-ai#6829 | 2026-08-08T23:37:05Z | 23:40:25Z | 23:40:26Z (1 s) | ready and
enqueued 6 h later |

Each disable event carries the converter as its actor. A timeline cannot
tell whether the platform wrote it or a scripted follow-up did. The line
records what was observed ("同秒").

## Where the old reading came from

- The "drops queue membership" reading first appears in `db6581a5`
(objectstack-ai#4893, card objectstack-ai#4892, 2026-08-03). It cites no PR. That is the same day
objectstack-ai#4745 dropped auto-merge on an armed PR that was not queued. The
queue-membership half has no instance behind it.
- `cd704cc4` (objectstack-ai#6799) later cited objectstack-ai#6732 for "only draft evicts; disable
alone does not". In objectstack-ai#6732's own timeline the PR stays queued after the
draft and is merged by the queue 34 minutes later.
- The sister-repo half ("姊妹仓曾保位照合", objectui 2026-08-25) is carried over
as it was. I did not re-read it.

## How this departs from the triage direction (5903760674)

I did not choose these quietly. Each one is a place where a measurement
changed what the direction assumed:

1. **`:42` is split by queue state, not "mixed" in the sense of opposite
readings.** No queued PR in the scanned windows left the queue because
of a draft conversion. The direction assumed the old half had its own
measurement. It has none, and the one PR later cited for it reads the
other way. What does go both ways in this repo is the effect by state:
unqueued, auto-merge drops; queued, the PR keeps its place.
2. **`:43` says "not seen to dequeue", not "not measured".** Three PRs
(objectstack-ai#6732, objectstack-ai#20420, objectstack-ai#20442) have both acts claimed in writing, and none was
removed by them. The caveat: on a queued PR, the disable leaves no
timeline event, so whether it was sent rests on the seats' own comments.
3. **`:41` loses "两向相反读数并存".** No opposite reading survived the re-read.

## Acceptance notes

- **Out of scope, same family, Tier H.** `AGENTS.md` states the
falsified reading as a premise in three places. Prime Directive objectstack-ai#14 says
"draft is what removes queue membership, disabling alone drops only the
arming". Multi-agent §7 says "flipping back to draft drops auto-merge
and queue membership at once". The "State on your PR" paragraph says the
draft flag "flipped back destroys auto-merge and queue membership at
once". I did not touch them. They are reported to the seat.
- **Eviction.** The only dequeue acts measured in this repo are these
two. First, the queue's own ejection after a red group build (objectstack-ai#20420,
objectstack-ai#20442). Second, a manual removal by a maintainer with no draft
conversion (objectstack-ai#20797, `removed_from_merge_queue` 2026-09-30T07:33:36Z).
Whether the remedy should name a different act is a question for the
seat. It is not a rule this PR adds.
- **Changeset.** `.claude/**` is not in any package's `files[]`, so this
PR publishes nothing. `skip-changeset` is the seat's to apply.
- **Governed surface, Tier S (`.claude/**`).** This PR stays a draft
until a `## Contract review` record at `CONTRACT_REVIEW_TIER` exists for
its head.

## Verification (head `02ea176a1`)

- The 20 gates from `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` all exit 0. They are
`check-closing-keyword-parity` (plus `--self-test`),
`check-comment-mask-corpus`, `check-governed-queue-guard --self-test`,
`check-harness-current --self-test`, `lint
check:doc-formula-expressions` (after building `@objectstack/lint` and
its dependency closure under `os-verify-lock.sh`),
`check:agent-test-spelling`, `check:cross-package-test-inputs`,
`check:doc-authoring`, `check:driver-memory-census`,
`check:gitlink-declared`, `check:nul-bytes`, `check:pm-governed-merges`,
`check:pm-half-states`, `check:pm-skill-id-lint`,
`check:pm-skill-ratchet`, `check:refd-timer-probe`,
`check:required-contexts`, `check:skill-frame-sync` and
`check:watch-hint-literal`.
- `pnpm check:pm-settings-deny-roster` was also run, because its roster
lives under `.claude`. Exit 0.
- `--ran` reconciliation: 20 derived, 20 run, 0 NOT-MEASURED. All 20
recorded an exit code.
- `check:pm-skill-ratchet` printed: `platform-readings.md is 469 lines
(ceiling 469; headroom 0)`, widest table row 0 bytes (pin 0).
- Not measured locally: `check-required-contexts --verify-required-set`
and `check-half-states --provenance`, which read the workflow event, and
the four CI type-check lanes. This diff touches no TypeScript.

## 维护者速读(草稿)

- **改了什么**:PM 协议参考文件 `platform-readings.md` 第 41-43 行,关于「已入队 PR 转 draft
能否踢出合并队列」的三条平台读数。行数不变。
- **为什么改**:旧文说本仓转 draft 会掉出队列。回查时间线后发现,本仓 4 个已入队后转 draft 的 PR 都没有因此出队。其中
2 个带着 draft 状态被队列直接合入,另外 2 个是在自身队列构建变红之后才被移出。旧说法唯一引用的实例,时间线恰好反向。转 draft
会掉 auto-merge,这一点只在「未入队」时成立。
- **风险与代价(含回滚)**:只改文档读数,不改任何规则或代码。补救仍是两手都做。风险在于读者以为有可靠的踢队手段,而实际没有。回滚就是
revert 这一个提交。
- **席位意见**:
- **你要做的**:无需操作,由席位按 Tier S 复核后落地。若要让 `AGENTS.md` 里的同一旧说法一并修正(Tier
H),需要你点头另开 PR。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg)_

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.

把「跟到合并为止 / CI 诊断纪律 / 生成物同步」写进 pm-dispatch 与 spec-property-retirement skill

3 participants