Shuttle¶
Shuttle dispatches coding agents against felt fibers. Felt keeps those fibers as markdown files in a directory. Shuttle adds one optional frontmatter block, plus a daemon that acts on it.
Nothing else about felt changes. Leave the block out and the fiber stays a plain note. Add the block and the daemon can hand the fiber to a coding agent as a constitution.
You can skip this whole section
Felt works without Shuttle. The CLI runs as a Go binary over a markdown tree. If you want a notes-and-tasks store, stop at Concepts — you lose nothing.
The block¶
---
name: Rewrite the covariance loader
status: active
shuttle:
kind: oneshot
host: my-laptop
project_dir: /home/me/dev/pipeline
agent: claude-opus
---
Those keys cover the whole interface. Felt validates the block's shape and otherwise treats it as opaque frontmatter. Remove the daemon and you still have a readable, greppable, version-controlled markdown file.
The loop¶
- Author. Write a fiber body that describes a desired state. Then run
felt shuttle installto attach the block and arm the fiber. - Dispatch. The daemon polls the fiber tree every 30 s by default. For each
eligible fiber it starts exactly one tmux session, running an agent CLI in
project_dir.felt shuttle session-name <fiber>prints the session name,<slug-leaf>-<uid>-shuttle. - Work. The worker reads the constitution fresh from disk. It reads the
previous session's
## Statushandoff. Then it drives toward the desired state and picks its own slice. Sequencing emerges; nobody scripts it. - Hand off. Before exiting, the worker rewrites
outcome:(the kanban headline) and the body's## Statusblock (the next worker's landing pad). It then runsfelt shuttle handoff <fiber>. That stamps a clean-exit marker and ends its own tmux session in one move. - Redispatch or review. A fiber still marked
activegets a fresh worker on the next tick, and that worker lands warm on## Status. A fiber the worker set tostatus: closedwaits for a human verdict.
State across sessions¶
Realization spans sessions by design. A context window is finite; a piece of work often is not. So the fiber carries the durable state, not a transcript. Each worker starts fresh, reads the spec and the handoff, and adds what it can.
Realization therefore stays asymptotic. You amend the constitution as the world changes. You do not empty it like a checklist. Work finishes when a human says it finishes.
Two state channels cross sessions. Keep them apart:
| Channel | Fields | Who writes it |
|---|---|---|
| Handoff prose | outcome:, the body's ## Status |
The worker, rewritten every session |
| Machine continuation | shuttle.runtime.{session_uuid, dispatched_at, run_id, handed_off_at} |
The daemon at dispatch; the worker at clean exit |
handed_off_at newer than dispatched_at marks a clean exit, so the next
worker starts fresh. Otherwise the worker died dirty, and a oneshot resumes the
prior transcript.
The pieces¶
- The daemon — an Elixir/OTP escript at
bin/shuttle. One process, bound to127.0.0.1:4000. It polls, dispatches, and serves an HTTP API. - tmux — hosts the worker process and reports its liveness. Not optional. tmux owns the worker process; Shuttle owns only the watcher. So restarting the daemon leaves live workers running — the daemon re-adopts them on boot.
- The board — a TypeScript kanban UI, served by the daemon at
http://127.0.0.1:4000/. It views the same fibers plus tmux liveness, and holds no state of its own. Skip it if you like: the CLI covers every operation. - The agent registry — maps an agent id (
claude-opus,codex,pi-sonnet, …) to a CLI invocation.felt shuttle agentsprints it.
Honest scoping¶
Shuttle runs the maintainer's machines every day, and parts of it still show that. This section lists them all; the other pages point here.
- Multi-host aggregation needs a macOS hub.
felt shuttle tunnels installwrites launchd autossh jobs and refuses on any other platform. Single-host use — the daemon, the board, and workers on one machine — is supported on Linux and macOS alike, each with its own keep-alive (a systemd user unit or a launchd LaunchAgent). A Linux machine can be a remote a Mac hub aggregates; the tunnel lives on the hub. - macOS gets the most use. The TCC workarounds and the tunnel plists are macOS-first, and the maintainer's own hub is a Mac.
- Several examples name the maintainer's store. Docs use
~/loom, a cross-project store, as the running example. Substitute your own store path everywhere it appears. - The daemon ships no release artifact. You build it from a checkout and you keep the checkout. See Installation.
None of this blocks a single-machine adopter. It does earn Shuttle the label "currently fleet-oriented" rather than "general-purpose orchestrator."
Next¶
- Constitutions — how to author one.
- Lifecycle — the worker loop, exit semantics, and the board.
- Installation — building the daemon from source.