Port bundle_freshness (content-fingerprint cache invalidation) to reports / drivers / datastores / vaults loaders
Opened by stack72 · 4/17/2026· Shipped 4/20/2026
Problem
PR systeminit/swamp#1188 closes issue #125 by replacing mtime-based bundle-cache freshness with sha-256 content fingerprinting in the models loader. The fix lives in a shared helper at src/domain/extensions/bundle_freshness.ts (pure findStaleFiles + computeSourceFingerprint).
The four sibling user-extension loaders — reports, drivers, datastores, vaults — still carry a verbatim copy of the old mtime-based findStaleFiles / bundleWithCache logic. They are therefore latent-vulnerable to the same bug under the same triggers (atomic-rename saves, mtime-preserving sync tools, sub-second second edits). No user has reported a stale-cache symptom on those loaders yet, but the root cause is identical.
Users affected — anyone iterating on a custom vault / driver / datastore / report extension whose editor or workflow produces a save that does not strictly advance source mtime past the cached bundle mtime. Same failure mode as #125 — method runs, validation or output fails because the executed bundle is stale.
Proposed solution
Port each of the four loaders onto the shared helper that already exists in extensions/bundle_freshness.ts —
- Replace each loader's private
findStaleFileswith a thin wrapper delegating tofindStaleFilesShared. - Replace each loader's
bundleWithCachemtime fast-path with the same content-fingerprint-aware shape introduced in PR #1188 (remove the freshness check frombundleWithCache, add theisExpectedBundleFailurepre-check for pulled extensions). - In each loader's
rebundleAndUpdateCatalogandpopulateCatalogFromDir— compute and persistsource_fingerprintalongsidesource_mtime(already optional onExtensionTypeRow, so this is additive). - Add one regression test per loader mirroring the models-loader regression added in #1188 —
Deno.utimerestores the source mtime after an atomic-rename-style content swap, buildIndex must rebundle.
The catalog schema and migration are already in place (source_fingerprint column with PRAGMA-guarded ALTER TABLE), so this is purely loader-side work.
Alternatives considered
- Do nothing and wait for a user to hit the bug on one of the four. Not worth the slip — the helper is one line away for each loader and the symptom is confusing for users ("my extension code change isn't taking effect").
- Extract the full loader lifecycle into a generic user-extension loader. Too large; the five loaders have enough idiosyncratic behavior that a full unification is a separate refactor.
Context
- Related PR — systeminit/swamp#1188
- Related issue — #125
- Shared helper —
src/domain/extensions/bundle_freshness.ts - Catalog schema —
src/infrastructure/persistence/extension_catalog_store.ts(column already present, migration already in place)
Acceptance criteria
- Each of reports / drivers / datastores / vaults loaders delegates freshness to
bundle_freshness.findStaleFilesand persistssource_fingerprinton every catalog write. - Each loader has a regression test mirroring
user_model_loader_test.ts"findStaleFiles detects content change when source mtime is preserved". deno check / lint / fmt / test / compileclean.- swamp-uat full suite passes against the recompiled binary.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.