Skip to content

fix(pm): the proxy re-exec guard is the caller's name, and it speaks when it suppresses - #19170

Merged
os-tesla merged 2 commits into
mainfrom
claude/issue-18989-governed-merges-plan-guard
Sep 19, 2026
Merged

os-tesla merged 2 commits into
mainfrom
claude/issue-18989-governed-merges-plan-guard

Conversation

@os-tesla

Copy link
Copy Markdown
Collaborator

Fixes #18989

Clause-②: no

scripts/pm/check-governed-merges.mjs carries its own proxyRearmPlan and that plan read ONE
hard-coded guard name — this file's. scripts/pm/ci-failure.mjs imports that plan and set the
same variable on the child it re-execs. So either tool's guard, once inherited by the other,
answered "already re-armed" for a run that had never re-armed; the suppressed run then took the
bypassed route and answered 401 Bad credentials on every endpoint, with no line printed anywhere
to say why. That is the #18939 mechanism in a second plan — the one PR #18980 does not touch —
and, for the plan itself, one route with two owners.

What changed, per file

scripts/pm/check-governed-merges.mjs

  • proxyRearmPlan({ …, guard = PROXY_REARM_GUARD } = {}) — the guard is the CALLER's name, and
    the default stays this file's own, so its own re-exec is unchanged.
  • The guarded branch SPEAKS: hint: true, guarded naming the suppressor, and a reason naming
    the variable to unset, the proxy the request was supposed to take, and the 401 the silence
    would otherwise be read as. It can only fire on the anomaly — a real re-exec's child carries
    the flag in execArgv and is answered by the branch above.
  • Both live call sites (rearmProxyOrNull, and the INCOMPLETE banner's plan) hand it
    guard: PROXY_REARM_GUARD explicitly: for a plan another tool imports, the default is not the
    contract.
  • rearmProxyOrNull now PRINTS the hint instead of returning through the silent !rearm.rearm
    line. Both hint branches — an older node, and a guard already set — previously reached a reader
    only through the sweep's INCOMPLETE banner, and only when the incompleteness was about
    attribution; every other mode bypassed the proxy in silence.

scripts/pm/ci-failure.mjs

  • Declares and sets its OWN OS_CI_FAILURE_PROXY_REARMED, spelled once, and hands it to the plan
    per call. The re-exec already spawned under that identifier, so the child now inherits this
    tool's name and nobody else's.
  • Imports the sibling's spelling as GOVERNED_MERGES_GUARD, so the self-test pins the two apart
    against the real constant rather than a hand-copied string: a rename on either side reds.

The arm taken: the copy STAYS, and the reason is measured rather than assumed

The card asks the taker to keep the copy only if the header's stated verbatim-copy reason still
holds, and to delete it and import the patrol's plan otherwise. Both halves were measured.

  1. The premise as written is false. check-governed-merges.mjs's header states no
    verbatim-copy reason, and the plan's own docblock said only "Pure, so every branch is
    offline-testable". The copy-verbatim argument lives in check-half-states.mjs — it is about
    copying THAT file into a sibling repo, and it bars the reverse edge, not this one.
  2. The import is refused by something stronger: it deadlocks the process. Measured
    2026-09-19 on a throwaway copy of scripts/ outside the repo (nothing in the tree under test
    was mutated):
leg invocation exit
control, unmodified check-governed-merges.mjs --test AGENTS.md 3 — the GOVERNED verdict
plus import { proxyRearmPlan } from './check-half-states.mjs' at module scope same argv 13 — "Detected unsettled top-level await at check-half-states.mjs:10044"
plus const patrol = await import('./check-half-states.mjs') inside selfTest() --self-test 13 — unsettled top-level await at this file's own await selfTest()

The cycle is short and live: check-half-states.mjs:10044 resolves its H43 register at module
scope with await loadGovernedRegister(), which awaits
import('./check-governed-queue-guard.mjs'), which imports THIS file at module scope. The lazy
door that check-governed-queue-guard.mjs's CONTRACT_REVIEW_LABEL describes is shut here too,
because this file's --self-test dispatch is itself a module-scope await. Importing would
therefore not tidy the duplication, it would take check:pm-governed-merges — the governed-surface
instrument in most derived unions — down with it, plus the patrol and the queue guard.

So the copy stays, and the triage's 「修法若只统一守卫名而不处理副本,下一次分叉照旧发生」 is answered by
DECLARING it: the plan's docblock names its owner, states both measured exits, and the self-test
pins the mirror to that owner the way CONTRACT_REVIEW_LABEL is pinned — by reading the owner's
SOURCE off disk (a constant asserting against itself proves nothing), and by pinning the REASON,
so when the patrol's module-scope await goes away the failing case says to make this a real import.

Escalation condition re-measured in the worktree: grep -rn 'function proxyRearmPlan' scripts/
returns exactly 2 definitions (check-governed-merges.mjs, check-half-states.mjs). No third
copy, so the deliverable stayed as graded.

The A/B — the card's own repro, read-only and offline

proxyRearmFor(['--pr','9774'], { env: { HTTPS_PROXY, GUARD: '1' } }) through a harness that
imports the module (no dispatch, no socket, no argv). Leg A is the base files restored with
git checkout HEAD~1 --, leg B this branch; the restore was verified byte-identical
(git hash-object equals the HEAD blob for both files, git diff HEAD empty).

guard set in the environment base (leg A) this branch (leg B)
none rearm: true rearm: true
OS_GOVERNED_MERGES_PROXY_REARMED=1 rearm: false, hint: false, reason "already re-armed once this run" — the silent cross-suppression rearm: true — the sibling's guard no longer silences this tool
OS_CI_FAILURE_PROXY_REARMED=1 (did not exist) rearm: false, hint: true, guarded: OS_CI_FAILURE_PROXY_REARMED, reason naming the variable, the proxy and the 401
OS_HALF_STATES_PROXY_REARMED=1 — rearm: true — the patrol's name is inert here too

Leg A is the defect exactly as filed: a tool that never re-armed, told it had, printing nothing.

Self-tests: both directions, both sides

reading before after
check-governed-merges.mjs --self-test 392 assertions, exit 0 410 assertions, exit 0
its battery roster / pinned roster size 28 batteries 29 (new: the #18989 guard set, floor 18)
ci-failure.mjs --self-test exit 0 exit 0
its proxyRearmFor: the transport trap battery floor 7 floor 17

The ci-failure count is measured, not asserted: with that floor mutated 17 to 18 through
scripts/ablation-replace.mjs, the run reds with "registered 17 case(s), below its pinned floor
of 18" (exit 1), and the tool restored the file to a blob equal to HEAD with git diff HEAD
empty. A first attempt at that probe passed the wrong flags, printed usage and exited 0 without
mutating anything — a no-op reading, discarded and re-run rather than believed.

Gates

Derived in the worktree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (the script takes the change set itself; the derivation names the repo and commit
d904ab5) — 32 families, all 32 run, every exit code captured before any pipe. Reconciled with
--ran: "32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED (a DERIVED zero — all 32
recorded an exit code and none of them is 3)". Every one exited 0, including
pnpm check:pm-governed-merges (this file's own self-test, 410 assertions), ci-failure.mjs --self-test, check-self-test-wired, check-self-test-workflow-commands,
check-scripts-symbol-anchors, check-declaration-mirrors and check:nul-bytes.
pnpm check:pm-dispatch-gates took 1025.1s on this box (1866 cases pass) — well past the
430-450s the dispatch estimated, worth knowing for anyone budgeting a foreground run while other
agents share the container.

Repo-wide sweeps (pnpm lint and the rest of the farm) are CI's run, not this branch's. A scan
for control bytes over both files (grep -naP over the C0 set plus DEL) exits 1 with no hits.

Acceptance notes


Generated by Claude Code

…when it suppresses

`check-governed-merges.mjs` carries its own `proxyRearmPlan` and read ONE
hard-coded guard name — its own. `ci-failure.mjs` imports that plan and set the
same variable on the child it re-execs, so either tool's guard, once inherited by
the other, answered "already re-armed" for a run that had never re-armed. The
suppressed run then took the bypassed route and answered 401 Bad credentials on
every endpoint: a self-consistent "the credential is dead" story, printed nowhere
at all.

Both halves are closed, in the shape PR #18980 landed on the patrol's plan:

- the plan takes `guard`, defaulting to this file's own name, and both live call
  sites hand it that name explicitly;
- the guard branch SPEAKS — `hint: true`, `guarded: <name>` and a reason naming
  the variable, the proxy and the 401 it would be mistaken for;
- `rearmProxyOrNull` prints the hint instead of returning through the silent
  `!rearm` line, so every mode says it bypassed the proxy, not only the sweep's
  INCOMPLETE banner;
- `ci-failure.mjs` declares and sets its OWN `OS_CI_FAILURE_PROXY_REARMED`, and
  imports the sibling's spelling as `GOVERNED_MERGES_GUARD` so its self-test can
  pin the two apart.

The copy itself stays a copy, and the reason is measured rather than assumed. A
module-scope import of the owner's plan exits 13 ("Detected unsettled top-level
await" at `check-half-states.mjs`'s `GOVERNED_REGISTER`) against a control exit 3,
and a lazy `await import()` inside `selfTest()` exits 13 too, because this file's
`--self-test` dispatch is itself a module-scope await. So the duplication is
declared a MIRROR and pinned to its owner the way `CONTRACT_REVIEW_LABEL` is: by
reading that file's source off disk, plus the REASON, so the next divergence reds
instead of happening silently.

Both self-tests pin both directions: own guard suppresses, the sibling's name does
not, and the suppressed branch names its suppressor.

Claude-Session: https://claude.ai/code/session_01W5y9kRg1YtYaMQYExVLRc2
Co-authored-by: Claude <noreply@anthropic.com>
The success line is what a reader of a green run is handed, and it enumerated
every other pinned set. Adds the guard clause: the caller's name, the speaking
suppression, the inert runner leg, and the mirror pinned to its owner's source.

⛔ No backticks in that template literal — one terminates it and the file stops
parsing (caught here by `node --check` before the gate ran).

Claude-Session: https://claude.ai/code/session_01W5y9kRg1YtYaMQYExVLRc2
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d904ab5e14596b2687055a4a4314b48db5485224

In-seat review by the domain:skills seat at the contract-review tier (served tier read at seating, marker 5738863635) — the record this lane owes on every round it delivers, Clause-②: no included. Verified against GitHub and the fetched branch (refs/pm/18989), ⛔ not against the report: two files, scripts/pm/check-governed-merges.mjs (+174 / −5) and scripts/pm/ci-failure.mjs (+80 / −3), 262 changed lines; two commits, both trailer pairs model-free; every hunk lies in the proxy plan, its constants, its two call sites and the self-test batteries — git diff origin/main shows no hunk on testVerdict, landsByHumanMerge, the size limb or the default-mode listing (#19036's region, still SERIAL behind this PR). check-governed-merges.mjs --pr 19170: NOT governed, 262 ≤ 5000.

① Derived judgments

  • Accept set / public surface: none moves. proxyRearmPlan gains an optional guard parameter defaulting to the file's own name — every existing call reads as before; the guarded branch now returns hint: true + guarded and a reason naming the variable, the proxy and the 401 it would be mistaken for; rearmProxyOrNull prints that hint instead of returning silently (the one hunk past the dispatch's named region — same defect class, declared in the report, accepted: without it 「speaks when it suppresses」 would be false of the file in --pr / --branch / sweep modes). ci-failure.mjs sets its OWN OS_CI_FAILURE_PROXY_REARMED (module-local, spelled once — the landed consumer shape; nothing imports that file) and hands it to the plan; it imports the sibling's spelling only to pin the two apart. On the head: grep -rn 'function proxyRearmPlan' scripts/ → exactly two definitions; guard names per file OS_GOVERNED_MERGES_PROXY_REARMED · OS_CI_FAILURE_PROXY_REARMED · OS_HALF_STATES_PROXY_REARMED.
  • The card's decision rule, tested on its terms and refuted by measurement (recorded in the PR body with its table, as the checklist requires): the header states no verbatim-copy reason, and importing the patrol's plan deadlocks the process both ways (exit 13, unsettled top-level await — the cycle governed-merges → half-states → queue-guard → governed-merges), so the copy stays and is DECLARED a mirror pinned to its owner's source (guard spelling, the owner's guard parameter, and the module-scope await that is the reason) with a remedy sentence that says to make it an import when that line goes. The triage's escalation condition (a third copy) is not met.
  • Pins read in the diff: own name suppresses; the sibling's and the patrol's names do NOT; the suppressor is reported machine-readably; the no-proxy Actions-runner leg stays inert; an already-flagged run is answered by the flag; both structural pins read the files' own source. Battery roster 28 → 29, floor 18; ci-failure.mjs battery floor 7 → 17 with the floor probe driven red and restored (byte-identical, git diff HEAD empty).

② Semver level

None owed — scripts/pm/** ships in no package; skip-changeset applied and read back.

③ Boundary flags

  • Dev deviations (4) read and accepted as stated above: the module-local guard; the rearmProxyOrNull hunk; the second commit adding the clause to the self-test's success line; check:pm-dispatch-gates at 1025 s on a shared box (a reading, not a red).
  • out_of_scope_findings (2, both 「noted, not filed」): the mirror accepts only PROXY_FLAG where its owner also accepts NODE_USE_ENV_PROXY=1 — a divergence now visible at the source pin, no defect; the shared checkout's --self-test exit 3 without node_modules — documented prerequisite. Neither is one of the three classes; not filed.
  • Gates: 32 derived, 32 run, 0 NOT-MEASURED per --ran; CI on the head read before any landing act (three checks in progress at this record's writing).

Implemented-by: claude/issue-18989-governed-merges-plan-guard
Reviewed-by: session_01W5y9kRg1YtYaMQYExVLRc2

VERDICT: PASS


Generated by Claude Code

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

Labels

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

Projects

None yet

2 participants