Repository navigation
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 intoSep 18, 2026
Conversation
…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>
…rge-driver-verdict-exit-split
os-elon-musk
marked this pull request as ready for review
September 18, 2026 04:59
os-elon-musk
deleted the
claude/issue-18166-merge-driver-verdict-exit-split
branch
September 18, 2026 05:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #18166
Clause-②: no
This is an audit card, and the audit's answer is that all three self-tests chained in
check:merge-driverare immune to the verdict/exit-code split. Nothing was repaired, becausenothing is broken. No script in the population changed; the diff is one audit record under
docs/audits/. Written by sessionsession_019srGWGCBBCBHqcDoRZpQRh.1. The population, re-derived from the script
package.json:19on this branch, read rather than copied from the card, which said its own listis a hint:
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 chainedwith
&&, which is the whole reason the audit matters: a zero from any one of them is the gatepassing.
2. The gate's own exit code, before and after
pnpm check:merge-driverbefore0b31d90fb(the branch point)pnpm check:merge-driveraftere18f4d2a7(this head)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 hashbefore and after), then restored from
HEADwith the restore proven byblob == HEADand an emptygit diff HEAD. No editor of the-ifamily was used and no mutation was left in the tree.git-env.mjsgit-env.mjsgit-env.mjsgit-env.mjsselfTest()returns a value that is not the verdictgit-merge-regen.mjshookIsExecutablereports failure byreturn falsealone — nofail(), and it still prints its own tickgit-merge-regen.mjsprocess.exitCodeline removedgit-merge-regen.mjsgit-merge-regen.mjsregisterCase()deleted, so the floor breaches throughfail()check-regen-pending.mjsdecisionTableSelfTestreturns false while all of its own rows print a tickcheck-regen-pending.mjscheck-regen-pending.mjscheck-regen-pending.mjsnoDistassertion forced falsecheck-regen-pending.mjsregisterCase()deleted, so the floor summand firesTen 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 recordlists 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:
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-driveris itself the gate that would have to judgethat 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 afterthe
origin/mainmerge so the stale-tree warning cleared.17 derived families, 17 run, all exit 0, 0 NOT MEASURED — reconciled with
--ran, whichconfirms the zero is derived from recorded exit codes rather than claimed:
Every exit code was captured by redirecting first and reading
$?second, never after a pipe.One family needed a second pass:
check:doc-formula-expressionsfirst answered 3, its ownPREREQUISITE NOT METcode ("Nothing was measured"), because@objectstack/formulaand@objectstack/lintwere unbuilt in a fresh worktree; afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lintit answered 0. That3 is recorded as not-a-finding, not as a failed measurement.
check:issue-citations --base origin/mainwas also run defensively, since the diff adds four issuecitations: exit 0, and it read 0 files —
docs/audits/is outside its declared surface, so thatis 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:eslint.config.mjsscopesits
filesglob tots,tsx,mts,cts,js,jsx,mjs,cjs. No block names a Markdown extension.--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.eslint.config.mjsstates, with its own positive control,that this repo "never enables type-aware linting (no
parserOptions.project, no typed@typescript-eslintrules) for ANY file". A one-file Markdown diff therefore cannot move theverdict 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:
check-regen-pending.mjs's two inline dispatch assertions (noDist,noTree) sit outside thebattery roster. That is a declared gap, stated at the roster with its cost and its reason,
and this audit measures that both feed the same exit sink (C4). Not a finding. Successor: the
reshape card the roster comment already points at.
fixtureSelfTest()reduces withresults.every(Boolean), which istrueover an empty array.It cannot be reached empty — every path that would skip the pushes throws out of a
trywith nocatch, which exits non-zero — so it is not this card's class either. Noted for whoever nextedits that function. Successor: none.
main()incheck-regen-pending.mjsis the productionpre-commit/pre-pushpath and isoutside
check:merge-driver, which runs--self-testonly. Its own verdict/exit coupling wasread statically (its stale printers increment the same
blockedcount thatdecide()turns intothe returned status) and deliberately not ablated. Successor: none.
staleclaim #15722 removed #16717 recordscheck:merge-driverstaying red there forunrelated reasons, and 155 of 158 scripts/** self-tests have no assertion floor: a battery that never ran is indistinguishable from one that passed #13799 is not addressed here.
维护者速读(草稿)
改了什么 —— 一个文件:
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