Releases: systeminit/swamp
swamp 20260311.132338.0-sha.398857eb
What's Changed
- fix: prevent datastore lock commands from deadlocking on stuck lock (#678)
Fixes #676
Summary
datastore lock statusanddatastore lock release --forcecalledrequireInitializedRepo()which acquires the datastore lock viaregisterDatastoreSync()— causing the breakglass commands to deadlock on the very lock they were meant to inspect or release- Added
resolveDatastoreForRepo()tosrc/cli/repo_context.ts— a lightweight function that resolves repo path + datastore config without acquiring the lock. The lock commands now use this instead - Refactored
requireInitializedRepo()to callresolveDatastoreForRepo()internally, keeping a single source of truth for repo validation and datastore config resolution - Zero user impact for normal usage — only the two broken breakglass commands are affected, and they now work
Test Plan
- New tests for
resolveDatastoreForRepo: returns correct config without acquiring lock, throwsUserErrorfor non-initialized repos - All 2799 tests pass
-
deno fmt --checkpasses -
deno lintpasses -
deno checkpasses - Verified no UAT test impact — no UAT tests exercise
datastore lockcommands
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.132338.0-sha.398857eb/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.132338.0-sha.398857eb/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.132338.0-sha.398857eb/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.132338.0-sha.398857eb/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.105043.0-sha.fa48cb0c
What's Changed
- fix: gracefully handle non-TTY context in interactive commands (#675)
Fixes #674
Summary
-
Interactive search commands (model search, type search, workflow search, etc.) use Ink for terminal UIs which requires raw mode on stdin. In non-TTY contexts (piped input, CI, AI agents), Ink crashes with "Raw mode is not supported". This was a pre-existing bug — it existed before datastores — but the introduction of the datastore lock made it much worse: the crash now leaks the lock, blocking all subsequent swamp commands for ~60 seconds until timeout.
-
Fix 1 —
interactiveOutputMode()helper (src/cli/context.ts): A new function that returns"json"whenctx.outputModeis"json"OR when stdin is not a TTY. Applied surgically to all 10 interactive search commands so they fall back to structured JSON output instead of crashing. Non-interactive commands (version, model get, workflow run, etc.) are unaffected — they keep their normal output mode. -
Fix 2 — Safety net in
main.ts: AddedflushDatastoreSync()to the top-level catch block so the datastore lock is released even on unexpected crashes that bypassrunCli()'s own error handling.flushDatastoreSync()is idempotent (nulls its state on first call), so calling it a second time is harmless.
Commands updated with interactiveOutputMode:
model search,type search,vault search,vault type searchworkflow search,workflow run search,workflow history searchmodel method history search,model output search,data search
Test Plan
- All 2797 tests pass (zero failures)
-
deno fmt --checkpasses -
deno lintpasses -
deno checkpasses - Verified the global outputMode approach (auto-switching all commands) caused 6 test failures — confirmed the surgical per-command approach is correct
- Verified non-interactive commands retain log-mode output in non-TTY contexts
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.105043.0-sha.fa48cb0c/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.105043.0-sha.fa48cb0c/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.105043.0-sha.fa48cb0c/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.105043.0-sha.fa48cb0c/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.230813.0-sha.fcc69c53
What's Changed
- fix: address adversarial review findings for distributed locking and S3 sync (#672)
Summary
Addresses findings from two rounds of adversarial review on the datastore PR.
Changes
Critical: Path traversal in S3 cache sync
- Added
assertSafePath()that validates resolved paths stay within the cache directory - Applied to
pullFile(),pullChanged(),pushFile(), andgetCachePath() - Prevents malicious S3 keys (e.g.,
../../../home/user/.bashrc) from writing outside the cache
High: Lock theft via unconditional heartbeat overwrites
- Added nonce-based fencing tokens to distributed locks
LockInfonow includes an optionalnoncefield (UUID generated on acquire)extend()reads the current lock and verifies the nonce matches before writing- If another process has acquired the lock (different nonce), the old holder self-revokes
- This prevents a paused process from overwriting a legitimately acquired lock after recovery
High: Stale lock detection TOCTOU
- Simplified stale lock cleanup to best-effort delete + conditional write retry
- The nonce fencing makes this safe: even if we accidentally delete a fresh lock, the old holder's
extend()will see the wrong nonce and self-revoke - Removed the ineffective double-check pattern that didn't actually close the race window
High: Degraded mode silently allows corruption
- Lock acquisition failure now throws instead of logging a warning and continuing
- Concurrent commands without locks would corrupt data; failing fast is the correct behavior
Medium: Hidden file exclusion asymmetry
pushChanged()andpushAll()now skip only specific metadata files (.datastore-index.json,.push-queue.json,.datastore.lock) instead of all dot-files- Matches
pullAll()behavior so hidden files like.gitkeepsync correctly
Medium: S3 bucket name validation
- Added regex validation for S3 bucket names (3-63 chars, lowercase, alphanumeric/hyphens/dots)
- Applied to both
SWAMP_DATASTOREenv var parsing and.swamp.yamlconfig loading
Medium: S3 client null body check
- Replaced
response.Body!non-null assertion with explicit null check and descriptive error
Medium: SIGINT handler timeout
- Lock release on Ctrl-C now has a 5-second timeout before force-exiting
- Prevents the process from hanging if S3 is unreachable
Medium: Force release holder verification
swamp datastore lock release --forcenow re-verifies the lock holder (via nonce) before deleting- Prevents accidentally deleting a lock that was legitimately acquired between inspect and delete
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses -
deno run test— 2797 tests pass -
deno run compilesucceeds
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230813.0-sha.fcc69c53/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230813.0-sha.fcc69c53/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230813.0-sha.fcc69c53/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230813.0-sha.fcc69c53/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.230044.0-sha.2610458f
What's Changed
- fix: prevent auto-merge race with adversarial review (#673)
Summary
- Replaces
gh pr merge --auto --squashwithgh pr merge --squashin the CI auto-merge job - The
--autoflag enables GitHub's persistent auto-merge, which merges based on branch protection rules rather than the YAMLneeds:dependencies - This caused PR #669 to auto-merge after the normal review passed but before the adversarial review completed
Root cause
The auto-merge job has needs: [test, deps-audit, claude-review, claude-adversarial-review], but --auto tells GitHub "merge this PR whenever branch protection requirements are met." If claude-adversarial-review isn't a required status check in branch protection settings, GitHub merges as soon as the other checks pass — ignoring the YAML dependency.
Fix
Remove --auto so the merge command executes directly when the job runs. Since the job is gated by needs:, it only runs after all four dependencies (including adversarial review) succeed.
Test plan
- Verify CI workflow passes on this PR
- Confirm PRs no longer merge before adversarial review completes
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230044.0-sha.2610458f/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230044.0-sha.2610458f/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230044.0-sha.2610458f/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230044.0-sha.2610458f/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.223411.0-sha.153e19a5
What's Changed
- feat: configurable datastores with S3 sync and distributed locking (#669)
Summary
Swamp's runtime data (model data, workflow runs, outputs, audit logs, telemetry, secrets, bundles) has always lived in .swamp/ inside the repo. This PR makes the storage backend configurable, adding S3 as a first-class option for team collaboration and introducing distributed locking across both backends to prevent data corruption from concurrent CLI invocations.
- Two backends: filesystem (default, backward-compatible) and S3 (with local cache + automatic sync)
- Distributed locking: both backends now acquire a lock before command execution and release it after — filesystem uses advisory lockfiles, S3 uses conditional writes
- New swamp datastore command group: setup, status, sync, and lock management
- Automatic migration: swamp datastore setup migrates existing data to the new backend
- Concurrent S3 transfers: pull/push operations run in batches of 10 to minimize sync latency
User Impact
Zero breaking changes for existing users
Existing repos with no datastore configuration continue to work identically. The default datastore is filesystem at {repoDir}/.swamp/, which is exactly where data lives today. The only behavioral difference is that a .datastore.lock file will now be created inside .swamp/ during command execution — this is transparent and auto-cleaned.
New: S3 datastore for team collaboration
Teams can now share runtime data via S3:
swamp datastore setup s3 --bucket my-bucket --prefix my-project --region us-east-1
This pushes existing local data to S3 and updates .swamp.yaml. Every subsequent command automatically pulls changes before execution and pushes changes after. The local cache at ~/.swamp/repos/{repoId}/ is fully disposable — deleting it or cloning the repo on a new machine repopulates from S3 on the next command.
New: external filesystem datastore
Move runtime data outside the repo (e.g., shared NFS mount):
swamp datastore setup filesystem --path /mnt/shared/swamp-data
Migration paths
| From | To | Command |
|---|---|---|
| Default .swamp/ | External filesystem | swamp datastore setup filesystem --path /path |
| Default .swamp/ | S3 | swamp datastore setup s3 --bucket name |
| External filesystem | S3 | swamp datastore setup s3 --bucket name |
| S3 | External filesystem | swamp datastore setup filesystem --path /path |
| Any backend | Default .swamp/ | Edit .swamp.yaml, remove datastore field |
Each setup command: verifies the target is accessible, migrates existing data, updates .swamp.yaml, and cleans up the source. Use --skip-migration to skip the data copy.
CI/CD: environment variable override
Override the datastore without modifying .swamp.yaml:
export SWAMP_DATASTORE=s3:my-bucket/my-prefix
export SWAMP_DATASTORE=filesystem:/tmp/swamp-data
Resolution priority: SWAMP_DATASTORE env var > --datastore CLI arg > .swamp.yaml > default.
New: lock management commands
If a process crashes without releasing the datastore lock, the lock auto-expires after 30 seconds. For immediate recovery:
swamp datastore lock status # Show who holds the lock
swamp datastore lock release --force # Force-release stuck lock
Design Choices
Why a lock for filesystem too?
Without locking, two concurrent swamp model run invocations on the same repo could corrupt shared state (overlapping writes to data versions, workflow runs, audit logs). Previously this was a silent race condition. Both backends now acquire a lock at command start and release at command end, making concurrent access safe. The filesystem lock uses advisory lockfiles with Deno.open({ createNew: true }) for atomic creation.
Why not lazy S3 pull?
We considered three approaches for S3 sync performance:
- Lazy pull (fetch on cache miss) — best latency but complicates offline behavior and requires intercepting all file reads throughout the codebase
- Directory-scoped pull (each command declares which subdirectories it needs) — lower transfer volume but fragile annotation maintenance
- Concurrent full pull (batch downloads in parallel) — simplest, no behavioral changes
We chose option 3 (concurrent transfers in batches of 10). It's the lowest-risk improvement and doesn't close any doors. Options 1 and 2 can be layered on later if repos grow large enough to warrant them.
Why size + mtime instead of content hashing?
Change detection compares stat.size and stat.mtime rather than computing content hashes. This avoids reading every file on every sync. The write paths (atomicWriteTextFile, Deno.writeFile) always update mtime, so mtime changes reliably detect rewrites even when file size doesn't change. Under the global lock, there are no concurrent writers to create ABA problems.
Shared S3Client
A single S3Client instance is shared between the lock and the sync service for each command invocation. This avoids duplicate credential resolution and connection overhead.
Sync coordinator supports lock-only mode
The datastore_sync_coordinator.ts accepts optional service and lock parameters independently. Filesystem datastores register lock-only (no pull/push). S3 datastores register both. This keeps the coordinator generic without backend-specific branching.
Lock heartbeat and TTL
Both lock implementations use a background heartbeat that refreshes the lock every TTL/3 (default: every 10 seconds for a 30-second TTL). This prevents long-running commands from having their lock expire. If a process crashes, the lock self-expires after the TTL. The SIGINT handler provides best-effort release on Ctrl-C.
Error-path lock release
flushDatastoreSync() is called on both the success path and the error path in runCli(). This ensures locks are released even when commands fail, preventing stuck locks from cascading into subsequent command failures.
Architecture
Follows domain-driven design with clean layer separation:
Domain layer (src/domain/datastore/):
- DatastoreConfig — discriminated union type for filesystem and S3 configs
- DatastorePathResolver — interface for routing file paths to the correct tier
- DatastorePatternMatcher — gitignore-style glob compiler for exclude patterns
- DatastoreVerifier — interface for health checks
- DatastoreMigrationService — file copy and verification for backend migration
- DistributedLock — interface with acquire(), release(), inspect(), withLock()
Infrastructure layer (src/infrastructure/persistence/):
- DefaultDatastorePathResolver — path resolver with pre-compiled exclude patterns
- S3Client — AWS S3 SDK wrapper (GetObject, PutObject, DeleteObject, HeadBucket, ListObjects)
- S3CacheSyncService — local cache management, index-based change detection, concurrent transfers
- S3Lock — conditional-write lock with heartbeat
- FileLock — advisory lockfile with heartbeat
- DatastoreSyncCoordinator — global singleton managing lock + sync lifecycle
CLI layer (src/cli/):
- resolve_datastore.ts — config resolution from env/CLI/yaml/default
- repo_context.ts — wires lock and sync into the repo lifecycle
- commands/datastore_*.ts — status, setup, sync, lock commands
- presentation/output/datastore_output.ts — log + JSON rendering
Other changes in this branch
This PR also includes changes from earlier commits on this branch (help command, skill updates, extension improvements, bug fixes). The datastore-specific files are listed above; all other changes are from previously-merged work.
Test plan
- deno check passes (type checking)
- deno lint passes
- deno fmt passes (formatting)
- deno run test passes (2794 tests, 0 failures)
- deno run compile succeeds
- Existing repos with no datastore config work identically (default filesystem backend)
- swamp datastore status reports health for default filesystem datastore
- swamp datastore setup filesystem --path /tmp/test-ds migrates data and updates .swamp.yaml
- swamp datastore setup s3 --bucket migrates data to S3 and updates .swamp.yaml
- swamp datastore sync performs bidirectional sync with S3
- swamp datastore lock status shows lock holder or null
- swamp datastore lock release --force force-releases a stuck lock
- SWAMP_DATASTORE=filesystem:/tmp/test overrides .swamp.yaml config
- Concurrent CLI invocations block on lock rather than corrupting data
- Ctrl-C during a command releases the lock (best-effort SIGINT handler)
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.223411.0-sha.153e19a5/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.223411.0-sha.153e19a5/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.223411.0-sha.153e19a5/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.223411.0-sha.153e19a5/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.220229.0-sha.817e290a
What's Changed
- fix: increase extension max file count to 150 (#671)
Summary
- The extension safety analyzer's
MAX_FILE_COUNTwas set to 100, which blocked pushing larger service extensions like@swamp/aws/ec2(104 models) - Increased the limit from 100 to 150 to accommodate extensions that cover broad service APIs
- Updated the corresponding test, design doc, and skill reference to match
Test Plan
- Updated test now creates 151 files and asserts the limit is 150
- All 111 extension domain tests pass
-
deno fmt --checkpasses -
deno lintpasses
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.220229.0-sha.817e290a/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.220229.0-sha.817e290a/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.220229.0-sha.817e290a/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.220229.0-sha.817e290a/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.214543.0-sha.ace8210d
What's Changed
- fix: allow multi-segment extension names (#670)
Summary
- The CLI's
SCOPED_NAME_PATTERNregex only allowed two-segment extension names (@collective/name), rejecting hierarchical names like@swamp/aws/ec2or@swamp/aws/accessanalyzer/analyzer - The swamp-club API already supports multi-segment names — this was a CLI-only restriction that blocked publishing extensions with deeper path hierarchies
- Updated the regex from
^@[a-z0-9_-]+\/[a-z0-9_-]+$to^@[a-z0-9_-]+\/[a-z0-9_-]+(\/[a-z0-9_-]+)*$in all 4 locations (manifest validation, pull, rm, yank commands) - Added tests for
@swamp/aws/ec2and@swamp/aws/accessanalyzer/analyzername formats
Test Plan
- New unit tests for multi-segment names pass
- All 2788 existing tests pass
-
deno fmt --checkpasses -
deno lintpasses - Verified swamp-club API already accepts multi-segment names (no server-side changes needed)
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.214543.0-sha.ace8210d/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.214543.0-sha.ace8210d/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.214543.0-sha.ace8210d/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.214543.0-sha.ace8210d/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260309.181646.0-sha.6cbf8466
What's Changed
- feat: add swamp help command and wire it into all skills (#663)
Summary
Fixes #657
Adds a hidden swamp help [command...] command and teaches all 8 skills to reference it, giving AI agents a reliable way to verify exact CLI syntax on demand.
Step 1: swamp help command
Added a hidden CLI command that outputs structured JSON schema for any swamp command tree. When an agent runs swamp help model or swamp help workflow run, it gets the complete, up-to-date flags, subcommands, and argument types — no guessing, no trial-and-error.
Why this is the right approach: Skills contain curated Quick Reference tables, but they can drift from the actual CLI as flags are added or renamed. swamp help reads directly from the Cliffy command tree at runtime, so it's always accurate. It's hidden from --help output to avoid cluttering the human-facing CLI, but fully available to agents.
Step 2: Skill references to swamp help
Added prominent **Verify CLI syntax** callouts to all 8 skills:
- Skills with CRITICAL sections (model, workflow, vault): Added as a bullet in the CRITICAL rules block — the section agents are trained to read first and follow strictly
- Skills without CRITICAL sections (data, repo, extension-model, issue, troubleshooting): Added as a bold callout right after the intro paragraph, before Quick Reference — the first thing an agent reads after the heading
Why prominent placement matters: An earlier iteration placed subtle blockquotes after Quick Reference tables. These were easy to skim past — an agent confused about flags would scan the table itself, not a quiet note below it. Moving the references into CRITICAL sections and intro blocks ensures agents see them at the point where they're forming their understanding of how to use the CLI.
User Impact
AI agents working with swamp will encounter fewer trial-and-error cycles when constructing CLI commands. Instead of guessing at flags or hallucinating options from the Quick Reference tables, agents can run swamp help <command> to get authoritative syntax. This reduces failed command attempts, speeds up automation workflows, and improves the overall agent experience for users delegating tasks to AI.
Test Plan
-
swamp helpoutputs full CLI schema as JSON -
swamp help modelscopes output to model subtree -
swamp help model method rundrills into nested commands - Unit tests for schema generation and help command
- All 8 skills updated with prominent references
-
deno run compilesucceeds with updated skills embedded
🤖 Generated with Claude Code
Co-authored-by: Blake Irvin blakeirvin@me.com
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.181646.0-sha.6cbf8466/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.181646.0-sha.6cbf8466/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.181646.0-sha.6cbf8466/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.181646.0-sha.6cbf8466/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260309.162343.0-sha.1e5e0c55
What's Changed
- fix: audit timeline misses today's entries in UTC+ timezones (#660)
Summary
Fixes #659
swamp audit failed to read the current day's audit file when the local timezone is east of UTC (e.g. CET/UTC+1). The root cause was a mismatch between how audit files are written vs read:
- Write path (
append): Uses the UTC date from the ISO timestamp (entry.timestamp.split("T")[0]), so files are named with UTC dates (e.g.commands-2026-03-09.jsonl). - Read path (
findByTimeRange): UsedsetHours(0,0,0,0)(local time) thentoISOString()(UTC) to generate filenames. In UTC+ timezones, local midnight converts to the previous UTC day, so today's file was never opened.
Fix
Changed three lines in findByTimeRange to use UTC methods consistently:
setHours(0,0,0,0)→setUTCHours(0,0,0,0)— start of date rangesetHours(23,59,59,999)→setUTCHours(23,59,59,999)— end of date rangesetDate(getDate()+1)→setUTCDate(getUTCDate()+1)— date iteration step
This ensures the read path generates the same UTC-based date strings as the write path, regardless of the user's local timezone.
User Impact
Users in UTC+ timezones (CET, IST, JST, AEST, etc.) will now see today's audit entries when running swamp audit. Previously, today's entries were silently missing — only older days' data appeared.
Test Plan
- Added regression test verifying
findByTimeRangereads the correct UTC-dated file for same-day queries - Added test verifying multi-day UTC date spanning works correctly
- All 2775 existing tests continue to pass
deno check,deno lint,deno fmtall pass
Co-authored-by: Blake Irvin blakeirvin@me.com
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.162343.0-sha.1e5e0c55/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.162343.0-sha.1e5e0c55/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.162343.0-sha.1e5e0c55/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260309.162343.0-sha.1e5e0c55/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260308.214522.0-sha.9aa5b71a
What's Changed
- docs: add sync method pattern to extension model skill (#656)
Summary
Closes #627.
Adds sync as a standard CRUD lifecycle method in the swamp-extension-model skill. This teaches Claude to generate sync methods by default when creating CRUD models, enabling drift detection without any changes to swamp core.
Why model-level, not core
A generic swamp status command would need to know:
- What the identifier field is called (
VpcIdvsidvsNamevsslug) - How to call the provider API to check existence
- What "not found" looks like (404, empty response, error code)
The model already knows all of this. The model is the domain expert for its service — sync belongs there, not in a generic framework command.
What changed
- SKILL.md — CRUD lifecycle section updated from create/update/delete to create/update/delete/sync. Explains the key UX distinction: unlike
get(which requires the resource ID as an argument),syncreads the ID from stored state, making it zero-arg and suitable for automated drift detection workflows. - references/examples.md — Sync method added to the VPC CRUD example. New standalone "Sync Method" section with a generic pattern and a workflow example showing how to orchestrate sync across an entire stack.
- references/scenarios.md — Sync method added to the S3 bucket CRUD scenario with the workflow updated to include a sync action.
Design decisions
syncis non-optional — unlike polling and idempotent creates (which are opt-in), every CRUD model gets sync by defaultsyncdoes not throw when the resource is gone — it writes anot_foundmarker, because the purpose is detection, not failure- No swamp core changes needed — all primitives already exist (
context.dataRepository.getContent(),swamp data list,swamp data search)
Test plan
- Verify skill files are valid markdown with correct internal links
- Verify
deno fmt --checkpasses - Test that Claude generates sync methods when asked to create a new CRUD model
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260308.214522.0-sha.9aa5b71a/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260308.214522.0-sha.9aa5b71a/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260308.214522.0-sha.9aa5b71a/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260308.214522.0-sha.9aa5b71a/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/