fix(repo): detect orphaned .swamp/ without .swamp.yaml marker (swamp-club#460)#1459
Conversation
…club#460) When a .swamp/ directory exists without a .swamp.yaml marker file, report a distinct error instead of the generic "Not a swamp repository" message. The new error warns that re-initializing may not reconnect to a previous remote datastore, and suggests restoring .swamp.yaml from version control or re-initializing with the correct --datastore flag. Also fixes extension loader setup creating .swamp/ as a side effect in non-repo directories by gating configureExtensionLoaders on the marker being present. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
Code Review
Clean, well-scoped fix for the orphaned .swamp/ detection. The change correctly addresses three entry points (CLI extension loader setup, datastore resolution, and upgrade) and includes comprehensive test coverage.
Blocking Issues
None.
Suggestions
-
Duplicated error message — The 4-line orphaned-repo error string is copy-pasted across
src/cli/repo_context.ts:231(throwRepoNotInitialized) andsrc/domain/repo/repo_service.ts:333(upgrade()). Consider extracting it to a shared constant or a factory onRepoServiceso future wording changes only need one edit. Not blocking since both call sites are small and discoverable. -
hasOrphanedSwampDirnaming vs. behavior — The method returnstrueeven for fully initialized repos (as the test atrepo_service_test.ts:2540documents). This is safe because callers always gate onisInitialized()first, and the JSDoc explains the contract. A name likehasSwampDirwould be more literally accurate, but the current name plus test title make the usage pattern clear enough. -
Multi-line JSDoc on
hasOrphanedSwampDir— CLAUDE.md prefers no comments or single-line max. The JSDoc here is useful since the method's semantics are non-obvious (returns true even when fully initialized), so it earns its keep — just flagging for awareness.
Overall: good DDD placement (hasOrphanedSwampDir on RepoService in the domain layer, error-throwing in CLI layer), proper test coverage for all three entry points, and the marker !== null guard in mod.ts is a clean fix for the side-effect bug. LGTM.
There was a problem hiding this comment.
Adversarial Review
Critical / High
None.
Medium
None.
Low
-
src/domain/repo/repo_service.ts:226—hasOrphanedSwampDirswallows all errors, not justNotFound.
The barecatchreturnsfalsefor any error — permission denied, I/O errors, etc. Compare withRepoMarkerRepository.exists()which only catchesDeno.errors.NotFoundand rethrows anything else. If the user has a.swamp/directory they can't stat (e.g., permission denied), they get the generic "Not a swamp repository" error instead of the orphan-specific one. The fallback behavior is still correct (they still get an error with actionable advice), so this is cosmetic. Extracting and rethrowing non-NotFounderrors would be more consistent with the rest of the codebase. -
Error message duplication across two files.
The orphan-specific error string is copy-pasted identically inrepo_context.ts:231(throwRepoNotInitialized) andrepo_service.ts:333(upgrade). If the message is ever updated, the two copies could drift. A shared constant or helper would prevent that. Not a bug today, but a maintenance friction point. -
hasOrphanedSwampDirdoes not actually detect orphans — it detects.swamp/directory existence.
As the test at line 2540 documents, the method returnstrueeven for a fully initialized repo. The orphan semantics are enforced entirely by callers gating onisInitialized()/markerRepo.read()first. The naming could confuse a future caller into using it without the prerequisite check. The JSDoc says "partially initialized or corrupted repo" but doesn't state the caller prerequisite. However, all current call sites are correct.
Verdict
PASS — Clean, well-scoped fix. The marker !== null gate in mod.ts correctly prevents configureExtensionLoaders from creating .swamp/ as a side effect in non-repo directories. The orphan detection logic in throwRepoNotInitialized and upgrade() correctly provides better error messages when .swamp/ exists without .swamp.yaml. All call sites properly gate on isInitialized / marker-read before calling hasOrphanedSwampDir. Test coverage is thorough.
Summary
.swamp/directory exists without a.swamp.yamlmarker, reports a distinct error warning about partial/corrupted repo state and remote datastore risks, instead of the generic "Not a swamp repository" messageconfigureExtensionLoaders) creating.swamp/as a side effect in non-repo directories by gating on the marker being presenthasOrphanedSwampDir()toRepoServicefor orphan detection, used byresolveDatastoreForRepo(),requireRepoMarker(), andupgrade()Test plan
hasOrphanedSwampDir()— orphaned, missing, and initialized casesresolveDatastoreForRepo()andrequireInitializedRepo()RepoService.upgrade().swamp/→ distinct error.swamp/createdextension searchin fresh dir no longer creates.swamp/Fixes: swamp-club#460
🤖 Generated with Claude Code