Files
stack/agents/filbert/work/queue-d-review-r2-2026-09-27.md
T
jason.woltjeandClaude Opus 5.5 f539466fcb feat(queue): Piece D, reviews as issue comments, raw per-seat token helper (row 12, #1508)
queue move ID in-review posts the review request as a Gitea comment and
review record reads verdicts back, so reviews stop being files in
docs/plans/reviews/. On a comment round, in-review to waiting-on-jason
now needs every listed reviewer's approval for the current round, the
same as in-review to done (Filbert r1 C1). scripts/gitea-api.sh reads
the raw per-seat token files (lead decisions 37 to 39): config built and
checked before curl starts, export attribute cleared, fixed base URL.
test-queue.sh skips its live checks outside the canonical root.

Darkwing authored. Filbert approved D r2 (cf1d3fd0) after r1 (a2dc2302)
and corrected the plan (293747cd). Rocko reviewed the helper (e896192f,
2096b0a3), and Sage's lead check passed under decision 38. Manifest
b402fb38, 19 files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 10:07:29 -05:00

4.7 KiB

Queue Piece D review, round 2 (#1508, row 12)

Filbert, 2026-09-27. Round 1: queue-d-review-r1-2026-09-27.md (sha256 a2dc2302…). This round checks C1 only, per Sage's ruling to fix it in D.

Verdict

Approved. The review covers build.patch sha256 8a19f7fa03e2ee6c3bd9ce7b2e296acb21655471c4cc56ba19f25464c8129209 (base 8ffbd73b, applied at cdcedb27), with build-manifest.sha256 647d0170… and build.md e77c8c5e…. The C1 fix is correct. One mutant of mine survives: an approval from an earlier round counted in the current one. The code handles that case correctly; the tests just don't check it. n1 gives a four-line test that kills the mutant. I'd add it before the commit, and it doesn't need a round 3. I checked it myself (below).

What I checked

All of this ran in a scratch clone, /tmp/fqd2, at cdcedb27 with push disabled, on frozen 0444 copies of the three inputs.

  • Manifest and suites. The manifest checks 10/10. node --test packages/queue/tests/ passes 142/142, and the ledger tests pass 51/51 (Darkwing's 58 includes the helper patch, which I didn't apply). scripts/test-queue.sh reports 27/0 with the "outside the canonical root" skip line.

  • What changed. I compared all ten manifest files with round 1's tree. Only queue.mjs, README.md and review.test.mjs differ, as Darkwing said.

  • The fix in queue.mjs.

    • requireApprovals is the round-1 inline check moved into a helper. in-review→done's messages are unchanged.
    • in-review→in-progress still checks only ownerOrPriv, which is right for the changes path.
    • in-review→waiting-on-jason runs ownerOrPriv, then refuseUnresolved, then requireApprovals when the last round is a comment round. unresolvedAttempts skips rounds that aren't comment rounds, and v1 entries can't open one. So v1 replay is unaffected.
    • There's no exemption for jason or sage, as agreed.
  • The tests. The new test runs my round-1 reproduction and now refuses at the step that used to pass. It covers a reviewer who asked for changes, a second round, and reviewers cleared after the round opened. The rewritten unresolved test keeps waiting-on-jason→done covered through a late post that turns an abandoned attempt into a conflict. That's a better route than the old one.

  • The README matches the code.

  • Mutations. I wrote eight mutants of my own for this round. The suite kills seven:

    • G1: sage bypasses the approval check on waiting-on-jason;
    • G2: a changes verdict counts as an approval;
    • G3: the check reads the first round, not the last;
    • G4: one approval is enough;
    • G5: the owner-or-privileged check dropped from waiting-on-jason;
    • G6: the unresolved check dropped from waiting-on-jason→done;
    • G7: the approval check dropped from in-review→done.

    G8 survives: approvals are collected from every round's receipts instead of the current round's. See n1.

  • My plan. Table line 1470 and 8.9's Receipts paragraph (plan sha256 293747cd…) describe the code as built.

Non-blocking

  • n1. Add a test that an earlier round's approval doesn't count (kills G8). In the new test, filbert approves round 1 and rocko asks for changes. In round 2 both approve before anything checks, so an old approval counting in round 2 goes unnoticed. In packages/queue/tests/review.test.mjs, replace the round-2 for loop (three lines) with:

    ok(s.run(["review", "record", "9", "--verdict", "approve", "--comment", "91", "--candidate", head, "--op", "record-9-rock02"], "rocko"));
    // Filbert's round 1 approval doesn't carry into round 2.
    no(s.run(["move", "9", "waiting-on-jason", "--op", "wait-9-000001"], "darkwing"), 2, /row 9 round 2 has no approval recorded by filbert$/m);
    ok(s.run(["review", "record", "9", "--verdict", "approve", "--comment", "90", "--candidate", head, "--op", "record-9-filb02"], "filbert"));
    

    I checked it in a copy of the scratch clone: review.test.mjs passes 20/20 on the candidate and fails 1 under G8.

  • n2. An owner who is also a listed reviewer blocks the row. set reviewers and assign don't stop the owner from being on the list. record refuses the owner, but requireApprovals still waits for the owner's approval. The row then can't reach done or waiting-on-jason until a privileged actor runs set reviewers. That fails closed, and it was already true of done in round 1. The refusal names the owner as missing, which could confuse. Don't fix it by dropping the owner from the required list: with reviewers [owner], that list would be empty and the row would pass with no review. A later fix could refuse the owner at set reviewers and assign. That's a follow-up, not D.