Files
stack/packages/bus/tests/limits-runtime.test.mjs
jason.woltje a8d9eecf65 organization: move packages/business to packages/organization (rename r2 stage B1)
git mv packages/business -> packages/organization (@mosaic/organization). The
old package stays as one-line re-export shims of each moved module, with
src/business.mjs re-exporting src/organization-config.mjs and src/cli.mjs
forwarding to the moved executable. BusinessError is OrganizationConfigError
(one constructor, name kept BusinessError); OrganizationError stays distinct.
The bus adapter gains organization-context.mjs (busOrganization), with
business.mjs (busBusiness) as the same function.

Import consumers, manifests, the lockfile, scripts/mosaic, stack-copy.sh
COPY_PATHS (both roots), seat sandbox RUNNER_FILES and the copy closure
walker (organization-imports.mjs, both roots required) move to the new path.
v1 serialization, v1 writers and the stage A fixtures are unchanged; the stage
A contract tests stay where they were and pass as before.
2026-11-09 11:47:48 -06:00

96 lines
6.3 KiB
JavaScript

// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): the runtime wires the limit checks. The limits verb
// carries each token limit's count, a reset moves since and the count follows, and the host's
// mayLaunch asks the shipped token-limit through the real loader.
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync, mkdirSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { setTimeout as delay } from 'node:timers/promises';
const SINCE = '2026-10-18T07:00:00.000Z';
const CODER = ['tokens', 'instances', 'coder'];
async function setup(t, plugins, limit = 0) {
const { startBroker } = await import('../src/runtime.mjs');
const { businessDoc, rolesCopy, systemFor, writeJson } = await import('../../organization/tests/helpers.mjs');
const root = mkdtempSync(join(tmpdir(), 'mosaic-limits-tokens-')), rolesDir = rolesCopy(root), doc = businessDoc(root, 'demo');
doc.plugins = { 'limit-check': plugins, harness: ['pi'] };
doc.launch.limits = { ...doc.launch.limits, tokens: { instances: { coder: { limit, since: SINCE } } } };
writeJson(join(root, 'businesses/demo.json'), doc);
mkdirSync(join(root, 'data'), { mode: 0o700 });
const bus = await startBroker({ dataRoot: join(root, 'data'),
businesses: { demo: { id: 'demo', human: 'jason', arbiters: { technical: 'coder', delivery: 'coder' },
roles: { coder: { definition: 'coder', authority: { withinRole: [], crossRole: [] } } }, definitions: { coder: {} }, projects: { stack: { root: '/nonexistent' } } } },
organization: { dir: root, rolesDir, system: systemFor(root) } });
t.after(async () => { await bus.close(); rmSync(root, { recursive: true, force: true }); });
return { bus, human: bus.broker.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }) };
}
test('REQ-LIMIT-3 REQ-LIMIT-4 runtime: limits carries the token count, mayLaunch refuses at the limit, a reset moves since', async t => {
const { bus, human } = await setup(t, ['token-limit']);
const view = bus.broker.request(human, { verb: 'limits' });
assert.equal(view.tokens.length, 1);
assert.deepEqual({ ...view.tokens[0], count: Number(view.tokens[0].count) },
{ path: CODER, limit: 0, since: SINCE, status: 'counted', check: 'token-limit', count: 0,
kinds: view.tokens[0].kinds, unknownTurns: 0 });
assert.deepEqual(await bus.mayLaunch({ business: 'demo', instance: 'coder', provider: 'zai', model: 'glm-5.3-flash' }),
{ allow: false, check: 'token-limit', hit: { path: CODER, limit: 0, count: view.tokens[0].count, unknownTurns: 0 } });
// An instance with no limit launches.
assert.deepEqual(await bus.mayLaunch({ business: 'demo', instance: 'reviewer', provider: 'zai', model: 'glm-5.3-flash' }), { allow: true });
const done = bus.broker.request(human, { verb: 'limits.set', args: { revision: view.revision, reset: [CODER] } });
assert.ok(done.tokens[0].since > SINCE, done.tokens[0].since);
assert.equal(done.tokens[0].status, 'counted');
});
test('REQ-LIMIT-3 runtime: with no limit check switched on, the limit shows not enforced and every launch is allowed', async t => {
const { bus, human } = await setup(t, []);
assert.deepEqual(bus.broker.request(human, { verb: 'limits' }).tokens,
[{ path: CODER, limit: 0, since: SINCE, status: 'not-enforced', reason: null }]);
assert.deepEqual(await bus.mayLaunch({ business: 'demo', instance: 'coder', provider: 'zai', model: 'glm-5.3-flash' }), { allow: true });
});
test('REQ-LIMIT-4 runtime: the broker asks the shipped token-limit after a turn, and the run at its limit stops on its next receive', async t => {
const { bus, human } = await setup(t, ['token-limit']);
const { broker } = bus;
const coder = broker.bindLaunch({ business: 'demo', role: 'coder', run: 'run1', harness: 'pi', provider: 'zai', model: 'glm-5.3-flash' });
broker.request(coder, { verb: 'role.claim' });
const { id } = broker.request(human, { verb: 'message.send', args: { to: 'coder', body: 'work' } });
broker.request(coder, { verb: 'message.receive' });
await broker.request(coder, { verb: 'usage.begin', args: { turn: id } });
await broker.request(coder, { verb: 'usage.end', args: { turn: id } });
assert.throws(() => broker.request(coder, { verb: 'message.receive' }), { code: 'not-holder' });
// endLaunch reads the reason from the claim row the stop wrote with by='limit-check'.
assert.deepEqual(bus.endLaunch({ business: 'demo', run: 'run1', reason: 'exited', exitCode: 22 }),
{ released: false, limit: 'limit: token-limit tokens.instances.coder limit 0 count 0' });
});
test('REQ-LIMIT-4 runtime: with no usage source a turn is unknown, counts zero and stops nothing under a limit of 1', async t => {
const { bus, human } = await setup(t, ['token-limit'], 1);
const { broker } = bus;
const coder = broker.bindLaunch({ business: 'demo', role: 'coder', run: 'run1', harness: 'pi', provider: 'zai', model: 'glm-5.3-flash' });
broker.request(coder, { verb: 'role.claim' });
const { id } = broker.request(human, { verb: 'message.send', args: { to: 'coder', body: 'work' } });
broker.request(coder, { verb: 'message.receive' });
await broker.request(coder, { verb: 'usage.begin', args: { turn: id } });
await broker.request(coder, { verb: 'usage.end', args: { turn: id } });
broker.request(coder, { verb: 'message.receive' });
const [row] = broker.request(human, { verb: 'limits' }).tokens;
assert.deepEqual({ count: row.count, unknownTurns: row.unknownTurns, status: row.status }, { count: 0, unknownTurns: 1, status: 'counted' });
assert.deepEqual(await bus.mayLaunch({ business: 'demo', instance: 'coder', provider: 'zai', model: 'glm-5.3-flash' }), { allow: true });
});
test('REQ-LIMIT-3 runtime: switching a limit check on with plugins.set loads it without a launch or a turn', async t => {
const { bus, human } = await setup(t, []);
const { revision } = bus.broker.request(human, { verb: 'plugins' });
bus.broker.request(human, { verb: 'plugins.set', args: { revision, plugins: { 'limit-check': ['token-limit'] } } });
let row;
for (let i = 0; i < 100; i++) {
[row] = bus.broker.request(human, { verb: 'limits' }).tokens;
if (row.status === 'counted') break;
await delay(20);
}
assert.equal(row.status, 'counted', JSON.stringify(row));
assert.equal(row.check, 'token-limit');
});