docs: brief for the ledger reasoning-row and issue-page fix (#1607)

Two fail-closed refusals stop every full weekly ledger run: T3's new
role 'reasoning' summary rows, and more than 50 issues updated since
the week's start. Owner darkwing after row 112, reviewer filbert.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 10:54:59 -05:00
co-authored by Claude Opus 5.5
parent 717e2d35b8
commit 960988bae2
@@ -0,0 +1,102 @@
# Ledger reads current T3 and a busy week's issues (2026-10-14)
Filed by Sage while running row 7's overdue week (2026-10-04 through
2026-10-10). Gitea #1607. Row 7 and row 13's Monday-run gate both wait
on this.
## The weekly ledger runs on current T3 and a week with more than 50 updated issues
### Problem
Two refusals stop every full weekly run today. Each one is a fail-closed
check doing its job, so the fix is to the checks, not to the run.
1. T3 messages with role `reasoning`. Since 2026-10-13T14:01Z, T3 stores
reasoning summaries in `projection_thread_messages` with role
`reasoning` and ids starting `reasoning:summary:` (2,975 rows on
2026-10-14; every one has that id prefix). `packages/ledger/src/t3.mjs:97`
refuses any role other than `user` or `assistant`, so the run exits 1
with `T3 message reasoning:summary:… has an unknown role`. `--no-t3`
is the only way past it, and that drops the seat table and the human
message count.
2. More than 50 issues updated since the start date.
`packages/ledger/src/ledger.mjs:46-68` reads one page of 50 and refuses
a full page (exit 2). The query is updated-since with no upper bound, on
purpose, so a close in range isn't hidden by a later update. That also
means `--until` can't narrow it. On 2026-10-14 the week from 2026-10-04
matched 132 issues (pages of 50, 50 and 32). `--no-issues` is the only
way past it, and that leaves issues closed, median hours open and human
messages per closed issue unknown.
The one-page rule came from the 2026-09-12 ledger brief (#1506, BUILD-LOG
"Single-request constraint"). It was there to stop silent partial totals,
not to save calls. Sage, as lead, replaces it here with a bounded page
loop that still refuses rather than guessing. The tradeoff is up to 10 GET
calls per run instead of 1.
### Owner and reviewer
- Owner: darkwing, after row 112.
- Reviewer: filbert (decision 130, not a sandbox row).
### Files owned
- `packages/ledger/src/t3.mjs`
- `packages/ledger/src/ledger.mjs`
- `packages/ledger/src/cli.mjs`, only if the coverage line or excluded
counts print there
- `packages/ledger/tests/` (changed or new tests and fixtures)
- `packages/ledger/README.md`
### What ships
1. T3 reasoning summaries are skipped, not refused:
- A message with role `reasoning` whose id starts `reasoning:` is not
counted anywhere, and the report shows how many were skipped next to
the imported and deleted thread counts.
- Role `reasoning` with any other id prefix still refuses, and so does
any other role that isn't `user` or `assistant`.
- Skipping happens before the text, `created_at` and header checks, so
a summary's content can't refuse or change a run.
2. Issues are read in pages, with a hard ceiling:
- Same query as today (all states, type issues, updated since the
start, limit 50), pages 1, 2, 3 and so on, stopping at the first page
with fewer than 50 rows.
- At most 10 pages. If page 10 is full, refuse with exit 2 and say the
ceiling was reached; never report partial totals.
- Every page gets today's record checks. A duplicate issue number
across pages refuses with exit 2, since an update during the read can
shift rows between pages.
- Any failed page refuses the whole read with today's error text rules,
with no stderr or response body echoed.
- The report says how many pages it read.
3. README: the T3 section names the `reasoning` skip and the remaining
refusals. "Gitea calls and missing evidence" describes the page loop,
the 10-page ceiling and the duplicate refusal, and drops "one-call
boundary" and "not hidden pagination".
4. Tests, offline with fixtures as today:
- A T3 fixture with a `reasoning:summary:` row counts the same as one
without it, and reports one skipped.
- Role `reasoning` with id `x-1` refuses, and so does role `system`.
- A fake gitea tool serving pages of 50, 50 and 7 yields 107 issues and
3 pages. 10 full pages refuse. A duplicate across pages refuses. A
failing page 2 refuses.
- A mutant that drops the id prefix check, and one that stops after
page 1, each fail a test. Record both in the packet.
5. Suites: every package suite and every `scripts/test-*.sh` on the packet
gate.
### Out of scope
- The open-issue list in `queue-checks.mjs` (`OPEN_LIMIT`, its `full`
flag). It reports rather than refuses; it's a separate row if it matters.
- Row 32's guideposts. That row stays parked.
- Running the week. Sage reruns 2026-10-04 through 2026-10-10 after
landing and posts it on #1508 under row 7.
### Gate
Filbert approves on the issue, naming the candidate manifest. Sage reruns
the full gate at the landing target. No live service runs this code, so
there are no Mos notices and no restart. After landing, Sage runs the week
without `--no-issues` or `--no-t3` and exits 0.