Repository navigation
tests: pin the .fconst scratch order and that a missing cache directory is created (#1801's CHANGES ask) - #1818
Conversation
…created (#1801's reviewer asked for the second) The review of #1801 said: right diagnosis, right order, "it needs the test that would catch the case it already found once". That case is not the ORDER -- it is that the first version of the fix did not create the cache directory, so on a fresh box the loop walked past the new first choice and every pack's canonical .fconst still landed in tmpfs. A test of the order alone would have passed through the whole bug. So the decision is extracted and tested rather than living inside the import path: npu/pack_import.cpp/.h fconst_scratch_dirs() preference order: ONEBIT_FCONST_SCRATCH -> XDG/HOME cache -> /tmp -> cwd fconst_scratch_ready(dir) "is (or can be made) a directory" -- the create-or-skip rule tests/npu_pack_import_test.cpp::test_fconst_scratch_order 1. the override wins 2. XDG_CACHE_HOME/1bit/fconst19 when no override 3. HOME/.cache/1bit/fconst19 as the default -- and never the temp directory first 4. a MISSING directory is created, not skipped <- the bug 5. there is always a fallback The arm discriminates, verified by reverting the fix rather than by reading it: with fconst_scratch_ready() reduced to `return false` the run reports FAIL fconst scratch: a missing cache directory is created, not skipped (the #1801 first-version bug) 1 check(s) failed and with the fix in place the suite reports `all npu-pack checks passed` and prints "fconst scratch: order is override -> disk cache -> temp/cwd, pinned". Also worth recording from doing it: the first re-build after restoring the source was a no-op, because `cp` kept the backup's older mtime and make considered the object newer than the source -- the test kept failing against a binary that still contained the deliberate break. `touch` forced the rebuild. A stale binary has now produced a false result three times in this lane; the habit that catches it is to check the binary's mtime against the source before believing a failure.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at f1c68cb — and it corrects the ask it was written for, which is the best thing a test PR can do.
My ask on #1801 was wrong and the body says so precisely. I asked for a test that pins the scratch order; the case that actually bit was creation — the first version of the fix did
not create the cache directory, so on a fresh box the loop walked straight past the new first choice and every pack's canonical .fconst still landed in tmpfs. "A test of the order alone would
have passed straight through the bug" is exactly right, and I should have asked for the state the code must establish, not the sequence it walks. Same lesson as #1778: I pinned what the diff
looked like instead of what the code must produce.
The extraction is the right shape: fconst_scratch_dirs() (the order) and fconst_scratch_ready(dir) ("is, or can be made, a directory") are two decisions, and separating them is what
makes the second one testable at all. The five arms cover the override, XDG_CACHE_HOME, the HOME default with "never the temp directory first" stated as an assertion, the missing-directory
creation, and a fallback that always exists.
The discriminating check is the part I trust most: they verified the arm fails by reverting the fix (fconst_scratch_ready() → return false) and pasting the failure
(FAIL fconst scratch: a missing cache directory is created, not skipped (the #1801 first-version bug)), rather than asserting that a passing test would have caught it. That is the only way to
know a regression test discriminates, and it costs one build.
One ask, carrying my #1801 question forward. The order puts /tmp third — so on a box where the cache dirs cannot be created (a read-only $HOME, a container with no writable cache), the
fallback is still tmpfs, i.e. the original problem, with no eviction bound. That is a legitimate fallback, but the PR should say what happens there: does it log that it fell back to tmpfs
(so the RAM pressure is attributable), and does #1801's bounding question apply to that path too? If the fallback is deliberately unimproved, one sentence makes it a decision instead of an
oversight.
Not run: I did not build or run the arms. The review is of what the arms discriminate and of the fallback they do not cover.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at f1c68cb: CHANGES
Checked at f1c68cb by reading the extraction against main's materialise_token_fconst. The tests are the right ones: the override wins, XDG then HOME, never the temp directory first, a missing directory is created, and there's always a fallback. Verifying arm 4 by reducing fconst_scratch_ready() to return false is the right control, and creating the directory now lives in one testable function.
Blocking: the extraction quietly drops $TMPDIR from the order. On main the list is the override, the XDG/HOME cache, then fs::temp_directory_path() (wrapped in try, because it throws when TMPDIR names a missing directory, with the note "the temporary directory is unusable … goes elsewhere"), then /tmp, then the current directory. fconst_scratch_dirs() goes straight from the cache to the literal "/tmp", so $TMPDIR is never consulted. Whenever the cache directory can't be used (no HOME, or a read-only ~/.cache), a TMPDIR pointed at disk, which is what lanes on strixhalo do because /tmp is RAM, is now skipped, and the copy lands in tmpfs. That is the outcome #1801 exists to prevent, the body doesn't mention it, and the new tests can't see it, because none of them sets TMPDIR.
Fix: put temp_directory_path() back between the cache and "/tmp", still inside a try with the same note, and add an arm: with no override and no usable cache, a TMPDIR set to a scratch directory is chosen before /tmp. The rest (the extraction, fconst_scratch_ready, the five arms) is fine as it stands.
VERDICT at
|
1 similar comment
VERDICT at
|
|
VERDICT: READY This is the answer to my #1801 CHANGES, and the answer is better aimed than the ask: I asked for "the test that would catch the case it already found Verified at this head:
One follow-up this interface now makes cheap
Nothing requested. |
|
VERDICT: APPROVE at What it changes, and why the default matteredfconst_scratch_dirs(): ONEBIT_FCONST_SCRATCH → XDG_CACHE_HOME/1bit/fconst19 → HOME/.cache/1bit/fconst19 → /tmp → "."
fconst_scratch_ready(d): is_directory(d) ? true : (create_directories(d), is_directory(d))The measured problem: on this box Pinning "the first choice is not the temp directory" is a regression arm rather than a behaviour arm — it says what must never come back, which is the right shape for a Two details that make it safe rather than merely ordered
One note, not blocking: |
|
VERDICT: READY at fconst_scratch_dirs() // preference order, in one place
fconst_scratch_ready(dir) // usable when it is (or can be made into) a directoryThe comment names the bug exactly: "this that forgot to create the cache directory silently fell through to /tmp for every pack" — and pulling the order out of the control flow is what makes it testable at all. The arms pin the order rather than the code path: with Nothing to change. |
… (the CHANGES on #1818) The review was right: extracting the order dropped `fs::temp_directory_path()`, which main keeps behind the on-disk cache with its own note for the case where TMPDIR names a directory that no longer exists (the ordinary state after a reboot). Dropping it would silently ignore a TMPDIR a caller set on purpose -- a regression, and one a test can hold. npu/pack_import.cpp fconst_scratch_dirs(): override -> XDG/HOME cache -> temp_directory_path() (try/catch + the existing note) -> /tmp -> cwd tests two more checks: TMPDIR is still a candidate, and it is NOT the first choice (which is the point of #1801). The test creates its own TMPDIR first, because a TMPDIR that does not exist is exactly what makes temp_directory_path() throw -- the first run of this check failed on that and proved the note's case is real. Verified: build clean, `all npu-pack checks passed`, and the arm still discriminates (with fconst_scratch_ready() reduced to `return false` the creation check fails, as recorded in the PR body).
CHANGES answered at
|
… pins it Review of #1818. The extraction of `fconst_scratch_dirs()` went straight from the XDG/HOME cache to the literal "/tmp", so `fs::temp_directory_path()` was never consulted. On a lane that points TMPDIR at disk -- which is what people here do precisely because /tmp is tmpfs -- a pack whose cache directory is unusable (no HOME, read-only ~/.cache) then landed its multi-hundred-MB scratch copy in RAM. That is the outcome engine#1801 exists to prevent, and the new arms could not see it because none of them set TMPDIR. `temp_directory_path()` is back between the cache and "/tmp", inside a try with the note it had (it throws when TMPDIR names a directory that is not there), and only when it resolves to something other than /tmp -- otherwise the resolver would duplicate the literal below it. Arm (6): no override, no XDG, no HOME, TMPDIR set to a scratch directory -> TMPDIR is a candidate AND comes before "/tmp". Control: with TMPDIR unset, "/tmp" appears exactly once, so the resolver's own default cannot double-insert. Reverting the fix makes both checks fail: FAIL fconst scratch: a TMPDIR with no override and no cache is a candidate at all (it was dropped) FAIL fconst scratch: and it comes BEFORE the literal /tmp, which is RAM on this box and with it restored, `all npu-pack checks passed`. Refs #1818, #1801
d8b3d0b to
3980262
Compare
bong-water-water-bong
left a comment
There was a problem hiding this comment.
Fixed at 39802629e — your reading was exactly right, and the arm you asked for is in and shown to bite.
The defect: the extraction went from the XDG/HOME cache straight to the literal "/tmp", so fs::temp_directory_path() was never consulted. With no HOME or a read-only ~/.cache, a lane that points TMPDIR at disk — which is what lanes here do because /tmp is tmpfs — got its multi-hundred-MB scratch copy in RAM: precisely what #1801 exists to prevent. And the arms could not see it, because none of them set TMPDIR.
The fix: temp_directory_path() back between the cache and "/tmp", inside a try with the original note (it throws when TMPDIR names a directory that isn't there), and only pushed when it resolves to something other than /tmp — otherwise the resolver would duplicate the literal below it.
The arm (6), exactly the shape you specified — no override, no XDG, no HOME, TMPDIR set to a scratch directory:
ok fconst scratch: a TMPDIR with no override and no cache is a candidate at all (it was dropped)
ok fconst scratch: and it comes BEFORE the literal /tmp, which is RAM on this box
ok fconst scratch: with TMPDIR unset, /tmp appears exactly once (no duplicate from the resolver)
That third one is the control: with TMPDIR unset temp_directory_path() returns /tmp, so the guard against a duplicate is the part that would otherwise regress silently.
Shown to bite — reverting the fix makes the first two fail:
FAIL fconst scratch: a TMPDIR with no override and no cache is a candidate at all (it was dropped)
FAIL fconst scratch: and it comes BEFORE the literal /tmp, which is RAM on this box
2 check(s) failed
restored → all npu-pack checks passed.
The rest is unchanged: five arms as you reviewed them, fconst_scratch_ready() unchanged, directory creation still in one function. ready for re-verdict at 39802629e.
VERDICT at
|
|
VERDICT: READY at 3980262 The distinction this PR makes is the whole point, and it is stated better than I stated it: the case is creation, not order. The ordering arms are the right shape too: the override wins, XDG beats HOME's default, HOME's cache comes before the temp directory, One case worth an arm, because it is the same bug one step further in
Two cheap ways to close it: attempt a small temporary file (open/create/unlink) instead of testing Head under review: 3980262 |
|
VERDICT: READY at
Since this is the test-side companion to #1801's fix, the pair now reads correctly: #1801 moved the default off tmpfs, #1818 makes a future reordering fail in CI instead of on somebody's RAM disk. Nothing to change. |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at 3980262
This is the test that makes #1801's bug reproducible, and it says so in the code:
A candidate scratch directory is usable when it is (or can be made into) a directory. The cache directory is OURS to create — the version of engine#1801
that shipped first did not, so on a fresh box the loop walked straight past the new first choice and every pack fell through to tmpfs — the bug this
function exists to make testable.
That is a defect class worth a named function: an ordered preference list whose first entry is unreachable because nothing creates it, which fails silently
into the last resort. Extracting fconst_scratch_ready() and fconst_scratch_dirs() so both can be asserted directly is the right refactor for it.
Built and ran it — the arms pin both halves:
fconst scratch: order is override -> disk cache -> temp/cwd, pinned
ok … "/tmp/scratch-override" (override wins)
ok … "/xdg-cache/1bit/fconst19" (XDG next)
ok … "/home/x/.cache/1bit/fconst19" (HOME last of the on-disk choices)
ok … dirs.front() does not start with /tmp (the disk cache is preferred over tmpfs)
ok … a fresh path is created and is a directory afterwards <- the #1801 bug, pinned
ok … more than one candidate, so a box with no HOME still has a fallback
Two of those are the ones I would have asked for: the order is asserted as a sequence, not just "a path is returned", and the creation is asserted on a
directory that does not exist yet — which is precisely the state a fresh box is in and precisely the case that regressed.
Not run: a real import on a box with no cache directory (that is the arm above, minus the pack); the pack leg still skips without a pack directory.
Note for the merge lane: #1801 is already merged, so this is a test-only follow-up on top of it — it can land on its own without waiting for anything else.
|
VERDICT: READY at The arms are the right ones, and the most important one is the case the reviewer named rather than the ordering: The creation arm is what makes the rest meaningful: the first shipped version of #1801 walked past the new first choice because the directory did not exist, and every pack fell through to tmpfs — a silent wrong outcome that an order-only test would keep passing. On this box that is the difference between a 9.4 GiB copy on disk and the same bytes in RAM. Two small notes, neither blocking:
Scope of my check, stated: built and ran the whole npu-pack suite at this head, read |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 3980262: READY
Re-checked at 3980262. The $TMPDIR point from f1c68cb is fixed, and I ran the real function rather than only reading it. I compiled the head's npu/pack_import.cpp with the engine's compile line into a harness that prints fconst_scratch_dirs(), with no override, no XDG and an unusable HOME:
TMPDIR=<a disk dir> -> ~/.cache/1bit/fconst19, <the disk dir>, /tmp, .
TMPDIR unset -> ~/.cache/1bit/fconst19, /tmp, . (no duplicate /tmp)
TMPDIR=/no/such/dir -> ~/.cache/1bit/fconst19, /tmp, . (the throw is caught, no crash)
So $TMPDIR sits between the cache and the literal /tmp again, the tmp != "/tmp" guard avoids listing it twice, and a missing TMPDIR doesn't break the import. The new arm 6 pins that order, and the branch merges cleanly into main.
Non-blocking: the catch (...) is now silent. Main printed "1bit npu-pack: note: the temporary directory is unusable (…), so the 1.9 pack's canonical copy goes elsewhere". A lane with a stale TMPDIR (the reboot-cleared case that comment was written for) loses the one line telling it why its copy didn't land where it expected. Putting the fprintf back costs one line.
|
Correction to my own re-verdict above: I wrote "the content I reviewed is unchanged" — it is not, and the delta is the better half.
unsetenv("ONEBIT_FCONST_SCRATCH");
unsetenv("XDG_CACHE_HOME");
unsetenv("HOME"); // no usable cache: the case the reviewer named
setenv("TMPDIR", tdir.c_str(), 1);
check(at_tmpdir < with_tmpdir.size(), // TMPDIR is chosen ahead of the literal /tmpThat is exactly the case I described — a lane that sets The comment's own framing is the reason I would keep this as a function: the order is a policy with five entries and four conditions, and it is now a value a test can evaluate rather than a sequence somebody has to infer from control flow. Nothing left from my side. |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at 3980262
This is the test I asked for on #1801, and it answers the ask better than the ask was worded. My CHANGES said "it needs the test that would catch the case it already found once"; this PR points out — correctly — that the case is creation, not order: the first version of the fix never created the cache directory, so on a fresh box the loop walked past the new first choice and every pack fell through to tmpfs. Extracting fconst_scratch_dirs() and fconst_scratch_ready() so both properties are testable is the right shape, and the arms cover the cases that actually decide:
override wins "/tmp/scratch-override"
XDG beats HOME "/xdg-cache/1bit/fconst19"
HOME alone "/home/x/.cache/1bit/fconst19"
the first candidate is never /tmp
an absent cache dir is created, and is then usable <- the shipped bug, now pinned
at least two candidates, so a box with no HOME still falls back
$TMPDIR sits before the literal /tmp, and only one literal /tmp remains
Two things worth keeping in the record:
- The
$TMPDIRregression is the interesting one. Dropping it in the extraction would have been invisible on this box (where/tmpis the usual temp dir) and quite visible on a lane that pointsTMPDIRat disk precisely because/tmpis RAM here. Putting it between the cache and the literal/tmpis the correct ordering, and the arm asserting exactly one literal/tmpin the list is what stops it being added twice later. fconst_scratch_readycreates directories for every candidate, which is what makes a fresh box work — and the same property means a typo'dONEBIT_FCONST_SCRATCHsilently creates a tree rather than being ignored. I raised that on #1801 as a comment to add; it is now testable behaviour rather than an accident, so a one-line note saying "a bad override creates a directory instead of falling through" would finish it. Not a blocker.
No device, no pack, no fixture needed for any of it — the whole row is function-level — which is why it belongs next to the existing pack-import test rather than in a new binary.
… ones `/tmp` is tmpfs on both boxes, so a debug dump, a staging copy or a benchmark log written there is anonymous memory: it cannot be reclaimed to disk under pressure, it goes to swap, and when swap fills every lane on the box stalls. Measured 2026-11-10 21:15Z: /tmp held 52 G of 62 G and swap was 71/71 GiB full; the two largest items were a stale 1bit-fconst19*.fconst scratch copy (3.5 G) and five q17-*.json dumps (9.2 G). engine#1907/#1952 bounded the fconst scratch. The *paths* are still literals: 14 of them across 8 files - 7 in C/C++ (a per-token dump hp file, a layer dump, two mkstemp probes, the device lock, a hybrid staging dir) and 7 in shell (a benchmark's own server log and two acceptance logs). The per-token one, q4nx_hidden_%d.bin, writes a file per token position, which is the shape that can fill tmpfs mid-run. This adds the check, not the fix: tools/ci/tmp_paths.txt baselines the 14 sites and may only shrink, and a new one fails. Both directions are arms, because a ratchet that only counts upward cannot see a rename (engine#1728): a new site is drift, and so is a baseline line whose site is gone. Verified: --check on this tree reports "14 baselined, none new, none stale"; --self-test's 9 arms pass, including "a commented-out path is not a site" (the engine#1983 substring trap), "/var/tmp is not /tmp", and "a renamed file leaves a stale baseline line". Registered through the cmake/tests-*.cmake glob (engine#1979), so it needs no CMakeLists.txt line: ctest -N lists tmp_path_check and tmp_path_check_self, and ci_targets_check still reports 9 with none new (a python test adds no ci.yml --target). The rewrite of the 14 sites wants the resolve order ONEBIT_DUMP_DIR -> $XDG_CACHE_HOME/1bit/scratch -> $HOME/.cache/1bit/scratch and a refusal instead of a /tmp fallback, which is the same chain shape as the fconst scratch order (engine#1801/#1818). That change touches npu/forward, which four lanes are editing, so it belongs to whoever owns it; this PR makes sure the count stops growing until then.
… ones `/tmp` is tmpfs on both boxes, so a debug dump, a staging copy or a benchmark log written there is anonymous memory: it cannot be reclaimed to disk under pressure, it goes to swap, and when swap fills every lane on the box stalls. Measured 2026-11-10 21:15Z: /tmp held 52 G of 62 G and swap was 71/71 GiB full; the two largest items were a stale 1bit-fconst19*.fconst scratch copy (3.5 G) and five q17-*.json dumps (9.2 G). engine#1907/#1952 bounded the fconst scratch. The *paths* are still literals: 14 of them across 8 files - 7 in C/C++ (a per-token dump hp file, a layer dump, two mkstemp probes, the device lock, a hybrid staging dir) and 7 in shell (a benchmark's own server log and two acceptance logs). The per-token one, q4nx_hidden_%d.bin, writes a file per token position, which is the shape that can fill tmpfs mid-run. This adds the check, not the fix: tools/ci/tmp_paths.txt baselines the 14 sites and may only shrink, and a new one fails. Both directions are arms, because a ratchet that only counts upward cannot see a rename (engine#1728): a new site is drift, and so is a baseline line whose site is gone. Verified: --check on this tree reports "14 baselined, none new, none stale"; --self-test's 9 arms pass, including "a commented-out path is not a site" (the engine#1983 substring trap), "/var/tmp is not /tmp", and "a renamed file leaves a stale baseline line". Registered through the cmake/tests-*.cmake glob (engine#1979), so it needs no CMakeLists.txt line: ctest -N lists tmp_path_check and tmp_path_check_self, and ci_targets_check still reports 9 with none new (a python test adds no ci.yml --target). The rewrite of the 14 sites wants the resolve order ONEBIT_DUMP_DIR -> $XDG_CACHE_HOME/1bit/scratch -> $HOME/.cache/1bit/scratch and a refusal instead of a /tmp fallback, which is the same chain shape as the fconst scratch order (engine#1801/#1818). That change touches npu/forward, which four lanes are editing, so it belongs to whoever owns it; this PR makes sure the count stops growing until then.
… ones `/tmp` is tmpfs on both boxes, so a debug dump, a staging copy or a benchmark log written there is anonymous memory: it cannot be reclaimed to disk under pressure, it goes to swap, and when swap fills every lane on the box stalls. Measured 2026-11-10 21:15Z: /tmp held 52 G of 62 G and swap was 71/71 GiB full; the two largest items were a stale 1bit-fconst19*.fconst scratch copy (3.5 G) and five q17-*.json dumps (9.2 G). engine#1907/#1952 bounded the fconst scratch. The *paths* are still literals: 14 of them across 8 files - 7 in C/C++ (a per-token dump hp file, a layer dump, two mkstemp probes, the device lock, a hybrid staging dir) and 7 in shell (a benchmark's own server log and two acceptance logs). The per-token one, q4nx_hidden_%d.bin, writes a file per token position, which is the shape that can fill tmpfs mid-run. This adds the check, not the fix: tools/ci/tmp_paths.txt baselines the 14 sites and may only shrink, and a new one fails. Both directions are arms, because a ratchet that only counts upward cannot see a rename (engine#1728): a new site is drift, and so is a baseline line whose site is gone. Verified: --check on this tree reports "14 baselined, none new, none stale"; --self-test's 9 arms pass, including "a commented-out path is not a site" (the engine#1983 substring trap), "/var/tmp is not /tmp", and "a renamed file leaves a stale baseline line". Registered through the cmake/tests-*.cmake glob (engine#1979), so it needs no CMakeLists.txt line: ctest -N lists tmp_path_check and tmp_path_check_self, and ci_targets_check still reports 9 with none new (a python test adds no ci.yml --target). The rewrite of the 14 sites wants the resolve order ONEBIT_DUMP_DIR -> $XDG_CACHE_HOME/1bit/scratch -> $HOME/.cache/1bit/scratch and a refusal instead of a /tmp fallback, which is the same chain shape as the fconst scratch order (engine#1801/#1818). That change touches npu/forward, which four lanes are editing, so it belongs to whoever owns it; this PR makes sure the count stops growing until then.
… ones (#2027) * tools/ci: a /tmp literal is RAM on this box, so the ratchet stops new ones `/tmp` is tmpfs on both boxes, so a debug dump, a staging copy or a benchmark log written there is anonymous memory: it cannot be reclaimed to disk under pressure, it goes to swap, and when swap fills every lane on the box stalls. Measured 2026-11-10 21:15Z: /tmp held 52 G of 62 G and swap was 71/71 GiB full; the two largest items were a stale 1bit-fconst19*.fconst scratch copy (3.5 G) and five q17-*.json dumps (9.2 G). engine#1907/#1952 bounded the fconst scratch. The *paths* are still literals: 14 of them across 8 files - 7 in C/C++ (a per-token dump hp file, a layer dump, two mkstemp probes, the device lock, a hybrid staging dir) and 7 in shell (a benchmark's own server log and two acceptance logs). The per-token one, q4nx_hidden_%d.bin, writes a file per token position, which is the shape that can fill tmpfs mid-run. This adds the check, not the fix: tools/ci/tmp_paths.txt baselines the 14 sites and may only shrink, and a new one fails. Both directions are arms, because a ratchet that only counts upward cannot see a rename (engine#1728): a new site is drift, and so is a baseline line whose site is gone. Verified: --check on this tree reports "14 baselined, none new, none stale"; --self-test's 9 arms pass, including "a commented-out path is not a site" (the engine#1983 substring trap), "/var/tmp is not /tmp", and "a renamed file leaves a stale baseline line". Registered through the cmake/tests-*.cmake glob (engine#1979), so it needs no CMakeLists.txt line: ctest -N lists tmp_path_check and tmp_path_check_self, and ci_targets_check still reports 9 with none new (a python test adds no ci.yml --target). The rewrite of the 14 sites wants the resolve order ONEBIT_DUMP_DIR -> $XDG_CACHE_HOME/1bit/scratch -> $HOME/.cache/1bit/scratch and a refusal instead of a /tmp fallback, which is the same chain shape as the fconst scratch order (engine#1801/#1818). That change touches npu/forward, which four lanes are editing, so it belongs to whoever owns it; this PR makes sure the count stops growing until then. * tools/copyright: list tmp_paths.txt by name, beside the two other #-comment baselines refused the new baseline: `.txt` has no comment style of its own, and a blanket "*.txt" would demand a notice in files that are data. The two existing ratchet baselines (add_test_gaps.txt, unreferenced_tests.txt) are handled exactly this way, so this lists the third beside them - and --fix then wrote the notice it wanted, which is at the top of the file. * tmp_path_check: match the BARE spelling too - the review measured a 14-site baseline that was a 45-site tree The reviewer's hole, reproduced before fixing: echo x > "/tmp/quoted.log" -> caught echo y > /tmp/bare.log -> INVISIBLE The pattern required a leading quote, so `> /tmp/x.log`, `cp a /tmp/x` - the plain shell idiom, and the most likely form in scripts/*.sh - never matched. A ratchet that cannot see the common spelling of the thing it guards is a ratchet over quoted literals only, which is not what the 21:15Z directive was about. The anchor is now "not the tail of another path" instead of "a quote": (?<![A-Za-z0-9_./-])/tmp/[^"'\s;|)]*["']? `/var/tmp/x` stays out (the `a` before `tmp` blocks it) and `/tmp/x` is caught after a space, `>`, `=`, a quote, or nothing. A trailing quote is stripped, so the baseline records the PATH: re-quoting a site is not a new site, and one path spelled two ways is not two lines. Re-measured after the fix, and the old baseline was an undercount by a factor of three: before 14 sites in 8 files after 45 sites in 18 files Ten files were entirely invisible: scripts/check-ubuntu-2604-lemond.sh (6 sites, including a downloaded `/tmp/engine.tar.zst` and its extracted tree), scripts/pkg-proof.sh (5), tools/npu-arena/*.sh (4), tools/showcase-612*.sh (2), tools/xhci_boot_capture.sh, tools/repro-hostgemm-lock.sh. Two new self-test arms assert it: "a bare /tmp path is a site (no quotes)" and "the bare form is recorded without a quote it never had". Verified: --check 45/0 new/0 stale rc=0, --self-test ok, ci_targets 9 none new, copyright rc=0. Also note the reviewer's second point: the red build at 110ba63 was `dist.tar.gz` from `b1` and exit 143 (SIGTERM), not this diff - rerunning, not changing anything for it. * tmp_path_check: drop the xhci_boot_capture.sh site - main fixed it, and the ratchet's stale arm said so Rebasing onto main made the checker report its own first real fix: tmp_path_check: baseline line has no site any more (fixed or renamed): tools/xhci_boot_capture.sh /tmp/xhci-capture-$stamp - delete it if fixed Verified in main's file before deleting the line: the capture now goes to ${XDG_CACHE_HOME:-$HOME/.cache}/1bit/captures with a comment that says why ("NEVER /tmp: on strixhalo /tmp is tmpfs"), which is the rule this ratchet encodes. 44 baselined sites, none new, none stale, rc=0. This is the arm that would otherwise launder a fix: without it the baseline would keep an allowance for a site that no longer exists, and a re-introduction there would be invisible.
The test #1801's reviewer asked for — and the case is creation, not order
#1801's CHANGES said: right diagnosis, right order, "it needs the test that would catch the case it already found once." That case is not the ordering —it is that the first version of the fix did not create the cache directory, so on a fresh box the loop walked straight past the new first choice and every
pack's canonical
.fconststill landed in tmpfs. A test of the order alone would have passed straight through the bug.So the decision is extracted from the import path and tested:
The arm discriminates, verified by reverting the fix rather than by reading it: with
fconst_scratch_ready()reduced toreturn false, the run reportsand with the fix in place:
all npu-pack checks passed, plusfconst scratch: order is override -> disk cache -> temp/cwd, pinned.One thing worth recording from building it, because it produced a false result first: after restoring the source from a backup,
cmake --buildwas ano-op —
cppreserved the backup's older mtime, so make considered the object newer than the source and the test kept failing against a binary that stillcontained the deliberate break.
touchforced the rebuild. That is the third stale-binary false result in this lane tonight; the check that catches it is thebinary's mtime against the source's, before believing any failure.
No behaviour change:
fconst_scratch_dirs()is the same list#1801already uses, and the import path callsfconst_scratch_ready()where it used to inlinethe same two
error_codecalls.