import test from 'node:test'; import assert from 'node:assert/strict'; import {mkdtempSync,mkdirSync,readFileSync,rmSync} from 'node:fs'; import {tmpdir} from 'node:os'; import {join} from 'node:path'; import {Broker} from '../src/broker.mjs'; import {Store} from '../src/store.mjs'; import {autonomyHost,authorityHost} from '../src/autonomy-host.mjs'; import {organizationWriter} from '../../organization/src/organization.mjs'; import {instanceSettings} from '../../organization/src/instance-settings.mjs'; import {hireDecisions} from '../../organization/src/hire-decisions.mjs'; import {loadBusiness} from '../../organization/src/organization-config.mjs'; import {businessDoc,rolesCopy,writeJson} from '../../organization/tests/helpers.mjs'; const inline={tools:['read'],network:'none',authority:{withinRole:[],crossRole:[]},credentials:[],instructions:'Changed role'}; const cap={limit:100000,since:'2026-01-01T00:00:00.000Z'}; function world(t,options={}) { const root=mkdtempSync(join(tmpdir(),'au-authority-')),rolesDir=rolesCopy(root),dataRoot=join(root,'data');mkdirSync(dataRoot,{mode:0o700}); const doc=businessDoc(root,'demo');doc.plugins={harness:['pi'],'limit-check':['token-limit']}; doc.launch.limits.tokens={providers:{zai:cap}}; // #1873: an account quota alone, covered only through the host's declared harness fact. if(options.account){doc.launch.account={name:'shared',credential:'FIXTURE_TOKEN'};doc.launch.limits.tokens={keys:{shared:cap}};} doc.definitions={judge:{rev:1,role:{...inline,authority:{withinRole:['decision.resolve.authority'],crossRole:[]}},instructions:'Judge'},scout:{rev:3,role:structuredClone(inline),instructions:'Prior role'}}; // Definition instructions live beside the role composition. for(const d of Object.values(doc.definitions))delete d.role.instructions; doc.roles.judge={definition:'org:judge',vars:{harness:'pi',model:'claude-opus-5-5'}}; doc.roles.scout={definition:'org:scout',vars:{harness:'pi',model:'claude-opus-5-5'}}; doc.launch.instances.push('scout');doc.autonomy={authority:'near the loop',authorityResolver:'judge',notices:true,reverseWindowHours:1}; writeJson(join(root,'businesses/demo.json'),doc); let reversing=false,broker,now=Date.now; const writer=organizationWriter({dir:root,rolesDir,barrier:point=>{if(reversing && point==='prepared' && options.expirePrepared)Date.now=()=>now()+3600001;if(!reversing && point==='prepared' && options.removeResolverPrepared){const b=structuredClone(loaded());b.roles.judge.authority.withinRole=[];broker.reloadBusiness('demo',b);}}}); const store=new Store(dataRoot),order=[]; const loaded=()=>{const b=loadBusiness('demo',{dir:root,rolesDir});return {...b,roles:Object.fromEntries(Object.entries(b.roles).map(([n,e])=>[n,{...b.definitions[e.definition],...e}]))};}; const reload=id=>{if(reversing && options.crossAfterCommit)Date.now=()=>now()+3600001;if(reversing && options.reloadRefused)return false;broker.reloadBusiness(id,loaded());return true;}; const service=hireDecisions({writer,rolesDir,configDir:root,dataRoot,assertClean:()=>{}}); const hires=authorityHost(service,{reload,modelKeys:{prepareRevoke:async(instance,guard)=>{guard();assert.equal(instance,'demo/scout');order.push('prepare');},completeRevoke:instance=>{assert.equal(instance,'demo/scout');assert.ok(store.get("SELECT 1 FROM events WHERE json_extract(body,'$.action')='fire.stopped'"));assert.equal(writer.read('demo').document.definitions.scout.instructions,'Changed role');order.push('complete');}}}); const host=autonomyHost({writer,dataRoot,business:()=>loaded(),hires,reload,...(options.countsAccount?{countsAccount:options.countsAccount}:{})}); const instances=instanceSettings({dir:root,rolesDir,writer,revoke:()=>{order.push('fire-revoke');}}); broker=new Broker({store,businesses:{demo:loaded()},hires,autonomy:host,organization:(id,verb,args,actor,context)=>{if(verb==='instance.fireCheck'){instances.checkFire(id,args.name);return {};}if(verb==='instance.fire')return instances.fire(id,args,{actor,context});throw Error('unexpected organization verb');}}); const caps={};const call=(who,verb,args={})=>broker.request(caps[who],{verb,args}); for(const role of ['chief','judge','scout']){caps[role]=broker.bindLaunch({business:'demo',role,run:role+'-run',harness:'pi'});call(role,'role.claim');} caps.owner=broker.bindConsole({business:'demo',human:'jason',subject:{provider:'test',iss:'test',sub:'owner'}}); t.after(()=>{Date.now=now;store.close();rmSync(root,{recursive:true,force:true});}); const raise=(extra={})=>call('chief','hire.raise',{op:'change',level:'org',name:'scout',role:{inline},route:{harness:'pi',model:'claude-opus-5-5'},reason:'Change the task',...extra}); const reverse=id=>{reversing=true;return call('owner','decision.reverse.console',{id});}; return {writer,store,order,broker,call,raise,reverse,loaded}; } for(const crossAfterCommit of [false,true])test(`AU authority reverse uses runtime key wrapper and relaunches after commit (deadline crossed: ${crossAfterCommit})`,async t=>{ const f=world(t,{crossAfterCommit}),d=await f.raise();assert.deepEqual(d.hire.flagged,[]);assert.equal(d.route_to,'judge'); await f.call('judge','decision.resolve',{id:d.id,choice:'yes'});assert.equal(f.writer.read('demo').document.definitions.scout.rev,4); const r=await f.reverse(d.id);assert.equal(r.committed,true);assert.equal(r.relaunch,'requested');assert.deepEqual(f.order,['prepare','complete']); const restored=f.writer.read('demo').document.definitions.scout;assert.equal(restored.rev,5);assert.equal(restored.instructions,'Prior role'); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse'").n,1); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse.failed'").n,0); assert.ok(f.store.get("SELECT 1 FROM events WHERE json_extract(body,'$.action')='lifecycle.launch' AND subject='scout'")); }); test('AU a committed authority reverse reports pending recovery when reload refuses',async t=>{ const f=world(t,{reloadRefused:true}),d=await f.raise();await f.call('judge','decision.resolve',{id:d.id,choice:'yes'}); const r=await f.reverse(d.id);assert.equal(r.committed,true);assert.equal(r.restart,true);assert.equal(r.relaunch,'pending');assert.equal(f.writer.read('demo').document.definitions.scout.instructions,'Prior role'); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse.failed'").n,0); }); test('AU authority reverse crossing deadline in prepared IO never publishes',async t=>{ const f=world(t,{expirePrepared:true}),d=await f.raise();await f.call('judge','decision.resolve',{id:d.id,choice:'yes'});const prior=f.writer.read('demo').revision; await assert.rejects(f.reverse(d.id),{code:'reverse-window-closed'});assert.equal(f.writer.read('demo').revision,prior);f.writer.recover('demo');assert.equal(f.writer.read('demo').revision,prior); }); test('AU authority resolver rechecks budget removal after a proposal was raised',async t=>{ const f=world(t),d=await f.raise(),b=structuredClone(f.loaded());b.plugins['limit-check']=[];f.broker.reloadBusiness('demo',b); const before=f.writer.read('demo').revision;await assert.rejects(f.call('judge','decision.resolve',{id:d.id,choice:'yes'}),{code:'autonomy-precondition'}); assert.equal(f.writer.read('demo').revision,before);assert.equal(f.store.get('SELECT count(*) n FROM decision_events WHERE decision=?',d.id).n,0); }); test('AU authority apply rechecks a removed resolve right at publication',async t=>{ const f=world(t,{removeResolverPrepared:true}),d=await f.raise(),before=f.writer.read('demo').revision; await assert.rejects(f.call('judge','decision.resolve',{id:d.id,choice:'yes'}),{code:'not-resolver'}); assert.equal(f.writer.read('demo').revision,before);f.writer.recover('demo');assert.equal(f.writer.read('demo').revision,before); }); test('AU authority hire reverse runs normal fire/revoke and records committed delisting',async t=>{ const f=world(t),d=await f.raise({op:'hire',name:'helper'});await f.call('judge','decision.resolve',{id:d.id,choice:'yes'}); assert.ok(f.writer.read('demo').document.launch.instances.includes('helper')); const r=await f.reverse(d.id);assert.equal(r.committed,true);assert.ok(!f.writer.read('demo').document.launch.instances.includes('helper'));assert.deepEqual(f.order,['fire-revoke']); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse'").n,1); }); test('AU completion-event failure after durable authority inverse records committed recovery, never reverse.failed',async t=>{ const f=world(t),d=await f.raise();await f.call('judge','decision.resolve',{id:d.id,choice:'yes'}); const run=f.store.run.bind(f.store);let injected=false; f.store.run=(sql,...args)=>{ if(!injected && sql.includes('INSERT INTO events') && args.some(x=>typeof x==='string' && x.startsWith('{') && JSON.parse(x).action==='fire.done')){ injected=true;throw new Error('injected completion event failure'); } return run(sql,...args); }; const r=await f.reverse(d.id);assert.equal(injected,true);assert.equal(r.committed,true);assert.equal(r.completion,'failed');assert.equal(r.restart,true);assert.equal(r.relaunch,'pending'); assert.equal(f.writer.read('demo').document.definitions.scout.instructions,'Prior role'); assert.equal(f.writer.read('demo').document.definitions.scout.rev,5); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse'").n,1); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse.failed'").n,0); }); test('AU inverse receipt-write failure returns committed with trailPending and never writes reverse.failed',async t=>{ const f=world(t),d=await f.raise();await f.call('judge','decision.resolve',{id:d.id,choice:'yes'}); const run=f.store.run.bind(f.store);let injected=false; f.store.run=(sql,...args)=>{ if(sql.includes('INSERT INTO events') && args.some(x=>typeof x==='string' && x.startsWith('{') && JSON.parse(x).operation==='autonomy.reverse')){ injected=true;throw new Error('injected receipt write failure'); } return run(sql,...args); }; const r=await f.reverse(d.id);assert.equal(injected,true);assert.equal(r.committed,true);assert.equal(r.trailPending,true);assert.equal(r.relaunch,'requested'); assert.equal(f.writer.read('demo').document.definitions.scout.instructions,'Prior role'); assert.equal(f.store.get("SELECT count(*) n FROM events WHERE json_extract(body,'$.operation')='autonomy.reverse.failed'").n,0); }); test('AU an account-quota authority raise routes to the resolver only when the host says every harness spends the account (#1873)',async t=>{ const covered=world(t,{account:true,countsAccount:name=>name==='pi'}),d=await covered.raise();assert.equal(d.route_to,'judge'); for(const countsAccount of [undefined,()=>false,()=>null]){ const f=world(t,{account:true,countsAccount}); await assert.rejects(f.raise(),{code:'autonomy-precondition'}); assert.equal(f.store.get("SELECT count(*) n FROM decisions").n,0); } }); // #1873 review finding 2: the runtime itself carries each harness module's countsAccount into the // broker's authority budget check. Only harness modules decide; the business names none here but // through plugins.harness. for(const [harness,code] of [['claude-code',null],['pi','autonomy-precondition']])test(`AU runtime: an account-quota authority setting with only ${harness} enabled ${code?'refuses':'saves'} (#1873)`,async t=>{ const {startBroker}=await import('../src/runtime.mjs'); const {systemFor}=await import('../../organization/tests/helpers.mjs'); const root=mkdtempSync(join(tmpdir(),'au-runtime-')),rolesDir=rolesCopy(root);t.after(()=>rmSync(root,{recursive:true,force:true})); const doc=businessDoc(root,'demo');doc.plugins={harness:[harness],'limit-check':['token-limit']}; for(const r of Object.values(doc.roles))if(r.vars?.harness)r.vars.harness=harness; doc.launch.account={name:'shared',credential:'FIXTURE_TOKEN'};doc.launch.limits.tokens={keys:{shared:cap}}; doc.definitions={judge:{rev:1,role:{...inline,authority:{withinRole:['decision.resolve.authority'],crossRole:[]}},instructions:'Judge'}}; delete doc.definitions.judge.role.instructions; doc.roles.judge={definition:'org:judge',vars:{harness,model:'claude-opus-5-5'}}; writeJson(join(root,'businesses/demo.json'),doc);mkdirSync(join(root,'data'),{mode:0o700}); const loaded=()=>{const b=loadBusiness('demo',{dir:root,rolesDir});return {...b,roles:Object.fromEntries(Object.entries(b.roles).map(([n,e])=>[n,{...b.definitions[e.definition],...e}]))};}; const bus=await startBroker({dataRoot:join(root,'data'),businesses:{demo:loaded()},organization:{dir:root,rolesDir,system:systemFor(root)}});t.after(()=>bus.close()); const owner=bus.broker.bindConsole({business:'demo',human:'jason',subject:{provider:'test',iss:'test',sub:'owner'}}); const {revision}=await bus.broker.request(owner,{verb:'autonomy.get.console'}); const set=bus.broker.request(owner,{verb:'autonomy.set.console',args:{revision,autonomy:{authority:'near the loop',authorityResolver:'judge',notices:true}}}); if(code)await assert.rejects(set,{code}); else {await set;assert.equal(JSON.parse(readFileSync(join(root,'businesses/demo.json'),'utf8')).autonomy.authority,'near the loop');} });