Files
stack/agents/darkwing/work/slice1-s2-review/probes.test.mjs
T
2026-10-04 23:13:35 -05:00

86 lines
5.5 KiB
JavaScript

// Darkwing review probes. Not part of the candidate.
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 { fork } from 'node:child_process';
import { once } from 'node:events';
import { Store } from '../src/store.mjs';
import { Broker } from '../src/broker.mjs';
const options = [{ key: 'yes', text: 'Allow' }, { key: 'no', text: 'Decline' }];
// Data-model table: cto crossRole task.scope.change, pm crossRole task.priority.change.
const businesses = { demo: { id: 'demo', human: 'jason', arbiters: { technical: 'cto', delivery: 'pm' }, roles: {
pm: { authority: { withinRole: ['message.send', 'task.create'], crossRole: ['task.priority.change'] } },
cto: { authority: { withinRole: ['message.send'], crossRole: ['task.scope.change'] } },
coder: { authority: { withinRole: ['message.send'], crossRole: ['task.scope.change'] } } } } };
function setup(t) {
const root = mkdtempSync(join(tmpdir(), 'dw-bus-'));
const store = new Store(root);
const b = new Broker({ store, businesses });
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
const agent = (role, run = role + '-run') => b.bindLaunch({ business: 'demo', role, run, harness: 'pi', address: run });
const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
return { b, store, agent, human };
}
const call = (b, cap, verb, args = {}) => b.request(cap, { verb, args });
const raise = (b, cap, action, extra = {}) => call(b, cap, 'decision.raise', { action, question: 'Allow?', options, recommendation: 'no', blocking: false, ...extra });
test('P1 revoked run reclaims its role with its old capability', (t) => {
const { b, agent, human } = setup(t), c = agent('coder'), p = agent('pm');
call(b, c, 'role.claim'); call(b, p, 'role.claim');
const d = raise(b, c, 'role.revoke', { target: 'pm' });
call(b, human, 'decision.resolve', { id: d.id, choice: 'yes' });
call(b, c, 'role.revoke', { role: 'pm', decision: d.id });
let out; try { out = call(b, p, 'role.claim'); } catch (e) { out = e.code; }
console.log('P1 revoked pm reclaims ->', JSON.stringify(out));
const p2 = agent('pm', 'pm-next');
let out2; try { out2 = call(b, p2, 'role.claim'); } catch (e) { out2 = e.code; }
console.log('P1 replacement pm claim ->', JSON.stringify(out2));
});
test('P2 arbiter resolves its own cross-role decision', (t) => {
const { b, agent } = setup(t), cto = agent('cto'), pm = agent('pm');
call(b, cto, 'role.claim'); call(b, pm, 'role.claim');
const d = raise(b, cto, 'task.scope.change', { domain: 'technical', target: 'x1' });
console.log('P2 cto raise route_to', d.route_to);
call(b, cto, 'decision.resolve', { id: d.id, choice: 'yes' });
console.log('P2 cto authorize ->', JSON.stringify(b.authorize(cto, 'task.scope.change', { decision: d.id, target: 'x1' })));
const d2 = raise(b, pm, 'task.priority.change', { target: 'x2' });
console.log('P2 pm raise route_to', d2.route_to);
call(b, pm, 'decision.resolve', { id: d2.id, choice: 'yes' });
console.log('P2 pm authorize ->', JSON.stringify(b.authorize(pm, 'task.priority.change', { decision: d2.id, target: 'x2' })));
});
test('P3 one approval authorizes repeatedly', (t) => {
const { b, agent, human } = setup(t), c = agent('coder');
call(b, c, 'role.claim');
const d = raise(b, c, 'deploy', { target: 'v1' });
call(b, human, 'decision.resolve', { id: d.id, choice: 'yes' });
for (let i = 0; i < 3; i++) console.log('P3 authorize', i, JSON.stringify(b.authorize(c, 'deploy', { decision: d.id, target: 'v1' })));
});
test('P4 refusals from a reader and a non-holder append events', (t) => {
const { b, store, agent } = setup(t), r = b.bindReader({ business: 'demo' }), c = agent('coder');
const before = store.get('SELECT count(*) n FROM events').n;
for (let i = 0; i < 5; i++) { try { call(b, r, 'role.claim'); } catch {} try { call(b, c, 'message.send', { to: 'pm', body: 'x' }); } catch {} }
console.log('P4 events added by 10 refusals:', store.get('SELECT count(*) n FROM events').n - before);
});
test('P5 empty in_reply_to', (t) => {
const { b, store, agent } = setup(t), c = agent('coder');
call(b, c, 'role.claim');
let out; try { out = call(b, c, 'message.send', { to: 'pm', body: 'x', in_reply_to: '' }); out = store.get('SELECT in_reply_to FROM messages WHERE id=?', out.id); } catch (e) { out = e.code; }
console.log('P5 ->', JSON.stringify(out));
});
test('P6 a refused bindLaunch over IPC stops the broker process', async (t) => {
const root = mkdtempSync(join(tmpdir(), 'dw-bus-p6-'));
const child = fork(new URL('../src/process.mjs', import.meta.url), [], { stdio: ['ignore', 'pipe', 'pipe', 'ipc'] });
t.after(() => { if (child.exitCode === null) child.kill('SIGKILL'); rmSync(root, { recursive: true, force: true }); });
let m = once(child, 'message');
child.send({ op: 'boot', config: { dataRoot: root, businesses, launches: [] } });
console.log('P6 boot', (await m)[0].ok);
const rec = { business: 'demo', role: 'cto', run: 'r1', harness: 'pi', pid: process.pid, startTime: '1' };
m = once(child, 'message'); child.send({ op: 'bindLaunch', record: rec }); console.log('P6 first bind', (await m)[0].ok);
const exit = once(child, 'exit');
m = once(child, 'message'); child.send({ op: 'bindLaunch', record: rec }); console.log('P6 duplicate bind', JSON.stringify((await m)[0]));
const timer = setTimeout(() => child.kill('SIGKILL'), 3000);
console.log('P6 exit code after refused bind', (await exit)[0]); clearTimeout(timer);
});