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]>
7.9 KiB
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:
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"→ violationsowner-invalid, age, resultfail;requiredSince: "2026-09-01T12:00:00.000Z"→ violationsowner-invalidonly. 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_REvalue parses asT00:00:00Z; anything else goes toDate.parseas it stands. - If the result is NaN, fail closed: an
ageundecided 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
requiredSince15 days old fails and one 14 days old doesn't, next to the existing date cases. Your fixtures buildrequiredSincewithsince(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
requiredSinceis 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-fullis added only when the page is full and some issue inwantedisunknown.wantedis the union of every row'scloses, 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 towardincomplete, and it matches the decision's wording ("some wanted issue"). Keep it.- The new end-to-end test drives the fake helper through
issueStatesandqueueChecksfor 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, andpackages/queue/testswithpackages/seat/testspasses 161/161. The first version had passed 75/75 and 161/161. -
Source. I read
queue-checks.mjsin full, and the diffs tocli.mjs, the README and both test files. I compared the amendment with the first version file by file. Onlyqueue-checks.mjs(the rule and its comment), the README (two passages) andqueue-checks.test.mjschanged. -
Local run on the first version, for 2026-09-27 with
--no-issues --no-t3and three seats declared: 0 violations, resultincomplete(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
requiredSinceon 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-fullwithout 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, notpid-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.
execFileSyncwith a 60-secondtimeoutsends SIGTERM togitea-api.shonly. I checked the behaviour with a script that runs a childsleep:execFileSyncthrowsETIMEDOUTat 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 intimeout -s KILLfor this. Doing the same here is a small change. It can go in this round or later. - n2.
closedInMetriccould also checkstate === 'closed'. It usesclosed_atalone. I haven't checked whether Gitea clearsclosed_atwhen 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. Checkingstateas well settles it either way. - n3. The unknown-budget message doubles a word. The test pins
unknown (unknown (budget)). It would read better asunknown (budget)orunknown (lookup budget). Cosmetic. - n4.
--unsupported-runtimeis 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.