Flow
Menu

Start

How Flow works

The records Flow keeps, what it computes from them on every read, and the two writes it refuses.

Flow keeps a small number of records and derives everything else from them each time they are read. Knowing which is which is most of knowing how to use it: what you write is an assertion somebody has to keep true, and what Flow computes cannot go stale.

What Flow keeps

A project (proj_…) is where work lives and who can see it: a product, a client, a household. Every task names its project on the call that creates it, and there is no current project to fall back on. Sharing a project shares everything in it and nothing outside it. A project you are not a member of answers as if it did not exist.

A task (task_…) is one piece of intent. It carries:

  • a status, the answer to “is this a commitment, and what holds it”: active, waiting, calendar, blocked, or one of the non-commitments and closed states. The full list, and which moves are allowed, is in the vocabulary;
  • a kind. Most tasks are commitments. An outcome that needs several steps is kind project and carries no work of its own. A decision records a choice, including a standing rule for agents. The rest are in the vocabulary;
  • an optional priority: very_low, low, medium, high or very_high, listed in the vocabulary. Most tasks have none;
  • tags, which group work and hold nothing up;
  • when it was last confirmed, and, for work, when it is due for review. A record (evidence) has no review date and no readiness, because there is nothing to start or confirm about it.

A capture (cap_…) is something that arrived and has not been decided: a sentence, an email, a webhook, verbatim, with its source. Captures sit in your inbox, which is personal. A capture is not a task and never becomes one. Triaging it creates a task that points back to it, and the capture stays as it arrived. See Capture.

An edge links two tasks with a kind from a closed set. blocks is the only kind that holds work up; the others record support, decisions, supersession and plain mentions. Edges are many to many, and there are no sub-tasks: an outcome is reached when the work that blocks it is done. See the link kinds.

A reference points outside the graph, at an issue, a pull request, a commit, a file or a URL, with a role: source, spec, implements or context. A reference is its URL, and any number of tasks may cite the same one: an issue that specifies four phases is four tasks, each referencing it. One piece of intent across three repositories is one task with three references, not three tasks.

A wait says what holds a task that no other task blocks. A scheduled wait carries a date and clears itself. An external wait names who or what you are waiting on, and may need chasing: its until is the date to chase on.

A change is kept for every move of a task’s status, priority or holder: who made it, from what to what, when, and the cause it named, which is a record’s id, a URL or the person’s own words (P4). Changes are only ever added. get-task lists them, oldest first, so “why did this stop, and who agreed?” is a read.

What Flow computes

Readiness is derived from the edges and waits on every read, and never stored. A task is ready when it is active, nothing open blocks it, and no wait holds it. A task that is not ready says why: blocked:dependency (another task holds it, the graph will clear it), blocked:external (chase someone) or waiting:scheduled (a date will clear it). A blocker in a project you are not a member of still holds your task; it is reported with no id or title, because naming it would reveal a project you cannot see.

The ready set is every ready task, ranked by the priority you asserted first and then by leverage: how many open tasks finishing this one would free. get-next answers its top task, one at a time, and more when asked. Asked by slice, the same call answers the reviews that are due and the drift: anything whose status contradicts what the graph can see.

The order is the blocks graph in waves. Everything in one wave can run in parallel, so the size of the first wave is how many agents can usefully start now. get-order answers it, and reports any cycle it finds.

What is due for review comes from each task’s review date: open work nobody has written or confirmed for a week, and incubated or reference tasks nobody has for a month. Priority orders the ready set and sets no review date. get-next slice=review answers it. get-next, asked for its recovery slices, answers the question after a gap, and validate-project lists what the graph contradicts about itself.

What Flow reports, and the two things it refuses

Flow accepts writes and reports what it noticed. A blocks cycle is stored and reported. A link to work nobody has captured comes back unresolved, and nothing is invented to fill it. A change to a commitment that names no cause is written and flagged needs_reconciliation until somebody supplies one with reconcile-task.

The first refusal is a status move the state table does not allow: reopening a closed task in place, closing an incubated idea as completed or cancelled when it was never a commitment, or moving reference anywhere but archived. Between the open statuses of work, any move is allowed. It answers 409 invalid_transition with the moves that are allowed, so the next call can be the right one.

The second is a calendar task with no date. calendar says a date holds the work, so creating one, moving a task onto it, or taking its date off without a scheduled wait carrying until answers 400 calendar_requires_date. The same call with the date lands.

Who can reach what

  • A client you connect, such as Claude Code, registers itself and asks for your consent the first time. On the consent screen you can restrict it to some of your projects, and it never sees the others.
  • An API key is for an agent that runs unattended, such as CI or a scheduled job. Give each agent its own, so the one that leaks can be revoked alone.
  • An ingest endpoint is a URL an app, a mailbox or a bot posts to. It can write a capture into your inbox and do nothing else. See an endpoint your app posts to.

Every task, project and capture has an id like task_x7kp2mq4vb, assigned by Flow and never chosen by the caller. It is the only thing any call uses to name a record.