Skip to main content
← Back to list
01Issue
BugOpenSwamp CLI
AssigneesNone

#187 swamp CLI cold-start latency (~5–7 s per invocation) compounds in workflows that shell out

Opened by john · 4/29/2026

Summary

Each invocation of swamp from a cold shell takes ~5–7 seconds before the actual command runs. Most of this is binary startup (Deno-compiled executable) + extension/repo discovery. Comparable CLIs (kubectl, helm) cold-start in under 100 ms.

Why it matters

Compounds in two common scenarios:

1. Agent-driven workflows. An LLM agent (claude, etc.) issuing 50–100 swamp commands per task burns 4–10 minutes purely on CLI cold-starts. We've measured this in benchmark runs at https://github.com/systeminit/swamp-benchmark — 30–35% of wall-clock time on the swamp variant is CLI startup, not actual work. The freeform/kubectl variant is 6× faster largely for this reason.

2. Workflows that shell out. Hit this directly today building the bootstrap step for @john/k8s's debug workflow. The bootstrap shells swamp model create 8 times to set up per-namespace instances. Sequentially:

for kind in pod service deployment event configmap pvc secret netpol; do
  swamp model create "@john/$kind" "${ns}-${kind}" --global-arg "namespace=${ns}" --json
done

This took >60 seconds (8 × ~7s startup + actual work) and hit the workflow step's 60 000 ms default timeout. Workaround was to parallelise (& done; wait) which fits in the timeout but only because we have 8 cores — won't scale.

Reproduction

# Cold cache
sync && sudo purge   # macOS: drop FS cache
time swamp --version
# Typical: 4.5–6.5s real time (mostly CPU)

# Subsequent invocations are still ~3–4s because the binary doesn't
# share state across processes (no daemon).
time swamp --version
time swamp --version

vs kubectl version --client: ~50 ms cold, ~30 ms warm.

Where the time goes (from a quick instrumented run)

  • ~1.5s: Deno runtime spin-up (compiled binary unpack/init)
  • ~1.0s: extension source discovery (walking .swamp/, .swamp-sources.yaml, pulled-extensions/)
  • ~1.0s: model registry construction (loading extensions/models/**/*.ts bundles)
  • ~1.0–2.0s: command parser/cliffy init + telemetry context

Suggested mitigations (independent of each other)

  1. Persistent daemonswamp serve already exists for the workflow API. Extend it (or a new lightweight swamp daemon) to serve a Unix socket; CLI invocations proxy via the socket and complete in <100 ms when the daemon is up. Falls back to cold-start when no daemon. Comparable to how git doesn't have one and gh doesn't either, but nix does (and it's how they fixed their startup time).

  2. Lazy registry loading — defer extension/model bundle parsing until a command actually needs it. swamp --version should not need to walk extensions/models/; today it does.

  3. Pre-compiled bundle cache — model TypeScript files get re-bundled per invocation. If the source hashes match the cache, skip re-bundle. Cache lives in .swamp/cache/.

  4. Drop telemetry init from the hot path — the post-success telemetry flush happens after the command completes, but the context build (telemetryCtx in src/cli/mod.ts) happens early. Could be deferred to the catch/finally.

Empirical context

Benchmarked at https://github.com/systeminit/swamp-benchmark (k8s-debug-v2 challenge). Repro of the bootstrap timeout case: try running @john/k8s's @john/debug-namespace-deep workflow on a fresh repo with the bootstrap job enabled. v2026.04.29.1 hit the timeout sequentially; v2026.04.29.2 works around with parallelism but it's a band-aid.

I think (1) is the highest-leverage — it'd benefit every CLI invocation, not just the workflow-shells-out case.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

4/29/2026, 11:41:27 AM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.