Files
stack/packages/bus/tests/requirements.test.mjs
T
jason.woltjeandClaude Opus 5.5 e918a915a8 tasks, bus, harness, seat: the approved requirement ids as data (#1584)
Row 90, round 2. Dewey built it, Filbert approved it (comment 28068) on
manifest 95611274 (21 paths, base b8539480). The approved PRD's
requirement ids live in packages/tasks/requirements.json. task_create
refuses an id outside it, and the PM session gets a list_requirements
tool. Round 2 adds requirements.mjs to the sandbox's RUNNER_FILES
(decision 127). Sage gate on c047d028 plus the patch, 23:50:16Z to
23:54:26Z: tasks 89, bus 111, harness 66, webui 101, cli 123, seat 33,
business 60, control-board 124, queue 150, conversation 182, discord 178,
ledger 78, runs 43, mosaic 69, all fail 0. Every scripts/test-*.sh
exited 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 18:55:49 -05:00

79 lines
3.9 KiB
JavaScript

// Row 90 (#1584): `requirements`, the approved PRD's ids that task.create takes, for the role
// sessions that create tasks; and the PM's two no-task decisions, a request no id fits
// (prd.approve) and a run it thinks should stop (role.revoke), which reach the human's Inbox.
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { Store } from '../src/store.mjs';
import { Broker } from '../src/broker.mjs';
import { loadRequirements } from '../../tasks/src/requirements.mjs';
const options = [
{ key: 'yes', text: 'Approve' },
{ key: 'no', text: 'Decline' },
];
const roles = {
pm: { authority: { withinRole: ['message.send', 'task.create'], crossRole: [] } },
coder: { authority: { withinRole: ['message.send'], crossRole: [] } },
};
const businesses = { demo: { id: 'demo', human: 'jason', arbiters: { technical: 'pm', delivery: 'pm' }, roles } };
function setup(t) {
const root = mkdtempSync(join(tmpdir(), 'bus-requirements-'));
const store = new Store(root);
const b = new Broker({ store, businesses });
t.after(() => {
store.close();
rmSync(root, { recursive: true, force: true });
});
const agent = (role) => {
const cap = b.bindLaunch({ business: 'demo', role, run: `demo-${role}-run`, harness: 'pi', address: role });
b.request(cap, { verb: 'role.claim' });
return cap;
};
const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
const reader = b.bindReader({ business: 'demo' });
return { b, agent, human, reader };
}
const call = (b, cap, verb, args = {}) => b.request(cap, { verb, args });
const raise = (b, cap, action, extra = {}) =>
call(b, cap, 'decision.raise', { action, question: `${action}?`, options, recommendation: 'no', blocking: false, ...extra });
test('requirements returns the committed list to a holding role session, and no one else', (t) => {
const { b, agent, human, reader } = setup(t);
const pm = agent('pm');
const list = call(b, pm, 'requirements');
assert.deepEqual(list, loadRequirements());
assert.equal(list.requirements.length, 24);
assert.deepEqual(call(b, agent('coder'), 'requirements'), list);
assert.throws(() => call(b, human, 'requirements'), /agent-required/);
assert.throws(() => call(b, reader, 'requirements'), /read-only/);
// A second launch of the role that never claimed it holds nothing.
const stale = b.bindLaunch({ business: 'demo', role: 'pm', run: 'demo-pm-other', harness: 'pi', address: 'pm2' });
assert.throws(() => call(b, stale, 'requirements'), /not-holder/);
assert.throws(() => call(b, pm, 'requirements', { prd: 'docs/prd/mosaic-stack.md' }), /invalid-request/);
});
test("the PM's prd.approve with no task, when no id fits, reaches the human's Inbox", (t) => {
const { b, agent, human, reader } = setup(t);
const pm = agent('pm');
const d = raise(b, pm, 'prd.approve', { target: 'mosaic-stack-prd' });
assert.deepEqual([d.class, d.route_to, d.blocking, d.task_ref], ['gated', 'human', false, null]);
for (const cap of [human, reader]) assert.deepEqual(call(b, cap, 'inbox').map((r) => r.id), [d.id]);
assert.deepEqual(call(b, pm, 'inbox'), []);
// Blocking needs a task to hold.
assert.throws(() => raise(b, pm, 'prd.approve', { blocking: true }), /task-required/);
});
test("the PM's non-blocking role.revoke with no task reaches the human's Inbox", (t) => {
const { b, agent, human } = setup(t);
const pm = agent('pm');
agent('coder');
const d = raise(b, pm, 'role.revoke', { target: 'coder' });
assert.deepEqual([d.class, d.route_to, d.blocking, d.task_ref], ['gated', 'human', false, null]);
assert.deepEqual(d.authorization, { action: 'role.revoke', target: 'coder', approvalChoice: 'yes' });
assert.deepEqual(call(b, human, 'inbox').map((r) => r.id), [d.id]);
assert.throws(() => raise(b, pm, 'role.revoke', { target: 'coder', blocking: true }), /task-required/);
});