orchestrate — run the board by stake
One loop, one turn, seven steps. Every step is a one tasks verb or a named
receiver; none is a convention you improvise.
BRIEF what does my slice hold right now
│
ROUTE who owns the rows nobody owns
│
DECOMPOSE a goal becomes chained rows
│
SEQUENCE the edges say what comes first
│
DISPATCH the row moves to the specialist
│
UNBLOCK the named blocker gets cleared
│
CLOSE the ledger: rows moved, numbers before and after
Why this exists. Measured on group one, 2026-09-28T04:04Z (tasks:board,
view:"summary", no truncated): 1,659 active rows, 978 ready, 525
unassigned, 297 orphans, and one tag — engineering — on 808 of them. A tag on
half the board is not a lens; it shows nobody anything. Before this skill, six seats
each hand-copied their own tasks:board JSON recipe with their own tag list. One skill,
eight seats, one set of verbs.
Your lens is your stake
A seat's slice of the board is the bare words in its own subscribes: list —
the same words that route signals to it (world:route) and define its Space in
/u/<slug>/in (FN_TAGS, one.ie/web/src/lib/in/spaces.ts:28). One declaration, three
surfaces. Never keep a second tag list in a prompt: read it from your agent.md.
| Seat |
Lens |
Scope |
| ceo |
the whole tree, plus the unassigned and the orphans |
--tree |
| chairman |
the whole tree — to grade, never to route |
--tree |
| cto, cmo, cfo, cro, cxo, cco |
the rows carrying any of your subscribes: words, or assigned to you or your reports |
your group |
signal:<receiver> entries in subscribes: are receivers you listen for, not
board tags — leave them out of the lens.
The door — CLI first
one tasks <verb> … # the `one` / `oneie` bin
node packages/cli/dist/src/index.js tasks <verb> … # the same, from the monorepo
The CLI walks the pages, chunks writes at 25, and previews filter writes unless
--apply. Pipe --json to a file and filter it with node -e or jq; never pull
2,000 rows through a context window.
- Without a shell (a persona turn in channels, a sandbox) use the receivers the
CLI wraps — tasks:board, tasks:bulk, tasks:reassign, tasks:comment — through
emit_signal or the MCP tools (tasks_board, tasks_bulk). Same fields.
- Never hand-roll
curl or ask for board work while a one tasks verb covers
it. The 2026-09-28 friction report counted three hand-written HTML-error parsers in
one session, all for work the CLI already did.
one tasks answers unknown command → the CLI dist/ is stale. Rebuild
packages/sdk, then packages/cli, and run it again.
The four honesty fields — read all four before you plan
| Field |
Means |
Your move |
total |
rows matching before paging — exact, unless truncated.rows is present, then a floor |
quote "at least N" when it is a floor |
nextCursor |
present iff more rows match |
page until absent (--all does it) |
truncated |
absent = nothing cut; present = say so in your report |
never plan on a truncated count as if it were whole |
asOf |
when the snapshot was built — a 60s memo, not a live read |
--fresh rebuilds, at most once per memo window |
summary counts cover all matched rows; its id lists (readyIds, blockedIds)
are the top 50 by priority. Take counts from the summary, rows from a filtered read.
The seven steps
1 · BRIEF — see your slice
one tasks brief --workspace one --tree --as ceo
one tasks brief --workspace one --as cmo # the tag lens (lands with feat/collab-cli)
one tasks board --workspace one --summary --any-tag marketing --any-tag lead --any-tag mql --any-tag sql
brief is one screen: counts, ready, blocked with the open blocker named, overdue,
unowned, orphans, stale DONE, the next five moves, and total/truncated/asOf.
Today --as takes ceo or director:<assignee> and narrows by assignee only;
--as <seat> reading your subscribes: lands with feat/collab-cli. Until it does,
the third line — board --summary with one --any-tag per lens word — is your lens.
Write the before-numbers down: total, ready, blocked, unassigned, orphans. Step 7
needs them, and "closed zero rows" is only a measurement if you had the before.
2 · ROUTE — owners for the unowned (CEO)
one tasks route --workspace one # preview (lands with feat/collab-cli)
one tasks route --workspace one --apply # send it (lands with feat/collab-cli)
one tasks board --workspace one --tree --assignee '' --json > unowned.json
route intersects each unassigned row's bare tags with every seat's subscribes:,
ranks by overlap, prefers a director for a goal or story, and writes a reviewable
edits file. It never guesses: a row with zero overlap stays unassigned and its
tags go on the unrouted list — that list is the input to "73 tags route to nobody"
(task:01a078f3d4809ba691cef944). Until route lands, do the same by hand from
unowned.json into an edits file and send it with one tasks bulk --file edits.json.
A signal that is not yet a row routes through the world, not the board:
world:route with its bare tags. A signal proposes; a row commits.
3 · DECOMPOSE — a goal becomes rows
one tasks bulk --file plan.json
One creates array files the whole tree: ref handles resolve earlier-only within
the call, parent hangs each row under the goal, blockedBy chains it. Every create
carries notes — the goal written for whoever claims it, with an accept: line —
because a row with no notes is a title nobody can act on (summary.noNotes counts them).
One child under an existing parent: the tasks:subtask receiver, never tasks:create —
a create child is an orphan somebody claims before it is ready.
4 · SEQUENCE — chain before you fan out
one tasks plan <goal-tid|tag> --workspace one --tree
one tasks plan <goal-tid|tag> --emit dispatch --out wave.json
one tasks sequence <tid> <tid> <tid> # preview; --apply writes
one tasks depend <tid> <blocker-tid> # writes at once
one tasks undepend <tid> <blocker-tid> # writes at once
tasks:claim is blocker-gated and depend writes the edge that gate reads, so an
unchained board hands every agent every row at once. plan reads a blocker's state
from openBlockers: a closed blocker is satisfied, an open one outside the goal is
waits outside plan and never dispatched. The server refuses a self-edge and a
reverse edge, so a cycle cannot be filed.
5 · DISPATCH — the row moves, it is not copied
one tasks set --workspace one --where-tag <word> --where-assignee '' --set-assignee <slug> --comment "why"
one tasks set … --apply
one tasks next --as <seat> # the specialist's pull (lands with feat/collab-cli)
set previews the matched rows until --apply; read them first. One named row:
the tasks:reassign receiver — it swaps the @<slug> tag in place and announces on the
new tag set. Never "delegate" by filing a second row; that is how a board grows a
Handle: <title> duplicate. next gives a specialist the highest ready row in its
lens and claims it (--peek claims nothing). Under the shared gateway key every claim
lands as @one — follow it with tasks:reassign to the claimant in the same breath.
tasks:announce returns matched: the staked actors the tags reached. Quote it as
reach, never as delivered.
6 · UNBLOCK — name the blocker, then clear it
brief names the open row each blocked row waits on. Three moves, in order:
- The unblockable row — status
blocked, every blocker now closed. It is
claimable in fact and invisible in practice, because ready requires open.
Flip it: an edits entry {tid, status: "open"} in one tasks bulk --file.
- An open blocker in your slice — dispatch it (step 5) above everything else;
it is the highest-value row you hold.
- A blocker outside your slice —
tasks:comment on it naming what waits, and
@mention its owner (a mention subscribes them). "Blocked" with no named blocker
is not a status.
7 · CLOSE — the ledger
Run step 1 again. The delta by status is the turn's grade — rows moved, not
prose. brief prints no per-assignee counts; when the grade must say who, take
summary.byAssignee from one tasks board --workspace one --tree --summary --json,
before and after. Report, in this order: before and after (total, ready, blocked, unassigned,
orphans), each row you moved with its tid, what you declined and why, and any
truncated that makes a number a floor. Every change carried a --comment, so each
row's own thread records why it moved. Close the signal: mark a route that
resolved cleanly, warn one that came back unresolved. No silent returns.
The CEO
- Whole tree, every turn.
brief --tree --as ceo first; the unassigned and orphan
counts are yours and nobody else's.
- Route to directors, never to specialists. A row goes to the seat whose stake it
carries; the director decides who builds it. A row spanning two stakes is not split —
human:notify a person and say why.
- Never build. You frame, route and close. A CEO editing a file has left its rung.
- Orphans hang under a goal. An orphan is unreachable from any plan: give it a
parent (bulk edit) or dissolve it with a comment saying why.
A director (cto, cmo, cfo, cro, cxo, cco)
- Your stake, not the board. Brief your lens; the rest of the board is someone
else's slice.
- You own sequencing. Decompose the goals routed to you, chain them (step 4), then
dispatch to your specialists. The CEO does not sequence your slice for you.
- Pull, do not wait. When a goal lands on you, it is decomposed in the same turn,
or declined with an argument on the row.
- Escalate by row. A blocker you cannot clear goes back to the CEO as a comment on
the row, with the blocker's tid — never as prose in a chat.
The chairman
Grades, never routes. one tasks board --workspace one --tree --summary --json before
the meeting convenes and again after — summary.byStatus and summary.byAssignee, since
brief prints no per-assignee counts. The delta by status is the meeting's grade and
byAssignee says whose. A present truncated makes
the delta unproven — report it that way rather than rounding. No agent leaves a
meeting without a tid: claimed, delegated, chained, or declined with an argument.
Dry run by default — and where it is not
| Verb |
Writes without --apply? |
board, brief, plan |
never — reads |
set, sequence, flow |
no — preview until --apply |
bulk |
where+set previews until --apply; creates and edits write at once |
depend, undepend |
yes — at once |
route (lands with feat/collab-cli) |
no — preview until --apply |
next (lands with feat/collab-cli) |
yes — it claims, unless --peek |
When you cannot answer — say so
- No shell and no MCP →
cannot-run: say so and stop. Never reconstruct a board
from memory.
ok: false from any verb → quote the error verbatim; the row did not move.
- A
truncated count → a floor, said as "at least N".
- A 5xx or an HTML page where JSON was expected → the door failed, not the board;
retry once, then report it as an outage, never as "nothing to do".
Coming — named here so nobody invents it
/in is where this loop will talk: one in context --as <seat> (the seat's decisions,
ready rows and live talk in one budgeted pack), typed handoff and decision
messages, and a story thread that shows its rows. None of that is built — it is the
in-agents-collaborate promise (text/in-agents.md). Until it lands, hand off with
tasks:comment on the row itself.
See also
.claude/skills/planning/SKILL.md — the board receivers in depth (the recipes, the
five mistakes)
.claude/skills/meeting/SKILL.md — claim · delegate · chat · chain in a meeting
text/tasks-cli.md — the CLI's promise · packages/cli/src/tasks.ts — the verbs
text/in-agents.md — where this loop is going