You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**`bin/shuttle` is an escript** — it bundles BEAM bytecode at build time and loads it at boot. A restart without `make build` is a no-op for picking up source edits; `mix compile` without restart is a no-op for an already-running daemon. **`make restart` always.** When `shuttle-ctl status` sees a fiber but `bin/shuttle snapshot` doesn't list it as eligible, the daemon is stale.
-**tmux owns the worker process; Shuttle owns the watcher.** Workers stay attachable via `tmux attach -t shuttle-<fiber-id>`. Supervise watchers, not workers.
72
-
-**Felt is the data layer; Shuttle shells out to the felt CLI.** Don't import felt internals.
73
-
-**Agent records live in one source of truth: `share/agents.json`.** Both runtimes (Elixir daemon, Go CLI) embed it at compile time — Elixir via `@external_resource` + `File.read!` in `lib/shuttle/agents.ex`, Go via `//go:embed` (generated `pkg/schema/agents_embedded.go`). Edit the JSON, then `make restart`. There is no `config/agents.exs` and no hand-edited fallback list anywhere — see `[[ai-futures/shuttle/finding-agent-registry-four-mirrors]]` for the cleanup that landed.
74
-
-**`shuttle.agent` field drives agent selection.** The `shuttle:` block's `agent:` field resolves against the registry. Bare `codex`/`pi` felt tags resolve via back-compat aliases when no `shuttle.agent` is set; default agent is `claude-sonnet`.
75
-
-**shuttle-ctl is the agent-facing CLI.** Write verbs validate before write; works offline. Elixir `bin/shuttle` handles daemon lifecycle and dispatch only.
76
-
-**No tag predicate for dispatch.**`constitution` is a human convention only — a fiber with a `shuttle:` block dispatches with or without it. Tags are free-form qualitative noticings; only `idea` is read by Portolan's kanban (for column placement).
62
+
-**tmux owns the worker process; Shuttle owns the watcher.** Workers stay
63
+
attachable via `tmux attach -t shuttle-<fiber-id>`. Supervise watchers,
64
+
not workers.
65
+
-**Felt is the data layer; Shuttle shells out to the felt CLI.** Don't
66
+
import felt internals.
67
+
-**Agent records live in one source of truth: `share/agents.json`.** Both
68
+
runtimes (Elixir daemon, Go CLI) embed it at compile time — Elixir via
69
+
`@external_resource` + `File.read!` in `lib/shuttle/agents.ex`, Go via
70
+
`//go:embed` (generated `pkg/schema/agents_embedded.go`). Edit the JSON,
71
+
then `make restart`. There is no `config/agents.exs`.
72
+
-**`shuttle.agent` field drives agent selection.** The `shuttle:` block's
73
+
`agent:` field resolves against the registry. Default agent is
74
+
`claude-sonnet`.
75
+
-**shuttle-ctl is the agent-facing CLI.** Write verbs validate before
76
+
write; works offline. `bin/shuttle` handles daemon lifecycle and dispatch.
77
+
-**No tag predicate for dispatch.** The `shuttle:` block's `enabled: true`
78
+
field is the dispatch signal. Tags are free-form qualitative noticings;
79
+
only `idea` is load-bearing for Portolan's kanban column placement.
77
80
78
81
## How dispatch works
79
82
80
-
-**Poller** (`lib/shuttle/poller.ex`) is the single GenServer that owns the
81
-
tick. It walks each configured `felt_host`, parses each `*.md` file's
82
-
frontmatter, and considers a fiber eligible iff `shuttle.enabled: true` AND
83
-
`status in ["open", "active"]` AND not already running/claimed AND deps
84
-
satisfied.
85
-
-**Configured hosts** come from `LOOM_HOMES` (comma-separated) →
**Cross-host visibility.**`--all` and `--remote` go through the local
112
-
daemon's `/api/v1/state/composite`; the daemon's `RemoteRegistry` polls
113
-
each configured remote (`config :shuttle, :remotes, [...]`) over its
114
-
SSH-tunnel-mapped port and merges the snapshots with freshness flags.
115
-
The CLI never talks to remote daemons directly — `SHUTTLE_DAEMON_URL`
116
-
overrides which local daemon to query, but remote URLs live in mix
117
-
config alone. See [[ai-futures/shuttle/constitution-shuttle-remote-dispatch]].
127
+
Dispatch sanity ladder:
118
128
119
-
## Dispatch prompt structure
120
-
121
-
The fresh, resume, and standing-run prompts all share the same shape (`compose_prompt/3` in `lib/shuttle/dispatcher.ex`):
122
-
123
-
1.**Orientation paragraph** — what Shuttle is, what the worker is here to do, how the practice loads. Per-prompt, not boilerplate. Goes first because in causal attention every downstream token sees the prefix.
3.**`From User · <relative time>`** — the most recent `--kind review-comment` event, if any. Pulled fresh at dispatch. The user's intent, inlined so it sits in attention prefix.
126
-
127
-
The fiber's outcome and last editorial event are *not* inlined — they're already in scope after `felt show <id>` (outcome + Recent line) and `felt history <id>` (full chain), which the shuttle skill prescribes the worker calls on arrival. Inlining either duplicates state and risks drift between the prompt's snapshot and felt's view.
128
-
129
-
Operational instructions (read the constitution, exit before half-full, append an editorial event, `kill $PPID`, standing-run awaiting-review handoff) live in the `shuttle` and `felt` skills, not the prompt. The prompt's job is orientation; duplicating practice means drift.
130
-
131
-
A useful sanity ladder when something isn't dispatching:
132
-
133
-
1.`shuttle-ctl status` shows `enabled: true, idle, oneshot`? → fiber is well-formed and the Go walker sees it.
134
-
2.`bin/shuttle snapshot` lists it under `eligible[]` with `state: running`? → daemon dispatched it.
135
-
3. If shuttle-ctl sees it but daemon doesn't → daemon binary is stale. `make restart`.
136
-
4. If daemon sees it but agent never appears → check `share/agents.json` for the resolved agent's `cli` and that the wrapper is on `PATH`.
137
-
5. If the snapshot has no `felt_hosts:` field → binary pre-`297a24d`. Same fix: `make restart`.
129
+
1.`shuttle-ctl status` shows `enabled: true, idle, oneshot`? → fiber is
130
+
well-formed and the Go walker sees it.
131
+
2.`bin/shuttle snapshot` lists it under `eligible[]`? → daemon dispatched.
132
+
3. shuttle-ctl sees it but daemon doesn't → daemon binary is stale.
133
+
`make restart`.
134
+
4. Daemon sees it but agent never appears → check `share/agents.json` for
135
+
the resolved agent's `cli` and that the wrapper is on `PATH`.
138
136
139
137
## Codebase layout
140
138
141
139
```
142
-
shuttle/ project root (this dir, flat — no nested shuttle/)
140
+
shuttle/
143
141
├── CLAUDE.md you're reading it
144
142
├── Makefile build + daemon lifecycle
145
143
├── mix.exs Mix project
146
144
├── bin/shuttle the daemon escript (built artifact)
0 commit comments