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]>
286 lines
15 KiB
Markdown
286 lines
15 KiB
Markdown
# Queue E (#1508, row 13), candidate for review, round 2
|
|
|
|
Darkwing, 2026-09-27. Piece E is the ledger's queue section, per plan
|
|
section 8.10 (`agents/filbert/work/queue-as-data-plan-2026-09-26.md`) and
|
|
the brief's "Piece E: ledger checks the queue". The ledger now checks
|
|
`docs/plans/queue.json` against Gitea and the seat registrations and prints
|
|
the result above the weekly table. Filbert reviews E. Sage commits. Nothing
|
|
is committed, staged or pushed.
|
|
|
|
Round 2 answers Filbert's round 1 review
|
|
(`agents/filbert/work/queue-e-review-r1-2026-09-27.md`, sha256 81f26f2e…):
|
|
C1, plus n1, n2 and n3. See "Round 2" below. The round 1 files are kept
|
|
unchanged in `r1/` (patch 02a01c29…, manifest cad51929…, build.md
|
|
d3bdb826…).
|
|
|
|
Round 1 was amended before its verdict for lead decision 40 (origin
|
|
2333d837): a full open-issue page is undecided only while some issue a row
|
|
closes has no known state. See "The full page" below.
|
|
|
|
Base is f3f48cfd. Nothing under `packages/ledger`, `packages/queue/src` or
|
|
`packages/seat/src` changed between it and origin 2333d837. In a fresh
|
|
clone at 2333d837 the patch applies, the result matches the manifest 5/5,
|
|
and `node --test packages/ledger/tests/` passes 78/78 in three runs.
|
|
|
|
## Files
|
|
|
|
`build.patch` (sha256
|
|
`ab1f12cad711284f8a722ea51fa73cd8e344c703701f8b76957ae33de091ae84`) changes 3
|
|
files and adds 2. `build-manifest.sha256` (sha256
|
|
`0b20bbca6c9e0a00d39dc7aae0ca2e268b6908c4b24e9f9b401df8268d29c5fe`) pins all 5
|
|
after the patch.
|
|
|
|
- `packages/ledger/src/queue-checks.mjs` (new): reads the queue, classifies
|
|
owners, makes the issue calls, runs the checks, lists protected changes,
|
|
formats the section.
|
|
- `packages/ledger/src/cli.mjs`: `--no-queue`, `--unsupported-runtime SEAT`
|
|
(repeatable), the queue read before any Gitea call, the section above the
|
|
table and under `queue` in `--json`.
|
|
- `packages/ledger/README.md`: a "Queue section" with the rules, the call
|
|
budget, the result levels and the weekly routine. The heading "One Gitea
|
|
call" becomes "Gitea calls", and the exit-code paragraph names the queue.
|
|
- `packages/ledger/tests/queue-checks.test.mjs` (new, 20 tests).
|
|
- `packages/ledger/tests/ledger.test.mjs`: the fixture copies
|
|
`packages/queue/src` and `packages/seat/src`, which the ledger now
|
|
imports, and its runs pass `--no-queue`. Those tests cover the weekly
|
|
table; the new file covers the queue section. 58 tests before, 78 now.
|
|
|
|
`docs/TOOLS.md` has no ledger entry today, so there is no TOOLS patch. The
|
|
README is the reference, as it was for Piece 3.
|
|
|
|
## What a run prints
|
|
|
|
```
|
|
Queue checks: queue.json revision 19, as of 2026-09-27T16:09:10.883Z
|
|
owner darkwing (row 13): exempt, declared with --unsupported-runtime
|
|
...
|
|
liveness: 0 pid-present (unverified), 3 exempt, 0 pid-unknown, 0 missing, 0 invalid, 0 pid-gone
|
|
declared unsupported runtime: darkwing, dewey, sage
|
|
queue issue checks: open list (full page), 2 lookups
|
|
protected changes in range: 0 (not checks; confirm the actors)
|
|
queue: 0 violations; result reduced pass
|
|
```
|
|
|
|
Each finding is a line `violation|undecided|disposition <check> <row> <#issue>:
|
|
<message>`, so its identity (check, row, issue) can be read off the line for
|
|
the same-day remediation run. The JSON carries the same objects.
|
|
|
|
## Choices and where they differ from the plan
|
|
|
|
1. **A new module.** The plan's file list puts `queueChecks` in
|
|
`ledger.mjs`. I put the section in `queue-checks.mjs`, as `t3.mjs` did
|
|
for the T3 source, so `ledger.mjs` stays the metric code. The CLI
|
|
wires it in.
|
|
2. **Registrations through `readRegistration`**, not the control board's
|
|
scan. It returns a record, null or a thrown error per seat, with no text
|
|
to parse, and it keeps the board's import chain out of the ledger.
|
|
Liveness is its own signal-0 probe; EPERM counts as present.
|
|
3. **Closure uses `closes`, not `issues`** (J6). Rows 9 to 12 name #1508
|
|
but close nothing, so they can be done while it is open. The brief's
|
|
literal "a row naming an open issue" would flag them.
|
|
4. **A malformed or absent pid is `invalid`, not `pid-unknown`.** The plan's
|
|
table puts it under `pid-unknown`. `validateRegistration` rejects a pid
|
|
that is not a positive integer or null, so the record fails validation
|
|
first. `invalid` is a violation where `pid-unknown` is undecided, so the
|
|
difference fails closed. `pid-unknown` is a valid record with a null pid.
|
|
5. **A registration for another checkout is `missing`.** The plan defines
|
|
missing as no registration for (canonical root, `repo`, seat). A `repo`
|
|
record whose `sessionsDir` is not under the queue's canonical root
|
|
matches the seat name but not the root.
|
|
6. **An unreadable config makes every non-exempt owner `invalid`**, and
|
|
the report still runs. It does not refuse, because the other checks don't
|
|
need the data root.
|
|
7. **Refusals.** A missing, symlinked or hand-edited `queue.json` exits 1
|
|
before any Gitea call; the message names `--no-queue`. A failed or
|
|
malformed open-list call exits 2, like the metric call, and names
|
|
`--no-issues`. A failed lookup, a 404, a pull request or a number that
|
|
doesn't match leaves only that issue unknown.
|
|
8. **On by default.** `--no-queue` skips the section and prints `Queue: not
|
|
checked (--no-queue)`. It can't be combined with `--unsupported-runtime`.
|
|
`--unsupported-runtime` is the only repeatable flag; a repeated seat is
|
|
refused.
|
|
9. **Age is as of the run**, not `--until`: the gate is about today's queue,
|
|
and the table's range doesn't move it. "More than 14 days" is whole days,
|
|
so a row required on 2026-09-13 turns on 2026-09-28.
|
|
10. **Exit 0 whenever a report was computed.** The result is in the text and
|
|
the JSON, like every other number the ledger prints.
|
|
11. **Protected changes (added).** The 8.10 checks don't include it, but R4
|
|
and R10 ("E lists every protected change from [the journal]"), J2 and
|
|
the queue README's trust boundary ("piece E lists changes for review")
|
|
do. So the section lists every log entry dated inside `--since`/`--until`
|
|
that changes a row which is required or parked before or after the
|
|
entry, with rev, verb, claimed actor and rows. It replays the log up to
|
|
the range with the queue's own `replay` and `applyEntry`. It is a list,
|
|
not a check: it never changes the result. For 2026-09-20 to 2026-09-26
|
|
it lists nothing, because genesis was 2026-09-27; for a range that
|
|
includes today it lists 18 entries at rev 19, on rows 8 to 13 and 26 to 30.
|
|
|
|
## Round 2
|
|
|
|
- **C1, an ISO `requiredSince` never aged.** `set required` and `add
|
|
--required` write an ISO time, and round 1 appended `T00:00:00Z` to it,
|
|
which parses to NaN. Now `Date.parse` reads the value as it is (a bare
|
|
date parses as 00:00Z), and the result is floored to its UTC day. One
|
|
deviation from the suggested fix: an ISO time counts from 00:00Z of its
|
|
day, not from its hour, so both forms age in whole UTC days and a Monday
|
|
run's result doesn't depend on the hour a row was made required. A row
|
|
required at 23:59Z on 2026-09-13 turns on 2026-09-28, as a date-only row
|
|
does. A value that doesn't parse is an `age-invalid` violation. I chose
|
|
a violation over undecided because row 13's gate counts violations, and
|
|
a bad value is a queue defect. The queue validator lets one through:
|
|
`ISO_RE` checks the shape, so `2026-13-01T00:00:00.000Z` passes it. That
|
|
gap is in `packages/queue/src/queue.mjs`, outside E; I'm raising it as a
|
|
follow-up, not fixing it here.
|
|
- **n1, the orphaned curl.** Each queue call now runs as `timeout -s KILL
|
|
60 gitea-api.sh GET ...`, as D's `callTool` does, so the kill takes the
|
|
helper's process group. A kill reads `no answer within 60 s`, and exit
|
|
126 or 127 from `timeout` (no helper) reads `gitea-api.sh unavailable`.
|
|
The metric call in `ledger.mjs` still uses execFileSync's timeout. It
|
|
isn't in this patch, so that is a follow-up too.
|
|
- **n2, a reopened issue.** Closed evidence from the metric page now needs
|
|
`state: "closed"` and a `closed_at`. Either one alone leads to a lookup.
|
|
- **n3.** The budget detail is `over the lookup budget`, so the message
|
|
reads `unknown (over the lookup budget)`.
|
|
|
|
Two tests are new. One covers an ISO `requiredSince` at 15 days (fails), at
|
|
14 (passes), at 23:59Z fifteen days back (fails), and one that doesn't
|
|
parse (`age-invalid`, result fail). The other gives the calls a hanging
|
|
helper that starts a hanging child and a 1-second deadline. Both calls
|
|
return at the deadline and neither process survives. A missing helper
|
|
reads `gitea-api.sh unavailable`. The metric fixture adds a reopened issue
|
|
and a `state: "closed"` entry with no `closed_at`, and both are looked up.
|
|
|
|
Round 2 mutants, all 12 killed:
|
|
|
|
1. The round 1 template restored.
|
|
2. No floor to the UTC day.
|
|
3. No NaN guard.
|
|
4. NaN as undecided.
|
|
5. Ceiling instead of floor.
|
|
6. The metric check on `closed_at` alone.
|
|
7. The metric check on `state` alone.
|
|
8. A Node timeout with SIGKILL in place of `timeout`, with ETIMEDOUT
|
|
ignored. The orphan assertion kills it.
|
|
9. The kill flag always false.
|
|
10. The kill flag read from the exit status alone.
|
|
11. No 126/127 mapping.
|
|
12. The old budget detail.
|
|
|
|
A first version kept the suggested `DATE_RE` branch, and dropping it was an
|
|
equivalent mutant because `Date.parse` already reads a bare date as 00:00Z.
|
|
I removed the branch.
|
|
|
|
## The full page (lead decision 40)
|
|
|
|
mosaicstack/stack has 50 or more open issues, so the open list is always a
|
|
full page. Plan 8.10 made a full page undecided on its own. But an issue
|
|
missing from the page is looked up, so the page matters only when an issue
|
|
is left without a known state, and that issue is already undecided. Sage
|
|
approved the change. Now `open-list-full` is added only when the page is
|
|
full and some issue a row closes is `unknown`, and its message names
|
|
those issues. The text still prints `open list (full page)`, and the JSON
|
|
keeps `openListFull`.
|
|
|
|
Tests cover a full page with every issue resolved (no undecided item), an
|
|
open issue off the page found by lookup (its done row fails), and a full
|
|
page with an issue past the budget (still incomplete). They drive the fake
|
|
helper end to end through `issueStates` and `queueChecks`.
|
|
|
|
## Live run (read-only)
|
|
|
|
Round 2, in a fresh clone at 2333d837 with the patch applied, through the
|
|
new `timeout` path: the same result as below with the exemptions, 0
|
|
violations, `reduced pass`, 2 lookups, exit 0.
|
|
|
|
Round 1, at 2333d837 in the verify clone, with my own token file in
|
|
`MOSAIC_GITEA_CREDENTIAL_FILE`, for 2026-09-20 to 2026-09-26: exit 0, the
|
|
metric call, the open list and 2 lookups. That is GET only, and nothing
|
|
was posted.
|
|
|
|
- Plain run: 3 violations, result `fail`. Rows 13 (darkwing) and 5 (dewey)
|
|
are `pid-gone`, from old pi launches; row 7 (sage) is `missing`. All
|
|
three seats run in T3, which writes no registration.
|
|
- With `--unsupported-runtime` for darkwing, dewey and sage: 0 violations,
|
|
result `reduced pass`. Before the amendment the same run was
|
|
`incomplete`, from the full page alone.
|
|
- No issue violations: every issue in a done row's `closes` is closed.
|
|
|
|
This is not the acceptance run. That is a dated run posted on #1508 after
|
|
approval.
|
|
|
|
## Lead decision 40
|
|
|
|
Sage ruled on the five points I raised:
|
|
|
|
1. The full page: approved as amended above.
|
|
2. Row 7's brief pins `packages/ledger/README.md` at blob 3a2ce27c. Sage
|
|
re-pins it with `set 7 brief` in a queue commit right after E lands.
|
|
3. Row 7 stays. Its gate is Jason's, so Q9 is amended. The weekly routine
|
|
stays in the README.
|
|
4. The age rule stays. From 2026-09-28 the Monday run lists rows 9, 10, 11
|
|
and 13, which is accurate.
|
|
5. The T3 exemption is accepted as built. The weekly run declares every T3
|
|
seat that owns an active row.
|
|
|
|
## Tests
|
|
|
|
`packages/ledger/tests/queue-checks.test.mjs`. In-process checks run on
|
|
fixture rows with a fixed clock and an injected pid probe. CLI tests use a
|
|
scratch repository whose `queue.json` the real queue CLI wrote, a temporary
|
|
config and data root, and a fake `gitea-api.sh` that routes the open list,
|
|
single issues and the metric page and logs every call. No test reads a real
|
|
token, registration, config or `~/.t3`; HOME and MOSAIC_CONFIG are
|
|
temporary.
|
|
|
|
- Issues: done with the issue open (fail) or closed (pass); a multi-row
|
|
issue open while one closer is pending, closed early (disposition), and
|
|
open with both done; rows 9 to 12's shape (issues without closes);
|
|
unknown and not run (incomplete); a full page is undecided only beside an
|
|
unknown issue.
|
|
- Issue calls: open list first, metric page next, then at most 10 lookups
|
|
in order and `unknown (budget)` after; a pull request on the open list is
|
|
not an issue; lookups of a pull request, a mismatched number and a 404
|
|
are unknown; a metric entry with no `closed_at` is not closed evidence;
|
|
a full page. The open list refuses on exit 3, exit 1, bad JSON and bad or
|
|
closed records, and never echoes the helper's stderr.
|
|
- Owners: every class in one run, including another checkout and a broken
|
|
record; two rows for one owner; briefed and done owners not checked; no
|
|
config.
|
|
- Age: 15 days fails, 14 doesn't; done and not-required rows are skipped;
|
|
the legacy bound at 20 days (fail), exactly 14 and 3 (undecided); an ISO
|
|
`requiredSince` by UTC day, and one that doesn't parse (round 2).
|
|
- Deadline: a hanging helper and its child are both killed (round 2).
|
|
- `pidAlive`: running, exited, and EPERM (pid 1, non-root).
|
|
- `readQueue`: a real queue, a hand edit, a missing file, a symlink.
|
|
- Protected changes: genesis, a note on a required row, a note on an
|
|
ordinary row (not listed), an unpark by jason (listed from the row
|
|
before), and range boundaries (start included, end excluded).
|
|
- CLI: section above the table; `--json` key; a clean queue prints `queue:
|
|
0 violations; result reduced pass`; call counts (3 with issues, 0 with
|
|
`--no-issues`, 1 with `--no-queue`); refusals cost no call; flag errors.
|
|
|
|
Mutation testing, round 1: 37 hand-made mutants of `queue-checks.mjs` and the CLI
|
|
wiring (boundaries, each class, each result level, budget, filters,
|
|
refusals, the protected-change range and guard, the full-page rule). All
|
|
37 are killed. The
|
|
first pass left three alive (the 14-day genesis edge, EPERM, a null
|
|
`closed_at`) and a later one three more (the before-row guard and both
|
|
range edges); the tests above were added for them.
|
|
|
|
Round 2 adds the 12 listed above.
|
|
|
|
Suites: `node --test packages/ledger/tests/` 78/78, three runs;
|
|
`packages/queue/tests` and `packages/seat/tests` 161/161.
|
|
|
|
## Verify
|
|
|
|
```sh
|
|
git clone -q /mnt/storage/src/mosaic-stack /tmp/e && cd /tmp/e
|
|
git checkout -q 2333d837
|
|
git apply /mnt/storage/src/mosaic-stack/agents/darkwing/work/queue-e/build.patch
|
|
sha256sum -c /mnt/storage/src/mosaic-stack/agents/darkwing/work/queue-e/build-manifest.sha256
|
|
ln -s /mnt/storage/src/mosaic-stack/node_modules node_modules
|
|
node --test packages/ledger/tests/
|
|
node packages/ledger/src/cli.mjs --since 2026-09-20 --until 2026-09-26 --no-t3 --no-issues
|
|
```
|