Skip to content

Bug: Composer agent has no tool permissions — cannot read codebase to decompose issues #405

@randomm

Description

@randomm

What happened

When running taskctl start <issueNumber>, the Composer agent is spawned to decompose a GitHub issue into a task graph. The agent attempts to read source files for context but gets permission denied for all tools. It falls back to decomposing based solely on the issue text, producing poor output with no codebase awareness.

Observed error:

I am encountering permission issues reading the source files directly. Based on the issue description, I have enough context from the issue itself to decompose this properly.

The Composer then returns invalid JSON because it cannot understand the codebase structure.

Expected behaviour

The Composer agent should be able to:

  • Read source files to understand relevant code locations
  • Search the codebase to find related modules and files
  • Produce accurate task decompositions with correct file paths and module labels

Root cause

In packages/opencode/src/agent/agent.ts (lines 204-216), the composer agent is defined with:

"composer": {
  permission: PermissionNext.merge(
    defaults,
    PermissionNext.fromConfig({
      "*": "deny",  // Denies ALL tools
    }),
    user,
  ),
}

The "*": "deny" rule overrides all default allows via last-match-wins evaluation (evaluate() iterates backwards in the merged ruleset). Since there are no explicit tool allows for the composer, disabled() hides every tool from the agent.

This contrasts with the explore agent (lines 132-157) which correctly uses "*": "deny" paired with explicit allows:

"explore": {
  permission: PermissionNext.merge(
    defaults,
    PermissionNext.fromConfig({
      "*": "deny",
      grep: "allow", glob: "allow", list: "allow",
      bash: "allow", webfetch: "allow", websearch: "allow",
      codesearch: "allow", read: "allow",
    }),
    user,
  ),
}

Additionally, Session.createNext in composer.ts:36-41 passes permission: [] (empty array), which does not override or supplement the agent-level permissions — the runtime merge is PermissionNext.merge(taskAgent.permission, session.permission ?? []).

Why this matters

Without codebase access, the Composer cannot:

  • Identify which files need modification
  • Validate that module/file labels reference real paths
  • Detect existing utilities that could be reused
  • Produce dependency-accurate task graphs

It operates blind, producing generic decompositions that miss critical codebase-specific context.

Proposed solution

Add explicit read-only tool allows to the composer agent definition, following the explore agent pattern. The allowed tool set should be extracted into a named constant for easy adjustment:

// In agent.ts — reusable read-only toolset for codebase-aware agents
const READONLY_TOOLS = {
  "*": "deny",
  read: "allow",
  grep: "allow",
  glob: "allow",
  list: "allow",
  codesearch: "allow",
  bash: "allow",       // for gh, vipune, colgrep
  external_directory: { [Truncate.GLOB]: "allow" },
} satisfies Config.Permission

Both composer and steering agents (which currently have the same "*": "deny" with no allows) should use this or a similar toolset. This makes it trivial to adjust permissions for any codebase-aware read-only agent in one place.

Acceptance Criteria

  • Composer agent can read source files during taskctl start
  • Composer agent can search codebase (grep, glob, codesearch)
  • Composer agent can run read-only bash commands (gh, vipune, colgrep)
  • taskctl start produces valid JSON decomposition with real file/module references
  • Read-only toolset is extracted into a reusable constant — changing it in one place updates all read-only agents
  • Steering agent also gains read-only tools (currently has same bug — "*": "deny" with no allows)
  • Tests verify composer and steering agents have expected visible tools after the fix
  • No type errors, lint violations, or regressions in existing tests

Quality Gates

  • TDD: Write tests before implementation
  • 80%+ coverage for new code
  • All linting passes
  • Local verification: bun run typecheck && bun test pass

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions