Before creating a plan, you need a project — the subtree ravel-lite will read, write, and commit against during a cycle. This chapter introduces what a project is in ravel-lite’s vocabulary, scaffolds a worked example we will use for the rest of the tutorial, and registers it in the per-user projects catalog.

What ravel-lite means by "project"

A project is the subtree the orchestrator controls during a cycle. It is not derived from where .git sits — a single repository can host many projects as nested subtrees. It is derived from where the plan lives: given a plan directory at <project>/<state-dir>/<plan>/ (typically <project>/LLM_STATE/<plan>/), ravel-lite computes the project as <plan_dir>/../.. and treats that subtree as everything it is allowed to read, write, and commit.

The decoupling matters in two situations. The common one is a single-purpose repository where the project root is the repository root — the path math just happens to coincide. The interesting one is a monorepo, where the project is a subtree like monorepo/services/billing/ and ravel-lite needs to stay out of sibling subtrees. In both cases the orchestrator scopes its git operations using a pathspec on the project subtree, so dirty-tree checks, baseline diffs, and the analyse-work snapshot all see only the project’s files.

For this tutorial the project will be a standalone directory under ~/Development/ — no monorepo concerns — but the path math is the same.

A non-code worked example

Throughout this tutorial we will operate on a small reading-list-manager: a curated list of articles and books to read, with notes and tags. There is no code; the project is purely a Markdown file and a YAML catalog. That is on purpose. A non-code project keeps the focus on the cycle — task selection, the headless phases, memory growth, the dream trigger — without diff-resolution gymnastics.

Bootstrap the project directory:

$ mkdir -p ~/Development/ravel-tutorial-example
$ cd ~/Development/ravel-tutorial-example
$ git init
Initialized empty Git repository in /Users/admin/Development/ravel-tutorial-example/.git/

git init is recommended even for a non-code project. The audit-trail commits ravel-lite produces (run-plan: work, run-plan: reflect, the per-phase baseline commits) need somewhere to land. Without a git repository, the git-commit-* audit-trail phases will fail loudly the first time they run.

Now seed two files. A README the work phase will read, and a placeholder reading list:

cat > README.md <<'EOF'
# Reading list

A curated list of articles and books I want to read, with notes and tags.

## Files

- `reading-list.md` — the list, organised by tag.
- `notes/` — per-entry notes, one Markdown file per item.

## Conventions

- Each entry has: title, author, source URL, status (`queued`, `reading`, `done`, `dropped`),
  one or more tags, and an optional one-line annotation.
- Notes for an entry live at `notes/<slug>.md`. The slug is the lower-kebab-case title.
- Tags are free-form lower-kebab-case.
EOF

mkdir notes
touch reading-list.md

git add .
git commit -m "Initial scaffold"

You now have a project. Two files, one directory, one commit. Ravel-Lite will treat ~/Development/ravel-tutorial-example/ as the project root once we point a plan at it.

Note

The README is not optional. The work phase reads <project>/README.md at the start of every iteration to ground the agent in project conventions. A missing README will not crash the phase, but the agent will work from less context — and any conventions you want enforced (the slug rule above, for instance) belong here so they survive across cycles.

Registering the project

Ravel-Lite maintains a per-user projects catalog at <config-dir>/projects.yaml. The catalog maps human-friendly component names to absolute paths on disk. Component names are the shared currency of the cross-plan graph (related-components.yaml); the catalog is the per-user resolver from name to path.

You do not have to populate the catalog manually — ravel-lite run auto-registers a project the first time it sees one. But it is instructive to do it by hand once. The add subcommand is silent on success:

$ ravel-lite state projects add --path ~/Development/ravel-tutorial-example
$ ravel-lite state projects list
schema_version: 1
projects:
- name: ravel-tutorial-example
  path: ../../Development/ravel-tutorial-example

The path stored in the catalog is relative to the config directory, not absolute — pathdiff::diff_paths normalises every entry against <config-dir> so the catalog stays portable: copying the config directory and the project directory together to another machine continues to resolve correctly without an edit. The relative form here (../../Development/ravel-tutorial-example) climbs out of ~/.config/ravel-lite/ to ~/, then descends into the project. At runtime the binary re-resolves to an absolute path before doing anything with it.

If --name is omitted, the basename of the path is used. The catalog rejects duplicate names and duplicate paths so two entries cannot disagree about the same project. To rename later — say, after refactoring or moving the directory — use state projects rename; the rename cascades into related-components.yaml and the discover-cache so cross-references stay consistent. The full surface is documented on the State commands reference.

The shape of a plan directory

A plan is a directory of state files representing an in-flight body of work. Conventionally it sits at <project>/LLM_STATE/<plan-name>/, though the state-directory name is configurable. We will create ~/Development/ravel-tutorial-example/LLM_STATE/main/ in the next chapter.

Inside a freshly-created plan, four files exist — the minimum scaffold ravel-lite create writes before the bootstrap conversation runs:

LLM_STATE/main/
├── phase.md           # one-line file naming the phase to run next ("work\n")
├── backlog.yaml       # tasks, statuses, dependencies, results, hand-offs
├── memory.yaml        # distilled learnings carried across cycles
└── dream-word-count   # word-count tracking for the dream trigger

backlog.yaml and memory.yaml start as empty shells (tasks: [] and entries: []) and are populated by the create conversation through ravel-lite state backlog add / state memory add calls. The bootstrap LLM never writes YAML directly — every mutation routes through the schemas in src/state/<name>/schema.rs, so a malformed entry never lands on disk.

As the cycle runs, a few more files appear in the same directory:

  • session-log.yaml and latest-session.yaml — the analyse-work phase appends one record per completed phase to the log and overwrites the latest-session pointer each cycle.

  • work-baseline, reflect-baseline, dream-baseline, triage-baseline — per-phase baseline SHAs captured so each phase has something to diff against.

  • commits.yaml and subagent-dispatch.yaml — written by triage when committing or when dispatching cross-plan subagents.

A project can host multiple plans. Each is its own directory under LLM_STATE/ and runs its own cycle independently. For this tutorial we will run a single plan called main. The full schema for each file lives on the State files reference; the current chapter only needs the high-level shape so the next chapter’s create command makes sense.

With the project directory in place and registered, you are ready to bootstrap the plan — the next chapter walks through ravel-lite create.