Files
stack/docs/plans/2026-09-13_queue-as-data.md
T

6.6 KiB

Queue as data: the process stops being prose (#1508)

Owner of the plan: Jason. Coordinator: the Claude session. Builder: darkwing. Tracking: #1508. Rows 9 to 13 in docs/plans/QUEUE.md, state required.

Jason, 2026-09-13: "It all starts out great, then goes off the rails. I want this iron-clad." This page is the plan, the decomposition, and the briefs. It is not a design discussion. Each piece below has one owner, one gate, and one commit-sized scope. Nothing on this page is optional and nothing moves to DEFERRED.md; a piece that cannot ship gets a Blocked line in QUEUE.md with the reason.

Problem, in one paragraph

The entry point for "what is next" was prose in four files (AGENTS.md, CURRENT.md, ROADMAP.md, CONDUCTOR.md), edited by hand by any seat in one shared checkout. State lived in sentences, so it drifted: three "Next action" paragraphs, edits swept into other seats' commits, 160 review files invented as a side channel, and Jason unable to find the next step. The fix is the same fix already applied to decisions (brain.py is the only writer) and to seat identity (registration.json is the only record): the queue becomes a record with one writer and checks that fail loudly.

Ordering and preconditions

Starts after piece 5 (darkwing on point, QUEUE row 6) reaches Gate F or is blocked, whichever comes first. Runs before the fleet migration (row 8). Pieces A to E below ship in order; each is one issue comment on #1508 and one QUEUE row move. Nothing on this page waits for the WebUI, the comms MVP, or the orchestrator to be finished.

Piece A: queue record and mosaic queue (QUEUE row 9)

Owner: darkwing. Reviewer: filbert. Package: packages/queue, same shape as packages/seat. Files owned: packages/queue/**, scripts/mosaic (subcommand dispatch only), docs/plans/QUEUE.md.

What ships

  • One file, <repo>/docs/plans/queue.json, committed. It is the queue. Array of rows: id (integer, never reused), piece (one line), owner (seat name, jason, coordinator, or unassigned), issue (integer or null), state (queued | briefed | in-progress | in-review | waiting-on-jason | done | blocked | parked | required), gate (one sentence), brief (repo path plus optional section), updatedAt, updatedBy (from MOSAIC_AGENT_NAME, or --by, else refused), note (one line, optional), blockedReason (required when state is blocked).
  • scripts/mosaic queue list [--owner SEAT] [--json], queue next [SEAT] (prints the one row that seat should work on, or "nothing" with exit 0), queue add --piece --owner --gate --brief [--issue], queue move ID STATE [--reason], queue assign ID SEAT, queue note ID TEXT. All writes through the CLI; the CLI validates the transitions below and refuses anything else with one line on stderr and exit 2.
  • Transitions: queued→briefed→in-progress→in-review→waiting-on-jason→done; any→blocked (reason required) and blocked→previous; required→in-progress only; parked only from queued or briefed and only with --by jason; done and parked rows never change. A seat may move only rows it owns; coordinator and jason may move any row.
  • docs/plans/QUEUE.md becomes a rendered view: mosaic queue render writes the table between two marker comments and the CLI runs it after every write. The header text above the markers stays hand-written.
  • Tests: fixtures for every transition and every refusal; render is byte-stable; queue next picks the first row by id, not by file order.

Out of scope: Gitea sync, board display, the ledger, hooks, any change under packages/seat, packages/control-board, packages/ledger.

Gate G (Jason): a seat launched fresh, with no prior session, is told only "run mosaic queue next and do it". It finds its piece, opens the brief the row names, and starts, with zero further human messages. Jason watches from the board. Pass or fail, one sentence on #1508.

Piece B: seats read the queue, not CURRENT.md (QUEUE row 10)

Owner: coordinator. Done 2026-09-13 for the cadence and pointer lines in AGENTS.md (cbf24b01). Remaining under this piece, after Piece A: the cadence line says mosaic queue next instead of naming the file, and agents/*/AGENTS.md or context files that still point at CURRENT.md for "what now" are changed to the command. One commit. No gate; verified by Gate G.

Piece C: brief template (QUEUE row 11)

Owner: darkwing, as part of Piece A's review round. File: docs/plans/BRIEF-TEMPLATE.md, one page, these headings only: Problem, Owner and reviewer, Files owned, What ships, Out of scope, Gate (who says yes, on what evidence). mosaic queue add refuses a --brief path that does not exist. The pieces on this page are written in that shape and are the first three uses. Passing: Piece A's brief and one later brief are accepted by Jason without a follow-up question about scope.

Piece D: reviews through a channel, not files (QUEUE row 12)

Owner: darkwing. After Piece A. Review requests and receipts go as issue comments on the piece's issue (scripts/gitea-api.sh exists) or as board messages once the comms MVP has a record; no new files under docs/plans/reviews/. The existing 160 files stay as history. mosaic queue move ID in-review posts the request comment itself from the row's data, so the seat does not write one. Passing: one full review round on a later piece with zero new files under docs/plans/reviews/.

Piece E: ledger checks the queue (QUEUE row 13)

Owner: darkwing. Reviewer: filbert. After Piece A. packages/ ledger gains a "queue" section: every open Gitea issue with a #N in a queue row has that row not done; every in-progress or in-review row has an owner seat with a live registration; every done row's issue is closed; every required row older than 14 days is listed by name. Violations print above the weekly table and in --json. First run Monday 2026-09-21 with the weekly number. Passing: zero violations, or every violation has a row moved within the day.

What "iron-clad" means here, so it can be checked

  1. The queue has one writer (mosaic queue) and refuses bad transitions.
  2. Seats are told a command, not a file, so nothing to reinterpret.
  3. Every row names an owner, a gate, and a brief that exists.
  4. The ledger reports drift weekly, next to the number Jason already reads.
  5. required rows cannot be parked; only Jason moves them.

If any of the five stops being true, that is a finding on #1508, reopened.

Log

  • 2026-09-13 — page written by the coordinator after Jason's "iron-clad" ruling; #1508 opened; QUEUE rows 9 to 13 point here.