The board

Ordering and relations

How blocked_by, deploy_depends_on, derived_from and discovered_from order and connect tasks.

Tasks can point at each other in four ways. Two of them are enforced order; two are provenance only — a record of where a task came from, which nothing gates on.

RelationDirection (as you set it)EnforcesSet by
blocked_bythis task waits for the ones you listWork order — parks the task until they're finishedYou, or an agent, at creation or update
deploy_depends_onthis task ships after the ones you listShipping order — refuses the release until they're liveYou, or an agent, at creation or update
derived_fromthis task was opened out of that analysisNothing — feeds the analysis's documents into the runAn agent, when opening implementation work from an approved analysis
discovered_fromthis task was found while working that oneNothing at allAutomatically, whenever a task is created from inside a run

All four point the same way — "this task comes after / came from the ones you name" — so nobody has to reason about which end of a relation they're holding.

blocked_by — work order

Set blocked_by on a task to name other tasks that must be finished (Done or Released) before this one may start. It's enforced in two places:

  • Dragging a card into Todo or In Progress by hand is refused while an open blocker remains.
  • If the task reaches a working column some other way — created straight into Todo, handed back by a sweeper, assigned — the dispatcher parks it on Blocked the moment it would otherwise start a run, with a comment naming every unfinished blocker.

A background sweep checks once a minute whether every blocker for a parked task has finished (or disappeared — deleting a task removes its relations with it) and releases the task the moment they have. Two parked tasks waiting on different blockers are independent: whichever one's blockers finish first is released first.

blocked_by adds to the existing set rather than replacing it — a blocker one planner learned about is never silently dropped by a second planner adding another.

Internally this is stored as a blocks relation with the blocker as the source and the waiting task as the target — the opposite direction from deploy_depends_on — but no tool or field ever asks you to think in that direction; you always name blockers through blocked_by.

deploy_depends_on — shipping order

Set deploy_depends_on to name tasks that must be live in production before this one may be released — for a real shipping-order dependency, like an API that has to exist in production before the client calling it can go out. Two tasks can be developed fully in parallel (neither blocks the other's work) and still have a hard deploy order.

Releasing a task is refused while any of its deploy dependencies lacks production evidence — a successful production deploy, a successful staging deploy on a repository with no production workflow mapped, or having reached the Released column itself — and the release is refused with the blocking keys named, not just a generic failure.

Unlike blocked_by, setting deploy_depends_on replaces the whole set: the release path always reads it as one complete statement, so a caller editing it is expected to know the whole set it's declaring.

derived_from — the analysis a task came from

Set on an implementation task to name the analiz task it was opened out of. This is provenance, not order — the human already approved the plan in Analiz Review before the implementation tasks were even created, so gating on it again would be redundant. What it actually does: the named analysis's documents (its spec and plan) are read into every run of this task automatically, and list_task_documents can re-read them at any time. Since an analysis's deliverable exists only as documents on that task — never a file on any branch — this relation is the only route an implementer has back to the specification they're building against.

discovered_from — found along the way

You never set this yourself. When an agent opens a new task while working on an existing one — something it ran into but wasn't asked to do — the new task is linked back to the one the run was working on automatically, with no tool argument involved. It's weaker than derived_from: "found while working on," not "specified by." It orders nothing and feeds no documents into anything; it's a trail to follow later, nothing more.

Cycles are refused where they're written

Both ordering relations refuse a cycle the moment the edge that would close it is written — at creation or update, not later at release time — and the error names the chain that closes it, for example:

deploy-order cycle refused: T-2 already ships after T-1 (T-2 → T-1)

A task also can't depend on itself.

Seeing both directions

A task's own detail view shows the relations it is the source of (what it ships after, what it blocks, what it was derived from) and, separately, the blocked_by edges pointing at it (who is waiting on this one, with their key and title) — a single-task view carries both; bulk board lists carry neither.

The generated ordering block

before_deploy keeps a block TaskTrooper generates and regenerates on its own whenever the relations change, fenced so your own notes around it survive:

<!-- tt:order -->
**Release order (generated from this task's relations — do not edit by hand):**
- Ships after: T-1 (task export endpoint). Each one must be live in production before this
  task is released; the release is refused otherwise.
- Built after: T-1 (task export endpoint). Work on this task does not start until those are done.
<!-- /tt:order -->

The same text, with the markers stripped, is what gets posted as a comment when the release is actually dispatched.

Why two ordering relations, not one

blocked_by and deploy_depends_on look similar — both mean "after those" — but they answer different questions, and a real project needs both answered separately: two tasks can be written in parallel with neither blocking the other's code (no blocked_by needed) while still having to ship in a fixed order (an API before the client that calls it, via deploy_depends_on). Collapsing them into one relation would force every task with a shipping order to also wait to start, even when there's no reason it couldn't be built alongside its dependency.

Setting relations from a task

All four relations are reachable wherever a task's fields are: the create dialog's deploy section for deploy_depends_on, and an agent's own tools (blocked_by, deploy_depends_on, derived_from arguments on the task- creation and update tools) for the rest. A relation always resolves either a task's UUID or its board key (T-12), so you never need the UUID on hand to reference one.

The ready queue

list_ready_tasks is the queue agents check for unblocked work: every task in Backlog or Todo with no unfinished blocked_by blocker, sorted critical → high → medium → low and then oldest first. A task parked on Blocked never appears in it — it's excluded before the blocker check even runs.

Edit this page on GitHub