Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesstack72

Relationships

↔ sibling #2621

#3117 datastore config migrate writes its sentinel before pushing; after a failed push a re-run reports 'already completed' and never publishes

Opened by stack72 · 10/6/2026· Shipped 10/6/2026

Summary

swamp datastore config migrate writes its "already migrated" sentinel (config/managed-config-migrated.json) into the local cache before it pushes to the datastore. If the push fails, the command exits non-zero, but every re-run finds the local sentinel, reports alreadyMigrated: true, exits 0 and pushes nothing. The datastore never receives the config, and nothing tells the user to run swamp datastore sync --push.

Where it happens (swamp 5517e708)

  • src/domain/datastore/managed_config_migration.ts:
    • :44-59: the "already migrated" check stats the sentinel in the local cache only.
    • :129-135: after copying, the sentinel is written unconditionally, before returning.
  • src/cli/commands/datastore_config_migrate.ts:
    • :126-181: the migration runs inside a root unit of work. The push runs only after the body has completed (pushWhen: "completed"), so the sentinel is already on disk when it starts.
    • :153-161: the alreadyMigrated branch returns early. pushed stays false, so the root's push does nothing.
    • :129-133: the push is a plain pushNamespace. Unlike other managedConfig writes since #2752, a failure is not wrapped in ManagedConfigUnpublishedError, so the user sees the raw sync error with no "Run 'swamp datastore sync --push' to publish it."

Recovery exists but is undiscoverable. The failed push leaves the cache marked as having unpushed changes, so an explicit swamp datastore sync --push, or any later config write such as model create, uploads the migrated files. Neither the error nor the "already completed" message says so.

Steps to reproduce

  1. A repo on @swamp/s3-datastore (or @swamp/gcs-datastore) against a local emulator.
  2. Make the emulator unreachable, for example docker compose pause.
  3. swamp datastore config migrate: exits non-zero with the raw push error.
  4. Restore the emulator, then run swamp datastore config migrate again. It prints "Config migration already completed" (--json: {"alreadyMigrated":true,"managedConfigSet":false}) and exits 0.
  5. A second repo that joins the datastore and pulls finds an empty config tier. swamp model get <name> for a model the first repo migrated is not found.

This was found by reading the source. It has not yet been reproduced against a paused emulator.

Expected

  • A failed push leaves migrate re-runnable. Either:
    • the sentinel is written (or published) only after a successful push; or
    • a re-run checks the sentinel in the datastore, not only in the local cache, and pushes when the datastore does not have it.
  • A failed push fails the way other managedConfig writes do since #2752: non-zero exit, local copy kept, and a message that names swamp datastore sync --push.

UAT

swamp-uat will cover this in tests/cli/datastore/config/migrate_test.ts as part of swamp-uat#490. The test cuts the datastore during the first migrate, restores it, runs migrate again, and checks that a clone joining afterwards sees the migrated definitions.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 2 MOREISSUE_LINKED+ 8 MOREREVIEW+ 10 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

10/6/2026, 11:29:15 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack7210/6/2026, 10:07:46 PM
stack72 linked sibling of #262110/6/2026, 10:08:54 PM

Sign in to post a ripple.