Skip to content

test: close sensitivity regressions from #549 structural-anchor review - #551

Merged
ryanbreen merged 3 commits into
mainfrom
fix/teardown-anchor-followups
Aug 13, 2026
Merged

test: close sensitivity regressions from #549 structural-anchor review#551
ryanbreen merged 3 commits into
mainfrom
fix/teardown-anchor-followups

Conversation

@ryanbreen

Copy link
Copy Markdown
Owner

Summary

Closes 9 of the 14 sensitivity-regression findings from the #549 structural-anchor refactor review, hardening tests/teardown_structure.rs so the ratchets stay mutation-sensitive.

  • F1: validate_process_page_table_retire_site — hoisted the contains guards above function_body calls so a removed reclaim_bounded/reclaim_deferred_process_resources_for_pass reports a failure instead of panicking and discarding every later validator's accumulated diffs.
  • F2: definition_offsets no 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.
  • F3: header_cfg now 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.
  • F4: RAW_SCHEDULER_LOCK_SITES matches any identifier ending in SCHEDULER, not just the bare name, closing the suffix-named-global bypass (GLOBAL_SCHEDULER, PROCESS_SCHEDULER, etc.).
  • F5: remaining bare assert!/assert_eq! calls converted to record() so a single failure no longer short-circuits the rest of the checks in the same test.
  • F8: the residual-pin regex is narrowed to the actual offset-to-line-number conversion idiom instead of the kernel-test source being contorted (byte == b'\n') to dodge an over-broad detector.
  • F9: expected_census now rejects duplicate anchor keys with an explicit panic instead of silently summing them into one allowance.
  • F11: anchor constants are readable again (no longer rustfmt-exploded across 5 lines per tuple) and #[cfg] no longer renders mid-path.
  • F12: BLOCKING_NAMES drops the vestigial trailing ( left over from the line-substring matching era.

Evidence

Full re-verification from scratch (verify2-evidence.md):

  • 13/13 tests pass, zero warnings, zero errors — clean baseline and final state.
  • 14/14 designated mutations (M1–M14) each fail the mapped test with a real diff and restore clean.
  • Both de-brittling negative controls (C1: 40 blank lines + comment block shifting every pinned line; C2: intra-function statement reorder/reflow in cleanup_for_exec) stay green — this is the acceptance criterion for de-brittling and it holds.
  • All six per-fix controls (D1–D6, covering F1/F2/F3/F4/F9/F5) turned the mapped test red with a real diff and restored clean.
  • All five family controls (finding 7's table: .terminate(, .terminate_minimal(, a fourth ProcessId::new(1), .kernel_stack_allocation =, on_process_exit() turned red and restored clean.
  • Residual-pin detector: 0 hits.

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

ryanbreen and others added 3 commits August 12, 2026 22:36
…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
ryanbreen merged commit 4aaf004 into main Aug 13, 2026
@ryanbreen
ryanbreen deleted the fix/teardown-anchor-followups branch August 13, 2026 03:13
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>
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>
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