diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 8aef9ffcc2..51ffc4d200 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -135,7 +135,10 @@ quote. - A new fake engine's `delete()` opens with `assertEngineDeleteDispatch(options)` from `@objectstack/objectql` — never a hand-mirrored id/multi check, which has exactly the hole `check:engine-double-contract` names. Copy one of the pinned fakes the gate lists on a - green run. + green run. Needing a double the file does not already have? The gate's message offers + "pin the new one rather than raising it", but a third remedy it does not name is usually + better: **override the file's existing double** instead of declaring a second one — then + there is no new double to pin and no ledger to touch at all. ## Local verification scope — targeted gates locally, the full farm is CI's job @@ -156,6 +159,21 @@ reads the real gate-job conclusions after your report. ⛔ Not licence to skip t families — they are the cheap half you still owe; what you no longer owe is waiting for CI before reporting. +**Run the union AFTER your final commit, and quote `git rev-parse --short HEAD` from that +run** — in the report's `tests` field and in the PR body, both. A gate log carries no sha, +so a union run taken before the last commit reports green over a tree that is no longer the +head and **nothing anywhere notices**: neither you re-reading your own transcript nor the +PM reading the PR body can tell a covering run from a stale one, and the natural order +(implement, verify, then answer review) puts the verification before the last commits by +construction — so the failure lands hardest on the PRs that got the most review, which is +the wrong way round. Ratchets are why this bites: a stale ordinary-lint run is usually +still true, but a stale **ratchet** run is a claim about a ledger measured against +different code, and the ratchets are precisely the gates a late commit moves. On any +post-review push, re-run the union — or at minimum the ratchet family — at the new head +**before** the report or the PR body is updated. An unquoted HEAD is not a small omission: +it makes a green union unreviewable, so quote it even when the union and the final commit +were obviously the same tree. + ## Standard clauses live HERE, not in your dispatch prompt The prompt carries only per-card deltas (ruling quotes, the 裁决 / PM-机制假设partition, diff --git a/.claude/skills/pm-dispatch/references/review-checklist.md b/.claude/skills/pm-dispatch/references/review-checklist.md index 073443d8ef..6afcbe85b1 100644 --- a/.claude/skills/pm-dispatch/references/review-checklist.md +++ b/.claude/skills/pm-dispatch/references/review-checklist.md @@ -50,6 +50,14 @@ `head_sha` —— 不一致的 run 是关于一个死提交的读数,绿与红**双向都不入账**:旧 head 的绿会把「新推送未验」读成「消费者干净」,旧 head 的红会把当前 head 已修 掉的缺陷重新挂回 PR。 +- **dev 本地跑的门禁并集,同样先钉 head —— 同一条纪律,不是第二条**:dev 的契约 + 要求在**最后一次提交之后**跑并集,并把那一跑的 `git rev-parse --short HEAD` 抄 + 进报告与 PR 正文;复核就读这个 HEAD 与 PR 当前 `head.sha` 比一次。对不上 ⇒ 那 + 份「本地全绿」是关于一棵死树的读数,与上一条同理**双向都不入账**;正文没抄 + HEAD ⇒ 按**没有读数**处理(既不是绿也不是红),以门禁 job 结论为准。门禁日志不 + 带 sha,所以这个抄写是唯一能把一次绿绑到一棵树上的东西 —— 而复核轮之后又推了提 + 交、报告里的 HEAD 却没跟着动,正是它要抓的那一形:补跑并集(至少棘轮族)再更报 + 告,棘轮恰恰是最后一次提交会动的那一族。 - **被取代 head 上的 run 永不重跑**:非当前 head 上的 `cancelled` 结论零动作 —— 新推送自带全套 run。重跑烧掉一整个重量级周期,还能忠实复现一个已被当前 head 修 掉的缺陷、给绿 PR 挂上假红;实测两次误重跑都源于读到 `cancelled` 没先比对