DatastoreProvider.resolveCachePath declared optional but silently required at runtime
Opened by stack72 · 4/22/2026
Summary
DatastoreProvider.resolveCachePath is declared as optional in the TypeScript
interface (resolveCachePath?(repoDir: string): string | undefined), but
swamp core's runtime consumer treats "method absent" and "method returning
undefined" as different code paths. Omitting the method entirely — which
the type system permits — silently breaks datastore sync --pull: the command
exits 0 but the reader's catalog is empty.
Impact
Regression shipped in @swamp/gcs-datastore 2026.04.21.1 (commit a30741ea)
where resolveCachePath was removed because the author believed returning
undefined and omitting the method were equivalent. It compiled clean against
the optional interface, passed unit tests, and produced 0-result pulls in UAT:
- swamp-uat run (last good, ext 2026.04.08.1): https://github.com/systeminit/swamp-uat/actions/runs/24747674286
- swamp-uat run (first bad, ext 2026.04.21.1): https://github.com/systeminit/swamp-uat/actions/runs/24789617322
- Failing UAT tests:
tests/cli/datastore/sync_test.ts:73and:149in systeminit/swamp-uat
The sibling @swamp/s3-datastore defines the method and returns undefined,
so its pull works. Isolates the defect to a TS-interface/runtime-contract
mismatch, not to either provider's logic.
Reproduction
Two fresh swamp repos against the same GCS bucket + prefix:
# Writer
swamp init
swamp datastore setup extension @swamp/gcs-datastore \
--config '{"bucket":"<bucket>","prefix":"<prefix>"}'
# install shell-echo model + single-step workflow
swamp workflow run single-step
swamp data list shell-echo --json # total: 4
# Reader (separate temp dir, same prefix)
swamp init
swamp datastore setup extension @swamp/gcs-datastore \
--config '{"bucket":"<bucket>","prefix":"<prefix>"}'
swamp datastore sync --pull # exits 0
swamp data list shell-echo --json # total: 0 <-- should be 4With the extension built from a provider that omits resolveCachePath, the
reader's cache lands somewhere swamp data list does not scan. Restoring the
method so it returns undefined makes core's repoId-keyed fallback fire and
the reader sees all 4 rows.
Root cause
The interface at DatastoreProvider.resolveCachePath? says this is optional.
The consumer in swamp core appears to do something like:
if (typeof provider.resolveCachePath === 'function') {
const override = provider.resolveCachePath(repoDir);
// ...branch that handles `override === undefined` by using the repoId fallback
} else {
// different branch — whatever this is, it does NOT produce the same
// cache path the reader's data.list scans
}Those two branches must be equivalent for the interface-level "optional" claim to be true. Right now they are not.
Requested fix (pick one)
Make the method strictly required in the interface — drop the
?fromDatastoreProvider.resolveCachePath. Forces every provider to be explicit: "I return undefined to get the default" vs. a hard-to-spot omission. This is the cheapest fix and most honest about current runtime semantics.Unify the two branches in the consumer — treat "method missing" as equivalent to "method returned undefined" and run the same fallback path. Keeps the interface truly optional.
Option 2 is nicer for extension authors; option 1 is safer short-term because it rules out the silent-failure mode by construction.
Workaround already applied downstream
systeminit/swamp-extensions has restored resolveCachePath on the GCS
provider (returning undefined, matching S3) and added a contract-level unit
test that asserts both typeof provider.resolveCachePath === "function" and
the undefined return value, so the regression cannot recur in that repo.
Environment
- swamp CLI: 20260422.161846.0 (sha 88acb3cc)
- Affected extensions: @swamp/gcs-datastore (confirmed), any provider that omits the method (latent)
- Interface file in extensions mirror:
datastore/gcs/extensions/datastores/_lib/interfaces.ts:106
In Progress
Click a lifecycle step above to view its details.
Sign in to post a ripple.