Skip to content

Releases: systeminit/swamp

swamp 20260506.233640.0-sha.5729ac50

06 May 23:37
Immutable release. Only release title and notes can be modified.
5729ac5

Choose a tag to compare

What's Changed

  • fix(extensions): reject quality and fmt on pulled extensions (swamp-club#239) (#1328)

Summary

  • extension quality and extension fmt fail on pulled extensions because the manifest's paths.base defaults to typedDir, resolving model files against extensions/models/ in the repo root instead of the pulled extension's directory
  • Added isPulledExtensionManifest() helper that detects manifests under .swamp/pulled-extensions/ and rejects both commands early with a clear error message
  • Pulled extensions are read-only registry copies — running fmt/quality on them is not useful

Test Plan

  • Unit tests for isPulledExtensionManifest (absolute/relative/local/non-.swamp paths)
  • Reproduced original bug in scratch repo: extension quality on pulled manifest → "Model file not found"
  • Verified fix: both commands now reject with clear error message
  • Verified local extensions still work normally
  • Full test suite passes (5570 tests, 0 failures)
  • deno check, deno lint, deno fmt all clean

Closes swamp-club#239

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.233640.0-sha.5729ac50/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/v20260506.233640.0-sha.5729ac50/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/v20260506.233640.0-sha.5729ac50/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/v20260506.233640.0-sha.5729ac50/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.221457.0-sha.15cd7fdc

06 May 22:16
Immutable release. Only release title and notes can be modified.
15cd7fd

Choose a tag to compare

What's Changed

  • fix(extensions): preserve catalog fingerprint when bundle build fails (swamp-club#265) (#1327)

Summary

  • When bundleWithCache returns a stale cached bundle (via isExpectedBundleFailure fast-path or error fallback), rebundleAndUpdateCatalog now preserves the catalog's stored source_fingerprint instead of writing the new one. This prevents permanent fingerprint poisoning where findStaleFiles sees matching fingerprints and never retries the bundle.
  • Adds kind-agnostic BundleResult type to bundle_freshness.ts with { js, fromCache } — shaped for W4's KindAdapter to absorb when it consolidates the loaders.
  • Emits a structured warning ("source fingerprint preserved, will retry on next command") only on the fallback case (fromCache && fingerprint differs), not on legitimate cache hits.
  • Applied mechanically across all 5 extension loaders (models, drivers, vaults, datastores, reports).
  • bundleAndIndexOne surfaces fromCache as an additive return field — no caller changes needed (reconcile and install services create Sources independently).
  • findStaleFiles signature unchanged (W2's RemoveExtensionService safety-net preserved).
  • Filed deferred tracker issues: swamp-club#270 (warm-start state oscillation debt) and swamp-club#271 (sourceToRow empty mtime).

Test Plan

  • Added warm-start regression integration test (integration/source_extension_rebundle_test.ts) with three assertion groups:
    • (a) Happy path: source-mounted extension primed with V1, modified to V2 with bare specifiers
    • (b) Permanent-failure determinism: two consecutive warm-starts assert identical catalog fingerprint and warning emitted both times
    • (c) Warning-log precision: assert warning fires for fallback case, assert NO warning for unchanged-source warm-start
  • Verified before/after against compiled binary vs swamp on PATH:
    • Before: fingerprint poisoned (faa89ac09cda69ead2141011), bundle stale, no warning, permanently stuck
    • After: fingerprint preserved (faa89ac09cda), bundle stale but retryable, warning emitted, deterministic
  • All 5562 existing tests pass, 0 failures
  • Existing bundle_cache_freshness_test.ts (issue #125 regression net) passes

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.221457.0-sha.15cd7fdc/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/v20260506.221457.0-sha.15cd7fdc/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/v20260506.221457.0-sha.15cd7fdc/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/v20260506.221457.0-sha.15cd7fdc/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.195744.0-sha.60b2e6d2

06 May 19:59
Immutable release. Only release title and notes can be modified.
60b2e6d

Choose a tag to compare

What's Changed

  • feat(cli): add swamp issue get command (swamp-club#118) (#1326)

Summary

  • Adds swamp issue get <number> CLI command to fetch and display Lab issue details (title, type, status, author, body, assignees, comment count)
  • Supports both log (human-readable) and json (structured) output modes
  • Adds fetchIssue method to SwampClubClient using x-api-key auth (consistent with existing methods)
  • Updates swamp-issue skill with new command documentation and JSON output shape

Closes swamp-club#118

Test Plan

  • Unit tests for issueGet libswamp generator (src/libswamp/issues/get_test.ts)
  • CLI command structure test (src/cli/commands/issue_get_test.ts)
  • deno check — all files pass type checking
  • deno lint — no lint errors
  • deno fmt — all files formatted
  • deno run test — 5542 passed (1 pre-existing flaky failure in unrelated workflow test)
  • deno run compile — binary compiles successfully

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.195744.0-sha.60b2e6d2/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/v20260506.195744.0-sha.60b2e6d2/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/v20260506.195744.0-sha.60b2e6d2/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/v20260506.195744.0-sha.60b2e6d2/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.190736.0-sha.f040ad12

06 May 19:08
Immutable release. Only release title and notes can be modified.
f040ad1

Choose a tag to compare

What's Changed

  • feat(extensions): W3 — ReconcileFromDisk + freshness-as-aggregate-query (swamp-club#252) (#1322)

Summary

Implements W3 of the extension catalog rearchitecture — the third workstream after W1a/W1b/W2:

  • ReconcileFromDiskService (src/libswamp/extensions/reconcile_from_disk_service.ts): new application service that walks on-disk source trees across all three origin types (locals, pulled, source-mounted), diffs against persisted aggregate state, and emits RowState transitions. Delegates to per-loader bundleAndIndexOne (not InstallExtensionService). Features: dryRun seam with structured ReconcileTransition return type, transition-count guardrail (>50% of rows → abort), cold-start trigger via anyKindNeedsInvalidation().

  • enforceI2 deterministic-winner transform: replaces the IntraExtensionDuplicateType throw with a lexicographic-winner + tombstone-loser transform within the Extension aggregate. Cross-aggregate uniqueness (I-Repo-1) still throws DuplicateTypeError at the repository layer.

  • Two-layer freshness model: type resolution is now a trivial aggregate query (isFresh = state.tag === "Indexed"). State maintenance is split between cold-start reconcile (ReconcileFromDisk) and warm-start incremental detection (findStaleFiles with fingerprint comparison, preserved for the hot-path development workflow).

  • UNREADABLE_DEP_SENTINEL removal: renamed to UNREADABLE_PLACEHOLDER (internal to computeSourceFingerprint). Zero remaining references in production code.

Deliberate scope change from plan v3

findStaleFiles retains fingerprint comparison for the warm-start path. The plan's "~20 LOC shim" target was wrong — warm-start incremental detection is load-bearing (12 loader tests verified this). The architecture agent confirmed the revert is correct and permanent. The design doc documents the resulting two-layer model.

Regression tests

Three regression tests proving the rebundle-loop bug class is structurally fixed:

  • #208: broken transitive dep → stable state, no rebundle loop
  • #209: schema-invalid extension → stable state, no rebundle loop
  • #212: cached bundle missing → rebundles once, not in a loop

Pulled reconcile matrix

Three tests covering the pulled-extension reconcile paths:

  • New source + lockfile entry → Indexed
  • Orphan (no lockfile entry) → Tombstoned
  • Missing source + lockfile present → EntryPointUnreadable

Performance benchmark

reconcile_from_disk_bench.ts: 50 local models cold-start = 1.2s, warm-start no-op = 7ms (Apple M2 Max). Pre-committed thresholds documented: ≤1.2x ship, 1.2-2x optimize, >2x redesign.

Note: The 1.2s cold-start number is the W3 baseline. A pre-W3 comparison measurement on main should be captured before merge to verify the ≤1.2x threshold.

Test plan

  • 13 dedicated ReconcileFromDisk tests (7 generic contract + 3 regression + 3 pulled matrix)
  • 3 new enforceI2 transform tests (two-way, three-way, idempotent)
  • Updated 1 repository test for W3 transform behavior
  • All 5542 tests pass (deno run test)
  • deno check — clean
  • deno lint — clean
  • deno fmt — clean
  • deno run compile — compiled
  • UNREADABLE_DEP_SENTINEL grep — zero occurrences
  • Author smoke: cold-start, warm-start, catalog-deleted recovery on scratch repo + parent repo with real extensions
  • Pre-W3 baseline comparison (~20 min, before merge)
  • Diversity-matrix soak: Linux + macOS, multiple repo shapes, 24-48h

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.190736.0-sha.f040ad12/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/v20260506.190736.0-sha.f040ad12/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/v20260506.190736.0-sha.f040ad12/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/v20260506.190736.0-sha.f040ad12/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.185912.0-sha.2fc92714

06 May 19:00
Immutable release. Only release title and notes can be modified.
2fc9271

Choose a tag to compare

What's Changed

  • feat(vault): add read-secret CLI command (swamp-club#238) (#1325)

Summary

  • Add swamp vault read-secret <vault> <key> CLI command that retrieves and displays secret values from vaults
  • Wires existing VaultService.get() to a CLI surface — no VaultProvider interface changes, every vault already implements get() for CEL expression resolution
  • Adds VaultSecretRead domain event for audit trail, --force flag for non-interactive use, and skips confirmation in --json mode for agent consumption
  • Fixes vault skill quick reference table that incorrectly referenced swamp vault get <vault> <key> (which doesn't exist)

Test plan

  • 6 unit tests for libswamp generator (happy path, vault not found, no vaults configured, empty vault name, empty key, secret not found)
  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run compile succeeds
  • UAT issue to be filed in swamp-uat for CLI coverage
  • Docs issue to be filed in swamp-club for content/manual/reference/vaults.md

Closes swamp-club#238

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.185912.0-sha.2fc92714/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/v20260506.185912.0-sha.2fc92714/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/v20260506.185912.0-sha.2fc92714/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/v20260506.185912.0-sha.2fc92714/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.184342.0-sha.725c7eb1

06 May 18:44
Immutable release. Only release title and notes can be modified.
725c7eb

Choose a tag to compare

What's Changed

  • test(extensions): remove flaky cross-process lifecycle stress test (#1324)

Summary

  • Removes integration/lifecycle_concurrent_stress_test.ts (introduced in swamp-club#254, commit 5a337b8)
  • The test runs 50 iterations of 4 concurrent subprocesses (pull alpha, pull beta, rm alpha, update) and checks catalog↔lockfile↔FS consistency after each iteration
  • Running rm and pull for the same extension concurrently produces inevitable transient inconsistencies due to the asymmetric orderings (install: FS→lockfile→catalog vs rm: catalog→lockfile→FS) — each Windows CI run surfaces a different interleaving failure (version skew, catalog-without-lockfile, etc.)
  • The unit-level FaultingStubRepository tests already pin the per-service rollback semantics; the cross-process stress test adds cost without reliable signal

Test plan

  • deno check passes
  • deno lint passes
  • deno fmt passes
  • Windows CI passes (the flaky test is removed)
  • Linux/macOS CI continues to pass

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.184342.0-sha.725c7eb1/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/v20260506.184342.0-sha.725c7eb1/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/v20260506.184342.0-sha.725c7eb1/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/v20260506.184342.0-sha.725c7eb1/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.180950.0-sha.58bede5e

06 May 18:10
Immutable release. Only release title and notes can be modified.
58bede5

Choose a tag to compare

What's Changed

  • feat(workflows): configurable concurrency limits for fan-out (swamp-club#260) (#1323)

Summary

  • Add optional concurrency field at workflow, job, and step levels to cap parallel execution in fan-out scenarios (forEach, parallel jobs/steps)
  • Implement Semaphore primitive and mergeWithConcurrency() stream combinator that wraps existing merge() with zero overhead on the unbounded path
  • Support SWAMP_MAX_CONCURRENT_STEPS env var as a host-level ceiling (min(local, global))

Details

Resolution order: step → job → workflow → unbounded. The most-local non-zero value wins. 0 or absent means unbounded (full backward compatibility).

Verified end-to-end: a 10-item forEach with concurrency: 3 correctly batches execution into groups of 3 (max concurrent = 3, ~8s total vs ~2s unbounded).

Files changed

Area Files
Infrastructure semaphore.ts (new), merge.ts (+mergeWithConcurrency), libswamp re-export
Domain schemas workflow.ts, job.ts, step.ts — optional concurrency field
Execution execution_service.ts — concurrency resolution + gated merge at both job and step levels
Design doc design/workflow.md — concurrency semantics section
Skill swamp-workflow SKILL.md + forEach reference
Tests 7 semaphore tests, 7 mergeWithConcurrency tests, 5541 total pass

Follow-up issues

  • swamp-club#266 — Docs: document concurrency limits in reference manual
  • systeminit/swamp-uat#192 — UAT: adversarial test for workflow concurrency limits

Closes swamp-club#260

Test plan

  • deno check — zero type errors
  • deno lint — clean
  • deno fmt — clean
  • deno run test — 5541 passed, 0 failed
  • deno run compile — binary compiled
  • End-to-end verification: concurrency: 3 caps 10-item fan-out (max concurrent = 3, batched in ~2s intervals)
  • Backward compatibility: workflows without concurrency field behave identically

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.180950.0-sha.58bede5e/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/v20260506.180950.0-sha.58bede5e/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/v20260506.180950.0-sha.58bede5e/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/v20260506.180950.0-sha.58bede5e/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260506.162853.0-sha.65f23545

06 May 16:29
Immutable release. Only release title and notes can be modified.
65f2354

Choose a tag to compare

What's Changed

  • fix(drivers): resolve vault sentinels for out-of-process drivers (swamp-club#263) (#1321)

Summary

  • Resolve vault sentinel tokens (__SWAMP_VSEC_*__) in ExecutionRequest.methodArgs and ExecutionRequest.globalArgs before dispatching to out-of-process drivers (docker, custom extension drivers)
  • The raw driver already resolves sentinels internally via DefaultMethodExecutionService.execute(), but the non-raw dispatch path sent sentinels through unresolved — models received literal sentinel strings instead of decrypted secret values
  • Resolution operates on structuredClone data from the definition, so persisted state is never mutated

Test Plan

  • Added unit test: registers a mock capture driver via DriverTypeRegistry, dispatches through executeWorkflow with a VaultSecretBag containing sentinel mappings, and asserts the captured ExecutionRequest has resolved plaintext values — not sentinels
  • Verified against reproduction scenario: workflow step with ${{ vault.get(...) }} input under docker driver now resolves correctly (sentinel __SWAMP_VSEC_8c14a9ea_0__ → redacted ***)
  • All 5528 existing tests pass
  • deno check, deno lint, deno fmt clean

Closes swamp-club#263

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260506.162853.0-sha.65f23545/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/v20260506.162853.0-sha.65f23545/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/v20260506.162853.0-sha.65f23545/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/v20260506.162853.0-sha.65f23545/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260505.231643.0-sha.5a337b81

05 May 23:17
Immutable release. Only release title and notes can be modified.
5a337b8

Choose a tag to compare

What's Changed

  • test(extensions): cross-process concurrency stress for W2 lifecycle services (swamp-club#254) (#1320)

Summary

Closes swamp-club#254. Post-merge follow-up to PR #1310 (W2 — lifecycle services).

Adds integration/lifecycle_concurrent_stress_test.ts that mirrors the swamp-club#234 race-regression pattern at integration/data_delete_test.ts. 50 iterations × 4 concurrent CLI subprocesses (extension pull @stress/alpha, pull @stress/beta, rm @stress/alpha, update) verify the W2 services' per-extension-atomicity claim under cross-process contention.

Five invariants per iteration:

  • (i) Catalog ↔ lockfile bijection: every Indexed _extension_catalog.db row for @stress/* matches a lockfile entry by name+version, and vice versa.
  • (ii) No DuplicateTypeError leakage across distinct fixture types (@stress/alpha-model vs @stress/beta-model). Fixtures deliberately declare distinct type ids — sharing them would surface a real cross-extension collision on every concurrent install of alpha+beta and the test would mis-categorise it as benign.
  • (ii') No lockfile-retry-budget exhaustion (Could not acquire lock on upstream_extensions.json) or SQLite-busy contention (SQLITE_BUSY, database is locked). These are real failures, not benign race outcomes.
  • (iii) extensions/models/upstream_extensions.json is well-formed JSON after every iteration.
  • (iv) No orphan files under .swamp/pulled-extensions/@stress/... — every file is referenced by some lockfile entry's files[]. Empty leftover directories are tolerated; extension rm only prunes the deepest empty directory, matching integration/extension_rm_test.ts's file-level absence assertion.

The harness ships a co-located Deno.serve fixture registry implementing the four ExtensionApiClient endpoints the install path requires (/{name}, /{name}/latest, /{name}@{version}/download, /checksum) so the test runs offline and deterministically. Inline comments at each fake endpoint reference the corresponding extension_api_client.ts callsite so future API drift fails this test loudly rather than silently shipping bad production code.

design/extension.md "Crash-state recovery" section gains one paragraph pinning this test as the load-bearing concurrency-claim verification, with inline swamp-club#254 and test-path references.

Test Plan

  • deno check clean
  • deno lint clean
  • deno fmt --check clean
  • New stress test runs 3× back-to-back green: 2m58s, 2m57s, 2m57s
  • Full suite (deno run test) green: 5527 passed / 0 failed / 28 ignored (3m15s)
  • deno run compile builds binary
  • Wall-clock cost: 2m57s for 50 iterations × 4 children. data_delete_test.ts is 1m39s for 50 × 2 children — ~1.78× ratio, expected scaling.

Notes for reviewers

  • Both load-bearing constants (RACE_ITERATIONS = 50, CONCURRENCY_PER_ITERATION = 4) are pinned with code-comments referencing swamp-club#254 — please don't shrink them silently.
  • N=50 is mirrored from data_delete_test.ts (where it was calibrated against an evidence-base) but this test verifies the absence of a race in code claimed safe — there's no calibration point. The header comment is honest about this; CI's natural soak across the merge train is the longer-tail coverage.

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260505.231643.0-sha.5a337b81/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/v20260505.231643.0-sha.5a337b81/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/v20260505.231643.0-sha.5a337b81/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/v20260505.231643.0-sha.5a337b81/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260505.203422.0-sha.d67b0d35

05 May 20:35
Immutable release. Only release title and notes can be modified.
d67b0d3

Choose a tag to compare

What's Changed

  • fix(persistence): close TOCTOU windows in YAML output repo walkers (swamp-club#249) (#1319)

Summary

Followup to #1307. Closes the two pre-existing TOCTOU windows in the YAML output repo walkers that were flagged as out-of-scope when #1307 merged.

What

Issue 1 — Directory-level TOCTOU in findAll

yaml_output_repository.ts:118 — pre-fix, a NotFound from Deno.readDir(methodDir) (concurrent bulk delete of a method directory) escaped the inner for-await to the outer catch and returned [], discarding outputs already collected from earlier method directories of the same type. The fix wraps the inner readDir(methodDir) body in its own try/catch that continues on NotFound. The outer catch keeps its role of "type directory itself doesn't exist."

The same shape exists in findAllGlobalSince (line 200) — same race, smaller blast radius (current model type only). Fixed for consistency.

Issue 2 — Per-file TOCTOU in findById and delete

yaml_output_repository.ts:65 (findById) and :195 (delete) — pre-fix, a NotFound from Deno.readTextFile on a non-target file (concurrent delete during iteration) propagated to the outer catch and returned null / aborted the search even when the target existed. The fix wraps the readTextFile + parseYaml + match block in a per-file try/catch that continues on NotFound.

For delete, the loop is restructured as find-then-act: per-file try/catch wraps only the search; notifyDirty + Deno.remove + cleanupEmptyParentDirs run after the loop in the existing outer try/catch, preserving the existing semantic that a NotFound from Deno.remove (target deleted concurrently) is absorbed silently (idempotent delete).

Why no fix in YamlWorkflowRunRepository.findById

The issue body lists this method too, but the existing entry.name.includes(runId) filter at line 78 restricts readTextFile to the target file only — runIds are 36-char UUIDs that never substring-match each other, and atomicWriteTextFile's .{freshUuid}.tmp intermediate files don't contain the runId. The only NotFound readTextFile can raise is for the target's own concurrent deletion, for which returning null is correct. Added an in-place comment documenting this so a future maintainer who removes that filter doesn't silently re-introduce the bug.

Tests

Four new strict regression tests in yaml_output_repository_test.ts, each fails against pre-fix code and passes against the fix:

  • findAll: method directory deleted mid-iteration is skipped, not fatal
  • findAllGlobalSince: method directory deleted mid-iteration is skipped, not fatal
  • findById: non-target file deleted mid-iteration still returns target
  • delete: non-target file deleted mid-iteration still deletes target

The "remove file/dir before call" pattern from #1307's tests doesn't strictly trigger the race on most filesystems (readdir returns only currently-existing entries). The new tests use a withPhantomReadDirEntry helper that monkey-patches Deno.readDir for the call's duration to yield a phantom entry pointing at nothing on disk; production code follows up with readTextFile/readDir on the phantom path, getting a real NotFound, deterministically exercising the TOCTOU window. The helper follows the same shape as the codebase's established Deno.env save/restore pattern.

Test plan

  • `deno fmt` — clean
  • `deno lint` — clean
  • `deno check` — clean
  • `deno run test` — full suite passes (one unrelated flaky parallel-test in `shutdown_handlers_test.ts` SIGINT-on-POSIX, passes in isolation both with and without these changes)
  • All four new tests verified failing pre-fix and passing post-fix via stash/unstash cycle
  • `deno run compile` — binary refreshed

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260505.203422.0-sha.d67b0d35/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/v20260505.203422.0-sha.d67b0d35/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/v20260505.203422.0-sha.d67b0d35/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/v20260505.203422.0-sha.d67b0d35/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/