Ravel User Guide
Status: Ravel is in architectural design phase. This guide describes the intended v1 user experience based on the committed architecture. Code has not yet been written. Commands and flags are indicative, not final.
This guide walks through the Ravel user experience: installation, first-time setup, daily workflow, and the mental model for using the daemon + TUI.
Contents
- What Ravel is
- Mental model
- Installation
- First-time setup
- Adopting a project
- Running the daemon
- Using the TUI
- Working on a plan
- Creating sub-plans
- Consulting experts
- Dispatching cross-plan tasks
- Editing routing rules
- Obsidian browsing
- Multi-machine sync
- Troubleshooting
What Ravel is
Ravel is a persistent orchestration daemon for AI-assisted development. Instead of opening individual Claude Code sessions and hoping they remember yesterday's context, you run Ravel once on your machine and it hosts:
- Plan actors — one per plan you're working on, tracking phase cycles, backlogs, memory, and dispatched tasks
- Expert actors — consultative domain experts you can query like co-workers
- Routing — declarative rules that send tasks to the right place automatically
- Ingestion — automatic promotion of plan-phase learnings into durable knowledge
You drive the daemon through a TUI client (written in Rust with ratatui) or, in future versions, through Obsidian or a web UI. All clients attach to the same daemon over a local Unix socket, so you can run multiple clients simultaneously without conflicting.
For the architectural overview, see architecture.md.
Mental model
The shift from Ravel v0.1.0 (a CLI tool) to v1 (a daemon) is significant. The key ideas:
You don't "run Ravel" per plan
The daemon is always on, like a background service. You start it once per machine (or let it auto-start on first login) and it hosts every plan across every adopted project.
Plans are where work happens
A plan is a directory containing plan-state.md. Plans are nested — a project has a single project-root plan, which may have sub-plans, which may have sub-sub-plans. Each plan runs its own four-phase cycle (work → reflect → compact → triage) and has its own memory and backlog.
You spend most of your time driving one plan at a time through the TUI. The TUI attaches to that plan's actor in the daemon, and you see its current state, backlog, and streaming output from any active LLM session.
Knowledge is consultative
Instead of reading knowledge files yourself or loading them into your LLM session, you ask an expert. Experts are actors with narrow domain scope and curated knowledge. When you query an expert, it spawns its own fresh-context session with just the relevant knowledge and returns an answer. Your session never pollutes with the expert's knowledge.
This is the key fresh-context discipline: don't load what you can ask for.
Dispatches are how plans talk to each other
If a plan identifies a concern that belongs in a different plan — same project or different project — it dispatches a task. The daemon routes the dispatch via declarative rules (user-editable, hot-reloadable) or, when rules don't decide, a fresh-context routing agent.
Dispatches have audit trails on both ends: the origin's Dispatched section and the target's Received section.
Installation
V1 installation path (indicative — not yet implemented):
# Install the Elixir daemon
mix archive.install hex ravel
# Install the Rust TUI client
cargo install ravel-tui
# Verify
ravel --version
ravel-tui --version
Prerequisites:
- Erlang/OTP 27+ and Elixir 1.17+ for the daemon
- Rust 1.80+ for the TUI client (or download a pre-built binary)
- Git for vault and project versioning
- Obsidian (recommended) for browsing the vault
- Claude Code as the v1 harness (other harnesses in v1.5+)
First-time setup
On first run, Ravel creates a dedicated vault at ~/Ravel-vault/ (configurable):
$ ravel daemon --init
Creating vault at /Users/you/Ravel-vault/...
• Vault identity: ravel.toml (schema v1)
• Obsidian template: .obsidian/ with Dataview + Templater
• Default routing rules: routing.ex (empty scaffolding)
• Default daemon config: daemon.toml
• Knowledge directories: knowledge/{languages,domains,tools,techniques,projects}
• Experts directory: experts/ (starter set of 6 experts)
• Runtime directory: runtime/ (ephemeral)
Daemon started. Socket: /Users/you/Ravel-vault/runtime/daemon.sock
Attach a client with `ravel-tui` in another terminal.
The vault is a git repository. Ravel commits its own writes automatically; you can commit manual edits anytime. The vault can be synced between machines via git push / git pull.
Adopting a project
Before you can drive a plan inside a project, you adopt the project into your vault:
$ cd ~/dev/my-awesome-project
$ ravel adopt-project .
Adopting my-awesome-project into vault at /Users/you/Ravel-vault/
• Creating /Users/you/dev/my-awesome-project/ravel/
• Creating project-root plan
• Creating vault symlink: /Users/you/Ravel-vault/projects/my-awesome-project
• Regenerating plan-catalog.md
• Rescanning vault
Project adopted. Root plan: my-awesome-project/project-root
Run `ravel-tui attach my-awesome-project/project-root` to start working.
Adoption does three things:
- Creates
<project>/ravel/project-root/with an empty root plan. - Creates a vault symlink at
<vault>/projects/<project-name>pointing at the project's ravel directory. This makes the project visible in Obsidian and to the daemon. - Regenerates
<vault>/plan-catalog.mdso the new project appears in catalogs.
Running the daemon
After initial setup, the daemon runs as a background process:
# Start in the background (daemonize)
$ ravel daemon --background
Daemon started. PID: 48291. Logs: /Users/you/Ravel-vault/runtime/daemon.log
# Or start in the foreground with log output
$ ravel daemon
[info] Loading vault at /Users/you/Ravel-vault/
[info] VaultDirectory cached: 12 plans, 6 experts
[info] Listening on /Users/you/Ravel-vault/runtime/daemon.sock
[info] Daemon ready
Common daemon commands:
ravel daemon # start in foreground
ravel daemon --background # start in background
ravel stop # graceful shutdown
ravel status # query daemon health
ravel rescan # refresh vault catalog and routing rules
ravel logs # tail daemon logs
Using the TUI
The TUI is where you interact with plans day-to-day.
$ ravel-tui
The TUI opens and shows the plan catalog — every plan in the vault, grouped by project, with current phase states. You navigate with arrow keys, select a plan, and press Enter to attach.
┌─ Ravel ─────────────────────────────────────────────────┐
│ Plans in vault │
│ │
│ ▼ Ravel │
│ project-root [reflect] 2h ago │
│ ▼ sub-F-hierarchy [work*] active now │
│ sub-F-dispatch [idle] │
│ sub-B-phase-cycle [triage] 1d ago │
│ │
│ ▼ APIAnyware-MacOS │
│ project-root [idle] │
│ sub-ffi-callbacks [reflect] 3h ago │
│ │
│ Experts │
│ rust-expert dormant │
│ research-expert dormant │
│ ffi-expert active │
│ │
│ [Enter] attach [r] rescan [q] quit │
└──────────────────────────────────────────────────────────────┘
Plan view
Attaching to a plan shows the plan's state: current phase, backlog, memory summary, recent session log, and live harness output if a phase is running.
┌─ Ravel/project-root/sub-F-hierarchy ──── [work] ─────────┐
│ Description: Plan hierarchy, actor model, inter-plan │
│ dispatch, declarative routing. │
│ │
│ Backlog (3 tasks) │
│ 1. Write design doc for F [in_progress] │
│ 2. Scaffold sibling plan [blocked: spike] │
│ 3. Land amendment tasks for A, B, C [not_started] │
│ │
│ Memory (14 entries) │
│ ▸ Plan hierarchy via project-root convention │
│ ▸ Dispatch target resolution asymmetry │
│ ▸ Actor-per-plan in BEAM daemon │
│ ... │
│ │
│ Session output (streaming) │
│ Writing docs/superpowers/specs/2026-04-14-sub-F-... │
│ ✓ Wrote 2188 lines to ... │
│ │
│ [w] work [r] reflect [c] compact [t] triage │
│ [d] dispatch [q] query [e] edit routing [Esc] detach │
└──────────────────────────────────────────────────────────────┘
Keybindings are configurable. Standard vim-like navigation works throughout.
Working on a plan
A plan's life is a repeating four-phase cycle: work → reflect → compact → triage.
Work phase
Press w in the plan view. The TUI sends run_phase work to the daemon, which:
- Renders a staging directory for the plan (placeholders substituted, prompts embedded, vault catalog injected).
- Spawns a Claude Code session via the harness adapter.
- Streams output to the TUI in real time.
- On session exit, runs the
DispatchProcessorandQueryProcessoron anydispatches.yaml/queries.yamlfiles the phase produced. - Updates
plan-state.mdand transitions to idle (or directly to reflect if auto-advance is enabled).
You watch the work happen in the TUI. You can interrupt with Ctrl-C at any time — the daemon preserves the staging directory under runtime/interrupted/ for forensic review.
Reflect phase
Press r. The reflect phase reads the work phase's output and writes structured updates to memory.md — distilled architectural decisions, learnings, and next-step suggestions. Reflect is lossy — it prunes redundancy and restructures.
Reflect may also write dispatches to dispatches.yaml if it identifies cross-cutting concerns affecting other plans.
Compact phase
Press c. Compact is lossless — it rewrites memory.md to remove redundancy without dropping information. Triggered automatically when memory.md exceeds a word-count threshold against its baseline. Manually triggerable but rarely needed.
Triage phase
Press t. Triage plans the next work cycle: updates backlog priorities, processes incoming Received items from other plans, identifies the next work starting point. Triage is the gate before the next work phase.
Creating sub-plans
As a plan grows, you'll want to split off focused sub-plans to keep each plan's scope tight and avoid context bloat.
From the plan view, press n (or run ravel-tui new-plan from the shell):
┌─ Create sub-plan ───────────────────────────────────────────┐
│ Parent: Ravel/project-root/sub-F-hierarchy │
│ │
│ Name: sub-F-datalog │
│ Description: Datalog rules format + Erlog integration for │
│ routing.ex evolution. │
│ (120 char limit; keyword-dense; noun-phrase) │
│ │
│ [Create] [Cancel] │
└──────────────────────────────────────────────────────────────┘
Creating a sub-plan scaffolds the new plan directory with initial files and registers a new PlanActor in the daemon. The catalog regenerates automatically.
Consulting experts
From a running work phase, your LLM session has an ask_expert tool available. The session invokes it when it wants a domain expert's opinion:
ask_expert(
target: "ffi-expert",
question: "What's the correct lifetime for a Rust closure passed to a C callback that may outlive the enclosing function?"
)
The daemon:
- Routes the query to the
ffi-expertactor. - The expert's actor spawns its own fresh-context session with: the expert's persona, retrieved knowledge from its scope (10 top entries matching the question), and the question itself.
- The expert session reasons and returns an answer.
- The daemon delivers the answer back to the originating session as a tool-call result.
- The originating session continues its work with the answer available in its context.
From the human side, you can also query experts directly from the TUI:
[q] query → pick target expert → type question → response opens in a pane
Multiple experts can be queried with the same question (parallel consultation) — each one responds independently with its own perspective.
Dispatching cross-plan tasks
When a work or reflect phase identifies a concern that belongs in a different plan, it writes a dispatch to dispatches.yaml. After the phase exits, the daemon's dispatch processor routes it.
Same-project dispatch
The LLM writes:
dispatches:
- target-plan: Ravel/project-root/sub-A-global-store
reason: Lock directory pinning question belongs to sub-A's scope.
body: |
Confirm the lock dir at <vault>/runtime/locks/ is the final
pin so sub-D can proceed with its brainstorm.
The daemon appends to the target plan's backlog.md under ## Received and to the origin's backlog.md under ## Dispatched. No LLM intervention — same-project targeting is mechanical.
Cross-project dispatch
The LLM writes:
dispatches:
- target-project: APIAnyware-MacOS
reason: FFI callback GC-protection pattern is APIAnyware territory.
body: |
Investigate whether gc-protect wrappers need to be applied
to all callback registration sites, not just the ones in the
current crash reproducer.
The daemon spawns a Level 2 routing agent — a fresh-context Claude Code session with read access to APIAnyware-MacOS's vault subtree (including source code). The agent reads relevant plans and code, decides the routing, and either appends to a specific plan's Received section or writes a rejection back to the origin's Dispatched section.
Dispatching from the TUI
You can also dispatch manually from the TUI with d:
[d] dispatch → pick target (plan / project / expert) → type body → confirm
This is the human driver path — the same dispatch mechanism, but initiated by you rather than an LLM phase.
Editing routing rules
Routing rules live in <vault>/routing.ex, a user-editable Elixir module. You edit it in your editor of choice:
defmodule Ravel.UserRouting do
def route(:query, facts) do
cond do
"rust" in facts or "cargo" in facts ->
{:target_expert, "rust-expert"}
"ffi" in facts or "callback_registration" in facts ->
{:target_expert, "ffi-expert"}
true ->
:no_route
end
end
def route(:dispatch, facts) do
cond do
"migration" in facts or "rename" in facts ->
{:target_plan, "Ravel/project-root/sub-G-migration"}
true ->
:no_route
end
end
end
Save the file. The daemon detects the change (or you run ravel rescan), recompiles the module via BEAM's hot code reload, and the new rules take effect immediately — no daemon restart needed.
If the file has a compile error, the previous version stays loaded and the error is surfaced in the TUI.
Rule suggestions
When the Level 2 routing agent runs (because rules didn't decide a case), it may propose a new rule. The TUI shows it as "Rule suggestion pending review" in a notification pane:
┌─ Rule suggestion pending ──────────────────────────────────────┐
│ Source: Level 2 routing agent (APIAnyware-MacOS context) │
│ │
│ Proposed rule: │
│ │
│ def route(:dispatch, facts) do │
│ if "gc_protect" in facts and "callback_registration" in │
│ facts do │
│ {:target_plan, │
│ "APIAnyware-MacOS/project-root/sub-ffi-callbacks"} │
│ else │
│ super(:dispatch, facts) │
│ end │
│ end │
│ │
│ Context: Today's dispatch from sub-F matched this target cleanly │
│ │
│ [a] accept (append to routing.ex) [e] edit [r] reject │
└──────────────────────────────────────────────────────────────────┘
Accepting appends the rule to routing.ex and commits the change to vault git. Over time, the deterministic routing path grows as you accept suggestions — novel cases teach the system.
Obsidian browsing
Open the vault in Obsidian:
obsidian ~/Ravel-vault/
You'll see:
plan-catalog.md— the auto-generated list of every plan and expert, with descriptions. Your at-a-glance "what's in my vault."projects/directory — symlinks to every adopted project's ravel/ directory. Browse plans, read memories, follow wikilinks between them.knowledge/directory — Tier 2 global knowledge entries, organized by axis.experts/directory — expert declaration files (one per expert).routing.ex— the routing rules, visible as source code.
Obsidian's built-in features work naturally:
- Dataview queries across frontmatter give you dashboards: all plans in reflect phase, all knowledge tagged with
ffi, all dispatches to APIAnyware-MacOS this week. - Graph view visualizes the relationships between plans, knowledge entries, and experts via wikilinks.
- File history shows every change to every file, so you can audit and roll back.
Obsidian is the primary explorer and curation UI for v1. The Rust TUI is the driver for work cycles; Obsidian is the browser for everything else.
Multi-machine sync
The vault is a git repository. To move to a new machine:
# On machine A:
$ cd ~/Ravel-vault
$ git push origin main
# On machine B:
$ git clone git@github.com:you/ravel-vault.git ~/Ravel-vault
$ ravel daemon --init-from-existing
Existing vault detected at /Users/you/Ravel-vault/
• Rebuilding project symlinks (may need manual path adjustments)
• Regenerating plan-catalog.md
• Starting daemon
Project symlinks are not in git (absolute paths are per-machine). The daemon rebuilds them on first startup, asking you to confirm each project's local path.
True multi-user team mode (multiple developers sharing a vault with cross-daemon dispatch) is a v2 feature. For v1, multi-machine sync is single-user-across-machines.
Troubleshooting
Daemon won't start
Error: another daemon is already running (pid 48291)
Check with ravel status. If the other daemon is stale, ravel stop --force will release the singleton lock.
Daemon refuses to start due to routing.ex compile error
Error: routing.ex has compile errors — daemon refuses to start
line 14: syntax error before: {
Edit routing.ex to fix the error, then start again. The daemon refuses to start with a broken routing module — this is deliberate to prevent silent routing failures.
Plan description too long
Error: plan description exceeds 120 character cap
plan: Ravel/project-root/sub-F-hierarchy
description: (127 chars) ...
Edit the plan's plan-state.md frontmatter, shorten the description, save. The daemon will pick up the change on next rescan.
Vault catalog out of sync
$ ravel rescan
This regenerates plan-catalog.md, reloads routing.ex, re-verifies vault invariants, and emits a summary of any problems.
Harness session hangs or crashes
The actor transitions to Faulted. The TUI shows it in red. Investigate via ravel logs and retry via the TUI's retry command (R in the faulted plan view).
Further reading
- Architecture overview — the full architectural picture
- Configuration reference —
daemon.toml,ravel.toml, and related config files - Knowledge format — how knowledge entries are structured
- Research sources — the cognitive science grounding