@@ -17,6 +17,9 @@ const root = fileURLToPath(new URL('../', import.meta.url));
1717const fakeZCode = join ( root , 'tests/fixtures/fake-zcode-cli.mjs' ) ;
1818const legacyBroker = join ( root , 'tests/fixtures/legacy-zcode-broker-v1.mjs' ) ;
1919const ownerStoreLockHolder = join ( root , 'tests/fixtures/owner-store-lock-holder.mjs' ) ;
20+ // Parallel Windows runners can spend more than 750 ms scheduling a legacy
21+ // broker request even though the SessionEnd cleanup budget remains bounded.
22+ const brokerTestRequestTimeoutMs = process . platform === 'win32' ? 2_000 : 750 ;
2023
2124function isGateRunPath ( path ) { return path . split ( sep ) . includes ( 'gate-runs' ) ; }
2225
@@ -207,17 +210,17 @@ test('HEAD cleanup interoperates with the hermetic v1 broker while a sibling rem
207210 const legacy = spawn ( process . execPath , [ legacyBroker , configPath ] , { cwd, env : { ...process . env , FAKE_ZCODE_RECORD : record , FAKE_ZCODE_STOP_ERROR_PREFIX : 'legacy-history-' } , stdio : [ 'ignore' , 'ignore' , 'pipe' ] } ) ; let legacyStderr = '' ; let legacyExited = false ; legacy . stderr . on ( 'data' , ( chunk ) => { legacyStderr += chunk ; } ) ; legacy . once ( 'exit' , ( ) => { legacyExited = true ; } ) ; await writeBrokerIdentity ( identityPath , { endpoint, pid : legacy . pid , instanceId, brokerToken } ) ;
208211 t . after ( async ( ) => { if ( ! legacyExited ) { try { process . kill ( legacy . pid , 'SIGTERM' ) ; } catch { /* already exited */ } } } ) ;
209212 const identity = { endpoint, pid : legacy . pid , instanceId, brokerToken } ; const readyDeadline = Date . now ( ) + 5_000 ; while ( Date . now ( ) < readyDeadline && ! await probeBrokerHealth ( identity ) ) await new Promise ( ( resolve ) => setTimeout ( resolve , 25 ) ) ; assert . equal ( await probeBrokerHealth ( identity ) , true , legacyStderr ) ;
210- const client = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : owner , requestTimeoutMs : 750 } ) ; assert . deepEqual ( await client . brokerCapabilities ( ) , { releaseOwnerExclusions : false } ) ; await client . createSession ( { workspace : cwd , sessionId : 'legacy-later-active' } ) ; await client . close ( ) ; const siblingClient = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : sibling , requestTimeoutMs : 750 } ) ; await siblingClient . createSession ( { workspace : cwd , sessionId : 'legacy-sibling-active' } ) ; t . after ( ( ) => siblingClient . close ( ) ) ; const probe = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : probeOwner , requestTimeoutMs : 750 } ) ; assert . deepEqual ( ( await probe . releaseOwner ( [ 'legacy-exclusion-probe' ] ) ) . releasedSessionIds , [ 'legacy-exclusion-probe' ] , 'v1 broker must faithfully ignore the future exclusion field' ) ; await probe . close ( ) ;
211- const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : owner , requestTimeoutMs : 750 } ) ; assert . equal ( processAlive ( legacy . pid ) , true , 'active sibling must keep the shared legacy broker alive' ) ; let owners = JSON . parse ( await readFile ( ownershipPath , 'utf8' ) ) . sessions ; assert . equal ( owners [ 'legacy-later-active' ] , undefined ) ; assert . equal ( owners [ 'legacy-sibling-active' ] , sibling ) ; assert . equal ( owners [ 'legacy-sibling-session' ] , sibling ) ; assert . equal ( histories . filter ( ( sessionId ) => owners [ sessionId ] === owner ) . length , 17 ) ; assert . ok ( result . failedSessionIds . length <= 17 ) ; assert . ok ( result . deferredSessionCount <= 17 ) ; assert . ok ( ( await siblingClient . listSessions ( ) ) . sessions . some ( ( session ) => session . sessionId === 'legacy-sibling-active' ) ) ;
212- const reuseOwner = ownerIdForSession ( 'legacy-reuse' ) ; const reuse = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : reuseOwner , requestTimeoutMs : 750 } ) ; await reuse . createSession ( { workspace : cwd , sessionId : 'legacy-later-active' } ) ; await reuse . close ( ) ; owners = JSON . parse ( await readFile ( ownershipPath , 'utf8' ) ) . sessions ; assert . equal ( owners [ 'legacy-later-active' ] , reuseOwner ) ;
213+ const client = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : owner , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; assert . deepEqual ( await client . brokerCapabilities ( ) , { releaseOwnerExclusions : false } ) ; await client . createSession ( { workspace : cwd , sessionId : 'legacy-later-active' } ) ; await client . close ( ) ; const siblingClient = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : sibling , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; await siblingClient . createSession ( { workspace : cwd , sessionId : 'legacy-sibling-active' } ) ; t . after ( ( ) => siblingClient . close ( ) ) ; const probe = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : probeOwner , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; assert . deepEqual ( ( await probe . releaseOwner ( [ 'legacy-exclusion-probe' ] ) ) . releasedSessionIds , [ 'legacy-exclusion-probe' ] , 'v1 broker must faithfully ignore the future exclusion field' ) ; await probe . close ( ) ;
214+ const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : owner , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; assert . equal ( processAlive ( legacy . pid ) , true , 'active sibling must keep the shared legacy broker alive' ) ; let owners = JSON . parse ( await readFile ( ownershipPath , 'utf8' ) ) . sessions ; assert . equal ( owners [ 'legacy-later-active' ] , undefined ) ; assert . equal ( owners [ 'legacy-sibling-active' ] , sibling ) ; assert . equal ( owners [ 'legacy-sibling-session' ] , sibling ) ; assert . equal ( histories . filter ( ( sessionId ) => owners [ sessionId ] === owner ) . length , 17 ) ; assert . ok ( result . failedSessionIds . length <= 17 ) ; assert . ok ( result . deferredSessionCount <= 17 ) ; assert . ok ( ( await siblingClient . listSessions ( ) ) . sessions . some ( ( session ) => session . sessionId === 'legacy-sibling-active' ) ) ;
215+ const reuseOwner = ownerIdForSession ( 'legacy-reuse' ) ; const reuse = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : reuseOwner , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; await reuse . createSession ( { workspace : cwd , sessionId : 'legacy-later-active' } ) ; await reuse . close ( ) ; owners = JSON . parse ( await readFile ( ownershipPath , 'utf8' ) ) . sessions ; assert . equal ( owners [ 'legacy-later-active' ] , reuseOwner ) ;
213216 const stops = ( await readFile ( record , 'utf8' ) ) . trim ( ) . split ( '\n' ) . filter ( Boolean ) . map ( JSON . parse ) . filter ( ( call ) => call . method === 'session/stop' ) . map ( ( call ) => call . params . sessionId ) ; assert . ok ( stops . includes ( 'legacy-later-active' ) ) ; assert . ok ( ! stops . includes ( 'legacy-sibling-active' ) ) ; assert . ok ( stops . filter ( ( sessionId ) => sessionId . startsWith ( 'legacy-history-' ) ) . length <= 32 , 'legacy failed prefix retries must stay bounded' ) ;
214217} ) ;
215218
216219test ( 'legacy fallback stays inside the hook budget when the owner store lock is contended cross-process' , async ( t ) => {
217220 const { cwd, data } = await workspace ( ) ; const record = join ( data , 'contended-zcode-calls.jsonl' ) ; await writeFile ( record , '' ) ; const owner = ownerIdForSession ( 'contended-owner' ) ; const sibling = ownerIdForSession ( 'contended-sibling' ) ; const histories = Array . from ( { length : 17 } , ( _ , index ) => `contended-history-${ String ( index ) . padStart ( 2 , '0' ) } ` ) ; await reconcileBrokerOwnership ( { dataRoot : data , workspace : cwd , ownerId : owner , ownedSessionIds : histories } ) ;
218221 const storage = await resolveWorkspaceStorage ( { dataRoot : data , workspace : cwd } ) ; const brokerDirectory = join ( storage . directory , 'broker' ) ; const identityPath = join ( brokerDirectory , 'identity.json' ) ; const ownershipPath = join ( brokerDirectory , 'session-owners.json' ) ; const configPath = join ( brokerDirectory , 'contended-config.json' ) ; const endpoint = brokerEndpointFor ( { dataRoot : data , workspace : storage . workspacePath } ) ; const instanceId = 'd' . repeat ( 48 ) ; const brokerToken = 'e' . repeat ( 64 ) ; await writeFile ( configPath , JSON . stringify ( { endpoint, instanceId, brokerToken, launch : { command : process . execPath , args : [ fakeZCode ] , target : fakeZCode } , workspace : storage . workspacePath , ownershipPath, identityPath } ) ) ;
219222 const legacy = spawn ( process . execPath , [ legacyBroker , configPath ] , { cwd, env : { ...process . env , FAKE_ZCODE_RECORD : record , FAKE_ZCODE_STOP_ERROR_PREFIX : 'contended-history-' } , stdio : [ 'ignore' , 'ignore' , 'pipe' ] } ) ; let legacyExited = false ; legacy . once ( 'exit' , ( ) => { legacyExited = true ; } ) ; await writeBrokerIdentity ( identityPath , { endpoint, pid : legacy . pid , instanceId, brokerToken } ) ; t . after ( ( ) => { if ( ! legacyExited ) { try { process . kill ( legacy . pid , 'SIGTERM' ) ; } catch { /* exited */ } } } ) ; const identity = { endpoint, pid : legacy . pid , instanceId, brokerToken } ; const readyDeadline = Date . now ( ) + 5_000 ; while ( Date . now ( ) < readyDeadline && ! await probeBrokerHealth ( identity ) ) await new Promise ( ( resolve ) => setTimeout ( resolve , 25 ) ) ; assert . equal ( await probeBrokerHealth ( identity ) , true ) ;
220- const target = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : owner , requestTimeoutMs : 750 } ) ; await target . createSession ( { workspace : cwd , sessionId : 'contended-target-active' } ) ; await target . close ( ) ; const siblingClient = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : sibling , requestTimeoutMs : 750 } ) ; await siblingClient . createSession ( { workspace : cwd , sessionId : 'contended-sibling-active' } ) ; t . after ( ( ) => siblingClient . close ( ) ) ;
223+ const target = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : owner , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; await target . createSession ( { workspace : cwd , sessionId : 'contended-target-active' } ) ; await target . close ( ) ; const siblingClient = await createZCodeClient ( { workspace : cwd , brokerEndpoint : endpoint , brokerToken, ownerId : sibling , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; await siblingClient . createSession ( { workspace : cwd , sessionId : 'contended-sibling-active' } ) ; t . after ( ( ) => siblingClient . close ( ) ) ;
221224 const beforeContention = JSON . parse ( await readFile ( ownershipPath , 'utf8' ) ) . sessions ; assert . equal ( beforeContention [ 'contended-target-active' ] , owner ) ; assert . equal ( beforeContention [ 'contended-sibling-active' ] , sibling ) ;
222225 const holder = spawn ( process . execPath , [ ownerStoreLockHolder , data , cwd , 'identity.json' ] , { stdio : [ 'pipe' , 'pipe' , 'pipe' ] } ) ; t . after ( ( ) => { try { process . kill ( holder . pid , 'SIGTERM' ) ; } catch { /* exited */ } } ) ; const holderArmed = await new Promise ( ( resolvePromise , reject ) => { holder . stdout . once ( 'data' , ( chunk ) => resolvePromise ( chunk . toString ( 'utf8' ) . trim ( ) ) ) ; holder . once ( 'error' , reject ) ; holder . once ( 'exit' , ( code ) => reject ( new Error ( `lock holder exited ${ code } ` ) ) ) ; } ) ; assert . equal ( holderArmed , `armed:${ ownershipPath } .lock` ) ;
223226 const holderReadyPromise = new Promise ( ( resolvePromise , reject ) => { holder . stdout . once ( 'data' , ( chunk ) => resolvePromise ( chunk . toString ( 'utf8' ) . trim ( ) ) ) ; holder . once ( 'error' , reject ) ; holder . once ( 'exit' , ( code ) => reject ( new Error ( `lock holder exited ${ code } ` ) ) ) ; } ) ; holder . stdin . write ( 'acquire' ) ; const holderReady = await holderReadyPromise ; assert . equal ( holderReady , `ready:${ ownershipPath } .lock` ) ; assert . equal ( processAlive ( holder . pid ) , true ) ;
@@ -244,7 +247,7 @@ test('owner release spans existing broker profiles and preserves mappings whose
244247 const sibling = await createManagedZCodeClient ( { dataRoot : data , workspace : cwd , launch, ownerId : ownerB , env : { ...process . env , FAKE_ZCODE_RECORD : record , ...( index ? { FAKE_ZCODE_ERROR : 'session/stop' } : { } ) } , ...profile } ) ;
245248 await sibling . createSession ( { workspace : cwd , sessionId : `owner-b-${ index } ` } ) ; await sibling . close ( ) ;
246249 }
247- const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : ownerA , requestTimeoutMs : 750 } ) ;
250+ const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : ownerA , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ;
248251 assert . deepEqual ( result . releasedSessionIds , [ 'owner-a-0' ] ) ; assert . deepEqual ( result . failedSessionIds , [ 'owner-a-1' ] ) ;
249252 const storage = await resolveWorkspaceStorage ( { dataRoot : data , workspace : cwd } ) ; const brokerFiles = await readdir ( join ( storage . directory , 'broker' ) ) ; const stores = brokerFiles . filter ( ( name ) => / ^ s e s s i o n - o w n e r s (?: - [ a - f 0 - 9 ] { 16 } ) ? \. j s o n $ / . test ( name ) ) ;
250253 const mappings = Object . assign ( { } , ...await Promise . all ( stores . map ( async ( name ) => {
@@ -264,7 +267,7 @@ test('hung owner stops are broker-bounded and retain every unconfirmed and sibli
264267 for ( let index = 0 ; index < 4 ; index += 1 ) await first . createSession ( { workspace : cwd , sessionId : `hung-owner-${ index } ` } ) ; await first . close ( ) ;
265268 const sibling = await createManagedZCodeClient ( { dataRoot : data , workspace : cwd , launch, ownerId : ownerB , env : peerEnv } ) ; await sibling . createSession ( { workspace : cwd , sessionId : 'hung-sibling' } ) ; await sibling . close ( ) ;
266269 const storage = await resolveWorkspaceStorage ( { dataRoot : data , workspace : cwd } ) ; const identity = JSON . parse ( await readFile ( join ( storage . directory , 'broker/identity.json' ) , 'utf8' ) ) ; const started = Date . now ( ) ;
267- const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : ownerA , requestTimeoutMs : 750 } ) ;
270+ const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : ownerA , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ;
268271 assert . ok ( Date . now ( ) - started < 2_000 , 'broker owner cleanup must finish inside the SessionEnd budget' ) ; assert . deepEqual ( result . releasedSessionIds , [ ] ) ; assert . deepEqual ( result . failedSessionIds . sort ( ) , [ 'hung-owner-0' , 'hung-owner-1' , 'hung-owner-2' , 'hung-owner-3' ] ) ; assert . equal ( result . deferredSessionCount , 0 ) ;
269272 const owners = JSON . parse ( await readFile ( join ( storage . directory , 'broker/session-owners.json' ) , 'utf8' ) ) . sessions ;
270273 assert . deepEqual ( owners , { 'hung-owner-0' : ownerA , 'hung-owner-1' : ownerA , 'hung-owner-2' : ownerA , 'hung-owner-3' : ownerA , 'hung-sibling' : ownerB } ) ;
@@ -276,7 +279,7 @@ test('owner release does not spawn ZCode when a live broker has only historical
276279 const { cwd, data } = await workspace ( ) ; const record = join ( data , 'zcode-calls.jsonl' ) ; await writeFile ( record , '' ) ; const ownerA = ownerIdForSession ( 'historical-a' ) ; const ownerB = ownerIdForSession ( 'historical-b' ) ;
277280 await reconcileBrokerOwnership ( { dataRoot : data , workspace : cwd , ownerId : ownerA , ownedSessionIds : [ 'historical-a-session' ] } ) ; await reconcileBrokerOwnership ( { dataRoot : data , workspace : cwd , ownerId : ownerB , ownedSessionIds : [ 'historical-b-session' ] } ) ;
278281 const identity = await ensureZCodeBroker ( { dataRoot : data , workspace : cwd , launch : { command : process . execPath , args : [ fakeZCode ] , target : fakeZCode } , env : { ...process . env , FAKE_ZCODE_RECORD : record } } ) ; const started = Date . now ( ) ;
279- const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : ownerA , requestTimeoutMs : 750 } ) ; assert . ok ( Date . now ( ) - started < 2_000 ) ; assert . deepEqual ( result , { releasedSessionIds : [ 'historical-a-session' ] , failedSessionIds : [ ] , deferredSessionCount : 0 } ) ; assert . equal ( await readFile ( record , 'utf8' ) , '' , 'cleanup must not spawn the configured ZCode peer' ) ;
282+ const result = await releaseManagedZCodeOwner ( { dataRoot : data , workspace : cwd , ownerId : ownerA , requestTimeoutMs : brokerTestRequestTimeoutMs } ) ; assert . ok ( Date . now ( ) - started < 2_000 ) ; assert . deepEqual ( result , { releasedSessionIds : [ 'historical-a-session' ] , failedSessionIds : [ ] , deferredSessionCount : 0 } ) ; assert . equal ( await readFile ( record , 'utf8' ) , '' , 'cleanup must not spawn the configured ZCode peer' ) ;
280283 const storage = await resolveWorkspaceStorage ( { dataRoot : data , workspace : cwd } ) ; assert . deepEqual ( JSON . parse ( await readFile ( join ( storage . directory , 'broker/session-owners.json' ) , 'utf8' ) ) . sessions , { 'historical-b-session' : ownerB } ) ;
281284 const deadline = Date . now ( ) + 2_000 ; while ( Date . now ( ) < deadline && processAlive ( identity . pid ) ) await new Promise ( ( resolve ) => setTimeout ( resolve , 25 ) ) ; assert . equal ( processAlive ( identity . pid ) , false ) ;
282285} ) ;
0 commit comments