{ "_meta": { "description": "D6 fixtures for spring-ai / tool-rendering-custom-catchall", "sourceScript": "d5-tool-rendering-custom-catchall.ts", "created": "2026-05-22" }, "fixtures": [ { "_comment": "custom-catchall turn 1 follow-up \u2014 after get_weather tool result. Must come before the tool-emitting fixture. Keyed on the actual probe prompt 'weather in Tokyo' (d5-tool-rendering-custom-catchall.ts PROMPT_TOOL_PAIRS[0]); the old key 'check Tokyo weather forecast' never matched anything the probe sends.", "match": { "userMessage": "Forecast Tokyo through the wildcard renderer", "toolCallId": "call_d5_get_weather_001", "context": "spring-ai" }, "response": { "content": "Tokyo is 22\u00b0C and partly cloudy \u2014 rendered through the custom wildcard catchall." } }, { "_comment": "custom-catchall turn 1 \u2014 emit get_weather tool call. The custom wildcard renderer fires for any tool. toolName-gated so agents without get_weather still fall through to tool-rendering.json's content-only fallback. No hasToolResult gate (LGP-gold parity): the toolCallId-gated follow-up above matches the post-tool leg first via first-match-wins ordering.", "match": { "userMessage": "Forecast Tokyo through the wildcard renderer", "toolName": "get_weather", "context": "spring-ai" }, "response": { "toolCalls": [ { "id": "call_d5_get_weather_001", "name": "get_weather", "arguments": "{\"location\":\"Tokyo\"}" } ] } }, { "_comment": "custom-catchall turn 2 follow-up \u2014 after get_stock_price tool result. Must come before the tool-emitting fixture.", "match": { "userMessage": "Quote AAPL through the wildcard renderer", "toolCallId": "call_d5_get_stock_price_cc_001", "context": "spring-ai" }, "response": { "content": "AAPL is trading at $338.37, down 2.96% on the day \u2014 rendered through the custom wildcard catchall." } }, { "_comment": "custom-catchall turn 2 \u2014 emit get_stock_price tool call. Both turns render through the same custom-wildcard-card testid with different data-tool-name values. The old hasToolResult:false gate could never match turn 2: turn 1's get_weather tool result is still in the thread (hasToolResult is a whole-thread check), so the request fell through to no-match -> backend RuntimeException -> RUN_ERROR. Deliberately NO turnIndex/hasToolResult gate: spring-ai's MessageListFilter reshapes prior-turn messages, making both counts unreliable (a turnIndex:2 gate also failed live). Loop safety comes from ordering \u2014 the toolCallId-gated follow-up above matches the post-tool leg first. This file loads before tool-rendering.json, so this also absorbs that demo's turnIndex:0 AAPL pill leg; that is benign-equivalent (same get_stock_price name, same ticker/price_usd/change_pct argument shape, near-identical follow-up content).", "match": { "userMessage": "Quote AAPL through the wildcard renderer", "toolName": "get_stock_price", "context": "spring-ai" }, "response": { "toolCalls": [ { "id": "call_d5_get_stock_price_cc_001", "name": "get_stock_price", "arguments": "{\"ticker\":\"AAPL\",\"price_usd\":338.37,\"change_pct\":-2.96}" } ] } } ] }