feat(queue): queue as data A2, migration, render and dispatch (#1508)

Filbert approved round 1 (f167b85e). Manifest 782bcb62, 21 files, plus
the QUEUE.md markers and the TOOLS.md section. Lead decision 35.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-26 20:14:09 -05:00
co-authored by Claude Opus 5.5
parent c9539baa0f
commit 6ca116b7ba
32 changed files with 4886 additions and 103 deletions
@@ -0,0 +1,21 @@
b18afc13db533b5cbfac8701234b25dde87c9c1b4fda970e738faf87b7a6d324 packages/queue/README.md
a1e72713398a3f706e4beb5ad012798ab6a55f2f76c2f3144db94011a45a3231 packages/queue/src/cli.mjs
962d0e6548121933030980b937f55ca5913ee3859ff2b8888095bd273dbf561e packages/queue/src/io.mjs
05682738482a56001bac168168a6aeb7e624f8ed1661a6c6592274f13da38c89 packages/queue/src/lock.mjs
b87756a89c3bdeb03bddd1b1e51706ae93975b01f008237076ffe34557a71de3 packages/queue/src/queue.mjs
b7708293363d33093a1106c2346c26ce5d48e052342abbec17c59ee058a4fb05 packages/queue/src/store.mjs
70a11be54d8bddd3db0fd52555a1bdf481efef0cae671ee8e1aab546e3ddd36a packages/queue/tests/data.test.mjs
b1c90f99eb2b5005ebc461dab5793164b80d7e846573f788606040ef88f5819e packages/queue/tests/helpers.mjs
e7c9b23b6aa28321754c1c649f8bd5ee2c97bfea0d4e180ae699a4a1f5c60c76 packages/queue/tests/lock.test.mjs
42120f0009815bedd3887b10e3630a694711825e4dee29a475733dbd212a49eb packages/queue/tests/store.test.mjs
f79195520a1108ce5748ad6833eb03d2bb3ef97d74d83807ee1079e461ebb1f4 packages/queue/tests/write.test.mjs
86c2a4f806e99fd09baf623dc7b3c95c1a630d147fe89c4fe5b93793fa4c5f50 scripts/mosaic
b6aec15a75305bb48bca20a665bd919111a0071ffd7a1cc3631df2beaaac5a1b scripts/test-queue.sh
dc32c0e0a865fe6b3623a808c4e9d8e8b284a3e74c36b3519554ea02d3a3ee5f packages/queue/tests/dispatch.test.mjs
0d83ab1e73663de0f3da719078e7d756b8ec8f828ea63c2ce446bf21fb27c754 packages/queue/tests/migration.test.mjs
3306b486be165b88c554ba13283cab4c79a5b67fbe3c2b6bfe72f987e1eddf07 packages/queue/tests/fixtures/genesis-render.md
fb43d5855726e517f0eaacd626debb110e8ea6c948c97b35ad8971ec0885d151 packages/queue/tests/fixtures/mosaic-pre-a2.sh
8f1927f3f11f260e71e7235a69efcbf10c8fd62bf263ddb89c3029f5e61833b7 packages/queue/tests/fixtures/queue-marked.md
012ddce99d03932512d79b6ae7ba74786e050469e800cfb00fda683a2dfe42a8 agents/darkwing/work/queue-migration-map.md
cdc49c447504f6c9f1c40da9ec0d89bf4d21c5e2a0730b1f12d1f20fbea63edb agents/darkwing/work/queue-a2/map-check.mjs
b2a738f0da52783dc8e8a7c6033ce62582582e587eb6e5b6bde988ea4b406f3e agents/darkwing/work/queue-a2/carry-forward.md
+202
View File
@@ -0,0 +1,202 @@
# Queue A2 (#1508), candidate for review
Darkwing, 2026-09-27. A2 is migration, render and dispatch (lead decision
20), plus every item Sage carried forward from A1's review (decision 26:
N5, N8, N10, N11, N12, P1, P2, P3). Base is HEAD c9539baa; A1 is 34a72af9.
Filbert reviews. Sage commits, then installs the hook and runs genesis.
Nothing is committed, staged or pushed.
## Files
`build.patch` (sha256 `dc0be7ce74aaaaaf43deebe68af439d1b2b80c77aad6538420e929de35e2f21f`) changes 13 files and adds 8.
`build-manifest.sha256` (sha256 `782bcb62e555659333a35888d418d986da63bdf522f38cafa447db7ddd0074b7`) pins all 21 after the patch.
In a fresh clone at c9539baa the patch applies and the result matches
the manifest 21/21, file modes included. `git apply` warns about one
blank line at the end of `fixtures/genesis-render.md`. It belongs there:
the render ends with one, and the golden has to match byte for byte.
- `packages/queue/src/`: `queue.mjs` (N8, N10, N11, P2), `store.mjs` (N12,
N11's shared check, P1's helper, P3's caller), `lock.mjs` (P1, P3),
`io.mjs` (N5), `cli.mjs` (usage comment).
- `scripts/mosaic`: `queue` execs `packages/queue/src/cli.mjs` with the
remaining arguments. Every other call reaches the seat CLI as before.
- `scripts/test-queue.sh`: syntax checks for `scripts/mosaic` and the new
fixture; `scripts/mosaic queue help` always; after genesis, `verify` and
`render --check` on the live queue through `scripts/mosaic queue`.
- `packages/queue/README.md`: the dispatch, N5, N7, N8, N10, N12, P2, the
map and `map-check.mjs`, two new test files.
- Tests: 107 in A1, 122 now. New: `dispatch.test.mjs` (3),
`migration.test.mjs` (3), and 9 more in data, lock, store and write.
Fixtures: `mosaic-pre-a2.sh` (the script before A2), `queue-marked.md`
(QUEUE.md with the markers), `genesis-render.md` (the golden render).
- `agents/darkwing/work/queue-migration-map.md`: the genesis input.
- `agents/darkwing/work/queue-a2/map-check.mjs`: the drift check.
- `agents/darkwing/work/queue-a2/carry-forward.md`: the item list Sage
confirmed (773dbd75), with the r1 section added after it.
Outside the patch, for Sage to apply (condition 2 keeps them out of A2):
- `queue-md.patch` (sha256 `2ca8f689e1dcda5bb30e9af4c3f867242d5239d72e13001369c7b7aca7956402`): the two markers, a header that points
seats at `scripts/mosaic queue next`, and a line freezing the old log of
table changes. It applies to HEAD.
- `tools-md.patch` (sha256 `c53aea1f6e871e17d04335ef60250adcdbe64f6466d9ff641042c00094d1c3a8`): a "Work queue" section in
`docs/TOOLS.md`. It applies to HEAD. Optional; the README already has
the detail.
## The five conditions
1. **Nothing ran against the canonical `.git`.** Tests use scratch
repositories under the temp directory. The dry run below used
`/tmp/qa2-dry`. Checked after all runs: no `mosaic-queue*` file in
`.git/`, `.git/hooks/pre-commit` absent, `core.hooksPath` unset in every
scope, nothing staged.
2. **No QUEUE.md, AGENTS.md or TOOLS.md edits.** The two proposals are
patch files. `git status` shows none of the three modified.
3. **`scripts/test-queue.sh` is green at a HEAD with no `queue.json`:**
24/24, the live checks skipped. After the dry-run genesis it ran 26/26, with `verify` and
`render --check` on the live queue.
4. **H is recorded before the canary.** `queue-commit.sh` is unchanged
from A1, and its test "F1: H is recorded before the canary, so HEAD
moving during the canary makes update-ref fail" passes.
5. **The fault layer is reachable only from tests.** N5 narrows this
further: tmpfs was the one fault-free path open to the CLI, and now
only a layer with `allowTmpfs: true` gets it. The test asserts
`realIo` has no such key and that `"yes"` doesn't count.
## Carried-forward items
| Item | Change | Test |
|---|---|---|
| N8 | `piece`, `gate`, `note`, move `reason`, brief anchor and `blockedReason` refuse `\` and `<`, at the CLI and in replay | "text the table shows refuses \ and <, everywhere it enters"; "every accepted text renders to nine cells on every row" (GFM's cell rule, no `marked` import) |
| N11 | Replay holds every log entry, round op and claim op to `CALLER_OP_RE` and refuses `.outcome`, genesis and `accept-history` included. `LOG_OP_RE` stays for Piece D's derived ids; no verb derives one yet | "replay holds every op id to the caller's rule" |
| N10 | `set issues` moves `closes` only if it equalled the old issues; a narrowed `closes` keeps its intersection, and the receipt says `(kept narrowed)` | "set issues keeps a logged narrowing of closes" |
| P2 | Each round records `issue`; `review` is `{rounds}` only. A later round keeps the last round's issue | "the row schema refuses a round with a null issue, and the A1 review shape", and A1's R2 tests updated |
| P1 | Both gate paths in `acquire` release through `releaseOrWarn`; a failed release is a message naming the lock, not a stack trace | "a release that fails on a gate path is reported, never a stack trace" |
| P3 | `lock.unlock` returns `{result, warning}`; nothing splits on newlines | "unlock keeps a multi-line lock record on stdout" |
| N5 | tmpfs left `FS_TYPES`; only `allowTmpfs === true` admits it | "tmpfs passes only a test layer that allows it" |
| N12 | `--by` still wins; a different non-empty `MOSAIC_AGENT_NAME` adds a stderr warning on success and on refusal. Nothing is logged | "--by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more" |
The rest, as `carry-forward.md` records and decision 26 confirmed: N7 is a
README line (the leftover `.git/mosaic-queue.lock.<pid>.<hex>.tmp` is
removed by hand). N15, N16, N13-a and ext2/ext3 are won't-do.
## Migration
The map (`queue-migration-map.md`) is built from QUEUE.md blob c8e3d34e,
which HEAD has. `node agents/darkwing/work/queue-a2/map-check.mjs` prints
`ok: QUEUE.md matches the map (blob c8e3d34e)`. Run it again just before
genesis. It exits 1 and lists the rows if QUEUE.md moved.
map-check did its job once already. HEAD moved from 8dba3ff7 to c9539baa
while I worked, and it reported row 5 (the CHAT-03 brief pinned, lead
decision 33). I rebuilt row 5's note, `queue-md.patch`, the marked fixture
and the golden render against the new blob. No other row changed.
The map's "Choices Sage should check" has eight items. None blocks review.
The ones that change what genesis writes: row 7 stays a live row (decision
27), row 10's owner stays `coordinator` with `queue assign 10 sage`
recommended as the first op, rows 12 and 13 have Sage as gate owner, row 16
stays `waiting-on-jason` unless #1510 is closed, and rows 9 to 12 close
nothing so row 13 closes #1508. Rows 9 to 13 have no `after`: decision 29
took the wait on row 6 off them, and `after: 9 done` on row 10 would wait
on a gate that needs row 10.
Dry run in a shared clone (`/tmp/qa2-dry`), the candidate plus
`queue-md.patch` and `tools-md.patch` committed on top of c9539baa. I
reran it from scratch after the rebase:
0. `map-check.mjs`: `ok: QUEUE.md matches the map (blob c8e3d34e)`.
1. `scripts/test-queue.sh`: 24/24, live checks skipped.
2. `scripts/queue-commit.sh --install-hook --by sage`: installed; canary
passed.
3. `scripts/mosaic queue genesis --root /tmp/qa2-dry --branch a2-dry --map
… --op genesis-2026-09-27 --by sage`: `ok genesis-2026-09-27 rev 0
genesis 30 rows`.
4. `scripts/queue-commit.sh --genesis`: committed.
5. `verify --current`: briefs match HEAD. `render --check`: current.
`legacyView` equals the marked body byte for byte, and `mapBlob` is the
map's blob.
6. `next`: darkwing resumes 9, sage resumes 7, dewey resumes 5, filbert
and rocko have nothing.
7. `scripts/test-queue.sh`: 26/26 with the live checks.
8. `queue assign 10 sage --op assign-10-dry --by sage`: `ok … rev 1 row 10 owner:
coordinator→sage`.
The dry clone's hook is `.git/hooks/pre-commit`, mode 0755, blob
abdf14e7. `core.hooksPath` is unset there too.
## Choices I made
- **N8 refuses instead of escaping.** An escape has to be right for every
Markdown renderer that reads QUEUE.md; a refusal doesn't. No line in
today's table has either character.
- **P2 drops `review.issue`.** The last round's issue is the kept one, so
a second copy could only disagree. Piece D reads `rounds[].issue`.
- **N10's edge.** A `closes` narrowed to nothing stays empty whatever the
new issues are. `set closes` with a reason is the way to widen it again.
- **N12 warns on a refusal too,** so a seat that mistyped `--by` sees it on
the error it was reading anyway.
- **`scripts/mosaic queue` uses `exec`,** so exit codes and signals are the
queue CLI's own. Any first argument other than exactly `queue` goes to
the seat CLI, as before; the dispatch test compares argv, cwd,
environment and stdin against the pre-A2 script.
## Mutations
Each mutation ran alone in a shared clone of the candidate, with
`node --test packages/queue/tests/`. A killed mutation fails at least one
test.
46 mutations. Each one below failed at least one test; the number is
how many.
| Item | Mutations |
|---|---|
| N8 | backslash 2, lt 2, noteVerb 1, rowNote 2, anchor 1, reason 1 |
| N11 | entryShape 1, outcome 2, genesisOnly 1, roundOp 1, claimOp 1 |
| N10 | always 2, noIntersect 1, receipt 1 |
| P2 | keptFirst 1, noCheck 1, reviewIssue 10 |
| P1 | gateErr 1, gatePresent 1, gatePresentMsg 1, withLock 1 |
| P3 | joined 2, storeSplit 1 |
| N5 | inTypes 1, truthy 1, anyType 1 |
| N12 | noWarnOk 1, noWarnErr 1, envWins 1, emptyEnv 1 |
| Dispatch | noShift 1, unsetArg 1, prefix 1, noExec 1 |
| Golden render | mapGate 1, mapState 1 |
| map-check | fixed26 1, owner 1, issues 1, lineDiff 1, parkedPiece 1 |
Five of them survived the first pass. Each now has a test that kills it:
- N11 roundOp and claimOp (replay stops checking a review round's op or a
claim's op). The N11 test now builds a claimed row and a reviewed row and
refuses an op of 73 characters and one ending `.outcome` in each.
- P1 withLock (the post-op release goes back to the throwing `release`).
New write test: the lock's unlink fails with EACCES after the op, and the
receipt and a refusal both carry "cannot release the queue lock (EACCES)".
- N12 emptyEnv (an empty `MOSAIC_AGENT_NAME` warns). The N12 test now runs
with it empty and expects no stderr.
- Dispatch noExec (`node` without `exec`, so a successful queue call falls
through to the seat CLI). The dispatch test now runs a queue call that
exits 0 and checks that only the queue CLI ran.
The map and golden-render mutations ran again after the rebase and were
still killed.
## Suites
All nine green at the final candidate: config 24, task 90, foundation
44, conductor 17, release 14, auth 15, discord 64, extension-package 18,
queue 24 (no `queue.json` at HEAD). `node --test packages/queue/tests/`:
122/122.
## After approval (Sage)
1. Check the canonical tree against `build-manifest.sha256`, then commit
the 21 files by path.
2. Apply `queue-md.patch` (and `tools-md.patch` if wanted) and commit.
3. `node agents/darkwing/work/queue-a2/map-check.mjs` must print `ok`.
4. `scripts/queue-commit.sh --install-hook --by sage`.
5. `scripts/mosaic queue genesis --root /mnt/storage/src/mosaic-stack
--branch refactor --map agents/darkwing/work/queue-migration-map.md
--op <id> --by sage`.
6. `scripts/queue-commit.sh --genesis -m MSG`.
7. `scripts/test-queue.sh` runs the live checks from here on.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
# Queue A2 (#1508): items carried from A1 review
Darkwing, 2026-09-26. This file records Sage's ruling on A1 r1 so it isn't
lost before A2 starts. A2's brief lists the two required items. A2's build
note repeats each disposition. Note numbers are Filbert's, from
`agents/filbert/work/queue-a1-review-2026-09-26.md` (sha256 6933b885).
## Required in A2, with tests
Both change what replay accepts or what the table shows, so they land
before genesis. Genesis follows A2.
- **N8. `cell()` escapes `|` but not `\`.** Piece text `a\| done | x`
renders so that `marked` splits it into an extra cell. Raw HTML passes
through too.
- The change: refuse `\` and `<` in rendered text fields at the CLI and
in replay. A refusal holds up better than an escape we would have to
get right for every Markdown renderer. None of the 34 table lines in
today's QUEUE.md contains either character, so the migration loses
nothing.
- The test: every text field with `\`, `<` and `a\| done | x` is
refused. A render of the allowed characters splits, by GFM's cell
rule, into the same number of cells on every row. `marked` is only a
transitive dependency in this repo, so the test doesn't import it.
- The mutation: allow `\`, and the cell-count test must fail.
- **N11. Replay is looser than the CLI on op ids.** `LOG_OP_RE` allows 80
characters for any entry, `accept-history` may end in `.outcome`, and
the genesis op isn't checked.
- The change: replay applies `CALLER_OP_RE` to every op a caller chose.
It allows the longer form only for the op ids the CLI derives. It
refuses `.outcome` on `accept-history` and pattern-checks the genesis
op.
- The test: a hand-built file with each of the three refused shapes
fails replay, and every op id the CLI writes still replays.
- The mutations: restore each looser check in turn.
## Dispositions of the other notes
| Note | Disposition | Reason |
|---|---|---|
| N5 tmpfs accepted outside tests; `0xef53` also matches ext2 and ext3 | A2: tmpfs becomes a test-only option, like the other fault options. ext2 and ext3: won't do | `statfs` can't tell ext2, ext3 and ext4 apart. The README names ext4, and the canonical checkout is ext4. |
| N7 temp files from killed acquires stay in `.git/` | A2: README line only | The files are small, carry the dead pid in their name, and never block a lock. Removing them safely needs the same liveness check `unlock` has, which isn't worth it for the space involved. |
| N10 `set issues` resets `closes`, undoing a logged narrowing | A2: fix with a test | It changes replayed state, so it lands before genesis. New rule: `closes` becomes the new issues only if it equalled the old issues. Otherwise it keeps its intersection with the new issues, and the log entry says so. |
| N12 `--by` silently overrides `MOSAIC_AGENT_NAME` | A2: stderr warning, no log field | Both values are self-asserted (J2), so a logged mismatch proves nothing a seat can't avoid. A warning catches the honest mistake, a typo or the wrong seat's shell. |
| N15 `--install-hook --by` is self-asserted | Won't do | J2. It's protocol: Sage runs the install at bootstrap. A check on a claimed name adds nothing. |
| N16 the hook refuses the first commit on an unborn HEAD | Won't do | The hook is installed only in the canonical checkout, which has history. It fails closed. |
Sage confirmed this file as written (773dbd75) on 2026-09-26. N5, N10 and
N12 stay in A2. N10 has to land before genesis because it changes replayed
state.
## From Filbert's r1 approval
Filbert approved A1 r1 and N13 on 2026-09-26 (review
`agents/filbert/work/queue-a1-review-r1-2026-09-26.md`, sha256 e464be6c).
He listed these as non-blocking and suitable for A2. The dispositions
below are my proposal, and Sage rules on them.
| Note | Proposed disposition | Reason |
|---|---|---|
| P1 `acquire` calls `release()` unguarded on both gate paths | A2: fix with a test | If release throws (EACCES on `.git`), the CLI prints a stack trace and doesn't say the lock stayed. The fix guards it the way `withLock` does and names the lock left behind. |
| P2 `--issue` on a later round overwrites `review.issue`; rounds don't record their own issue | A2: each round records its issue, with a test | Piece D posts per round, and the round's own issue is the evidence of where it posted. It changes the round schema and replayed state, so it lands before genesis, like N10. |
| P3 `unlock` splits its result on newlines, so a hand-formatted record spills onto stderr | A2: fix with a test | `lock.mjs`'s `unlock` returns the result and the warning separately, so nothing is split on text. |
| N13-a `n13-check.sh` copies the suites from the canonical tree, not from the pinned patch | Won't do | N13 is approved on `n13.patch` and the two suite hashes, and Filbert applied the patch himself. The check script is evidence, not shipped code. |
N13-b is Sage's: check the canonical working tree against both pins
before committing from it.
@@ -0,0 +1,80 @@
#!/usr/bin/env node
// Usage: node agents/darkwing/work/queue-a2/map-check.mjs [QUEUE.md] [MAP]
//
// Run before genesis. The map names the QUEUE.md blob it was built from.
// This lists every table line that differs from that blob, then every row
// whose piece, owner or issues no longer match the map. Reads only; the
// one git call is `cat-file`. Exit 0 no drift, 1 drift, 4 usage.
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const top = join(dirname(fileURLToPath(import.meta.url)), "../../../..");
const { parseMigrationMap } = await import(join(top, "packages/queue/src/queue.mjs"));
// Table lines by id: `| N | ...` rows, then the parked table's items
// numbered on from the highest row, as the map numbers them.
export function tableLines(text) {
const out = new Map();
const items = [];
let parked = false;
for (const line of text.split("\n")) {
if (line.startsWith("## ")) parked = line.startsWith("## Parked");
const m = /^\| (\d+) \|/.exec(line);
if (m && !parked) out.set(Number(m[1]), line);
else if (parked && line.startsWith("| ") && !line.startsWith("| Item ") && !line.startsWith("|---")) items.push(line);
}
let next = Math.max(0, ...out.keys()) + 1;
for (const line of items) out.set(next++, line);
return out;
}
const cells = (line) => line.slice(2, -2).split(" | ");
export function check(queueText, mapText, oldText) {
const drift = [];
const now = tableLines(queueText);
const then = tableLines(oldText);
for (const id of new Set([...now.keys(), ...then.keys()])) {
if (now.get(id) !== then.get(id)) drift.push(`row ${id}: ${!then.has(id) ? "added" : !now.has(id) ? "removed" : "changed"} since the map's QUEUE.md blob`);
}
const map = parseMigrationMap(mapText);
const byId = new Map(map.rows.map((r) => [r.id, r]));
for (const [id, line] of now) {
const r = byId.get(id);
if (!r) { drift.push(`row ${id}: in QUEUE.md, not in the map`); continue; }
const c = cells(line);
if (!/^\d+$/.test(c[0])) {
if (c[0] !== r.piece) drift.push(`row ${id}: parked item ${JSON.stringify(c[0])} is not the map's piece`);
continue;
}
if (c[1] !== r.piece) drift.push(`row ${id}: piece differs from the map`);
const owner = /^[a-z][a-z0-9-]*/.exec(c[2])?.[0];
if (owner !== r.owner) drift.push(`row ${id}: owner ${owner} in QUEUE.md, ${r.owner} in the map`);
const issues = [...c[3].matchAll(/#(\d+)/g)].map((x) => Number(x[1]));
if (issues.join() !== r.issues.join()) drift.push(`row ${id}: issues ${issues.join(",") || "none"} in QUEUE.md, ${r.issues.join(",") || "none"} in the map`);
}
for (const id of byId.keys()) if (!now.has(id)) drift.push(`row ${id}: in the map, not in QUEUE.md`);
return drift;
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const args = process.argv.slice(2);
if (args.length > 2) {
console.error("usage: map-check.mjs [QUEUE.md] [MAP]");
process.exit(4);
}
const queueText = readFileSync(args[0] ?? join(top, "docs/plans/QUEUE.md"), "utf8");
const mapText = readFileSync(args[1] ?? join(top, "agents/darkwing/work/queue-migration-map.md"), "utf8");
const blob = /QUEUE\.md` blob `([0-9a-f]{40})`/.exec(mapText)?.[1];
if (!blob) {
console.error("the map names no QUEUE.md blob");
process.exit(1);
}
const oldText = execFileSync("git", ["-C", top, "cat-file", "blob", blob]).toString("utf8");
const drift = check(queueText, mapText, oldText);
for (const d of drift) console.log(d);
console.log(drift.length ? `${drift.length} differences; update the map before genesis` : `ok: QUEUE.md matches the map (blob ${blob.slice(0, 8)})`);
process.exit(drift.length ? 1 : 0);
}
@@ -0,0 +1,65 @@
diff --git a/docs/plans/QUEUE.md b/docs/plans/QUEUE.md
index c8e3d34..dd155c6 100644
--- a/docs/plans/QUEUE.md
+++ b/docs/plans/QUEUE.md
@@ -1,29 +1,32 @@
# QUEUE — the one task list
-Read this first. One row per piece. Nobody needs to read the prose plans to
-know what is next; the Brief column says which section to open only when you
-are the owner of that row.
+Read this first. One row per piece. The table between the markers is
+rendered from `docs/plans/queue.json`, and `scripts/mosaic queue` is its only
+writer. Don't edit the table by hand: `queue verify` and the commit hook
+refuse a table that isn't the render. `packages/queue/README.md` has the verbs.
How to find your next thing:
-- **Jason**: the first row whose State starts with `Jason:`.
-- **A seat**: the first row where Owner is you and State is `in progress`,
- `in review` or `briefed`. If there is none, you have nothing; say so on the
- board and stop.
-- **Sage, project lead** (Jason's ruling 2026-09-26; Darkwing before that): update this table at every gate, before
- anything else is written. CURRENT.md is the narrative log; this table wins
+- **A seat**: run `scripts/mosaic queue next`. It names the row to resume,
+ review or start, or says there is nothing; if nothing, say so on the board
+ and stop.
+- **Jason**: rows in state `waiting-on-jason`, and parked rows to reopen.
+- **Sage, project lead** (Jason's ruling 2026-09-26): changes the queue with
+ `scripts/mosaic queue` and commits `queue.json` with QUEUE.md through
+ `scripts/queue-commit.sh`. CURRENT.md is the narrative log; the queue wins
if they disagree.
-States: `queued` (no brief yet) → `briefed` (brief written, not started) →
-`in progress` → `in review` → `Jason: <what he must do>` → `done`. `parked`
-means not before the rows above it and not without Jason's say. `required`
-means it cannot be parked or reordered below `queued` rows; only Jason moves it.
+States: `queued` (brief exists, not accepted) → `briefed` → `in-progress` →
+`in-review` → `waiting-on-jason` → `done`. `blocked` returns to the state it
+left. `parked` rows wait for Jason to reopen them. A `required` row can't be
+parked, and only Jason clears the flag. `after` names the rows a piece waits
+for.
-Brief locations: "plan page" is `docs/plans/2026-09-12_control-board-mvp.md`.
Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
## Pieces (in order)
+<!-- mosaic-queue:begin -->
Lead: Sage from 2026-09-26 (Jason's ruling); Darkwing is a collaborating seat.
Jason is preparing the target for the next phase; until it arrives, the
priority below stands.
@@ -79,8 +82,13 @@ Gate F or when blocked."
| Console features outside the refined session-chat brief, including Fresh creation and model switching | Deferred by WEBUI Q1; required history/control/stop now belong to row 5, not this parked item | `2026-09-13_webui-session-chat.md` |
| Open gaps from the MVP work | Fixed only when a gate needs them | DEFERRED.md, "Open" |
+<!-- mosaic-queue:end -->
+
## Log of table changes
+Frozen at genesis. Since then the log in `docs/plans/queue.json` records every
+change; the entries below are history.
+
- 2026-09-13 — created; rows 1 to 8 taken from CURRENT.md, DEFERRED.md and the plan page.
- 2026-09-13 — rows 9 to 13 added (#1508): the process itself becomes data with one writer and ledger checks. Jason: "I want this iron-clad." Required, not parked; rule: these rows cannot be moved to parked, only to done.
@@ -0,0 +1,29 @@
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
index 5f993af0..02e0de9e 100644
--- a/docs/TOOLS.md
+++ b/docs/TOOLS.md
@@ -147,6 +147,24 @@ npm-global `mosaic` CLI; run by path. Exit codes: the launch script's own
once it runs; before that 1 could not start, 2 invalid config or seat, 4
usage. Details and the record's fields: `packages/seat/README.md`.
+## Work queue (`scripts/mosaic queue`)
+
+```bash
+scripts/mosaic queue list | show ID | next [SEAT]
+scripts/mosaic queue add|move|release|assign|note|set ... --op ID [--by NAME]
+scripts/mosaic queue verify [--current] | render [--check] | sync | unlock [--check-gate]
+scripts/queue-commit.sh -m MSG
+```
+
+`docs/plans/queue.json` holds the rows and an append-only log; the table in
+`docs/plans/QUEUE.md` between the `mosaic-queue` markers is rendered from it.
+Canonical checkout only. Every change needs an `--op ID` chosen before the
+first attempt and reused on retry; only an op whose `ok <op> rev N` receipt
+printed is done. The lead commits queue changes with `scripts/queue-commit.sh`;
+the pre-commit guard refuses any other commit that stages the two queue files.
+Exit codes: `0` ok · `1` failed · `2` invalid or refused · `3` uncertain,
+retry the same op · `4` usage. Details: `packages/queue/README.md`.
+
## Discord connector (`scripts/discord.sh`)
```bash
+878
View File
@@ -0,0 +1,878 @@
# Queue migration map (#1508, A2)
Darkwing, 2026-09-27. This is the reviewed input to `queue genesis --map
agents/darkwing/work/queue-migration-map.md`. Genesis reads the one
`json queue-map` block at the end. Everything above it is for review.
Built from `docs/plans/QUEUE.md` blob `c8e3d34e6efcb98070ebf226157ac027811ffa54`
(HEAD c9539baa). If a row in QUEUE.md changes before genesis, the map has to
change with it. `queue-a2/map-check.mjs` lists the table lines that differ
from that blob and any piece or owner that no longer matches.
## What genesis keeps
- Rows 1 to 25 keep their ids. The five parked items become rows 26 to 30,
in table order, with state `parked`, owner `unassigned` and no issue
(plan Q9). `highWater` is 30 and nothing is retired.
- The text between the QUEUE.md markers goes into genesis as `legacyView`,
byte for byte. That covers every row, the parked table, the start message
and the priority paragraph. A shortened note loses nothing, because the
full State text is there.
- The State column becomes `state` plus `note`. Piece and Gate are copied
verbatim. Every text passes A2's rule: no `\` or `<`, piece and gate at
most 300 characters, notes at most 500.
- Each row has one brief, `{path, anchor}`, and genesis pins the blob. Every
anchor occurs once as a heading in HEAD. Done and parked rows keep their
briefs; `verify --current` skips them.
- `createdAt` comes from the log of table changes. Parked items 1, 2, 3 and 5
date from 7c8e530a, the commit that created QUEUE.md (2026-09-13 UTC). Item
4 has no evidence before 2026-09-26, so it is `unknown`.
- `requiredSince` for rows 9 to 13 is 2026-09-13, the log line that made them
required.
- `reviewers` holds seats under `agents/` only. A lane named for a fleet seat
(`rev-code-02`, `orch-01`) stays in the note.
- Genesis gives a claim to each `in-progress`, `in-review` and
`waiting-on-jason` row: 5, 7, 9, 11 and 16.
## Choices Sage should check
1. **Row 7 stays a row.** Plan Q9 retired it, but lead decision 27 made it
Sage's weekly ledger run, with `packages/ledger/README.md` as its brief.
The map keeps it as `in-progress` with Sage's claim, so `queue next sage`
resumes it and Sage records the weekly number with `queue note 7`.
Retiring it would instead need `retired: [7]` and a pointer line under
the markers.
2. **Row 10 keeps owner `coordinator`.** No seat can act as `coordinator`,
so only Jason or Sage can move the row, and its work edits AGENTS.md,
which is Sage's. I recommend `queue assign 10 sage` as the first
operation after genesis, so the change is logged instead of hidden in
the migration.
3. **Rows 10, 12 and 13 have no `after`.** QUEUE.md says "after row 9", but
row 9 stays open until Gate G, and Gate G also verifies row 10, so `after:
9 done` would hold row 10 behind a gate that needs it. A's code is in place
once genesis is committed, and each note says so.
4. **Row 11 is `waiting-on-jason`.** QUEUE.md says the `queue add` refusal
lands with A2. It landed in A1: `add` runs the same brief check genesis
does. Only the gate is left, and the gate is Jason's.
5. **Gate owners.** Jason holds every gate except rows 12 and 13, which the
map gives to Sage. Both are checks the lead can see: a review round with
no new file under `reviews/`, and the ledger run Sage does each Monday.
With Sage as gate owner, J5 lets `in-review` → `done` happen without
Jason.
6. **Row 16 stays open, as `waiting-on-jason`.** Its State doesn't say done.
Lead decision 29's "rows 14 to 25 are done" sits in the #1509 paragraph,
and row 16 is #1510. If #1510 is closed, change the row to `done` before
the map is committed.
7. **`closes`.** Rows 9 to 12 close nothing and row 13 closes #1508, so piece
E won't report #1508 open under a done row 9. This is J6's narrowing, and
this line is its reason. Every other row closes its issues.
8. **Row 5's note** matches QUEUE.md at c9539baa: the CHAT-03 brief is
pinned, and Sage names the source author after A2 lands. When the row
changes before genesis, the note changes with it.
## Row by row
| # | State | Owner; reviewers | Issues | Required | After | Gate owner | Created | Why |
|---|---|---|---|---|---|---|---|---|
| 1 | done | darkwing | #1503 | no | none | jason | 2026-09-13 | Done per lead decision 29. |
| 2 | done | darkwing | #1504 | no | none | jason | 2026-09-13 | Brief: the plan page's Step 3. |
| 3 | done | darkwing | #1505 | no | none | jason | 2026-09-13 | |
| 4 | done | darkwing | #1506 | no | none | jason | 2026-09-13 | |
| 5 | in-progress | dewey; filbert, rocko | #1507 | no | none | jason | 2026-09-13 | Rocko added as reviewer (adversarial lane). The note is shortened; the full State text is in `legacyView`. |
| 6 | done | darkwing; dewey | #1511, #1512 | no | none | jason | 2026-09-13 | Done per lead decision 29. One brief per row, so the plan page's Piece 5; the other two briefs are named in the note. Dewey is the reviewer; Filbert authored the pilot. |
| 7 | in-progress | sage | none | no | none | jason | 2026-09-13 | Kept as a live row, not retired (see question 1). Recurring, so `in-progress` with Sage's claim. |
| 8 | parked | unassigned | none | no | none | jason | 2026-09-13 | Brief: Sage's stub (J9). |
| 9 | in-progress | darkwing; filbert | #1508; closes none | yes, 2026-09-13 | none | jason | 2026-09-13 | No `after`: lead decision 29 took the wait on row 6 off rows 9 to 13. The note is written as of genesis. |
| 10 | briefed | coordinator | #1508; closes none | yes, 2026-09-13 | none | jason | 2026-09-13 | Owner kept as `coordinator` (see question 2). No `after`: see question 3. |
| 11 | waiting-on-jason | darkwing | #1508; closes none | yes, 2026-09-13 | none | jason | 2026-09-13 | Both parts are in A1 already, so only the gate is left (see question 4). |
| 12 | briefed | darkwing | #1508; closes none | yes, 2026-09-13 | none | sage | 2026-09-13 | Gate owner `sage` (see question 5). |
| 13 | briefed | darkwing; filbert | #1508 | yes, 2026-09-13 | none | sage | 2026-09-13 | Closes #1508; rows 9 to 12 close nothing. Gate owner `sage` (question 5). |
| 14 | done | coordinator; filbert | #1509 | no | none | jason | 2026-09-13 | Reviewer lane named Filbert or orch-01; Filbert kept, orch-01 isn't a seat under `agents/`. |
| 15 | done | coordinator | #1509 | no | none | jason | 2026-09-13 | |
| 16 | waiting-on-jason | darkwing; filbert | #1510 | no | none | jason | 2026-09-13 | Kept open as `waiting-on-jason` (see question 6). |
| 17 | done | coordinator | #1509 | no | none | jason | 2026-09-13 | |
| 18 | done | darkwing | #1509 | no | none | jason | 2026-09-13 | The note keeps the coordinator's part. |
| 19 | done | coordinator | #1509 | no | none | jason | 2026-09-13 | |
| 20 | done | coordinator | #1509 | no | none | jason | 2026-09-13 | Done: lead decision 29 closed #1509 with rows 14 to 25 done. |
| 21 | done | coordinator | #1509 | no | none | jason | 2026-09-14 | Done: lead decision 29. `rev-code-02` isn't a seat under `agents/`, so no reviewer. |
| 22 | done | darkwing; filbert | #1503 | no | none | jason | 2026-09-14 | Done: #1503 closed (lead decision 29). |
| 23 | done | coordinator | #1509 | no | none | jason | 2026-09-16 | Done per lead decision 29. |
| 24 | done | coordinator | #1509 | no | none | jason | 2026-09-16 | Done per lead decision 29. |
| 25 | done | coordinator | #1509 | no | none | jason | 2026-09-20 | Done per lead decision 29. |
| 26 | parked | unassigned | none | no | none | jason | 2026-09-13 | Parked item 1. |
| 27 | parked | unassigned | none | no | none | jason | 2026-09-13 | Parked item 2. |
| 28 | parked | unassigned | none | no | none | jason | 2026-09-13 | Parked item 3. |
| 29 | parked | unassigned | none | no | none | jason | unknown | Parked item 4. First committed 2026-09-26 (0f5b7cb9); no earlier evidence, so `unknown`. |
| 30 | parked | unassigned | none | no | none | jason | 2026-09-13 | Parked item 5. |
## Map
```json queue-map
{
"rows": [
{
"id": 1,
"piece": "Control board MVP (scanner + page)",
"owner": "darkwing",
"issues": [
1503
],
"closes": [
1503
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "A passed 2026-09-12",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-12_control-board-mvp.md",
"anchor": "Control board MVP — plan"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-27 (Sage, lead decision 29): D-001 MVP delivered, Gate A passed 2026-09-12, attention status operator-accepted (row 22), relaunch activity, attribution and Host guard landed; #1503 closed; cross-harness board work belongs to Gate E (row 5)",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 2,
"piece": "Seat registration and `mosaic seat task`",
"owner": "darkwing",
"issues": [
1504
],
"closes": [
1504
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "B passed 2026-09-12",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-12_control-board-mvp.md",
"anchor": "Step 3: daily use and fixes"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-13",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 3,
"piece": "Reply-from-board (piece 2 on the plan page)",
"owner": "darkwing",
"issues": [
1505
],
"closes": [
1505
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "C passed 2026-09-12",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-12_control-board-mvp.md",
"anchor": "Piece 2: reply-from-board"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-12",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 4,
"piece": "Ledger (piece 3 on the plan page)",
"owner": "darkwing",
"issues": [
1506
],
"closes": [
1506
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "D: 18.9 human messages per closed issue, week of 2026-09-06",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-12_control-board-mvp.md",
"anchor": "Piece 3: ledger (numbers for the rails)"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-13",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 5,
"piece": "WebUI first screen on the Console design (piece 4 on the plan page)",
"owner": "dewey",
"issues": [
1507
],
"closes": [
1507
],
"state": "in-progress",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "E: all-seat interactive demonstration then Jason workday ruling; live cutover separately approved",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_webui-session-chat.md",
"anchor": "WebUI session chat: refined brief and delivery plan"
},
"after": [],
"reviewers": [
"filbert",
"rocko"
],
"note": "CHAT-00/01/01C published (370823b3, 28d4e98a, b023841c); CHAT-02 done: backend a5beb6d9, Console c9e771cf, live check passed 2026-09-26; CHAT-03 brief approved and pinned 2026-09-27 (BRIEF.md 1ef15ac0, rescoped to Gate E, lead decisions 30 to 33; Filbert r3 approve, Rocko blocker closed, Filbert scope check pass); source author named by Sage after queue A2 lands; CHAT-04..08 not chartered; Gate E blocked",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 6,
"piece": "Darkwing on point: darkwing assigns and gates filbert's work (piece 5 on the plan page)",
"owner": "darkwing",
"issues": [
1511,
1512
],
"closes": [
1511,
1512
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "F: filbert's item closes with zero human messages from Jason; code phase does not claim Gate F",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-12_control-board-mvp.md",
"anchor": "Piece 5: darkwing on point (orchestrator behaviour)"
},
"after": [],
"reviewers": [
"dewey"
],
"note": "done 2026-09-27 (lead decision 29): #1511 and #1512 landed in af4203ca (pushed), closed; Gate F not passed (2 human messages in filbert's T3 thread; the first 11 days predate the T3 source); Gate G (row 9) carries the test. Filbert authored the pilot. Briefs also: 2026-09-15_relaunch-activity.md, 2026-09-14_task-attribution.md",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 7,
"piece": "Weekly ledger run and rails number",
"owner": "sage",
"issues": [],
"closes": [],
"state": "in-progress",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "under 10 human messages per closed issue for the week of 2026-09-13",
"gateOwner": "jason",
"brief": {
"path": "packages/ledger/README.md",
"anchor": "Ledger"
},
"after": [],
"reviewers": [],
"note": "recurring, every Monday (lead decision 27); Jason reads; 09-13..19: 47.7, 09-20..26: 33.0 human messages per closed issue",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 8,
"piece": "Fleet seats (`~/.mosaic`) onto `mosaic launch`",
"owner": "unassigned",
"issues": [],
"closes": [],
"state": "parked",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason's call",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-26_fleet-seats-onto-mosaic-launch.md",
"anchor": "Row 8: fleet seats onto `mosaic launch` (stub brief)"
},
"after": [],
"reviewers": [],
"note": "parked by current owner direction; no `~/.mosaic` changes during internal bootstrap",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 9,
"piece": "Queue as data: `docs/plans/queue.json`, `mosaic queue` is the only writer",
"owner": "darkwing",
"issues": [
1508
],
"closes": [],
"state": "in-progress",
"previousState": null,
"required": true,
"requiredSince": "2026-09-13",
"gate": "G: a fresh seat told only \"run `mosaic queue next` and do it\" starts its piece with zero human messages",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_queue-as-data.md",
"anchor": "Piece A: queue record and `mosaic queue` (QUEUE row 9)"
},
"after": [],
"reviewers": [
"filbert"
],
"note": "A1 (journal, lock, CLI, verify) committed 34a72af9; A2 (migration, render, dispatch) committed with this map; Filbert approved both; genesis and hook install by Sage; Gate G also verifies row 10",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 10,
"piece": "Seats read the queue, not CURRENT.md (AGENTS.md cadence, seat context files)",
"owner": "coordinator",
"issues": [
1508
],
"closes": [],
"state": "briefed",
"previousState": null,
"required": true,
"requiredSince": "2026-09-13",
"gate": "verified by Gate G",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_queue-as-data.md",
"anchor": "Piece B: seats read the queue, not CURRENT.md (QUEUE row 10)"
},
"after": [],
"reviewers": [],
"note": "AGENTS.md lines done 2026-09-13; the rest starts once genesis is committed",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 11,
"piece": "Brief template `docs/plans/BRIEF-TEMPLATE.md`; `queue add` refuses a missing brief",
"owner": "darkwing",
"issues": [
1508
],
"closes": [],
"state": "waiting-on-jason",
"previousState": null,
"required": true,
"requiredSince": "2026-09-13",
"gate": "two briefs accepted by Jason with no scope question",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_queue-as-data.md",
"anchor": "Piece C: brief template (QUEUE row 11)"
},
"after": [],
"reviewers": [],
"note": "template and the `queue add` brief refusal both committed with A1 (34a72af9); the gate counts the first briefs added through the queue",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 12,
"piece": "Reviews as issue comments posted by `queue move ID in-review`, not files in `docs/plans/reviews/`",
"owner": "darkwing",
"issues": [
1508
],
"closes": [],
"state": "briefed",
"previousState": null,
"required": true,
"requiredSince": "2026-09-13",
"gate": "one review round with no new file under reviews/",
"gateOwner": "sage",
"brief": {
"path": "docs/plans/2026-09-13_queue-as-data.md",
"anchor": "Piece D: reviews through a channel, not files (QUEUE row 12)"
},
"after": [],
"reviewers": [],
"note": "starts once genesis is committed; the live posting test needs the per-seat credential file (8.9)",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 13,
"piece": "Ledger \"queue\" section: issue/row/seat drift printed with the weekly number",
"owner": "darkwing",
"issues": [
1508
],
"closes": [
1508
],
"state": "briefed",
"previousState": null,
"required": true,
"requiredSince": "2026-09-13",
"gate": "first run Monday 2026-09-21, zero violations or every one moved same day",
"gateOwner": "sage",
"brief": {
"path": "docs/plans/2026-09-13_queue-as-data.md",
"anchor": "Piece E: ledger checks the queue (QUEUE row 13)"
},
"after": [],
"reviewers": [
"filbert"
],
"note": "starts once genesis is committed; closes #1508 when done, so rows 9 to 12 close nothing; first run is the Monday after E is approved (Q7)",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 14,
"piece": "Discord connector pilot: Sage answers in Shared Signals (chat only, no tools, no repo writes)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "H: offline suite green, eight-step live pilot with private receipts, then Jason says the reply reads as Sage",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_discord-connector-pilot.md",
"anchor": "Discord connector pilot: Sage on Shared Signals"
},
"after": [],
"reviewers": [
"filbert"
],
"note": "done: rev-code-02 APPROVE 26170 (round 9), committed 786e379c; pilot steps 1-8 done with private receipts, Gate H passed (Jason, 2026-09-13: replies read as Sage); connector left running for MVP iteration; reviewer lane was Filbert or orch-01",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 15,
"piece": "Discord connector: eyes reaction on every admitted message as a read receipt (MVP iteration 1)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason sees the reaction on a live message",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_discord-connector-pilot.md",
"anchor": "11. MVP iteration, after the pilot"
},
"after": [],
"reviewers": [],
"note": "done: committed 93d6b624, live check passed 19:21 UTC (turn record receipt ok, Jason: test is successful), receipt `mvp1-read-receipt-20260913T192158Z.json` in the private evidence dir; reaction placed at admission before the engine runs, outcome in the turn record, none on drops or refusals; `scripts/test-discord.sh` 28/28 (90 node tests)",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 16,
"piece": "Repository-native development bootstrap: five internal agents, Darkwing coordination",
"owner": "darkwing",
"issues": [
1510
],
"closes": [
1510
],
"state": "waiting-on-jason",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "source approved; Researcher live response passed 26216; existing native Rocko lock preserved, no new Rocko model test; no MVP acceptance/publication",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_internal-development-bootstrap.md",
"anchor": "Repository-native development bootstrap"
},
"after": [],
"reviewers": [
"filbert"
],
"note": "source approved by internal Filbert, exact R1 receipt 26204; six offline tests independently pass in both copies; 24 config tests and five no-effect checks are author evidence",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 17,
"piece": "Discord connector: systemd user service with a supervised pre-start (`recover`, exit 3 never retried) (MVP iteration 2)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "the Sage connector runs under `mosaic-discord@shared-signals`, survives a kill with a clean restart, and stays down behind `discord.sh stop`",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_discord-connector-pilot.md",
"anchor": "11. MVP iteration, after the pilot"
},
"after": [],
"reviewers": [],
"note": "done, operator-verified by Jason 2026-09-13 (all four steps): suite 40/40 (95 node tests); Sage seat migrated 19:35 UTC, SIGKILL recovered in 16 s with the dead lock cleared, brake held (exit 3, no restart), released and READY; receipt `mvp2-service-unit-*.json` in the private evidence dir; the first cut (ExecStartPre) looped and was replaced by `run --supervised` before any traffic",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 18,
"piece": "Control board row for the Discord connector (MVP iteration 3): discovery from binding files, liveness from run.lock, reply refused",
"owner": "darkwing",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "a Sage (discord) row on the board shows live, offline and braked correctly, and reply from the board is refused",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-14_discord-board-row.md",
"anchor": "Discord connector board row"
},
"after": [],
"reviewers": [],
"note": "done for bounded local delivery; Jason accepted visual test (parfait), R3 26257 reviewed, 322 tests and live 409 refusal verified; owner by Jason's ruling 2026-09-13, coordinator answered connector-side questions; also briefed in the pilot page section 11",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 19,
"piece": "Discord connector: binding reload without a restart (`reload` verb, SIGHUP, `systemctl --user reload`); channels, users, limits and guildName apply in place, identity, engine and context stay fixed, an invalid file is refused and the old binding kept (MVP iteration 4)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "edit the binding, run `scripts/discord.sh reload shared-signals`, the change applies with no restart, a broken edit is refused and journaled",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_discord-connector-pilot.md",
"anchor": "11. MVP iteration, after the pilot"
},
"after": [],
"reviewers": [],
"note": "done: caaef941; live 00:03 UTC: reload applied Carmen's entry with no restart, unknown key refused by the CLI (exit 2), fixed key refused in the process with the binding kept, `systemctl --user reload` applied; suite 41/41 (101 node tests); receipt `mvp4-5-reload-carmen-*.json`",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 20,
"piece": "Discord connector: per-user channel allowlist in the binding and Carmen enrolled (all listed rooms except #sage-admin) (MVP iteration 5)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Carmen gets a reply in #general and silence in #sage-admin; Jason unchanged",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_discord-connector-pilot.md",
"anchor": "11. MVP iteration, after the pilot"
},
"after": [],
"reviewers": [],
"note": "done: caaef941 (`users[].channels` allowlist, `channel-not-for-user` drop); Carmen enrolled live by reload 00:03 UTC; her first message was the remaining check; #1509 closed with rows 14 to 25 done (lead decision 29)",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 21,
"piece": "Discord connector: read-only tools for the Discord Sage through a Mosaic pi extension confined to declared roots (MVP iteration 6)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Sage answers a question from a file under a declared root with the reads in the turn record; a read outside the roots is refused and recorded",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-14_discord-readonly-tools.md",
"anchor": "Discord Sage: read-only tools (iteration 6)"
},
"after": [],
"reviewers": [],
"note": "approved: rev-code-02 round 2 verdict 26276 (tree 43f0329b), committed; Jason ruled R1–R7 2026-09-14 (roots docs/ and agents/sage/, Carmen included); done with rows 14 to 25 when #1509 closed (lead decision 29); reviewer per Q12",
"blockedReason": null,
"createdAt": "2026-09-14"
},
{
"id": 22,
"piece": "Board attention status: completed replies idle, explicit human input waiting",
"owner": "darkwing",
"issues": [
1503
],
"closes": [
1503
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "operator accepted full targeted status sequence: explicit request waiting, Seen hides attention but preserves waiting, completion returns idle; broader MVP/cross-harness acceptance and publication separate",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_board-attention-status.md",
"anchor": "Board attention status correction"
},
"after": [],
"reviewers": [
"filbert"
],
"note": "source approved, receipt 26248; reviewer 193 per copy, author union 213 pass; actual Researcher scan idle; operator accepted (row 1); #1503 closed (lead decision 29)",
"blockedReason": null,
"createdAt": "2026-09-14"
},
{
"id": 23,
"piece": "Discord connector: writes confined to the `shared-signals` root plus web fetch and search for the Discord Sage (MVP iteration 7)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Sage writes a naming shortlist into the repository from #ideas with the write and web calls in the turn record; a write outside the root is refused",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-16_discord-write-and-web-tools.md",
"anchor": "Discord Sage: writes into the strategy repository, and web research (#1509, QUEUE row 23)"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-27 (Sage, lead decision 29): committed and pushed 1685deb4; live turns wrote `vault/Businesses/naming.md` with web_search and web_fetch in the turn record; the outside-root refusal rests on the offline suite, not a live turn; reviewer per Q12",
"blockedReason": null,
"createdAt": "2026-09-16"
},
{
"id": 24,
"piece": "Discord connector: git verbs (status, commit, pull ff-only, push) for the Discord Sage on the `shared-signals` root, seat identity through the existing credential helper (MVP iteration 8)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Sage commits and pushes a decision file from #ideas; GitHub shows Sage as author with a Requested-by trailer; no token in any record",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-16_discord-git-tools.md",
"anchor": "Discord Sage: git for the strategy repository (#1509, QUEUE row 24)"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-27 (Sage, lead decision 29): committed and pushed 1949ed8d; live-configured 2026-09-18, never used from Discord; records now go through row 25 and SetSpark cutover freezes `vault/`, so first real use is the check and a failure opens a new issue; reviewer per Q12",
"blockedReason": null,
"createdAt": "2026-09-16"
},
{
"id": 25,
"piece": "Discord connector: SetSpark record client for the Discord Sage, fixed verbs against setspark-api, connector-verified approvals (MVP iteration 9)",
"owner": "coordinator",
"issues": [
1509
],
"closes": [
1509
],
"state": "done",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Sage creates one work item from #sage-admin and the API shows it with revision 1; a proposal is approved by the Approve button and the audit row carries Sage's key id and Jason's Discord id separately; rev-code-02 approves on #1509",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-20_discord-setspark-client.md",
"anchor": "Discord Sage: SetSpark record client (#1509, QUEUE row 25)"
},
"after": [],
"reviewers": [],
"note": "done 2026-09-26: committed and pushed 43d7574d, live check passed (DEC-010, request 2, approved by button at 21:38:58Z, lead decision 19); approver rule enforced by the service since shared-signals cc74d92 (lead decisions 24 and 28); reviewer per Q12",
"blockedReason": null,
"createdAt": "2026-09-20"
},
{
"id": 26,
"piece": "Registry increment 3 (headless identity-env leak)",
"owner": "unassigned",
"issues": [],
"closes": [],
"state": "parked",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason reopens",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/CURRENT.md",
"anchor": "Completed checkpoint: #1500 increment 2 (historical)"
},
"after": [],
"reviewers": [],
"note": "#1500 closed on increment 2; Jason has not asked for 3",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 27,
"piece": "Auth/provider/harness registry review",
"owner": "unassigned",
"issues": [],
"closes": [],
"state": "parked",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason reopens",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-03_auth-provider-harness-registry.md",
"anchor": "Harness declaration + centralized auth/provider registry"
},
"after": [],
"reviewers": [],
"note": "Paused for owner alignment",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 28,
"piece": "CI runners, second real adapter, push automation",
"owner": "unassigned",
"issues": [],
"closes": [],
"state": "parked",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason reopens",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/ROADMAP.md",
"anchor": "Explicitly deferred"
},
"after": [],
"reviewers": [],
"note": "Deferred by owner 2026-09-03",
"blockedReason": null,
"createdAt": "2026-09-13"
},
{
"id": 29,
"piece": "Console features outside the refined session-chat brief, including Fresh creation and model switching",
"owner": "unassigned",
"issues": [],
"closes": [],
"state": "parked",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason reopens",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/2026-09-13_webui-session-chat.md",
"anchor": "WebUI session chat: refined brief and delivery plan"
},
"after": [],
"reviewers": [],
"note": "Deferred by WEBUI Q1; required history/control/stop now belong to row 5, not this parked item",
"blockedReason": null,
"createdAt": "unknown"
},
{
"id": 30,
"piece": "Open gaps from the MVP work",
"owner": "unassigned",
"issues": [],
"closes": [],
"state": "parked",
"previousState": null,
"required": false,
"requiredSince": null,
"gate": "Jason reopens",
"gateOwner": "jason",
"brief": {
"path": "docs/plans/DEFERRED.md",
"anchor": "Open"
},
"after": [],
"reviewers": [],
"note": "Fixed only when a gate needs them",
"blockedReason": null,
"createdAt": "2026-09-13"
}
],
"retired": [],
"highWater": 30
}
```