Repository navigation
Commit 3aaea38
Fixes #18055
`scripts/pm/check-governed-merges.mjs` `main()` read `rearm.hint` out of
a scope that never bound
`rearm`, and it did so inside the **argument** of the `console.error`
that prints the sweep's
`sweep INCOMPLETE` banner. An argument that throws means the call never
happens: the
`ReferenceError` deleted the one warning saying the printed list must
not read as clean, and the
`return EXIT_INCOMPLETE` behind it.
## What changed (one file, `scripts/pm/check-governed-merges.mjs`)
1. **The binding, in the scope that reads it.** `main()` now builds the
re-arm plan at the point
the banner is built, from the same three inputs `rearmProxyOrNull`
builds its own from
(`process.env`, `process.execArgv`,
`process.allowedNodeEnvironmentFlags.has(PROXY_FLAG)`), and
the banner reads `.hint` / `.reason` from that binding. One binding;
nothing widened.
2. **The banner owns a function**, `reportSweepIncomplete(state, emit =
console.error)` — the same
words, the same order, the same stream, the same `EXIT_INCOMPLETE`. It
is extracted for exactly
one reason: `main()` reads git and the network and cannot be driven down
the
attribution-failure path offline, and a banner that dies while being
built is pinnable only by
something that builds it.
3. **A battery of its own**, 11 cases, declared in `SELF_TEST_BATTERIES`
with its floor, so a case
that stops registering names itself instead of going quiet.
## Premise readings — measured before writing, worktree at base
`d438b3a9a`
| premise | reading | when (UTC) |
|:--|:--|:--|
| **P1** — line 3087 reads `attributionFailed && rearm.hint` in `main()`
and no `rearm` exists in that scope | **HOLDS.** `git grep -n rearm`
over the file returns hits only at `:2273`-`:2281` (the `proxyRearmPlan`
return objects), `:2821`-`:2838` (`rearmProxyOrNull`'s own `const
rearm`), `:3087`-`:3088` (the banner) and `:3951`-`:3958` (self-test,
all calling `proxyRearmPlan` directly). Nothing binds `rearm` in
`main()`. | 2026-09-14T01:13Z |
| **P2** — the expression sits inside the `console.error(...)` argument,
so a throw loses the banner AND the `return EXIT_INCOMPLETE` after it |
**HOLDS.** Read at `:3084`-`:3093`: the ternary is the last operand of
the string concatenation passed as the sole argument, and `return
EXIT_INCOMPLETE;` is the next statement. | 2026-09-14T01:13Z |
| **P3** — no existing self-test case drives `main()` down the
`attributionFailed` path | **HOLDS, with one refinement worth
recording.** Probe on a scratch copy (never the worktree file; copy
removed by an `EXIT` trap, verified absent): replacing only `rearm.hint`
with an unconditionally throwing IIFE — so the throw fires exactly when
`attributionFailed` is true — left the battery **green: 317 assertions,
exit 0**. Refinement: replacing the **whole** condition
`attributionFailed && rearm.hint` with a throwing IIFE reds one
pre-existing case,
`a-mixed-sweep-attributes-the-dropped-repo-in-the-INCOMPLETE-footer-itself`,
which drives `main()` down the INCOMPLETE path for an **unaudited
repo**, never for attribution. So the banner was reached by the battery;
the `attributionFailed` branch of it was not. | 2026-09-14T01:14Z (whole
condition) and 01:17Z (hint only) |
Baseline for both probes: `node scripts/pm/check-governed-merges.mjs
--self-test` on the unmodified
tree, `✓ ... 317 assertions`, exit 0, empty stderr (2026-09-14T01:17Z).
## The battery, and the falsifiability pair
`node scripts/pm/check-governed-merges.mjs --self-test` →
`✓ check-governed-merges --self-test: 328 assertions`, exit 0, empty
stderr (2026-09-14T01:21Z).
317 before, 328 after: the 11 new cases, battery only grows, nothing
deleted or weakened.
**Ablation** (one-shot, on the committed fix, restore proven by state):
delete the `const rearm =
proxyRearmPlan({...})` binding `main()` now carries. On-disk proof, not
an editor exit code —
`git hash-object` before `eab2fbd5ec0010fe84584f8acd61d2022dc7e2cf`
(equal to the `HEAD` blob),
after `c455a42e43f704f1bd504a34cccdc1f235eca9ff`; the file still parses
(`node --check` clean), as
it did before the fix, because the defect is a runtime `ReferenceError`.
Ablated run: **exit 1, 4 failures, by name** —
```
✗ check-governed-merges --self-test — 4 failure(s)
• a-mixed-sweep-attributes-the-dropped-repo-in-the-INCOMPLETE-footer-itself
• a-sweep-whose-every-attribution-channel-fails-exits-EXIT_INCOMPLETE-not-a-crash
• ⭐ and-the-INCOMPLETE-banner-REACHES-STDERR-the-one-thing-the-crash-deleted
• and-nothing-on-either-stream-is-a-ReferenceError
```
with the card's own stack reproduced inside the failure detail:
`ReferenceError: rearm is not defined at main
(.../check-governed-merges.mjs:3128:73)`.
⚠️ Direction stated as measured, not as predicted: the ablation is
**broader** than the original
defect. Removing the binding makes the reference throw on **every**
INCOMPLETE path (the plan is
now passed as a call argument), where the shipped defect threw only when
`attributionFailed` was
true — which is why the pre-existing unaudited-path case reds here too
and did not red on `main`.
The three new end-to-end cases are the ones that answer for this card.
The six offline cases stay
green under the ablation by construction: they call the banner function
directly and do not go
through `main()`, which is exactly why the end-to-end case exists.
**Restored** (`git checkout HEAD --
scripts/pm/check-governed-merges.mjs`): worktree blob back to
`eab2fbd5ec0010fe84584f8acd61d2022dc7e2cf`, `git status --porcelain`
empty, `git diff HEAD` empty,
and the battery green again at 328 assertions, exit 0
(2026-09-14T01:23Z). No ablation artefact is
left in the tree.
## The live invocation the card measured — a reading, not the pin
`node scripts/pm/check-governed-merges.mjs --since
2026-09-13T13:00:00Z`, stdout and stderr
captured to separate files by redirect, exit captured on the command
itself (2026-09-14T01:23Z):
| string | stdout | stderr |
|:--|--:|--:|
| `attribution unavailable` | 0 | 0 |
| `merged_by names an ACCOUNT` | 1 | 0 |
| `PR #` | 3 | 0 |
| `sweep INCOMPLETE` | 0 | **1** |
| `ReferenceError` | 0 | 0 |
`LIVE_EXIT=2` — `EXIT_INCOMPLETE`.
⛔ **The `attributionFailed` path is NOT taken in this container**, so
this run does not
discriminate the fix from the defect: the re-exec worked (`ℹ️ re-exec
with --use-env-proxy ...`),
both lookups resolved through the env token, and the card's positive
control
`attribution unavailable` reads **0/0** here where the card measured
**1/0**. The INCOMPLETE this
run reports is the other kind — 4 governed repos unaudited (one stale
mirror, three with no
checkout in this container). What the run does show is that the banner
now reaches stderr and the
exit is 2 on a path that reaches the same statement. **The pin is the
self-test case**, which
drives the attribution-failure path deliberately: a fixture governed
merge in the window and
`GITHUB_API_URL` pointed at a port nothing listens on, so every channel
fails at the transport,
offline, in milliseconds.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` from the
worktree derived **34 commands**; every one was run, each exit captured
by redirect-then-capture,
each recorded as `command :: exit N`, all **exit 0**. Reconciliation:
```
Run reconciliation — 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 34 derived famil(ies) accounted for — 34 run, 0 NOT-MEASURED
(a DERIVED zero — all 34 recorded an exit code and none of them is 3).
```
Included in that 34 and green: `pnpm check:pm-governed-merges` (this
script's own self-test),
`pnpm check:pm-dispatch-gates`, `pnpm check:nul-bytes`, `pnpm
check:entry-guard`,
`pnpm check:parse-guard`, `node scripts/check-self-test-wired.mjs`,
`node scripts/check-scripts-symbol-anchors.mjs`, `node
scripts/check-declaration-mirrors.mjs`.
The tool's own accounting names what stays outside that total (50
artifact-roster families, 11
declared-wide families, 14 pending-changeset families, 1 path-scheduled
CI job and the always-runs
tail); those are CI's run, not a local claim.
Beyond the derived set, the repo-wide `grep -naP` control-character scan
over the edited file
returned nothing and `pnpm check:nul-bytes` passed over 8634 tracked
text files.
## Changeset
No changeset; this PR takes **`skip-changeset`**. What the check reads:
the changeset gate is
`pr-automation.yml`'s `changeset-check` job, and it has **no path
exemption** — its only two
exemptions are the `skip-changeset` label (read live from the PR, not
from the frozen event
payload) and the changesets release branch. The diff is one file under
`scripts/pm/`, repo tooling
that sits outside every published package directory and therefore
outside every package's
`files[]`; nothing published moves. Same declaration shape as PR #18089.
## Acceptance notes
- **Extraction scope.** `reportSweepIncomplete` is module-local, not
exported: the self-test lives
in the same module, and the exported surface (which
`scripts/pm/ci-failure.mjs` imports) is
unchanged.
- **The banner text is untouched**, deliberately, including its `#4690`
citation and the
`NODE_OPTIONS` prescription. The only textual additions are the new
battery name in
`SELF_TEST_BATTERIES`, its floor (11), and one clause appended to the
self-test's verdict
enumeration so that line stays an honest inventory.
- **`rearm.hint` is still read strictly** (no `?.`): a defensive
optional-chain would hide the next
missing binding instead of failing, and the new end-to-end case is what
catches it by name.
- Noted, not filed: the live sweep reports this worktree's own checkout
as `[stale-mirror]` because
`origin/main` moved while this card was in flight. That is the audit
working as designed, not a
defect. Carrier: none — it is a property of the container, not of the
tree.
`Clause-②: no` — no contract accept/reject behaviour changes and no
public surface widens; the
edit is repo tooling.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8)_
Co-authored-by: Claude <noreply@anthropic.com>
1 parent a26a114 commit 3aaea38
1 file changed
Lines changed: 184 additions & 20 deletions
0 commit comments