import test from 'node:test'; import { DatabaseSync } from 'node:sqlite'; import assert from 'node:assert/strict'; import { mkdirSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { startBroker } from '../src/runtime.mjs'; import { bootConfig, loadSystem } from '../../cli/src/config.mjs'; import { fixture, tmp } from '../../cli/tests/helpers.mjs'; import { prdFixture, git } from '../../project/tests/prd-fixture.mjs'; import { createRequestStore } from '../src/request-store.mjs'; async function setup(t, mode = 'autonomous') { const root = tmp(t, 'activation-'), f = fixture(root, 'demo'); const source = join(root, 'project'); mkdirSync(join(source, '.mosaic'), { recursive: true }); writeFileSync(join(source, '.mosaic/project.json'), JSON.stringify({ projectVersion: 1, id: 'stack', vars: { 'tracker.project': 1 } })); git(source, 'init', '-qb', 'main'); git(source, 'add', '-A'); git(source, 'commit', '-qm', 'Factory'); const system = loadSystem({ env: f.env }); const boot = async () => { const config = bootConfig({ system, businessId: 'demo', env: f.env, warn: () => {} }); const bus = await startBroker(config); t.after(() => bus.close().catch(() => {})); return bus; }; const bus = await boot(), caps = {}; const db = new DatabaseSync(join(f.dataRoot, 'bus/bus.sqlite'), { readOnly: true }); t.after(() => db.close()); const store = { get: (sql, ...a) => db.prepare(sql).get(...a), all: (sql, ...a) => db.prepare(sql).all(...a) }; const bind = b => { caps.human = b.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }); caps.console = b.bindConsole({ business: 'demo', human: 'jason', subject: { provider: 'test', iss: 'test', sub: 'owner' } }); caps.reader = b.bindReader({ business: 'demo' }); for (const role of ['pm','cto','coder']) { caps[role] = b.bindLaunch({ business: 'demo', role, run: role + '-run', harness: 'pi' }); b.request(caps[role], { verb: 'role.claim' }); } }; bind(bus.broker); const call = (who, verb, args = {}) => bus.broker.request(caps[who], { verb, args }); const sent = call('human', 'message.send', { to: 'pm', body: 'Build a timer.\n' }); let state = await call('pm', 'request.open', { request: sent.request }); state = await call('pm', 'request.propose', { request: sent.request, revision: state.revision, op: 'plan', source_project: 'stack', project: 'timer', name: 'Timer', tracker_project: 2 }); call('human', 'decision.resolve', { id: state.selection.decision, choice: mode }); state = await bus.requestLifecycle.resume('demo', sent.request); const context = { business: { id: 'demo', human: 'jason', projects: { timer: { root: join(f.dataRoot, 'products/demo/timer/code') } } }, project: 'timer', dataRoot: f.dataRoot }; const p = await prdFixture(t, context); const args = { request: sent.request, revision: state.revision, op: 'prd1', prd_version: '1.0', records_commit: p.args.records_commit, prd_path: p.args.prd_path, requirement_ids: p.args.requirement_ids }; const submit = () => call('pm', 'prd.submit', args); const approve = (submission, who = mode === 'guided' ? 'human' : 'cto') => call(who, 'decision.resolve', { id: submission.decision.id, choice: 'yes' }); const activate = submission => bus.requirementCatalog.activate({ request: sent.request, decision: submission.decision.id }); return { ...p, f, bus, caps, call, state, request: sent.request, args, submit, approve, activate, boot, bind, config: bootConfig({ system, businessId: 'demo', env: f.env, warn: () => {} }), store, journal: createRequestStore(store) }; } test('autonomous PM submits technical PRD to distinct CTO, no task before atomic idempotent activation', async t => { const f = await setup(t), submission = await f.submit(); assert.equal(submission.decision.action, 'prd.approve'); assert.equal(submission.decision.route_to, 'cto'); assert.equal(submission.decision.authorization.target.startsWith('prd-'), true); assert.equal(submission.decision.spec_commit, submission.candidate.spec_commit); assert.equal(submission.decision.blocking, false); assert.equal(f.store.get('SELECT count(*) n FROM task_snapshots').n, 0); await assert.rejects(f.activate(submission), { code: 'prd-not-approved' }); assert.throws(() => f.call('pm', 'decision.resolve', { id: submission.decision.id, choice: 'yes' }), { code: 'not-resolver' }); f.approve(submission); f.call('cto', 'role.release'); const snap = await f.activate(submission); assert.equal(snap.requirements[0].text, 'A working timer.'); assert.equal(snap.origin, 'product'); assert.deepEqual(await f.activate(submission), snap); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 1); assert.equal(f.journal.readRequest('demo', f.request).phase, 'planning'); assert.equal(f.journal.effects('demo', f.request).filter(e => e.kind === 'prd-activate' && e.stage === 'committed').length, 1); assert.equal((await f.bus.broker.requirementsFor(f.caps.pm, { project_key: 'timer', requirement_snapshot: snap.id, requirement: 'REQ-POMO-1' })).id, snap.id); }); test('guided routes the same PRD action to owner without a routing citation', async t => { const f = await setup(t, 'guided'), sub = await f.submit(); assert.equal(sub.decision.route_to, 'human'); assert.equal(sub.decision.spec_commit, null); assert.throws(() => f.approve(sub, 'cto'), { code: 'human-required' }); f.approve(sub); assert.equal((await f.activate(sub)).approval.by, 'jason'); }); test('submit is PM-only, strict, revision checked, idempotent and size rejected before a decision', async t => { const f = await setup(t); for (const who of ['coder','cto','human','reader']) await assert.rejects(Promise.resolve().then(() => f.call(who, 'prd.submit', f.args))); await assert.rejects(f.call('pm', 'prd.submit', { ...f.args, prd_blob: 'forged' })); await assert.rejects(f.call('pm', 'prd.submit', { ...f.args, revision: 1 }), { code: 'request-revision-conflict' }); const count = f.store.get('SELECT count(*) n FROM decisions').n; const records_commit = f.save('docs/prd-1.0.md', 'x'.repeat(32769)); await assert.rejects(f.call('pm', 'prd.submit', { ...f.args, records_commit }), { code: 'prd-candidate-size' }); assert.equal(f.store.get('SELECT count(*) n FROM decisions').n, count); }); for (const path of ['docs/prd-1.0.md','requirements/REQ-POMO-1.json','spec/north-star.md']) test(`activation rereads current selected content and intent: ${path}`, async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); f.save(path, path.endsWith('.json') ? JSON.stringify({ recordVersion: 1, id: 'REQ-POMO-1', title: 'Other', text: 'Other', status: 'accepted' }) : 'Changed\n'); await assert.rejects(f.activate(sub)); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 0); }); test('forged accepted status is no authority, wrong project and business cannot borrow a snapshot', async t => { const f = await setup(t); f.args.records_commit = f.save('requirements/REQ-POMO-1.json', JSON.stringify({ recordVersion: 1, id: 'REQ-POMO-1', title: 'Timer', text: 'A working timer.', status: 'accepted' }), 'pm.pm'); await assert.rejects(f.bus.broker.requirementsFor(f.caps.pm, { project_key: 'timer' }), { code: 'requirement-snapshot-required' }); const sub = await f.submit(); f.approve(sub); const snap = await f.activate(sub); await assert.rejects(f.bus.broker.requirementsFor(f.caps.pm, { project_key: 'stack', requirement_snapshot: snap.id })); await assert.rejects(f.bus.requirementCatalog.resolve({ business: 'other', project: 'timer', snapshot: snap.id })); }); test('fresh runtime resolves identical approved snapshot from committed records and journal alone', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); const snap = await f.activate(sub); await f.bus.close(); const next = await f.boot(); assert.deepEqual(await next.requirementCatalog.resolve({ business: 'demo', project: 'timer', snapshot: snap.id }), snap); }); test('owner-only explicit revocation rejects stale revisions and makes pending work wait for replacement', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); const snap = await f.activate(sub); const args = { project: 'timer', snapshot: snap.id, revision: 1, reason: 'Replace requirements', op: 'revoke1' }; for (const who of ['pm','human','reader']) assert.throws(() => f.call(who, 'prd.revoke.console', args), { code: 'human-required' }); await assert.rejects(f.call('console', 'prd.revoke.console', { ...args, revision: 0 })); const revoked = await f.call('console', 'prd.revoke.console', args); assert.deepEqual(await f.call('console', 'prd.revoke.console', args), revoked); await assert.rejects(f.bus.broker.requirementsFor(f.caps.pm, { project_key: 'timer', requirement_snapshot: snap.id }), { code: 'requirement-snapshot-revoked' }); assert.equal(f.journal.readRequest('demo', f.request).wait.code, 'requirement-snapshot-revoked'); }); test('mode and spec changes invalidate admissions and active snapshots', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); let state = f.journal.readRequest('demo', f.request); await f.call('console', 'request.mode.console', { request: f.request, revision: state.revision, mode_revision: state.mode_revision, mode: 'guided', op: 'mode1' }); await assert.rejects(f.activate(sub)); state = f.journal.readRequest('demo', f.request); const next = await f.call('pm', 'prd.submit', { ...f.args, revision: state.revision, op: 'prd2' }); f.approve(next, 'human'); const snap = await f.activate(next); const spec = await f.call('human', 'project.file', { project: 'timer', path: 'spec/north-star.md' }); await f.call('human', 'project.file.save', { project: 'timer', path: spec.path, base: spec.commit, content: 'Changed intent\n' }); await assert.rejects(f.bus.requirementCatalog.resolve({ business: 'demo', project: 'timer', snapshot: snap.id }), { code: 'requirement-snapshot-revoked' }); }); for (const fault of ['missing-authority','self-instance','same-definition']) test(`autonomous arbiter ${fault} is a setup wait without a decision`, async t => { const f = await setup(t), b = structuredClone(f.config.businesses.demo); if (fault === 'missing-authority') b.roles.cto.authority.withinRole = b.roles.cto.authority.withinRole.filter(a => a !== 'decision.resolve.technical'); if (fault === 'self-instance') b.arbiters.technical = 'pm'; if (fault === 'same-definition') b.roles.cto.definition = 'pm'; f.bus.broker.reloadBusiness('demo', b); const count = f.store.get('SELECT count(*) n FROM decisions').n, result = await f.submit(); assert.equal(result.wait.kind, 'setup'); assert.match(result.wait.code, /^prd-(arbiter-unavailable|self-approval)$/); assert.equal(f.store.get('SELECT count(*) n FROM decisions').n, count); }); test('activation rechecks current role authority and cannot use another candidate decision', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); await assert.rejects(f.bus.requirementCatalog.activate({ request: f.request, decision: f.state.selection.decision }), { code: 'prd-candidate-required' }); const b = structuredClone(f.config.businesses.demo); b.roles.cto.authority.withinRole = b.roles.cto.authority.withinRole.filter(a => a !== 'decision.resolve.technical'); f.bus.broker.reloadBusiness('demo', b); await assert.rejects(f.activate(sub), { code: 'prd-arbiter-unavailable' }); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 0); }); test('submission replay has one decision and rejects a changed operation binding', async t => { const f = await setup(t), sub = await f.submit(); assert.deepEqual(await f.submit(), sub); await assert.rejects(f.call('pm', 'prd.submit', { ...f.args, prd_version: '2.0' }), { code: 'request-operation-conflict' }); }); test('changed current intent is refused before submission creates a decision', async t => { const f = await setup(t), count = f.store.get('SELECT count(*) n FROM decisions').n; f.save('spec/north-star.md', 'Changed spec\n'); await assert.rejects(f.submit(), { code: 'stale-spec' }); assert.equal(f.store.get('SELECT count(*) n FROM decisions').n, count); }); test('activation rollback leaves no snapshot, receipt or continuation after an insert failure', async t => { const f = await setup(t), sub = await f.submit(); // A host fixture trigger injects a failure after the snapshot insert, before its activation event. const db = new DatabaseSync(join(f.f.dataRoot, 'bus/bus.sqlite')); db.exec("CREATE TRIGGER reject_activation BEFORE INSERT ON request_snapshot_events BEGIN SELECT RAISE(ABORT,'fixture-interruption'); END"); f.approve(sub); await assert.rejects(f.activate(sub)); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 0); assert.equal(f.journal.effects('demo', f.request).filter(e => e.kind === 'prd-activate').length, 0); assert.notEqual(f.journal.readRequest('demo', f.request).phase, 'planning'); db.exec('DROP TRIGGER reject_activation'); db.close(); }); test('owner draft read shows exact bound PRD and requirements without a generic file endpoint', async t => { const f = await setup(t), sub = await f.submit(); const draft = await f.call('console', 'request.draft.console', { request: f.request }); assert.deepEqual(draft.candidate, sub.candidate); assert.equal(draft.prd, '# Timer 1.0\nA timer.\n'); assert.equal(draft.requirements[0].id, 'REQ-POMO-1'); for (const who of ['pm','human','reader']) assert.throws(() => f.call(who, 'request.draft.console', { request: f.request }), { code: 'human-required' }); }); test('owner draft snapshot counter follows activation and revoke independently of the request revision', async t => { const f = await setup(t); const read = () => f.call('console', 'request.draft.console', { request: f.request }); assert.deepEqual(await read(), { candidate: null, prd: null, requirements: [], snapshot: { id: null, revision: 0 } }); const sub = await f.submit(); assert.deepEqual((await read()).snapshot, { id: null, revision: 0 }); f.approve(sub); const snap = await f.activate(sub), active = await read(); assert.deepEqual(active.snapshot, { id: snap.id, revision: 1 }); assert.notEqual(active.snapshot.revision, f.journal.readRequest('demo', f.request).revision); const args = { project: 'timer', snapshot: active.snapshot.id, revision: active.snapshot.revision, reason: 'Replace this version', op: 'revoke-read' }; await assert.rejects(f.call('console', 'prd.revoke.console', { ...args, revision: f.journal.readRequest('demo', f.request).revision }), { code: 'request-revision-conflict' }); const receipt = await f.call('console', 'prd.revoke.console', args); assert.equal(receipt.kind, 'revoked'); assert.deepEqual(await read(), { candidate: null, prd: null, requirements: [], snapshot: { id: null, revision: 2 } }); await assert.rejects(f.call('console', 'prd.revoke.console', { ...args, op: 'stale-read' }), { code: 'request-revision-conflict' }); for (const who of ['pm', 'cto', 'coder', 'human', 'reader']) assert.throws(() => f.call(who, 'request.draft.console', { request: f.request }), { code: 'human-required' }); // The next submission sees the existing project counter even though it has no active snapshot yet. await f.call('pm', 'prd.submit', { ...f.args, revision: f.journal.readRequest('demo', f.request).revision, op: 'prd-again' }); assert.deepEqual((await read()).snapshot, { id: null, revision: 2 }); }); test('3p through 3s migration preserves existing request rows and the append-only guards', async t => { const { readFileSync, chmodSync } = await import('node:fs'); const { Store, schemaDigest } = await import('../src/store.mjs'); const root = tmp(t, 'prd-migration-'); mkdirSync(join(root, 'bus'), { mode: 0o700 }); const path = join(root, 'bus/bus.sqlite'), ddl = readFileSync(new URL('../schema.sql', import.meta.url), 'utf8') // 3s-3t (#797) adds project_archives; the 3p fixture predates it and the replayed chain recreates it. .replace(readFileSync(new URL('../src/migrations/3s-3t.sql', import.meta.url), 'utf8'), '') .replace(readFileSync(new URL('../src/migrations/3t-3u.sql', import.meta.url), 'utf8'), '') // 3u-3v (#1873, U4a) adds store_tasks after project_system_types; the same holds for it. .replace(readFileSync(new URL('../src/migrations/3u-3v.sql', import.meta.url), 'utf8'), '') .replace(",'prd.submitted','prd.activated','prd.invalidated','question.asked','question.answered','attach.selected','mode.changed','advanced'", ''); const db = new DatabaseSync(path); db.exec(ddl); const at = '2026-11-03T00:00:00.000Z'; db.prepare('INSERT INTO meta VALUES(?,?)').run('schema_version', '3p'); db.prepare('INSERT INTO meta VALUES(?,?)').run('schema_digest', schemaDigest(db)); db.prepare('INSERT INTO meta VALUES(?,?)').run('comment_cursor_epoch', at); db.prepare("INSERT INTO messages(id,at,business,from_role,from_run,to_role,class,body) VALUES('m',?,'demo','human','human','pm','INFO','Timer')").run(at); db.prepare("INSERT INTO events(id,at,business,kind,subject,body) VALUES('r',?,'demo','human.input','m',?)").run(at, JSON.stringify({ kind: 'instruction', message: 'm' })); db.prepare("INSERT INTO request_events(business,request,revision,kind,at,body) VALUES('demo','r',1,'opened',?,'{}')").run(at); const before = db.prepare('SELECT * FROM request_events').all(); assert.throws(() => db.prepare("INSERT INTO request_events(business,request,revision,kind,at,body) VALUES('demo','r',2,'prd.submitted',?,'{}')").run(at)); db.close(); chmodSync(path, 0o600); assert.throws(() => new Store(root), /schema-mismatch/); const store = new Store(root, { migrateSchema: true }); t.after(() => store.close()); assert.deepEqual(store.all('SELECT * FROM request_events'), before); assert.equal(store.get("SELECT value FROM meta WHERE key='schema_version'").value, '3v'); store.run("INSERT INTO request_events(business,request,revision,kind,at,body) VALUES('demo','r',2,'prd.submitted',?,'{}')", at); assert.throws(() => store.run('DELETE FROM request_events'), /append-only/); assert.throws(() => store.run("UPDATE request_events SET body='{}'"), /append-only/); assert.throws(() => store.run("INSERT INTO request_events(business,request,revision,kind,at,body) VALUES('demo','r',4,'prd.activated',?,'{}')", at), /revision-conflict/); }); test('guided Console approval uses the existing confirmation digest and attributed resolution evidence', async t => { const f = await setup(t, 'guided'), sub = await f.submit(); const preview = f.call('console', 'decision.preview.console', { id: sub.decision.id }); f.call('console', 'decision.resolve.console', { id: sub.decision.id, choice: 'yes', digest: preview.digest }); const snap = await f.activate(sub); assert.equal(snap.approval.via, 'console'); assert.equal(f.store.get("SELECT count(*) n FROM messages WHERE decision=? AND class='DECISION'", sub.decision.id).n, 1); }); test('declined draft returns to discovery and mode change revokes an active snapshot', async t => { const f = await setup(t), sub = await f.submit(); f.call('cto', 'decision.resolve', { id: sub.decision.id, choice: 'no' }); const discovery = await f.bus.requestLifecycle.resume('demo', f.request); assert.equal(discovery.phase, 'discovery'); assert.equal(discovery.wait.code, 'prd-changes-requested'); const next = await f.call('pm', 'prd.submit', { ...f.args, revision: discovery.revision, op: 'revision2' }); f.approve(next); const snap = await f.activate(next); const state = f.journal.readRequest('demo', f.request); await f.call('console', 'request.mode.console', { request: f.request, revision: state.revision, mode_revision: state.mode_revision, mode: 'guided', op: 'mode2' }); await assert.rejects(f.bus.requirementCatalog.resolve({ business: 'demo', project: 'timer', snapshot: snap.id }), { code: 'requirement-snapshot-revoked' }); }); const archiveProduct = f => { const p = f.store.get("SELECT id FROM projects WHERE business='demo' AND repo='timer'"); assert.ok(p); f.call('human', 'project.archive', { project: p.id }); return p.id; }; test('archive fences product admission and PRD submission by repository key', async t => { const f = await setup(t), count = f.store.get('SELECT count(*) n FROM decisions').n; const p = archiveProduct(f); assert.equal(f.bus.admission({ business: 'demo', project: 'timer' }).code, 'project-archived'); assert.equal(f.bus.admission({ business: 'demo', project: 'timer' }).ready, false); assert.equal(f.bus.admission({ business: 'demo', project: 'stack' }).ready, true); await assert.rejects(f.submit(), { code: 'project-archived' }); assert.equal(f.store.get('SELECT count(*) n FROM decisions').n, count); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 0); f.call('human', 'project.unarchive', { project: p }); assert.equal(f.bus.admission({ business: 'demo', project: 'timer' }).ready, true); assert.ok(await f.submit()); }); test('archive after PRD approval refuses activation without publishing a snapshot', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); archiveProduct(f); await assert.rejects(f.activate(sub), { code: 'project-archived' }); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 0); }); test('archived products refuse task targets and requirements admission but retain snapshot reads and cancellation', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); const snap = await f.activate(sub); archiveProduct(f); const args = { request: f.request, project_key: 'timer', requirement_snapshot: snap.id, op: 'new-task', requirement: 'REQ-POMO-1' }; assert.throws(() => f.bus.broker.taskCreateTarget(f.caps.pm, args), { code: 'project-archived' }); await assert.rejects(f.bus.broker.requirementsFor(f.caps.pm, args), { code: 'project-archived' }); await assert.rejects(f.bus.broker.requirementsFor(f.caps.pm, { project_key: 'timer', requirement_snapshot: snap.id }), { code: 'project-archived' }); assert.equal((await f.bus.requirementCatalog.resolve({ business: 'demo', project: 'timer', snapshot: snap.id })).id, snap.id); const state = await f.call('console', 'request.get.console', { request: f.request }); const cancelled = await f.call('console', 'request.cancel.console', { request: f.request, revision: state.revision, op: 'cancel-archived' }); assert.equal(cancelled.phase, 'cancelled'); assert.deepEqual(await f.bus.requestLifecycle.resume('demo', f.request), cancelled, 'archive recovery must not rewrite terminal request state'); }); test('archived products disappear from the request picker and refuse attach/propose explicitly', async t => { const f = await setup(t); const sent = f.call('human', 'message.send', { to: 'pm', body: 'New work.' }); let state = await f.call('pm', 'request.open', { request: sent.request }); state = await f.call('pm', 'request.propose', { request: sent.request, revision: state.revision, op: 'another-plan', source_project: 'stack', project: 'new-timer', name: 'New Timer', tracker_project: 3 }); f.call('human', 'decision.resolve', { id: state.selection.decision, choice: 'existing' }); state = await f.bus.requestLifecycle.resume('demo', sent.request); assert.ok((await f.call('console', 'request.projects.console', { request: sent.request })).some(p => p.project === 'timer')); archiveProduct(f); assert.ok(!(await f.call('console', 'request.projects.console', { request: sent.request })).some(p => p.project === 'timer')); await assert.rejects(f.call('console', 'request.attach.console', { request: sent.request, revision: state.revision, project: 'timer', op: 'archived-pick' }), { code: 'project-archived' }); await assert.rejects(f.call('pm', 'request.propose', { request: sent.request, revision: state.revision, op: 'archived-plan', source_project: 'timer', project: 'timer', name: 'Timer', tracker_project: 2 }), { code: 'project-archived' }); }); test('archive stops new continuations and request-bound launch/manifest recovery', async t => { const f = await setup(t); const before = f.store.get('SELECT count(*) n FROM messages').n; archiveProduct(f); await f.bus.requestLifecycle.resume('demo', f.request); assert.equal(f.store.get('SELECT count(*) n FROM messages').n, before); assert.ok(!f.bus.requestLaunches('demo').some(i => i.project === 'timer')); assert.throws(() => f.bus.requestManifest({ business: 'demo', instance: 'pm', project: 'timer', run: 'pm-run' }), { code: 'project-archived' }); }); test('a pending resource plan cannot schedule intake from an archived source product', async t => { const f = await setup(t); const sent = f.call('human', 'message.send', { to: 'pm', body: 'Build another timer.' }); let state = await f.call('pm', 'request.open', { request: sent.request }); state = await f.call('pm', 'request.propose', { request: sent.request, revision: state.revision, op: 'pending-plan', source_project: 'timer', project: 'another-timer', name: 'Another timer', tracker_project: 3 }); archiveProduct(f); f.call('human', 'decision.resolve', { id: state.selection.decision, choice: 'guided' }); const before = f.store.get('SELECT count(*) n FROM messages').n; state = await f.bus.requestLifecycle.resume('demo', sent.request); assert.equal(state.wait.code, 'project-archived'); assert.equal(f.store.get('SELECT count(*) n FROM messages').n, before); assert.ok(!f.bus.requestLaunches('demo').some(i => i.request === sent.request)); }); test('activation rechecks archive after asynchronous candidate reads', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); const activating = f.activate(sub); archiveProduct(f); await assert.rejects(activating, { code: 'project-archived' }); assert.equal(f.store.get('SELECT count(*) n FROM request_snapshots').n, 0); }); test('requirements admission rechecks archive after asynchronous catalog reads', async t => { const f = await setup(t), sub = await f.submit(); f.approve(sub); const snap = await f.activate(sub); const admission = f.bus.broker.requirementsFor(f.caps.pm, { project_key: 'timer', requirement_snapshot: snap.id }); archiveProduct(f); await assert.rejects(admission, { code: 'project-archived' }); });