Files
stack/agents/darkwing/work/queue-a2/build.md
T
jason.woltjeandClaude Opus 5.5 6ca116b7ba 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]>
2026-09-26 20:14:09 -05:00

11 KiB

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:

  1. map-check.mjs: ok: QUEUE.md matches the map (blob c8e3d34e).
  2. scripts/test-queue.sh: 24/24, live checks skipped.
  3. scripts/queue-commit.sh --install-hook --by sage: installed; canary passed.
  4. 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.
  5. scripts/queue-commit.sh --genesis: committed.
  6. verify --current: briefs match HEAD. render --check: current. legacyView equals the marked body byte for byte, and mapBlob is the map's blob.
  7. next: darkwing resumes 9, sage resumes 7, dewey resumes 5, filbert and rocko have nothing.
  8. scripts/test-queue.sh: 26/26 with the live checks.
  9. 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.