Skip to main content
  • Version 0.1.0
  • Purity pure zero-dep (browser-safe; no workspace or external deps, no node:*, no I/O)
  • Purpose Declare the seven task statuses and the legal-transition table once, so the durable board, the orchestration engine, and the browser board UI all read the same rulebook instead of hand-maintained copies that can silently drift.
  • Workspace deps none
  • External deps none
Three layers need these rules and only one of them can touch a database:
  • @clawboo/db re-exports the module and enforces canTransition inside the write transaction, against the freshly-read row.
  • @clawboo/team-orchestration types its BoardClient surface with TaskStatus.
  • The board UI (apps/web/src/features/board) derives its columns and its manual status editor from TASK_STATUSES + legalTargets, so it only ever offers moves the server will accept.
Before this package existed, each of those declared its own copy. They agreed, but nothing linked them: a newly-legal transition on the server would have left the UI hiding a move it now accepts, with green CI. Extracting the rules made drift a compile error rather than something a test has to notice.
The module is import-free, and that is load-bearing rather than incidental: it is what lets the same file ship into the Vite SPA without dragging the sqlite/server graph along. Two tests pin it — a source guard in this package asserts state-machine.ts declares no import at all, and apps/web/src/__tests__/browserBundlePurity.test.ts asserts the built artifact declares zero bare specifiers in either module format.
The server stays the authority. Any UI or REST-layer pre-check is fast-fail ergonomics; the transactional check in updateStatus is the real gate, and an illegal transition surfaces as a 409.

Public API

Functions

Types & constants

The transition table

done and cancelled are terminal. in_progress → todo is the “release” path orphan reconciliation uses.

Dispatch rules

Whether a todo task will ever be picked up on its own, from two durable facts the board already stores. The engine’s ready-pump (@clawboo/team-orchestration) and the server’s pump scan (@clawboo/db isLedgerAutoFireable) both call these, so they cannot disagree about what will run.

Needs-you classification

taskAttention({ status, sourceDelegationId, execs, verificationBlocked }): TaskAttention | null says why a task is waiting on a person, or null when the board will move it on its own. The server attaches the result to every task it returns (the attention field), and the board UI routes every task that has one into its Needs you column. TaskAttention is { reason, failedRuns, detail? }, where reason is an AttentionReason.

Source

packages/board-core/src/state-machine.ts, dispatch.ts and attention.ts, plus a barrel. See Board internals for how the repository enforces the transitions and The board for the concepts.
Last modified on August 3, 2026