Files
stack/packages/plugins/tests/token-limit.test.mjs
T
jason.woltje ca15d73e9f limits: a daily token quota for the account the stack uses, and bus schema 3h to 3i (row 126, #1620)
The business file can name the account claude-code launches use,
`launch.account` = {name, credential}. `name` is an identifier and
`credential` names one login set's environment variables, never a value.
`launch.limits.tokens.daily.<account>` = {limit, since, reset, timeZone}
sets a daily quota for that account. A daily key that isn't the account's
name refuses the business read.

The new plug-in limit-check/daily-quota (interface 2, pure) starts a day at
the first instant at or after `reset` on that local date in `timeZone`. Across
DST that makes a 23- or 25-hour day, and a reset in a skipped hour starts at
the gap's end. It counts the account's known records of all four kinds since
the later of the day start and `since`, and counts unknown turns apart.

The launcher refuses account-credential-missing, account-credential-ambiguous
and account-is-credential (an account name equal to a session credential's
value). Messages name variables, never values. The account rides mayLaunch,
bindLaunch, the usage records, mustStop and the launch record. A pi launch
has no account, records NULL, and no daily quota covers it. `mosaic limits`
and the Console's Settings show one fixed line when a daily row shows:
"Daily quotas count only this stack's own use of the account, not its use
anywhere else."

Bus schema 3h to 3i: 3h reserved usage_records.account as
CHECK (account IS NULL). The new step 3h-3i.sql rebuilds the table in one
transaction, keeping every row's seq and the sqlite_sequence value, and
recreates the three append-only triggers. All of sqlite_master equals 3h's
apart from the account line. A 3h engine refuses a 3i store.
packages/bus/tests/account-migration.test.mjs covers all three, with a
step that fails partway and leaves 3h intact. 3i goes live only through
`stack-release.sh activate ... --migrate` with Jason present (P19 371); the
activate path takes the pre-migrate backup, and the way back is restoring
it (agents/dewey/work/queue-126/MIGRATION.md).

Mos's go mos-0835, six conditions: (1) one transaction, account-migration
test 2 and mutant D29; (2) sqlite_master against 3h, test 1 and mutant D28;
(3) account is never a credential, the launcher's account-is-credential
refusal (launch-account.test.mjs) and the business name pattern
(token-limit-settings.test.mjs); (4) MIGRATION.md, accepted by Mos in
mos-0837; (5) the packet's DESIGN.md "no schema change" line corrected;
(6) the public/ digest below.

The public/ change in bus.js (the daily line under the token-limit table)
carries Mos's decision 123 go mos-0833 on the +/- digest 03e6d9da...ba78,
unchanged at landing.

Owner Dewey, brief docs/plans/2026-10-14_prd-1.2-build.md, "Daily quota for
the credential the stack uses today". Review round 2 rebases the round 4
build onto 1df04f52f (row 167). Reviewer: Filbert approve 29068 on round 1
(manifest d18314f7...8e65); Filbert approve 29080 on
round 2 (manifest f332db76...6fa6).
Seat run on round 2: touched 158/0, singles 1388/0 with 1 esbuild skip,
test-land-work.sh 86/0, 30/30 mutants killed.
Sage gate on 54319f552: GATE_PASSED, 2474+422/0, all 15 packages, the queue
suite and 10 script selftests.

Nothing live.
2026-10-18 20:35:02 -05:00

152 lines
9.7 KiB
JavaScript

// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): the token-limit check on its own. It is pure, so
// these tests pass it limits and records and read its answers; no loader, store or file.
import test from "node:test";
import assert from "node:assert/strict";
import * as check from "../limit-check/token-limit/index.mjs";
const SINCE = "2026-10-18T00:00:00.000Z";
const NOW = "2026-10-18T12:00:00.000Z";
const subject = (over = {}) => ({ instance: "coder", provider: "anthropic", model: "opus", account: null, ...over });
let seq = 0;
const known = (over = {}, counts = [10, 20, 30, 40]) => {
const [input, output, cacheWrite, cacheRead] = counts;
return { at: "2026-10-18T06:00:00.000Z", run: "r1", turn: `t${++seq}`, source: "pi-session", instance: "coder",
provider: "anthropic", model: "opus", account: null, status: "known", input, output, cacheWrite, cacheRead, ...over };
};
const unknown = (over = {}) => ({ ...known(over), status: "unknown", input: null, output: null, cacheWrite: null, cacheRead: null });
const limits = tokens => ({ tokens });
const args = (tokens, records, over = {}) => ({ business: "demo", limits: limits(tokens), subject: subject(), records, now: NOW, ...over });
test("REQ-LIMIT-3 token-limit exports interface version 2 with mayLaunch, mustStop and report", () => {
assert.equal(check.interfaceVersion, 2);
for (const name of ["mayLaunch", "mustStop", "report"]) assert.equal(typeof check[name], "function");
});
test("REQ-LIMIT-3 each subject kind refuses at its limit and allows under it", () => {
const records = [known(), known()]; // 100 tokens each, 200 in all
const cases = [
{ tokens: { instances: { coder: { limit: 200, since: SINCE } } }, path: ["tokens", "instances", "coder"], subject: {} },
{ tokens: { providers: { anthropic: { limit: 200, since: SINCE } } }, path: ["tokens", "providers", "anthropic"], subject: {} },
{ tokens: { models: { anthropic: { opus: { limit: 200, since: SINCE } } } }, path: ["tokens", "models", "anthropic", "opus"], subject: {} },
{ tokens: { keys: { k1: { limit: 200, since: SINCE } } }, path: ["tokens", "keys", "k1"], subject: { account: "k1" },
records: records.map(r => ({ ...r, account: "k1" })) },
];
for (const c of cases) {
const recs = c.records ?? records;
const at = args(c.tokens, recs, { subject: subject(c.subject) });
const hit = { path: c.path, limit: 200, count: 200, unknownTurns: 0 };
assert.deepEqual(check.mayLaunch(at), { allow: false, hit }, c.path.join("."));
assert.deepEqual(check.mustStop(at), { stop: true, hit }, c.path.join("."));
const over = JSON.parse(JSON.stringify(c.tokens).replace('"limit":200', '"limit":201'));
assert.deepEqual(check.mayLaunch(args(over, recs, { subject: subject(c.subject) })), { allow: true }, c.path.join("."));
assert.deepEqual(check.mustStop(args(over, recs, { subject: subject(c.subject) })), { stop: false }, c.path.join("."));
}
});
test("REQ-LIMIT-3 a limit covers only its own subject's records and launches", () => {
const tokens = { instances: { coder: { limit: 150, since: SINCE } }, models: { anthropic: { opus: { limit: 150, since: SINCE } } } };
const records = [known({ instance: "tester", model: "sonnet" }), known({ instance: "tester", model: "sonnet" })];
// Another instance's and another model's records count toward neither limit.
assert.deepEqual(check.mayLaunch(args(tokens, records)), { allow: true });
// A launch of another subject isn't covered by a limit that is over.
const full = [known(), known()];
assert.deepEqual(check.mayLaunch(args(tokens, full, { subject: subject({ instance: "tester", model: "sonnet" }) })), { allow: true });
// A model limit is per provider: the same model name under another provider doesn't match it.
assert.deepEqual(check.mayLaunch(args({ models: { anthropic: { opus: { limit: 150, since: SINCE } } } },
full.map(r => ({ ...r, provider: "other" })), { subject: subject({ provider: "other" }) })), { allow: true });
// A provider limit counts only that provider's records and covers only its launches.
const byProvider = { providers: { anthropic: { limit: 150, since: SINCE } } };
const other = full.map(r => ({ ...r, provider: "other" }));
assert.deepEqual(check.mayLaunch(args(byProvider, other)), { allow: true });
assert.equal(check.report(args(byProvider, other))[0].count, 0);
assert.deepEqual(check.mayLaunch(args(byProvider, full, { subject: subject({ provider: "other" }) })), { allow: true });
assert.equal(check.mayLaunch(args(byProvider, full)).allow, false);
});
test("REQ-LIMIT-4 only known records count; an unknown turn adds nothing and is counted apart", () => {
const tokens = { instances: { coder: { limit: 100, since: SINCE } } };
const u = unknown({ run: "r2", turn: "u1" });
const records = [known({}, [1, 2, 3, 4]), u, { ...u, source: "other-source" }, unknown({ run: "r2", turn: "u2" })];
assert.deepEqual(check.mayLaunch(args(tokens, records)), { allow: true });
const [entry] = check.report(args(tokens, records));
assert.deepEqual(entry, { path: ["tokens", "instances", "coder"], limit: 100, since: SINCE, count: 10,
kinds: { input: 1, output: 2, cacheWrite: 3, cacheRead: 4 }, unknownTurns: 2 });
// With no usage source, every turn is the '@none' unknown record: the count is 0.
const none = [unknown({ source: "@none" }), unknown({ source: "@none" })];
const [empty] = check.report(args(tokens, none));
assert.equal(empty.count, 0);
assert.equal(empty.unknownTurns, 2);
assert.deepEqual(check.mayLaunch(args(tokens, none)), { allow: true });
});
test("REQ-LIMIT-3 a reset moves since: records before it no longer count", () => {
const tokens = { instances: { coder: { limit: 100, since: "2026-10-18T07:00:00.000Z" } } };
const records = [known({ at: "2026-10-18T06:59:59.999Z" }), known({ at: "2026-10-18T07:00:00.000Z" }, [1, 1, 1, 1])];
assert.deepEqual(check.mayLaunch(args(tokens, records)), { allow: true });
assert.equal(check.report(args(tokens, records))[0].count, 4);
const before = { instances: { coder: { limit: 100, since: SINCE } } };
assert.equal(check.mayLaunch(args(before, records)).allow, false);
});
test("REQ-LIMIT-3 limit 0 refuses with no records; the first limit hit in file order is named", () => {
assert.deepEqual(check.mayLaunch(args({ instances: { coder: { limit: 0, since: SINCE } } }, [])),
{ allow: false, hit: { path: ["tokens", "instances", "coder"], limit: 0, count: 0, unknownTurns: 0 } });
const tokens = { instances: { coder: { limit: 50, since: SINCE } }, providers: { anthropic: { limit: 50, since: SINCE } } };
assert.deepEqual(check.mustStop(args(tokens, [known()])).hit.path, ["tokens", "instances", "coder"]);
});
test("REQ-LIMIT-3 a key limit is not enforced while every record's account is null (decision 150 Q1)", () => {
const tokens = { keys: { k1: { limit: 1, since: SINCE } }, instances: { coder: { limit: 1000, since: SINCE } } };
const report = check.report(args(tokens, [known()]));
assert.deepEqual(report, [
{ path: ["tokens", "instances", "coder"], limit: 1000, since: SINCE, count: 100,
kinds: { input: 10, output: 20, cacheWrite: 30, cacheRead: 40 }, unknownTurns: 0 },
{ path: ["tokens", "keys", "k1"], limit: 1, since: SINCE, notEnforced: "no records name a key" },
]);
// A launch whose subject has no account isn't covered by a key limit.
assert.deepEqual(check.mayLaunch(args(tokens, [known()])), { allow: true });
// Once a record names an account, the key limit is counted.
const named = check.report(args(tokens, [known({ account: "k1" })]));
assert.equal(named[1].count, 100);
});
test("REQ-LIMIT-3 counts past the safe integer range come back as digit strings", () => {
const big = Number.MAX_SAFE_INTEGER;
const tokens = { instances: { coder: { limit: 1, since: SINCE } } };
const [entry] = check.report(args(tokens, [known({}, [big, big, 0, 0])]));
assert.equal(entry.count, (2n * BigInt(big)).toString());
assert.equal(entry.kinds.input, big);
assert.equal(check.mustStop(args(tokens, [known({}, [big, big, 0, 0])])).hit.count, (2n * BigInt(big)).toString());
});
test("REQ-LIMIT-4 bad records or times throw a code, and the check reads nothing but its arguments", () => {
const tokens = { instances: { coder: { limit: 1, since: SINCE } } };
for (const fn of ["mayLaunch", "mustStop", "report"]) {
assert.throws(() => check[fn](args(tokens, null)), { code: "bad-records" }, fn);
assert.throws(() => check[fn](args(tokens, [known({ at: "yesterday" })])), { code: "bad-time" }, fn);
assert.throws(() => check[fn](args(tokens, [known({}, [-1, 0, 0, 0])])), { code: "bad-record" }, fn);
assert.throws(() => check[fn](args({ instances: { coder: { limit: 1, since: "never" } } }, [known()])), { code: "bad-time" }, fn);
}
const input = args(tokens, [known()]);
const copy = structuredClone(input);
check.mayLaunch(input); check.mustStop(input); check.report(input);
assert.deepEqual(input, copy);
});
test("REQ-LIMIT-3 no token limits: every launch is allowed and the report is empty", () => {
for (const l of [{}, { tokens: {} }, { agents: 3 }]) {
const a = { business: "demo", limits: l, subject: subject(), records: [known()], now: NOW };
assert.deepEqual(check.mayLaunch(a), { allow: true });
assert.deepEqual(check.mustStop(a), { stop: false });
assert.deepEqual(check.report(a), []);
}
});
test("REQ-LIMIT-3 token-limit ignores tokens.daily, which the daily-quota check enforces (row 126)", () => {
const daily = { k1: { limit: 0, since: SINCE, reset: "05:00", timeZone: "America/Chicago" } };
const a = { business: "demo", limits: { tokens: { daily } }, subject: subject({ account: "k1" }), records: [known({ account: "k1" })], now: NOW };
assert.deepEqual(check.mayLaunch(a), { allow: true });
assert.deepEqual(check.mustStop(a), { stop: false });
assert.deepEqual(check.report(a), []);
});