Daedalus, wizard state machine
How the 13 wizard steps progress and gate
The Daedalus wizard is a state machine. Each of the 13 steps depends on prior steps having completed. The current step is tracked in daedalus.json and the URL.
States
welcome → repository → domain → provider → email → database → compute
↓
deploy ← oauth ← secrets ←──────────────────────────────────────
↓
health → accounts → applications
↓
destroy (one-way exit)Each box is a wizard page at a route like /repository, /domain, etc.
Gating
A step is "ready" when its prerequisites have completed. The Zustand store tracks per-step status:
interface WizardState {
steps: Record<StepName, {
status: "pending" | "in_progress" | "completed" | "errored";
completed_at?: string;
error?: string;
}>;
}The next step's UI is enabled only when the current step is completed.
Operators can navigate backwards (revisit completed steps) but the wizard prompts before overwriting persisted decisions.
Persistence
After each step completes, the relevant fields are written to daedalus.json. On Daedalus restart, the store is rehydrated from this file, and the operator resumes where they left off.
State transitions
Each step's "Complete" button:
- Validates inputs.
- Performs the step's action (
gh repo fork,doctl droplet create, etc.). - On success: updates
daedalus.json, advances to next step. - On failure: shows error, keeps the operator on the current step.
Error states
Errors during a step:
- The wizard does not auto-advance.
- The error is shown inline in the step UI.
- The operator can retry, edit inputs, or skip (where applicable).
Some steps are non-skippable (Compute, Deploy). Skipping is only allowed where it's safe (e.g. skip Email if you'll configure later, although the deploy will lack email until you do).
Destroy is a different shape
destroy doesn't fit the linear flow. It's accessible at any time and tears down the deployment. After destroy, daedalus.json is wiped and you're back at welcome.
MCP and state
When Claude drives Daedalus via MCP, it calls read_context to inspect the state, navigate to a step, and step-specific tools (form_input, click) to advance. The state machine logic stays Rust-side; MCP is read-and-act, not read-and-decide-via-extra-state.
Adding a wizard step
If a future expansion adds, say, "DNS Configuration" as its own step:
- Add
dnstoStepNametype. - Add
src/app/dns/page.tsxwith the UI. - Add a Zustand action for the DNS-completion.
- Update the navigation flow (which step comes after Domain, which step comes before).
- Update the MCP
navigatetool's allowed paths list.
Each step takes ~1-2 days for a clean, tested addition.