ravel-lite create ~/Development/ravel-tutorial-example/LLM_STATE/main
A plan is the unit ravel-lite drives a cycle against. Plans are not hand-written. They are bootstrapped through an interactive Claude Code conversation that asks you what you want to work on, then materialises the plan-state files from your answers. This chapter walks through ravel-lite create against the reading-list-manager from the previous chapter.
ravel-lite create
The command takes one argument — the path of the new plan directory — and refuses to create it if the directory already exists. The parent must exist (so it knows where to write):
Three things happen, in order:
-
Pre-spawn scaffold. The runner creates the plan directory and writes four empty-but-valid files inside it:
phase.mdcontainingwork,backlog.yamlcontainingtasks: [],memory.yamlcontainingentries: [], anddream-word-countcontaining0. This scaffold is mechanical work the runner can do deterministically; it is not LLM output. -
Prompt composition. Ravel-Lite reads the embedded
create-plantemplate (the source-of-truth lives atdefaults/create-plan.mdin the Ravel-Lite tree), appends the absolute target plan path with explicit instructions, and uses the result as the system prompt for a fresh Claude Code session. -
Headful claude session. Ravel-Lite spawns Claude Code with stdio inherited so the conversation runs directly in your terminal — no TUI, no JSON streaming, just a normal claude session. The model used is whichever model you have configured for the work phase (in
<config-dir>/config.luaviaravel.set_model("work", …)); plan creation is work-phase-like reasoning, so the same model is reused.
Pi is not currently supported as the create-plan agent — create is interactive, and the integration was built first against Claude Code.
|
Note
|
The first time claude runs against a new directory it asks for trust. If you have not used Claude Code in this project subtree before, expect a prompt like "Do you want to trust this directory?" — answer yes once and the trust grant is remembered in |
The conversation
The create-plan template gives claude six topics to clarify in §1: what the plan is for, what tasks you already know need doing, what categories make sense for grouping them, known dependencies or blockers, peer-project relationships, and any work-phase-specific instructions. Claude is told to ask follow-ups one at a time rather than firehosing all six at once.
The template also has a deliberate escape hatch: if your first turn is a complete, concrete description, claude will skip the multi-turn dialogue and proceed straight to writing the plan files. A single-task plan is treated as a valid plan. This is the common path in practice — most users have something specific in mind and hand claude the whole picture in one paragraph.
Below is one such single-shot description against the reading-list-manager. The exact wording of your description shapes the resulting backlog and memory; this is illustrative, not prescriptive.
> What is this plan for? What do you already know needs doing?
I want a plan to populate a curated reading-list-manager. The
project is at ~/Development/ravel-tutorial-example. The README
defines the entry format and naming conventions. There is no
code, just reading-list.md and notes/<slug>.md per entry.
Initial backlog has three tasks, all in a single category called
"reading-list":
1. Add the first ten distributed-systems entries.
2. Add five type-theory entries focused on dependent-typing
introductions.
3. Cross-link entries that cite each other.
No dependencies between tasks, no peer-project relationships.
Memory should remember three standing facts:
(a) Each entry needs a one-line annotation only; longer thoughts
go in notes/<slug>.md.
(b) Slugs are lower-kebab-case derived from the title; the
convention is in the README.
(c) Tags should be reused before inventing new ones; check
existing tags in reading-list.md first.
No special work-phase instructions. Please go ahead and write
the plan with that scope.
Claude then writes the plan using the ravel-lite state CLI rather than direct file edits. That choice matters: it routes every mutation through the schemas in src/state/<name>/schema.rs, which means a malformed entry never lands on disk. You will see claude run ravel-lite state backlog add once per task and ravel-lite state memory add once per memory entry, with the body of each entry passed via a temp file --body-file rather than inline.
After the conversation finishes you will see the populated plan directory:
$ ls ~/Development/ravel-tutorial-example/LLM_STATE/main/
backlog.yaml
dream-word-count
memory.yaml
phase.md
Four files, exactly the scaffold the runner laid down — but backlog.yaml and memory.yaml are now populated rather than empty shells. Other files appear later: session-log.yaml and latest-session.yaml are written by the analyse-work phase after the first cycle completes, and the per-phase *-baseline files (work-baseline, reflect-baseline, etc.) are written by the phase loop as it captures pre-phase SHAs to diff against.
Inspecting what create produced
Read the backlog the conversation seeded:
$ ravel-lite state backlog list ~/Development/ravel-tutorial-example/LLM_STATE/main --format markdown
## reading-list
| title | status | deps |
|---|---|---|
| Add first ten distributed-systems entries | not_started | — |
| Add five type-theory entries on dependent-typing introductions | not_started | — |
| Cross-link entries that cite each other | not_started | — |
And the seeded memory:
$ ravel-lite state memory list ~/Development/ravel-tutorial-example/LLM_STATE/main
entries:
- id: one-line-annotations-only-in-reading-list-md
title: One-line annotations only in reading-list.md
body: |
Each entry in `reading-list.md` gets a **one-line annotation only**.
Anything longer — summaries, quotes, open questions, follow-up reading
— belongs in the entry's `notes/<slug>.md` file, not in the main list.
This keeps `reading-list.md` scannable as an index.
- id: slug-convention-lower-kebab-case-from-title
title: 'Slug convention: lower-kebab-case from title'
body: |
Slugs are **lower-kebab-case derived from the entry title**. The
authoritative convention lives in the project README — consult it
before coining a new slug rather than guessing.
The slug is used both as the `notes/<slug>.md` filename and as the
stable identifier for cross-links.
- id: reuse-existing-tags-before-inventing-new-ones
title: Reuse existing tags before inventing new ones
body: |
Reuse existing tags before inventing new ones. Before tagging a new
entry, scan the current tag set in `reading-list.md` and prefer an
existing tag that fits — even a slightly imperfect match is usually
better than a near-duplicate new tag (e.g. don't add `consensus-algos`
if `consensus` already exists).
Only coin a new tag when no existing tag plausibly applies.
These are the two files the work phase will read at the start of the first cycle. Memory is durable — these three entries will carry across every future cycle of this plan, sharpened by reflect and (eventually) compacted by dream.
Note that the entry IDs are claude’s own slug-isations of the titles you described, not byte-for-byte renderings of your input — claude tightens prose for the body and derives stable identifiers in lower-kebab-case form. Hand-edit memory.yaml later if you prefer different IDs; the runtime never holds shadow state, so an in-place edit between phases is the supported customisation path.
|
Note
|
The plan files are plain YAML. You can hand-edit them between phases — typo a task title, reorder the backlog, add a memory entry — and the next phase will read whatever is there. The orchestrator never holds shadow state. The State files reference documents the schema and per-file lifecycle. |
When create fails
The two failure modes new users hit:
-
Plan directory already exists.
createrefuses to clobber. Move the existing directory aside or pick a different plan name. -
Trust modal hangs. Historically, the claude trust prompt could fail to render cleanly inside multiplexers (tmux, screen) or in some non-default terminal configurations, leaving the create session apparently hung. Versions of ravel-lite from
1.0.3onward route the claude subprocess through a captured PTY (src/agent/pty_capture.rs) and have not exhibited the invisible-modal hang in practice. If you do see a hang, the historical workaround still applies — runclaudeonce at the project’s parent directory beforehand to grant trust outside of ravel-lite, or runcreatein a plain terminal.
Either way, no plan files are written if the conversation does not finish — create is atomic on the orchestrator side. A partial LLM_STATE/main/ directory after a failed create is rare, but if it happens, removing it manually and re-running is safe.
With the plan in place, the next chapter runs the first cycle: ravel-lite run, the work phase, the headless phases that follow, and what the audit-trail commits look like in git log.