Project memory, versioned with the code.
Keep intent, work, and decisions as Markdown in your repository, under a schema you own. People and agents work the same record. Git carries its history. No required server or account.
curl -fsSL https://raw.githubusercontent.com/oddurs/cairn/main/install.sh | shOr brew tap oddurs/cairn && brew trust oddurs/cairn && brew install cairn. Linux, macOS and Windows.
Use Harrow beside your editor or agent to watch the work and decide what needs attention. It reads the same files and makes changes through Cairn.
---
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
- [ ]See the whole thing at a glance
Columns come from the statuses you declared, in the order you declared them. Width follows content, so an empty column does not take a full share. A leading ! marks work that cannot be started because something else is unfinished.
backlog 2 planned 1 in progress 1 blocked 0
───────────────────────────── ────────────────────────────────── ─────────────────────── ─────────
!0004 p1 Rate-limit the publ… 0005 p1 Board shears on narrow t… 0003 p0 Support OAuth…
0006 p2 Document the export…
3 ready · 1 in progress · 1 blockedThe loop is two commands
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 blockedWork 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.
claimed 0003 Support OAuth login
you · doing · cairn/items/0003-support-oauth-login.mdTakes 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.
Recorded, not illustrated
Every terminal on this page — the file, the board, the listings, the rejected tool call — was captured by running the real thing. So was the session below. All of it is regenerated on each change, and continuous integration fails if any of it stops matching what the program does.
Agents get tools, not instructions
cairn mcp serves the backlog over the Model Context Protocol. A rejected write comes back as something the model can act on, so it corrects itself instead of inventing a format.
update_item {"id": 3, "fields": {"status": "in progress"}}
unknown status `in progress`
known: backlog, planned, doing, blocked, done, droppedBuilt to be left alone
Writes cannot be half-finished
Every file is written beside its target, flushed, and renamed over it. A crash leaves the old contents or the new ones, never a mixture. There is a test that kills the program mid-write forty times.
Work travels with the branch
Review the item beside the code it describes. Git integration helps reconcile generated output and identifier collisions; conflicting decisions remain visible for a person to resolve.
The format is specified
The item format is written down independently of this program, versioned, and pinned by a corpus of files that must keep parsing the same way.
Concurrent writers are safe
Writers sharing an item directory take a lock. Forty processes creating items at once produce forty distinct identifiers. Separate branches and worktrees carry their own state, so assignments still need coordination.
Milestones, from the same files
The roadmap is derived from the items, so it cannot describe a plan nobody is working on.
Nimbus
v0.1 First usable version
[--------------------] 0% 0/2 due 2026-12-01
v0.2 Hardening
[--------------------] 0% 0/2 due 2027-02-01