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]>
169 lines
7.9 KiB
Markdown
169 lines
7.9 KiB
Markdown
# 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.
|