Files
stack/agents/filbert/work/queue-d-review-r1-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

143 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Queue Piece D review, round 1 (#1508, row 12)
Filbert, 2026-09-27. Plan: `queue-as-data-plan-2026-09-26.md` §8.9.
A2 round 1 review: `queue-a2-review-r1-2026-09-27.md` (sha256 f167b85e).
## Verdict
**Changes requested, one item (C1).** The review covers `build.patch`
sha256 cb7a6c6649f88496887eacab52db77ef1cc62aaf59a48c3cadcd5f8e33ae1243
on base 8ffbd73b (it applies cleanly at 8efc0ff3), with
`build-manifest.sha256` 5646d938…. Everything else in the candidate is
ready. C1 is a gap in my own plan's transition table, and D implements that
table as written. Sage can rule it a plan question and approve without it,
but I recommend fixing it now: no semantics-2 entry is logged yet, so the
fix needs no semantics 3.
I re-hashed the inputs after the review. `build.patch` and
`build-manifest.sha256` match the hashes Darkwing sent. `build.md` changed
from b079fbc1… to ced0946b… while I reviewed. The diff is two lines: the
`helper.patch` hash now names helper round 2, and a helper test count went
from 56 to 57. Both are in the helper, which is Rocko's scope, so the
change doesn't touch this review.
## C1. A Jason-gated row can close without its reviewers' verdicts
**Where:** `packages/queue/src/queue.mjs`, `applyMove`, the branch
`} else if (from === "in-review" && (to === "in-progress" || to === "waiting-on-jason")) {`
(lines 766–767 in the patched file). It calls only `ownerOrPriv`.
**What happens.** On a semantics-2 comment round, in-review→waiting-on-jason
checks neither the reviewers' approvals nor the unresolved attempts. The
in-review→done path checks both, but a Jason-gated row never takes that
path; it goes through waiting-on-jason. After that, waiting-on-jason→done
checks only for unresolved attempts. So on a Jason-gated row with
reviewers, the listed reviewers are advisory.
**Reproduced** with a temporary probe test on the `review.test.mjs`
harness (since deleted):
1. `set 9 gate-owner jason` on a row with reviewers.
2. The owner's move to in-review posts the request (comment 1000). The
round's `receipts` is `[]`.
3. darkwing: `move 9 waiting-on-jason` exits 0.
4. jason: `move 9 done` exits 0.
No reviewer recorded a verdict at any point.
**Why it matters now.** Row 5 (CHAT-03 source) is next through this path.
Its gate owner is jason and its reviewers are filbert and rocko. D exists so
that the queue, not a seat's memory, holds "reviewed before Jason sees it".
**Cause.** My plan's table, line 1470, gives
in-review→waiting-on-jason as "owner, or privileged | —". I wrote the
approval condition only on in-review→done (line 1472). D follows the table.
**Fix.** When the current round is a comment round, in-review→waiting-on-jason
runs `refuseUnresolved` and requires an approving receipt from every listed
reviewer, the same checks as in-review→done (lines 779–787). Share them in
one helper. In-review→in-progress stays as it is: that's the changes path,
and the next round already refuses unresolved attempts.
- Scope it to `cur?.request === "comment"`. Only semantics-2 entries create
such rounds, so v1 replay is unaffected.
- Whether `by === "jason"` is exempt is Sage's call. I lean against it:
an exemption is one more path to test, and the owner can't use it.
- Add a test: the steps above refuse at step 3, then pass once every
reviewer has recorded an approval. Add a mutant that drops the check.
- Update the plan table line 1470 and the README's review section to match.
I'll make the plan edit if Sage wants it in my file.
## What I checked
All of this ran in a scratch clone, `/tmp/fqd`, with push disabled, on
frozen 0444 copies of the inputs in `/tmp/fqd-in`.
- **Manifest and suites.** The manifest checks 10/10 after
`git apply build.patch`. `node --test packages/queue/tests/` passes
141/141, and the ledger tests pass 51/51. `scripts/test-queue.sh` reports
27/0 with its "outside the canonical root" skip line, as Sage described.
- **The source**, read in full: `review.mjs`, and the diffs to `queue.mjs`,
`store.mjs`, `cli.mjs`, `scripts/test-queue.sh`, the README, the tests and
`fixtures/fake-gitea.mjs`. `tools-md.patch` matches the code; it's Sage's
to apply.
- **The deadline.** `timeout -s KILL` kills the helper's grandchild. I
checked it with a helper that forks a sleeper: the grandchild is gone
after the deadline, and `spawnSync` reports `signal: "SIGKILL"`, which
`classifyPost` maps to uncertain.
- **Mutations.** I wrote 24 mutants of my own, separate from Darkwing's 61,
and the suite kills all 24. They cover:
- a same-op retry falling through to a second POST;
- an abandoned attempt followed by a late post, which must be a conflict;
- resolve accepting a comment with another id, another author, or no
round marker;
- `credCheck` skipping the mode check or the realpath check;
- a 201 with no comment id treated as posted;
- other HTTP codes treated as failed instead of uncertain;
- a killed call treated as failed;
- done without every approval;
- a new round, or waiting-on-jason→done, ignoring unresolved attempts;
- a new request while one is posted;
- resolve without the GET;
- the body limit removed;
- sage posting as sage instead of jarvis;
- `verify-commit` ignoring the mode or a deletion;
- the outcome-write failure exiting 1 instead of 3;
- a failed pre-send still posting;
- `next` offering a round the reviewer already answered;
- abandon leaving `duplicateRisk` false;
- record accepting a different candidate digest.
## Non-blocking
- **n1. Done doesn't require a posted request.** If every attempt failed
or was abandoned, reviewers can still record approvals and the row can
close. That's acceptable: the receipts are the evidence, and the comment
is how reviewers are asked, not what they approve.
- **n2. A review issue that is a pull request.** `checkComment` needs
`issue_url` to end in `/issues/N`. I haven't checked what Gitea returns
for a comment on a pull request. If it's anything else, resolve refuses.
That fails closed, and our review issues are issues, so no change now.
- **n3. Abandoning a `requesting` attempt sets `duplicateRisk`** even when
the kill came before the POST. That's per plan: the queue can't tell
which side of the POST the kill landed on.
- **n4. The body travels on argv as a JSON argument.** It's capped at
60,000 bytes, well under the per-argument limit. An E2BIG would classify
as uncertain, which is the safe side.
- **n5. For Rocko:** `scripts/gitea-api.sh` writes the response to the
predictable path `/tmp/gitea-api-response.$$`. That's the helper, not D.
What I'd keep as it is:
- resolve checking the comment's author against the requester's login.
That goes beyond the plan, and it's right;
- semantics recorded per entry, with review verbs refused on v1 entries;
- the late-outcome rules in `outcomeState`;
- fixed detail strings, so nothing from a response reaches the log.
## For Darkwing and Sage
Fix C1 with its test and mutant, re-send the patch and manifest, and I'll
review round 2 against this list only. If Sage rules C1 out of D's scope,
the candidate is approved as it stands, and C1 has to land before row 5
moves to in-review.