Phases
The model, before anything runs: what a phase card is made of, how membership is written down, why it runs one direction only, and what the board flags when the list does not resolve.
Some work is a run of related cards rather than one card: three that have
to land in order, each building on the one before it. A phase is how
bench holds that — a card that lists its cards, an integration branch of
its own, a beat that works the list one member at a time, and a single PR
into main at the end. Everything else on the board reaches it by
inheritance, because a phase card is a card.
A phase is a card that lists its cards
A phase is a group of related tasks meant to run one after another. It is
not a directory, a stage or a registry — it is a task card like any other,
marked **Type:** Phase, with a ## Cards section naming its members in the
order they run:
## Cards
- 31 — Stand up site/ and its build
- 32 — Serve it from a Cloudflare Worker
- 33 — The landing page
The name of the phase is the card itself, its number and title, so nothing is
named twice. Document order is run order. The number is what is parsed — 31,
#31 and 031 are the same card — and whatever follows it is for the reader,
never matched against anything.
Membership runs one direction only: the phase card lists its members, and a
member card says nothing about phases. So membership cannot disagree with
itself, there is exactly one place to edit when it changes, and a member's
phase and position are derived — a ⟶ <phase> 3/5 chip in the card's footer
row, beside CI and PR ↗, opening the phase card.
What the list does not resolve is flagged on the card, in the same spirit as
status drift: a number no card has, a card two phase cards both list (both
are flagged), a card one phase lists twice, a line naming no number, and a
phase listed by a phase — phases do not nest. Each is an authoring mistake
that would otherwise surface later as a runner behaving oddly.
**Depends on:** is the other half, and it guards rather than orders: the
list says what runs next, a member's dependencies say whether it may. The
board parses the numbers out of the line and shows them; acting on them
belongs to the runner below.
A phase is normally written whole — members listed and each member's
dependencies filled in — before anything reaches the board, which is what
makes it a thing you can read in a diff. For the card you decide belongs
after all there is ⟶ phase, on backlog/ and to-do/ cards that are
not already in a phase and are not phase cards themselves. It opens a
sheet naming the phase cards in to-do/ and what each already holds, and
picking one appends - <n> — <title> — the way a person writes it — to
the end of that phase's ## Cards. Nothing else moves: the card stays in
its stage, because joining a phase is not a commitment to start it. The
append goes through the board's own write path, so it commits itself under
BOARD_COMMIT_MOVES and reaches the other boards; an addition that never
left one working tree is not an addition the phase would run. Only phases
in to-do/ are offered — one in in-progress/ is running, its members
being worked in the order the list had when it started — and with no phase
waiting there the action is absent rather than present and empty.