Skip to content

feat(pm): provenance-aware exception for the docs-audit --write artifact in the governed-fence predicate - #11078

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-9866-governed-fence-provenance
Aug 22, 2026
Merged

feat(pm): provenance-aware exception for the docs-audit --write artifact in the governed-fence predicate#11078
os-zhuang merged 1 commit into
mainfrom
claude/issue-9866-governed-fence-provenance

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#9866

What

Implements the maintainer-ruled shape 2 — the provenance-aware exception — in scripts/pm/check-governed-merges.mjs (rulings on the card, verbatim: 「10277 同意 C」 2026-08-20, re-confirmed over the same day's earlier shape-1 ruling: 「A:按方案 2(最新裁定)」 2026-08-22).

The --test predicate now lifts exactly one path — .claude/workflows/docs-accuracy-audit.js — from the governed hit set, and only when that file's diff byte-equals what node scripts/docs-audit/check-audit-scope.mjs --write produces on the tree under test: the tree's copy must equal replaceBlock(baseFileAtMergeBase, docsDerivedFromThisTree), computed with the generator's own exported replaceBlock and this tree's own docs derivation. So a page-adding docs PR whose only .claude/** touch is the gate's own regeneration no longer forks to a human merge, while every hand edit to the same file still does.

The four ruled constraints, where each lands

  1. Recompute, never a stored baselinerecomputeDocsAuditProvenance produces the expected bytes at verdict time from the tree under test (git merge-base origin/main HEADgit show for the base file; affected-docs.mjs --all --json on this tree for the derivation; the generator's own replaceBlock for the splice). Nothing stored or cached is consulted.
  2. Byte-exact, no shape heuristics — the only pass is string equality against the recomputed output; a reordered-but-set-equal block rejects (pinned).
  3. A NAMED single-file exceptionGENERATED_SURFACE_EXCEPTIONS is structurally a per-surface list, seeded with only this entry per the ruling; GOVERNED_SURFACES and every other judgment are unchanged (a verdict that never consults the exception renders byte-identically, pinned).
  4. Self-test shipped — 29 new assertions (90 → 119) covering the four ruled cases against the generator's real renderBlock/replaceBlock (pure regen passes · in-block hand edit rejects · out-of-block edit rejects · mixed regen+hand-edit rejects), plus byte-exactness on reorder, fail-closed on every unverifiable input (no base version / unreadable file / empty derivation / splice failure / absent provenance), single-file-not-a-class (a sibling .claude/workflows/*.js never consults provenance), and the untouched mixed-diff rule: a hit on any OTHER governed path still forks the whole PR.

Fail-closed is the default everywhere: if the recompute cannot run in the caller's environment (no git, no base, generator failure), the path simply stays governed with a stated reason — the fence holds; nothing degrades to a baseline compare.

Deliberately unchanged: the post-merge SWEEP still lists a pure-regeneration merge. Under-enumeration is the one direction the sweep must never be wrong in (#9902), and recomputing a generator against historical trees is a different machine — the exception narrows the pre-merge predicate, not the audit trail.

Verification (all at 7dd5db4)

  • node scripts/pm/check-governed-merges.mjs --self-test✓ check-governed-merges --self-test: 119 assertions (…), exit 0.
  • Ablation (fail-open mutation === true!== false, confirmed on disk by marker grep 1/0 before running): self-test exit 1, failing assertion absent-provenance-fails-closed-never-open; restore leg confirmed (diff 0 lines, marker count 0, 119 green again). No build step exists for these scripts; the mutation/restore proof is the on-disk grep.
  • End-to-end on this tree (temp docs page + real --write, restored via trap after each leg):
    • real +1/-0 regeneration (the measured incident shape) → --test exit 0, PURE REGENERATION line;
    • in-block hand edit (confirmed on disk) → exit 3, did NOT lift … differs from the generator output recomputed on this tree;
    • out-of-block hand edit → exit 3;
    • mixed diff with .claude/skills/pm-dispatch/SKILL.md → exit 3 (workflow lifted, PR still governed).
  • Landing class of this PR: --test scripts/pm/check-governed-merges.mjs0 of 1 path(s) hit the register … ✅ NOT governed, exit 0.
  • Derived gate families (dispatch-gates.mjs, no paths passed — change set from merge base): check:cross-package-test-inputs · check:entry-guard · check:parse-guard · check:pm-governed-merges · check-ci-filter-parity.mjs all exit 0; plus card-adjacent check:pm-governed-prose (28 self-test cases + 2 surfaces OK), check:nul-bytes (OK, 6402 files), and the generator's own check-audit-scope.mjs --self-test (24 cases) + verify mode (in sync, 189 docs) — all exit 0.

Scripts-only tooling change; releases nothing — skip-changeset applies.

Generated by Claude Code


Generated by Claude Code

…act in the governed-fence predicate
The --test predicate lifts .claude/workflows/docs-accuracy-audit.js from the
governed hit set only when the tree's copy byte-equals the generator output
recomputed on that same tree (replaceBlock(baseFile, docs derived from the
tree under test)) — never a stored baseline, no shape heuristics, fail closed
on every unverifiable input; the mixed-diff rule and every other surface
judgment are unchanged, and the post-merge sweep still lists every entry.
Maintainer rulings on #9866: 「10277 同意 C」 (2026-08-20), re-confirmed
「A:按方案 2(最新裁定)」 (2026-08-22).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsbKEG4LtERSLaDrbehM3e
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Console Pin Gate — 失败步骤: Build the Console SPA at the pinned objectui SHA

    ✗ Build failed in 5.83s
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

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

Merged via the queue into main with commit 9ac93cfAug 22, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-9866-governed-fence-provenance branch August 22, 2026 16:07
os-zhuang pushed a commit that referenced this pull request Aug 25, 2026
Implements the maintainer's 2026-08-25 ruling on #11705 (option A) by
EXTENDING the provenance-aware register PR #11078 built in
`scripts/pm/check-governed-merges.mjs` — no second mechanism.
A row is a GENERATOR, not a path exemption. A hit lifts only when both
halves are the generator's own answer on the tree under test: it declared
the path among its outputs (`--generated-manifest`, written from the same
map the write disposition uses), and its own `--check` reported no drift.
Hand-authored `skills/**` content is not on the register at all.
- `packages/spec/scripts/lib/generated-output.ts`: the shared sink declares
its output set on demand, so the register never restates a path list.
- `scripts/pm/check-governed-merges.mjs`: two ruled rows (gen:skill-refs,
gen:react-blocks), the shared recompute driver, per-row co-edit fences.
- `scripts/pm/check-governed-queue-guard.mjs`: consumes the shared
membership test and driver instead of its own copy.
- `.claude/skills/pm-dispatch/references/state-machine.md`: one-line note.
Closes#11705
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check:docs-audit-scope forces every page-adding docs PR across the .claude/** governed fence — 4 for 4, and 3 merged unnoticed

2 participants

@os-zhuang@claude