Beans and todos
On this page
This page is generated from docs/source/guides-beans-and-todos/ — each section below links to its own source.
Beans are for agents. Todos are for humans. They share a word in ordinary speech and nothing else, and conflating them is how a work plan comes to hold two kinds of thing that need different handling.
A bean is process state an agent keeps while doing work: claimed before
starting so a sibling session avoids it, kept current as the work proceeds,
and closed with a record of what changed. The store is beans/, committed,
so it survives a reclaimed container and is visible to every other session.
A todo is a person’s own reminder — what they mean to look at next. It is theirs to create, reorder and discard on no schedule but their own, and nothing about an agent’s process should be deciding when it is done.
This page describes the first in full. The second is not built yet and says so.
Two things that share a word
The distinction is not terminology. The two have different owners, different lifecycles and different failure modes.
| Bean | Todo | |
|---|---|---|
| Owner | An agent session | A person |
| Purpose | Process state for work in flight | A reminder of intent |
| Store | beans/, committed and shared | Not yet defined |
| Created by | An agent, after an exact-title check | The person, whenever they like |
| Closed by | The agent that did the work, with a summary | The person, on their own judgement |
| Visible to | Every sibling session | The person, primarily |
| Failure mode | Duplicated, or closed by the wrong session | Forgotten |
The failure modes are what make the split worth keeping. A duplicated bean starves the work plan of signal — in one folio an unguarded re-run produced 14,688 duplicates, 92 % of every open bean in the repository. A forgotten todo inconveniences one person. Machinery sized for the first is ceremony on the second, and machinery sized for the second is nowhere near enough for the first.
The agent bean lifecycle
The diagram above is the whole cycle, and three of its edges are the ones worth reading twice.
The first step is a search, not a creation. beans create is not
idempotent: it mints a fresh identifier on every call and de-duplicates on
nothing, so re-entering a step duplicates the plan rather than doing nothing.
The exact-title check is what stands between a repeated workflow step and a
corpus of near-identical beans.
Ownership is a gateway, not a courtesy. A bean another session claimed is not yours to resolve, scrap or delete. Claiming is how two sessions avoid picking up the same item; closing someone else’s is how one of them loses work it had not finished reporting.
“Blocked” is not “not wanted”. A blocked bean returns to todo with its
blocker recorded and something said about what would clear it. Leaving it
in-progress is worse than either: an item nobody is progressing reads, to
every other session, as active work.
A bean is never deleted
beans delete exists in the CLI. Do not use it.
Work that turns out not to be wanted is scrapped — status: scrapped,
with a section saying why. That is what “disable” means here; the CLI has no
disabled state, and its vocabulary is draft, todo, in-progress,
completed, scrapped.
The reason is not tidiness. A scrapped bean records that something was considered and rejected, and on what grounds — which is worth as much to the next agent as the record of work that was done, because it is what stops the same dead end being re-entered. A deleted bean leaves a sibling session unable to tell abandonment from accident, and leaves the next agent free to start over on the thing you already ruled out.
The prohibition is written here, and in the diagram’s own documentation, precisely because the tool offers the operation. A rule that depends on nobody noticing a command is not a rule.
Engine operations are not CLI calls
Two different things can change a bean, and a step that confuses them will announce an effect it does not have.
The workflow engine performs claim, note and resolve on a process
instance’s own bean when a step marked <cat-harness.processes:bean op="…"/> completes.
claim sets in-progress and is idempotent; note appends; resolve
completes the bean only once the instance itself has completed, because
whether work is done is a judgement and a bean is not closed on someone else’s
say-so.
The agent does everything else with the beans CLI: creating, scrapping,
retitling, setting a blocker. The engine has no operation for any of these.
This is not a hypothetical distinction. An activity in the CRDM diagram
carried <cat-harness.processes:bean action="create"/> — action, where the engine reads
op — so it parsed as no operation at all. The step advertised that sign-off
produced beans, performed nothing, and did so silently until somebody read the
parser.
When you add a bean-marked step, use one of the three operations the
engine implements, and say in the step’s documentation which parts are the
agent’s own CLI calls. bun run cat check:workflow-refs will catch a skill
reference that does not resolve; it cannot catch a plausible attribute the
engine never reads.
Human todos — not built yet
Not built yet, and stated here rather than left as an absence somebody has to notice.
A person’s todos need a cycle of their own, and none of it exists:
- When a todo is shown. Surfacing every open item on every turn is noise; surfacing none is a list nobody reads. Whatever the rule is, it is not the same as the work-plan priming an agent gets at session start.
- When a person may create one, and through what — chat, a command, the session-start surface.
- How one is retired. A person abandoning a reminder is ordinary and needs no “reasons for scrapping” section; the bean rules do not transfer.
Until that is designed, do not repurpose beans for it. A store whose entries are claimed by sessions, closed with change summaries and never deleted is the wrong shape for a reminder somebody wants to drop without ceremony — and mixing the two would put items in the agent work plan that no agent should be acting on.
What the work plan holds right now
The rest of this page says what a bean is. This section says what the
store currently holds — read live from
/assets/beans/index.json, the projection
gen-docs-pages.ts writes from beans/defs/ on every build, beside the todo
index it has published for longer.
Until 2026-09-20 there was nothing to put here. todos/ had a reader, a
published index and a board over it; beans/ had none of the three, though it
is the larger store by two orders of magnitude. A work plan nobody can look at
is one nobody checks.
What the dashboard shows, and why each panel is the shape it is. The
counts are a headline, so they are numbers rather than a chart of four bars
saying the same thing. The work plan itself is a tree, goal → epic →
feature → story, because the question a reader brings is “where does this sit”,
and a bar per epic answered “how many” and listed one level. The type filter
and the search prune that tree to what matches and open it down to them. The
store’s task type is shown as story, since a Task here is a BPMN task.
The findings are three or four
rows, so they are a list — with an icon and a label on every row, because a
status colour never carries meaning alone.
The findings are computed from committed data only, and one of them is
missing on purpose. A stale in-progress detector would have to read the
clock, and the projection is gated on exact content — anything computed
against the clock changes the file on every build and the staleness gate then
fires forever. So the projection publishes each bean’s updated_at as a fact
and the page computes age when you open it. Age therefore moves without the
file moving, which is the right way round.
There is a fuller view, and it is browsable. Every declared state graph
gets its own visualiser at the path this instance already uses for that graph —
/beans/, /todos/, and one for each of the others. The
route is not this page’s invention: bean o7eq carries the owner’s ruling that
a rendered asset is addressed by its instance’s name with the declared
graph as a path segment, and that this instance elides its own name because
its docs/ is what the site serves, and the harness-requirements skill
names that exact URL as the obligation — “a requirement of them is to provide
visualisers accessible at <base-url>/beans“. There is no index above them,
because / is the documentation site’s, so each page lists its siblings.
A graph with no projection says so. qa, health, issue-marks and
uploads are declared state graphs that nothing publishes a projection for
yet. Their dashboards say exactly that and name bean 2krx, rather than
rendering zeros for a store nobody read — a dashboard that opens at zero is
indistinguishable from a store with nothing in it, and those are opposite
facts.
Every identifier on those pages is a link. A bean id resolves to the bean’s file, an issue or pull-request number to the forge, and each finding carries a pencil to where writing actually happens. A reference that cannot be resolved stays as plain text, which is the visible difference between “follow this” and “this points somewhere I could not reach”.
What is not here. Where a bean sits in a BPMN process, which is the other
half of bean v49e. That join needs beans/workflows/, the declared
workflow-state graph, and it is empty — so a position view would report
“nowhere” for every bean in the store. It waits for instance state to exist.
The live work-plan dashboard needs JavaScript. The projection it reads is a plain JSON file and can be read directly.