// L1/#651: real independent agent retirements, first failure still reported. import test from 'node:test'; import assert from 'node:assert/strict'; import { BusError } from '../src/broker.mjs'; import { lifecycleFixture, id } from './model-lifecycle-fixtures.mjs'; async function setup(t){const f=await lifecycleFixture(t),c=f.create();await c.manager.reconcile();const a=await c.manager.ensure('demo/coder'),b=await c.manager.ensure('demo/other');await c.manager.rotate('demo/coder');await c.manager.rotate('demo/other');f.state.runs=[{instance:'demo/coder',run:'blocked-own-run',keyId:id(a.key)}];f.advance(121*60_000);return {...f,...c,a,b};} test('L1 blocked first agent does not starve independent later retirement',async t=>{ const f=await setup(t);await assert.rejects(f.manager.sweep(),e=>e.code==='proxy-stop-failed'); assert.equal(f.state.keys.includes(f.b.key),false,'later independent key retired despite earlier failure'); assert.equal(f.credentials.modelRecord('demo/other').retiring.length,0);assert.equal(f.state.keys.includes(f.a.key),true);assert.equal(f.credentials.modelRecord('demo/coder').retiring.length,1); assert.deepEqual(f.state.stopAttempts,['blocked-own-run']);f.valueFree(); }); test('L1 successful retry retains first refusal semantics without deleting blocked metadata',async t=>{ const f=await setup(t);await assert.rejects(f.manager.sweep(),e=>e.code==='proxy-stop-failed'); assert.ok(f.receipts.some(r=>r.action==='proxy.sweep'&&r.instance==='demo/coder'&&r.code==='proxy-stop-failed'),'failed instance has value-free sweep outcome'); f.state.stopFailure=false;await f.manager.sweep();assert.equal(f.state.keys.includes(f.a.key),false);assert.equal(f.state.keys.includes(f.b.key),false);f.valueFree(); }); test('L1 no-error sweep still completes both independent retirements',async t=>{ const f=await setup(t);f.assertHistoryGuard();f.state.runs=[];await f.manager.sweep();assert.equal(f.state.keys.includes(f.a.key),false);assert.equal(f.state.keys.includes(f.b.key),false);f.valueFree(); }); test('L1 preserves exact original first error after visiting later independently failing agent',async t=>{ const f=await setup(t),first=new BusError('proxy-stop-failed'),second=new BusError('proxy-down'); f.state.discoveryFaults.set('demo/coder',first);f.state.discoveryFaults.set('demo/other',second); await assert.rejects(f.manager.sweep(),error=>error===first,'original first error identity retained'); assert.deepEqual(f.state.discoveries,['demo/coder','demo/other'],'later independent failure also visited');f.valueFree(); }); test('L1 repeated no-op sweeps produce no per-instance complete receipts',async t=>{ const f=await lifecycleFixture(t),c=f.create();await c.manager.reconcile();await c.manager.ensure('demo/coder');f.receipts.length=0; await c.manager.sweep();await c.manager.sweep();await c.manager.sweep(); assert.equal(f.receipts.some(r=>r.action==='proxy.sweep'&&r.outcome==='complete'),false,'idle sweeps do not emit per-second success churn');assert.deepEqual(f.receipts,[]);f.valueFree(); });