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