@@ -178,8 +178,58 @@ describe("shouldAbortForStall — execution-watchdog-exempt tools do not pin for
178178 ) . toBe ( true ) ;
179179 } ) ;
180180
181- // A sibling tool.done clears the last announced name while the poll is in
181+ // A sibling tool.done clears the last announced name while the poll is in
182182 // flight; keying only that name would leave it unbounded forever.
183+ // ask_director is bounded, not exempt (CL-10206): the primary never mounts
184+ // it, so the name is a guard, and a stray mount must notice, then abort,
185+ // like wait_agents. Both shapes the turn state can hold are pinned: the
186+ // announced call, and a leftover tracked only per-id after a sibling
187+ // cleared currentToolName and the shared callIdByName slot.
188+ test . each ( [
189+ {
190+ representation : "direct" ,
191+ currentToolName : "ask_director" ,
192+ activeToolCalls : [ "ask-1" ] ,
193+ callIdByName : { ask_director : "ask-1" } ,
194+ callNameById : { "ask-1" : "ask_director" } ,
195+ } ,
196+ {
197+ representation : "mapping-only" ,
198+ currentToolName : null ,
199+ activeToolCalls : [ "ask-1" ] ,
200+ callIdByName : { } ,
201+ callNameById : { "ask-1" : "ask_director" } ,
202+ } ,
203+ ] ) (
204+ "in-flight ask_director ($representation) is quiet, then notices, then aborts, and every surface agrees" ,
205+ ( ask ) => {
206+ const inFlightAsk = {
207+ ...collect ,
208+ ...ask ,
209+ streamingType : "tool" as const ,
210+ stallNoticeMs : STALL_NOTICE_MS ,
211+ repeating : false ,
212+ } ;
213+ const points = [
214+ { nowMs : STALL_NOTICE_MS - 1 , level : "quiet" } ,
215+ { nowMs : STALL_NOTICE_MS , level : "notice" } ,
216+ { nowMs : STALL_TIMEOUT_MS - 1 , level : "notice" } ,
217+ { nowMs : STALL_TIMEOUT_MS , level : "abort" } ,
218+ ] as const ;
219+
220+ for ( const { nowMs, level } of points ) {
221+ const args = { ...inFlightAsk , nowMs } ;
222+ expect ( stallLevel ( args ) ) . toBe ( level ) ;
223+ expect ( shouldAbortForStall ( args ) ) . toBe ( level === "abort" ) ;
224+ expect ( shouldNoticeStall ( args ) ) . toBe ( level === "notice" ) ;
225+ expect ( isStalledForDisplay ( args ) ) . toBe ( level !== "quiet" ) ;
226+ }
227+ } ,
228+ ) ;
229+
230+ // tool.done of a sibling bash clears currentToolName and streamingType
231+ // while wait_agents is still in activeToolCalls. Keying only the last
232+ // name would leave that poll unbounded forever.
183233 test ( "sibling tool.done while collect is in-flight still aborts at the stall budget" , ( ) => {
184234 const afterSiblingDone = {
185235 ...collect ,
0 commit comments