Skip to main content
← Back to list
01Issue
BugOpenSwamp CLIPublic
AssigneesNone

Relationships

#2563 Partial extension catalog saves delete rows for sources mounted from outside the repo

Opened by stack72 · 9/26/2026

Description

Found while working on swamp-club#2355. ExtensionRepository.saveAll always runs ExtensionCatalogStore.pruneUnreachableSources (src/infrastructure/persistence/extension_catalog_store.ts). It deletes every non-tombstoned catalog row whose source path is outside the repo root, except rows belonging to extensions in that same save.

Extensions mounted through .swamp-sources.yaml can live outside the repo root (a sibling checkout, or the git main worktree when the repo is a worktree). Any save that does not include their aggregate therefore deletes their catalog rows. Today that covers every partial save: extension pull (InstallExtensionService), extension rm (RemoveExtensionService) and upgrade (UpgradeExtensionService). The full cold-start reconcile is safe because it saves every aggregate.

Confirmed with a probe during #2355. A row for a live source outside the repo root was present before a partial saveAll and gone after it.

The in-memory registry of a running process is unaffected. The rows stay missing until something re-indexes those sources. That should be the next buildIndex or full reconcile, but it is not verified.

#2355 avoids triggering this from its new serve-reload path: its scoped reconcile calls saveAll with pruneUnreachable false. The pull, rm and upgrade paths still prune.

Why the obvious fix is wrong

Changing the prune to keep an out-of-root row while its source file still exists was tried in #2355 and reverted. The prune also repairs a copied or moved repo. If a repo at path A is copied to path B and A still exists, the rows for A would no longer be pruned by partial saves. resolveOriginConflicts treats paths outside the pulled-extensions dir as local, and local wins, so the stale A rows would clear the type on B's real pulled rows.

Suggested direction

Make the prune mount-aware. Keep an out-of-root row only if its source is under a root that the current .swamp-sources.yaml resolves to (expandSourcePaths and resolveSourceExtensionDirs) and the source still exists. Every ExtensionRepository caller that saves (pull, rm, upgrade, reconcile, serve reload) would then need those mount roots, for example passed at construction.

Steps to reproduce

  1. In a swamp repo, mount an extension directory from outside the repo root through .swamp-sources.yaml, and let a command catalogue it.
  2. Run swamp extension pull for any extension.
  3. Query the extension catalog (.swamp/_extension_catalog.db, table bundle_types): the mounted source's row is gone.
02Bog Flow
◉OPEN○TRIAGED○IN PROGRESS○SHIPPED

Open

9/26/2026, 12:42:48 AM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.