Skip to content

docs(audits): the verdict/exit-code audit of check:merge-driver's three self-tests — all three immune - #18896

Merged
os-elon-musk merged 2 commits into
mainfrom
claude/issue-18166-merge-driver-verdict-exit-split
Sep 18, 2026
Merged

os-elon-musk merged 2 commits into
mainfrom
claude/issue-18166-merge-driver-verdict-exit-split

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

Fixes #18166

Clause-②: no

This is an audit card, and the audit's answer is that all three self-tests chained in
check:merge-driver are immune
to the verdict/exit-code split. Nothing was repaired, because
nothing is broken. No script in the population changed; the diff is one audit record under
docs/audits/. Written by session session_019srGWGCBBCBHqcDoRZpQRh.

1. The population, re-derived from the script

package.json:19 on this branch, read rather than copied from the card, which said its own list
is a hint:

"check:merge-driver": "node scripts/git-env.mjs --self-test && node scripts/git-merge-regen.mjs --self-test && node scripts/check-regen-pending.mjs --self-test"

Exactly three files, in this order — scripts/git-env.mjs, scripts/git-merge-regen.mjs,
scripts/check-regen-pending.mjs. The re-derivation agrees with the card's hint. They are chained
with &&, which is the whole reason the audit matters: a zero from any one of them is the gate
passing.

2. The gate's own exit code, before and after

run tree exit
pnpm check:merge-driver before 0b31d90fb (the branch point) 0
pnpm check:merge-driver after e18f4d2a7 (this head) 0

Unchanged, and it has to be: no file the gate runs was edited.

3. The reading — exit code, never output

Per file: is there any path on which the dispatch prints a failing verdict, or counts a failure,
while the process still exits 0? Thirteen ablations, one mutation each, landed and verified on
disk
by scripts/ablation-replace.mjs (anchor hit exactly once, occurrence counts and blob hash
before and after), then restored from HEAD with the restore proven by blob == HEAD and an empty
git diff HEAD. No editor of the -i family was used and no mutation was left in the tree.

# file the mutation reading direction
A1 git-env.mjs one registered case reports failure 1 fires
A2 git-env.mjs failing verdict line kept, exit decoupled from the count 0 discriminates
A3 git-env.mjs case floor raised, so the floor summand fires 1 fires
A4 git-env.mjs selfTest() returns a value that is not the verdict 1 fires
B1 git-merge-regen.mjs hookIsExecutable reports failure by return false alone — no fail(), and it still prints its own tick 1 fires
B2 git-merge-regen.mjs a failure counted and PR #18165's process.exitCode line removed 0 discriminates
B3 git-merge-regen.mjs same forced count, that line left in place 1 fires
B4 git-merge-regen.mjs a registerCase() deleted, so the floor breaches through fail() 1 fires
C1 check-regen-pending.mjs decisionTableSelfTest returns false while all of its own rows print a tick 1 fires
C2 check-regen-pending.mjs a failure counted, verdict line still printed, exit decoupled 0 discriminates
C3 check-regen-pending.mjs same forced count, exit branch intact 1 fires
C4 check-regen-pending.mjs the inline noDist assertion forced false 1 fires
C5 check-regen-pending.mjs a registerCase() deleted, so the floor summand fires 1 fires

Ten firing controls, and they fire. Every exit sink of every file is covered: git-env has two
sinks and three failure signals (A1/A3/A4), git-merge-regen has two (B1/B3 for the count,
B4 for fail()), check-regen-pending has one sink fed by three summands (C1/C4/C5). The record
lists each sink with its line number.

Three discrimination controls, in the other direction. Ten non-zero readings on their own are
also what an instrument that cannot see exit 0 produces. So each file additionally carries the same
forced failure with the count-to-exit coupling removed, and all three printed a failing verdict and
exited 0 — the card's defect, reproduced on each of the three files, by this exact reading:

A2   git-env --self-test: 1 failure(s) [ABLATED: exit decoupled from the count]        exit 0
B2   merge driver wiring is inconsistent - 1 failure(s) (cases and floor); see above.  exit 0
C2   self-test failed -- 1 failure(s) [ABLATED: exit decoupled]                        exit 0

B3/C3 pair with B2/C2 on the same forced count and differ only in whether the coupling is present,
which isolates the coupling as the thing that makes the difference. B2 in particular measures that
PR #18165's repair line is load-bearing rather than decorative.

4. The judgement the card left open

The card raises one shared verdict/exit helper against three copies of if (failures > 0),
expressly as a judgement on measurements. The measurement puts the repair population at zero,
so neither route earns itself: rewriting the verdict site of three working instruments would add
risk with no defect to remove, and check:merge-driver is itself the gate that would have to judge
that rewrite. No gate weakened, no exemption added — acceptance clause 2 holds trivially, since
no file the gate reads was touched.

5. Gates

Derived from the actual changed path, never from a named list:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, re-derived after
the origin/main merge so the stale-tree warning cleared.

17 derived families, 17 run, all exit 0, 0 NOT MEASURED — reconciled with --ran, which
confirms the zero is derived from recorded exit codes rather than claimed:

dispatch-gates --ran: 17 derived famil(ies) accounted for — 17 run, 0 NOT-MEASURED (a DERIVED zero — all 17 recorded an exit code and none of them is 3).

Every exit code was captured by redirecting first and reading $? second, never after a pipe.
One family needed a second pass: check:doc-formula-expressions first answered 3, its own
PREREQUISITE NOT MET code ("Nothing was measured"), because @objectstack/formula and
@objectstack/lint were unbuilt in a fresh worktree; after
turbo run build --filter=@objectstack/formula --filter=@objectstack/lint it answered 0. That
3 is recorded as not-a-finding, not as a failed measurement.
check:issue-citations --base origin/main was also run defensively, since the diff adds four issue
citations: exit 0, and it read 0 files — docs/audits/ is outside its declared surface, so that
is a not-applicable rather than a pass.

Repo-wide eslint, narrowed — and the narrowing is measured, not assumed. Taken at
e18f4d2a7, this branch's final commit:

  1. Population, read from eslint's own config: every config block in eslint.config.mjs scopes
    its files glob to ts,tsx,mts,cts,js,jsx,mjs,cjs. No block names a Markdown extension.
  2. Count, from --format json: 1 result for the changed path, carrying exactly one message —
    File ignored because no matching configuration was supplied. Zero rules were evaluated on it.
  3. Invariance for untouched files: eslint.config.mjs states, with its own positive control,
    that this repo "never enables type-aware linting (no parserOptions.project, no typed
    @typescript-eslint rules) for ANY file". A one-file Markdown diff therefore cannot move the
    verdict of any file it does not contain.

So the narrowing excludes nothing: the only changed path is not in the examined population at all.
CI still runs the full farm.

Acceptance notes

Observations from the audit, noted and deliberately not filed — none is a reproducible defect,
a declared-contract violation, or a metadata-authoring trap:

维护者速读(草稿)

改了什么 —— 一个文件:docs/audits/2026-09-merge-driver-verdict-exit-split.md。三个被审计的
脚本一个字节都没动。

为什么改 —— 卡面要的是一次测量,不是一次修复:check:merge-driver 用 && 串起三个
--self-test,任何一个「打印失败判决却退出 0」就让整条电池悄悄变成 advisory。测量结果是三个都
免疫,所以本 PR 的产物是那份可复核的审计记录本身,而不是代码改动。留在 GitHub 评论里的读数下一
个人复核不到,所以它落进仓库。

风险与代价(含回滚) —— 风险接近零:无代码改动、无门禁改动、无豁免、不发布任何包
(skip-changeset)。回滚 = 删掉这一个文件。唯一的真实代价是 161 行仓内文档的维护面。

席位意见 ——

你要做的 —— 判断这份审计记录该不该留在 docs/audits/。如果认为「审计结论写进卡面评论就够
了」,直接关掉本 PR 即可,卡面报告评论已独立承载同一组读数;如果认为该留,照常合并。另:卡面
的验收第 1、3 条(每个文件带点火对照 + 重新推导 population)由这份记录与本正文同时满足。


Generated by Claude Code

…r's self-tests

The population, re-derived from package.json:19, is exactly three files chained
with `&&`: scripts/git-env.mjs, scripts/git-merge-regen.mjs and
scripts/check-regen-pending.mjs. Each was measured by ablation, reading the
dispatch's EXIT CODE rather than its output: 13 mutations, 10 firing controls
that fire and 3 discrimination controls that reproduce the defect shape (a
failing verdict printed, exit 0) on the same three files.

All three are immune. No script changed, no gate weakened, no exemption added,
and no shared verdict/exit helper: the measurement puts the repair population at
zero, so a refactor of three working verdict sites would add risk with no defect
to remove.

Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh
Co-authored-by: Claude <noreply@anthropic.com>
@os-elon-musk os-elon-musk added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 18, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 18, 2026
@os-elon-musk
os-elon-musk marked this pull request as ready for review September 18, 2026 04:59
@os-elon-musk
os-elon-musk added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit 625db0e Sep 18, 2026
38 checks passed
@os-elon-musk
os-elon-musk deleted the claude/issue-18166-merge-driver-verdict-exit-split branch September 18, 2026 05:40
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 skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants