File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -311,6 +311,47 @@ mod tests {
311311 assert_eq ! ( bad, vec![ "file_access" . to_string( ) , "shell" . to_string( ) ] ) ;
312312 }
313313
314+ #[ test]
315+ fn no_subagent_reachable_group_contains_dotted_tool_names ( ) {
316+ // Invariant: subagent toolsets never contain dotted tool names
317+ // (`subagents.run`, `harness.*`). If this ever flips, the Azure
318+ // sanitization in `render_for_openai_filtered` would start renaming
319+ // tools inside the subagent catalog and we'd need to add a matching
320+ // reverse-map in the subagent loop. Currently the bidirectional
321+ // mapping only exists in the coordinator path.
322+ let all_coordinator_controllable_strings = [
323+ "environment_read" ,
324+ "workspace_read" ,
325+ "diff_read" ,
326+ "git_read" ,
327+ "git_write" ,
328+ "shell_read" ,
329+ "shell_write" ,
330+ "web_read" ,
331+ "memory_read" ,
332+ "memory_write" ,
333+ "plans_read" ,
334+ "plans_write" ,
335+ "tasks_read" ,
336+ "tasks_write" ,
337+ "rules_skills_read" ,
338+ "rules_skills_write" ,
339+ ] ;
340+ for s in all_coordinator_controllable_strings {
341+ let g = ToolGroup :: parse ( s) . expect ( "known group" ) ;
342+ for tool in g. tool_names ( ) {
343+ assert ! (
344+ !tool. contains( '.' ) ,
345+ "group {s:?} exposes dotted tool {tool:?} — would conflict with Azure sanitizer"
346+ ) ;
347+ }
348+ }
349+ // SubagentSubmit is force-pushed onto every subagent's group list.
350+ for tool in ToolGroup :: SubagentSubmit . tool_names ( ) {
351+ assert ! ( !tool. contains( '.' ) , "SubagentSubmit tool {tool:?} must be dotless" ) ;
352+ }
353+ }
354+
314355 #[ test]
315356 fn parse_allowed_groups_strict_returns_empty_when_all_unknown ( ) {
316357 // The pathological case the subagents fix is built to catch: a
You can’t perform that action at this time.
0 commit comments