feat: auto-update @swamp/ datastore extensions once per day#942
feat: auto-update @swamp/ datastore extensions once per day#942
Conversation
Add a daily staleness check for @swamp/ datastore extensions during datastore resolution. When the check cache is older than 24 hours, queries the registry for the latest version and auto-pulls if outdated. - New domain type: per-extension update check cache with 24h cooldown - New orchestration function: maybeAutoUpdateDatastoreExtension() - Helper maybeAutoUpdateSwampDatastore() called from all three @swamp/ resolution paths in resolveDatastoreConfig(), placed between resolveDatastoreType() and createProvider() so updated code is used - Only triggers for @swamp/ collective, never for third-party extensions - Registry errors and pull failures are silently ignored Closes #939 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The previous implementation used resolver.resolve() which only installs missing extensions — it doesn't update already-installed ones. Switch to installExtension() with force:true + UserDatastoreLoader hot-reload, matching the pattern used by the auto-resolver adapter. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ebug logging The lockfile path was hardcoded to "models/" but the actual directory is resolved via resolveModelsDir() (defaults to "extensions/models"). Also add debug logging to diagnose auto-update check flow. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
CLI UX Review
Blocking
None.
Suggestions
-
Duplicate log messages on successful update — When an update is pulled, users see two
info-level messages back-to-back:datastore_auto_update.ts:120:"Updating @swamp/s3-datastore 2026.03.15.1 → 2026.03.30.1"(before the pull)resolve_datastore.ts:158:"Updated @swamp/s3-datastore 2026.03.15.1 → 2026.03.30.1"(after the pull)
Only one message is needed. The pre-pull
"Updating ..."indatastore_auto_update.tsis redundant since the caller already logs the result. Suggest removing thelogger.infoatdatastore_auto_update.ts:120-123and keeping only the post-pull"Updated ..."inresolve_datastore.ts. -
No user control over auto-update behavior — There's no flag to opt out (e.g.,
--no-auto-update) or force an immediate check. Not blocking for v1, but worth tracking as the feature matures.
Verdict
PASS — the auto-update runs silently in the background, failures are swallowed, and the only user-visible output is the update log message. The duplicate messaging is minor noise and doesn't block.
There was a problem hiding this comment.
Adversarial Review
Critical / High
None found.
Medium
-
Cache written before pull — failed pull suppresses retry for 24h (
src/libswamp/extensions/datastore_auto_update.ts:108-113)The cache is updated at line 109-113 before
pullExtensionis called at line 124. IfpullExtensionthrows (network timeout, disk full, checksum mismatch), the outercatchat line 131 returnsnull, but the cache already recordscheckedAt: nowwith the latest version. For the next 24 hours,isExtensionCheckStale()returnsfalseand the update is never retried.Breaking scenario: User runs a command while their network is flaky. The registry check succeeds (getting
latestVersion), but the archive download inpullExtensionfails. The extension stays at the old version, and no retry happens until the cache expires 24 hours later.Suggested fix: Move the cache write to after the successful pull (between lines 124 and 126). For the "already at latest" path (line 115-117), the cache write at its current position is fine — split it into two: write-on-up-to-date before the early return, and write-on-successful-pull after
pullExtensionresolves. This way a pull failure allows an immediate retry on the next command. -
Import from internal libswamp path violates architecture rule (
src/cli/resolve_datastore.ts:44)maybeAutoUpdateDatastoreExtensionis imported from../libswamp/extensions/datastore_auto_update.ts, but CLAUDE.md requires CLI code to import libswamp functions fromsrc/libswamp/mod.ts. The function is already exported frommod.ts(this PR adds it). The import should be:import { maybeAutoUpdateDatastoreExtension } from "../libswamp/mod.ts";
Low
-
Variable shadowing of
resolvedRepoDir(src/cli/resolve_datastore.ts:75andsrc/cli/resolve_datastore.ts:103)resolvedRepoDiris declared withconstat line 75 (resolve(repoDir)) in the outer function scope, then declared again at line 103 inside thepullExtensioncallback with the sameresolve(repoDir)expression. The values are identical so behavior is correct, but the inner declaration is unnecessary and could mask a future bug ifrepoDirwere ever reassigned. Use the outerresolvedRepoDirdirectly in the closure. -
Non-atomic cache file writes (
src/infrastructure/persistence/extension_update_check_repository.ts:48-51)Deno.writeTextFileis not atomic — a crash mid-write leaves a truncated file. Theread()method's catch-all means this self-heals (returns{}), so worst case is one extra registry check. Existing pattern in the codebase (update_check_cache.ts) has the same characteristic, so this is consistent.
Verdict
PASS — The core logic is sound and well-tested. The cache-before-pull ordering (Medium #1) is the most actionable finding — it causes a 24h retry blackout on pull failure. The import path (Medium #2) is a straightforward fix. Neither blocks the merge but both are worth addressing.
Summary
Fixes #939 — automatically checks for and pulls updates to
@swamp/datastore extensions once per day.When a
@swamp/datastore extension is loaded during command startup, swamp now checks if the installed version is outdated (using a 24h-cooldown cache). If a newer version is available on the registry, it auto-pulls and hot-reloads before theDatastoreProvideris instantiated.Scope: Only
@swamp/collective datastore extensions. Does not apply to models, workflows, vaults, drivers, reports, or third-party datastores.Key design decisions:
resolveDatastoreConfig()betweenresolveDatastoreType()andcreateProvider()— ensures updated registry entry is used when provider is instantiatedmaybeAutoUpdateSwampDatastore()called from all three@swamp/resolution paths (env var, .swamp.yaml renamed, .swamp.yaml custom).swamp/extension-update-checks.json— shared via S3 for remote datastores, so all machines share the 24h cooldownTest plan
deno check,deno lint,deno fmtall pass🤖 Generated with Claude Code