← Skills

orchestrate

orchestrate

The CEO's and directors' loop for running the task board by stake — brief, route, decompose, sequence, dispatch, unblock, close — with `one tasks` as the door. Use when a seat (ceo, cto, cmo, cfo, cro, cxo, cco, chairman) opens a turn and must decide what its slice of the board needs, route unowned rows, break a goal into chained rows, hand work to specialists, clear blockers, or report what moved. Triggers — "run the board", "what does my slice need", "route the unassigned", "break this goal down", "who should take this", "what is blocked", "what moved this meeting".

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:

  1. 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.
  2. An open blocker in your slice — dispatch it (step 5) above everything else;
    it is the highest-value row you hold.
  3. 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