Skip to content

PRD: Per-project SWE Manager session (delegation-only primary agent + worktree tools) #217

Closed

Description

@OpenCodeEngineer

Problem

Long-running coding tasks today fall into two failure modes:

  1. Single session sprawl — one agent does plan + edit + review in the same context window; context bloat degrades reasoning and hides parallelism.
  2. Manual fan-out — humans open N terminals, juggle worktrees, copy-paste prompts between them. The bottleneck is coordination, not the models.

opencode already has the primitives (primary agents, task tool, child sessions, parent/child IDs), but there is no first-class per-project supervisor that owns the project's mental model and dispatches work. Users have to assemble one from opencode.json, external orchestrators (CAO, oh-my-opencode), and ad-hoc worktree scripts.

Proposal

Ship a built-in primary agent — manager — that acts as the project's Software Engineering Manager. It owns no execution tools; it owns delegation, worktrees, and session bookkeeping.

Manager capabilities

Tool Purpose
task spawn subagents in child sessions (existing)
worktree_create / worktree_delete isolate each subagent on its own branch
session_list / session_attach enumerate and re-enter active child sessions
todo maintain the project punch list across turns
read, glob, grep read-only repo inspection for routing decisions

Explicitly denied: edit, write, bash, patch. The manager cannot mutate the repo directly — only via subagents.

Default subagent roster (overridable per project)

  • @planner — read-only, produces task breakdowns
  • @implementor — full edit/bash, scoped to one worktree
  • @reviewer — read-only, runs after @implementor returns
  • @test-author — write access restricted to test paths

Worktree contract

  • Each task invocation that needs to mutate code MUST run inside a worktree created by the manager.
  • Worktrees live at `~/.local/share/opencode/worktree///` (matches existing convention).
  • On subagent completion the manager decides: merge → PR, keep open for iteration, or discard.

Session hierarchy

  • Manager session is pinned per project (one per `projectID`), survives restarts.
  • All spawned child sessions inherit `parentID = manager.sessionID` and a name prefix `|`.
  • TUI: `+m` jumps to the project's manager session from anywhere.

Design decisions

  1. Manager is a primary agent, not a new agent class. Reuses existing `permission.task` / `permission.edit` machinery. No runtime changes required for the agent itself — only config + a few new tools.
  2. Worktree management lives in opencode core, not a plugin. Plugin-based worktree tooling (`kdcokenny/opencode-worktree`) proves the pattern but fragments UX. Bring it in-tree behind a `worktree` permission gate.
  3. No tmux dependency. Visualization (oh-my-opencode style pane-per-subagent) is a separate optional integration; the manager itself must work headless and over `opencode serve`.
  4. Bounded recursion. Manager-spawned subagents get `task: deny` by default to prevent the unbounded recursion seen in upstream Subagents can infinitely recurse via Task tool — no max depth limit anomalyco/opencode#18100. Opt-in per agent if a workflow genuinely needs two-level delegation.
  5. REST-safe. Manager flows must not regress on upstream Sessions hang indefinitely when Task tool spawns subagents via REST API (opencode serve) anomalyco/opencode#6573 (Task tool hangs over `opencode serve`). Add an integration test that spawns a child subagent via the HTTP API and asserts completion.

Out of scope

  • Cross-project orchestration (manager is per-project).
  • Linear/Jira integration — leave to user-defined `@pm` subagents.
  • Cloud-hosted supervisor / multi-user collaboration.
  • Replacement for the existing `build` / `plan` primary agents.

Open questions

  1. Does the manager auto-spawn on `opencode` startup in a project, or is it user-invoked (`opencode manager`)?
  2. Should the manager persist its TODO list to disk (`.opencode/manager-state.json`) for resumability across restarts?
  3. How does the manager surface a stuck/hung subagent? Heartbeats from the session-status reconciler (in flight on `fix/session-status-reconciler-v2`) are the natural signal.
  4. Permissions UX: should denying `edit`/`bash` on a primary agent be a single-flag preset (`role: "manager"`) instead of three separate denies?

Prior art

Known upstream blockers

Activity

  1. OpenCodeEngineer commented on May 20, 2026

    @OpenCodeEngineer
    Author

    Code audit (2026-05-20)

    Audited the proposal against the codebase before promoting to TDD. Most of the PRD is already supported by existing primitives — actual new work is small.

    What already exists

    PRD claim Already in code Citation
    worktree_create/delete tools Full Worktree.Service: create/list/remove/reset, exposed via control plane packages/opencode/src/worktree/index.ts:164-630; control-plane/adapters/worktree.ts:20-69
    Worktree path ~/.local/share/opencode/worktree/<project>/<branch>/ Exact match worktree/index.ts:231
    Per-agent permission presets Agent.Info.permission: Permission.Ruleset; Permission.merge(defaults, user, agent) composes them agent/agent.ts:33-56; config/permission.ts:24-45
    Worker session creation with parentID, agent, permission `POST /session` already accepts all of these server/routes/instance/session.ts:221-246
    Fire-and-forget dispatch `POST /session/:id/prompt_async` exists, returns 204 server/routes/instance/session.ts:908-948
    Status polling, child listing, wait-until-idle `GET /session/status`, `GET /session/:id/children`, `POST /api/session/:id/wait` session.ts:99-122,156-188; httpapi/groups/v2/session.ts:111-121
    Stop a worker `POST /session/:id/abort` session.ts:419-449
    Worker event stream SSE `GET /event` already publishes `session.status`, `session.idle`, `session.error`, `messageV2.part.delta`, `worktree.ready/failed`, permission/question events routes/instance/event.ts:12-90; bus/index.ts:14-19

    Corrections to the original PRD

    1. `parentID` is not "for attribution only." It triggers three side effects the PRD assumed away:

      • UI session list defaults to `roots: true` and filters children out (`session-load.ts:5`, `session.ts:842`).
      • Removing a session cascade-deletes its children (`session.ts:561-563`).
      • Default title is auto-prefixed `"Child session - "` (`session.ts:511`).
        "Top-level visible worker with a manager link" therefore requires decoupling at least two of these behaviors (visibility + cascade) — likely via an `isWorker: true` flag or per-session opt-outs.
    2. `@planner` / `@implementor` invocation syntax does not exist. No `@agent-name` parser anywhere in the codebase. Subagents are invoked only via the `task` tool with `subagent_type`. Either the manager LLM calls `task({subagent_type: "planner", ...})` explicitly, or `@` parsing becomes a separate feature ticket — out of scope here.

    3. `task` cannot be reused as `worker_spawn`. `task` is synchronous: yields on `ops.prompt()` (`tool/task.ts:138`) and joins the child's last text into `<task_result>` (`tool/task.ts:160-166`). A worker tool must return a session id immediately and never join output. The reusable internals are `Session.create()` + `POST /prompt_async`, not the `task` tool itself.

    4. No built-in `planner` / `implementor` / `reviewer` / `test-author` agents. Built-ins are `build`, `autopilot`, `plan`, `general`, `explore`, `scout`, `compaction`, `title`, `summary` (`agent/agent.ts:128-320`). The PRD roster ships either as new built-ins or as a documented `opencode.json` example.

    5. No worker→manager push channel. The bus is per-instance and `/event` SSE has no per-session filter. Cheapest add: a new bus event type (`worker.update` or similar) emitted by a `notify_manager` tool — manager picks it up from the existing SSE. ~50 LOC, no new endpoint.

    Re-scoped proposal — four real deltas

    1. `worker_*` tool family — `worker_spawn`, `worker_send`, `worker_stop`, `worker_status`, `worker_list`, `worker_diff`. Wrappers over existing REST and `Worktree.Service`. Pure plumbing.
    2. Decouple `parentID` side effects — add a per-session flag (e.g. `isWorker` or `detachedChild: true`) that opts out of root-list filtering and cascade deletion. Only schema/data-model change in scope.
    3. Manager agent + subagent roster as built-ins or docs example — config-only, no engine changes.
    4. Optional: `worker.update` bus event + `notify_manager` tool — small push channel for "done" / "needs attention" without changing the polling default.

    Recommendation on next doc

    Skip the TDD. Remaining design surface is small enough that a TDD would be more text than implementation. Right move: amend this PRD with the above, then open four follow-up issues — one per delta. Each delta is a single-PR-sized piece.

  2. dzianisv commented on Jun 22, 2026

    @dzianisv
    Owner

    All 4 deltas implemented:

    Manager agent can now spawn workers in worktrees, delegate to planner/implementor/reviewer/test-author subagents, and workers have proper lifecycle (visible in session list, survive parent deletion).

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

    enhancementNew feature or requestmetaProject management and coordinationpriority:mediumMedium priority

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions