Repository navigation
fix(scripts): measure the entry-guard probe's noise floor so both derived gates share one NODE_OPTIONS - #17764
Merged
Conversation
…asserting empty stderr `check-required-contexts.mjs --verify-required-set` exits 2 = NOT VERIFIED without `--use-env-proxy` and prescribes exactly that flag. Both gates are derived for the same change surface, so they run in one batch under one NODE_OPTIONS -- and under that flag node opens every child with the UNDICI-EHPA experimental warning plus its `--trace-warnings` hint. The entry-guard case asserted a literally empty child stderr, so obeying the other gate turned it red on a tree the seat had just changed. Neither assertion was wrong, so neither is weakened. The probe now spawns an identical child that imports nothing, and compares against what that child prints: anything beyond the measured floor came from the import. Node's pid is the only normalisation. Not a shape filter: node's hint line carries no `[CODE] Warning:`, so a filter written to that shape leaves it behind -- and would swallow a module line wearing the same shape. Four controls pin this, including a module line disguised as a node warning, and the battery floor rises 2 -> 6 to hold them. Closes #15234 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
This was referenced Sep 12, 2026
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.
Closes #15234
Clause-②: no
Two gates derived for the same change surface disagreed about what
NODE_OPTIONSmay be, and following the correct advice of one turned the other red.What was wrong
check-required-contexts.mjs --verify-required-setexits2 = NOT VERIFIEDwithout--use-env-proxyand prints that remedy itself, citing #9642 for why the inference it prevents matters.dispatch-gatesderives both gates for the same change surface, so a seat runs them in one batch under oneNODE_OPTIONS. Under that flag node opens every child process with two stderr lines, before any user code runs:The entry-guard case in
check-cross-package-test-inputs.mjs --self-testasserted a literally empty child stderr. So obeying gate A turned gate B red on a tree the seat had just changed, and the natural reading is "my diff broke the entry guard". The other direction is worse because it is silent: a seat that reads gate A's exit 2 as noise never runs the required-set sweep at all, which is the entire reason #9642 exists.The fences, held
Neither assertion was the defect, and neither is weakened. The entry-guard property is still pinned; gate A's proxy advice is still printed, and
check-required-contexts.mjsis not touched by this PR.The direction chosen, and the evidence that decided it
Triage listed three candidates and deliberately chose none. Two were rejected on measurement, not taste:
Rejected — filter node's warning shape. Triage described this as ignoring lines matching
(node:NNN) [...] Warning:. Measured against the real output, that shape matches only the first of the two lines:A filter written to that description leaves the hint line behind and the case stays red. Worse, any such filter would also swallow a line the module wrote wearing the same shape.
Rejected —
--no-warnings, or stripping the flag for that spawn. Both mask real runtime warnings, and both are a per-flag allowlist: the nextNODE_OPTIONSvalue that makes node talk re-breaks the case.Chosen — measure the noise floor instead of describing it. An identical child that imports nothing is spawned first, with the same argv shape and the same inherited env. Whatever it prints is what this runtime prints unprompted; anything the real probe prints beyond that came from the import. No pattern describes the noise, so this cannot rot when node changes its warning text. The single normalisation is node's pid, which differs between the two children by construction; nothing else about the text is touched.
Four-way control (acceptance item 2)
Same tree, same commit, flag as the only variable:
--use-env-proxycheck-required-contexts.mjs --verify-required-setcheck-cross-package-test-inputs.mjs --self-test— beforeBoth gates now pass under one
NODE_OPTIONS=--use-env-proxy --max-old-space-size=4096. Gate A's exit 2 without the flag is unchanged and is not a failure of the tree: it classifies the environment.Positive controls (acceptance item 3)
The case must not be green because it looks at nothing. Four controls are added beside the two original cases and all run in both environments:
The battery floor for
the entry guard, driven for realrises2 -> 6so the controls cannot be dropped back out quietly.Ablation — the real case, not only the controls
A
console.errorwas inserted at module scope, outside the entry guard, so it runs on import. On-disk landing was proven by marker count (0before,1after) and by blob hash before and after; restore was proven by hash equality with theHEADblob and an emptygit diff HEAD, from a trap.--use-env-proxyFAIL importing this module prints NOTHING OF ITS OWNThe real case reds in both environments, so the green above was not bought with blindness.
The boundary of this measurement
The subtraction assumes the two children's runtime noise is identical apart from the pid. That holds for everything driven here, but it is an assumption about node's behaviour, not a property proved of it. If some
NODE_OPTIONSvalue ever makes the runtime write something that varies between two otherwise-identical spawns — a port, a temp path, an elapsed-time figure, any token minted per process — the two stderrs differ, that difference is attributed to the import, and the case goes red with the whole probe stderr quoted at it.That is the opposite failure from the one fixed here, and it is the safer of the two: it is loud rather than silent, and it cannot hide a real entry-guard regression — it can only manufacture a false one. It is a real boundary all the same, and it is not measured.
⭐ What was actually driven is
--use-env-proxy, plus the no-flag control, as the four-way table above records. Nothing else in theNODE_OPTIONSspace was exercised, so that flag is the single value this baseline is known to be stable under.⇒ If it ever bites, the remedy is to normalise the varying token at the line, exactly as the pid is normalised today, with the reason recorded beside it. ⛔ Not by widening the comparison into a general "ignore output" filter — that is precisely the trade this PR exists to refuse.
Reverse-read — what this makes false, zeros included
Two sentences, both re-judged in place and neither deleted:
SELF_TEST_BATTERIES['the entry guard, driven for real'] = 2— raised to6, with the reason recorded at the line.importing this module prints NOTHING— nowprints NOTHING OF ITS OWN. Read literally, the old wording was already false under the flag; that was the defect.The zeros, each one read rather than assumed:
docs/**,content/docs/**orAGENTS.mdare falsified; the entry-guard probe's stderr assertion is described nowhere outside the file.check-required-contexts.mjs; every--use-env-proxymention in the tree was read and all 21 remain true.check-published-list-mirrorsrow for this module: it governsRECOGNISED_PATH_SPELLINGSagainstAGENTS.md, which this PR does not touch. Gate re-run green.Changeset — measured, not assumed
skip-changeset, applied as a label. Root manifest isprivate: true; of the 70 published packages with afiles[], 0 ship anything under repo-rootscripts/, and the edited path is outside every package directory. Nothing published moves.验收备注
Noted, not filed: none beyond the finding below.
Filed as #17765: the same defect exists in
scripts/pm/dispatch-gates.mjs, whose own self-test asserts(imported.stderr ?? '').trim() === ''on a spawned consumer and reds identically under the flag (measured directly on that consumer shape). It is not fixed here: the landing point for this card is the two gates it names, and that file's self-test exceeds this container's 10-minute foreground cap (it timed out atBASEtoo, before this diff), so an edit there could not be verified in this run.Generated by Claude Code