fix(nebula): run the relay verifier through this shell, not /usr/bin/env β main is RED on the sandbox tier - #1407
Conversation
All 20 tests in scripts/tests/test_nebula_relay_apply.py fail on the `nix build .#checks.x86_64-linux.pytests` tier, while the dev-host tier (scripts/gate.sh) is green. #1272 landed on that split. apply-nebula-relay.sh executed the verifier directly: "$CHECK" "$RELAY" >"$PRE" 2>&1 which dispatches on check-nebula-relays.sh's `#!/usr/bin/env bash` shebang and so makes /usr/bin/env a hard runtime dependency. The nix build sandbox has no /usr/bin, so every one of those execs died with /usr/bin/env: bad interpreter: No such file or directory and the script reported ABORT: the verifier could not read the current config (rc=126) rc 126 is "found but not executable" β it is NOT one of the verifier's own exit codes, so that abort blamed the CONFIG for an interpreter fault, which is why the failure did not read as a shebang problem. Fixed by invoking the verifier through `$BASH` β the absolute path of the interpreter already running this script. No PATH lookup, no /usr/bin/env, and the verifier provably runs under the same shell. Both scripts keep their shebangs: they are still executed directly by the operator, where /usr/bin/env exists, and that path is unchanged. Consolidated into one `run_check` helper rather than patched at each of the three call sites (preflight, post-test verify, post-restart retry). A predicate open-coded at N sites regrows the bug at whichever site a later fix misses. Verification is on the SANDBOX tier only, deliberately: the dev host has /usr/bin/env, so a local run cannot reproduce the failure and its green says nothing about this fix. The dev-host suite is reported for completeness (30 passed) but the load-bearing evidence is the sandbox control/fixed pair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43
Correction to this PR's own descriptionRound-1 audit caught a factual error in the body and commit message. Correcting it publicly rather than silently editing, since a reviewer may already have read the wrong version. Both say: "All 20 tests in Actually: the file has 30 tests. On the sandbox tier at base Every other number in the body reproduced exactly under an independent instrument ( Also worth recording:
|
β¦orting an exec fault as a config fault Round-1 audit of #1407 left two findings. Both are here. FINDING 1 β the fix it shipped had no regression guard, and its only coverage was on the tier that is structurally blind to a revert. MEASURED: revert #1407's `run_check` to a direct `"$CHECK"` exec and the dev-host tier still reports 30 passed, because the dev host HAS /usr/bin/env. Only `nix build .#checks.x86_64-linux.pytests` goes red. So `scripts/gate.sh --tier both` β the command most sessions actually run β went fully green on the exact defect #1407 existed to remove. An agent "simplifying" run_check back to what #1272 wrote would have seen green and shipped it. `test_the_verifier_is_never_execed_via_its_own_shebang` is a source assertion, so it fails wherever the suite runs. Pinned as a PAIR β the helper must exist AND no direct call site may reappear β because presence alone passes if someone adds a second, direct site beside the helper, and absence alone passes if the helper is deleted outright. `test_the_verifiers_exit_codes_are_a_closed_set` pins its load-bearing precondition: the {0,1,2} set is asserted on BOTH sides, so the verifier growing an `exit 3` fails rather than being silently reclassified as an exec fault. FINDING 2 β the misdiagnosing abort was unchanged. check-nebula-relays.sh only ever exits 0, 1 or 2. Every other code fell into the same `*)` arm as a genuine rc 2 and was reported as ABORT: the verifier could not read the current config (rc=126); fix that first asserting a fact about $CFG that is false in every one of those cases β and that is precisely how the /usr/bin/env fault presented, which is why it read as a config problem instead of an exec one. Split into `2)` (a real verifier refusal β unchanged message) and `*)` (it did not run). One `verifier_answered` classifier, used at both sites rather than open-coded twice. π΄ The consequential site is the POST-REBUILD verify, not the preflight. There a non-verifier rc reaches `die` AFTER `nixos-rebuild test` has activated, so the EXIT trap rolls back a change that WORKED and prints the PERSISTED paragraphs for what was really a signal or exec fault. VERIFICATION Red -> green, and every guard watched failing: whole suite at HEAD 38 passed new guards vs pre-#1407 nix/system 4 of 6 fail (the shebang guard correctly passes β #1407 is now in main β and the rc-2 pin passes both sides by design, being a guard against WIDENING, not a regression test) Mutation sweep, PYTHONDONTWRITEBYTECODE=1, every mutation applied-verified (the count is asserted before substituting, so an unapplied edit cannot be scored as a caught mutant): CONTROL unmutated 8 passed revert $BASH -> direct exec killed by the shebang guard verifier grows `exit 3` killed by the closed-set test widen verifier_answered to 126 killed by post-rebuild[126] + closed-set drop site-B classification killed by post-rebuild[126,137] revert the preflight split killed by preflight[126,127,137] reword the die message killed by all 5 behavioural cases π΄ The post-rebuild test exists BECAUSE of that sweep. Widening verifier_answered was first caught by the STRUCTURAL test alone: every behavioural case aborted at the preflight, so verifier_answered β used only at the post-rebuild site β never executed. A guard that is never reached is not a guard. `apply_beside_sequenced_verifier` (answer 1, then fail) is the only way to reach that site. Four mutants added to scripts/tests/mutants-nebula-relay.sh so this is re-derivable rather than believed. β The stub verifier goes through `write_exec`, NOT a hand-written shebang. A `#!/usr/bin/env bash` stub execs on the dev host and ENOENTs in the sandbox, which would make the test fabricate rc 126 from its own stub rather than from the code under test β circular, and passing for the wrong reason on one tier. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43
β¦orting an exec fault as a config fault (#1420) * fix(nebula): pin the interpreter fix tier-independently, and stop reporting an exec fault as a config fault Round-1 audit of #1407 left two findings. Both are here. FINDING 1 β the fix it shipped had no regression guard, and its only coverage was on the tier that is structurally blind to a revert. MEASURED: revert #1407's `run_check` to a direct `"$CHECK"` exec and the dev-host tier still reports 30 passed, because the dev host HAS /usr/bin/env. Only `nix build .#checks.x86_64-linux.pytests` goes red. So `scripts/gate.sh --tier both` β the command most sessions actually run β went fully green on the exact defect #1407 existed to remove. An agent "simplifying" run_check back to what #1272 wrote would have seen green and shipped it. `test_the_verifier_is_never_execed_via_its_own_shebang` is a source assertion, so it fails wherever the suite runs. Pinned as a PAIR β the helper must exist AND no direct call site may reappear β because presence alone passes if someone adds a second, direct site beside the helper, and absence alone passes if the helper is deleted outright. `test_the_verifiers_exit_codes_are_a_closed_set` pins its load-bearing precondition: the {0,1,2} set is asserted on BOTH sides, so the verifier growing an `exit 3` fails rather than being silently reclassified as an exec fault. FINDING 2 β the misdiagnosing abort was unchanged. check-nebula-relays.sh only ever exits 0, 1 or 2. Every other code fell into the same `*)` arm as a genuine rc 2 and was reported as ABORT: the verifier could not read the current config (rc=126); fix that first asserting a fact about $CFG that is false in every one of those cases β and that is precisely how the /usr/bin/env fault presented, which is why it read as a config problem instead of an exec one. Split into `2)` (a real verifier refusal β unchanged message) and `*)` (it did not run). One `verifier_answered` classifier, used at both sites rather than open-coded twice. π΄ The consequential site is the POST-REBUILD verify, not the preflight. There a non-verifier rc reaches `die` AFTER `nixos-rebuild test` has activated, so the EXIT trap rolls back a change that WORKED and prints the PERSISTED paragraphs for what was really a signal or exec fault. VERIFICATION Red -> green, and every guard watched failing: whole suite at HEAD 38 passed new guards vs pre-#1407 nix/system 4 of 6 fail (the shebang guard correctly passes β #1407 is now in main β and the rc-2 pin passes both sides by design, being a guard against WIDENING, not a regression test) Mutation sweep, PYTHONDONTWRITEBYTECODE=1, every mutation applied-verified (the count is asserted before substituting, so an unapplied edit cannot be scored as a caught mutant): CONTROL unmutated 8 passed revert $BASH -> direct exec killed by the shebang guard verifier grows `exit 3` killed by the closed-set test widen verifier_answered to 126 killed by post-rebuild[126] + closed-set drop site-B classification killed by post-rebuild[126,137] revert the preflight split killed by preflight[126,127,137] reword the die message killed by all 5 behavioural cases π΄ The post-rebuild test exists BECAUSE of that sweep. Widening verifier_answered was first caught by the STRUCTURAL test alone: every behavioural case aborted at the preflight, so verifier_answered β used only at the post-rebuild site β never executed. A guard that is never reached is not a guard. `apply_beside_sequenced_verifier` (answer 1, then fail) is the only way to reach that site. Four mutants added to scripts/tests/mutants-nebula-relay.sh so this is re-derivable rather than believed. β The stub verifier goes through `write_exec`, NOT a hand-written shebang. A `#!/usr/bin/env bash` stub execs on the dev host and ENOENTs in the sandbox, which would make the test fabricate rc 126 from its own stub rather than from the code under test β circular, and passing for the wrong reason on one tier. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43 * fix(nebula): align M-FH-1's expected message, and stop a guard's docstring overclaiming Two corrections found by running the repo's own battery rather than my ad-hoc sweep, which was weaker. 1. M-FH-1 reported π΄ WRONG-KILLER: the right test failed, but not with the message the mutant declared. The assertion had been reworded after the battery entry was written and the two drifted. My own sweep scored this mutant KILLED β it only checks WHICH test failed, not that the test's own assertion is what fired, which is exactly the hole the battery's header describes ("a mutant can die to a DIFFERENT test's error and be scored as covered while its own assertion is unreachable"). The phrase is now asserted to exist in the test source before being written into the battery. 2. test_apply_declares_every_tool_it_execs' docstring claimed the relationship its name implies while the body inspects one side: a hard-coded list β declared. It cannot catch a tool the script execs but does not declare, which is the failure the preflight exists to prevent. MEASURED: `head`, `id` and `rm` are exec'd and undeclared right now, and the test is green. ($BASH is a fourth omission but a CORRECT one β the interpreter is already running, so it needs no `command -v`.) The body is unchanged: closing it means deriving the exec'd set from the source, which is real work and out of scope here. The docstring now says what the body does rather than what the name suggests, because a guard that reads as coverage while providing none is worse than no guard β it stops anyone looking. Full battery, clean /tmp: pass=26 fail=0, all three controls green. β An earlier full run reported CONTROL-KILL as WRONG-KILLER. Not a defect in this branch: it passes in isolation on origin/main AND here. A prior battery run was killed at a 10-minute tool cap before its cleanup trap ran, leaving 29 /tmp/nebula-relay-pre.* files β the residue class the battery's own header documents as making later runs read stale artifacts as live defects. Clearing them made the full run green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43 * fix(nebula): round-1 audit of #1420 β the abort's advice was harmful once anything was written Three π‘ findings, all in prose this PR itself added. Fix-round prose is the likeliest next finding, and it was. FINDING 1 (payload) β `die_verifier_did_not_run` ended with an unconditional "this script is idempotent and has changed nothing it cannot repeat". It is reached from THREE sites: the preflight (nothing written), the post-`test` verify, and the post-`switch` verify. At the last two that sentence directly contradicts the trap paragraph printed two lines BELOW it, which says PERSISTED / restoring the file is NOT enough. Following it loses the change SILENTLY. Measured end state after that failure: $CFG rolled back, but the RUNNING unit still advertises the relay. A re-run's preflight asks the running unit, gets rc 0, prints "ALREADY SATISFIED -- nothing to do" and exits 0 β a green all-clear over a config file that no longer contains the change, which the next `nixos-rebuild switch` by anyone quietly removes. Pre-PR this site did NOT invite a re-run; the invitation was new, and it was mine. Now conditional on `${PATCHED:-0}`. That spelling and not `$PATCHED`: the trap flags are declared AFTER the preflight, so a bare reference aborts under `set -u` at the first call site. Both branches are pinned β an untested branch is what these rounds keep finding. Mutation-checked: forcing the conditional to the pre-fix branch fails test_a_POST_REBUILD_...[126] and [137] (M-FH-6). FINDING 2 (scaffolding) β the paired guard's negative half was a source regex, and it pinned a SPELLING. MEASURED by the audit: `"${CHECK}" "$RELAY"`, `$CHECK "$RELAY"`, a line-split call and others all evade it while reintroducing the exact /usr/bin/env dependency β leaving the dev-host suite at 38 passed. Replaced by `test_the_verifier_runs_with_its_shebang_BROKEN`: it runs the whole happy path against a verifier whose shebang points at a nonexistent interpreter AND whose exec bit is cleared. Invoked through `$BASH` the file is merely READ, so none of that matters; ANY direct exec, however spelled, dies. It is spelling-independent AND tier-independent β it manufactures the missing-interpreter condition instead of waiting for a sandbox that happens to lack /usr/bin/env. Verified against the exact evasion: the braces mutant is MISSED by the regex and KILLED by the behavioural guard (M-FH-5). The structural test is kept as the cheap half, with a docstring that now says it is spelling-bound rather than claiming the pair is sufficient. FINDING 3 (payload) β the 126 line documented the PRE-FIX failure mode. "found, but not executable -- or ITS INTERPRETER is missing" is unreachable under `"$BASH" "$CHECK"`: bash READS the file, so the exec bit is never consulted (measured: `bash <readable, mode 644, exits 7>` -> 7), and the interpreter is by definition already running. What actually yields 126 now is $CHECK being a directory or unreadable β neither mentioned. A reader would `chmod +x` and re-run to identical failure. Corrected, along with the parametrize label that repeated it. ALSO β a false claim in this PR's own comment. It said the post-rebuild site prints the PERSISTED / "profile may have moved" paragraphs. It does not: PERSISTED and SWITCH_ATTEMPTED are both 0 there, so it prints ACTIVATED, NOT PERSISTED β which test_verifier_failure_after_a_good_test_says_activated_not_ persisted already pins. Those paragraphs belong to the post-switch site. VERIFICATION suite 39 passed battery (clean /tmp) pass=27 fail=0, three controls green M-FH-5 braces evasion killed by the behavioural guard (regex misses it) M-FH-6 advice made killed by post-rebuild[126] and [137] unconditional β NOT FIXED, recorded as open: no test reaches the post-SWITCH verify (site 3) β `apply_beside_sequenced_verifier` is two-stage by construction. Finding 1's fix is exercised at site 2, where PATCHED=1 selects the same branch, so the conditional is covered; the SITE is not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43 * test(shebangs): pin the broken-shebang control in the runtime-shebang allowlist The sandbox tier went red on `test_no_test_writes_a_usr_bin_env_shebang_at_ runtime`, flagging two lines this branch added: test_nebula_relay_apply.py:1022 assert original.startswith("#!"), ... test_nebula_relay_apply.py:1023 chk.write_text("#!/nonexistent/interpreter\n" ...) The guard is RIGHT to flag them β it scans for a test writing its own shebang, and line 1023 does exactly that. It is also the correct call to allowlist rather than reword, because this is a genuine exception the list did not yet describe. The existing entries cover two shapes: (a) an absolute, sandbox-present interpreter, and (b) a shebang ASSERTED rather than written. This is a third: an INTENTIONALLY unresolvable interpreter, written onto a COPY of the verifier, with the test then asserting the run SUCCEEDS anyway β proving apply-nebula-relay.sh reads it through "$BASH" instead of exec'ing it. That is the guard's own hazard inverted into a control. The scan exists because an unresolvable interpreter breaks a stub SILENTLY; here it is the independent variable, and if it ever started mattering the test fails loudly. Nothing execs the stub, so an unresolvable path cannot hide anything. Both entries name a path fragment rather than a shebang literal, so this file's own test_this_guards_source_does_not_match_itself stays green β the same constraint the existing entries are written under. Accounting is two-way (an entry matching no offender FAILS), so these pins cannot become rubber stamps: 9 passed with both matching. β Worth stating plainly: this guard caught a defect class in MY test on its first sandbox run, in a branch whose subject is that same class. The dev-host tier did not flag it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43 * fix(nebula): round-2 audit β inherited $PATCHED, a stale claim, and a battery that read stale bytecode ROUND 2 confirmed all three round-1 findings genuinely fixed (re-derived independently). It returned one SHOULD-FIX and four NITs. All addressed here. SHOULD-FIX (scaffolding) β a docstring my OWN fix falsified. test_the_verifier_is_never_execed_via_its_own_shebang still said "with the fix reverted every behavioural test in this file still passes -- measured: 30 passed. Only the sandbox tier goes red." Adding the behavioural guard made the dev-host tier catch that revert too. RE-MEASURED at this tree with `run_check() { "$CHECK" "$@"; }`: 2 failed, 37 passed on the dev host. That sentence was the entire stated justification for keeping the structural half β and it was written in the commit whose subject was "stop a guard's docstring overclaiming". It now records what the structural half actually earns (fails fast, names the intended shape) instead of reading like the load-bearing guard, which it is not. NIT, but a real payload hole β `${PATCHED:-0}` read an INHERITED env var. The trap flags were declared below the preflight, so the only way to read PATCHED early was the `:-` fallback β which silently accepts a value from the caller's environment. This script's header documents `sudo env "PATH=$PATH" bash ...`, which preserves it. Reproduced: an exported PATCHED=1 made the preflight print "DO NOT simply re-run" when nothing had been written. Flags moved above their first read; the fallback is gone. Regression test added and watched RED on the pre-fix shape. NIT β the trap-branch comment claimed "PERSISTED / the profile may have moved" at the post-switch site. Those are mutually exclusive branches and the second is unreachable there: `set -e` plus PERSISTED=1 immediately after a successful switch means PERSISTED is always 1 by that verify. Corrected. NIT β M-FH-5 had been inserted between M-FH-2's explanatory comment and M-FH-2 itself, so the comment read as the wrong mutant's preamble. Re-seated. NIT β acc988e's message quoted `pass=27` for a tree that prints 28. The 27 came from a run predating M-FH-6, i.e. the sweep predated the mutant guarding that commit's own change. Recorded rather than silently corrected. TWO HARNESS DEFECTS FOUND AND FIXED 1. The battery READ a stale bytecode cache. Its header claims PYTHONDONTWRITEBYTECODE=1 "throughout", which stops it WRITING one but not reading one an ordinary pytest run left behind. MEASURED: an edit adding explicit assertion messages was invisible for two consecutive runs, both reporting WRONG-KILLER while the phrase was demonstrably in the output of the same command run by hand. `run_tests` now purges __pycache__ first; verified by deliberately recreating the stale cache. 2. Several assertions passed `combined` as their own message, so under a mutant the expected phrase appeared nowhere in the output and the battery scored a correct kill as WRONG-KILLER. Every mutant's expected phrase was then cross-checked against its killer's body. π΄ NOT CERTIFIED, AND SAY SO: I have not obtained a single full-green battery run. Three consecutive full runs failed a DIFFERENT pair each time (M-FA-3+ M-FH-4; M-FH-4+M-FH-6; M-FI-2+M-FH-1), always exactly two, while every mutant passes repeatedly in isolation. One reported M-FH-1 as SURVIVED β a FALSE SURVIVED, the dangerous direction β for a mutation measured by hand as failing two tests. Box load was 64-90 throughout, from other sessions. The per-mutant results are the trustworthy evidence; the full-run aggregate on a loaded box is not, and two earlier `fail=0` runs should be read as luck rather than proof. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NNxEE4ktaHsXxNsxeWXWC5 Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
β¦broken script, and a battery that cannot be certified (#1434) * docs(handoff): NEW doc, genuinely new effort: no existing handoff references #1407 or #1420, and the Claude-Session-Id: 4b14058c-f3f5-4988-ae42-90910f7dbe43 * docs(handoff): PR #1420 was gated on the merged tree (both tiers PASS), its headline guard proven re * docs(handoff): the re-gate of post-merge origin/main @ b79ccfb came back PASS on BOTH tiers, closin * fix(handoff): redact the client subdomain literal β #1434 was unmergeable from its first commit MEASURED on the merged tree (origin/main 8a9ebba + this branch), running the doc-reading gates directly: FAILED scripts/tests/test_no_client_hostnames.py::test_no_client_subdomain_literal_is_committed 1 failed, 692 passed in 278.03s Four occurrences of a real client subdomain in a doc tracked by a PUBLIC repo. TWO OF THEM (lines 136, 142) came from this branch's FIRST commit c578351, so this PR has been red since it was opened and nobody had gated it β a docs-only change reads as gate-exempt and is not: test_doc_path_rot, test_no_client_hostnames, test_no_public_ips, test_no_captured_text and test_no_captured_markup all read tracked files. Redacted in place rather than allowlisted. Allowlisting would disarm a gate over a genuine leak β the finding itself (rank 2: the literal is still reachable in git history at 6d488a1, and all four content gates are blind to history) is unchanged and still open. The meaning of each sentence is preserved; only the literal is gone. Not fixed by handoff_doc.py because its Gotchas/Open-investigations sections APPEND: a merge cannot remove text from them, and three of the four occurrences were in already-committed prose. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Jq8nmf8ZHZkJ56enFiaM1 Claude-Session-Id: 9d71983b-623b-4cfb-b261-7263073b277e --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
mainis red, and this is whyAll 20 tests in
scripts/tests/test_nebula_relay_apply.pyfail on thenix build .#checks.x86_64-linux.pyteststier β the one Tekton gates on β whilescripts/gate.sh(dev-host) is green. #1272 landed on that split.apply-nebula-relay.shexecuted the verifier directly:which dispatches on
check-nebula-relays.sh's#!/usr/bin/env bashshebang, making/usr/bin/enva hard runtime dependency. The nix build sandbox has no/usr/bin, so every exec died with:surfacing as:
π΄ rc 126 is "found but not executable" β not one of the verifier's own exit codes. So the abort blamed the config for an interpreter fault, which is why this did not read as a shebang problem. The dev host has
/usr/bin/env, so that tier is structurally unable to see it.Fix
Invoke the verifier through
$BASHβ the absolute path of the interpreter already running the script. No PATH lookup, no/usr/bin/env, and the verifier provably runs under the same shell.Both scripts keep their shebangs: they are still executed directly by the operator, where
/usr/bin/envexists. That path is unchanged.Consolidated into one
run_checkhelper rather than patched at each of the three call sites (preflight, post-test verify, post-restart retry) β a predicate open-coded at N sites regrows the bug at whichever site a later fix misses.Verification β redβgreen, control watched failing
Base and fix built one at a time, control =
origin/main@14126d94:bad interpreterlinesmain, unmodified)RESULT: FAIL (exit=1)RESULT: PASS (exit=0)collectedis identical on both β the 20 were fixed, not skipped or removed.passedrose by exactly 20.Second tier, fixed tree:
nodetestsβRESULT: PASS,tests=1449 pass=1449 fail=0, floor 1367.β The dev-host tier is deliberately not offered as evidence. It was already green before this change (30 passed), because the dev host has
/usr/bin/envβ so it cannot distinguish the fix from the bug. Only the sandbox pair is load-bearing.Notes
claim-workbefore starting (claim/nebula-relay-sandbox-shebang) β four sessions collided on the previousmain-red investigation.mainmoved four times during this work; rebased and re-pointed the control each time rather than reusing a stale measurement.