Relationships
↔ sibling #2855#2861 Conformance: behavioural round-trip suite for DatastoreSyncService in packages/testing
Opened by stack72 · 9/30/2026· Shipped 10/1/2026
Background (read this first)
swamp is about to rework its datastore layer. The design proposal is design/enablers/datastore-commit-log.md on the datastore-rework branch. It is not merged; §2 of it is a write-path inventory.
How the datastore works today.
- Repositories write files into the repo's
.swamp/directory, or into the datastore cache dir for a custom datastore. They then call a mark hook,markDirty(relPath?), which reachesDatastoreSyncService(src/domain/datastore/datastore_sync_service.ts, interface at :168, the eight-rulemarkDirtycontract at :199-257). - A caller then flushes, through one of four paths:
acquireModelLocks().flush(src/cli/repo_context.ts:1723);- the global coordinator
flushDatastoreSync(src/cli/mod.ts:2251); pushManagedConfigChanges(src/cli/managed_config_sync.ts:45/153);- serve's
pushChangedToRemote(src/serve/handlers/shared.ts:83-97) under the sync gate.
- The S3 and GCS sync implementations live in swamp-extensions, not here. They own the dirty set, the
.datastore-sync-state.jsonsidecar,_indexshards and_meta.json. - A filesystem datastore has no sync service at all (
repo_context.ts:1170-1178). - Each repo has a SQLite catalog,
.swamp/data/_catalog.db, thatDataQueryServicereads. - Serve runs pollers that pull peers' changes: Config, AccessData, RuntimeData.
What changes. The refactor lands on main in phases:
- Phase 1: a
UnitOfWorkport with a legacy adapter, so repositories stage writes instead of calling the hook directly. - Phase 2: libswamp use cases own the unit of work, and CLI commands and serve stop calling
markDirty/pushChangedthemselves. - Later phases: a new commit-log engine behind an opt-in format.
Phases 1 and 2 must not change any behaviour.
Why this issue exists. Before any of that lands, this repo's own tests have to pin today's behaviour, so a dropped mark, a lost delete or a missed catalog refresh fails a test instead of reaching users. An audit in September 2026 found:
- no in-memory remote to test sync against;
- no test that every file a repository changes gets marked, and several real unmarked paths;
- no two-repo propagation test;
- no ratchet on the write seams Phase 1 will move;
- the sync-service conformance suite only checks method shapes.
This issue is one of a set. The tracking issue is listed under Related. End-to-end CLI tests for the same risks are in swamp-club/swamp-uat #481-#485, #487, #490 and #492.
Rules (from AGENTS.md)
- Unit tests are in-process only: no subprocesses, no process-global mutation; use
withMockedEnv. - Integration tests in
integration/wire real components on a temp filesystem and must not spawn the CLI. - Registry-registered test types use a per-run name built with
crypto.randomUUID(), orinvalidateTypein afinally. - No fixed sleeps (use
waitForfrom@swamp-club/swamp-testing), no wall-clock assertions, andDeno.utimefor mtimes. - Use
@std/pathfor paths andassertPathEqualsfor path comparisons, since tests run on Windows too. - New files need the AGPLv3 header (
deno run license-headers). - Pin today's behaviour, including known gaps. When today's behaviour is a bug, assert it as it is, with a comment naming the bug. Prefer an explicit pinned list checked with
assertPinnedSet, so a later fix forces the test to be updated on purpose. Do not fix production code in these issues unless the issue says so.
Goal
Turn the sync-service conformance suite into a behavioural contract, so any sync implementation is held to the same rules. That covers this repo's in-memory remote, the S3 and GCS extensions in swamp-extensions, third-party datastores, and the Phase 1 legacy adapter. Today the suite only checks that the methods exist.
Current state
packages/testing/datastore_conformance.ts:assertDatastoreExportConformance(:68) checks shape;assertLockConformance(:216) andassertLockTimeoutConformance(:377) test real behaviour;assertVerifierConformance(:434);assertSyncServiceConformance(:491) only checks the methods exist, callsmarkDirty/pull/pushonce, and checks return types and thecapabilities()shape.
- The suite does not test the eight-rule
markDirtycontract (src/domain/datastore/datastore_sync_service.ts:199-257), round-trips, delete propagation, orpreparePush/commitPush(:379/:405). - Who runs it:
- here: in-memory stubs only (
packages/testing/datastore_conformance_test.ts:155-177); - swamp-extensions: the S3 and GCS extensions consume the package.
- here: in-memory stubs only (
Work
Add assertSyncServiceRoundTripConformance(factory) to packages/testing/datastore_conformance.ts.
factoryreturns two service instances bound to two separate cache dirs on one backend, plus a cleanup.- Cases:
- Write a file in cache 1, mark it and push. Pull on instance 2: the file is identical.
- Delete the file in cache 1, mark it (absent on disk means delete) and push. Pull on instance 2: the file is gone.
- A bare mark followed by push uploads every changed file.
- A push that fails, via an injected transport failure if the factory offers one, leaves the path dirty, and the next push uploads it. Skip this case when the factory has no failure hook, and report it as skipped.
preparePushdoes not clear dirty state;commitPushapplies the change and clears it; a secondcommitPushwith nothing prepared does nothing.- Pull with nothing new returns 0 (or void, per the interface) and changes no local file.
- Relative paths use forward slashes on every OS.
- Run it against the in-memory remote from the fake-remote issue in
packages/testing/datastore_conformance_test.ts. - Document it in the package README and changelog, so the S3 and GCS extensions adopt it in swamp-extensions. Adopting it there is a follow-up in that repo; note it on the tracking issue.
Done when
The new suite exists, is exported, is documented, and passes against the in-memory remote.
Dependencies
Blocked by: swamp-club#2854
Blocks: nothing
Full dependency graph and waves: swamp-club#2865.
Related
- Tracking issue: swamp-club#2865 (https://swamp-club.com/lab/2865).
- Adoption by the S3/GCS extensions is a follow-up in swamp-extensions.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.