Files
jason.woltje f65faf9df8 fix(row 333 R4): a moved key quota keeps its count; orphan set on every refresh (#1831)
Ruling 1: the bus runtime passes launchSettings a key lineage lookup read
from its own credential records (ids only). A new daily key quota takes the
earliest stored since among the old document's daily key quotas in its key's
lineage, and now only when there is none. A reset still sets now, a
caller-supplied since still refuses, and a quota removed and added back in a
later write is new. A failed or malformed lookup refuses proxy-key-refused; a
write that adds no new daily key quota never calls it.

Ruling 2: every limits refresh updates the remembered orphan set, an empty
set when no daily key quota is left, so a quota removed and added back logs
again. In memory only: a restart is a fresh observation.

Ruling 3: pollSettledAfterTick yields on the real setImmediate, captured at
load, inside its deadline.

Tests: p5-key-lineage (writer move counts 160, reset restarts, lookup
failure, orphan remove and re-add), p5-runtime (exhausted on A, moved to B
through the real runtime, refuses), p5-background (barrier with every timer
mocked), token-limit-settings (earliest, no match, re-add, caller since,
malformed answers, default refusal). READMEs: business, plugins, daily-quota,
cliproxy-queue.
2026-11-02 19:02:45 -06:00
..

Daily quota check

Row 126 (#1620), REQ-LIMIT-2 and REQ-LIMIT-4, limit-check interface 2. Enable with "plugins": { "limit-check": ["daily-quota"] } in the organization file, keeping its other plug-in kinds. Human changes use mosaic plugins set and the current revision.

It enforces launch.limits.tokens.daily, one quota per account:

"daily": {"jason-max": {"limit": 40000000, "since": "…", "reset": "05:00", "timeZone": "America/Chicago"}}
  • The account is launch.account.name. Only a claude-code launch carries it; a pi launch has account null, so no daily quota covers it.
  • A day starts at reset, wall-clock time in timeZone. A date's reset is the earliest instant on that local date at or after reset: when the clocks skip it, the day starts at the first instant after the gap (02:30 on 2026-03-08 in America/Chicago is 03:00 CDT); when they repeat it, the first one starts the day and the second starts nothing. A day with a DST change is 23 or 25 hours long.
  • A count is input + output + cacheWrite + cacheRead summed over the known per-turn records whose account is the quota's and whose at is at or after the later of the day's start and since. An unknown turn adds nothing; unknownTurns counts the distinct unknown turns apart, so a count is a floor, not proof.
  • mayLaunch and mustStop answer the first quota for the subject's account whose count is at or over it. A limit of 0 refuses every launch on the account.
  • report gives one counted entry per quota for the day that holds now. Its since is the quota's own since, not the day's start.
  • The count is this stack's own use of the account. Use of the same account anywhere else isn't in the records, and the CLI and Settings say so.
  • Counts are summed as BigInt; one past Number.MAX_SAFE_INTEGER is reported as a digit string.

Model-key quotas (row 333)

A launch.limits.tokens.keys.<12-hex key id> leaf with the same shape is a daily quota on one agent's model key. Core passes lineages, one array of key ids per hired agent, whenever such a leaf exists. The leaf applies to a subject whose modelKeyId is in the lineage holding the leaf's id and counts every record of that lineage, so rotation and re-issue keep the quota and its count. A leaf whose id is in no lineage (the agent was fired) counts its own id only and limits no launch. Missing or malformed lineages, or an id in two lineages, throws bad-lineages; account quotas never need them.

since is the settings writer's, not this module's (row 333 R4). A new key quota takes the earliest since among the old document's key quotas in its lineage, so a quota moved to the agent's new key id keeps its count. A reset sets now, and so does adding a quota that no earlier one in its lineage holds, including one removed in an earlier write and added back.

The module is pure: it reads no file and no store. A bad since, record at or now throws bad-time, a bad amount bad-record, records that aren't a list bad-records, and a quota without a whole limit, an HH:MM reset or a zone Intl accepts bad-leaf; core turns any throw into a refusal, a stop, or check failed (fail closed).