feat: add swamp extension trust CLI commands#766
Conversation
…ollectives Add CLI commands to manage trusted collectives for extension auto-resolution, making a previously hidden configuration discoverable via the CLI. Commands: - `swamp extension trust list` — show explicit, membership, and resolved collectives - `swamp extension trust add <collective>` — add a collective to the trusted list - `swamp extension trust rm <collective>` — remove a collective from the trusted list - `swamp extension trust auto-trust <on|off>` — toggle membership auto-trust Architecture follows the libswamp + renderer pattern (issue #739): - Generators in src/libswamp/extensions/ with injected deps for testability - Log + Json renderers in src/presentation/renderers/ - CLI commands in src/cli/commands/ as pure wiring Also extracts resolveTrustedCollectives and DEFAULT_TRUSTED from src/cli/mod.ts into src/libswamp/extensions/trust.ts as shared domain logic, eliminating duplication and ensuring a single source of truth. Updates design docs and 7 skill files to reference the new CLI commands.
The 3 new trust renderers use writeOutput from infrastructure (same pattern as all existing renderers like auth_whoami). Bump the KNOWN_PRESENTATION_INFRA_VIOLATIONS ratchet from 43 to 46.
There was a problem hiding this comment.
Review Summary
This PR adds well-designed CLI commands for managing trusted collectives (swamp extension trust list/add/rm/auto-trust), solving an important discoverability problem.
No Blocking Issues
The PR follows all project requirements and patterns correctly.
Architecture (DDD Compliant)
The architecture properly follows the libswamp + renderer pattern:
| Layer | Files | Assessment |
|---|---|---|
| Shared types | src/libswamp/extensions/trust.ts |
DEFAULT_TRUSTED, TrustModifyData, TrustModifyEvent, resolveTrustedCollectives() properly placed |
| Application Services | src/libswamp/extensions/trust_{list,add,rm,auto_trust}.ts |
Correct async generator pattern with Deps injection, LibSwampContext, and typed events using kind discriminant |
| Renderers | src/presentation/renderers/trust_*.ts |
Proper log + json output modes |
| CLI wiring | src/cli/commands/extension_trust*.ts |
Pure wiring as expected |
Code Quality
- Named exports throughout (no default exports)
- All files include AGPLv3 copyright headers
- CLI commands correctly import from
src/libswamp/mod.ts - Input validation on collective names (
/^[a-z0-9][a-z0-9_-]*$/) prevents malformed input - Proper error handling with
SwampErrortypes - The
AnyOptionspattern is consistent with 68+ existing CLI commands
Test Coverage
All new source files have corresponding test files:
- 5 generator test files in
src/libswamp/extensions/ - 3 renderer test files in
src/presentation/renderers/ - Tests cover happy paths, error cases, and edge cases (empty arrays, defaults, deduplication)
Documentation
Good updates to design docs and 7 skill files for discoverability.
Note on DDD Ratchet
The bump from 43 to 46 KNOWN_PRESENTATION_INFRA_VIOLATIONS is appropriate - the new renderers use writeOutput from infrastructure following the existing pattern.
There was a problem hiding this comment.
Adversarial Review
I've systematically traced code paths, examined edge cases, and stress-tested error handling across all changed files. This is a well-structured PR that follows the libswamp + renderer pattern consistently.
Critical / High
None found. The code is solid.
Medium
-
Race condition in read-modify-write (theoretical) —
trust_add.ts:64-87,trust_rm.ts:52-75,trust_auto_trust.ts:60-72All three generators follow a read-modify-write pattern without locking:
const marker = await deps.readMarker(); // ... modify marker.trustedCollectives ... await deps.writeMarker(marker);
Breaking scenario: Run
swamp extension trust add orgAandswamp extension trust add orgBconcurrently. Both read the same initial state, both append their collective, and whichever writes last wins — the first addition is lost.Severity: Medium because (a) atomic write prevents corruption, only data loss, (b) concurrent CLI invocations of the same command are rare in practice, (c) the effect is limited to trust configuration, not critical data.
Suggested fix: Not required for merge. If ever needed, use file locking or optimistic concurrency (read version, fail if changed).
Low
-
@prefix in collective name causes confusing error —trust_add.ts:27,54-62Users familiar with extension types (
@swamp/model) might inputswamp extension trust add @myorg. The validation error says "Must match [a-z0-9][a-z0-9_-]*" but doesn't explain that collective names don't use the@prefix.Example:
swamp extension trust add @acme→ error about regex, user confusedSuggested fix: Add a specific check for leading
@with a clearer message: "Collective names don't include the '@' prefix. Use 'acme' instead of '@acme'." -
Infrastructure exceptions bubble up raw —
trust_add.ts:87,trust_rm.ts:75,trust_auto_trust.ts:72If
writeMarkerthrows (disk full, permission denied), the raw Deno exception propagates to the user rather than a SwampError. This produces less consistent error messaging compared to validation errors.Example:
Deno.errors.PermissionDenied: permission deniedinstead of "Failed to update .swamp.yaml: permission denied"Suggested fix: Wrap writeMarker in try/catch and yield an error event with a user-friendly message. Not blocking since atomic write prevents partial state.
Verdict
PASS — No blocking issues found. The code is well-tested (50 new tests), follows established patterns, and handles the common paths correctly. The race condition is theoretical for a CLI tool, and the UX improvements in Low findings are polish, not correctness issues.
Summary
Adds CLI commands to manage trusted collectives for extension auto-resolution, solving a discoverability problem where the feature was completely invisible to both users and AI agents.
The Problem
A user asked Claude about trusted collectives in their swamp repo, and Claude responded: "The trusted collectives feature doesn't exist in swamp yet." Despite the feature being fully implemented (PRs #725 and #727), it was only configurable by manually editing
.swamp.yaml— no CLI command, no--helptext, no discoverability path.The Solution
Four new commands under
swamp extension trust:The
listcommand shows the full picture — explicit collectives from.swamp.yaml, membership collectives from auth, and the resolved/merged effective list. This means even on a fresh repo with no config, a user sees thatswampandsiare trusted by default.Architecture
Follows the libswamp + renderer pattern (issue #739):
src/libswamp/extensions/trust.tsDEFAULT_TRUSTED,TrustModifyData,TrustModifyEvent,resolveTrustedCollectives()src/libswamp/extensions/trust_{list,add,rm,auto_trust}.tssrc/presentation/renderers/trust_{list,modify,auto_trust}.tssrc/cli/commands/extension_trust*.tsRefactoring
resolveTrustedCollectives()fromsrc/cli/mod.tsintosrc/libswamp/extensions/trust.ts— it was domain logic living in the CLI layersrc/cli/mod_test.tstosrc/libswamp/extensions/trust_test.tsto follow the codeTrustModifyEventshared by bothtrust_addandtrust_rmgenerators (identical event shapes, no reason for separate types)DEFAULT_TRUSTEDdefined once, used everywhereDocumentation Updates
design/extension.md— documents CLI commands in "Trusted Collectives" sectiondesign/repo.md— cross-references CLI commands fromtrustedCollectivesconfigswamp extension trustreferences for discoverabilityTest Plan
src/libswamp/extensions/) — all passsrc/presentation/renderers/trust_*_test.ts) — all passresolveTrustedCollectivestests moved to proper location — all passmod_test.tstests still pass (57 tests, down from 65 after moving 8)deno checkpasses on all filesdeno lintpassesdeno fmtapplied🤖 Generated with Claude Code