Skip to content

gates: drain the guest capture before SIGTERM and name it on the FAIL line (failure-capture PR-5) - #883

Merged
ryanbreen merged 13 commits into
mainfrom
gates/ftc-pr5-drain-capture
Sep 6, 2026
Merged

gates: drain the guest capture before SIGTERM and name it on the FAIL line (failure-capture PR-5)#883
ryanbreen merged 13 commits into
mainfrom
gates/ftc-pr5-drain-capture

Conversation

@ryanbreen

Copy link
Copy Markdown
Owner

What

Implements PR-5 of docs/planning/green-program/failure-capture/PLAN-2026-09-05.md
(section 6): gates drain an open BXCAP capture before their own kill and name
the result on the FAIL line. A new shared library,
docker/qemu/lib/gate-capture-drain.sh, wired into the four gate scripts
that run a guest QEMU process through to a scored outcome and then kill it:

  • docker/qemu/run-aarch64-boot-test-strict.sh
  • docker/qemu/run-aarch64-prod-profile-boot-test.sh
  • docker/qemu/run-x86-boot-tests.sh
  • docker/qemu/run-x86-prod-profile-boot-test.sh

(The plan's own file list additionally named
run-aarch64-service-sequence-gate.sh; this round's task scope named the
two production-profile gates in its place. Not converted, not touched.)

The drain contract

On any outcome that is not already a confirmed pass, immediately before a
gate's own kill line:

  1. Settle, unconditionally: GCD_SETTLE_MS (default 300ms), a flat
    sleep, not a stability poll — catches a kill landing mid-write on any
    serial line, not only inside a BXCAP block.

  2. Re-check. If the last [BXCAP:BEGIN ...] has no matching
    [BXCAP:END ...] (paired by seq=, not by which file a line landed
    in), wait for the file(s) to go byte-stable for GCD_QUIET_MS (default
    250ms), bounded by GCD_MAX_MS (default 3000ms) total.

  3. Classify and print two bracket lines:

    [CAPTURE_DRAIN:capture=<complete|partial|absent|n/a>:seq=<v>:edge=<v>:cpu=<v>:records=<v>:drain_ms=<v>]
    [CAPTURE_DRAIN_EVENTS:last_events=<name(payload),...|none|n/a>]
    

On a confirmed-pass outcome the drain step does not run at all:
capture=n/a, drain_ms=0, no re-read — the same predicate each gate's own
poll loop already uses to decide it is done polling. Latency bound: a PASS
boot pays 0ms; a non-PASS boot pays at most GCD_SETTLE_MS + GCD_MAX_MS
(≤3300ms at shipped defaults), added only on the failure path, once per
boot, after the outcome is known and before QEMU is killed.

Wire vocabulary is complete|partial, not the plan text's
complete|truncated — matches the landed PR-3 emitter's own
verdict=complete|partial field on its in-capture [BXCAP:END ...] line,
so the two vocabularies agree about the same fact. absent is this round's
own addition, for "no BEGIN was ever written," a case the in-capture
emitter has no line for.

capture_selftest and the gates stay disjoint throughout: each
capture_selftest boot in this round was built by hand outside the gate
script and run against the unmodified gate; no gate script's own text
gained the string capture_selftest anywhere (the existing structural
guard for that stayed green throughout).

Ratchet: tests/gate_capture_drain_structure.rs

Six tests below (0 of 6 boot a kernel):

  1. converted_gates_exist_and_call_both_drain_functions — census over
    docker/qemu/**/*.sh filtered to scripts sourcing the drain library;
    each must call both gcd_drain_and_report and gcd_pass_report.
  2. every_guest_kill_site_is_preceded_by_a_drain_decision — the actual
    ratchet: each guest-kill line in the converted set must have a drain
    call within the preceding 30 lines. Anti-vacuity floor: at least 6 kill
    sites (this round wired exactly six across the four gates).
  3. moving_a_kill_above_its_drain_decision_reddens_the_check — mutation
    leg: confirms the real, unmutated gate passes first, then relocates a
    real kill line above its drain decision and asserts the check now
    fails. Caught its own vacuity bug live on the first run (the relocated
    kill's new lookback window still matched this test file's own prose
    comments naming the drain functions) — fixed by excluding comment
    lines from what counts as coverage.
  4. classify_distinguishes_complete_partial_and_absent_with_no_boot
    three static fixtures, no boot.
  5. classify_pairs_a_begin_and_end_split_across_two_serial_files_by_seq
    the x86 shape (BEGIN in one file, END in the other), paired by seq=.
  6. drain_disabled_reads_partial_drain_enabled_reads_complete_same_race
    the oracle the task asked for, no QEMU boot: a background process
    appends the closing half of a real BXCAP capture 150ms after the check
    starts; BREENIX_GATE_DRAIN_DISABLE=1 reads capture=partial, the
    drain enabled reads capture=complete — same underlying race, only
    whether draining happened differs.
$ bash scripts/run-structure-tests.sh gate_capture_drain_structure
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Live-boot oracle: the identical, real capture read two ways

Section 2 item 6 establishes the mechanism with a synthetic fixture. Section
3 of the round doc reruns the same claim against a real QEMU boot's own
bytes:

BREENIX_STRICT_FORCE_FAIL=1 ./docker/qemu/run-aarch64-boot-test-strict.sh 1
[CAPTURE_DRAIN:capture=complete:seq=0:edge=SELFTEST:cpu=0:records=76:drain_ms=300]

Full serial:
docs/planning/green-program/failure-capture/serials/pr5/aarch64-strict-forcefail-complete-serial.txt
([BXCAP:BEGIN at line 674, [BXCAP:END at line 750).

The reverse leg is derived from the identical boot's own bytes, not
hand-authored: a straight byte-range cut ending strictly between BEGIN and
END, committed at
docs/planning/green-program/failure-capture/serials/pr5/aarch64-strict-forcefail-partial-serial.txt,
classifies as partial. The "kill before draining" race replayed on this
exact real content, both directions: drain-disabled reads partial at
drain_ms=0; drain-enabled reads complete at drain_ms=300. Red to
green, both directions, from one identical real capture.

Gates run at this branch's head

Leg Result
aarch64 strict, default PASS: 1/1, capture=n/a
aarch64 strict, forced-fail + selftest kernel FAIL (forced), capture=complete:...:records=76
aarch64 prod-profile, default PASS, capture=n/a
aarch64 prod-profile, forced-fail capture=absent:...:drain_ms=300
x86 boot-tests (beast) PASS, capture=n/a
x86 prod-profile (beast) PASS, capture=n/a

Full structural suite at review-passed HEAD: 42/42 tests/*_structure.rs
files, 676 tests, 0 failed; claim-lint clean.

What this round found in its own work (both by-catch, both fixed this round)

  1. x86 prod-profile $reached duplicate. The first forced-fail knob set
    reached=false a second time after the poll loop, tripping
    x86_production_profile_gate_verdict_discipline_holds's pin on exactly
    one reached=false assignment (and, on a second pass, a prose comment
    that happened to quote the literal variable expansion). Fixed by moving
    the knob into the poll loop as an early break.
  2. x86 boot-tests $passed duplicate. Reading $passed a second time
    after the loop tripped validate_x86_frame_custody_harness's
    passed_reads == 1 pin. Fixed with a CAPTURE_LINES="" sentinel set
    alongside passed=true/passed=false rather than read from either.

Neither ratchet was weakened to pass; both were doing exactly the job they
were built for, on new code unconnected to why either was originally
written. Caught by running the full structural suite, not by reading the
ratchet source first. A later pass over this round's own prose (docs and
gate-script comments) found and fixed six unquantified-absolute
claim-lint findings against the tool's own rules, plus one stale file
count in this doc's own claim-lint citation — corrected in place, not
carried forward.

Landing merge and re-smoke

git merge --no-ff origin/main (two stages, origin/main having moved
between them): 0 conflicts in either stage, no kernel/ file touched by
either, no file this branch's replayed fixtures depend on changed by
either — no fixture re-recording needed. At the merged head: 45/45
tests/*_structure.rs suites (700 tests, 0 failed), claim-lint and its
self-test both clean, a from-scratch aarch64 rebuild (no-neon PASS, clean
20/20 strict gate, PASS prod-profile gate) and beast x86 (0 build
warnings/errors, PASS run-x86-boot-tests.sh 1). run-x86-prod-profile-boot-test.sh
needed two attempts: the first FAILED on the console-prompt liveness check
under 4-5 concurrent agents' QEMU workloads on the shared beast container;
the second PASSED cleanly once host load measurably dropped, consistent
with #766's wake-dispatch-latency mechanism rather than a defect this
branch introduced. Recorded attempt-by-attempt, not silently retried away
— full detail and serial paths in the round doc's own landing section.

What is NOT claimed

  • PR-4 has not landed. Each leg above that needed a live BXCAP capture
    used PR-3's capture_selftest self-test edge. On a genuine panic or fault
    today, on any of these four gates, capture=absent is the honest and
    correct reading — no capture emitter is wired to those edges yet. This
    round's drain mechanism is shown working end to end; its production value
    on a real crash starts the day PR-4 lands.
  • The multi-file pairing in gcd_classify/gcd_last_events matches by
    seq= only
    , not a stricter check that the two halves are the same
    capture beyond that number colliding — a by-construction argument (the
    counter is a monotonic atomic fetch_add), not a race exercised by any
    test here.
  • PR-6's QMP backstop is still conditional, per the plan's own arming
    condition, unchanged by this round: each red in this round's gates was a
    forced-fail knob's own deliberate FAIL, not a red carrying
    capture=absent on a non-PASS boot that should have shown a capture.
  • The x86 gates' PASS reading in this round is at -smp 1; the
    multi-CPU interleaving hazard PR-3's own round doc measured on aarch64's
    -smp 4 is not exercised by anything in this round's x86 legs.
  • run-aarch64-service-sequence-gate.sh is not converted — the plan
    named it, this round's task scope named the two production-profile gates
    instead; no line of it changed.

Round doc, full detail: docs/planning/green-program/failure-capture/PR-5-2026-09-06.md.
Related: #826, #827.

ryanbreen and others added 13 commits September 6, 2026 04:59
…kill

Adds docker/qemu/lib/gate-capture-drain.sh and wires it into the four gate
scripts that run a guest QEMU process to completion
(run-aarch64-boot-test-strict.sh, run-aarch64-prod-profile-boot-test.sh,
run-x86-boot-tests.sh, run-x86-prod-profile-boot-test.sh): on a non-PASS
outcome, each gate now waits (bounded) for an open [BXCAP:BEGIN ...] capture
to reach its matching [BXCAP:END ...] before sending SIGTERM/KILL, and
reports capture=complete|partial|absent plus the last trace events on the
FAIL line and the per-boot facts sidecar. A PASS outcome adds no latency
and reports capture=n/a, unread.

tests/gate_capture_drain_structure.rs adds a census of the four converted
gates' guest-kill sites, a mutation leg proving the check reddens when a
kill is relocated above its drain decision, and a functional oracle over the
shell library (no QEMU boot) showing the identical race read two ways:
capture=partial with the drain disabled, capture=complete with it enabled.

Also fixes two forced-fail knobs added along the way from accidentally
duplicating a `reached`/`passed` textual occurrence that
tests/teardown_structure.rs's x86 gate verdict-discipline ratchets pin at
exactly one -- both x86 knobs now change control flow (an early loop break)
rather than reassigning the pinned verdict variable.

docs/planning/green-program/failure-capture/serials/pr5/ carries the live
serials this round's oracle is built from: a script-default strict-gate PASS
(capture=n/a), a capture_selftest + BREENIX_STRICT_FORCE_FAIL boot showing
capture=complete with real trace events, and a partial-serial fixture cut
from that same boot's own bytes.

Part of docs/planning/green-program/failure-capture/PLAN-2026-09-05.md
section 6, PR-5.
Round doc for failure-trace-capture PR-5
(docs/planning/green-program/failure-capture/PLAN-2026-09-05.md section 6):
the drain contract and its exact latency bound, the vocabulary departure
from the plan's own text (complete|partial, matching the landed PR-3
emitter's own verdict= field, not the plan's complete|truncated), the
capture_selftest/no-gate-builds-it guard this round stayed inside, the four
forced-fail knobs and the two verdict-discipline regressions they caused
and how each was fixed, the six-test structural ratchet, the live-boot
oracle shown against a real captured serial's own bytes (both the complete
reading and a partial reading cut from the identical bytes), each gate run
this round with its facts+capture output, the full 42-suite/676-test
structural sweep, and what this round does not claim.

Part of docs/planning/green-program/failure-capture/PLAN-2026-09-05.md
section 6, PR-5.
…time

gcd_drain_and_report's four BREENIX_GATE_DRAIN_*_MS/EVENTS_N overrides were
assigned once, at top level, when gate-capture-drain.sh was sourced. A
caller that prefixes the env var onto the function call itself
(`BREENIX_GATE_DRAIN_SETTLE_MS=50 gcd_drain_and_report ...`) -- the exact
shape tests/gate_capture_drain_structure.rs's oracle test uses -- had no
effect: the top-level GCD_SETTLE_MS etc. were already fixed to their
defaults before that prefix could ever be seen, so the function's body read
the cached default, not the override. The oracle test's "tightened for
test speed" leg still passed, but only because the un-overridden 300ms
default settle also happens to exceed the fixture's 150ms race delay, not
because the override reached anywhere.

Move the four reads inside gcd_drain_and_report's own body, as `local`
assignments evaluated at call time, so a per-call env-var prefix reaches
them. Verified the oracle test now genuinely exercises the tightened
50/100/2000ms leg (scripts/run-structure-tests.sh gate_capture_drain_structure,
6/6 passed, ~0.57s -- fast enough that the tightened bounds, not the
defaults, ran).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ained file

Both aarch64 gates (strict, prod-profile) decided their pass/fail verdict
from the live serial file after gcd_drain_and_report's own wait had already
run. That wait deliberately keeps QEMU alive up to
BREENIX_GATE_DRAIN_SETTLE_MS + BREENIX_GATE_DRAIN_MAX_MS longer so an open
BXCAP capture can finish writing, which is real added wall-clock life QEMU
did not have before this PR's drain step existed. Scoring the live file
after that wait let it move which side of the loop's own deadline a boot
landed on in both directions: a poll_exhausted boot could read scored_pass
on content that would not have existed under the old, near-zero-gap timing,
and -- separately -- a boot the loop's provisional check saw as early_pass
kept gcd_pass_report's 0-cost capture=n/a even when the post-kill rescore
later disagreed (scored_fail), since the code path as it stood did not
re-read the file after that choice was made.

Freeze a byte-for-byte snapshot of the serial file the instant the poll
loop stops, before the drain-or-pass decision runs, and score the final
verdict off that snapshot (DEADLINE_SERIAL / VERDICT_SERIAL_FILE) instead
of the live file. gcd_drain_and_report itself still reads the live file, so
the capture evidence it reports keeps benefiting from the extra wait; only
the pass/fail verdict is pinned to the pre-drain snapshot, so the drain
step can no longer change which side of the deadline a boot lands on. The
snapshot is removed again once the rescore is done, so a boot's evidence
directory keeps the same files it did before this fix.

For the early_pass-then-scored_fail case, add gcd_classify_report -- a
zero-wait entry point that classifies the (by then dead, so unchanging)
live file honestly instead of leaving gcd_pass_report's capture=n/a on a
boot that turned out not to pass -- and call it from both gates when the
post-kill rescore disagrees with the loop's provisional early_pass read.

Verified: scripts/run-structure-tests.sh gate_capture_drain_structure,
6/6 passed; `bash -n` on all three changed files; shellcheck -x on all
three (only pre-existing, unrelated warnings, none touching the changed
lines).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…late fails

report_gate_failure() only drained/classified the guest's BXCAP capture when
QEMU_PID was still non-empty, but the main flow clears QEMU_PID to "" right
after its own kill -- which runs after the steady-state check succeeds and
before ~40 more assertions (the liveness/wedge check, then the teardown
census) still have to pass for a real PASS. Any of those later assertions
tripping the ERR trap left capture_lines at its initial empty string, so
capture_drain.txt was written out blank for a confirmed FAIL -- not even an
explicit n/a marker -- defeating the drain feature for exactly the failure
shape (a wedge/hang caught by the liveness check) it is most useful for.

Add an elif branch: when QEMU_PID is empty but the serial files exist (only
reachable once QEMU has launched and the main flow's own kill has already
run), classify the frozen files with gcd_classify_report instead of leaving
capture_lines blank. QEMU is already dead by then, so no further wait is
useful, but the files' real capture state is genuine evidence for a
non-PASS outcome.

Verified with a same-fixture before/after comparison, not a live boot: a
fixture serial file carrying a real open ([BXCAP:BEGIN] with no matching
END) capture, driving the extracted, unmodified report_gate_failure()
body from both HEAD~1 and this commit against that one fixture.
HEAD~1's body writes an empty capture_drain.txt; this commit's body
writes capture=partial:seq=9:edge=FAULT:cpu=0 -- the same before/after
contrast gate-capture-drain.sh's own oracle test uses for its mutation.
`bash -n` and `shellcheck -x` pass on the changed file (only
pre-existing, unrelated warnings from prior rounds). A live x86
prod-profile boot was not run for this change (~8min on beast per
CLAUDE.md).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
run-x86-boot-tests.sh preserves a failed boot's serial_kernel.txt/
serial_user.txt pair plus capture_drain.txt under
breenix_x86_boot_tests_failures/<timestamp>_<pid>/, but the importer only
recognized three aarch64-only failure containers
(breenix_aarch64_strict_failures, breenix_prod_profile_failures,
breenix_testing_profile_failures) -- this new directory was invisible to
`breenix-runs import`. The directory name also carries a trailing `_<pid>`
that parseTimestamp's exact-match date format cannot parse on its own.

Add isX86BootTestsFailureRunDirectory / x86BootTestsFailureRunEntry
alongside the existing isProdFailureRunDirectory / prodFailureRunEntry
pair, reusing the generic serialSources/captureSources helpers
importGateIteration already uses (this directory's shape -- an arbitrary
set of serial_*.txt files plus sidecar captures -- already matches what
those scan for, unlike the aarch64 prod entry's single hard-coded
serial.txt). Add timestampFromPidSuffixedName to strip and validate the
`_<pid>` suffix before handing the remainder to parseTimestamp, and wire
the new container name into isPreservedFailureContainer /
profileForFailureContainer / preservedFailureEntries / a standalone
importPath entry, mirroring the existing prod-profile wiring at each site.

run-x86-prod-profile-boot-test.sh's own breenix_x86_prod_profile_failures
directory uses the same `_<pid>`-suffixed naming and remains unrecognized
by the importer -- that gap pre-dates this PR (the review finding this
commit closes explicitly scopes to the directory this PR itself
introduces) and is not fixed here.

Verified with two new ImporterTests cases -- a pid-suffixed run directory
importing both serials with the correct SerialStream (com1/com2) plus the
capture_drain.txt sidecar, and a malformed (non-numeric) pid suffix being
correctly unrecognized rather than misparsed -- plus the full existing
suite: `swift test` in tools/breenix-runs, 90/90 passed, 0 failures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Section 5's quoted transcript read "claim-lint: clean (11 file(s)
checked, changed hunks vs 2be24e3)". Re-running python3
scripts/claim-lint.py directly at this round's own HEAD (9f3d710), via a
detached worktree, reports 12 files, not 11 -- both runs say clean, so this
is not a live gate failure, but the doc's own quoted reproduction did not
reproduce byte-for-byte at the commit it is committed alongside. Most
likely explanation: the doc's own file is the 13th changed path (12
checkable after the one extension-skipped .gitattributes), captured before
that file had landed in the tree.

Corrected the quoted line to 12 and added a note explaining the
discrepancy, so the transcript matches what the tool reports at the commit
the doc documents.

claim-lint: python3 scripts/claim-lint.py --files docs/planning/green-program/failure-capture/PR-5-2026-09-06.md -> exit 0

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The deadline-snapshot fix for aarch64-gates-drain-decision-uses-provisional-
not-final-verdict introduced a new VERDICT_SERIAL_FILE variable and
renamed the ~35 assertion/echo reads of $SERIAL_FILE to it. Running the full
structure suite (not just gate_capture_drain_structure) surfaced the
consequence: current_teardown_bypass_surface_is_exact
(tests/teardown_structure.rs) pins four of those read sites as exact
literal text containing "$SERIAL_FILE" (PROD_SEAM_ABSENT_COUNT,
KERNEL_ORACLE_COUNT, INIT_EXIT_COUNT, BSSHD_COUNT), so the rename read as
those four markers no longer being asserted at all.

Same effect, no rename: freeze the PROD_DEADLINE_SERIAL snapshot at the
same point as before, but instead of introducing a second variable name,
reassign $SERIAL_FILE itself to that snapshot once the drain-or-pass
decision (the only step that still needs the live file) has run. Each of
the ~35 existing assertion/echo call sites keeps its original, unmodified
text, so
the four literal-text ratchets above pass again, and so would any other
ratchet checking one of the ~30 other ${SERIAL_FILE} reads in this
block, without needing to be found and updated individually.

Verified: `bash -n` and `shellcheck -x` on the changed file (only
pre-existing, unrelated warnings); scripts/run-structure-tests.sh
teardown_structure current_teardown_bypass_surface_is_exact and
x86_production_profile_gate_verdict_discipline_holds both pass; the full
structure suite (`for f in tests/*_structure.rs; do bash
scripts/run-structure-tests.sh "$(basename "$f" .rs)"; done`) reports
676/676 passed, 0 failed, across the same 42 suites the round doc's own
section 5 counted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The explanatory comment added for
x86-prod-profile-capture-lines-locked-before-liveness-and-teardown-assertions
quoted the verdict check verbatim (`` `test "$reached" = true` ``) as a
cross-reference. tests/teardown_structure.rs's
x86_production_profile_gate_verdict_discipline_holds ratchet counts
occurrences of the literal $reached substring and the VERDICT constant
across the whole script and requires exactly one of each (the real verdict
machinery's own false/true/spent ordering check); the quoted comment text
was a second, earlier match, which broke both the count and the ordering
the ratchet derives from it.

Reworded the cross-reference to name the check in prose instead of quoting
its shell text, so the literal count returns to one.

Verified: `bash -n` on the changed file; scripts/run-structure-tests.sh
teardown_structure x86_production_profile_gate_verdict_discipline_holds
passes; the full structure suite reports 676/676 passed, 0 failed (same
run this commit shares with the sibling aarch64-prod-gate fixup).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`python3 scripts/claim-lint.py` on the tree (required before push, not
just on commit messages) flagged six instances of the tool's own
unquantified-absolute vocabulary inside review-finding explanatory
comments this round added across gate-capture-drain.sh and the three
gate scripts -- claim-lint:ok: this commit's own `python3
scripts/claim-lint.py` run, quoted below, is the resolving evidence.
Reworded each flagged paragraph to state the same fact with a
quantified or narrower phrase in its place. One reword needed a second
pass: an
initial fix for the SCORE_ONLY paragraph in
run-aarch64-prod-profile-boot-test.sh claimed the replay mode "takes an
early `return` out of this function" -- inspected the actual script and
that file has no such function or return; it is a top-level `if` guard.
Corrected to describe the real control-flow shape (the two top-level `if`
branches on $SCORE_ONLY_SERIAL) rather than ship a wrong claim to clear a
lint warning.

Comment-only changes; verified `bash -n` and `shellcheck -x` on all four
touched files (only pre-existing, unrelated warnings) and the full
structure suite (`for f in tests/*_structure.rs; do bash
scripts/run-structure-tests.sh "$(basename "$f" .rs)"; done`) still
reports 676/676 passed, 0 failed.

claim-lint: python3 scripts/claim-lint.py -> exit 0

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merges 26 commits from main (the #865 x86 host QEMU lock arch-aware
extension, the #821 TTY-IRQ-under-PM-lock fix and its own landing
re-smoke, and the #866 breenix-runs release-build fix plus a
Swift-compiler-crash-workaround refactor of RunShow.renderSubsystems)
into this branch's PR-5 failure-capture-drain work.

3 conflicts, 0 of them touching kernel/:

- .gitattributes: both sides appended an independent -text entry
  block (this branch's pr5 serials, main's #821 serials); resolved by
  keeping both blocks.
- docker/qemu/run-x86-boot-tests.sh and
  docker/qemu/run-x86-prod-profile-boot-test.sh: both sides added an
  independent `source lib/*.sh` line at the same spot (this branch's
  lib/gate-capture-drain.sh, main's lib/qemu-host-lock.sh and
  lib/gate-boot-facts.sh) and, in run-x86-boot-tests.sh, an
  independent per-boot state variable at two more sites
  (CAPTURE_LINES vs POLL_BREAK_REASON); resolved by keeping both
  sides' hunks. Both scripts still drain the capture and sample the
  host-side facts before their own kill line, per each side's own
  comment on why that ordering matters; `bash -n` confirms both files
  parse.

No kernel/ file is part of any conflict; main's kernel/ changes
(process/mod.rs, syscall/handlers.rs, tty/driver.rs,
line_discipline.rs, main.rs, test_framework/registry.rs) applied
cleanly with no overlap against this branch's non-kernel diff.

No scorer contract this branch's replayed fixtures depend on changed
on main's side: main did not touch
tools/breenix-runs/Sources/BreenixRuns/Store/Importer.swift,
docker/qemu/lib/gate-capture-drain.sh, or the BXCAP/GATE_BOOT_FACTS
line formats those fixtures were recorded against (the RunShow.swift
change is a compiler-crash-workaround refactor whose own comment
states the per-state text is unchanged, and the gate-boot-facts.sh/
qemu-host-lock.sh edits are `|| true` and arch-name-portability
robustness fixes, not format changes) -- no fixture re-recording is
needed at this merged head.

43 of 43 tests/*_structure.rs suites and scripts/test_claim_lint.py
pass at this merged tree; scripts/claim-lint.py is clean over the
changed hunks.
Merges 6 further commits from main that landed after this branch's
first landing merge (815bdb8): a new structure-test ratchet on
gate-boot-facts.sh's own set -e/pipefail-fallback shape (the file
itself unchanged, only the test asserting on it is new) plus the
chk-debt PR-0 critical-path-logging census round (a new denylist
entry for serial_print!/log_serial_print!/log::log! in
check-critical-path-violations.sh and its own structure-test census).

0 conflicts across 6 changed paths: 5 of 6 (three new docs, two new
tests/*_structure.rs files) are additions; the sixth,
scripts/check-critical-path-violations.sh, auto-merged clean.
`git log --oneline 0481960..815bdb8 -- scripts/check-critical-path-violations.sh`
returns 0 of the 11 pre-merge PR-5 commits, so main's PR-0 widening
of that file's existing denylist array applied with no overlapping
hunk from this branch.

`git diff 783a6a5 origin/main --stat` names the same 6 paths above,
and neither tools/breenix-runs/Sources/BreenixRuns/Store/
Importer.swift, docker/qemu/lib/gate-capture-drain.sh, nor
docker/qemu/lib/gate-boot-facts.sh is among them -- the scorer
contracts this branch's replayed fixtures depend on carry 0 lines of
change here, so no fixture re-recording is needed at this merged
head.
Records the full re-smoke run at this branch's merged-to-main head: the
two-stage `git merge --no-ff origin/main` (815bdb8 then b0ea446, 0
conflicts either stage, no kernel/ file touched, no fixture re-recording
needed); 45/45 tests/*_structure.rs suites (700 tests, 0 failed);
python3 scripts/claim-lint.py and scripts/test_claim_lint.py both clean;
a from-scratch aarch64 rebuild (0 own-code warnings, no-neon PASS) with a
clean 20/20 strict-gate pass and a PASS prod-profile gate on the first
attempt each; and beast x86 (0 build warnings/errors, a clean
run-x86-boot-tests.sh 1 PASS) where run-x86-prod-profile-boot-test.sh
needed two attempts -- the first FAILED on the console-prompt liveness
check under 4-5 concurrent agents' QEMU workloads on the shared beast
container (serial evidence preserved under scratchpad/ftc5-serials/
land-resmoke/), the second PASSED cleanly once host load measurably
dropped, consistent with #766's wake-dispatch-latency mechanism rather
than a defect this branch introduced -- recorded attempt-by-attempt per
the project's testing-integrity rule, not silently retried away.

claim-lint:ok: python3 scripts/claim-lint.py -> exit 0 (12 files checked,
this commit's own changed hunks)
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