cairn

Getting started

Install it, create a project, and work through the loop it is built around — find something to do, take it, finish it.

Install

A prebuilt binary, with no Rust toolchain needed:

curl -fsSL https://raw.githubusercontent.com/oddurs/cairn/main/install.sh | sh

It detects your platform, verifies the release checksum and installs to ~/.local/bin. Homebrew takes three commands, because recent versions require third-party taps to be trusted explicitly:

brew tap oddurs/cairn
brew trust oddurs/cairn
brew install cairn

From source, cargo install --git https://github.com/oddurs/cairn cairn-md.

Create a project

$ cairn init
created cairn.toml
created cairn/items/

That writes the schema and the directory items live in. Run it at the root of a repository — items are meant to be committed alongside the code they describe.

cairn init --preset minimal --bare starts from a small schema without example items. The default writes a fully commented schema to adapt to your project.

The loop

Four commands carry the day-to-day. Everything else is machinery underneath them.

$ cairn next
ID    PRIORITY  STATUS       MILESTONE  TITLE
0003  p0        in progress  v0.1       Support OAuth login
0005  p1        planned      v0.1       Board shears on narrow terminals
0006  p2        backlog      v0.2       Document the export format

3 ready · 1 in progress · 1 blocked

Work that is genuinely startable. Anything waiting on unfinished work is excluded, and what is already under way comes first — the most useful next action is usually finishing something.

$ cairn claim --next
claimed 0003  Support OAuth login
  you · doing · cairn/items/0003-support-oauth-login.md

Takes the top-ranked unclaimed item, writes your name into it, and prints its body so work can begin. It refuses an item somebody else holds, and one that is blocked.

Then record what you learned and finish:

$ cairn note 3 "Chose the authorization code flow; implicit is deprecated."
$ cairn close 3

3, 0003 and the project’s own rendering all name the same item.

cairn note appends to the item under a dated heading. A status records what was decided; a note records why, which is the difference between a backlog and a list of things nobody remembers rejecting.

next excludes unfinished dependencies; it does not decide which ideas your project has approved. If your schema uses planned for selected work, scope an agent’s claim with cairn claim --next --filter 'status=planned'.

Harrow is the optional terminal counterpart. Run it beside an editor or agent to watch the same backlog and make interactive decisions. It reads the files directly and delegates changes to Cairn.

What gets written down

An item is a Markdown file, and that is the whole storage format.

cairn/items/0003-support-oauth-login.md
---
id: 3
uid: 00000003-0000-4000-8000-000000000003
title: Support OAuth login
type: feature
status: doing
milestone: v0.1
assignee: you
claimed: 2026-09-05
created: 2026-09-05
updated: 2026-09-05
priority: p0
---

## Problem

## Proposal

## Acceptance criteria

- [ ]

Everything above the closing --- is checked against the schema. The body is yours; conventions such as acceptance checkboxes and proposals let tools help you work it. Edit the file in your editor or from the command line — both keep working, and changing a title normally renames the file to match.

The generated roadmap

ROADMAP.md is built from the items, and rebuilt whenever one changes. Never edit it by hand.

$ cairn render
wrote ROADMAP.md  14 items
$ cairn check
cairn: cairn/items/0009-bad.md:4: unknown status `nope`

failed: 1 error(s), 0 warning(s) across 7 item(s)

cairn check --render --strict belongs in continuous integration. It validates every item against the schema and proves the committed roadmap still matches them, which is what stops a roadmap from quietly becoming fiction.

Where to go next

  • The schema — item types, statuses, fields and milestones, and why status categories matter.
  • Agents — serving the backlog over MCP, and the loop an agent follows.
  • Durability — what happens on a crash, a merge, or two people writing at once.