Skip to content

Tool distribution across process types creates dead-end delegation chains #453

@jamiepine

Description

@jamiepine

Problem

The current tool assignment matrix creates dead-end delegation chains — a process gets delegated work but lacks the tools to complete it, so it delegates again, and the next process has even less context. Basic multi-step workflows that should complete in one hop require three or more hops, with context lost at each handoff.

Concrete failure scenario

  1. User asks Spacebot to sync open GitHub PRs with the internal taskboard
  2. Channel spawns a worker to gather PR data (correct — workers have shell/file tools)
  3. Worker runs gh CLI, gathers data, writes a comprehensive analysis file to its workspace, reports summary back to channel
  4. User confirms: "yes, create the tasks"
  5. Channel can't create tasks (no task tools) → branches to think about it
  6. Branch has task_create but no file tools → can't read the analysis file the worker wrote → spawns another worker to read the file
  7. New worker has file tools, reads the analysis file, but has no task_create → has no idea about the internal taskboard → starts calling gh CLI looking for a GitHub Projects board
  8. Complete failure. Three hops, context lost at every handoff.

The worker that gathered the data should have been able to create tasks directly. Alternatively, the branch should have been able to read the file. Either path would have completed the workflow.

Root cause

Two critical gaps in the tool assignment matrix:

Gap 1: Workers cannot create or list tasks

Workers are the processes that do actual work — gathering data, running commands, producing artifacts. But they have no way to register outcomes on the internal taskboard.

The only task tool workers get is TaskUpdateTool::for_worker(), which is restricted to subtask/metadata updates on the worker's own assigned task (src/tools/task_update.rs:149-181). Workers cannot:

  • Create new tasks (task_create)
  • List existing tasks (task_list)
  • Update the status/title/description of any task (including tasks they created)

Current worker tool registration (src/tools.rs:571-620):

ShellTool, TaskUpdateTool::for_worker (restricted), SetStatusTool, ReadSkillTool,
FileReadTool, FileWriteTool, FileEditTool, FileListTool,
SecretSetTool (conditional), Browser tools (conditional), WebSearchTool (conditional),
MCP tools (conditional)

Gap 2: Branches cannot read files

Branches are the thinking layer — they have the channel's full context, recall memories, make decisions, and create tasks. But if a worker produced results as files (which is what workers naturally do), a branch is completely blind to them. It must spawn yet another worker just to read a file, and that worker lacks the branch's context.

Current branch tool registration (src/tools.rs:514-560):

MemorySaveTool, MemoryRecallTool, MemoryDeleteTool, ChannelRecallTool,
SpacebotDocsTool, EmailSearchTool, WorkerInspectTool,
TaskCreateTool, TaskListTool, TaskUpdateTool::for_branch,
MemoryPersistenceCompleteTool (conditional), SpawnWorkerTool (conditional)

No file tools at all.

Proposed changes

1. Give workers task_create and task_list (high priority)

In create_worker_tool_server() (src/tools.rs:571), add:

  • TaskCreateTool::new(task_store, agent_id, "worker")
  • TaskListTool::new(task_store, agent_id)

This requires threading task_store and agent_id through from the existing params (both are already available — task_store is passed for TaskUpdateTool::for_worker() at line 587, and agent_id is passed at line 572).

The worker system prompt should mention the internal taskboard and these tools so the LLM knows to use them instead of looking for external project management.

Consider also: upgrading TaskUpdateTool::for_worker() to allow status changes on tasks the worker created (not just its assigned task). Currently the guard at src/tools/task_update.rs:149-181 only allows the worker to update task_store.get_by_worker_id(), and restricts updates to subtasks/metadata. A worker that creates 10 tasks from gathered data should be able to update their status too.

2. Give branches read-only file tools (high priority)

In create_branch_tool_server() (src/tools.rs:514), add:

  • FileReadTool
  • FileListTool

Not FileWriteTool or FileEditTool — branches think, they don't execute. Read-only access is the correct boundary.

This requires adding workspace: PathBuf and sandbox: Arc<Sandbox> to the function signature. The call site in channel_dispatch.rs:257 has access to both via state.deps.runtime_config.workspace_dir and state.deps.sandbox.

3. Consider giving workers memory_save (medium priority)

Workers gather information and produce results, but can't persist memories. A worker doing research writes to files that may never be read. If it could save key findings as memories, those findings would be available to future branches via memory_recall.

The compactor worker already gets memory_save via create_cortex_tool_server() (src/tools.rs:626-638). Extending this to task workers is the same pattern — add a MemorySaveTool with the memory event bus.

This is a secondary priority and could be a separate PR.

Affected code locations

File Lines What to change
src/tools.rs 571-620 create_worker_tool_server() — add TaskCreateTool, TaskListTool
src/tools.rs 514-560 create_branch_tool_server() — add FileReadTool, FileListTool, expand signature
src/tools.rs 1-30 Module doc comment — update topology docs
src/agent/channel_dispatch.rs 257-268 Call site — pass workspace and sandbox to branch tool server
src/tools/task_update.rs 149-181 Worker scope guard — optionally allow status updates on worker-created tasks
src/tools/task_create.rs 1 Doc comment says "for branch processes" — update
Worker system prompt (prompts/) Mention internal taskboard tools
AGENTS.md Module Map, tools list Update tool assignments per process type
README.md Process table Update "What Each Process Gets" table

What NOT to change

  • Channels stay delegation-only. No task tools, no file tools, no memory tools. The ambassador model is correct.
  • Workers should NOT get memory_recall or channel context. Fresh prompt + task description is the right model.
  • Branches should NOT get file_write/file_edit/shell. They think, they don't execute.

Full current tool matrix (for reference)

Tool Channel Branch Worker Compactor Cortex Chat
reply Yes* - - - -
branch Yes - - - -
spawn_worker Yes Yes** - - Yes
route Yes - - - -
cancel Yes - - - -
skip Yes - - - -
react Yes - - - -
memory_save - Yes - Yes Yes
memory_recall - Yes - - Yes
memory_delete - Yes - - Yes
channel_recall - Yes - - Yes
spacebot_docs - Yes - - Yes
task_create - Yes MISSING - Yes
task_list - Yes MISSING - Yes
task_update - Yes (full) Yes (restricted) - Yes (full)
file_read - MISSING Yes - Yes
file_list - MISSING Yes - Yes
file_write - - Yes - Yes
file_edit - - Yes - Yes
shell - - Yes - Yes
set_status - - Yes - -

*Conditional on config/state — see add_channel_tools() in src/tools.rs:348-445 for details.
**Branch spawn_worker only available for channel-originated branches.

Related

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