Skip to content

Your first run

With a project onboarded, you’re ready to dispatch real work. Put an issue in your board’s Ready column, then run one sweep.

/echo:tick runs one hub sweep across every enabled registered project. For each project it checks capacity and the Ready column, and dispatches a worker when there’s dispatchable work and a free slot:

/echo:tick

It prints one status line per project, then a final tick: sweep complete:

demo: dispatch (3 Ready, 0/2 slots)
└ status=spawned_worker issue=42
tick: sweep complete

Other per-project lines you might see instead:

  • demo: no Ready items — nothing to do this sweep
  • demo: slots full (2/2) — all worker slots for this project are busy
  • demo: skipped (global worker cap 4 reached) — the platform-wide cap is hit
  • demo: board unreadable — skipped — the board read failed this sweep

When a project has Ready work and a free slot, the sweep hands off to the per-project dispatch pipeline, which runs deterministically:

  1. Acquire the per-project lock so concurrent firings are no-ops — only one dispatch runs at a time per project.

  2. Select the next eligible Ready item (highest-priority first) and pre-validate it with a cheap model — a malformed or blocked item is deferred or escalated here, before any worker starts.

  3. Preflight gh auth in the scrubbed worker environment. If gh isn’t authenticated with GH_TOKEN/GITHUB_TOKEN removed, the item is routed to Needs Human rather than spawning a worker that can’t reach GitHub.

  4. Flip Ready → Planning and spawn the detached worker. The worker then flips Planning → In progress once it has written its plan, implements the change, runs the adversarial review, opens the PR, labels it echo:merge-ready, and stops in AI reviewing.

  5. Post a “worker spawned” comment on the issue (with the worker’s pid and log directory) and release the lock.

The dispatch prints exactly one machine-readable result — spawned_worker, no_eligible_items, deferred, needs_human, lock_held, or error — which the sweep surfaces on the └ status=... line.

/echo:status prints a cross-project table — one row per registered project — from a single board read each:

/echo:status
demo enabled=true board=ready:2,inprogress:1,needshuman:0 workers=1/2 outbox=0 last=2026-07-16T14:03

Each row shows:

  • enabled — the registry’s enabled flag (a disabled project’s board read is skipped entirely)
  • board — Ready / In progress / Needs Human counts, using this project’s own configured status names
  • workers — live worker count over configured slots (e.g. 1/2)
  • outbox — Discord escalations queued for delivery
  • last — mtime of this project’s newest run directory ( if it’s never run)

A broken or unreachable project degrades its own row to ? placeholders but never stops the sweep — every other project still gets a row.

The worker stops with the PR in AI reviewing; it never merges. A trusted dispatcher-side gated-merge step re-runs the deterministic gates on the next sweep and either squash-merges the PR or holds it for a human — and after a merge, deploy-watch reconciles the item to Done. Those stages are covered in the guides; running /echo:tick on your interval is all that keeps the loop turning.