plans: row 126 brief rewritten on Dewey's tokens.daily design after row 125 landed (#1620)

Row 125's core lets a check see only launch.limits and accept hits on the
four DEPTH kinds, so the quota gets its own depth-1 kind,
tokens.daily.<account>, in the two DEPTH tables and the organization-file
schema. Display is a fixed line keyed on the daily path; no report field.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-18 15:27:07 -05:00
co-authored by Claude Opus 5.5
parent de74dd4735
commit 7ce156259d
+57 -17
View File
@@ -485,26 +485,66 @@ Build step 9a, small to medium. Gitea #1620.
- Reviewer: filbert (organization file and launcher, no sandbox change).
- Requirements: REQ-LIMIT-2, REQ-LIMIT-4.
Sage rewrote this section on 2026-10-18, after row 125 landed (aa625565f),
with Dewey's option A from
`agents/dewey/work/queue-126/DESIGN-NOTES.md`. The first version said the
quota needed no change outside its own check. Row 125's core doesn't allow
that: a check sees only `launch.limits`, a hit must name a leaf that
`limitLeaves` returns, and `limitLeaves` walks only the four token kinds in
`DEPTH` (`packages/bus/src/limits.mjs:32`). A quota on `tokens.keys.<account>`
would also be counted forever by `token-limit`, which counts every key leaf
since its `since`.
**What ships.**
1. The organization file names the one account the bus uses today
(`SESSION_CREDENTIALS`, `packages/seat/src/session.mjs:76`, decision
119) by a name and a credential reference, never the secret
(invariant 3). Launches log the account name, and step 7's records
carry it.
2. A daily quota on that account, in tokens (a draft position, PRD
REQ-LIMIT-2), with a reset time and time zone. It ships as a second
limit check, `packages/plugins/limit-check/daily-quota/`, with no
change to the launcher or bus.
3. At the quota, new launches on the account are refused and running
sessions stop at the end of their current turn. Both are logged.
4. Settings and `mosaic limits` show the quota with the line that it
counts only the stack's own use (lead decision 142, ruling 9).
1. **The account name.** The organization file names the one account the
bus uses today (`SESSION_CREDENTIALS`,
`packages/seat/src/session.mjs:98`, decision 119) by a name and a
credential reference: the name of the `SESSION_CREDENTIALS` set the bus
uses, never the secret (invariant 3). The launcher
(`packages/cli/src/launcher.mjs`) logs the account name on each launch.
Usage records carry it as `account` instead of `NULL`
(`packages/bus/src/usage.mjs:93`), so `tokens.keys.<account>` limits,
which match nothing today, start to count.
2. **A new token kind, `tokens.daily.<account>`.** Its leaf is
`{limit, since, reset, timeZone}`: `limit` in tokens (a draft position,
PRD REQ-LIMIT-2), `reset` a wall-clock `HH:MM` and `timeZone` an IANA
name. It is a depth-1 kind beside `instances`, `providers`, `keys` and
`models`. Three places learn it, and nothing else in core changes: the
`DEPTH` table in `packages/bus/src/limits.mjs`, the `DEPTH` table in
`packages/business/src/launch-settings.mjs` (which stamps and resets
`since`), and `checkTokenLimits` in `packages/business/src/launch.mjs`
(the organization-file schema). `token-limit` doesn't read `daily`, so
the two checks never share a leaf.
3. **The check**, `packages/plugins/limit-check/daily-quota/`, a second
limit check. The day window starts at the latest `reset` time in
`timeZone` at or before `now`, found with `Intl.DateTimeFormat`. The
count runs from the later of that start and `since`, over records whose
`account` is the leaf's account.
4. **Refusal and stop** use core's existing `mayLaunch` and `mustStop`
paths. At the quota, new launches on the account are refused and
running sessions stop at the end of their current turn. Both are logged
as `limit-reached` with the check's name.
5. **Display.** Settings and `mosaic limits`
(`packages/cli/src/limits.mjs`) show the daily quota with a fixed line,
keyed on the `daily` path, saying it counts only the stack's own use
(lead decision 142, ruling 9). No new report field: core's report
validation stays as row 125 left it.
**Acceptance.** Tests for the count, the day boundary in a non-UTC time
zone, the refusal and the stop. Outside
`packages/plugins/limit-check/daily-quota/`, the diff touches only item
1's account naming and the organization-file schema. Mutants for each.
**Acceptance.**
- Tests for the count; the day boundary in America/Chicago across the
reset; both DST changes; a `reset` time that a DST change skips or
repeats; a refusal at the quota; a stop at the end of the turn that
crosses it; and a new launch allowed after the reset.
- Tests that `token-limit` ignores `tokens.daily`, that a `daily` leaf
with a bad `reset` or `timeZone` fails the organization-file read, and
that a launch logs the account name and its usage records carry it.
- Outside `packages/plugins/limit-check/daily-quota/`, the diff touches
only item 1's account naming, the two `DEPTH` tables, the
`checkTokenLimits` schema and item 5's fixed line, with their tests and
docs. No sandbox change.
- One mutant per behavior above, each with the test that kills it.
## Quota for more than one account or per proxy key (parked on #237)