Skip to content

Bug: squad_route SDK tool returns false success without spawning a session #1065

Closed

Description

@jagilber

Bug: squad_route SDK tool returns false success without spawning a session

Summary

The squad_route tool exported by @bradygaster/squad-sdk is documented as routing tasks and creating new sessions for a target agent, but the current implementation never actually spawns anything. It returns resultType: 'success' with a confident "Task routed to X" payload, so SDK embedders following the documented usage receive a silent no-op masquerading as success.

Where it is documented as working

  • packages/squad-sdk/README.md (lines ~83–95):

    squad_route — Hand off work between agents

    const tool = toolRegistry.getTool('squad_route');
    await tool.handler({ targetAgent: 'McManus', task: '...', priority: 'high' });

    "The lead routes a task to DevRel. A new session is created, context is passed, and the task is queued with priority. No human in the loop."

  • docs/src/content/docs/reference/sdk.md — Built-in tools table: squad_route — "Route a task to another agent".

  • docs/src/content/docs/reference/api-reference.md — Built-in tools table: same.

  • docs/src/content/docs/guide/build-autonomous-agent.md — Listed alongside squad_decide / squad_memory (which work fully) as "Routes a follow-up task to a teammate".

None of these surfaces disclose that squad_route is an unimplemented stub.

What the implementation actually does

packages/squad-sdk/src/tools/index.ts (current dev):

handler: async (args) => {
  // Validate target agent exists (stub for now, will check roster later)
  if (!args.targetAgent || args.targetAgent.trim() === '') { /* ... */ }

  // Create route request (session creation wired later)
  const routeRequest: RouteRequest = { /* ... */ };

  return {
    textResultForLlm: `Task routed to ${args.targetAgent}. Priority: ${routeRequest.priority}. Session creation will be implemented when session lifecycle is in place.`,
    resultType: 'success',
    toolTelemetry: { routeRequest },
  };
},

The two "stub for now" / "wired later" notes are inline source comments only. The "will be implemented" caveat is in textResultForLlm (visible to the calling agent at runtime), not in any user-facing doc that an embedder would read before integrating.

Impact

  • Severity: silent data-integrity bug for SDK embedders. resultType: 'success' is a contract claim. Embedders that branch on success vs. failure will record successful routing telemetry, advance workflow state, or report completion to users while no spawn occurred.
  • Who is affected: any consumer of @bradygaster/squad-sdk who calls toolRegistry.getTool('squad_route').handler(...) as documented, or exposes squad_route to an LLM as part of a custom session built on the SDK.
  • Who is not affected: the squad CLI itself. The CLI's coordinator path delegates via the Copilot CLI task tool / Coordinator.route() → spawnParallel directly (packages/squad-sdk/src/coordinator/coordinator.ts:168), bypassing the tool registry entirely. CLI-level concurrency/fan-out is unrelated to this bug.

Repro

import { ToolRegistry } from '@bradygaster/squad-sdk/tools';

const registry = new ToolRegistry('./.squad');
const route = registry.getTool('squad_route')!;
const result = await route.handler({
  targetAgent: 'McManus',
  task: 'Write a blog post on the new casting system',
  priority: 'high',
});

console.log(result.resultType); // 'success'
// No session was created. No agent was spawned. No event was emitted.

Expected

One of:

  1. The handler actually spawns the target agent (matching documentation), or
  2. The handler returns a structured failure so embedders can detect the unimplemented state, or
  3. Documentation is updated to mark the tool as a non-functional stub / preview.

(1) is preferable — the rest of the documented surface around squad_decide, squad_memory, squad_status, squad_skill is implemented; squad_route is the odd one out.

Proposed fix

Wire the handler to spawnParallel (the same primitive Coordinator.route() already uses) via an optional fanOutDepsGetter constructor argument on ToolRegistry:

  • When fanOutDepsGetter returns dependencies, squad_route calls spawnParallel with a single config (charter compile → model resolve → createSession → initial message).
  • When absent or undefined, the handler returns resultType: 'failure' with error: 'fan-out-deps-unavailable' and remediation guidance instead of fake-success.
  • The new constructor parameter is the 5th positional and optional, so existing 4-arg constructions remain compatible.

A behavior change: embedders that today rely on the stub's unconditional success would start seeing honest failures unless they wire fanOutDepsGetter or intercept squad_route via SquadSessionHooks.onPreToolUse. That is the intended fix — false success → honest failure or real spawn.

Related

Activity

  1. added 3 commits that reference this issue on May 2, 2026
    9c9ac6e
    364ed18
    e0898ad
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions