Swarm

Open questions

Status: living
  • OQ-1 Name / npm package.RESOLVED (2026-08-20): the project is swarm. A swarm of agents is the product's mental model and fits the dashboard's Fleet view. Published scoped as @ra3orblade/swarm (the bare swarm npm name is taken by Docker/Ethereum); CLI command swarm, daemon swarmd, hook swarm-hook, MCP swarm-mcp. Bare npx swarm would need the unscoped name — revisit only if we want it.
  • OQ-2 Hook install scope default. User-level (every session everywhere, matches "pick any folder") vs. project-level opt-in (less surprising for people trying it). Leaning user-level with swarm install --project as the alternative.
  • OQ-3 Fail-open vs fail-closed when the daemon is down. Proposal: fail open except for rules tagged critical; critical rules are evaluated locally in the shim from a cached rules file so they work without the daemon.

    Decision (2026-08-23, M8.1c): fail open by default; fail closed only for rules the org policy locks. The daemon keeps ~/.swarm/policy.cache.json (locked modes + live-session/held-worktree snapshot, sha256 integrity hash) while any rule is locked; the hook shim evaluates it with the same guardBash/guardWrite when the daemon is unreachable. Unlocked rules still fail open. Cryptographic signing of the cache waits for the team daemon's key distribution (M8.3).

  • OQ-4 Worktree location. .worktrees/ inside the repo (needs gitignore + test-runner exclusions) vs ~/.swarm/worktrees/<project>/<task> (zero footprint, matches repo-agnostic). Leaning outside the repo by default, .swarm.toml can override.

    Decision (2026-08-20): default ~/.swarm/worktrees/<project>/<task>; .swarm.toml worktrees = overrides. Zero footprint in the monitored repo and no vitest/gitignore exclusions needed.

  • OQ-5 Task source for M1.6. Markdown tables only, or also a tasks.json? Keep markdown-table only until a second format is actually needed.

    Decision (2026-08-22): markdown tables only. [tasks] source in .swarm.toml names one markdown file; every ID | Task | … | Status table in it is parsed (✅ done / 🟡 active / ⚪ todo, Depends may name a task or a milestone prefix). A second format waits for a real request.

  • OQ-6 Web stack. Vite+React+shadcn (familiar, batteries-included) vs plain HTML/htmx over SSE (no build, smaller). Dashboard will get interactive (stdin, run control); React.
  • OQ-7 Schema drift. How to pin tested Claude Code versions and detect new hook/stream event types — doctor warning plus raw passthrough is the proposal.
  • OQ-8 Non-Claude agents. Out of scope for v1; keep the adapter seam but do not design for it.
  • OQ-9 Memory search. ✅ RESOLVED (2026-08-22): SQLite FTS5 (BM25) first, no embedding model. bun:sqlite ships FTS5, so memory search needs no native extension, no model download and no network — it works on a fresh install, offline, and stays in the one swarm.db. Indexed: handoffs, incidents (the stopped command + reason), gate runs (rubric + evidence) and what each session last said; never the monitored codebase. Queries are sanitised into MATCH expressions that cannot error (core/memory.ts). sqlite-vec + local embeddings stay on the table as a second ranker if keyword recall proves insufficient on real use — revisit with evidence, not upfront. Original note: Vector search over Swarm's own data only (handoffs, incidents, gate evidence, notes, denied commands), local embeddings via sqlite-vec, never over the monitored codebase (Claude Code greps better than RAG). Scheduled under M4; decide embedding model then.

Decision: FTS5/BM25 over Swarm's own data; revisit vectors only with evidence of missed recall.

  • OQ-10 First non-Claude agent. ✅ RESOLVED (2026-08-20): Codex CLI first (real ~/.codex rollout logs, closest shape to Claude Code), then Gemini CLI, Aider, opencode/Cline, and Grok — all behind the same AgentAdapter interface. Original note: Which agent gets the first adapter (M5.3)? Candidates with a machine-readable session/telemetry surface a local daemon can read: OpenAI Codex CLI (JSONL session/rollout logs), Gemini CLI, Aider (.aider.*.history), opencode, Cline. GUI tools (Cursor) are harder (no hooks; would need their local DB/logs). Decides M5.3–M5.5.
  • OQ-11 Desktop packaging & signing. (Partly resolved 2026-08-21: reuse the existing Apple Developer account creds — same cert + notarization as the sibling Electron project — mapped to Tauri var names in docs/11-desktop-signing.md; the Tauri updater keypair is new. Remaining: set the CI secrets and cut a real signed release; Windows signing later.) Tauri (M6): tray+window vs window-only; sidecar daemon (bun build --compile) vs embedded runtime; and signing — macOS notarization needs an Apple Developer account, Windows needs a code-signing cert. Update channel: single stable vs beta/stable. These gate M6.3–M6.4.
  • OQ-12 Message delivery channel (M7.6). How does a message reach a running Claude Code session? Options: (a) additionalContext on the next hook response — zero new surface, but only arrives when the agent next calls a tool and PreToolUse context is advisory; (b) the agent polls swarm_inbox — reliable but only if the agent is told to poll (SessionStart context can say so); (c) stdin for spawned runs only (M3.1 already streams user messages). Leaning (a)+(b) together with (c) for spawned runs; interactive sessions cannot be interrupted, only informed. Decides M7.6–M7.7.
  • OQ-13 Where do workflow steps and executed gates run (M7.4, M7.8)? Executed gates could run inside the daemon (simple, but the daemon is the only DB writer and should not block on a 10-minute test suite) or as a detached process in the registry (M1.4 proc, pid + start-time liveness, logs in ~/.swarm/logs) with the result posted back. Leaning the process registry: gates and workflow steps are just registered processes with an on_exit that records the gate run / advances the workflow; the daemon never blocks. Also: is a workflow's state a new table or derived from gate runs + runs per task? Leaning a small workflow_runs table (task, name, step index, status) — derivation gets ambiguous on retries.

    Decision (2026-08-23): executed gates (and later dispatch/workflow steps) run as detached processes in the registry (kind: gate, pid + start-time liveness, log in ~/.swarm/logs/<project>/gate-<task>-<gate>.log); the daemon records the gate run when the process exits and never blocks. Workflow state (0.8) gets its own workflow_runs table.

  • OQ-14 Dispatch safety (M7.5). What stops a dispatched run from flipping a task ✅ on its own? Proposal: the run never edits the task source; "done" is derived (required gates pass + PR opened, M7.3) and shown on the Board, and only a human (or a workflow's final built-in step) writes status back. Also the concurrency cap: per project only, or global (machine-wide token spend)? Leaning per project with a global default in ~/.swarm/config.toml.

    Decision (2026-08-23): a dispatched run never edits the task source. "Done" on the Board is derived — required gates pass (executed or recorded) and a PR is open — and only a human flips status. Concurrency cap per project: .swarm.toml [dispatch] max_parallel (default 2), global default in ~/.swarm/config.toml.

  • OQ-15 Commercial model / open-core boundary (M8). Swarm can become open-core without betraying local-first: everything that runs on one machine stays free and Apache-2.0 (daemon, dashboard, claims/worktrees/gates, rules, spawned runs, transcripts/cost, all agent adapters, .swarm.toml policies); everything that needs a second person is the paid tier (team daemon, SSO/OIDC, RBAC, org-level non-overridable policy, audit export / SIEM sinks, chargeback, fleet install, support). Constraints that must hold: (a) never relicense the existing packages — the team daemon lives in a separate package/repo under a source-available or closed license; (b) the free tier keeps "no telemetry, no account" — the account lives in the team daemon only; (c) self-hosted first — the buyers who chose Swarm because code never leaves the machine want the team daemon on their own infra (one binary, SQLite or Postgres); a hosted offering can wrap the same binary later if demand appears; (d) the free tier is never crippled to sell the paid one — M8.1/M8.2 below are free and land in core.

    Decision (2026-08-23): open-core on the single-machine / multi-person line; self-hosted team daemon first, hosted later; no relicensing of existing packages; free tier stays telemetry- and account-free. Pricing, license text and package split are open until M8.3 starts.

  • OQ-16 Daemon auth default (M8.2). daemon.auth is loopback-optional in 0.x so existing installs, the dashboard and the Tauri app keep working without a token. Should required become the default at 1.0, and how does the desktop app obtain the token (read daemon.json from the shell, or a one-shot /v1/session-token on loopback)? See 14-teams.
  • OQ-17 Local human identity before OIDC (M8.2). actor.kind = "human" needs an id on a machine with no login: OS username (spoofable on shared boxes), git config user.email (what the forge already attributes commits to), or just "laptop owner". Proposal: OS username now, replaced by the OIDC subject in M8.3; record both when available. See 14-teams.

Source: docs/07-open-questions.md · v0.8.0.