// C2-04 M4 and real async transport, with made-up local library data. import test from 'node:test'; import assert from 'node:assert/strict'; import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } 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 { serve } from '../src/server.mjs'; import { Client } from '../src/client.mjs'; import { startBroker } from '../src/runtime.mjs'; import { mappingEntry, repo, git, BASE, MARKER } from '../../library/tests/helpers.mjs'; import * as library from '../../library/src/index.mjs'; function world(t, adapter = {}) { const root = mkdtempSync(join(tmpdir(), 'bus-library-')), store = new Store(root); t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); }); const business = { id: 'acme', human: 'jason', vars: { 'bus.tester': true }, projects: { books: mappingEntry() }, arbiters: { technical: 'chief', delivery: 'chief' }, roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { authority: { withinRole: [], crossRole: [] } } } }; const broker = new Broker({ store, businesses: { acme: business }, library: adapter }); const human = broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }); const agent = broker.bindLaunch({ business: 'acme', role: 'coder', run: 'library-agent', harness: 'pi' }); const reader = broker.bindReader({ business: 'acme' }), tester = broker.bindTester({ business: 'acme' }); const call = (cap, verb, args = {}) => Promise.resolve().then(() => broker.request(cap, { verb, args })); return { root, store, broker, business, human, agent, reader, tester, call }; } test('C2-04 M4: both verbs reject every agent before arguments or adapter access', async t => { let touched = 0; const f = world(t, { readFile: () => { touched++; }, status: () => { touched++; } }); for (const verb of ['library.file', 'library.status']) { await assert.rejects(f.call(f.agent, verb, { unknown: true }), { code: 'human-required' }); await assert.rejects(f.call(f.tester, verb, { unknown: true }), { code: 'human-required' }); await assert.rejects(f.call(f.reader, verb, { unknown: true }), { code: 'read-only' }); } assert.equal(touched, 0); const refusals = f.store.all("SELECT body FROM events WHERE kind='action.refused'").map(row => JSON.parse(row.body)); assert.equal(refusals.length, 6); assert.equal(JSON.stringify(refusals).includes('unknown'), false); const absent = world(t, null); for (const verb of ['library.file', 'library.status']) await assert.rejects(absent.call(absent.agent, verb, {}), { code: 'human-required' }); }); test('C2-04 bus: human reads validate keys and return adapter results without path leakage', async t => { const seen = []; const f = world(t, { readFile: async args => { seen.push(args); return { project: 'books', path: 'Book One/A.md', commit: 'a'.repeat(40), content: '# Made up\n' }; }, status: async args => { seen.push(args); return { project: 'books', served: null, problems: [] }; }, LibraryError: library.LibraryError }); assert.equal((await f.call(f.human, 'library.file', { project: 'books', path: 'Book One/A.md' })).content, '# Made up\n'); assert.equal((await f.call(f.human, 'library.status', { project: 'books' })).served, null); assert.equal(seen[0].business.id, 'acme'); assert.equal(seen[1].project, 'books'); for (const [verb, args] of [['library.file', { project: 'books' }], ['library.file', { project: 'books', path: 'Book One/A.md', commit: 'a'.repeat(40) }], ['library.status', { project: 'books', extra: 1 }]]) await assert.rejects(f.call(f.human, verb, args), { code: 'invalid-request' }); }); test('C2-04 socket: each async library verb resolves its result rather than serializing {}', async t => { const f = world(t, { readFile: async () => ({ project: 'books', path: 'Book One/A.md', commit: 'a'.repeat(40), content: '# Fixture\n' }), status: async () => ({ project: 'books', served: null, problems: [] }), LibraryError: library.LibraryError }); const path = join(f.root, 'bus.sock'); const server = await serve({ broker: f.broker, path, authenticateHuman: () => f.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }) }); t.after(() => server.close()); const human = new Client({ path, human: { fixture: true } }); assert.deepEqual(await human.call('library.status', { project: 'books' }), { project: 'books', served: null, problems: [] }); assert.equal((await human.call('library.file', { project: 'books', path: 'Book One/A.md' })).content, '# Fixture\n'); }); test('C2-04 runtime: protectedRoots skips library but retains real code-root credential protection', async t => { const f = repo(t), dataRoot = join(f.root, 'data'), token = join(f.root, 'fixture-token'); mkdirSync(dataRoot, { mode: 0o700 }); writeFileSync(token, 'made-up-fixture-token-123456', { mode: 0o600 }); const business = { id: 'acme', human: 'jason', projects: { books: mappingEntry(), stack: { root: f.repo } }, arbiters: { technical: 'chief', delivery: 'chief' }, roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { authority: { withinRole: [], crossRole: [] }, credentials: { gitea: { file: token, rotateBy: '2099-01-01' } } } } }; const runtime = await startBroker({ dataRoot, businesses: { acme: business } }); await runtime.close(); const mirror = library.mirrorPath({ business, project: 'books', dataRoot }); assert.equal(git(mirror, 'rev-parse', '--is-bare-repository'), 'true'); const bad = join(f.repo, 'credential'); writeFileSync(bad, 'made-up-fixture-token-123456', { mode: 0o600 }); business.roles.coder.credentials.gitea.file = bad; await assert.rejects(startBroker({ dataRoot, businesses: { acme: business } })); }); test('C2-04 runtime: unsafe existing mirror refuses boot and never publishes a socket', async t => { const f = repo(t), dataRoot = join(f.root, 'data'); mkdirSync(dataRoot, { mode: 0o700 }); const business = { id: 'acme', human: 'jason', projects: { books: mappingEntry() }, arbiters: { technical: 'chief', delivery: 'chief' }, roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { authority: { withinRole: [], crossRole: [] } } } }; const mirror = join(dataRoot, 'projects/acme/books'); mkdirSync(mirror, { recursive: true }); writeFileSync(join(mirror, 'keep'), 'made-up note'); await assert.rejects(startBroker({ dataRoot, businesses: { acme: business } }), { code: 'library-git' }); assert.equal(existsSync(join(dataRoot, 'bus.sock')), false); assert.equal(readFileSync(join(mirror, 'keep'), 'utf8'), 'made-up note'); }); test('C2-04 runtime: real library adapters reach the socket and lock waits leave other bus reads responsive', { timeout: 15000 }, async t => { const f = repo(t), dataRoot = join(f.root, 'data'); mkdirSync(dataRoot, { mode: 0o700 }); const business = { id: 'acme', human: 'jason', projects: { books: mappingEntry() }, arbiters: { technical: 'chief', delivery: 'chief' }, roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { authority: { withinRole: [], crossRole: [] } } } }; const runtime = await startBroker({ dataRoot, businesses: { acme: business } }); t.after(() => runtime.close()); const cap = runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }); const path = join(f.root, 'human-fixture.sock'); const server = await serve({ broker: runtime.broker, path, authenticateHuman: () => runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }) }); t.after(() => server.close()); const human = new Client({ path, human: { fixture: true } }); assert.equal((await human.call('library.status', { project: 'books' })).served, null); const mirror = library.mirrorPath({ business, project: 'books', dataRoot }); git(f.repo, '-c', 'protocol.file.allow=always', 'push', '-q', mirror, 'HEAD:refs/heads/served'); writeFileSync(join(mirror, library.SYNC_STATE_FILE), JSON.stringify({ version: 1, branch: 'main', served: f.commit, lastFetch: null, lastError: null, diverged: null, problems: [] }), { mode: 0o600 }); const file = await human.call('library.file', { project: 'books', path: 'Book One/Characters/Tomas.md' }); assert.equal(file.content, BASE['Book One/Characters/Tomas.md']); assert.equal(file.commit, f.commit); let release, done = false; const hold = library.withLibraryLock(mirror, () => new Promise(r => { release = r; })); const deadline = Date.now() + 5000; while (!release && Date.now() < deadline) await new Promise(r => setTimeout(r, 5)); assert.equal(typeof release, 'function'); t.after(async () => { release(); await hold; }); const pending = human.call('library.status', { project: 'books' }).then(result => { done = true; return result; }); const ordinary = await Promise.race([human.call('agents'), new Promise((_, reject) => { const timer = setTimeout(() => reject(Error('ordinary bus read blocked by lock acquisition')), 1000); timer.unref(); })]); assert.ok(ordinary && typeof ordinary === 'object'); assert.equal(done, false); release(); await hold; assert.equal((await pending).served, f.commit); });