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):

ravel-lite create ~/Development/ravel-tutorial-example/LLM_STATE/main

Three things happen, in order:

  1. Pre-spawn scaffold. The runner creates the plan directory and writes four empty-but-valid files inside it: phase.md containing work, backlog.yaml containing tasks: [], memory.yaml containing entries: [], and dream-word-count containing 0. This scaffold is mechanical work the runner can do deterministically; it is not LLM output.

  2. Prompt composition. Ravel-Lite reads the embedded create-plan template (the source-of-truth lives at defaults/create-plan.md in 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.

  3. 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.lua via ravel.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 ~/.claude.json for the next run. Versions of ravel-lite from 1.0.3 onward (paired with the PTY-capture work in src/agent/) render this modal correctly even in non-multiplexer terminal sessions; earlier versions had a known invisible-modal hang on fresh machines.

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:

  1. Plan directory already exists. create refuses to clobber. Move the existing directory aside or pick a different plan name.

  2. 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.3 onward 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 — run claude once at the project’s parent directory beforehand to grant trust outside of ravel-lite, or run create in 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.