@@ -277,47 +277,6 @@ public async Task<GenerationResult> GenerateTestsAsync(
277277 // the actual error message. Generation proceeds immediately
278278 // because the SDK doesn't block session creation on MCP load.
279279
280- // Diagnostic snapshot: once MCP servers finish loading, ask the
281- // SDK for the merged tool list and write each tool name to the
282- // debug log. Lets us answer "did testimize tools actually reach
283- // the agent's tool list?" by looking at .spectra-debug.log
284- // instead of guessing from tool-call traces. Best-effort only —
285- // failures here must not block generation.
286- if ( mcpServers is not null )
287- {
288- try
289- {
290- var loaded = await Task . WhenAny (
291- mcpServersLoadedTcs . Task ,
292- Task . Delay ( TimeSpan . FromSeconds ( 10 ) , ct ) ) ;
293- var loadedInTime = loaded == mcpServersLoadedTcs . Task ;
294- DebugLog ( $ "TOOL LIST waiting_for_mcp_loaded={ loadedInTime } ") ;
295-
296- var modelName = ProviderMapping . GetModelName ( _provider ) ;
297- var toolList = await service . ListSessionToolsAsync ( modelName , ct ) ;
298- var toolCount = toolList ? . Tools ? . Count ?? 0 ;
299- DebugLog ( $ "TOOL LIST count={ toolCount } model={ modelName } ") ;
300- if ( toolList ? . Tools is { } toolItems )
301- {
302- var testimizeHits = 0 ;
303- foreach ( var tool in toolItems )
304- {
305- var displayName = ! string . IsNullOrEmpty ( tool . NamespacedName )
306- ? tool . NamespacedName
307- : tool . Name ?? "?" ;
308- DebugLog ( $ "TOOL LIST item={ displayName } ") ;
309- if ( displayName . IndexOf ( "testimize" , StringComparison . OrdinalIgnoreCase ) >= 0 )
310- testimizeHits ++ ;
311- }
312- DebugLog ( $ "TOOL LIST testimize_tools_visible={ testimizeHits } ") ;
313- }
314- }
315- catch ( Exception ex )
316- {
317- DebugLog ( $ "TOOL LIST error={ ex . GetType ( ) . Name } : { ex . Message } ") ;
318- }
319- }
320-
321280 // Build the combined prompt with system instructions and user request.
322281 // The profile format (JSON schema sent to the AI) is resolved from
323282 // profiles/_default.yaml on disk if present, else the embedded default.
@@ -342,6 +301,25 @@ public async Task<GenerationResult> GenerateTestsAsync(
342301 // Slower / reasoning models may need 10–20+ minutes per batch.
343302 var timeoutMinutes = Math . Max ( 1 , _config . Ai . GenerationTimeoutMinutes ) ;
344303 var batchTimeout = TimeSpan . FromMinutes ( timeoutMinutes ) ;
304+ // v1.48.2: gate the prompt send on MCP servers being fully loaded.
305+ // Without this, the SDK builds the model's tool catalog at session
306+ // start before testimize finishes its initialize handshake (~600ms
307+ // typical), so testimize tools are absent from the catalog the
308+ // model sees. The .spectra-debug.log under 1.48.1 showed BATCH
309+ // START firing ~600–700ms before TESTIMIZE LOADED on every batch.
310+ // Best-effort with a 30s ceiling — if loading is slow, we still
311+ // send the prompt rather than blocking generation indefinitely.
312+ if ( mcpServers is not null )
313+ {
314+ var gateSw = System . Diagnostics . Stopwatch . StartNew ( ) ;
315+ var winner = await Task . WhenAny (
316+ mcpServersLoadedTcs . Task ,
317+ Task . Delay ( TimeSpan . FromSeconds ( 30 ) , ct ) ) ;
318+ gateSw . Stop ( ) ;
319+ var result = winner == mcpServersLoadedTcs . Task ? "loaded" : "timeout" ;
320+ DebugLog ( $ "GATE mcp_loaded waited={ gateSw . Elapsed . TotalSeconds : F2} s result={ result } ") ;
321+ }
322+
345323 var sw = System . Diagnostics . Stopwatch . StartNew ( ) ;
346324 DebugLogAi ( $ "BATCH START requested={ requestedCount } timeout={ timeoutMinutes } min", null , null , estimated : false ) ;
347325 _onStatus ? . Invoke ( $ "Starting AI generation ({ requestedCount } tests, timeout { timeoutMinutes } min)...") ;
0 commit comments