@@ -175,8 +175,17 @@ beforeEach(() => {
175175 }
176176} )
177177
178- afterEach ( ( ) => {
178+ afterEach ( async ( ) => {
179179 cleanup ( )
180+ // Background passes (follow-up suggestions) wait out the foreground grace and may still be queued
181+ // when a test ends; let them finish so the next test's model replies are its own.
182+ const { modalityQueue } = await import ( '../src/main/modality-queue/queue' )
183+ const deadline = Date . now ( ) + 20_000
184+ for ( ; ; ) {
185+ const { running, queued } = modalityQueue . getState ( )
186+ if ( ( ! running . length && ! queued . length ) || Date . now ( ) > deadline ) break
187+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) )
188+ }
180189 fake . reset ( )
181190} )
182191
@@ -213,6 +222,11 @@ async function inTranscript(text: string | RegExp): Promise<HTMLElement> {
213222 )
214223}
215224
225+ /** Follow-up suggestions are background passes: they wait out the 3 s foreground grace after a reply,
226+ * and behind any background pass still queued. Tests that wait for several get a matching timeout. */
227+ const SUGGESTION_TEST_TIMEOUT_MS = 60_000
228+ const SUGGESTION_WAIT_MS = 15_000
229+
216230describe ( 'production workspace bridge' , ( ) => {
217231 it ( 'sends a rendered chat turn through preload, IPC, the model socket, and SQLite' , async ( ) => {
218232 fake . enqueue (
@@ -238,7 +252,7 @@ describe('production workspace bridge', () => {
238252 [ 'assistant' , 'The production bridge persisted this answer.' ]
239253 ] )
240254 } )
241- expect ( await screen . findByPlaceholderText ( 'Check the saved answer (Tab)' ) ) . toBeTruthy ( )
255+ expect ( await screen . findByPlaceholderText ( 'Check the saved answer (Tab)' , undefined , { timeout : SUGGESTION_WAIT_MS } ) ) . toBeTruthy ( )
242256 expect ( fake . requests ) . toHaveLength ( 2 )
243257 } )
244258
@@ -325,10 +339,12 @@ describe('production workspace bridge', () => {
325339 } , 15_000 )
326340
327341 it ( 'shows the real memory-chat failure instead of a fabricated answer' , async ( ) => {
328- fake . enqueue ( {
342+ // An unavailable model is tried again once (chat fallback policy): it fails both times here.
343+ const unavailable = {
329344 errorStatus : 503 ,
330345 errorBody : JSON . stringify ( { error : { message : 'Memory model is unavailable.' } } )
331- } )
346+ }
347+ fake . enqueue ( unavailable , unavailable )
332348 const user = userEvent . setup ( )
333349 renderChat ( )
334350
@@ -389,15 +405,15 @@ describe('production workspace bridge', () => {
389405 fireEvent . change ( composer , { target : { value : 'Start a long conversation' } } )
390406 await user . click ( screen . getByRole ( 'button' , { name : / ^ s e n d $ / i } ) )
391407 expect ( await inTranscript ( longAnswer ) ) . toBeTruthy ( )
392- expect ( await screen . findByPlaceholderText ( 'Continue after the context fills (Tab)' ) ) . toBeTruthy ( )
408+ expect ( await screen . findByPlaceholderText ( 'Continue after the context fills (Tab)' , undefined , { timeout : SUGGESTION_WAIT_MS } ) ) . toBeTruthy ( )
393409
394410 fireEvent . change ( composer , { target : { value : 'Continue after the context fills' } } )
395411 await user . click ( screen . getByRole ( 'button' , { name : / ^ s e n d $ / i } ) )
396412 expect ( await inTranscript ( 'The next answer still works.' ) ) . toBeTruthy ( )
397413 expect (
398414 await screen . findByText ( 'Compacted conversation to make room for more messages.' )
399415 ) . toBeTruthy ( )
400- expect ( await screen . findByPlaceholderText ( 'Review the shorter conversation (Tab)' ) ) . toBeTruthy ( )
416+ expect ( await screen . findByPlaceholderText ( 'Review the shorter conversation (Tab)' , undefined , { timeout : SUGGESTION_WAIT_MS } ) ) . toBeTruthy ( )
401417 const summaryRequest = JSON . stringify ( fake . requests [ 2 ] )
402418 expect ( summaryRequest ) . toContain ( '## Facts and preferences' )
403419 const nextModelRequest = JSON . stringify ( fake . requests [ 3 ] )
@@ -413,7 +429,7 @@ describe('production workspace bridge', () => {
413429 const notice = getRagMessages ( conversation ! . id ) . find ( ( { content } ) => content === '_Compacted_' )
414430 expect ( notice ) . toBeTruthy ( )
415431 expect ( JSON . parse ( String ( notice ! . context ) ) ) . toEqual ( { notice : true , compactionSummary : summary } )
416- } )
432+ } , SUGGESTION_TEST_TIMEOUT_MS )
417433
418434 it ( 'uses the same chat Thinking control for remote provider requests' , async ( ) => {
419435 const endpoint = `http://127.0.0.1:${ fake . port } /v1`
@@ -436,7 +452,7 @@ describe('production workspace bridge', () => {
436452 fireEvent . change ( composer , { target : { value : `Test ${ provider } with Thinking on` } } )
437453 await user . click ( screen . getByRole ( 'button' , { name : / ^ s e n d $ / i } ) )
438454 expect ( await inTranscript ( `${ provider } answered with Thinking on.` ) ) . toBeTruthy ( )
439- expect ( await screen . findByPlaceholderText ( 'Try with Thinking off (Tab)' ) ) . toBeTruthy ( )
455+ expect ( await screen . findByPlaceholderText ( 'Try with Thinking off (Tab)' , undefined , { timeout : SUGGESTION_WAIT_MS } ) ) . toBeTruthy ( )
440456 const onRequest = fake . requests [ 0 ]
441457 if ( provider === 'openrouter' ) {
442458 expect ( onRequest ?. reasoning ) . toEqual ( { max_tokens : 1024 } )
@@ -451,7 +467,7 @@ describe('production workspace bridge', () => {
451467 fireEvent . change ( composer , { target : { value : `Test ${ provider } with Thinking off` } } )
452468 await user . click ( screen . getByRole ( 'button' , { name : / ^ s e n d $ / i } ) )
453469 expect ( await inTranscript ( `${ provider } answered with Thinking off.` ) ) . toBeTruthy ( )
454- expect ( await screen . findByPlaceholderText ( 'Compare both answers (Tab)' ) ) . toBeTruthy ( )
470+ expect ( await screen . findByPlaceholderText ( 'Compare both answers (Tab)' , undefined , { timeout : SUGGESTION_WAIT_MS } ) ) . toBeTruthy ( )
455471 const offRequest = fake . requests [ 2 ]
456472 if ( provider === 'openrouter' ) {
457473 expect ( offRequest ?. reasoning ) . toEqual ( { effort : 'none' } )
@@ -470,7 +486,7 @@ describe('production workspace bridge', () => {
470486 } )
471487 await window . api . setLlmSettings ( { reasoningBudget : 0 } )
472488 }
473- } , 15_000 )
489+ } , SUGGESTION_TEST_TIMEOUT_MS )
474490
475491 it ( 'shows Gemini thinking and completes a signed reasoning tool round' , async ( ) => {
476492 const endpoint = `http://127.0.0.1:${ fake . port } /v1`
0 commit comments