vm-fingerprint: prune the bun install cache wholesale — targets were moving the CORE digest (proposed) - #27
Merged
Conversation
The layer-2 file walk pruned only $d/install/cache/*.npm, so every
EXTRACTED tree under ~/.bun/install/cache/<pkg>@<ver>/** was printed and
sha256'd into the CORE digest. That was deliberate and correct when bun
itself was the only thing that grew the directory: the trees are
byte-stable, and they are what global/node_modules links into.
It stopped being correct once layer-3 targets arrived. An arc-installed
package runs `bun install` for its own dependencies, so the software
under test now populates the cache. Two consequences, both fatal to what
the digest is for:
- target-visibility. The CORE digest moves whenever the target changes
a dependency, so "same environment, two target versions" - the
comparison the whole exercise is built on - stops being expressible.
This is the same failure share/metafactory and bin were pruned for,
arriving by a third door.
- non-determinism. Those dependency trees are transitively unpinned. A
rebuild from the same definition can resolve a different transitive
version and digest differently with nothing about the environment
having changed.
So the cache is pruned WHOLESALE now, blobs and extracted trees alike,
in both find passes - the listing and the hashing pass.
Tooling identity, verified rather than assumed: bun keeps its identity
via `bun --version` in the layer2 versions section, and that was already
the only place it lived. The $d/bin prune covers .bun/bin as well as
.local/bin, so the bun binary itself has not been hashed since that
prune landed - this change removes no bun coverage that layer2 versions
was not already carrying. test-vm-fingerprint.sh asserts the bun line is
still present, with comments stripped so the prose cannot satisfy it.
install/global is deliberately NOT pruned: the globally installed
package set stays in the listing, and a check goes red if a future prune
widens to $d/install and takes it out.
MIGRATION - old captures are incomparable across this change, and the
only migration is RE-CAPTURING. Stated precisely because the obvious
guess is wrong: `--from-file` does NOT help here. It re-digests the
capture BODY as it finds it, and an old body still contains the cache
lines this change stops emitting, so re-digesting an old capture
reproduces that capture-s OLD core digest and can never converge with a
post-change one. Verified on a synthetic pre-change capture: all four
planted cache lines survive the re-digest, and the re-digested old
capture and an equivalent post-change capture land on different core
digests. The header-s existing note about `--from-file` is accurate and
unchanged - it scopes the tool to re-cutting the core/provider SPLIT,
which moves lines between sections without changing the body. This
change alters the body itself, which is a different operation. Nothing
has been re-digested here; no captures are tracked in this repo.
evidence/ac-0.md quotes pre-change digests and is left alone
deliberately - it records what was observed at the time.
Corrected two claims in the bin-prune rationale rather than leave known
-false justifications sitting next to a fresh one:
- it said the shim dispatch targets "still live under a tree that IS
hashed (the bun global node_modules, the extracted install cache)".
The cache half this change invalidates. The node_modules half was
already wrong.
- the replacement text is scoped, not absolute. install/global carries
nothing for the bun-LINK install path arc uses - bun links those
trees and the pass is -type f, which is why the captures quoted in
evidence/ac-0.md have no install/global hash line. That is a fact
about the install path, not the directory: `bun add -g` copies
regular files there and they WOULD be hashed, which is correct and
wanted. A globally installed tool is environment.
The hashing pass now gets its own EXECUTING test. The grep checks match
a substring that survives reordering: written as
-o -type f -path "$d/install/cache" -prune -o -type f -print0
the clause is false for every file under the cache (-path names the dir,
not the files), the prune never fires, and every cache file is hashed
again - while the substring the grep looks for is still in the line.
Two new checks close it from both sides: one executes the hashing
expression against the synthetic tree and asserts a planted cache file
produces no hash line while a control file does, and one reads the
remote script for the reordered form directly, since the executing check
runs the test-s own copy of the expression rather than the script-s.
Detectors observed failing before being trusted, per the repo rule.
Injected into a scratch copy:
old extension-only prune restored in vm-fingerprint.sh
-> both layer2 find passes prune the bun install cache RED
-> the old extension-only .npm prune is gone from the code RED
same injection into the test-s duplicated listing expression
-> extracted cache trees are excluded too RED
-> the install cache dir itself is excluded RED
listing prune widened to $d/install
-> the prune stops at cache: install/global stays RED
hashing pass reordered to -type f -path ... -prune, script only
-> the cache prune is never reordered behind -type f RED
same reorder also in the test-s hashing expression
-> the HASHING pass does not hash extracted cache trees RED
-> the HASHING pass does not hash .npm blobs RED
hashing prune widened to $d/install
-> the HASHING pass still hashes what it should RED
Gates: scripts/test-vm-fingerprint.sh 32/32 green; shellcheck clean on
both scripts; the single-quoted remote script parses under sh -n and
still contains zero apostrophes.
6 tasks
vpzed
approved these changes
Sep 1, 2026
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.
Proposed — shared-above-the-seam file, so this comes upstream rather than living in a fork. Found by the adversarial review lane on the
metafactory_cortexrole build (crucible#26 thread).The defect. The layer-2 walk prunes bun's install cache by extension only (
*.npmblobs), keeping the extracted trees — deliberate when only bun itself grew that directory. It stopped being right when layer-3 targets arrived: an arc-installed target runningbun installwrites its transitive dependency trees into~/.bun/install/cache/**, which the CORE digest hashes. So the software under test moves the environment digest it is supposed to be invisible to — and since transitive deps are not pin-determined, the same VM definition can rebuild to a different core digest.The fix. Prune
$d/install/cachewholesale from both find passes (listing + hashing), same shape as the existingshare/metafactoryandbinprunes. Bun's identity was never at risk:.bun/binwas already inside the bin prune, sobun --versioninlayer2 versionswas the sole carrier before this change — verified against the capture quoted inevidence/ac-0.md.Detector discipline (your own rule: untrusted until observed failing): 8 fault injections across the two commits, every new check observed red, including a subtle one — the behavioral test originally executed only the listing pass, so a reorder fault in the hashing pass stayed green; the suite now both executes the hashing expression against a synthetic tree AND greps the script for the reordered form. 32/32 green, shellcheck clean.
Consequence, stated honestly: this re-cuts the CORE digest. Captures made before this change are incomparable with captures after it, and
--from-filere-digesting cannot bridge them (it replays the stored body verbatim — measured, not assumed). The only migration is re-capturing. Your call on timing for the fingerprints/ overlay on your side.Known gap, left for your judgement:
test-vm-fingerprint.shis not in CI (validate.yamlnever invokes it). It's pure bash, seconds to run — happy to follow up with the workflow line if you want it, separately.🤖 Generated with Claude Code
https://claude.ai/code/session_018PtJarXm8d8bSHDbhmskVm