diff --git a/docs/design-cli-redesign-analysis.html b/docs/design-cli-redesign-analysis.html
index 84b1e5c33..745cfec89 100644
--- a/docs/design-cli-redesign-analysis.html
+++ b/docs/design-cli-redesign-analysis.html
@@ -431,7 +431,7 @@ ao start
Returns a comprehensive annotated YAML schema covering every config field:
ports, defaults (runtime, agent, workspace, notifiers), project settings (repo, path, branch,
- agentConfig, agentRules, workspace symlinks/postCreate, tracker, SCM, decomposer),
+ agentConfig, agentRules, workspace symlinks/postCreate, tracker, SCM),
notification channels, and notification routing. Used by ao config-help.
diff --git a/openclaw-plugin/index.ts b/openclaw-plugin/index.ts
index 9bd0f14eb..80a7f8b23 100644
--- a/openclaw-plugin/index.ts
+++ b/openclaw-plugin/index.ts
@@ -943,15 +943,11 @@ export default function (api: PluginApi) {
type: "string",
description: "Immediately claim an existing PR number for the session",
},
- decompose: {
- type: "boolean",
- description: "Decompose issue into subtasks before spawning",
- },
},
},
async execute(
_toolCallId: string,
- params: { issue?: string; agent?: string; claimPr?: string; decompose?: boolean },
+ params: { issue?: string; agent?: string; claimPr?: string },
) {
const args = ["spawn"];
if (params.issue) {
@@ -963,7 +959,6 @@ export default function (api: PluginApi) {
}
if (params.agent) args.push("--agent", sanitizeCliArg(params.agent));
if (params.claimPr) args.push("--claim-pr", sanitizeCliArg(params.claimPr));
- if (params.decompose) args.push("--decompose");
const result = await spawnWithRetry(config, args);
if (!result.ok) {
return {
diff --git a/packages/cli/src/commands/spawn.ts b/packages/cli/src/commands/spawn.ts
index 1e0b6fb24..81395de4c 100644
--- a/packages/cli/src/commands/spawn.ts
+++ b/packages/cli/src/commands/spawn.ts
@@ -4,14 +4,8 @@ import type { Command } from "commander";
import { resolve } from "node:path";
import {
loadConfig,
- decompose,
- getLeaves,
- getSiblings,
- formatPlanTree,
TERMINAL_STATUSES,
type OrchestratorConfig,
- type DecomposerConfig,
- DEFAULT_DECOMPOSER_CONFIG,
} from "@aoagents/ao-core";
import { DEFAULT_PORT } from "../lib/constants.js";
import { exec } from "../lib/shell.js";
@@ -168,8 +162,6 @@ export function registerSpawn(program: Command): void {
.option("--agent ", "Override the agent plugin (e.g. codex, claude-code)")
.option("--claim-pr ", "Immediately claim an existing PR for the spawned session")
.option("--assign-on-github", "Assign the claimed PR to the authenticated GitHub user")
- .option("--decompose", "Decompose issue into subtasks before spawning")
- .option("--max-depth ", "Max decomposition depth (default: 3)")
.action(
async (
first: string | undefined,
@@ -179,8 +171,6 @@ export function registerSpawn(program: Command): void {
agent?: string;
claimPr?: string;
assignOnGithub?: boolean;
- decompose?: boolean;
- maxDepth?: string;
},
) => {
// Catch old two-arg usage: ao spawn
@@ -232,59 +222,7 @@ export function registerSpawn(program: Command): void {
await runSpawnPreflight(config, projectId, claimOptions);
await ensureLifecycleWorker(config, projectId);
- if (opts.decompose && issueId) {
- // Decompose the issue before spawning
- const project = config.projects[projectId];
- const decompConfig: DecomposerConfig = {
- ...DEFAULT_DECOMPOSER_CONFIG,
- ...(project.decomposer ?? {}),
- maxDepth: opts.maxDepth
- ? parseInt(opts.maxDepth, 10)
- : (project.decomposer?.maxDepth ?? 3),
- };
-
- const spinner = ora("Decomposing task...").start();
- const issueTitle = issueId;
-
- const plan = await decompose(issueTitle, decompConfig);
- const leaves = getLeaves(plan.tree);
- spinner.succeed(`Decomposed into ${chalk.bold(String(leaves.length))} subtasks`);
-
- console.log();
- console.log(chalk.dim(formatPlanTree(plan.tree)));
- console.log();
-
- if (leaves.length <= 1) {
- console.log(chalk.yellow("Task is atomic — spawning directly."));
- await spawnSession(config, projectId, issueId, opts.open, opts.agent, claimOptions);
- } else {
- // Create child issues and spawn sessions with lineage context
- const sm = await getSessionManager(config);
- console.log(chalk.bold(`Spawning ${leaves.length} sessions with lineage context...`));
- console.log();
-
- for (const leaf of leaves) {
- const siblings = getSiblings(plan.tree, leaf.id);
- try {
- const session = await sm.spawn({
- projectId,
- issueId, // All work on the same parent issue for now
- lineage: leaf.lineage,
- siblings,
- agent: opts.agent,
- });
- console.log(` ${chalk.green("✓")} ${session.id} — ${leaf.description}`);
- } catch (err) {
- console.error(
- ` ${chalk.red("✗")} ${leaf.description} — ${err instanceof Error ? err.message : err}`,
- );
- }
- await new Promise((r) => setTimeout(r, 500));
- }
- }
- } else {
- await spawnSession(config, projectId, issueId, opts.open, opts.agent, claimOptions);
- }
+ await spawnSession(config, projectId, issueId, opts.open, opts.agent, claimOptions);
} catch (err) {
console.error(chalk.red(`✗ ${err instanceof Error ? err.message : String(err)}`));
process.exit(1);
diff --git a/packages/cli/src/lib/config-instruction.ts b/packages/cli/src/lib/config-instruction.ts
index 1ad8b3493..85063247a 100644
--- a/packages/cli/src/lib/config-instruction.ts
+++ b/packages/cli/src/lib/config-instruction.ts
@@ -95,13 +95,6 @@ projects:
scm:
plugin: github # github | gitlab
- # ── Task decomposition (optional) ─────────────────────────────
- decomposer:
- enabled: false # Auto-decompose backlog issues
- maxDepth: 3 # Max recursion depth
- model: claude-sonnet-4-20250514
- requireApproval: true # Require human approval before executing
-
# ── Per-project reaction overrides (optional) ─────────────────
# reactions:
# ci-failed:
diff --git a/packages/core/package.json b/packages/core/package.json
index c32b6e0e8..9c1c99bcc 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -47,7 +47,6 @@
"clean": "rm -rf dist"
},
"dependencies": {
- "@anthropic-ai/sdk": "^0.52.0",
"yaml": "^2.7.0",
"zod": "^3.24.0"
},
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index d7c9f1db7..41bf358b6 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -163,20 +163,6 @@ const RoleAgentConfigSchema = z
})
.optional();
-const DecomposerConfigSchema = z
- .object({
- enabled: z.boolean().default(false),
- maxDepth: z.number().min(1).max(5).default(3),
- model: z.string().default("claude-sonnet-4-20250514"),
- requireApproval: z.boolean().default(true),
- })
- .default({
- enabled: false,
- maxDepth: 3,
- model: "claude-sonnet-4-20250514",
- requireApproval: true,
- });
-
const ProjectConfigSchema = z.object({
name: z.string().optional(),
repo: z.string(),
@@ -204,7 +190,6 @@ const ProjectConfigSchema = z.object({
.enum(["reuse", "delete", "ignore", "delete-new", "ignore-new", "kill-previous"])
.optional(),
opencodeIssueSessionStrategy: z.enum(["reuse", "delete", "ignore"]).optional(),
- decomposer: DecomposerConfigSchema.optional(),
});
const DefaultPluginsSchema = z.object({
diff --git a/packages/core/src/decomposer.ts b/packages/core/src/decomposer.ts
deleted file mode 100644
index 4868238a8..000000000
--- a/packages/core/src/decomposer.ts
+++ /dev/null
@@ -1,276 +0,0 @@
-/**
- * Task Decomposer — LLM-driven recursive task decomposition.
- *
- * Classifies issues as atomic (one agent can handle it) or composite
- * (needs to be broken into subtasks). Composite tasks are recursively
- * decomposed until all leaves are atomic.
- *
- * Integration: sits upstream of SessionManager.spawn(). When enabled,
- * complex issues are decomposed into child issues before agents are spawned.
- */
-
-import Anthropic from "@anthropic-ai/sdk";
-
-// =============================================================================
-// TYPES
-// =============================================================================
-
-export type TaskKind = "atomic" | "composite";
-export type TaskStatus = "pending" | "decomposing" | "ready" | "running" | "done" | "failed";
-
-export interface TaskNode {
- id: string; // hierarchical: "1", "1.2", "1.2.3"
- depth: number;
- description: string;
- kind?: TaskKind;
- status: TaskStatus;
- lineage: string[]; // ancestor descriptions root→parent
- children: TaskNode[];
- result?: string;
- issueId?: string; // tracker issue created for this subtask
- sessionId?: string; // AO session working on this task
-}
-
-export interface DecompositionPlan {
- id: string;
- rootTask: string;
- tree: TaskNode;
- maxDepth: number;
- phase: "decomposing" | "review" | "approved" | "executing" | "done" | "failed";
- createdAt: string;
- approvedAt?: string;
- parentIssueId?: string;
-}
-
-export interface DecomposerConfig {
- /** Enable auto-decomposition for backlog issues (default: false) */
- enabled: boolean;
- /** Max recursion depth (default: 3) */
- maxDepth: number;
- /** Model to use for decomposition (default: claude-sonnet-4-20250514) */
- model: string;
- /** Require human approval before executing decomposed plans (default: true) */
- requireApproval: boolean;
-}
-
-export const DEFAULT_DECOMPOSER_CONFIG: DecomposerConfig = {
- enabled: false,
- maxDepth: 3,
- model: "claude-sonnet-4-20250514",
- requireApproval: true,
-};
-
-// =============================================================================
-// LINEAGE CONTEXT
-// =============================================================================
-
-/** Format the task lineage as an indented hierarchy for LLM context. */
-export function formatLineage(lineage: string[], current: string): string {
- const parts = lineage.map((desc, i) => `${" ".repeat(i)}${i}. ${desc}`);
- parts.push(`${" ".repeat(lineage.length)}${lineage.length}. ${current} <-- (this task)`);
- return parts.join("\n");
-}
-
-/** Format sibling tasks for awareness context. */
-export function formatSiblings(siblings: string[], current: string): string {
- if (siblings.length === 0) return "";
- const lines = siblings.map((s) => (s === current ? ` - ${s} <-- (you)` : ` - ${s}`));
- return `Sibling tasks being worked on in parallel:\n${lines.join("\n")}`;
-}
-
-// =============================================================================
-// LLM CALLS
-// =============================================================================
-
-const CLASSIFY_SYSTEM = `You decide whether a software task is "atomic" or "composite".
-
-- "atomic" = a developer can implement this directly without needing to plan further. It may involve multiple steps, but they're all part of one coherent unit of work.
-- "composite" = this clearly contains 2+ independent concerns that should be worked on separately (e.g., backend + frontend, or auth + database + UI).
-
-Decision heuristics:
-- If the task names a single feature, endpoint, component, or module: atomic.
-- If the task bundles unrelated concerns (e.g., "build auth and set up CI"): composite.
-- If you're at depth 2 or deeper in the hierarchy, it is almost certainly atomic — only mark composite if you can name 2+ truly independent deliverables.
-- When in doubt, choose atomic. Over-decomposition creates more overhead than under-decomposition.
-
-Respond with ONLY the word "atomic" or "composite". Nothing else.`;
-
-const DECOMPOSE_SYSTEM = `You are a pragmatic task decomposition engine for software projects.
-
-Given a composite task, break it into the MINIMUM number of subtasks needed:
-- A simple task might only need 2 subtasks.
-- A complex task might need up to 7, but only if each is truly distinct.
-- Do NOT pad with extra subtasks. Do NOT create "test and polish" or "define requirements" subtasks.
-- Do NOT create subtasks that overlap or restate each other.
-- Each subtask should represent real, distinct work.
-
-Think about how an experienced developer would actually split this work.
-
-Respond with a JSON array of strings, each being a subtask description. Example:
-["Implement Stripe webhook handler", "Build subscription management UI"]
-
-Nothing else — just the JSON array.`;
-
-async function classifyTask(
- client: Anthropic,
- model: string,
- task: string,
- lineage: string[],
-): Promise {
- const context = formatLineage(lineage, task);
- const res = await client.messages.create({
- model,
- max_tokens: 10,
- system: CLASSIFY_SYSTEM,
- messages: [{ role: "user", content: `Task hierarchy:\n${context}` }],
- });
-
- const text = res.content[0].type === "text" ? res.content[0].text.trim().toLowerCase() : "";
- return text === "composite" ? "composite" : "atomic";
-}
-
-async function decomposeTask(
- client: Anthropic,
- model: string,
- task: string,
- lineage: string[],
-): Promise {
- const context = formatLineage(lineage, task);
- const res = await client.messages.create({
- model,
- max_tokens: 1024,
- system: DECOMPOSE_SYSTEM,
- messages: [{ role: "user", content: `Task hierarchy:\n${context}` }],
- });
-
- const text = res.content[0].type === "text" ? res.content[0].text.trim() : "[]";
- const jsonMatch = text.match(/\[[\s\S]*\]/);
- if (!jsonMatch) {
- throw new Error(`Decomposition failed — no JSON array in response: ${text}`);
- }
-
- const subtasks = JSON.parse(jsonMatch[0]) as string[];
- if (!Array.isArray(subtasks) || subtasks.length < 2) {
- throw new Error(`Decomposition produced ${subtasks.length} subtasks — need at least 2`);
- }
-
- return subtasks;
-}
-
-// =============================================================================
-// TREE OPERATIONS
-// =============================================================================
-
-function createTaskNode(
- id: string,
- description: string,
- depth: number,
- lineage: string[],
-): TaskNode {
- return { id, depth, description, status: "pending", lineage, children: [] };
-}
-
-/** Recursively decompose a task tree (planning phase — no execution). */
-async function planTree(
- client: Anthropic,
- model: string,
- task: TaskNode,
- maxDepth: number,
-): Promise {
- const kind = task.depth >= maxDepth ? "atomic" : await classifyTask(client, model, task.description, task.lineage);
-
- task.kind = kind;
-
- if (kind === "atomic") {
- task.status = "ready";
- return task;
- }
-
- task.status = "decomposing";
- const subtaskDescriptions = await decomposeTask(client, model, task.description, task.lineage);
-
- const childLineage = [...task.lineage, task.description];
- task.children = subtaskDescriptions.map((desc, i) =>
- createTaskNode(`${task.id}.${i + 1}`, desc, task.depth + 1, childLineage),
- );
-
- // Recurse on children concurrently
- await Promise.all(task.children.map((child) => planTree(client, model, child, maxDepth)));
-
- task.status = "ready";
- return task;
-}
-
-// =============================================================================
-// PUBLIC API
-// =============================================================================
-
-/** Create a decomposition plan for a task. */
-export async function decompose(
- taskDescription: string,
- config: DecomposerConfig = DEFAULT_DECOMPOSER_CONFIG,
-): Promise {
- const client = new Anthropic();
- const tree = createTaskNode("1", taskDescription, 0, []);
-
- await planTree(client, config.model, tree, config.maxDepth);
-
- return {
- id: `plan-${Date.now()}`,
- rootTask: taskDescription,
- tree,
- maxDepth: config.maxDepth,
- phase: config.requireApproval ? "review" : "approved",
- createdAt: new Date().toISOString(),
- };
-}
-
-/** Collect all leaf (atomic) tasks from a tree. */
-export function getLeaves(task: TaskNode): TaskNode[] {
- if (task.children.length === 0) return [task];
- return task.children.flatMap(getLeaves);
-}
-
-/** Get sibling task descriptions for a given task. */
-export function getSiblings(root: TaskNode, taskId: string): string[] {
- function findParent(node: TaskNode): TaskNode | null {
- for (const child of node.children) {
- if (child.id === taskId) return node;
- const found = findParent(child);
- if (found) return found;
- }
- return null;
- }
-
- const parent = findParent(root);
- if (!parent) return [];
- return parent.children.filter((c) => c.id !== taskId).map((c) => c.description);
-}
-
-/** Format the plan tree as a human-readable string. */
-export function formatPlanTree(task: TaskNode, indent = 0): string {
- const prefix = " ".repeat(indent);
- const kindTag = task.kind === "atomic" ? "[ATOMIC]" : task.kind === "composite" ? "[COMPOSITE]" : "";
- const statusTag = task.status !== "ready" ? ` (${task.status})` : "";
- let line = `${prefix}${task.id}. ${kindTag} ${task.description}${statusTag}`;
-
- if (task.children.length > 0) {
- const childLines = task.children.map((c) => formatPlanTree(c, indent + 1)).join("\n");
- line += "\n" + childLines;
- }
-
- return line;
-}
-
-/** Propagate done/failed status up the tree. */
-export function propagateStatus(task: TaskNode): void {
- if (task.children.length === 0) return;
- task.children.forEach(propagateStatus);
- if (task.children.every((c) => c.status === "done")) {
- task.status = "done";
- } else if (task.children.some((c) => c.status === "failed")) {
- task.status = "failed";
- } else if (task.children.some((c) => c.status === "running" || c.status === "done")) {
- task.status = "running";
- }
-}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 611e027f8..96720f477 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -61,25 +61,6 @@ export type { LifecycleManagerDeps } from "./lifecycle-manager.js";
export { buildPrompt, BASE_AGENT_PROMPT } from "./prompt-builder.js";
export type { PromptBuildConfig } from "./prompt-builder.js";
-// Decomposer — LLM-driven task decomposition
-export {
- decompose,
- getLeaves,
- getSiblings,
- formatPlanTree,
- formatLineage,
- formatSiblings,
- propagateStatus,
- DEFAULT_DECOMPOSER_CONFIG,
-} from "./decomposer.js";
-export type {
- TaskNode,
- TaskKind,
- TaskStatus,
- DecompositionPlan,
- DecomposerConfig,
-} from "./decomposer.js";
-
// Orchestrator prompt — generates orchestrator context for `ao start`
export { generateOrchestratorPrompt } from "./orchestrator-prompt.js";
export type { OrchestratorPromptConfig } from "./orchestrator-prompt.js";
diff --git a/packages/core/src/prompt-builder.ts b/packages/core/src/prompt-builder.ts
index 2cbcdccbb..eb0a972fe 100644
--- a/packages/core/src/prompt-builder.ts
+++ b/packages/core/src/prompt-builder.ts
@@ -58,12 +58,6 @@ export interface PromptBuildConfig {
/** Explicit user prompt (appended last) */
userPrompt?: string;
-
- /** Decomposition context — ancestor task chain (from decomposer) */
- lineage?: string[];
-
- /** Decomposition context — sibling task descriptions (from decomposer) */
- siblings?: string[];
}
// =============================================================================
@@ -165,25 +159,6 @@ export function buildPrompt(config: PromptBuildConfig): string {
sections.push(`## Project Rules\n${userRules}`);
}
- // Layer 4: Decomposition context (lineage + siblings)
- if (config.lineage && config.lineage.length > 0) {
- const hierarchy = config.lineage.map((desc, i) => `${" ".repeat(i)}${i}. ${desc}`);
- // Add current task marker using issueId or last lineage entry
- const currentLabel = config.issueId ?? "this task";
- hierarchy.push(`${" ".repeat(config.lineage.length)}${config.lineage.length}. ${currentLabel} <-- (this task)`);
-
- sections.push(
- `## Task Hierarchy\nThis task is part of a larger decomposed plan. Your place in the hierarchy:\n\n\`\`\`\n${hierarchy.join("\n")}\n\`\`\`\n\nStay focused on YOUR specific task. Do not implement functionality that belongs to other tasks in the hierarchy.`,
- );
- }
-
- if (config.siblings && config.siblings.length > 0) {
- const siblingLines = config.siblings.map((s) => ` - ${s}`);
- sections.push(
- `## Parallel Work\nSibling tasks being worked on in parallel:\n${siblingLines.join("\n")}\n\nDo not duplicate work that sibling tasks handle. If you need interfaces/types from siblings, define reasonable stubs.`,
- );
- }
-
// Explicit user prompt (appended last, highest priority)
if (config.userPrompt) {
sections.push(`## Additional Instructions\n${config.userPrompt}`);
diff --git a/packages/core/src/session-manager.ts b/packages/core/src/session-manager.ts
index 1d9765b63..c9c27eb8f 100644
--- a/packages/core/src/session-manager.ts
+++ b/packages/core/src/session-manager.ts
@@ -1061,8 +1061,6 @@ export function createSessionManager(deps: SessionManagerDeps): OpenCodeSessionM
issueId: spawnConfig.issueId,
issueContext,
userPrompt: spawnConfig.prompt,
- lineage: spawnConfig.lineage,
- siblings: spawnConfig.siblings,
});
// Get agent launch config and create runtime — clean up workspace on failure
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 463dc02ea..df7338455 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -232,10 +232,6 @@ export interface SessionSpawnConfig {
agent?: string;
/** Override the OpenCode subagent for this session (e.g. "sisyphus", "oracle") */
subagent?: string;
- /** Decomposition context — ancestor task chain (passed to prompt builder) */
- lineage?: string[];
- /** Decomposition context — sibling task descriptions (passed to prompt builder) */
- siblings?: string[];
}
/** Config for creating an orchestrator session */
@@ -1185,18 +1181,6 @@ export interface ProjectConfig {
| "kill-previous";
opencodeIssueSessionStrategy?: "reuse" | "delete" | "ignore";
-
- /** Task decomposition configuration */
- decomposer?: {
- /** Enable auto-decomposition for backlog issues (default: false) */
- enabled: boolean;
- /** Max recursion depth (default: 3) */
- maxDepth: number;
- /** Model to use for decomposition (default: claude-sonnet-4-20250514) */
- model: string;
- /** Require human approval before executing decomposed plans (default: true) */
- requireApproval: boolean;
- };
}
export interface TrackerConfig {
diff --git a/packages/web/src/__tests__/services.test.ts b/packages/web/src/__tests__/services.test.ts
index c798f2fd8..db3fe8869 100644
--- a/packages/web/src/__tests__/services.test.ts
+++ b/packages/web/src/__tests__/services.test.ts
@@ -49,11 +49,6 @@ vi.mock("@aoagents/ao-core", () => ({
getStates: vi.fn(),
check: vi.fn(),
}),
- decompose: vi.fn(),
- getLeaves: vi.fn(),
- getSiblings: vi.fn(),
- formatPlanTree: vi.fn(),
- DEFAULT_DECOMPOSER_CONFIG: {},
TERMINAL_STATUSES: new Set(["merged", "killed"]) as ReadonlySet,
}));
diff --git a/packages/web/src/lib/services.ts b/packages/web/src/lib/services.ts
index 0e7fc5649..9c2874dd4 100644
--- a/packages/web/src/lib/services.ts
+++ b/packages/web/src/lib/services.ts
@@ -17,10 +17,6 @@ import {
createPluginRegistry,
createSessionManager,
createLifecycleManager,
- decompose,
- getLeaves,
- getSiblings,
- formatPlanTree,
type OrchestratorConfig,
type PluginRegistry,
type OpenCodeSessionManager,
@@ -30,8 +26,6 @@ import {
type Tracker,
type Issue,
type Session,
- type DecomposerConfig,
- DEFAULT_DECOMPOSER_CONFIG,
isOrchestratorSession,
TERMINAL_STATUSES,
} from "@aoagents/ao-core";
@@ -272,65 +266,8 @@ export async function pollBacklog(): Promise {
if (activeIssueIds.has(issue.id.toLowerCase())) continue;
try {
- const decompConfig = project.decomposer;
- const shouldDecompose = decompConfig?.enabled ?? false;
-
- if (shouldDecompose) {
- // Decompose the issue before spawning
- const taskDescription = `${issue.title}\n\n${issue.description}`;
- const decomposerConfig: DecomposerConfig = {
- ...DEFAULT_DECOMPOSER_CONFIG,
- ...decompConfig,
- };
-
- console.log(`[backlog] Decomposing issue ${issue.id}: "${issue.title}"`);
- const plan = await decompose(taskDescription, decomposerConfig);
- const leaves = getLeaves(plan.tree);
-
- if (leaves.length <= 1) {
- // Atomic — spawn directly
- await sessionManager.spawn({ projectId, issueId: issue.id });
- availableSlots--;
- } else if (decomposerConfig.requireApproval) {
- // Post plan as comment and wait for human approval
- const treeText = formatPlanTree(plan.tree);
- if (tracker.updateIssue) {
- await tracker.updateIssue(
- issue.id,
- {
- comment: `## Decomposition Plan\n\n\`\`\`\n${treeText}\n\`\`\`\n\n${leaves.length} subtasks identified. Remove \`agent:backlog\` and add \`agent:decompose-approved\` to execute.`,
- labels: ["agent:decompose-pending"],
- removeLabels: ["agent:backlog"],
- },
- project,
- );
- }
- console.log(
- `[backlog] Posted decomposition plan for ${issue.id} (${leaves.length} subtasks, awaiting approval)`,
- );
- continue;
- } else {
- // Auto-execute: spawn each leaf with lineage context
- console.log(
- `[backlog] Auto-executing decomposition for ${issue.id} (${leaves.length} subtasks)`,
- );
- for (const leaf of leaves) {
- if (availableSlots <= 0) break;
- const siblings = getSiblings(plan.tree, leaf.id);
- await sessionManager.spawn({
- projectId,
- issueId: issue.id,
- lineage: leaf.lineage,
- siblings,
- });
- availableSlots--;
- }
- }
- } else {
- // No decomposition — spawn directly (classic behavior)
- await sessionManager.spawn({ projectId, issueId: issue.id });
- availableSlots--;
- }
+ await sessionManager.spawn({ projectId, issueId: issue.id });
+ availableSlots--;
activeIssueIds.add(issue.id.toLowerCase());
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6be3b461a..12045319e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -141,9 +141,6 @@ importers:
packages/core:
dependencies:
- '@anthropic-ai/sdk':
- specifier: ^0.52.0
- version: 0.52.0
yaml:
specifier: ^2.7.0
version: 2.8.2
@@ -721,10 +718,6 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
- '@anthropic-ai/sdk@0.52.0':
- resolution: {integrity: sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==}
- hasBin: true
-
'@asamuzakjp/css-color@3.2.0':
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
@@ -4247,8 +4240,6 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
- '@anthropic-ai/sdk@0.52.0': {}
-
'@asamuzakjp/css-color@3.2.0':
dependencies:
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)