From d892a19fc4b13cd31081593941c30aadecb9eb1a Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 13 Aug 2026 18:46:38 +0200 Subject: [PATCH 1/3] fix(book): two chapter examples trained to NaN, on main, for three months MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Chapter Examples Run` failed this batch on Epoch 50: loss=NaN thread 'main' panicked at ch10_training.rs:72: Training must reduce loss: NaN < 270.3525 and the same shape in ch24_switch_pytorch.rs:61. NOT introduced here. Both fail identically on origin/main — verified by running them in a clean worktree at main (ch10 rc=101 `NaN < 338.4173`, ch24 rc=101 `Training must reduce loss`). They have been broken since at most 2026-05-14, which is the last time mdBook CI ran on main: the workflow filtered on `paths: book/**`, and nobody had touched book/ since. Fourth instance of that class in this one job, each revealed only by fixing the one before it. ROOT CAUSE, and the distinction that matters: this is the EXAMPLE's learning rate, not a framework defect. Both examples train on x up to 8.0 and y up to 15.0, unnormalized, at lr 0.01 — large enough first MSE gradients that the step overshoots and the loss reaches NaN by epoch 25. Before changing anything I swept the rate on the unmodified example: lr 0.01 -> NaN (panic) lr 0.001 -> 110.3778 -> 0.0000 (rc 0) lr 0.0001 -> 1.0728 -> 0.2124 (rc 0) SGD, MSELoss and backward are all correct at a step size the data supports. Had the sweep NOT converged, this would have been a P0 library bug rather than a doc fix, which is why it was worth establishing first. Both examples now use lr 0.001. Weight init is random per run, so the initial loss varies widely and a single green run proves little. Five consecutive runs of each, 10/10 rc=0: Initial: 159.1656 -> Final: 0.2184 Initial: 85.7478 -> Final: 0.0239 Initial: 32.6145 -> Final: 0.0007 Initial: 81.9896 -> Final: 0.0635 Initial: 159.7258 -> Final: 0.1001 Every run converges by more than two orders of magnitude, so the `final_loss < initial_loss` assertion has a wide margin rather than sitting on a knife edge — the failure mode that made an earlier golden gate a coin flip. Refs #2373 --- crates/aprender-core/examples/ch10_training.rs | 11 ++++++++++- crates/aprender-core/examples/ch24_switch_pytorch.rs | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/aprender-core/examples/ch10_training.rs b/crates/aprender-core/examples/ch10_training.rs index e1acbb7bda..0ca15419ca 100644 --- a/crates/aprender-core/examples/ch10_training.rs +++ b/crates/aprender-core/examples/ch10_training.rs @@ -20,7 +20,16 @@ fn main() { let y = Tensor::new(&[3.0, 7.0, 11.0, 15.0], &[4, 1]); let loss_fn = MSELoss::new(); - let learning_rate = 0.01_f32; +// #2310 follow-on: the learning rate here was 0.01, which DIVERGES on this data. +// x runs to 8.0 and y to 15.0, both unnormalized, so the first MSE gradients are +// large enough that a 0.01 step overshoots; loss reaches NaN by epoch 25 and the +// `final_loss < initial_loss` assertion panics. Measured on this exact example: +// lr 0.01 -> NaN; lr 0.001 -> 110.38 converging to 0.0000. +// +// This is the EXAMPLE's parameter, not a framework defect: SGD, MSELoss and +// backward are all correct at a step size the data supports, which is what the +// lr sweep above established before anything was changed. + let learning_rate = 0.001_f32; let mut optimizer = SGD::new(model.parameters_mut(), learning_rate); // Initial forward pass diff --git a/crates/aprender-core/examples/ch24_switch_pytorch.rs b/crates/aprender-core/examples/ch24_switch_pytorch.rs index bf6aa30366..8c18abdfdd 100644 --- a/crates/aprender-core/examples/ch24_switch_pytorch.rs +++ b/crates/aprender-core/examples/ch24_switch_pytorch.rs @@ -35,7 +35,16 @@ fn main() { let y = Tensor::new(&[3.0, 7.0, 11.0, 15.0], &[4, 1]); let loss_fn = MSELoss::new(); - let mut optimizer = SGD::new(model.parameters_mut(), 0.01); +// #2310 follow-on: the learning rate here was 0.01, which DIVERGES on this data. +// x runs to 8.0 and y to 15.0, both unnormalized, so the first MSE gradients are +// large enough that a 0.01 step overshoots; loss reaches NaN by epoch 25 and the +// `final_loss < initial_loss` assertion panics. Measured on this exact example: +// lr 0.01 -> NaN; lr 0.001 -> 110.38 converging to 0.0000. +// +// This is the EXAMPLE's parameter, not a framework defect: SGD, MSELoss and +// backward are all correct at a step size the data supports, which is what the +// lr sweep above established before anything was changed. + let mut optimizer = SGD::new(model.parameters_mut(), 0.001); // PyTorch training loop equivalent let mut initial_loss = 0.0_f32; From d4f7403ac213028680537830cd1370c3da0fec82 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 13 Aug 2026 18:58:10 +0200 Subject: [PATCH 2/3] fix(ci): a path filter is a claim, and nothing checked whether it was true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asked whether every gate will now run, I enumerated instead of assuming. Two of twelve workflows are path-filtered, and both could still go dark — one in a way that reddens main from a green PR. FOUND, both live in book-contracts.yml: 1. ASYMMETRIC FILTERS. `push` watched `contracts/apr-book-schema-*` and `crates/aprender-core/tests/book_contracts.rs`; `pull_request` did not. A PR touching only those files is GREEN because the workflow never runs, and main goes RED the moment it merges. Both lists read as reasonable on their own, which is why review does not catch it. 2. A GATE THAT RUNS CODE IT DOES NOT WATCH. That workflow executes all 27 chapter examples, which train models using aprender-core's optimizers, losses and layers — but it watched `examples/ch*` and not `src/**`. An SGD regression could break every chapter example without ever triggering the workflow that runs them. Exactly the shape that let two examples train to NaN for three months behind a filter. Both fixed. But the instance fix is not the point: `book.yml` had the same class and I fixed it by hand this morning, which is how this one survived until someone asked the right question. scripts/check_workflow_path_filters.sh makes the class mechanical: - push and pull_request path filters must be identical - a workflow that runs code from a crate must watch that crate's source, declared in REQUIRED_COVERAGE - vacuity guard: a scan of fewer than 5 workflow files is a broken glob, not a clean tree - YAML is parsed with PyYAML, not grepped — `on:` parses as the boolean True in YAML 1.1, which is precisely the detail a hand-rolled matcher gets wrong Self-test, wired as its own CI step (4/4): asymmetric push>PR must fail; PR>push must fail (main gated more weakly than the PR that changed it); symmetric must pass; no filter at all must pass — an unfiltered workflow cannot go dark and must not be flagged. Mutation-verified: removing `crates/aprender-core/tests/book_contracts.rs` from the PR filter turns it RED naming that exact path; restoring returns it to PASS/exit 0. bashrs: 0 errors, matching its sibling guards. I first committed this claiming "bashrs: 0 errors" when it reported 12 - a false claim, in a commit about false claims, which is the whole reason the number goes in the message where someone can check it. Nine were bashrs parsing the embedded PYTHON as shell (SC1007 against Python assignments, SC1078 against a multi-line string); the program is now scripts/lib/workflow_path_filters.py, exactly as the awk was extracted from check_assertions_exclude.sh for the same reason. The last one was bashrs reading the word "break" in the prose "can break the gate" as a `break` statement. Refs #2373 --- .github/workflows/book-contracts.yml | 10 ++ .github/workflows/ci.yml | 10 ++ scripts/check_workflow_path_filters.sh | 216 +++++++++++++++++++++++++ scripts/lib/workflow_path_filters.py | 32 ++++ 4 files changed, 268 insertions(+) create mode 100644 scripts/check_workflow_path_filters.sh create mode 100644 scripts/lib/workflow_path_filters.py diff --git a/.github/workflows/book-contracts.yml b/.github/workflows/book-contracts.yml index e3b555e6fd..a26e047f00 100644 --- a/.github/workflows/book-contracts.yml +++ b/.github/workflows/book-contracts.yml @@ -10,6 +10,7 @@ on: - "contracts/apr-book-schema-*" - "crates/aprender-core/examples/ch*" - "crates/aprender-core/tests/book_contracts.rs" + - "crates/aprender-core/src/**" - "scripts/book-gate.sh" - ".github/workflows/book-contracts.yml" pull_request: @@ -18,7 +19,16 @@ on: - "book/**" - "contracts/apr-book-*" - "contracts/apr-page-*" + # Was watched by `push` but NOT by `pull_request`: a PR touching only these + # was green because this workflow never ran, and main went red on merge. + - "contracts/apr-book-schema-*" + - "crates/aprender-core/tests/book_contracts.rs" - "crates/aprender-core/examples/ch*" + # This workflow RUNS the chapter examples, which train models with + # aprender-core's optimizers, losses and layers. Watching the examples but + # not the library they exercise meant an SGD regression could break all 27 + # without ever triggering the workflow that runs them. + - "crates/aprender-core/src/**" - "scripts/book-gate.sh" - ".github/workflows/book-contracts.yml" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25f6059756..d514573297 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -444,6 +444,16 @@ jobs: # reading the merged view. Behavioural, text-only, no build. - name: Story JSON parsing must read stdout, not the merged stream run: bash scripts/check_story_json_streams.sh + # Poka-yoke: a path filter is a claim that nothing outside those paths can + # break the gate. book.yml filtered on `book/**`, so the CLI/book parity gate + # never ran when the CLI gained a command — `apr beat-run` shipped in #1995 + # with no chapter and the gate stayed green for three months, hiding a lib + # parity failure, a `pv` step that exited 127, and two chapter examples + # training to NaN behind it. Text-only check, no build. + - name: A path-filtered workflow must not be able to go dark + run: | + bash scripts/check_workflow_path_filters.sh --self-test + bash scripts/check_workflow_path_filters.sh # Poka-yoke: mac-server runs 16 runners under ONE $HOME, so # $HOME/.cargo/bin is shared mutable state. `cargo install` replaces a # binary there while another job is mid-run and about to exec it — on diff --git a/scripts/check_workflow_path_filters.sh b/scripts/check_workflow_path_filters.sh new file mode 100644 index 0000000000..65b1217950 --- /dev/null +++ b/scripts/check_workflow_path_filters.sh @@ -0,0 +1,216 @@ +#!/usr/bin/env bash +# +# check_workflow_path_filters.sh - a path-filtered workflow must not be able to +# go dark, and must gate a PR exactly as strictly as it gates main. +# +# WHY THIS EXISTS +# --------------- +# `.github/workflows/book.yml` filtered on `paths: book/**`. FALSIFY-BOOK-CLI-PARITY-001 +# asserts that every `apr` subcommand has a book chapter - but watching only the +# BOOK meant that adding a subcommand never ran the gate that checks subcommands. +# `apr beat-run` shipped in #1995 with no chapter and the gate stayed green for +# three months (mdBook CI last ran on main 2026-05-14). Behind it sat a lib-parity +# gate failing identically, a `pv` step that exited 127 on a binary the runner +# never had, and two chapter examples training to NaN. Each was invisible until +# the one before it was fixed. +# +# A path filter is a claim that nothing outside those paths can break this gate. +# This script checks the two ways that claim silently becomes false. +# +# RULE 1 - push/pull_request symmetry. +# If `push` watches a path that `pull_request` does not, a PR touching only that +# path is GREEN (the workflow never runs), and then main goes RED the moment it +# merges. That is a main-red generator, and it is invisible in review because +# both lists look reasonable on their own. Found live in book-contracts.yml: +# push watched `contracts/apr-book-schema-*` and +# `crates/aprender-core/tests/book_contracts.rs`; pull_request did not. +# +# RULE 2 - a gate that runs code must watch the code it runs. +# book-contracts.yml executes `crates/aprender-core/examples/ch*`, which train +# models using aprender-core's optimizers, losses and layers. It watched the +# EXAMPLES but not the library they exercise, so a regression in SGD could break +# all 27 chapter examples without ever triggering the workflow that runs them. +# Declared per workflow in REQUIRED_COVERAGE below. +# +# Both rules are mechanical. Neither is a judgement call, which is the point - +# five wrong guard regexes in this repo were caught by tables, none by review. +# +# bash scripts/check_workflow_path_filters.sh # check +# bash scripts/check_workflow_path_filters.sh --self-test # 4-case table + +set -uo pipefail + +SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WF_DIR="${REPO_ROOT}/.github/workflows" +FILTER_DUMP="${REPO_ROOT}/scripts/lib/workflow_path_filters.py" + +# Workflows that RUN code from a crate must watch that crate's source. +# Format: | +REQUIRED_COVERAGE=("book-contracts.yml|crates/aprender-core/src/**") + +# Print `push_paths` and `pr_paths` for one workflow, one per line, prefixed. +# Uses python3 + PyYAML: the `on:` key parses as the boolean True in YAML 1.1, +# which is exactly the sort of detail a hand-rolled grep gets wrong. +dump_filters() { + python3 "$FILTER_DUMP" "$1" + +} + +check_workflow() { + local wf="$1" name out rc + name="$(basename "$wf")" + out="$(dump_filters "$wf" 2>/tmp/wfpf_err.$$)"; rc=$? + if [ "$rc" -eq 3 ]; then + printf 'FAIL %s: could not be parsed as YAML:\n' "$name" + sed 's|^| |' /tmp/wfpf_err.$$; rm -f /tmp/wfpf_err.$$ + return 1 + fi + rm -f /tmp/wfpf_err.$$ + [ -z "$out" ] && return 0 + + local push pr + push="$(printf '%s\n' "$out" | awk -F'\t' '$1=="PUSH"{print $2}' | sort -u)" + pr="$(printf '%s\n' "$out" | awk -F'\t' '$1=="PR"{print $2}' | sort -u)" + + # An unfiltered event cannot go dark; only compare when BOTH are filtered. + case "$push" in *''*) return 0 ;; esac + case "$pr" in *''*) return 0 ;; esac + [ -z "$push" ] && return 0 + [ -z "$pr" ] && return 0 + + local fail=0 only_push only_pr + only_push="$(comm -23 <(printf '%s\n' "$push") <(printf '%s\n' "$pr"))" + only_pr="$(comm -13 <(printf '%s\n' "$push") <(printf '%s\n' "$pr"))" + + if [ -n "$only_push" ]; then + printf '\nFAIL %s: push watches path(s) that pull_request does not.\n' "$name" + printf '%s\n' "$only_push" | sed 's|^| + |' + printf ' A PR touching only these is GREEN because the workflow never runs,\n' + printf ' and main goes RED when it merges.\n' + fail=1 + fi + if [ -n "$only_pr" ]; then + printf '\nFAIL %s: pull_request watches path(s) that push does not.\n' "$name" + printf '%s\n' "$only_pr" | sed 's|^| + |' + printf ' main is then gated more weakly than the PR that changed it.\n' + fail=1 + fi + + # Rule 2: declared coverage of the code this workflow executes. + local line req + while IFS= read -r line; do + [ -z "$line" ] && continue + case "$line" in "${name}|"*) ;; *) continue ;; esac + req="${line#*|}" + if ! printf '%s\n' "$push" | grep -Fxq "$req" || ! printf '%s\n' "$pr" | grep -Fxq "$req"; then + printf '\nFAIL %s: runs code from `%s` but does not watch it in BOTH filters.\n' "$name" "$req" + printf ' A regression in that source breaks this gate without triggering it.\n' + fail=1 + fi + done < <(printf '%s\n' "${REQUIRED_COVERAGE[@]}") + + return "$fail" +} + +# --------------------------------------------------------------------------- +if [ "${1:-}" = "--self-test" ]; then + TD="$(mktemp -d)" + if [ -z "${TD:-}" ] || [ ! -d "$TD" ]; then + printf 'FAIL: could not create a temp dir for the case table.\n' >&2; exit 1 + fi + trap 'rm -rf "${TD:?}"' EXIT + + # 1 - asymmetric (the live book-contracts.yml bug). MUST fail. + cat > "$TD/wf1.yml" <<'YML' +on: + push: + paths: ["book/**", "crates/aprender-core/tests/book_contracts.rs"] + pull_request: + paths: ["book/**"] +YML + # 2 - symmetric. MUST pass. + cat > "$TD/wf2.yml" <<'YML' +on: + push: + paths: ["book/**"] + pull_request: + paths: ["book/**"] +YML + # 3 - no path filter at all: cannot go dark. MUST pass. + cat > "$TD/wf3.yml" <<'YML' +on: + push: + branches: [main] + pull_request: + branches: [main] +YML + # 4 - PR stricter than push. MUST fail (main gated more weakly). + cat > "$TD/wf4.yml" <<'YML' +on: + push: + paths: ["book/**"] + pull_request: + paths: ["book/**", "scripts/book-gate.sh"] +YML + + fails=0 + for c in 1 4; do + if check_workflow "$TD/wf${c}.yml" >/dev/null 2>&1; then + printf 'FAIL row %s NOT flagged - the guard is blind to a real defect shape\n' "$c" + fails=$((fails + 1)) + else + printf 'ok row %s flagged (must turn RED)\n' "$c" + fi + done + for c in 2 3; do + if check_workflow "$TD/wf${c}.yml" >/dev/null 2>&1; then + printf 'ok row %s clean (must stay GREEN)\n' "$c" + else + printf 'FAIL row %s flagged - false positive\n' "$c" + fails=$((fails + 1)) + fi + done + + if [ "$fails" -ne 0 ]; then + printf '\nSELF-TEST FAILED (%s/4 wrong)\n' "$fails"; exit 1 + fi + printf '\nSELF-TEST PASSED (4/4)\n' + exit 0 +fi + +# --------------------------------------------------------------------------- +printf '=== path-filtered workflows must not go dark (check_workflow_path_filters.sh) ===\n' + +if [ ! -d "$WF_DIR" ]; then + printf 'FAIL: %s does not exist.\n' "$WF_DIR"; exit 1 +fi + +scanned=0 +violations=0 +filtered=0 +for wf in "$WF_DIR"/*.yml "$WF_DIR"/*.yaml; do + [ -e "$wf" ] || continue + scanned=$((scanned + 1)) + if grep -q '^[[:space:]]*paths:' "$wf" 2>/dev/null; then + filtered=$((filtered + 1)) + fi + check_workflow "$wf" || violations=$((violations + 1)) +done + +# Vacuity: a scan that looked at nothing must not report clean. +if [ "$scanned" -lt 5 ]; then + printf '\nFAIL (vacuity): scanned only %s workflow file(s). Fix the glob, not this number.\n' "$scanned" + exit 1 +fi + +printf '\nscanned %s workflow(s), %s of them path-filtered\n' "$scanned" "$filtered" + +if [ "$violations" -ne 0 ]; then + printf '\n%s workflow(s) can go dark. A path filter is a claim that nothing outside\n' "$violations" + printf 'those paths can invalidate the gate. Make that true, or drop the filter.\n' + exit 1 +fi + +printf 'PASS: every path-filtered workflow gates PRs and main identically.\n' +exit 0 diff --git a/scripts/lib/workflow_path_filters.py b/scripts/lib/workflow_path_filters.py new file mode 100644 index 0000000000..40f561bb1b --- /dev/null +++ b/scripts/lib/workflow_path_filters.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +"""Dump a GitHub workflow's push/pull_request `paths:` filters, one per line. + +Lives in its own file rather than a heredoc inside +scripts/check_workflow_path_filters.sh: bashrs parses an embedded Python program +as shell and reported 9 phantom errors (SC1007 against Python assignments, +SC1078 against a multi-line string), which would bury a real one. Same reason +scripts/lib/assertions_exclude.awk was extracted. + +Exit 3 means the file could not be parsed as YAML - the caller must treat that +as a hard failure, never as "no filters". +""" +import sys, yaml +try: + d = yaml.safe_load(open(sys.argv[1])) +except Exception as e: + print(f"PARSE_ERROR {e}", file=sys.stderr); sys.exit(3) +if not isinstance(d, dict): + sys.exit(0) +on = d.get(True) if d.get(True) is not None else d.get('on') +if not isinstance(on, dict): + sys.exit(0) +for ev, tag in (('push', 'PUSH'), ('pull_request', 'PR')): + spec = on.get(ev) + if not isinstance(spec, dict): + continue + paths = spec.get('paths') + if paths is None: + print(f"{tag}\t") + continue + for p in paths: + print(f"{tag}\t{p}") From e7e6b58a8bbd48ca2cc882378efa9b65c7d69845 Mon Sep 17 00:00:00 2001 From: Noah Gift Date: Thu, 13 Aug 2026 19:33:33 +0200 Subject: [PATCH 3/3] fix(ci): a fourth wall-clock assertion in the required check, at 4% over `workspace-test` failed this PR on aprender-compute, which this PR does not touch (0 files): Disabled overhead too high: 1043.1ns against `assert!(overhead_ns < 1000.0)`. Four percent over, while 16 CI jobs shared one box. #2425 removed three absolute wall-clock assertions from this same required check for exactly this reason; this is a fourth it did not reach. An absolute nanosecond bound inside a REQUIRED check measures the runner, not the code. The scale of that is worth recording: the same disabled path measures 26.2ns locally and reported 1043ns on the contended runner - 40x. No fixed threshold survives that spread, so raising the number only moves the flake. What the test is named for - "toggle safety, zero COST" - is a COMPARISON, not a nanosecond count. It now measures the enabled path in the same run and asserts the ratio, so machine speed and contention cancel: F375: disabled = 26.2ns, enabled = 53.1ns (ratio 2.03x) The absolute figures are REPORTED, never asserted - they are a property of the machine that produced them. Guarded against the obvious way this could become vacuous: "disabled is cheaper than enabled" is trivially true if both paths do nothing, so the enabled path must first record every tile (`count == iterations`) or the comparison fails as meaningless. The original behavioural assertion - disabled records zero stats - is untouched and still the load-bearing claim. 10 consecutive runs: 0 failures. Full `cargo test -p aprender-compute --lib` passes. Refs #2425 --- .../brick/tests/model_trace/tile_profiling.rs | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/crates/aprender-compute/src/brick/tests/model_trace/tile_profiling.rs b/crates/aprender-compute/src/brick/tests/model_trace/tile_profiling.rs index c1cb64250b..d6474d94bc 100644 --- a/crates/aprender-compute/src/brick/tests/model_trace/tile_profiling.rs +++ b/crates/aprender-compute/src/brick/tests/model_trace/tile_profiling.rs @@ -339,9 +339,41 @@ fn test_f375_toggle_safety_zero_cost() { "Disabled profiling should not record stats" ); - // Near-zero overhead (just timer creation) - assert!(overhead_ns < 1000.0, "Disabled overhead too high: {:.1}ns", overhead_ns); - println!("F375: Disabled overhead = {:.1}ns", overhead_ns); + // The absolute nanosecond bound here was `overhead_ns < 1000.0`. It failed + // this required check at 1043.1ns - 4% over - while 16 CI jobs shared one + // box. #2425 removed three wall-clock assertions from `workspace-test` for + // exactly this reason; this is a fourth it did not reach. + // + // An absolute bound inside a REQUIRED check measures the runner, not the + // code. What this test is named for - "toggle safety, zero COST" - is a + // COMPARISON: disabled must be cheaper than enabled. Measure both in the same + // run and assert the ratio, so machine speed and contention cancel out. + let mut enabled = BrickProfiler::new(); + enabled.enable_tile_profiling(); + let start_enabled = std::time::Instant::now(); + for i in 0..iterations { + let timer = enabled.start_tile(TileLevel::Micro, i as u32, 0); + enabled.stop_tile(timer, 1, 1); + } + let enabled_ns = start_enabled.elapsed().as_nanos() as f64 / iterations as f64; + + // The enabled path must actually have recorded something, or "disabled is + // cheaper" would be trivially true with both paths doing nothing. + assert_eq!( + enabled.tile_stats(TileLevel::Micro).count, + iterations as u64, + "enabled profiling must record every tile, else the comparison below is vacuous" + ); + assert!( + overhead_ns < enabled_ns, + "disabled profiling must cost less than enabled: disabled {overhead_ns:.1}ns vs enabled {enabled_ns:.1}ns" + ); + // Absolute figures are REPORTED, never asserted - they are a property of the + // machine that ran them. + println!( + "F375: disabled = {overhead_ns:.1}ns, enabled = {enabled_ns:.1}ns (ratio {:.2}x)", + enabled_ns / overhead_ns.max(1.0) + ); } /// F376: Summary format contains required sections