// Row 175 (#1673, Q173, mos-0870 N2, mos-0872): the test system's tester. It reads what the human // reads, makes seven writes recorded with actor `tester`, and every other human action still // refuses it tester-refused. Row 177 (#1675) adds the thread verbs, tested in tester-threads.test.mjs. import test from 'node:test'; import assert from 'node:assert/strict'; import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { Store } from '../src/store.mjs'; import { Broker, TESTER_VERBS, TESTER_ORGANIZATION_VERBS } from '../src/broker.mjs'; import { startBroker } from '../src/runtime.mjs'; import { businessDoc, rolesCopy, systemFor, writeJson } from '../../organization/tests/helpers.mjs'; const none = { authority: { withinRole: ['message.send'], crossRole: [] } }; // Row 177 (#1675): thread.list left this list; the tester's lists its own threads (tester-threads.test.mjs). const READS = [['usage', {}], ['names', {}], ['limits', {}], ['plugins', {}], ['instances', {}], ['project.list', {}]]; test('row 175: TESTER_VERBS and the organization branch tester set are pinned', () => { assert.deepEqual([...TESTER_VERBS], [ 'message.send', 'message.receive', 'message.read', 'inbox', 'agents', 'tasks', 'trail', 'decisions', 'messages', 'replies', 'usage', 'names', 'limits', 'plugins', 'instances', 'project.list', 'thread.list', 'name.set', 'project.create', 'credentials', // Row 177 (#1675): the tester's own threads. 'thread.create', 'thread.open', 'thread.send', 'thread.messages', 'assistant.stop', // Row 194 (#1692, D4.1): launch and terminate, and the read of its own requests. 'instance.launch', 'instance.end', 'instance.request', // Row 264a (#1769): the sessions of the tester's own threads. 'session.list', 'session.create', // Row 271 (264d, #1776): clear context in the tester's own threads (mos-1325). 'session.clear', 'session.cancel', 'session.operation', ]); assert.deepEqual([...TESTER_ORGANIZATION_VERBS], ['limits', 'plugins', 'instances', 'plugins.set', 'instances.set', 'routes.set', 'routes']); assert.ok(Object.isFrozen(TESTER_VERBS) && Object.isFrozen(TESTER_ORGANIZATION_VERBS)); }); // A real organization file and writer behind the bus, as routes-verb.test.mjs's runtime test. async function runtime(t) { const root = mkdtempSync(join(tmpdir(), 'tester-writes-')); const rolesDir = rolesCopy(root); const doc = businessDoc(root, 'demo'); doc.plugins = { ...doc.plugins, harness: ['pi'] }; for (const r of Object.values(doc.roles)) r.vars = { harness: 'pi', model: 'claude-sonnet-5-5', thinking: 'high', ...r.vars }; const file = join(root, 'businesses/demo.json'); writeJson(file, doc); mkdirSync(join(root, 'data'), { mode: 0o700 }); const bus = await startBroker({ dataRoot: join(root, 'data'), businesses: { demo: { id: 'demo', human: 'jason', arbiters: { technical: 'chief', delivery: 'chief' }, vars: { 'bus.tester': true }, roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { definition: 'coder', ...none }, assistant: { definition: 'assistant', ...none } }, }, }, organization: { dir: root, rolesDir, system: systemFor(root) }, }); t.after(async () => { await bus.close(); rmSync(root, { recursive: true, force: true }); }); const human = bus.broker.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }); const tester = bus.broker.bindTester({ business: 'demo' }); const call = (cap, verb, args = {}) => bus.broker.request(cap, { verb, args }); const code = (cap, verb, args = {}) => { try { call(cap, verb, args); } catch (e) { return e.code; } return null; }; const history = join(root, 'businesses/.history/demo/changes.jsonl'); const log = () => (existsSync(history) ? readFileSync(history, 'utf8').trim().split('\n').filter(Boolean).map(JSON.parse) : []); const bytes = () => readFileSync(file, 'utf8'); return { root, file, bus, human, tester, call, code, log, bytes }; } test('row 175: each of the six reads as the tester deep-equals the human read', async (t) => { const r = await runtime(t); r.call(r.human, 'name.set', { instance: 'coder', name: 'rex' }); const project = r.call(r.human, 'project.create', { name: 'alpha' }).id; r.call(r.human, 'thread.create', { project, name: 'one' }); for (const [verb, args] of READS) assert.deepEqual(r.call(r.tester, verb, args), r.call(r.human, verb, args), verb); // Not empty, so names has a row. Row 177: the human's thread is not the tester's to list. assert.equal(r.call(r.human, 'thread.list').length, 1); assert.deepEqual(r.call(r.tester, 'thread.list', { project }), []); assert.equal(r.call(r.tester, 'names').length, 1); assert.equal(r.call(r.tester, 'project.list').length, 1); }); test('row 175: W1-W3 as the tester land with receipt actor tester; the human still logs the human', async (t) => { const r = await runtime(t); const p = r.call(r.tester, 'plugins.set', { revision: r.call(r.tester, 'plugins').revision, plugins: { 'search-source': 'searxng-url' } }); assert.deepEqual(JSON.parse(r.bytes()).plugins['search-source'], 'searxng-url'); assert.deepEqual([r.log().at(-1).actor, r.log().at(-1).state, r.log().at(-1).transaction], ['tester', 'committed', p.transaction]); const i = r.call(r.tester, 'instances.set', { revision: p.revision, instance: 'sec-1', op: 'add', definition: 'security-reviewer' }); assert.deepEqual(JSON.parse(r.bytes()).roles['sec-1'].definition, 'security-reviewer'); assert.deepEqual([r.log().at(-1).actor, r.log().at(-1).transaction], ['tester', i.transaction]); const route = { harness: 'pi', model: 'claude-opus-5-5', thinking: 'minimal', fallback: null }; const s = r.call(r.tester, 'routes.set', { revision: i.revision, routes: { coder: route } }); assert.deepEqual(s.instances.coder, route); assert.deepEqual([r.log().at(-1).actor, r.log().at(-1).transaction], ['tester', s.transaction]); const h = r.call(r.human, 'routes.set', { revision: s.revision, routes: { coder: { ...route, thinking: 'low' } } }); assert.deepEqual([r.log().at(-1).actor, r.log().at(-1).transaction], ['jason', h.transaction]); }); test('row 175: W2 as the tester refuses credentials, protected definitions, human, tester and bus.tester, and changes nothing', async (t) => { const r = await runtime(t); const revision = r.call(r.tester, 'instances').revision; const before = r.bytes(); const entries = r.log().length; const refused = [ [{ instance: 'reviewer-2', op: 'add', definition: 'reviewer' }, 'organization-credentials-out-of-scope'], // Row 194 (brief item 7): only the human's add binds credentials. A valid bot doesn't lift it. [{ instance: 'reviewer-2', op: 'add', definition: 'reviewer', tracker: { bot: 'bot-reviewer-2', botId: 40 } }, 'organization-credentials-out-of-scope'], [{ instance: 'thread-2', op: 'add', definition: 'assistant' }, 'organization-definition-refused'], [{ instance: 'human', op: 'add', definition: 'merge-gate' }, 'invalid-request'], [{ instance: 'tester', op: 'add', definition: 'merge-gate' }, 'invalid-request'], // bus.tester is a business-only var (organization/src/vars.mjs), so no instance carries it. [{ instance: 'gate-2', op: 'add', definition: 'merge-gate', vars: { 'bus.tester': true } }, 'invalid-request'], [{ instance: 'coder', op: 'change', vars: { 'bus.tester': true } }, 'invalid-request'], ]; for (const [args, want] of refused) { assert.equal(r.code(r.tester, 'instances.set', { revision, ...args }), want, JSON.stringify(args)); assert.equal(r.bytes(), before, JSON.stringify(args)); assert.equal(r.call(r.tester, 'instances').revision, revision); assert.equal(r.log().length, entries, `a refused write logs nothing: ${JSON.stringify(args)}`); } // The control: the same add with an instance var lands, so the refusal above is the var. r.call(r.tester, 'instances.set', { revision, instance: 'gate-2', op: 'add', definition: 'merge-gate', vars: { model: 'claude-sonnet-5-5' } }); assert.ok(r.log().length > entries); assert.deepEqual([r.log().at(-1).actor, r.log().at(-1).state], ['tester', 'committed']); }); test('row 175: the human-only actions refuse a tester tester-refused, and the file and store stay as they were', async (t) => { const r = await runtime(t); const revision = r.call(r.human, 'limits').revision; const project = r.call(r.human, 'project.create', { name: 'alpha' }).id; const thread = r.call(r.human, 'thread.create', { project, name: 'one' }).id; const before = r.bytes(); // The runtime keeps its store to itself, so the human's own reads stand in for it. const views = () => ['thread.list', 'project.list', 'names', 'messages', 'instances', 'limits', 'plugins', 'agents'].map((verb) => r.call(r.human, verb)); const seen = views(); const opened = r.call(r.human, 'thread.messages', { thread }); for (const [verb, args] of [ ['limits.set', { revision, limits: { total: 0 } }], ['organization.set', { revision, limits: { total: 0 } }], ['organization.revision', {}], // The gate runs before the argument checks: a bad key still refuses tester-refused. ['organization.set', { revision, nope: 1 }], ['organization.revision', { nope: 1 }], ['decision.resolve', { id: 'd-1', choice: 'yes' }], ['launch.revoke', {}], ['launch.restore', {}], ]) assert.equal(r.code(r.tester, verb, args), 'tester-refused', verb); // Row 177 (#1675): the thread verbs are the tester's now, but the human's thread is not. for (const [verb, args] of [ ['thread.open', { thread }], ['thread.send', { thread, body: 'hello' }], ['thread.messages', { thread }], ]) assert.equal(r.code(r.tester, verb, args), 'thread-not-owned', verb); assert.equal(r.code(r.tester, 'assistant.stop', { instance: 'assistant' }), 'not-held'); // The project file verbs answer a promise. for (const [verb, args] of [['project.file.save', { project: 'stack', path: 'README.md', content: 'x', head: 'a'.repeat(40) }]]) await assert.rejects(Promise.resolve().then(() => r.call(r.tester, verb, args)), { code: 'project-spec-human-only' }, verb); assert.equal(r.bytes(), before); assert.equal(r.call(r.human, 'limits').revision, revision); assert.deepEqual(views(), seen); assert.deepEqual(r.call(r.human, 'thread.messages', { thread }), opened); }); // The plain broker, as names.test.mjs: the in-store writes W4-W6. function broker(t, vars = { 'bus.tester': true }) { const root = mkdtempSync(join(tmpdir(), 'tester-writes-store-')); const store = new Store(root); t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); }); const b = new Broker({ store, businesses: { demo: { id: 'demo', human: 'jason', arbiters: { technical: 'chief', delivery: 'chief' }, ...(vars ? { vars } : {}), roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, dev: { definition: 'coder', ...none }, lead: { definition: 'pm', ...none } }, }, }, }); const call = (cap, verb, args = {}) => b.request(cap, { verb, args }); return { b, store, call }; } test('row 175: W4 as the tester writes by tester and keeps every name check', (t) => { const { b, store, call } = broker(t); const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }); const tester = b.bindTester({ business: 'demo' }); const events = store.get('SELECT count(*) n FROM events').n; assert.deepEqual(call(tester, 'name.set', { instance: 'dev', name: 'rex' }), { instance: 'dev', name: 'rex' }); assert.deepEqual({ ...store.get('SELECT instance, name, by FROM instance_names') }, { instance: 'dev', name: 'rex', by: 'tester' }); assert.equal(store.get('SELECT count(*) n FROM events').n, events, 'no authority consumed, no event'); call(human, 'name.set', { instance: 'lead', name: 'patty' }); assert.equal(store.get("SELECT by FROM instance_names WHERE instance='lead'").by, 'jason'); assert.throws(() => call(tester, 'name.set', { instance: 'dev', name: 'patty' }), { code: 'name-taken' }); for (const name of ['lead', 'human', 'tester']) assert.throws(() => call(tester, 'name.set', { instance: 'dev', name }), { code: 'name-is-instance' }, name); for (const name of ['coder', 'pm']) assert.throws(() => call(tester, 'name.set', { instance: 'dev', name }), { code: 'name-is-role' }, name); assert.equal(store.get('SELECT count(*) n FROM instance_names').n, 2); assert.deepEqual(call(tester, 'names').map(({ instance, name }) => [instance, name]), [['dev', 'rex'], ['lead', 'patty']]); }); test('row 175: W6 as the tester records created_by tester; the human records the human', (t) => { const { b, store, call } = broker(t); const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }); const tester = b.bindTester({ business: 'demo' }); call(tester, 'project.create', { name: 'alpha' }); call(human, 'project.create', { name: 'beta' }); assert.deepEqual(store.all('SELECT name, created_by FROM projects ORDER BY seq').map((p) => [p.name, p.created_by]), [['alpha', 'tester'], ['beta', 'jason']]); assert.throws(() => call(tester, 'project.create', { name: 'alpha' }), { code: 'project-exists' }); assert.deepEqual(call(tester, 'project.list'), call(human, 'project.list')); }); test('row 175: with bus.tester unset or not exactly true no tester binds, so no read or write runs', (t) => { for (const vars of [null, {}, { 'bus.tester': false }, { 'bus.tester': 'true' }, { 'bus.tester': 1 }]) { const { b, store } = broker(t, vars); assert.throws(() => b.bindTester({ business: 'demo' }), { code: 'tester-off' }, JSON.stringify(vars)); for (const table of ['instance_names', 'projects', 'messages']) assert.equal(store.get(`SELECT count(*) n FROM ${table}`).n, 0, table); } });