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.
152 lines
9.7 KiB
JavaScript
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), []);
|
|
});
|