Files
jason.woltje 98d29769e2 bus, cli: tester reads and the W1-W7 tester writes, recorded as tester; scripts/mosaic sends plugins, name and names to the CLI (row 175, #1673)
TESTER_VERBS gains usage, names, limits, plugins, instances, project.list,
thread.list, name.set and project.create. A tester reads limits, plugins
and instances and sends plugins.set, instances.set and routes.set, each
logged with actor tester. The project file verbs refuse a tester
tester-refused. The broker and CLI drop the [mos test]
prefix rule (mos-0872 Q1). The CLI takes --tester on the seven reads and
the W1-W7 write forms and refuses it (exit 4) elsewhere, bus start and
stop included.

project init --tester checks the project area, refuses the area root
itself, binds the tester with one agents read, then commits the layout as
tester (mos-0872 Q2). scripts/mosaic sends plugins, name, names, follow,
threads and thread to packages/cli (sage-1081). The row 164 pins are
untouched.

Built by Dewey on 9e74925c2 (three commits to 989699f7c, manifest
2e30f2a1...29d5f), approved by Filbert in round 3 (#1673 comment 29279),
Sage's gate green on 716ddcf1e plus the patch.
2026-10-19 19:04:50 -05:00

183 lines
9.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Store } from '../src/store.mjs';
import { Broker } from '../src/broker.mjs';
// Row 141 (#1635, REQ-MSG-2): messages are addressed by an instance's name. The human sets names
// through the CLI; the chief of staff sets them because its role file grants `instance.name`
// within-role. The grant is read from the shipped roles/chief-of-staff.json, so removing it there
// fails the chief's test. Instance keys differ from definitions here, so each refusal is its own.
const shipped = (name) =>
JSON.parse(readFileSync(fileURLToPath(new URL(`../../../roles/${name}.json`, import.meta.url)), 'utf8'));
const none = { authority: { withinRole: ['message.send'], crossRole: [] } };
const roles = {
chief: { definition: 'chief-of-staff', authority: shipped('chief-of-staff').authority },
lead: { definition: 'pm', ...none },
dev: { definition: 'coder', authority: { withinRole: ['message.send'], crossRole: ['instance.name'] } },
};
const demo = () => ({
demo: {
id: 'demo',
human: 'jason',
arbiters: { technical: 'lead', delivery: 'chief' },
roles,
definitions: { reviewer: {} },
vars: { 'bus.tester': true },
},
});
function setup(t) {
const root = mkdtempSync(join(tmpdir(), 'bus-names-'));
const store = new Store(root);
t.after(() => {
store.close();
rmSync(root, { recursive: true, force: true });
});
const b = new Broker({ store, businesses: demo() });
const caps = { human: b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }) };
for (const role of Object.keys(roles)) {
caps[role] = b.bindLaunch({ business: 'demo', role, run: `${role}-run`, harness: 'pi' });
b.request(caps[role], { verb: 'role.claim' });
}
caps.reader = b.bindReader({ business: 'demo' });
caps.tester = b.bindTester({ business: 'demo' });
const call = (who, verb, args = {}) => b.request(caps[who], { verb, args });
return { b, store, caps, call };
}
test('REQ-MSG-2: the chief of staff names an instance under its within-role grant, and the write is logged', (t) => {
const { store, call } = setup(t);
assert.ok(shipped('chief-of-staff').authority.withinRole.includes('instance.name'), 'the shipped chief file grants instance.name');
assert.deepEqual(call('chief', 'name.set', { instance: 'dev', name: 'rex' }), { instance: 'dev', name: 'rex' });
const [event, ...rest] = store
.all("SELECT actor_role, subject, body FROM events WHERE kind='action.allowed' AND json_extract(body,'$.action')='instance.name'")
.map((e) => ({ ...e, body: JSON.parse(e.body) }));
assert.equal(rest.length, 0);
assert.equal(event.actor_role, 'chief');
assert.equal(event.subject, 'dev');
assert.equal(event.body.class, 'within-role');
assert.equal(event.body.target, 'dev');
assert.deepEqual({ ...store.get('SELECT business, instance, name, by FROM instance_names') }, {
business: 'demo',
instance: 'dev',
name: 'rex',
by: 'chief-run',
});
assert.deepEqual(
call('chief', 'names').map(({ instance, name }) => ({ instance, name })),
[{ instance: 'dev', name: 'rex' }],
);
});
test('REQ-MSG-2: the human names an instance through the CLI', (t) => {
const { store, call } = setup(t);
assert.deepEqual(call('human', 'name.set', { instance: 'lead', name: 'patty' }), { instance: 'lead', name: 'patty' });
assert.equal(store.get('SELECT by FROM instance_names').by, 'jason');
assert.deepEqual(
call('human', 'names').map(({ instance, name }) => ({ instance, name })),
[{ instance: 'lead', name: 'patty' }],
);
});
test('REQ-MSG-2: a role without the grant, or holding it cross-role, is refused not-authorized', (t) => {
const { store, call } = setup(t);
// lead holds no instance.name at all; dev holds it cross-role. Neither may set a name: the refusal
// comes before #checkAuthority, so dev gets not-authorized, not decision-required, and name.set
// takes no decision argument at all.
assert.throws(() => call('lead', 'name.set', { instance: 'dev', name: 'rex' }), /not-authorized/);
assert.throws(() => call('dev', 'name.set', { instance: 'dev', name: 'rex' }), /not-authorized/);
assert.throws(
() => call('dev', 'name.set', { instance: 'dev', name: 'rex', decision: '00000000-0000-4000-8000-000000000000' }),
/invalid-request/,
);
assert.throws(() => call('reader', 'name.set', { instance: 'dev', name: 'rex' }), /read-only/);
assert.equal(store.get('SELECT count(*) AS n FROM instance_names').n, 0);
// Row 175 (#1673): the tester sets a name like the human, recorded by `tester`.
call('tester', 'name.set', { instance: 'dev', name: 'rex' });
assert.equal(store.get('SELECT by FROM instance_names').by, 'tester');
});
test('REQ-MSG-2: a name is unique in the business and is never an instance key or a role name', (t) => {
const { store, call } = setup(t);
call('human', 'name.set', { instance: 'dev', name: 'rex' });
assert.throws(() => call('human', 'name.set', { instance: 'lead', name: 'rex' }), /name-taken/);
assert.throws(() => call('chief', 'name.set', { instance: 'lead', name: 'rex' }), /name-taken/);
for (const key of ['chief', 'lead', 'human', 'tester'])
assert.throws(() => call('human', 'name.set', { instance: 'dev', name: key }), /name-is-instance/, key);
// A definition an instance uses, and one the business only defines.
for (const definition of ['coder', 'chief-of-staff', 'reviewer'])
assert.throws(() => call('human', 'name.set', { instance: 'dev', name: definition }), /name-is-role/, definition);
for (const bad of ['', 'Rex', '_bus', 'a b', 'x'.repeat(65), 7])
assert.throws(() => call('human', 'name.set', { instance: 'dev', name: bad }), /invalid-request/, String(bad));
assert.throws(() => call('human', 'name.set', { instance: 'nobody', name: 'zed' }), /unknown-role/);
// Renaming frees the old name for another instance.
call('human', 'name.set', { instance: 'dev', name: 'rexy' });
call('human', 'name.set', { instance: 'lead', name: 'rex' });
assert.deepEqual(
call('human', 'names').map(({ instance, name }) => ({ instance, name })),
[{ instance: 'dev', name: 'rexy' }, { instance: 'lead', name: 'rex' }],
);
// The store refuses a duplicate on its own, and the table is append-only.
assert.throws(
() => store.run("INSERT INTO instance_names(at,business,instance,name,by) VALUES('2026-01-01T00:00:00.000Z','demo','chief','rex','t')"),
/unique in its business/,
);
assert.throws(() => store.run("UPDATE instance_names SET name='zed'"), /append-only/);
assert.throws(() => store.run('DELETE FROM instance_names'), /append-only/);
});
test('REQ-MSG-2: a send by name reaches the instance and records both; a send by instance key is unchanged', (t) => {
const { call } = setup(t);
call('human', 'name.set', { instance: 'dev', name: 'rex' });
const byName = call('lead', 'message.send', { to: 'rex', body: 'by name' });
const byKey = call('lead', 'message.send', { to: 'dev', body: 'by key' });
const fromHuman = call('human', 'message.send', { to: 'rex', body: 'from the human' });
const got = call('dev', 'message.receive');
assert.deepEqual(
got.map(({ id, to_role, to_name, body }) => ({ id, to_role, to_name, body })),
[
{ id: byName.id, to_role: 'dev', to_name: 'rex', body: 'by name' },
{ id: byKey.id, to_role: 'dev', to_name: null, body: 'by key' },
{ id: fromHuman.id, to_role: 'dev', to_name: 'rex', body: 'from the human' },
],
);
// The tester may send by name too; a name no instance holds is an unknown role.
call('tester', 'message.send', { to: 'rex', body: '[mos test] by name' });
assert.equal(call('dev', 'message.receive')[0].to_role, 'dev');
assert.throws(() => call('lead', 'message.send', { to: 'nobody', body: 'x' }), /unknown-role/);
// After a rename, the old name no longer reaches anyone.
call('human', 'name.set', { instance: 'dev', name: 'rexy' });
assert.throws(() => call('lead', 'message.send', { to: 'rex', body: 'x' }), /unknown-role/);
assert.equal(call('lead', 'message.send', { to: 'rexy', body: 'renamed' }).id.length, 36);
});
test('REQ-MSG-2: an instance key beats a name that equals it, as when a later instance takes the key', (t) => {
const { store, call } = setup(t);
// name.set refuses this name; the store's trigger doesn't know the business, so a row stands in for
// a name set before an instance with that key joined.
store.run("INSERT INTO instance_names(at,business,instance,name,by) VALUES('2026-01-01T00:00:00.000Z','demo','dev','lead','jason')");
call('chief', 'message.send', { to: 'lead', body: 'to the key' });
const [m] = call('lead', 'message.receive');
assert.equal(m.body, 'to the key');
assert.equal(m.to_role, 'lead');
assert.equal(m.to_name, null);
assert.deepEqual(call('dev', 'message.receive'), []);
});
test('REQ-MSG-2: a role name beats a name that equals it, as when a later definition takes the word (sage-0590)', (t) => {
const { store, call } = setup(t);
// As above, rows stand in for names set before the business knew these definitions: one an
// instance uses (coder) and one the business only defines (reviewer). Neither is an instance key.
store.run("INSERT INTO instance_names(at,business,instance,name,by) VALUES('2026-01-01T00:00:00.000Z','demo','dev','reviewer','jason')");
store.run("INSERT INTO instance_names(at,business,instance,name,by) VALUES('2026-01-01T00:00:00.000Z','demo','lead','coder','jason')");
for (const word of ['reviewer', 'coder'])
assert.throws(() => call('chief', 'message.send', { to: word, body: 'to the role' }), /unknown-role/, word);
assert.deepEqual(call('dev', 'message.receive'), []);
assert.deepEqual(call('lead', 'message.receive'), []);
});