Releases: systeminit/swamp
swamp 20260424.234336.0-sha.0022d1ca
What's Changed
- feat(doctor): add swamp doctor audit preflight diagnostic (swamp-club#156) (#1227)
Summary
Adds swamp doctor audit [--tool <name>] — a new preflight diagnostic that verifies the AI-tool audit integration configured in the repo is healthy end-to-end. Non-zero exit on any fail, safe to gate in CI.
doctor is registered as a parent namespace so future diagnostics (doctor datastore, doctor vault, ...) plug in as siblings. Implements swamp-club#156.
Five checks
| Check | Scope | What it catches |
|---|---|---|
binary-on-path |
all 4 tools | AI tool binary missing from PATH |
swamp-binary-on-path |
all 4 tools (+ Kiro baked-path) | swamp missing from PATH, or kiro hook's baked absolute path orphaned by brew upgrade |
agent-config-loadable |
per-tool parser | config missing, malformed, or broken (e.g. Kiro tools: ["*"]) |
default-agent-set |
Kiro only | .kiro/settings/cli.json not pointing chat.defaultAgent at swamp |
recording-smoke-test |
all 4 tools | upstream normalizer drift — synthetic postToolUse payload piped through swamp audit record --from-hook must land as a row in today's audit JSONL |
The headline verification
Temporarily removing "shell" from KIRO_SHELL_TOOL_NAMES in hook_input.ts, recompiling, and running doctor against a freshly-init'd kiro repo produces:
✗ recording-smoke-test synthetic kiro payload did NOT land in today's audit JSONL
hint: The hook normalizer in src/domain/audit/hook_input.ts may have drifted from the upstream contract...
4 passed, 1 failed, 0 skipped — OVERALL: FAIL
Reverted before this commit. This is the load-bearing proof that the feature protects against future upstream drift instead of being theatre.
Name override
The issue body says "please NOT doctor". The team picked doctor anyway — the command is a namespace and will grow; the name fits that shape. Acknowledging here for reviewer context.
Scope note on the three cited Kiro bugs
Issue 156 cites three concrete bugs as motivation. Their status in this PR:
tools: ["*"]in kiro agent config →agent-config-loadableflags it with a hint; NOT fixed here.- Missing
chat.defaultAgent: "swamp"→default-agent-setflags it; NOT fixed here. - Hard-coded
tool_name === "execute_bash"in Kiro normalizer → already fixed upstream of this PR.src/domain/audit/hook_input.ts:146-150hasKIRO_SHELL_TOOL_NAMES = new Set(["execute_bash", "shell", "execute_cmd"]). This PR's diagnostic catches the class of bug, it does not fix that specific one.
Ancillary changes
- Extract
todaysAuditFilePathhelper.src/infrastructure/persistence/jsonl_audit_repository.tshad two duplicate copies of thecommands-YYYY-MM-DD.jsonlformat string; moved tosrc/domain/audit/audit_path.tsand consumed by both the writer and the doctor smoke-test reader. Single source of truth for the filename format. - Reserve
echo swamp-doctor-smoke-testas a sentinel command prefix. The smoke-test writes a real audit row with this prefix; the audit timeline filters it out of the default view. Opt in withswamp audit --include-diagnostic. - Add
parseAiToolOrThrowinsrc/cli/ai_tool_parser.ts— central validator for--toolflags, so bogus values fail with a usage error listing valid names. - Docs:
design/audit.md,design/audit-doctor.md, and a short "verify the audit integration" block in theswamp-reposkill.
Follow-ups
- UAT coverage tracked in systeminit/swamp-uat#160 (CLI) and systeminit/swamp-uat#161 (adversarial).
- swamp-club has GitHub issues disabled —
content/manual/reference/doctor.mdand a "verify the integration" section for eachcontent/manual/how-to/use-swamp-with-*.mdneed to be routed through the team's docs process. - POSIX-only PATH resolution — documented; Windows support deferred.
Test Plan
-
deno check— clean -
deno lint— clean -
deno fmt --check— clean -
deno run test— 4922 passed, 0 failed, 1 ignored -
deno run compile— produces working binary - Manual smoke-run on freshly-init'd kiro / claude / cursor / opencode repos — all pass
- Regression injection (remove
"shell"fromKIRO_SHELL_TOOL_NAMES, recompile, rerun) — smoke-test correctly FAILS on kiro - Real-repo test against
~/code/kiro-audit(existing audit history) — passes; sentinel filter confirmed withswamp audit --include-diagnostic - CWD-independence adversarial test — ran from CWD=
/with--repo-dir /tmp/..., synthetic row landed in the target repo, not the CWD
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.234336.0-sha.0022d1ca/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/v20260424.234336.0-sha.0022d1ca/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/v20260424.234336.0-sha.0022d1ca/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/v20260424.234336.0-sha.0022d1ca/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.230602.0-sha.46b67446
What's Changed
- docs(skills): document markDirty in swamp-extension-datastore (#1226)
Summary
Pick up the `markDirty()` addition from #1225 in the skill that guides
authors building custom datastore providers.
- `references/api.md` — interface block includes `markDirty(): Promise`,
plus a section describing when it's load-bearing (fast-path implementations
that cache a clean/dirty watermark) versus when a no-op is correct (sync
services that unconditionally walk the cache). - `references/examples.md` — the working sync service example returns a
no-op `markDirty` with a comment pointing at the fast-path pattern in
`design/datastores.md`.
No code changes, docs only — keeps guidance for extension authors in
lock-step with the core interface change that already shipped.
Test Plan
- `deno fmt` clean per the skill-files rule in CLAUDE.md.
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.230602.0-sha.46b67446/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/v20260424.230602.0-sha.46b67446/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/v20260424.230602.0-sha.46b67446/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/v20260424.230602.0-sha.46b67446/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.224539.0-sha.ce117565
What's Changed
- fix(datastore): signal cache writes to sync service fast path (#1225)
Summary
The s3/gcs zero-diff fast path in DatastoreSyncService maintains a
local-dirty watermark that it flips during its own pushFile-equivalent.
Swamp core writes into the cache directly from the persistence
repositories (UnifiedDataRepository, YamlOutputRepository,
YamlWorkflowRunRepository, YamlEvaluatedDefinitionRepository), which
bypasses that hook — the next pushChanged short-circuits past the
write and the data lives only in the local cache until eviction. Single
writer setups against a low-traffic bucket are most exposed; high
traffic masks the bug because unrelated remote mutations invalidate the
sidecar.
Fix
- Add a required
markDirty()method toDatastoreSyncService(mirrored
inpackages/testing/datastore_types.ts). - Every public mutation on the four datastore-tier repositories calls
notifyDirty()before any cache write begins, so a crash mid-write
still leaves the watermark dirty — per-method (not per-atomicWrite)
keeps the repositories clean and the hook is idempotent anyway. - Single sync service instance threads through
requireInitializedRepoUnlocked→acquireModelLocksand through the
serve context (OpenServerState,ConnectionContext,
WebhookServiceDeps), so an implementation that caches the sidecar
flag in memory stays coherent across themarkDirtyhook and the
pull/push fast-path read. The `acquireModelLocks` param is optional
for backward compatibility; every caller in the repo now passes one. - Document the contract under the Zero-Diff Fast Path section in
`design/datastores.md`. - Filesystem datastores wire no service; `notifyDirty` is a no-op for
them.
Extension follow-up
The required interface change surfaces as a compile-time signal to
extension authors (`@swamp/s3-datastore`, `@swamp/gcs-datastore`,
swamp-club/166) that they need to implement `markDirty()` — trivial:
it's the same primitive as their internal `pushFile` hook's dirty-flip.
Test Plan
- New repository contract tests pin `markDirty` invocation on every
public mutation, including the negative cases (reads, dry-run GC,
no-op delete) where it must not fire. - Existing `datastore_sync_service_test.ts` still passes; 12 stub
sync services across tests updated to include the new field. - `testing_package_compat_test.ts` structurally verifies the
`@systeminit/swamp-testing` mirror still matches core. - `deno check`, `deno lint`, `deno fmt --check`,
`deno run test` (4854 passed, 0 failed, 1 ignored),
`deno run compile` all clean.
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.224539.0-sha.ce117565/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/v20260424.224539.0-sha.ce117565/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/v20260424.224539.0-sha.ce117565/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/v20260424.224539.0-sha.ce117565/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.194520.0-sha.bd275a89
What's Changed
- fix(extensions): complete legacy layout migration on repo upgrade (swamp-club#160) (#1224)
Summary
Closes swamp-club#160.
swamp repo upgrade was silently deleting user working-tree files when it detected extensions tracked at a legacy on-disk layout. The two-phase migration added in #1186 renamed gen-1 paths to gen-2, then deleted the gen-2 files expecting the user to run swamp extension install afterward to restore them. No warning, no prompt, no automatic follow-up.
The fix collapses the split between the two primitives that owned "make extension state correct":
extensionInstallnow treats legacy-layout entries as needing re-pull (not just missing files), installs them into the per-extension subtree, and sweeps the original legacy paths only after a successful install.repo upgradeinvokesextensionInstalldirectly as its final step.migrateExtensionLayoutand its two phase methods are deleted (~280 LOC) along withExtensionLayoutMigrationSummary,pruneEmptyDirsUp, and theextensionMigrationresult field.
Net LOC: −170 despite adding the install-pass wiring, a test seam, a shared factory, and several new tests.
User-visible behavior
swamp repo upgradenow completes the migration end-to-end. No manualswamp extension installfollow-up required for the common case.- On install failure (network down, extension yanked, auth missing), legacy files are preserved and the error names the failed extensions with a recovery command.
swamp extension installalso migrates legacy-layout repos. Previously a silent no-op on legacy state — positive side effect of unifying the two primitives.
Latent bug caught during verification
Running an end-to-end manual test against @swamp/aws/s3 surfaced a second bug: empty _lib/ directories were being stranded after the sweep. Root cause: resolveRepoDir returns "." by default, which propagated through join(".", "path") → "path" (relative), and cleanupEmptyParentDirs's startsWith(stopAtDir) check silently failed against relative paths. The factory now absolutizes repoDir up front so downstream filesystem helpers always see absolute paths. Added a regression test asserting nested parent dirs are pruned.
This one wouldn't have been caught by unit tests alone — my initial unit tests used absolute tmpDir from Deno.makeTempDir, which masked the relative-path issue. Running against a real extension with a nested _lib/ found it immediately.
Test plan
-
deno check— clean -
deno lint— clean (1081 files) -
deno fmt— clean (1199 files) -
deno run test— 4728 pass, 0 fail, 1 ignored (added 8 new tests) -
deno run compile— binary produced - Manual E2E happy path:
@swamp/aws/s3seeded as gen-1 legacy,swamp repo upgraderun; all 11 model files migrated to.swamp/pulled-extensions/@swamp/aws/s3/models/…,filesChecksumanchor matches original pull byte-for-byte (a9378bf483…), legacy paths (including nested_lib/) fully swept. - Manual E2E failure path: lockfile referencing
@me/launchd-daemon(not in registry);swamp repo upgradeexits non-zero with clear error naming the extension + recovery command; all 4 legacy files preserved on disk. - Manual E2E via
extension install: same legacy seed;swamp extension installalso migrates successfully (positive side effect verified).
Notes for reviewers
- The
swamp extension installbehavior change (no-op → auto-migrate on legacy repos) is intentional; documented in theswamp-reposkill. - There is no
CHANGELOG.mdin this repo, so the user-visible release notes live in this PR description. - I skipped the integration test against a real/fake registry as out of scope for this PR; unit coverage + manual verification cover the critical paths. A follow-up would be reasonable.
- Private/auth-gated extensions are not directly verified end-to-end — the factory uses the same
ExtensionApiClientasswamp extension install, so coupling should hold, but worth spot-checking if a reviewer has a private extension in a test lockfile.
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.194520.0-sha.bd275a89/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/v20260424.194520.0-sha.bd275a89/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/v20260424.194520.0-sha.bd275a89/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/v20260424.194520.0-sha.bd275a89/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.185624.0-sha.633e3ae7
What's Changed
- fix(datastore): clarify --timeout scope in SyncTimeoutError message (#1223)
Summary
Follow-up to #1222 addressing the two actionable findings from review:
1. CLI UX Review — --timeout remedy was misleading for implicit syncs
The timeout fires from flushDatastoreSync() after every command, not just swamp datastore sync. The previous message led with • Rerun with --timeout <seconds>, which would fail for a user whose timeout fired from e.g. swamp model run (the flag only exists on swamp datastore sync).
Fix: reorder remedies so the env var (universal escape hatch) is listed first and the --timeout flag is explicitly scoped to swamp datastore sync:
```
Datastore push to "@swamp/s3-datastore" timed out after 300000ms. Try one of:
• Set SWAMP_DATASTORE_SYNC_TIMEOUT_MS for the duration of your shell session — applies to every command that triggers sync, including the implicit pull/push around write commands.
• If the timeout fired on an explicit 'swamp datastore sync', rerun with --timeout (e.g. --timeout 1800 for large one-off syncs). The flag is not available on other commands; use the env var above for those.
• If you are on @swamp/s3-datastore or @swamp/gcs-datastore at scale, update to the latest extension — the fingerprint fast path short-circuits zero-diff syncs.
• If a prior process crashed without releasing the lock, run 'swamp datastore lock release --force' (add --model / for a specific model lock).
```
New scope-clarification test pins the contract: the message MUST name swamp datastore sync explicitly and MUST mention the env var covers implicit syncs. That prevents a future well-meaning reword from reintroducing the same trap.
2. Code Review + Adversarial Review — CreateDatastoreSyncDepsOptions not re-exported from barrel
The new interface lived at the internal path src/libswamp/datastores/sync.ts. Any consumer outside the CLI wanting to type an options variable would have had to violate CLAUDE.md's barrel-import convention. One-line fix: add type CreateDatastoreSyncDepsOptions to the export from src/libswamp/mod.ts.
What this does NOT change
- The precedence chain (CLI flag → config → env var → default) is unchanged.
- The structured fields of
SyncTimeoutError(label,direction,timeoutMs,cause) are unchanged. - No behavior change for users who don't hit the timeout.
Test plan
-
deno check— clean -
deno lint— clean (1094 files) -
deno fmt— clean -
deno run test— 4843 passed / 0 failed -
deno run compile— binary built - New test
SyncTimeoutError: --timeout remedy is scoped to 'swamp datastore sync'passes
Links
- Primary PR: #1222
- Lab issue: https://swamp.club/lab/164
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.185624.0-sha.633e3ae7/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/v20260424.185624.0-sha.633e3ae7/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/v20260424.185624.0-sha.633e3ae7/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/v20260424.185624.0-sha.633e3ae7/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.184437.0-sha.44543d36
What's Changed
- feat(datastore): add --timeout flag and enrich SyncTimeoutError (swamp-club#164) (#1222)
Summary
Companion PR to the @swamp/s3-datastore fingerprint fast-path fix for lab/164 (minutes-slow swamp datastore sync at 4k-file scale, even on zero-diff). This PR adds the swamp-core ergonomics: a --timeout CLI flag for one-off sync overrides, and a richer SyncTimeoutError message so users get actionable next steps when the timeout does fire.
Either PR may land first — the enriched error uses version-free wording, so there is no ordering dependency.
What changed
--timeout <seconds> flag on swamp datastore sync
- Per-invocation override; wins over
CustomDatastoreConfig.syncTimeoutMsandSWAMP_DATASTORE_SYNC_TIMEOUT_MS. - Validated at the CLI boundary: positive integer, rejected ≤0, capped at 21600s (6h) — generous enough for initial seeding from a slow link, bulk recovery after an outage, or 100k-file migrations, while rejecting fat-finger misconfigs. The env var stays uncapped as the shell-session escape hatch.
- Scoped to
swamp datastore synconly. Write commands that trigger implicit sync keep their existing escape hatch (env var). Adding--timeouteverywhere would inflate the CLI surface without commensurate benefit. --helpclearly describes precedence so users reach for the right knob.
Enriched SyncTimeoutError message
Before:
```
Datastore push to "@swamp/s3-datastore" timed out after 300000ms. If a prior process crashed without releasing the lock, run 'swamp datastore lock release --force' (add --model / for a specific model lock).
```
After:
```
Datastore push to "@swamp/s3-datastore" timed out after 300000ms. Try one of:
• Rerun with --timeout (e.g. --timeout 1800 for large one-off syncs).
• Set SWAMP_DATASTORE_SYNC_TIMEOUT_MS for the duration of your shell session.
• If you are on @swamp/s3-datastore or @swamp/gcs-datastore at scale, update to the latest extension — the fingerprint fast path short-circuits zero-diff syncs.
• If a prior process crashed without releasing the lock, run 'swamp datastore lock release --force' (add --model / for a specific model lock).
```
Version-free wording keeps the message stable across extension releases. No changes to the error's structured fields (label, direction, timeoutMs, cause).
Internal plumbing
resolveSyncTimeoutMs(config, overrideMs?)— new optional parameter at highest precedence. Non-positive overrides silently fall through to the next source.createDatastoreSyncDeps(repoDir, resolver, options?)— newCreateDatastoreSyncDepsOptionsinterface withsyncTimeoutMsOverride.
Docs
design/datastores.md — expanded "Sync Timeout" section with the 4-tier precedence chain and reference to the enriched error. New "Zero-Diff Fast Path (Extension Guidance)" subsection with backend-agnostic wording (fingerprint + dirty-flag sidecar pattern), so future extension authors have a breadcrumb. Notes that sync is not a content-integrity tool — use DatastoreVerifier for that.
What this does NOT do
- Does not fix the 5-minute zero-diff sync itself — that's the
@swamp/s3-datastorefingerprint fast-path PR. This PR is the ergonomics layer. - Does not change default behavior for anyone who doesn't pass
--timeoutor hit the timeout. - Does not close the AbortSignal plumbing gap in the s3 extension.
Test plan
-
deno check— clean -
deno lint— clean (1091 files) -
deno fmt— clean -
deno run test— 4822 passed / 0 failed (19 new tests) -
deno run compile— binary built -
./swamp datastore sync --help—--timeoutflag documented correctly - Reporter runs
swamp datastore sync --timeout 60against their 4k-file repo and confirms: (a) validation rejects 0/negative/>21600, (b) enriched error message renders all four remedies, (c) flag override wins over env var when both are set. Tracked on lab/164 as the verification gate.
New tests
| File | What it covers |
|---|---|
src/domain/datastore/datastore_sync_service_test.ts |
Enriched message contents; version-free extension hint (regex guards against calendar-version and semver); structured field preservation; multi-line JSON round-trip. |
src/domain/datastore/datastore_config_test.ts |
resolveSyncTimeoutMs precedence: override wins over config/env/default; non-positive override falls through; undefined preserves existing precedence. |
src/cli/commands/datastore_sync_test.ts |
parseTimeoutFlag validation: seconds→ms conversion, upper-bound acceptance, zero/negative/over-cap/non-integer/non-number rejection. |
Links
- Lab issue: https://swamp.club/lab/164
- Companion PR:
@swamp/s3-datastorefingerprint fast-path (insysteminit/swamp-extensions, being opened in parallel) - Follow-up issue: https://swamp.club/lab/166 (gcs mirror)
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.184437.0-sha.44543d36/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/v20260424.184437.0-sha.44543d36/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/v20260424.184437.0-sha.44543d36/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/v20260424.184437.0-sha.44543d36/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.182054.0-sha.e40c597d
What's Changed
- feat(drivers): finish repo-level defaultDriver — direct path + definition tier (swamp-club#165) (#1221)
Summary
Closes swamp-club#165. Completes the cleanup gaps left by #1219 so the repo-level defaultDriver priority chain now applies uniformly to both workflow and direct swamp model method run paths. Also fixes a concurrent-read race on the marker load and un-breaks the silently-dropped definition.driver tier.
Three deliverables, one commit:
- Direct
swamp model method runnow honorsdefaultDriver.src/libswamp/models/run.tsroutes throughresolveDriverConfigwith cli/definition/repo tiers. Serve/WS (src/serve/connection.ts) and HTTP SSE (src/serve/open/http.ts) paths inherit for free. A newloadRepoMarkerdep onModelMethodRunDepsis built via the extractedcreateRepoMarkerLoaderhelper. - Promise-memoize the marker read. Previously
WorkflowExecutionService.loadRepoMarkercached the resolved value — two concurrent callers both sawundefinedand both hit the file. Nowthis.markerPromise ??= markerRepo.read(...);.swamp.yamlis read at most once per service instance. The primitive is extracted tosrc/infrastructure/persistence/repo_marker_loader.tsand reused by all four factories (workflow service, CLI, serve/WS, HTTP SSE). - Make the
definitiontier live.src/domain/workflows/execution_service.ts:523used to readctx.driver ?? evaluatedDefinition.driver— dead code, becausectx.driverwas always populated by the old upstreamresolveDriverConfigcall (with"raw"as its fallback), so the RHS never fired anddefinition.driverwas silently dropped.StepExecutionContextnow carries unresolveddriverTiers: { cli, step, job, workflow, repo }and final resolution happens insideDefaultStepExecutor.executeModelMethodwhereevaluatedDefinitionis in scope.
Behavior changes
swamp model method run <m> <meth>in a repo withdefaultDriver: dockerin.swamp.yamlnow runs via docker without--driver.--driver rawoverride still wins.- Model definitions with
driver:set at the definition level are now honored during workflow execution. Internal blast radius: zero — grep ofextensions/**/manifest.yamlandextensions/**/*.yamlreturned no hits. Third-party extensions that setdriver:at the definition level will now apply it (this was the documented design from day one but was never wired). - Malformed
.swamp.yamlnow aborts every direct method run with a YAML parse error (previously direct runs didn't read the marker at all). Consistent with workflow behavior and documented indesign/execution-drivers.md.
Tests added
src/infrastructure/persistence/repo_marker_loader_test.ts— concurrency primitive: two simultaneous calls through aPromise.withResolverslatch, assert exactly one underlying.read()call. Covers null-result caching too.src/domain/models/method_execution_service_test.ts— pre-flight check receives the resolveddriver/driverConfigfrom MethodContext (propagation audit).src/libswamp/models/run_test.ts— four direct-path resolution tests: defaultDriver from marker wins; CLI override wins; raw fallback; definition tier applies when no higher tier set.src/domain/workflows/execution_service_test.ts— existing three workflow-path resolution tests updated to assert on the newdriverTiersshape.integration/driver_resolution_test.ts— end-to-end CLI smoke (no docker required): defaultDriver honored, CLI override wins, malformed YAML aborts loudly.
Docs
design/execution-drivers.md— added a short note under "Resolution Priority" stating that the priority chain applies uniformly to workflow and direct paths and that malformed.swamp.yamlaborts at start-of-run.- Follow-up docs gap (not in this PR): the swamp-club manual reference at
content/manual/reference/repository-configuration.mdstill does not documentdefaultDriver/defaultDriverConfig— pre-existing gap from #1219.systeminit/swamp-clubhas GitHub issues disabled; flagging here so the gap can be routed to the swamp-club lab.
Out of scope (per issue body, separate follow-ups)
- Named-fields refactor of
resolveDriverConfig(positionalDriverSource | undefinedparams). WorkflowExecutionService.execute()option forwarding (currently dropsdriver).- Runtime schema validation of
RepoMarkerData(theparseYaml(...) as RepoMarkerDatacast). - UAT coverage for
--driver/defaultDriverinsysteminit/swamp-uat.
Test plan
-
deno check— passes -
deno lint— passes -
deno fmt --check— passes -
deno run test— 4823/4823 pass -
deno run compile— binary produced - Local smoke: scratch repo with
defaultDriver: raw→ honored;--driver rawbeatsdefaultDriver: docker; malformed YAML aborts with exit code 1.
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.182054.0-sha.e40c597d/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/v20260424.182054.0-sha.e40c597d/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/v20260424.182054.0-sha.e40c597d/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/v20260424.182054.0-sha.e40c597d/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.180844.0-sha.03130a3d
What's Changed
- docs(skills): document bare-specifier vs scorer hermeticity (swamp-club#161) (#1220)
Summary
Closes swamp-club#161. Following #1218 shipping swamp extension quality, authors now discover locally that the scorer strips the tarball's deno.json and writes a controlled one with no imports map — so bare "zod" imports fail with deno doc --json failed: Import "zod" not a dependency before factor scoring even begins. This PR teaches that pattern to authors through the two skills that previously led them into the trap.
Three files, +50/-7 lines:
swamp-extension-quality/references/templates.md— the JSDoc entrypoint skeleton was showing bare"zod"; switched to"npm:zod@4"with a rationale bullet explaining hermeticity. An author who copies the template literally now produces an extension that scores.swamp-extension-publish/references/publishing.md— the Import Rules had two contradictory bullets ("npm:zod@4"is "always required" vs. bare specifiers "can be used" withdeno.json). Rewritten into one coherent paragraph that carries both load-bearing signals: hermeticity at score time AND zod-specific externalization (why zod in particular is called out, not merely a consequence of hermeticity).swamp-extension-quality/SKILL.md— added a gotcha block under "Essential mechanics authors get wrong" that names the failure mode explicitly, calls out the Denono-import-prefixdefault that activates when adeno.jsonwithimportsis present (which is every swamp repo's default state, creating a catch-22), and gives concrete mitigations: either global"lint": { "rules": { "exclude": ["no-import-prefix"] } }indeno.json, or per-import// deno-lint-ignore no-import-prefix. Also added a one-sentence cross-link from the new "Self-check your score locally" section and a symptom→fix row to "Common patterns" so a reader landing anywhere in the skill finds the fix.
Test Plan
Three-layer verification:
-
deno fmt --checkclean on all three files -
deno checkclean -
deno lintclean (1088 files) -
deno run test— 4803 passed, 0 failed - Executable round-trip in
/tmp/swamp-issue-161-verify:- Bare
"zod"import →swamp extension qualitythrows the exact error documented in the gotcha (deno doc --json failed: Warning Import "zod" not a dependency) - Switch to
"npm:zod@4"→ scorer runs and reports 11/12 (91%) factor results - The test also surfaced the Deno
no-import-prefixdefault rule firing as predicted, and the documented mitigation (lint.rules.exclude) unblocked the round-trip — confirming both halves of the gotcha match the tool's actual behaviour.
- Bare
Follow-up (not in this PR)
The deadlock between the scorer (strips deno.json) and Deno's default lint rules (forbid npm: prefix when an imports map is present) is ultimately a swamp-club server-side concern. A future follow-up issue could make the analyzer honour the tarball's files/deno.json with appropriate hardening — the README/LICENSE promotion logic already does similar. Until then, the gotcha documents the author-side workaround.
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.180844.0-sha.03130a3d/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/v20260424.180844.0-sha.03130a3d/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/v20260424.180844.0-sha.03130a3d/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/v20260424.180844.0-sha.03130a3d/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.171017.0-sha.76b653b9
What's Changed
- feat(drivers): repo-level defaultDriver in .swamp.yaml (swamp-club#159) (#1219)
Summary
Closes swamp-club#159. Adds a repo-level defaultDriver (and defaultDriverConfig) to .swamp.yaml so a repo can declare a baseline execution driver once, without touching every workflow or passing --driver on every CLI invocation.
New resolution chain for workflow steps:
cli > step > job > workflow > definition > repo > "raw"
RepoMarkerDatagains two optional fields (defaultDriver,defaultDriverConfig) — same shape as the existingdatastorefield.resolveDriverConfiggains an optional 6threpotier, positioned belowdefinitionand above the"raw"fallback.WorkflowExecutionServiceloads.swamp.yamlonce perrun()(memoized on the instance so nested workflow calls don't re-read) and passes the repo tier intoresolveDriverConfigat the step-context build site.design/execution-drivers.mdupdated to document the new tier with an example.
Scope: workflow execution only. Direct swamp model method run invocations go through a separate code path that doesn't call resolveDriverConfig today and will still default to "raw" when no CLI override is supplied. Flagged in the adversarial review as ADV-1; can be a follow-up if desired.
Test Plan
-
deno check— passes -
deno lint— passes -
deno fmt --check— passes -
deno run test— 4732 passed / 0 failed / 1 ignored -
deno run compile— produces a working binary
New tests added:
- 5 cases in
driver_resolution_test.tscovering repo-tier precedence (wins over default, loses to workflow/job/step/cli, skipped whendriverundefined butdriverConfigset) - 1 roundtrip in
repo_marker_repository_test.tswithdefaultDriver+defaultDriverConfig - 3 service-level tests in
execution_service_test.tswith a real.swamp.yamlon disk: default applied, CLI overrides, no default → raw
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.171017.0-sha.76b653b9/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/v20260424.171017.0-sha.76b653b9/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/v20260424.171017.0-sha.76b653b9/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/v20260424.171017.0-sha.76b653b9/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260424.164547.0-sha.31006b67
What's Changed
- feat(extensions): add quality command and opportunistic package cache (swamp-club#163) (#1218)
Summary
Implements swamp-club#163 — a new swamp extension quality <manifest> CLI that scores an extension against the Swamp Club rubric locally, mirroring the server's scorecard pipeline so local results match the score the registry will compute after publish.
- New CLI command:
swamp extension quality manifest.yaml [--json]. Exits non-zero if any factor misses; suitable for CI self-checks. - Server-mirrored scorer:
extension_rubric_scorer.tsmirrorsanalysis-factors.tsand the rubric-v2 composition fromscore.ts— same slow-type code set, same JSDoc coverage shape, same README/LICENSE lookup in the extracted tarball, same 12-point weighting. - Parity tests: 24 tests marked
[parity]port the upstream swamp-club test suite so logic drift is caught before release. - Opportunistic package cache at
.swamp/cache/packages/<hash>/: keyed on source inputs (manifest + resolved file contents + deno config).extension qualitywrites the packaged tarball;extension pushchecks the cache before packaging and reuses the bytes on a hit. Any source change invalidates the entry by construction — authors who run quality before push don't pay twice, and the bytes scored equal the bytes uploaded.
Opt-in by design
quality is uniformly opt-in: push's behaviour is unchanged, no new flags, no mandatory gate. The swamp-extension-publish and swamp-extension-quality skills gain brief pointers at the new CLI between states 6 and 7 of the publish flow.
Known caveat
repository-verified is a structural check on the CLI side (HTTPS + allowlisted host). The server additionally performs an HTTP HEAD to confirm the repo is public — so a local "earned" can come back "missing" from the registry if the repo URL is malformed or private. The log renderer surfaces this caveat. Follow-up issue: narrow the gap (HTTP HEAD from the CLI, or a shared scoring package between this repo and swamp-club).
Smoke test
Ran against systeminit/swamp-extensions/datastore/s3 — reports 12/12 points, 100%, all factors earned, matching the registry.
Test Plan
- 10 unit tests for
extension_package_cache.ts(hit/miss, determinism, cache invalidation) - 39 unit tests for
extension_rubric_scorer.tscovering every factor and edge case - 24
[parity]tests ported from swamp-club'sanalysis-factors_test.tsandscorecard_test.ts - 6 integration tests in
integration/extension_quality_test.tscovering the full CLI pipeline (package → cache → extract → deno doc → compose), including cache hit/miss, source mutation invalidating cache, log and JSON modes - Smoke test against real
@si/s3-datastoreextension — 12/12 (100%), matches registry -
deno checkclean -
deno lintclean -
deno fmt --checkclean -
deno run test(4,802 passed, 1 pre-existing flaky unrelated to this change, 1 ignored) -
deno run compilesucceeds
Follow-ups (not in this PR)
- File issue in
systeminit/swamp-uatfor CLI UAT coverage (tests/cli/extension/quality/) - File issue in
systeminit/swamp-clubto document the new command incontent/manual/how-to - Consider a shared scoring package between
systeminit/swampandsysteminit/swamp-clubto eliminate the current ~100 lines of mirrored logic
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260424.164547.0-sha.31006b67/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/v20260424.164547.0-sha.31006b67/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/v20260424.164547.0-sha.31006b67/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/v20260424.164547.0-sha.31006b67/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/