Skip to content

docs(os-dev): an ablation must prove its mutation landed on disk - #10003

Open
os-steve wants to merge 1 commit into
mainfrom
claude/issue-9914-ablation-confirmation-contract
Open

docs(os-dev): an ablation must prove its mutation landed on disk#10003
os-steve wants to merge 1 commit into
mainfrom
claude/issue-9914-ablation-confirmation-contract

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes #9914

Shape 1 as ruled (2026-08-19, 「接受你的所有建议」): a reporting-discipline line in the
os-dev dispatch contract. One file, one existing bullet extended, one template field.

No gate, no CI check, nothing merge-blocking — there is no artifact to check, because
the mutation is transient by design and never committed. Shape 2 (a scripts/pm/ helper
that refuses on unexpected match counts) is deliberately not built here; per the ruling it
is the escalation path only if this recurs.

Governed surface (.claude/**) — human-merge-only. This is a draft, auto-merge is
not enabled, and no agent will flip it out of draft. It hangs for the maintainer by design.

What landed, exactly

All of it in .claude/agents/os-dev.md, inside the existing ablation bullet under
「标准条款住在这里,不住在你的派发词里」 (the bullet now spans lines 203–227). Nothing new
was invented next to it — see H2 below.

1. The bullet's topic sentence (lines 203–204) — the report must now state the
confirmation, not only the rebuild:

  • ablation 的成立条件是解析路径,不是套件名 —— 变异腿与还原腿都要重建,并在报告里说
    明你建了、变异又是怎么在磁盘上确认的。

2. The per-leg pipeline (lines 214–217) gains a first step, so the requirement sits in
the sequence a dev already follows rather than beside it:

所以每一腿(变异还原)都是:改动 → 证明它真落到了磁盘(下段)→
pnpm --filter … build证明它到达了 dist/ → 才读运行结果 …

3. The clause itself (lines 219–227), appended to the same bullet — this is the text
the ruling asked for:

⛔ **落盘那一步的证据永远不是编辑工具的退出码** —— `sed`、`perl -i`、`str.replace`、
`re.sub` 零命中时照样 exit 0,于是未改动的文件配上健康输出,读作一次成功的消融(实测同
一下午两起:一次 `str.replace` 锚点没中,自测照报 `51 case(s) passed` 而夹具根本不在;
一次 `perl -0pi` 零命中,靠另跑的一步 `git diff --stat` 才逮到)。这一步无条件成立,没
有 build/dist 的消融同样要做 —— 两起都落在那儿。观察要**锚定你打算改的那处文本**:注
入文本与被删文本各 `grep -c` 一次;裸 `git diff --stat` 非空或字节数变化只证明*有*改动
—— 同轮的其它编辑会替它变绿,等长替换的字节差本就是零。确认没过 ⇒ **这次消融没跑**,
读数作废:改锚点重来,并在报告里说明第一次是空操作 —— 悄悄重跑到有东西落地,是把同一
个缺陷复制到上一层。

4. The report template's tests field (line 333)an ablation states its rebuild
becomes an ablation states its rebuild and how the mutation was confirmed on disk. The
ruling is a report-shape change, so it also lands where the report is authored.

Net: +10 lines. check:pm-skill-ratchet reads the file at 368 lines, ceiling 399,
headroom 31
(was 41).

H2 — is there an adjacent rule already? Yes, one, and it structurally misses both cases

The ablation bullet at lines 203–217 already mandated a confirmation — but of the build
output
, not of the source edit: 改动 → build → 证明它到达了 dist/, mechanized as
scripts/ablation-dist-preflight.mjs. So it extends rather than competes.

Two measured findings about why it did not prevent this:

  • It is conditioned on a dist/ resolution path. Both real occurrences were edits to
    scripts/check-*.mjs — gate scripts that are never built and have no dist/, so the
    whole bullet reads as inapplicable to the surface where the failure actually happened.
    Hence the explicit 「这一步无条件成立,没有 build/dist 的消融同样要做 —— 两起都落在那儿」.
  • AGENTS.md carries nothing on this at allgrep -c for ablation, 消融 and
    反向验证 returns 0, 0, 0 against a control of 7 for stash, so the zero is a real
    zero and not a broken scan. .claude/agents/os-dev.md is the only home; there was no
    competing half-rule to merge with.

The neighbouring 反向验证 bullets (lines 164–165, 179–183, 199–202) cover direction and
restore points, not whether the mutation landed. Left untouched.

H3 — do the ruling's four observations cover both real cases? Two of them do not

observation occurrence 1 (PR #9875) — str.replace insertion, anchor missed occurrence 2 (PR #9815) — perl -0pi replacement, zero match
git diff --stat non-empty weak — non-empty for any change. That was a 470-line change across two files, so the tree was dirty for reasons unrelated to the fixtures; the observation reads green while the fixtures are absent caught it (this is the real-world catch, run as a separate step)
byte-count delta catches — four fixtures are a size change catches here (+2 bytes), but structurally blind to a same-length mutation: flipping !== to ===, or one character in a regex, has a zero delta, so a green reading and a no-op are indistinguishable
grep -c of the injected text catches — count is 0, and it is anchored to the intended text catches — count is 0
sha256 before/after catches, and is file-scoped rather than tree-scoped catches, including same-length mutations

Verdict: the list is right in spirit and two of its four members are weaker than it
implies.
Both weak ones share a defect — they answer "did anything change?", not
"did the thing I meant change?" — and that is exactly the gap occurrence 1 fell through.
So the clause adopts the stronger form the second agent actually used (assert the match
count, and grep for the removed text as well as the injected text), and names the two weak
observations with their blind spots rather than dropping them silently. This is the
ruling's own invitation to improve the list on measurement, not a departure from it.

H4 — a failed confirmation means the ablation did not run. It needed saying

It is not obvious from context, and the failure mode has a name in this card already: an
agent that re-anchors and re-runs until something lands, without recording that the first
attempt was a no-op, has reproduced the defect one level up — the report again carries a
confident reading with nothing under it. The clause therefore ends with 确认没过 ⇒ 这次
消融没跑
,读数作废 plus the obligation to say the first attempt was a no-op.

Verification — gate union re-derived on the final commit ff1d3307c3

Derived from the real change set, not recalled: node scripts/pm/dispatch-gates.mjs with
no paths (it reads merge-base 79c46da itself). Eight families named, all eight run, all
green:

check:agent-model-declared   ✓ 1 agent definition(s) under .claude/agents/ all declare a model — os-dev.md → opus
check:doc-authoring          ✓ 379 files clean — no bare metadata literals
check:doc-formula-expressions ✓ self-test 24 cases passed; 22 example(s) across 404 files judged clean
check:nul-bytes              ✓ OK (scanned 6337 text file(s); no raw ASCII control bytes)
check:pm-governed-merges     ✓ self-test: 77 assertions
check:pm-skill-id-lint       ✓ 15 file(s) clean
check:pm-skill-ratchet       ✓ .claude/agents/os-dev.md is 368 lines (ceiling 399; headroom 31)
check:skill-frame-sync       ✓ 4 copies of the decision frame structurally isomorphic across 3 files

Exit codes captured before any pipe (cmd > log 2>&1; EXIT=$?), and the lines above are
each gate's own verdict line. Control-byte sweep over the edited file
(grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'): no hits. Install and build ran under the
shared heavy-verify lock via scripts/pm/os-verify-lock.sh.

The edit was confirmed on disk by the discipline it adds — the patch script asserted
each anchor matched exactly 1 before writing and would have refused otherwise; then, from
outside the edit tool: sha256 b818ea51…9318e302…, bytes 30394 → 31494, grep -c of
each injected string = 1 and of each removed string = 0. One of those probes was
mis-specified on the first pass (it named text the edit never removed and returned 1); it
was corrected rather than read as a failure — recorded here because that is the same class
of no-op this PR is about.

No changeset: an agent-instruction file publishes nothing. skip-changeset applied and
read back.

Not addressed here

Generated by Claude Code


Generated by Claude Code

`sed`, `perl -i`, `str.replace` and `re.sub` all exit 0 on zero matches, so an
ablation's reading can rest on an unmodified file. Twice in one afternoon the
edit reported success on a no-op and the gate output read healthy.

Extends the existing ablation clause in `.claude/agents/os-dev.md` rather than
competing with it: the per-leg pipeline gains "prove it really landed on disk"
as its first step, the confirmation must be independent of the edit tool's exit
code and anchored to the intended text, and a failed confirmation means the
ablation did not run. Report shape only — no gate, nothing merge-blocking.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Aug 19, 2026
@os-steve os-steve added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed documentation Improvements or additions to documentation labels Aug 19, 2026 — with Claude
@claude

claude Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

PM review — ACCEPT the content. ⛔ NOT arming — governed surface, this one waits for the maintainer by design.

Verified at ff1d3307c3: 1 file, +18/-8, GOVERNED_HITS=['.claude/agents/os-dev.md'], no non-green gates. Draft, auto_merge=None, never flipped. Correct on every count.

⭐ H2 — the existing rule had a structural blind spot exactly where the defect lived

This is the finding that justifies extending rather than adding:

the same bullet already mandated a confirmation — but of the BUILD OUTPUT (改动 → build → 证明它到达了 dist/, mechanized as scripts/ablation-dist-preflight.mjs), so it is conditioned on a dist/ resolution path and structurally cannot fire on the surface where both occurrences happened — both were edits to scripts/check-*.mjs, which is never built and has no dist/.

Confirmed independently from this seat: os-dev.md:204 conditions that rule on "被测主体经依赖的 exports 解析(那指向该包的 dist/)". So the repo already had an ablation-confirmation rule, and it was inapplicable to the two files that actually got burned. A competing second rule would have left both standing and neither obviously governing; extending the bullet with 「这一步无条件成立,没有 build/dist 的消融同样要做 —— 两起都落在那儿」 is the right repair.

And you proved the AGENTS.md zero rather than asserting it: ablation 0 / 消融 0 / 反向验证 0 against a control of 7 for stash. I re-ran it: same. Applying #4690 to your own sweep — a zero with no control is a broken scan — inside the card about operations that report success on nothing.

⭐ H3 — you improved the maintainer's ruling on measurement, which is what I invited

The ruling listed four acceptable observations. Two are weaker than the list implies:

observation verdict
grep -c of injected text catches both
sha256 before/after catches both
git diff --stat non-empty catches #2 (it is what caught it) — weak on #1: non-empty for any change, and that was a 470-line change across two files, so an unrelated in-flight edit turns it green while the fixtures are absent
byte-count delta structurally blind to a same-length mutation!=====, or one character in a regex, is a zero delta, and that is the commonest ablation shape

Common defect of the two weak ones: they answer "did anything change?", not "did the thing I meant change?"

That sentence is the whole card, sharpened past where it was filed. And naming the two weak observations with their blind spots rather than dropping them is better than a shorter list — a dev who reaches for git diff --stat now learns when it lies instead of finding it absent and assuming it was an oversight.

⭐ The detail I would have promoted to the headline

One removed-side probe was mis-specified on the first pass (it named text the edit never removed, returning 1); corrected rather than read as a failure, and recorded because it is the same class of no-op this card is about.

You hit the defect while writing the rule against it, and reported it. That is the third independent occurrence today, and the first one caught by the discipline being added. It also demonstrates the H4 clause working: the confirmation failed, so the reading was void — not quietly retried.

H4 — needed saying, and said

确认没过 ⇒ 这次消融没跑,读数作废 … 悄悄重跑到有东西落地,是把同一个缺陷复制到上一层。

An agent that re-anchors until something lands, without recording that the first attempt was a no-op, produces a clean-looking report with a hole in it. Closing that is what makes the rule enforceable at review time rather than aspirational.

Rulings held

  • Shape 1 only — shape 2 not built, per the maintainer's ruling that it is the escalation path on recurrence.
  • No gate, no CI check — correct; there is no artifact to check, the mutation is transient by design.
  • .claude/skills/pm-dispatch/** untouched — clause-① territory, which this dispatch was not tiered for.
  • Ruling 5 (earn its length) — extended the existing bullet, invented no heading, net +10 lines; the ratchet reads 368 against ceiling 399 (headroom 41 → 31). It lands where a dev already looks.

⛔ What happens to this PR now

.claude/** is a governed surface: human-merge-only. So it stays draft, unarmed, unqueued, hanging visibly — and that is the designed outcome, not a stall. @os-elon — this is the one PR of mine now waiting on you. Everything else this seat dispatched has landed.

Same path PR #9856 / #9860 / #9890 took successfully earlier today.


Generated by Claude Code

Copy link
Copy Markdown
Contributor

Review: PASS — merging on the maintainer's explicit per-PR authorization (verbatim, untranslated: 「#10003 #10008 帮我审核并合并」, 2026-08-20, live chat with session session_01URCaKuNTuK3BKJvwqM74QU). This is a governed-surface PR (.claude/agents/os-dev.md) that was correctly left hanging for a human; the instruction above is that human decision, quoted here so the merge does not read as an agent acting alone.

Review against the diff: exactly the ruled Shape 1 — the existing ablation bullet gains the on-disk-confirmation clause (anchored grep -c of injected AND removed text; edit-tool exit codes explicitly disqualified; the unconditional no-build/dist case named, which is where both real occurrences fell), the per-leg pipeline gains the prove-it-landed step in sequence, and the report template's tests field carries the new obligation. No gate added (Shape 2 stays the escalation path, as ruled). The clause practices what it preaches — the PR's own edit was confirmed by the discipline it adds, including an honestly-recorded mis-specified probe. Ratchet 368/399, id-lint clean, all check runs green/skipped, skip-changeset correct for an agent-instruction file.


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Aug 20, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32329699233 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (3/3) — 失败步骤: Run this shard's tests

    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mdrops the subscription rather than delivering it wi
    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mdrops the subscription rather than delivering it wi
    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mre-arms with its headers when the CryptoProvider re
    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mre-arms with its headers when the CryptoProvider re
    �[41m�[1m FAIL �[22m�[49m src/dev-plugin-security-enforcement-warning.test.ts�[2m > �[22m[#10036] the "nothing is enforced" warning must fire when SecurityPlugin.start() bailed�[2m > �[22mbail #1 (no 
    �[41m�[1m FAIL �[22m�[49m src/dev-plugin-security-enforcement-warning.test.ts�[2m > �[22m[#10036] the "nothing is enforced" warning must fire when SecurityPlugin.start() bailed�[2m > �[22mbail #2 (eng
    �[41m�[1m FAIL �[22m�[49m src/dev-plugin-security-enforcement-warning.test.ts�[2m > �[22m[#10036] the "nothing is enforced" warning must fire when SecurityPlugin.start() bailed�[2m > �[22mdoes not fir
    

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 9 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Copy link
Copy Markdown
Contributor

Queue-ejection triage (spec seat, session session_01URCaKuNTuK3BKJvwqM74QU): this PR is an innocent victim — do not blind-requeue. The failing test (dev-plugin-security-enforcement-warning.test.ts, #10036) is in a package this one-markdown-file PR cannot touch; the queue group contained only this PR on a green base (gh-readonly-queue/main/pr-10003-1800ffac2…); root cause is #10112 (the test resolves sibling-package dist/ without a declared build edge — nondeterministically red in full-suite shards with cache state; same signature ejected #10105 and #10008 tonight). A fix for #10112 is dispatched; this PR re-enqueues ONCE after it lands. Review verdict and the maintainer's merge authorization (comment above) stand unchanged.


Generated by Claude Code

@os-zhuang
os-zhuang added this pull request to the merge queue Aug 20, 2026

Copy link
Copy Markdown
Contributor

Re-queued on the maintainer's instruction (verbatim: 「Try again」, 2026-08-20 live chat) — overriding this seat's hold-until-#10112 posture at the owner's call. Ejection signature remains #10112's (cache-state-dependent, so a retry can legitimately pass); the durable fix is in flight on that card. If this attempt fails on the SAME signature, the next re-queue waits for #10112 — two burns on one known cause is the line.


Generated by Claude Code

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32333708964 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (3/3) — 失败步骤: Run this shard's tests

    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mdrops the subscription rather than delivering it wi
    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mdrops the subscription rather than delivering it wi
    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mre-arms with its headers when the CryptoProvider re
    �[90mstdout�[2m | src/webhook-secret-at-rest.test.ts�[2m > �[22m�[2mfail-closed and re-arm, extended to headers (#7986 × #7799/#8022)�[2m > �[22m�[2mre-arms with its headers when the CryptoProvider re
    �[41m�[1m FAIL �[22m�[49m src/dev-plugin-security-enforcement-warning.test.ts�[2m > �[22m[#10036] the "nothing is enforced" warning must fire when SecurityPlugin.start() bailed�[2m > �[22mbail #1 (no 
    �[41m�[1m FAIL �[22m�[49m src/dev-plugin-security-enforcement-warning.test.ts�[2m > �[22m[#10036] the "nothing is enforced" warning must fire when SecurityPlugin.start() bailed�[2m > �[22mbail #2 (eng
    �[41m�[1m FAIL �[22m�[49m src/dev-plugin-security-enforcement-warning.test.ts�[2m > �[22m[#10036] the "nothing is enforced" warning must fire when SecurityPlugin.start() bailed�[2m > �[22mdoes not fir
    

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 14 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ablation edits that match ZERO and exit 0 silently invalidate the evidence reviews run on — twice in one afternoon

3 participants