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, TASK_PAGE } from '../src/broker.mjs'; // Row 109 (#1603): `state` is what a run reads before it plans: the launch state with the time and // id of the event that set it, and the open tasks from the tracker snapshot, capped. const authority = { withinRole: ['message.send', 'role.launch'], crossRole: [] }; const roles = { pm: { authority }, coder: { authority } }; const businesses = { demo: { id: 'demo', human: 'jason', arbiters: { technical: 'pm', delivery: 'pm' }, launch: { by: 'pm', instances: ['coder'], max: { opus: 2, sonnet: 2 } }, roles }, }; const at = '2026-10-12T12:00:00.000Z'; const digest = 'd'.repeat(64); const ref = (i) => `vikunja:3/${i}`; const fields = (i, done = false) => ({ project_id: 3, title: `Task ${i}`, description: `

${i}

`, done, bucket: 11 }); function setup(t) { const root = mkdtempSync(join(tmpdir(), 'bus-state-')); const store = new Store(root); const b = new Broker({ store, businesses }); t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); }); const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }); const pm = b.bindLaunch({ business: 'demo', role: 'pm', run: 'demo-pm', harness: 'claude-code' }); b.request(pm, { verb: 'role.claim' }); const snap = (i, over = {}, read_at = at) => ({ task_ref: ref(i), updated: read_at, digest, fields: { ...fields(i), ...over }, via: 'board', read_at }); const record = (...snapshots) => b.recordTask({ business: 'demo', snapshots }); const state = (cap = pm, args = {}) => b.request(cap, { verb: 'state', args }); return { b, store, human, pm, snap, record, state, reader: b.bindReader({ business: 'demo' }) }; } test('a business that never switched launches reads allowed with no time and no event', (t) => { const { state } = setup(t); assert.deepEqual(state().launch, { state: 'allowed', at: null, event: null }); }); test('launches revoked and then restored read allowed, with the restore time and event id', (t) => { const { b, store, human, pm, state } = setup(t); const revoked = b.request(human, { verb: 'launch.revoke' }).id; assert.deepEqual( { state: state().launch.state, event: state().launch.event }, { state: 'revoked', event: revoked }, ); const restored = b.request(human, { verb: 'launch.restore' }).id; const row = store.get('SELECT at FROM events WHERE id=?', restored); assert.deepEqual(state(pm).launch, { state: 'allowed', at: row.at, event: restored }); assert.notEqual(restored, revoked); }); test('a task closed on the tracker leaves the open list, and total counts the open ones', (t) => { const { snap, record, state } = setup(t); record(snap(1), snap(2), snap(3)); assert.deepEqual(state().tasks, { open: [1, 2, 3].map((i) => ({ task_ref: ref(i), title: `Task ${i}` })), total: 3, cap: TASK_PAGE, }); record(snap(2, { done: true }, '2026-10-12T13:00:00.000Z')); const { open, total } = state().tasks; assert.deepEqual(open.map((x) => x.task_ref), [ref(1), ref(3)]); assert.equal(total, 2); }); test('a tombstone is not open', (t) => { const { snap, record, state } = setup(t); record(snap(1), { ...snap(2), fields: { gone: 'no-access' }, via: 'task' }); assert.deepEqual(state().tasks.open.map((x) => x.task_ref), [ref(1)]); }); test('the open list stops at the cap and says so; total still counts every open task', (t) => { const { snap, record, state } = setup(t); record(...Array.from({ length: TASK_PAGE + 5 }, (_, k) => snap(k + 1))); const { open, total, cap } = state().tasks; assert.equal(open.length, TASK_PAGE); assert.equal(total, TASK_PAGE + 5); assert.equal(cap, TASK_PAGE); }); test('a role and a reader session read state; arguments, and an unbound cap, refuse', (t) => { const { reader, pm, state } = setup(t); assert.deepEqual(state(reader), state(pm)); assert.throws(() => state(pm, { business: 'other' }), { code: 'invalid-request' }); assert.throws(() => state('f'.repeat(64)), /unauthenticated/); }); test('a human session reads the same state, with the same cap', (t) => { const { snap, record, human, pm, state } = setup(t); record(...Array.from({ length: TASK_PAGE + 2 }, (_, k) => snap(k + 1))); const read = state(human); assert.deepEqual(read, state(pm)); assert.equal(read.tasks.cap, TASK_PAGE); assert.equal(read.tasks.open.length, TASK_PAGE); assert.equal(read.tasks.total, TASK_PAGE + 2); }); test('reading state writes nothing: every table keeps its row count', (t) => { const { b, store, human, pm, reader, snap, record, state } = setup(t); b.request(human, { verb: 'launch.revoke' }); record(snap(1), snap(2)); const counts = () => Object.fromEntries( store.all("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'") .map(({ name }) => [name, store.get(`SELECT COUNT(*) AS n FROM "${name}"`).n]), ); const before = counts(); assert.ok(before.events > 0 && before.task_snapshots > 0); for (const cap of [pm, reader, human]) state(cap); assert.deepEqual(counts(), before); });