Commit e4e22c7
fix(scripts): measure the entry-guard probe's noise floor so both derived gates share one NODE_OPTIONS (#17764)
Closes #15234
Clause-②: no
Two gates derived for the same change surface disagreed about what
`NODE_OPTIONS` may be, and following the correct advice of one turned
the other red.
## What was wrong
`check-required-contexts.mjs --verify-required-set` exits `2 = NOT
VERIFIED` without `--use-env-proxy` and prints that remedy itself,
citing #9642 for why the inference it prevents matters. `dispatch-gates`
derives both gates for the same change surface, so a seat runs them in
one batch under one `NODE_OPTIONS`. Under that flag node opens **every**
child process with two stderr lines, before any user code runs:
```
(node:NNN) [UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental, expect them to change at any time.
(Use `node --trace-warnings ...` to show where the warning was created)
```
The entry-guard case in `check-cross-package-test-inputs.mjs
--self-test` asserted 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.mjs` is **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:
```
MATCHED | (node:7905) [UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental, ...
UNMATCHED | (Use `node --trace-warnings ...` to show where the warning was created)
```
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
next `NODE_OPTIONS` value 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:
| gate | no flag | `--use-env-proxy` |
| --- | --- | --- |
| A `check-required-contexts.mjs --verify-required-set` | **2** (NOT
VERIFIED, unchanged) | **0** |
| B `check-cross-package-test-inputs.mjs --self-test` — before | **0** |
**1** (the spurious red) |
| B — after this PR | **0** (156 cases) | **0** (156 cases) |
Both 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:
- NEGATIVE CONTROL — importing a module that writes nothing is clean
under this runtime.
- POSITIVE CONTROL — one line the module writes to stderr still reds.
- POSITIVE CONTROL — a module line **disguised as a node warning** still
reds. This is the case that separates a measured baseline from a shape
filter: a shape filter swallows it, subtracting a measured baseline
cannot, because the baseline child never wrote it.
- POSITIVE CONTROL — a module that writes to **stdout** still reds; that
half of the assertion is an exact match and is untouched.
The battery floor for `the entry guard, driven for real` rises `2 -> 6`
so the controls cannot be dropped back out quietly.
## Ablation — the real case, not only the controls
A `console.error` was inserted at module scope, outside the entry guard,
so it runs on import. On-disk landing was proven by marker count (`0`
before, `1` after) and by blob hash before and after; restore was proven
by hash equality with the `HEAD` blob and an empty `git diff HEAD`, from
a trap.
| | no flag | `--use-env-proxy` |
| --- | --- | --- |
| mutated | **1** — `FAIL importing this module prints NOTHING OF ITS
OWN` | **1** — same single FAIL |
The real case reds in **both** environments, so the green above was not
bought with blindness.
## The boundary of this measurement
1 parent 52fa9db commit e4e22c7
1 file changed
Lines changed: 105 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
262 | 262 | | |
263 | 263 | | |
264 | 264 | | |
265 | | - | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
266 | 269 | | |
267 | 270 | | |
268 | 271 | | |
| |||
2793 | 2796 | | |
2794 | 2797 | | |
2795 | 2798 | | |
2796 | | - | |
| 2799 | + | |
| 2800 | + | |
| 2801 | + | |
| 2802 | + | |
| 2803 | + | |
| 2804 | + | |
| 2805 | + | |
| 2806 | + | |
| 2807 | + | |
| 2808 | + | |
| 2809 | + | |
| 2810 | + | |
| 2811 | + | |
| 2812 | + | |
| 2813 | + | |
| 2814 | + | |
| 2815 | + | |
| 2816 | + | |
| 2817 | + | |
| 2818 | + | |
| 2819 | + | |
| 2820 | + | |
| 2821 | + | |
| 2822 | + | |
| 2823 | + | |
| 2824 | + | |
| 2825 | + | |
| 2826 | + | |
| 2827 | + | |
| 2828 | + | |
| 2829 | + | |
| 2830 | + | |
| 2831 | + | |
| 2832 | + | |
| 2833 | + | |
| 2834 | + | |
| 2835 | + | |
2797 | 2836 | | |
2798 | | - | |
| 2837 | + | |
2799 | 2838 | | |
2800 | 2839 | | |
| 2840 | + | |
| 2841 | + | |
| 2842 | + | |
| 2843 | + | |
| 2844 | + | |
| 2845 | + | |
| 2846 | + | |
| 2847 | + | |
| 2848 | + | |
2801 | 2849 | | |
2802 | | - | |
2803 | | - | |
| 2850 | + | |
| 2851 | + | |
2804 | 2852 | | |
2805 | 2853 | | |
2806 | 2854 | | |
2807 | 2855 | | |
2808 | 2856 | | |
2809 | 2857 | | |
| 2858 | + | |
| 2859 | + | |
| 2860 | + | |
| 2861 | + | |
| 2862 | + | |
| 2863 | + | |
| 2864 | + | |
| 2865 | + | |
| 2866 | + | |
| 2867 | + | |
| 2868 | + | |
| 2869 | + | |
| 2870 | + | |
| 2871 | + | |
| 2872 | + | |
| 2873 | + | |
| 2874 | + | |
| 2875 | + | |
| 2876 | + | |
| 2877 | + | |
| 2878 | + | |
| 2879 | + | |
| 2880 | + | |
| 2881 | + | |
| 2882 | + | |
| 2883 | + | |
| 2884 | + | |
| 2885 | + | |
| 2886 | + | |
| 2887 | + | |
| 2888 | + | |
| 2889 | + | |
| 2890 | + | |
| 2891 | + | |
| 2892 | + | |
| 2893 | + | |
| 2894 | + | |
| 2895 | + | |
| 2896 | + | |
| 2897 | + | |
| 2898 | + | |
| 2899 | + | |
| 2900 | + | |
| 2901 | + | |
| 2902 | + | |
| 2903 | + | |
| 2904 | + | |
| 2905 | + | |
| 2906 | + | |
| 2907 | + | |
| 2908 | + | |
| 2909 | + | |
2810 | 2910 | | |
2811 | 2911 | | |
2812 | 2912 | | |
| |||
0 commit comments