# Plans, phases, and the frontier


A plan in cairn is a graph of tasks, not a document. There is no `plan.md` that an agent reads, edits, and re-reads until it drifts from the work. The plan is the root task, the phases are its children, and the ordering between phases is an edge type the query language understands. Because the plan is structure rather than prose, cairn can answer a question a markdown file never could: of everything left to do, which tasks are ready *right now*. That answer is the frontier, and it is the single question an agent asks at the top of every working session.

This matters because the reader resuming a plan is usually a fresh context. A flat checklist forces that fresh context to re-derive what blocks what; a graph carries the dependencies as data, so the next task is computed, not remembered.

## A plan is a root task with phase children

The plan is a [task](/cairn/concepts/tasks-observations-handoffs#the-task-a-node-with-a-date-prefixed-slug) — a node in the [task graph](/cairn/concepts/the-task-graph) — that you grow phases beneath. You create the root with `task_create`, then break it into phases with `task_fork`. Each fork records a `phase-of` edge from the child up to the parent, so the plan's shape lives in the graph the moment you build it.

`task_create` takes one required argument, `name`, and mints the slug from it; the slug carries today's date as a prefix.

```text
task_create(name="Migrate the auth service to the new token format")
-> Created 2026-06-22-migrate-auth-service.
```

`task_fork` carves a phase off whatever task it forks `from` (the parent slug; it defaults to the current task), and its `edge_type` defaults to `phase-of`. The parent argument is `from` — there is no `parent` or `parent_id`.

```text
task_fork(name="Add the new token codec", from="2026-06-22-migrate-auth-service")
-> Forked 2026-06-22-add-the-new-token-codec from 2026-06-22-migrate-auth-service (phase-of).
```

A phase is a phase because of its `phase-of` edge, not because of anything in its description. `phase-of` is a *structural* edge: in the store it is folded into the parent foreign key, so every task has at most one parent and the phase backbone is always a tree. That single-parent rule is what lets the plan view start from one task and walk straight down to its phases. See [The task graph](/cairn/concepts/the-task-graph#edges-come-in-two-classes) for why structural and lateral edges are stored differently.

A phase is meant to be an independently verifiable unit of work — something that can move from `open` to `completed` on its own, with its own acceptance, while the rest of the plan stays put. Phases that bundle three unrelated outcomes are hard to mark done and harder to resume into. Forking one phase per checkable result keeps the frontier honest.

## depends-on edges order the phases

Phase-of says *what belongs to the plan*. It says nothing about *what comes first*. Ordering is a separate, lateral edge: `depends-on`, added with `task_link`.

```text
task_link(target_id="2026-06-22-add-the-new-token-codec", edge_type="depends-on", task_id="2026-06-22-cut-over-the-verifier")
-> Linked 2026-06-22-cut-over-the-verifier -> 2026-06-22-add-the-new-token-codec (depends-on).
```

Read `A depends-on B` as "A cannot proceed until B is settled." A `depends-on` edge is lateral — it lives in the edges table, not the parent key — so a phase can depend on any number of others without disturbing the phase-of tree. The two edge types compose: phase-of builds the plan's spine, depends-on threads ordering across it. The closed edge vocabulary accepted by `task_link` is `phase-of`, `depends-on`, and `related`; see [Edges, statuses, and fields](/cairn/reference/edges-statuses-and-fields#edge-types).

Keeping the backbone and the ordering on different edge types is deliberate. You can restructure the plan (re-parent a phase) without touching the dependency order, and you can re-order work without re-parenting anything. Neither operation forces a rewrite of a prose plan, because there is no prose plan to rewrite.

## The frontier is the ready subset

Two named [views](/cairn/reference/views) answer the two questions a plan poses. `plan` returns the whole plan scoped to where you are standing. `plan-frontier` returns the subset of that plan that is ready to work.

The `plan` view is current-scoped: it reads from the [current task pointer](/cairn/concepts/the-current-task-pointer) and walks one hop down the phase-of backbone to the phases, falling back to your sibling phases when the current task is itself a phase.

```lisp
(-> (current) (:follow :phase-of) (:or-else (-> (current) (:back :phase-of) (:follow :phase-of))) (:enrich))
```

`plan-frontier` filters `plan` down to the tasks that are both unfinished and unblocked:

```lisp
(-> (query "plan")
    (:where (and (not (or (= :status "completed") (= :status "abandoned")))
                 (all (:follow :depends-on)
                      (or (= :status "completed") (= :status "abandoned"))))))
```

Read that predicate as the frontier rule, in two clauses:

- **Not yet settled.** The task's own status is not `completed` or `abandoned`. A settled task is behind you, not ahead. (`completed` and `abandoned` are the *dormant* half of the status enum; `open`, `active`, and `blocked` are the *active* half — see [Edges, statuses, and fields](/cairn/reference/edges-statuses-and-fields#statuses).)
- **Every dependency settled.** Walking each `depends-on` edge forward, *all* the tasks it points to are `completed` or `abandoned`. A phase with one unfinished dependency is not on the frontier yet — its turn comes when that dependency settles.

A task with no `depends-on` edges trivially satisfies the second clause: `all` over an empty set is true. So the frontier of a fresh plan is exactly its unblocked entry points. As you complete phases, the frontier advances to the phases they unblocked. You never compute this by hand; you ask for it.

`frontier` is an alias — the view `frontier` is defined as `(query "plan-frontier")`, so either name returns the same ready subset.

## The shape of working a plan

With the plan in the graph, an agent's session has a fixed shape, and the shape is the point. The loop is a frontier query, a completion, and a re-query: ask which tasks are ready, settle one of them, and ask again. Nothing in between edits a list. The loop runs over [task_query](/cairn/reference/tools#task-query).

The frontier query inherits `plan`'s enrichment, so each line carries its inline counts — `obs=N edges=N` — after the status:

```text
task_query(query="(query \"plan-frontier\")")
-> 2 tasks:
- 2026-06-22-add-the-new-token-codec (open) obs=0 edges=1
- 2026-06-22-write-the-migration-guide (open) obs=2 edges=0
```

A completion is a single `task_update_status` against any task on the frontier:

```text
task_update_status(status="completed", task_id="2026-06-22-add-the-new-token-codec")
-> 2026-06-22-add-the-new-token-codec is now completed.
```

The re-query is the same call as before, and the phase that depended on the one just settled now appears — the frontier moved without anyone editing a list. This is the same heartbeat the [cairn-method](/cairn/concepts/the-cairn-method) wraps in research, plan, implement, and validate discipline. For the step-by-step version on a toy plan, see [Plan and resume](/cairn/get-started/plan-and-resume).

## When a plan graph is overkill

A plan graph earns its keep when work outlives one context: many phases, real ordering between them, and a resume that has to recompute what is ready. For a single task you will finish in one sitting, that machinery is friction. Create the task, observe against it, and skip the phases — a one-node "plan" has a frontier of one, which is just the task itself. Reach for `task_fork` and `depends-on` when the ordering is something a future context would otherwise have to reconstruct from memory.

## Related

- [Plan and resume](/cairn/get-started/plan-and-resume) — build the root, fork phases, order them with `depends-on`, and resume the plan
- [The task graph](/cairn/concepts/the-task-graph) — the two stores and the structural-vs-lateral edge split
- [The current task pointer](/cairn/concepts/the-current-task-pointer) — why `plan` and `plan-frontier` are current-scoped
- [Views](/cairn/reference/views) — the built-in views `plan`, `plan-frontier`, `leaf-tasks`, and `stale-phases` with their TQ source text
- [The TQ language](/cairn/reference/tq-language) — the query language the frontier rule is written in
