Files
stack/agents/filbert/work/queue-e-review-r1-2026-09-27.md
T
jason.woltjeandClaude Opus 5.5 fd72d26899 feat(ledger): Piece E, queue section in the weekly ledger (row 13, #1508)
The ledger prints a queue section above the weekly table. It checks four
things:
- open issues named by done rows;
- owner registrations for active rows;
- closed issues for done rows;
- the age of required rows.
The result is fail, incomplete or reduced pass. It uses its own Gitea
budget of the open list plus at most 10 lookups. A full open page counts
only while an issue in some row's closes has no known state (lead
decision 40). T3 seats are exempt per run with --unsupported-runtime.
The weekly routine is in packages/ledger/README.md.

Built by Darkwing (build.patch ab1f12ca, manifest 0b20bbca). Filbert
reviewed it: round 1 81f26f2e asked for changes (C1, ISO requiredSince
never aged); round 2 ce8ce150 approved. Also carries Filbert's plan
amendment for decision 40 (68a25ffe).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 11:33:44 -05:00

169 lines
7.9 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 E review, round 1 (#1508, row 13)
Filbert, 2026-09-27. Plan: `queue-as-data-plan-2026-09-26.md` §8.10,
amended for lead decision 40 (plan sha256 68a25ffe…, uncommitted).
## Verdict
**Changes requested, one item (C1).** The review covers the amended
round-1 candidate: `build.patch` sha256
02a01c291890626f58ba103d1c65e853b042b609887868bf59dea7f9b50b227d at
2333d837, with `build-manifest.sha256` cad51929… and `build.md` d3bdb826….
I had also reviewed the first version (patch 78445322…, manifest
8f0e4fea…) in full. The amendment changes only the full-page rule, its
tests and the README, so everything I checked on the first version still
holds.
C1 is a real bug in the age check, and it's small. Everything else is
ready, including the decision-40 change.
## C1. The age check never flags a row made required after genesis
**Where:** `packages/ledger/src/queue-checks.mjs`, line 200 in the amended
file:
```js
const days = ageDays(Date.parse(`${r.requiredSince}T00:00:00Z`), now);
if (days > AGE_LIMIT_DAYS) add(violations, 'age', ...
```
**What happens.** The code assumes `requiredSince` is a date. Genesis
rows carry dates (rows 9, 10, 11 and 13 have `2026-09-13`). But the queue
writes a full ISO time for any row made required later:
`set <id> required true` sets `requiredSince = entry.at`
(`packages/queue/src/queue.mjs:860`), and `add --required` does the same
(line 904). The validator accepts both forms (line 322, `checkTime(…,
{ date: true })`). For an ISO time the template gives
`2026-09-01T12:00:00.000ZT00:00:00Z`, `Date.parse` returns NaN, `days`
is NaN, and `NaN > 14` is false. The row is never an age violation, and
nothing is reported as undecided either.
**Reproduced** in-process at 2026-09-27T12:00Z on one required,
in-progress row:
- `requiredSince: "2026-09-01"` → violations `owner-invalid, age`, result
`fail`;
- `requiredSince: "2026-09-01T12:00:00.000Z"` → violations `owner-invalid`
only. The age violation is missing.
(`owner-invalid` comes from passing no seats; it doesn't matter here.)
**Why it matters.** Today's rows all have dates, so Monday's run is right.
The first row made required through the queue CLI would slip past the
14-day gate without a word. A check that goes quiet is the failure the
ledger's result levels exist to prevent.
**Fix.**
- A `DATE_RE` value parses as `T00:00:00Z`; anything else goes to
`Date.parse` as it stands.
- If the result is NaN, fail closed: an `age` undecided item naming the
row, or a violation. Never silence. (The validator should make this
unreachable, but the check shouldn't depend on that.)
- Tests: an ISO `requiredSince` 15 days old fails and one 14 days old
doesn't, next to the existing date cases. Your fixtures build
`requiredSince` with `since(n)`, so an ISO variant fits in the same test.
- Mutant: restore the old template. The new test should kill it.
- The README's age paragraph can say that `requiredSince` is a date for
genesis rows and an ISO time for later ones.
## The full page (lead decision 40)
I agree with the ruling, and the amendment implements it correctly. An
issue is never treated as closed because it's missing from the open list.
It's looked up or left `unknown (budget)`, and the unknown state already
makes the run incomplete. A server that caps pages below 50 would make
`full` meaningless anyway, so dropping it as a standalone signal costs
nothing.
What I checked on the delta:
- `open-list-full` is added only when the page is full and some issue in
`wanted` is `unknown`. `wanted` is the union of every row's `closes`,
including rows that aren't done. An unknown issue on a pending row can
only affect a disposition, but it still keeps the page undecided. That
errs toward `incomplete`, and it matches the decision's wording ("some
wanted issue"). Keep it.
- The new end-to-end test drives the fake helper through `issueStates` and
`queueChecks` for all three cases: resolved, open off the page, and past
the budget.
- The README's result list and call table match the code.
- My plan now says the same thing: 8.10's budget table and result list,
a pointer from section 2's E text, and Q9 and 2.A for row 7.
## What I checked
All of this ran in scratch clones with push disabled: `/tmp/fqe` at
f304eaa5 for the first version, and `/tmp/fqe2` at 2333d837 for the
amendment. The inputs were frozen as 0444 copies.
- **Manifest and suites.** The amended manifest checks 5/5. `node --test
packages/ledger/tests/` passes 76/76, and `packages/queue/tests` with
`packages/seat/tests` passes 161/161. The first version had passed
75/75 and 161/161.
- **Source.** I read `queue-checks.mjs` in full, and the diffs to
`cli.mjs`, the README and both test files. I compared the amendment with
the first version file by file. Only `queue-checks.mjs` (the rule and its
comment), the README (two passages) and `queue-checks.test.mjs` changed.
- **Local run** on the first version, for 2026-09-27 with `--no-issues
--no-t3` and three seats declared: 0 violations, result `incomplete`
(issues not run), 18 protected changes.
- **Mutations.** I wrote 21 mutants of my own against the first version
(E1–E21), apart from Darkwing's 37. The suite kills 19. The two
survivors are equivalent:
- E2 checks the metric page before the open list. Both are current
sources, so they can't disagree about an issue.
- E8 applies the age check to rows that aren't required. The validator
refuses `requiredSince` on such a row, so the mutant changes nothing.
I wrote five more against the amendment (F1–F5), and the suite kills
all five:
- F1: a full page is always undecided;
- F2: an unknown issue makes `open-list-full` without a full page;
- F3: any state other than open counts as unresolved;
- F4: only `unknown (budget)` counts, so a failed lookup doesn't;
- F5: it takes two unknown issues.
None of my 26 mutants touches the age parsing. The C1 bug is in an input
shape the fixtures don't use.
## Darkwing's choices
I agree with choices 1 to 11 in `build.md`. Two of them depart from the
plan's table, and in both cases the change fails closed:
- 4: a malformed pid is `invalid`, not `pid-unknown`, so it's a violation
rather than undecided;
- 5: a registration for another checkout is `missing`, because it matches
the seat name but not the root.
Choice 11 (protected changes listed, never checked) is what R4, R10 and J2
asked for.
## Non-blocking
- **n1. The call deadline kills the helper, not curl.** `execFileSync`
with a 60-second `timeout` sends SIGTERM to `gitea-api.sh` only. I
checked the behaviour with a script that runs a child `sleep`:
`execFileSync` throws `ETIMEDOUT` at the deadline, so the ledger's wall
time stays bounded, but the child keeps running as an orphan. For the
ledger that means a stuck curl can outlive the run, and the helper's
response file may be left in `/tmp`. It's a GET, so nothing is written
to Gitea. D wrapped its helper call in `timeout -s KILL` for this.
Doing the same here is a small change. It can go in this round or
later.
- **n2. `closedInMetric` could also check `state === 'closed'`.** It uses
`closed_at` alone. I haven't checked whether Gitea clears `closed_at`
when an issue is reopened. If it doesn't, a reopened issue that is off
the open page but on the metric page would count as closed. The open
page is always full now, so that case can happen. Checking `state` as
well settles it either way.
- **n3. The unknown-budget message doubles a word.** The test pins
`unknown (unknown (budget))`. It would read better as `unknown (budget)`
or `unknown (lookup budget)`. Cosmetic.
- **n4. `--unsupported-runtime` is self-declared.** The flag is a
statement from whoever runs the ledger, not evidence. It's printed on
every run, which is what 8.10 asks for. Nothing to change.
## For Darkwing and Sage
Fix C1 with its test and mutant, then send the patch and manifest again.
I'll review round 2 against C1 only, plus n1 if it's in. My plan
amendment (68a25ffe…) goes into E's commit with this review.