Skip to content

fix(802): the AF_UNIX preflight fails through the gate verdict path - #805

Merged
ryanbreen merged 2 commits into
mainfrom
fix/802-prod-gate-preflight-verdict
Sep 5, 2026
Merged

fix(802): the AF_UNIX preflight fails through the gate verdict path#805
ryanbreen merged 2 commits into
mainfrom
fix/802-prod-gate-preflight-verdict

Conversation

@ryanbreen

Copy link
Copy Markdown
Owner

The regression

PR #801 (main at 2a444455) added two preflight checks on the
operator-controlled BREENIX_GATE_TMP to
docker/qemu/run-x86-prod-profile-boot-test.sh — the absolute-path check from
review finding F6 on #797 at line 208, and the AF_UNIX sun_path length check
from F7 at line 224. Both sit beside the assignments that derive their
subjects, and at that point in the script the only way to stop is a bare
exit 1: report_gate_failure is defined ~550 lines further down and the
ERR trap that calls it is installed after that definition.

That turned tests/teardown_structure.rs::x86_production_profile_gate_verdict_discipline_holds
red on main — 82 passed, 1 failed, with the test's own stderr line
x86 production-profile gate gained a pre-empting exit: exit 1. This is #802.

The rule

tests/teardown_structure.rs:15336-15346:

    // No exit may pre-empt the verdict. The trap's re-raise is the only one this
    // gate needs, and it runs after a verdict has already been found false.
    for line in script.lines() {
        let statement = line.trim();
        if statement.split_whitespace().next() == Some("exit")
            && statement != "exit \"$exit_code\""
        {
            eprintln!("x86 production-profile gate gained a pre-empting exit: {statement}");
            return Err(());
        }
    }

It is scoped to this gate because, as validate_x86_prod_profile_harness's doc
comment puts it, this gate "is the only x86 boot that ever executes the shipped
zero-feature kernel, so a silent abort inside it is worse than in the boot-test
harness: there is no second x86 gate that would catch the same regression."

The fix

No rule was weakened and the test is unchanged. Both preflights moved into a
BASE-DIR PREFLIGHT block placed immediately after the ERR trap is
installed, so they are the first commands to run under the handler, and they
reject with the echo + bare false shape this script already uses for its
missing-userspace-artifact preflight further down. A bare false under
set -e/set -E fires the trap, so a rejection is spent through
report_gate_failure: the operator gets the specific diagnostic (which value,
how long, what to shorten), then the gate's own
x86 production-profile gate: FAIL (...) verdict line, the failing command,
and a nonzero status. The script now carries one exit — the trap's re-raise
exit "$exit_code", which the rule admits by name.

The purposes are intact. F6 is about cd-order, and the block sits at line
799-831 while cd "$BREENIX_ROOT" is at line 944, so the absolute-path check
still runs on the same side of that cd. F7 is about failing before a wasted
build, and the first rm is the stale-image rm -f at line 965, so both
checks still reject ahead of the build and the boot — measured at 0.030 s and
0.029 s on beast, with neither leg leaving an output directory behind.

The absolute-path check's exit 1 was on a line starting with the case label
*), so split_whitespace().next() returned Some("*)") and the scan did not
report it; only F7's line-leading exit 1 reddened the test. It is converted
here too rather than left as the same defect in a shape the ratchet cannot
read.

Disclosed scope: #797 put the same absolute-path guard into eight gate scripts
and the sun_path guard into two; this PR converts the two in
run-x86-prod-profile-boot-test.sh. The other seven keep #797's shape.
x86_production_profile_gate_verdict_discipline_holds is the one ratchet in
tests/ that pins verdict discipline for a gate script and it names this gate
alone, so whether each sibling's preflight should print its own gate's FAIL
line is a judgement about that gate's verdict model, not a red this branch is
carrying.

Evidence

Structure suites (Mac, this branch's head)

suite result
scripts/run-structure-tests.sh teardown_structure exit 0, 83 passed / 0 failed — the test #802 names is in that 83
tests/*_structure.rs sweep 29 of 29 suites green, 542 cases, 0 failed

The sweep glob finds 29 files at this head; #802's text says 30, which is the
count on the slice-3b branch the red was found from, not on main.

Beast (breenix-x86 Incus VM, clone /root/breenix-health)

Head 4b6b82d4; the gate script's sha256 in the clone,
131d1304db8a1a78b4d36db06022a311d6a007458c5ba72d192a953c29ba0764, matches the
branch's working tree. BREENIX_GATE_TMP=/root/gate-tmp-802 for the gate run
(R18). Transcripts:
docs/planning/green-program/gates/serials/802-2026-09-05/.

  1. Clean build. cargo build --release --features testing,external_test_bins --bin qemu-uefi returned 0 and
    grep -E "^(warning|error)" over its log printed no line.

  2. Default gate run, 1 run: exit 0, verdict line

    PASS: x86 production profile reached steady state with the teardown census at rest
    

    booted image sha256
    d1b3eb0e4461845bcb01381e8c6a66439c54924f7641df9ccfc242ac21abd71e (R17),
    liveness prompt count 1 -> 2 over the 60s window.

  3. Simulated preflight failure, BREENIX_GATE_TMP long enough to push the
    console socket path to 147 characters:

    x86 production-profile gate preflight: console socket path "/root/gate-tmp-802-ggg...ggg/breenix_x86_prod_profile/console.sock" is 147 chars, over the AF_UNIX sun_path limit of 107 -- shorten BREENIX_GATE_TMP
    x86 production-profile gate: FAIL (set -e abort at docker/qemu/run-x86-prod-profile-boot-test.sh:830, exit 1)
      failing command: false
    

    exit status 1.

  4. Simulated preflight failure, BREENIX_GATE_TMP=relative-not-absolute
    (the F6 leg the scan could not see):

    x86 production-profile gate preflight: BREENIX_GATE_TMP must be an absolute path, got: relative-not-absolute
    x86 production-profile gate: FAIL (set -e abort at docker/qemu/run-x86-prod-profile-boot-test.sh:826, exit 1)
      failing command: false
    

    exit status 1.

Neither rejection leg reaches a boot, so report_gate_failure's compgen -G
over $OUTPUT_DIR/serial_*.txt matches no file and it skips its preservation
block — which is why those transcripts are three lines rather than a serial
tail.

Commits

No kernel source is touched. This is the fix for #802: the gate fails through
its own verdict path. The issue is closed by hand after merge, with the merge
SHA in the closing comment.

claim-lint: scripts/claim-lint.py                     -> exit 0
claim-lint: scripts/claim-lint.py --files <this body> -> exit 0

ryanbreen and others added 2 commits September 5, 2026 02:55
#801 added two preflight checks on the operator-controlled BREENIX_GATE_TMP
beside the assignments that derive their subjects, and at that point in the
script the only way to stop was a bare `exit 1`.
tests/teardown_structure.rs::x86_production_profile_gate_verdict_discipline_holds
forbids that shape: "No exit may pre-empt the verdict. The trap's re-raise is
the only one this gate needs, and it runs after a verdict has already been
found false." The sun_path check's line-leading `exit 1` is what the scan sees,
and it turned that ratchet red on main at 2a44445 (82 passed / 1 failed).

Both checks now run immediately after the ERR trap is installed, as the first
command under it, and reject with the `echo` + bare `false` shape this script
already uses for its missing-userspace-artifact preflight. A rejection is
therefore spent through report_gate_failure: the gate prints its
`x86 production-profile gate: FAIL (...)` verdict line, names the failing
command, and re-raises the nonzero status, so a verdict line is recorded before
the process ends. The assignments the checks judge (OUTPUT_DIR,
CONSOLE_SOCK_PATH) stay where they were, because report_gate_failure reads them.

The preflight purpose is intact. The absolute-path check still runs ahead of
`cd "$BREENIX_ROOT"`, which is what finding F6 on #797 is about, and both
checks run ahead of the `rm -f` of the stale UEFI image that opens the build
step, so a bad BREENIX_GATE_TMP is still rejected in well under a second,
before the build and the boot.

The absolute-path check's `exit 1` sat after a `*)` case label, so the scan's
`split_whitespace().next()` test does not see it. It is converted here too,
rather than left as the same defect in a shape the ratchet cannot read.

Mac evidence at this head: `scripts/run-structure-tests.sh teardown_structure`
-> exit 0, 83 passed / 0 failed (the failing test now passes); the
tests/*_structure.rs sweep -> 29 of 29 suites green, 542 cases, 0 failed.
Running the gate with BREENIX_GATE_TMP=relative-not-absolute and with a
133-character socket path each printed the preflight diagnostic followed by
`x86 production-profile gate: FAIL (set -e abort at
docker/qemu/run-x86-prod-profile-boot-test.sh:<line>, exit 1)` and exited 1.

claim-lint: scripts/claim-lint.py                        -> exit 0
claim-lint: scripts/claim-lint.py --files <this message> -> exit 0

Co-Authored-By: Ryan Breen <ryan.breen@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Adds docs/planning/green-program/gates/GATE-PREFLIGHT-VERDICT-802-2026-09-05.md
and the serials directory it reads. The document quotes the ratchet rule that
went red, shows what replaced the two bare `exit 1` preflights, states why F6's
cd-order property and F7's fail-before-the-build property still hold at the new
placement, and discloses that the other seven #797 scripts keep the shape this
one is moving away from.

Beast record (clone /root/breenix-health, head 4b6b82d, gate-script sha256
131d1304db8a1a78b4d36db06022a311d6a007458c5ba72d192a953c29ba0764, matching this
branch's working tree):

- `cargo build --release --features testing,external_test_bins --bin qemu-uefi`
  returned 0 with `grep -E "^(warning|error)"` printing no line.
- The production-profile gate with BREENIX_GATE_TMP=/root/gate-tmp-802, 1 run:
  exit 0, verdict line "PASS: x86 production profile reached steady state with
  the teardown census at rest", booted image sha256
  d1b3eb0e4461845bcb01381e8c6a66439c54924f7641df9ccfc242ac21abd71e (R17),
  console prompt count 1 -> 2 over the 60s liveness window.
- Over-length BREENIX_GATE_TMP (147-char socket path): the preflight diagnostic
  followed by "x86 production-profile gate: FAIL (set -e abort at
  docker/qemu/run-x86-prod-profile-boot-test.sh:830, exit 1)", exit 1.
- Relative BREENIX_GATE_TMP: the preflight diagnostic followed by the same FAIL
  verdict line at line 826, exit 1.
- A timed repeat of the two rejection legs: 0.030 s and 0.029 s, with `ls -d`
  reporting no such directory for either output path afterwards.

Also adds a short "Superseded for run-x86-prod-profile-boot-test.sh (#802)"
subsection to GATE-TMP-BASEDIR-2026-09-05.md, whose F6/F7 code snippets are
this gate's pre-#802 shape.

claim-lint: scripts/claim-lint.py                        -> exit 0
claim-lint: scripts/claim-lint.py --files <this message> -> exit 0

Co-Authored-By: Ryan Breen <ryan.breen@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@ryanbreen
ryanbreen merged commit 2cef355 into main Sep 5, 2026
@ryanbreen
ryanbreen deleted the fix/802-prod-gate-preflight-verdict branch September 5, 2026 07:08
ryanbreen added a commit that referenced this pull request Sep 5, 2026
…pairs

Records the census that scoped the previous commit's repair (36 scripts
grepped for verdict-line shapes, 7 found with the report_gate_failure/ERR-trap
architecture, 6 of those 7 not yet carrying #805's idiom, 29 classified as a
different verdict shape and left untouched), the per-script repair details,
the widened Rust ratchet's design, and default-env + simulated-preflight-
failure run proofs for each of the 6 repaired scripts: 4 on this Mac
(aarch64), 2 on beast (x86, own clone /root/breenix-verdict). Also documents
what those proofs do and do not establish -- this worktree's missing
rust-fork/prebuilt-userspace gap and the read-only artifacts substituted for
it on the aarch64 side, and the structure-test scanner's inherited
first-token-of-line blind spot, caught live while writing this round's own
anti-vacuity mutation and disclosed rather than silently worked around.

Raw output for each of the 12 runs (6 scripts x 2 runs) is under
docs/planning/green-program/gates/serials/verdict-widened-2026-09-05/.

claim-lint: scripts/claim-lint.py                          -> exit 0
claim-lint: scripts/claim-lint.py --files <the doc>         -> exit 0
claim-lint: scripts/claim-lint.py --commit-msg <this file>  -> exit 0

Co-Authored-By: Ryan Breen <ryan.breen@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant