// Row 149 probe (a), for sage-0650: when a merge-gate or security-reviewer turn ends and the runner's // automatic RESULT reply is refused, what does the store gain, and does any of it reach the human's // inbox or decision lists? Scratch store only, under TMPDIR. Mirrors runner.mjs:386-391. import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; const R = '/home/jwoltje/dewey-scratch/r149/packages'; const { Store } = await import(`${R}/bus/src/store.mjs`); const { Broker } = await import(`${R}/bus/src/broker.mjs`); const { busBusiness } = await import(`${R}/bus/src/business.mjs`); const { resolveInstance, validateBusinessDocument } = await import(`${R}/business/src/index.mjs`); const { businessDoc, REPO_ROLES, systemFor } = await import(`${R}/business/tests/helpers.mjs`); const root = mkdtempSync(join(tmpdir(), 'probe-149a-')); const doc = businessDoc(root, 'demo'); doc.roles['merge-gate'] = { definition: 'merge-gate' }; doc.roles['security-reviewer'] = { definition: 'security-reviewer' }; const business = validateBusinessDocument(doc, join(root, 'businesses', 'demo.json'), { rolesDir: REPO_ROLES }); const resolved = {}; for (const i of Object.keys(business.roles)) resolved[i] = resolveInstance({ system: systemFor(root), business, instance: i }); const store = new Store(root); const b = new Broker({ store, businesses: { demo: busBusiness(business, resolved) } }); const caps = {}; for (const role of ['merge-gate', 'security-reviewer', 'cto']) { caps[role] = b.bindLaunch({ business: 'demo', role, run: `${role}-run`, harness: 'pi' }); b.request(caps[role], { verb: 'role.claim', args: {} }); } const human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }); const tables = store.all("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name").map((r) => r.name); const counts = () => Object.fromEntries(tables.map((t) => [t, store.get(`SELECT count(*) n FROM "${t}"`).n])); const humanViews = () => ({ inbox: b.request(human, { verb: 'inbox', args: {} }).length, roles: b.request(human, { verb: 'decisions', args: { list: 'roles' } }).length, recent: b.request(human, { verb: 'decisions', args: { list: 'recent' } }).length, }); const out = { cases: [] }; for (const role of ['merge-gate', 'security-reviewer']) { for (const [label, cap] of [['from cto', caps.cto], ['from human', human]]) { b.request(cap, { verb: 'message.send', args: { to: role, body: `probe to ${role}` } }); const [m] = b.request(caps[role], { verb: 'message.receive', args: {} }); const before = counts(), seqBefore = store.get('SELECT max(seq) s FROM events').s ?? 0; const viewsBefore = humanViews(); // The runner's try block: the reply, then message.read; the reply's refusal skips the read. let reply = 'sent', read = 'not-run'; try { b.request(caps[role], { verb: 'message.send', args: { to: m.from_role, body: 'turn answer', class: 'RESULT', in_reply_to: m.id } }); b.request(caps[role], { verb: 'message.read', args: { id: m.id } }); read = 'done'; } catch (e) { reply = e.code; } const after = counts(); const grew = Object.fromEntries(tables.filter((t) => after[t] !== before[t]).map((t) => [t, after[t] - before[t]])); const events = store.all('SELECT kind, actor_role, subject, body FROM events WHERE seq>?', seqBefore); const again = b.request(caps[role], { verb: 'message.receive', args: {} }).length; out.cases.push({ role, message: label, reply, read, grew, events, humanBefore: viewsBefore, humanAfter: humanViews(), redelivered: again }); } } out.decisionsTotal = store.get('SELECT count(*) n FROM decisions').n; console.log(JSON.stringify(out, null, 1)); store.close(); rmSync(root, { recursive: true, force: true });