test: close sensitivity regressions from #549 structural-anchor review - #551
Merged
Conversation
…549 review Post-merge review of PR #549 (structural ratchet anchors) found 14 defects; this closes the nine regression- and grind-class ones. Only tests/teardown_structure.rs changes, and all 75 anchor tuples are byte-identical to main, so no pin moved. - F1: validate_process_page_table_retire_site guards for reclaim_bounded and reclaim_deferred_process_resources_for_pass before calling function_body, so removing either reports a failure instead of panicking away every census diff already accumulated in the test. - F2: definition_offsets finds the body brace at paren/bracket depth 0, so a `;` inside a signature ([u64; 32], -> [u8; N]) no longer drops the definition and silently keeps validate_blocking_primitives green. - F3: header_cfg concatenates every stacked #[cfg] in source order, so narrowing an item's cfg is an anchor change; item_spans stops treating a bracketed `;` as a header boundary; duplicate (path, item) renderings are marked and reported instead of silently summing. - F4: the raw-scheduler-lock ratchet matches any identifier ending in SCHEDULER again. #549 narrowed it to the exact identifier, so a GLOBAL_SCHEDULER.lock() outside the instrumented wrappers was red before #549 and green after - a real sensitivity regression. - F5: the remaining bare assertions in the four report-all tests accumulate through a new check() helper, so one run reports every failure rather than aborting on the first. - F8: the residual-pin detector's 4th alternative is narrowed to the offset-to-line conversion idiom; the two lexer newline checks revert to `byte == b'\n'` and the guard comment that institutionalised the contortion is gone. - F9: expected_census panics on a duplicate anchor key instead of summing it, which is the assert AUDIT-DESIGN 5.17 required on the merged return_lease allowlist. - F11: the anchor constants are #[rustfmt::skip] with one tuple per line, so the table reads as a table during re-anchoring. - F12: BLOCKING_NAMES drops the vestigial trailing paren and its consumer drops the trim_end_matches. Self-test coverage added for stacked cfgs, the duplicate-path marker, array-signature definitions and the SCHEDULER-suffix matcher. Both frozen helper self-tests stay byte-identical to main. cargo test --test teardown_structure: 13 passed, 0 failed, zero compiler warnings, residual-pin detector 0. Co-Authored-By: Ryan Breen <rbreen@getfastr.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The D2 control from the extended verification (a tenth blocking primitive added as `pub fn block_current_probe(&self, saved_regs: [u64; 32])`) left `validate_blocking_primitives` green. The census searched nine exact identifiers, so a new primitive under a new name was structurally invisible - pre-refactor main had the same hole, and AUDIT-DESIGN 5.12 claims sensitivity to "a 10th blocking primitive appearing anywhere". Extract `definition_span` from `definition_offsets` (same depth-aware body-brace scan, parameterised by the identifier end) and add `definition_prefix_offsets`, which matches every `fn` definition whose name begins with a prefix and rejects a mid-identifier hit such as `unblock_current`. `BLOCKING_NAMES` becomes `BLOCKING_NAME_PREFIXES` (`block_current`, `prepare_to_wait`); over the current tree the prefix census resolves to exactly the nine pinned definitions, so `BLOCKING_PRIMITIVES` is unchanged and every anchor keeps its item path. Proofs: a `definition_prefix_offsets` case in `item_path_is_cfg_and_impl_scoped`, and a `block_current_probe` synthetic negative in `deliberately_broken_variants_fail_the_ratchet`. With the kernel mutation applied, `v3_structural_closures_are_exact` now fails naming `impl WaitQueueHead::fn block_current_probe`; restored, the suite is 13/13 green with zero warnings and the residual-pin detector at 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Notes the fix/teardown-anchor-followups round closing 9 sensitivity regressions from the #549 structural-anchor review. Co-Authored-By: Claude Code <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Aug 15, 2026
…ames (#527) The ratchet keyed on a closed list of six literals (`with_thread_mut`, `with_scheduler`, `lock_scheduler`, `try_lock_scheduler`, `SCHEDULER.lock`, `SCHEDULER.try_lock`). scheduler.rs exports ~30 module-level functions that acquire the same global lock without containing any of those strings, so inserting `crate::task::scheduler::current_thread_id()` into an ARM64 exec body — a blocking SCHEDULER acquire under the process-manager guard, the exact #527 defect — left the suite at 14 passed, 0 failed. This is the same defect PR #551 had to fix in teardown_structure, and with the runtime oracle dark it was the whole gate. Replace the list with two censused detectors, both applied to the ARM64 exec bodies and module-wide to manager.rs: - every item reached through a `scheduler::` path (plain, braced use-group, or glob) must be on an explicit allowlist, which holds only the `ExecSchedCommit` receipt type; this also catches the import-alias evasion, since the import itself names a `scheduler::` path; - the lock-taking family is derived from scheduler.rs rather than written down: lock roots are matched by name family, propagated to a fixpoint over module-level bodies, and the public closure of that set may not be called unqualified from manager.rs. A census that stops finding the known exports fails loudly instead of silently passing. The reviewer's injection is now a negative control, alongside use-import, glob import, unqualified-call and census-collapse controls. Also ratchets the exec smoke itself: init's spawn, both binaries' registration, the launcher's execv, the target's marker ordering, the post-commit boot_tests counter emission, and each gate script's new assertions. Proven on the real tree: the injection turns T1 and T2 red ("names disallowed scheduler item `current_thread_id`"), manager.rs restored byte-identical, suite green again. Co-Authored-By: Ryan Breen <rbreen@jrni.com> Co-Authored-By: Claude Code <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Aug 16, 2026
…hed rows at dispatch Tranche-2 Phase 3, part 2 (DESIGN AC-7 admission half, PLAN "Scope - part 2, clone/exec admission"). One mechanism, three inseparable pieces - the non-runnable publication is only safe because dispatch refuses Creating rows, so neither half ships without the other. Admission. `ProcessManager::admit_clone_into` reads the parent row and asks `Process::admits_clone()` inside the same process-manager transaction that later publishes the child, so no snapshot of the parent's lifecycle is carried across a guard drop. `sys_clone` calls it before copying any parent state out and returns EAGAIN on refusal - DESIGN AC-7's stated evidence shape: the child is either published or the caller gets EAGAIN, never a runnable unrequested member. A missing parent refuses for the same reason a dying one does. Non-runnable publication. The child row used to be fully Ready before it existed: `state = Ready` plus `set_main_thread` (which also writes Ready) both ran ahead of `insert_process`. Now the child thread is constructed Blocked, the row is attached through `attach_main_thread_unpublished` and stays Creating through insertion, and only then - still under the same guard - are the row and its thread made Ready. Nothing runnable ever refers to a row that does not yet exist. Third dispatch arm. `refuse_unpublished_dispatch` is a cheap field read on a row the caller already holds - no lock, no allocation, no formatting, no page walk - placed before CR3 resolution at both x86 dispatch sites, and its aarch64 counterpart returns the new `TtbrResult::RowUnpublished` before any TTBR0 value is computed. It is a third arm on #570's already-refactored site, in that site's shape, with the same one-shot raw-serial breadcrumb. A refused row is NOT terminated: Creating is transient by construction, so the recovery is set_need_resched + idle return, the same shape PmLockBusy already uses on aarch64. Terminating would be an over-free of a row about to become legitimately runnable. Both predicates are exhaustive matches over ProcessState with no catch-all, so a new variant forces a decision here rather than defaulting. Both exits from Creating are accounted for. `set_main_thread` and `set_ready` both write Ready, and the oracle drives the refusal closed through EACH of them in turn. The ratchet pins the Ready-write CENSUS FAMILY - every write of ProcessState::Ready across kernel/src must live in process.rs, the row's own lifecycle module - rather than the single set_main_thread site that the plan text names. That is the PR #551 sensitivity lesson: pin the family shape, never one site or a closed name list. Anti-vacuity. Both new counters are driven nonzero by this PR's own workload. `clone_admission_oracle_test` runs on both arches and exercises every arm of both predicates: a live parent admits, a Terminated parent refuses, a never-inserted pid refuses, a Creating row is refused at dispatch, and the same row admits once published - once via set_ready and once via set_main_thread. It emits [CLONE_ADMISSION_ORACLE:<arch>:...] with measured values and returns the frame allocator to baseline. Also closes out the exec-detach oracle's frame accounting, which the first aarch64 boot run caught at balance=18 while every mechanism field was already exact. Diagnosis: not page tables - all roots, tables, pending-old tables and leaves were reclaimed - but the fixture's own user stacks, which GuardedStack::drop still does not reclaim ("cleanup not yet implemented"). The oracle now returns those external leaf frames through a fail-closed boot-test-only helper that compare-exchanges the exact LEAF_EXTERNAL state and goes through the normal return_lease choke point, so it can refuse but never over-free. Co-Authored-By: Ryan Breen <rbreen@jrni.com> Co-Authored-By: Claude Code <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Aug 17, 2026
…ed_init (#527, P5a) Tranche-2 Phase 5a, commit 2 of the phase's two-commit seam. Commit 1 shipped the reservation, the ticket and the authority with its production validation as its live consumer; this commit moves the production init sites and their dependent reads onto designated_init(), ratchets the result, and repairs the userspace contract the plan wrongly believed did not exist. Kernel migration - all four production sites and all five dependent reads: ProcessManager::exit_process_locked and ProcessScheduler::handle_thread_exit both reparent through the one authority, reparent_children_to_init, which returns whether it ran so the exiting row's children are cleared on exactly the path that cleared them before. handle_thread_exit's "init has no children to reparent" test compares against the designation instead of the literal. syscall/signal.rs loses const INIT_PID entirely; kill(-1) and its existence check exclude the designated init when one exists. With no designated init the defined behaviour is that nothing is excluded by identity and children keep their existing parent rather than being pointed at a PID-1 row that does not exist - the oracle drives both arms in the same run. grep for ProcessId::new(1) in kernel/src now returns only the three allowlisted test_userspace.rs sites. Userspace contract (coordinator ruling R7, marshal brief planContradiction PC-1). The plan's premise that init_shell.rs:1028 was the only userspace PID contract was false: bsh.rs decided it was the boot shell by testing getpid() == 2 || 3, which reserving PID 1 renumbers straight through, taking /etc/init.js and the whole service chain with it, silently. The role is now conferred by init passing --init-shell and by nothing else; no PID value participates, so renumbering cannot reach it. init_shell.rs:1028 is untouched and pinned. Ratchets (tests/teardown_structure.rs, 36 -> 41 tests). PRODUCTION_INIT_PID_SITES is now empty. Five new census tests, all shape-anchored per the #549/#551 lesson - never line pins, never a closed list of names: - the reservation is single-sourced: exactly one next_pid.fetch_add site, the eight allocate_ordinary_pid call sites pinned by enclosing item, and the base expressed as FIRST_ORDINARY_PID with no numeric literal; - the init-PID constant FAMILY is confined (ruling R10, PC-2): the old census saw only the text ProcessId::new(1) and was blind to const INIT_PID: u64 = 1 and to direct as_u64()/raw() comparisons against 1; - the designation authority is closed: one field, two writers (the designation transaction and its retirement), and the two ticket types are constructed at exactly one site each, are neither Clone nor Copy, and have no public constructor or public field; - the mechanism items carry zero #[cfg]; - the init-shell role is argument-derived, and the oracle's launch wiring is non-vacuous: one main.rs direct call, one registry TestDef, and the marker pinned in both of the x86 gate's lists. Each new census carries its own in-test anti-vacuity control - reintroducing const INIT_PID, an as_u64() == 1 comparison, a foreign ticket construction, a designation write inside a constructor, or a #[cfg] inside a mechanism item each turns its census red - so deleting a control is visible. The x86 gate's oracle literal is pinned at construct_residual=4 and the aarch64 gates at 2, both read off green measured runs: the residual is the counted frame residue of the two construction-failure arms, which is arch-specific because the two page-table constructors record different table counts. Doc repairs in the same PR, per coordinator rulings R8/R10/R11/R12: the held-publication ticket's terminal step is defined against each arch's real shape rather than a run-queue publish the aarch64 constructor never performs; the P0 ratchet paragraph now specifies the constant family; the standard gate's inventory is corrected from seven pinned x86 custody lines to nine and from four ratchet suites to the seven the tree actually has (202 tests); and DESIGN section 3's phase cells are reconciled - AC-5 is P5a, and AC-2's "clone admission" half is split into what P3/#587 landed versus what P5b still holds on #575. Co-Authored-By: Ryan Breen <rbreen@getfastr.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Aug 20, 2026
…528 guard it never had (#589, review B2) Re-running the acceptance battery at HEAD produced 0/6 on the strict gate and 21 consecutive "futex handoff oracle marker missing" boots on the service-sequence gate. Neither was a kernel regression. Both gates were booting a PRODUCTION kernel, and every failure named a specific oracle, reproduced perfectly, and was completely wrong. cargo keeps one cached artifact per feature set and hardlinks the requested one into the single path target/aarch64-breenix-kernel/release/kernel-aarch64. Switching profiles takes 0.06s with no recompilation and no output worth reading; the file changes size and its mtime moves BACKWARDS. tests/ kernel_no_neon_guard.rs builds that kernel with no features, by design. So running the structural suites silently swaps the boot_tests kernel for a production one and every gate afterwards boots the wrong binary. The previous battery escaped this only by luck of ordering: its --rebuild full test happened to sit between the suites and the service-sequence gate. Move the full test to the end, as any reasonable person would, and the whole run is garbage that looks exactly like a real regression. Fixed at source rather than by reordering a driver script. require_boot_tests_kernel is now a preflight in the service-sequence gate, the strict gate and the full-system test, sitting immediately after the #528 no-neon guard and reading the same way: it greps the kernel BINARY for a census of five boot_tests-only marker literals and exits 1 before booting anything if any is missing. Fifty boots of an attributable-looking false red is worse than no run at all. The guard corrected its own first draft. [BLOCK_EINTR_ORACLE: was in the initial census and the guard immediately refused a known-good boot_tests kernel over it. It was right to: that marker is emitted from USERSPACE, lives in the ext2 image, and was never in the kernel binary at all. The census is now five markers each verified present in a boot_tests build and absent from a production build. Pinning that guard turned up a second, worse gap. run-aarch64-boot-test-strict.sh is the kernel-merge gate and it was the ONLY aarch64 gate with no check-kernel-no-neon.sh call at all - the full-system, production-profile and service-sequence gates all carried it. A NEON-target kernel, which re-arms #528, could have merged through the one gate that decides merges while every other gate would have caught it. Both it and run-aarch64-boot-test-native.sh now carry the guard. Also replaces two ratchets that pinned the service-sequence census-sum lines as whole-line literals freezing a closed list of bucket names - the exact line-pin/closed-list shape this project has been bitten by three times (#549, #551, #527-r1), and which failed on the correct addition of count_609. The replacement DERIVES the expectation: it reads every arm of the classifier's CLASS_BUCKET dispatch, takes each arm's count_* counter, and requires that counter in census_sum and its TOTAL_* counterpart in TOTAL_SUM. That catches a bucket added but dropped from either accounting line - the real invariant - instead of failing whenever any bucket is added at all. Mutations, all red: each of the three require_boot_tests_kernel invocations deleted in turn; the marker census shrunk to two; and a bucket removed from census_sum with its dispatch arm left in place. Every script restored byte-identically after each. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 22, 2026
ryanbreen
added a commit
that referenced
this pull request
Aug 28, 2026
Review of the framework as landed, plus the pieces the pilot's spec requires that were not yet there. Corrected: * Arch-gate the module and the macro's active arm on aarch64. The driver, the stimulus battery and the pen all reach the GIC, the virtual timer and `kthread_run_on_cpu_for_test`, none of which x86 offers; the seams live in arch-shared `scheduler.rs`, so `proof_point!` must still COMPILE on x86. Without the arch in the cfg, `--features coreproof` on the x86 target does not build at all. An x86 driver is the #608 hunt's own work, not the pilot's. * Read the two teardown counters BY NAME rather than by index into `snapshot()`. Indices 49 and 77 silently re-point at different counters the moment one is inserted into the registration array, leaving the predicate passing while measuring something else - the brittle-literal failure this campaign has been bitten by three times (#549, #551, #527-r1). * Derive `order` from the site instead of drawing it independently. Drawn, it could produce `site=BlockAfterDeparture:order=before`, a record contradicting itself. * Bound every pen rendezvous. An unbounded spin on a peer that is never dispatched would hang the driver, produce no RUN record, and report the harness as if it were a kernel result. Exhausting the budget degrades the run to ambient and says `degraded=1` in the record. * Move the existing-marker sweep and the strand census onto a cadence. Both read shared state; paying them per trial spends the budget re-reading counters that move on a millisecond timescale, and iterations inside one boot are the harness's actual lever. * Name the field `violated_predicates`. At most one record is emitted per predicate per run, so `violations` read as a count and would have understated a run where one predicate fired thousands of times. * Test-before-set in `mark_visited`, so the seam is a load and a compare after the first visit rather than a read-modify-write inside a masked critical section. * Rename the PRNG's `state` field to `word`: a name collision with the thread-state store census, not a finding. * Rename the probe `block_current_coreproof_probe`. #647's caller rule forbids a CALLER of the blocking family from open-coding a ready-queue departure and exempts the family's own members. The probe plants and removes a queue entry on purpose, exactly as `block_current_departure_gate` does, so it belongs on the family's side of that rule and now carries a name that says so. Start the harness AFTER the boot-test cohort, and spawn it unpinned. Both came from the first smoke boot, which turned `census_widen_oracle` red: that oracle needs a strand census with nothing queued on a non-dispatching CPU, and the harness's own driver - pinned to one CPU by an affine spawn, and looping while the cohort ran - was that thread. The oracle was measuring correctly, so the harness moved rather than the oracle. This is the strand oracle's own recorded hazard in concrete form: a perturbation loop can manufacture its own failure. Added `mutations.rs`, the planted-defect register - feature, issue, fixing PR, site and expected predicate for each, plus the pre-registered reading of a miss (the site labelling is wrong, a one-round fix; never "the bug is unfindable"). `mut=` now appears in the run record, so a mutation run and an unmutated one stay distinguishable once only the serials survive. Module documentation on all seven files, including what a seed does and does not replay on four-CPU MTTCG, and why the timer is a stimulus source rather than a seam. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Aug 28, 2026
`docker/qemu/run-coreproof-gate.sh --component A --seeds N --profile
max|cortex-a72|both --mode pen|adversarial|ambient [--seed 0xHEX]`.
Three gate-failing conditions, and they are the whole verdict:
1. any VIOLATION line;
2. a missing or malformed RUN record - a boot that emitted no run record
proves nothing, and "no violations" from a harness that never ran is the
exact false green this gate exists to refuse;
3. `sites_visited < sites_declared`, the vacuity guard. Both numbers come from
the harness's own census, so the gate compares two numbers and never a
literal list of site names - the ratchet lesson bitten three times (#549,
#551, #527-r1).
A boot that panics, faults or fails its ordinary boot tests fails the gate too.
UNATTRIBUTED is gate-failing; the harness rides a real boot and an unexplained
boot failure is never absorbed.
`degraded=1` is surfaced but does not fail: an ambient run is a weaker
measurement, not a wrong one, and the point is that it can never be read as a
penned one.
Verdict discipline follows #668 - `set -E` plus an ERR trap, because a silent
`set -e` abort prints nothing of its own and a genuine red would otherwise die
with no verdict text and no serial pointer.
`--seed` and `--mode` reach the BUILD, not the boot: both are `option_env!`
channels, and passing the mode only to QEMU would leave every run in the default
pen while the verdict line claimed otherwise. Own target dir, always the
soft-float kernel target [[gate-target-fidelity-528]], and it runs the no-NEON
and seam ratchets before booting anything.
Default 25 boots per profile per the 2026-08-18 directive. More boots is
deliberately the wrong lever for this harness: the lever is more iterations and
better site labelling inside one boot, and a run that needs 200 boots to find
something is a run whose sites are wrong.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Sep 1, 2026
…oor/seed ratchet layout.rs's #742 commit claimed "there is nowhere left for either bound and the allocators' seed/floor to drift apart again" and that the MMAP_REGION_START accept-assert is "a proof about the lowest address a real allocator can hand out" -- neither was backed by anything that would fail a build (PR #744 review B2, the same unpinned-drift shape that produced #742 in the first place). Add tests/mmap_floor_structure.rs: a census-shaped ratchet (never a literal file/line list, per the #549/#551/#527 lesson) pinning, by (file, enclosing function, occurrence count), the six producer floor comparisons #742 fixed and the five mmap_hint seed sites, all resolving to the named MMAP_REGION_START/_END constants rather than an independently hardcoded literal. Mutation-proven against the real tree (temporarily reverted one graphics.rs site to the stale 0x1000_0000 literal; both the census test and a direct anti-regression grep failed with the right diagnostic; reverted clean) as well as nine synthetic-source tests covering both directions (re-literalized/deleted sites redden; reflowed lines and renamed locals stay green). Rewrite layout.rs's prose to describe what the ratchet actually proves -- today's known producers, not a mathematical impossibility of future drift -- and close five cheap non-blocking review items along the way: the non-emptiness assert's honest-scope note now records its real distinguishing mutation (found and verified: START=0x7FFF_FEE0_0000, END=0x5000_0000 reddens only that assert) instead of a non-distinguishing one; the N1 justification's inverted "falls below" is corrected to "falls above" with the omitted stack-arm half restored; sys_mmap's MAP_FIXED comment now states its Linux-semantics deviation explicitly; sys_munmap now uses checked_add like its sys_mmap sibling instead of relying on incidental release-profile wraparound. Also corrects the #728 aarch64 durable record (review B1): PATCHed the posted issue comment, which rested on three claims falsified by the kernel's own source (a spinning writer does not bound the livelock to one CPU once new readers must also spin at acquisition; ordinary readers do not park when WRITER/UPGRADED is already set; aarch64's -smp 4+ configs are not a structural mitigation, just a workload fact that no boot service writes ext2 during the concurrent-read window today). Widened #728's title/body from x86-only to both arches and both filesystems (HOME_EXT2 carries the identical shape and was never mentioned). No lock-discipline fix attempted -- that remains its own arc. Verification: x86 and aarch64 builds clean (zero warnings), teardown_structure 81/81, all listed host structure suites green, aarch64 strict boot 20/20. Co-Authored-By: Ryan Breen <ryan.breen@gmail.com> Co-Authored-By: Claude Code <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Sep 2, 2026
review-707.md finding F5: the README (and issue #763) quoted `grep -n -- "--features" docker/qemu/run-aarch64-*.sh` and then reported a 5-script census. Re-running that exact command: the glob matches 16 scripts, 9 of which contain `--features`, not 5. Four gate scripts were missing from the original list -- run-aarch64-refusal-drain-gate.sh, run-aarch64-percpu-stack-custody-gate.sh, run-aarch64-tty-oracle-gate.sh, and run-aarch64-arma609-arm.sh -- two of which build kernels with their own additional oracle feature sets. Separately, run-aarch64-boot-test-strict.sh was mischaracterized as building "with no --features at all." It builds nothing (0 of 2 `cargo build` mentions in the file execute; both are echo suggestions) -- it is the strict kernel-merge gate and it refuses any kernel that is not --features boot_tests via a 7-marker literal census, the opposite of what the original sentence said. The conclusion survives: 0 of 9 named scripts build --features testing in gate form. But "the 5 gates" was the wrong census shape to publish and act on -- this is the same closed-name-list failure mode this campaign has been bitten by three times before (#549, #551, #527-r1). claim-lint: scripts/claim-lint.py --files docs/planning/green-program/sockets/serials/707-2026-09-02/README.md -> exit 0 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Sep 4, 2026
…ir own bytes The expectations the previous commit removed from the kernel land here, in the two scripts that own the QEMU command lines, derived the way the pre-existing EXPECTED_VIRTIO_BLOCK leg derives its count: by grepping this file's own -device/-netdev flag lines (#549/#551/[[gate-target-fidelity-528]], census not a hand-pinned list). One table per script maps a QEMU device model to the vendor:device and class the kernel's PCI_FN line prints for it: virtio-blk-pci with disable-modern=on -> 1af4:1001 class=01/00, e1000 -> 8086:100e class=02/00. run-x86-boot-tests.sh passes no NIC flag, so its e1000 expectation comes from QEMU's implicit-default-NIC rule (no -net/-netdev/-nic option present, and -nic none is what suppresses the default) -- the same rule the CENSUS_NETWORK >= 1 leg above it already relies on, here tightened from ">= 1 network device" to "exactly one 8086:100e". Both scripts carry both arms of that rule so a future flag edit moves the expectation with the bytes in either direction. claim-lint:ok: "-nic none" is a QEMU flag name, not a quantifier; the rule it names is documented at the CENSUS_NETWORK leg of docker/qemu/run-x86-boot-tests.sh. Asserted per script: the PCI_FN line count equals the kernel's own PCI_FN_TOTAL; the matched function count per vendor:device equals the flag-derived count; and each matched function shows BAR 0 with a non-zero size AND a non-zero address and an interrupt line other than 0xff. Both BAR halves are checked because the round-1 predicate checked size > 0 while its message claimed a non-zero decode (F7). claim-lint:ok: "non-zero" names the predicate; it is the awk block in docker/qemu/run-x86-boot-tests.sh and docker/qemu/run-x86-prod-profile-boot-test.sh. Each failure arm is a `test` under set -e, so it reaches each script's ERR trap and produces the canonical verdict line plus the serial tail; the round-1 `exit 1` arm that bypassed the trap is gone (F9). No per-drive identity labels (F11) and no `enumerated >= expected` count that cannot fire (F12). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ryanbreen
added a commit
that referenced
this pull request
Sep 4, 2026
…nd is now derived tests/dispatch_strand_census_structure.rs asserted the literal `stale_limit_ms = 5000` in scripts/x86-strand-census.sh. Deriving the bound from #766 reddened it, which is the ratchet doing its job -- but a literal value in a ratchet is a second place to update, and the bound TIGHTENS when #766 lands, so the next change would redden it again for no defect. That is this repository's binding lesson from the census ratchets of #549 and #551. The ratchet now holds the SHAPE: an assignment to stale_limit_ms exists, the age line prints the bound it applied, and the header carries the derivation's source document. Removing the citation reddens host_consumers_have_no_removed_record_dependency and 0 other tests. The VALUE is held where it is observable -- tests/x86_gate_verdict_test.rs asserts the printed `bound 15000 ms` on a committed capture and on a synthesised stale one. dispatch_strand_census_structure: 6 passed, 0 failed. x86_gate_verdict_test: 20 passed, 0 failed. claim-lint: python3 scripts/claim-lint.py --base acf55c4 -> exit 0 Co-authored-by: Ryan Breen <ryan@breen.xyz> Co-authored-by: Claude Code <noreply@anthropic.com>
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.
Summary
Closes 9 of the 14 sensitivity-regression findings from the #549 structural-anchor refactor review, hardening
tests/teardown_structure.rsso the ratchets stay mutation-sensitive.validate_process_page_table_retire_site— hoisted thecontainsguards abovefunction_bodycalls so a removedreclaim_bounded/reclaim_deferred_process_resources_for_passreports a failure instead of panicking and discarding every later validator's accumulated diffs.definition_offsetsno longer treats any;before the body brace as a bodyless declaration — a 10th blocking primitive with an array-type parameter/return ([u64; 32]) is no longer silently invisible to the census.header_cfgnow folds all stacked#[cfg]attributes instead of keeping only the last one, so cfg-narrowing (e.g. adding#[cfg(feature = "boot_tests")]above an existing#[cfg(target_arch = "aarch64")]) is visible to the anchor.RAW_SCHEDULER_LOCK_SITESmatches any identifier ending inSCHEDULER, not just the bare name, closing the suffix-named-global bypass (GLOBAL_SCHEDULER,PROCESS_SCHEDULER, etc.).assert!/assert_eq!calls converted torecord()so a single failure no longer short-circuits the rest of the checks in the same test.byte == b'\n') to dodge an over-broad detector.expected_censusnow rejects duplicate anchor keys with an explicit panic instead of silently summing them into one allowance.#[cfg]no longer renders mid-path.BLOCKING_NAMESdrops the vestigial trailing(left over from the line-substring matching era.Evidence
Full re-verification from scratch (
verify2-evidence.md):cleanup_for_exec) stay green — this is the acceptance criterion for de-brittling and it holds..terminate(,.terminate_minimal(, a fourthProcessId::new(1),.kernel_stack_allocation =,on_process_exit() turned red and restored clean.Follow-ups
The remaining findings (F6, F7, F10, F13, F14 — evidence gaps and efficiency/readability nits that don't affect current mutation-sensitivity) are tracked at #550.
Co-Authored-By: Claude Code noreply@anthropic.com