diff --git a/docs/specification.md b/docs/specification.md index f0803345d..ab82a38d6 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -47095,3 +47095,151 @@ These architectural invariants must be maintained across all milestones: 8. **BDD tests**: All unit-level tests expressed as Behave/Gherkin scenarios. No xUnit-style tests. 9. **File size limit**: No source file exceeds 500 lines. Split into modules if approaching limit. 10. **Atomic commits**: One logical change per commit. No mixed concerns. + +--- + +## Subplan System (v3.3.0) + +### Overview + +The Subplan System enables plans to spawn child plans (subplans) during execution. Subplans execute in parallel with configurable concurrency limits (`max_parallel`). Results are merged back into the parent plan using three-way merge strategies. The parent plan tracks all subplan statuses and waits for completion before proceeding. + +### Module Boundaries + +- **Module**: `cleveragents.subplans` +- **Layer**: Domain (with Application orchestration) +- **Responsibilities**: + - Spawning subplans from a parent plan during Execute phase + - Managing subplan lifecycle (pending → running → complete/failed) + - Enforcing `max_parallel` concurrency limits + - Tracking parent-child plan relationships + - Providing subplan status to parent plan +- **Public Interfaces**: + - `SubplanSpawner` — creates and registers subplans from a parent plan + - `SubplanRepository` — CRUD for subplan entities (domain repository interface) + - `SubplanExecutor` — orchestrates parallel subplan execution with concurrency control + - `SubplanStatusTracker` — tracks and reports subplan completion status +- **Forbidden Dependencies**: Must not import from `cleveragents.cli` or `cleveragents.tui` + +### Data Models + +#### Subplan Entity + +```python +@dataclass +class Subplan: + id: UUID + parent_plan_id: UUID + root_plan_id: UUID # Top-level ancestor plan + depth: int # 1 = direct child of root, 2 = grandchild, etc. + title: str + description: str + action_id: UUID # Action template driving this subplan + status: Literal["pending", "running", "complete", "failed", "cancelled"] + max_parallel: int # Max concurrent sub-subplans this plan may spawn + created_at: datetime + started_at: Optional[datetime] + completed_at: Optional[datetime] + result: Optional[SubplanResult] + metadata: dict +``` + +#### SubplanResult + +```python +@dataclass +class SubplanResult: + subplan_id: UUID + status: Literal["success", "partial", "failed"] + output: dict # Structured output from the subplan + artifacts: list[str] # File paths or resource IDs produced + error: Optional[str] + merge_conflicts: list[MergeConflict] # Conflicts detected during merge +``` + +#### SubplanTree + +```python +@dataclass +class SubplanTree: + root_plan_id: UUID + + def get_children(self, plan_id: UUID) -> list[Subplan]: ... + def get_all_descendants(self, plan_id: UUID) -> list[Subplan]: ... + def get_depth(self, plan_id: UUID) -> int: ... + def get_running_count(self, plan_id: UUID) -> int: ... +``` + +### Database Schema + +```sql +CREATE TABLE subplans ( + id UUID PRIMARY KEY, + parent_plan_id UUID NOT NULL REFERENCES plans(id) ON DELETE CASCADE, + root_plan_id UUID NOT NULL REFERENCES plans(id), + depth INTEGER NOT NULL DEFAULT 1, + title VARCHAR(500) NOT NULL, + description TEXT NOT NULL, + action_id UUID NOT NULL REFERENCES actions(id), + status VARCHAR(20) NOT NULL DEFAULT 'pending' + CHECK (status IN ('pending', 'running', 'complete', 'failed', 'cancelled')), + max_parallel INTEGER NOT NULL DEFAULT 4, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + started_at TIMESTAMPTZ, + completed_at TIMESTAMPTZ, + result JSONB, + metadata JSONB NOT NULL DEFAULT '{}' +); + +CREATE INDEX idx_subplans_parent_plan ON subplans(parent_plan_id); +CREATE INDEX idx_subplans_root_plan ON subplans(root_plan_id); +CREATE INDEX idx_subplans_status ON subplans(status); +``` + +### Spawning Algorithm + +During the Execute phase, the LangGraph execute node may spawn subplans: + +1. The LLM decides to decompose a task into subtasks +2. `SubplanSpawner.spawn(parent_plan_id, subtasks)` creates subplan records +3. Each subplan is assigned an Action template appropriate for the subtask +4. `SubplanExecutor.execute_parallel(subplans, max_parallel)` begins execution +5. Subplans are dispatched in batches of `max_parallel` +6. Each subplan runs its own Strategize → Execute → Apply lifecycle +7. Subplans may themselves spawn sub-subplans (hierarchical decomposition, 4+ levels) +8. Parent plan waits for all subplans to complete before proceeding + +### Concurrency Control + +- `max_parallel` is configurable per plan (default: 4, max: 16) +- Concurrency is enforced using a semaphore per parent plan +- If a subplan fails, sibling subplans continue unless `fail_fast=True` +- Cancelled subplans are marked `cancelled` and their results are excluded from merge + +### Integration Points + +| Integration | Direction | Description | +|---|---|---| +| Plan Executor | Called by | Execute node spawns subplans via `SubplanSpawner` | +| Three-Way Merge | Calls | `SubplanExecutor` calls merge strategy after all subplans complete | +| Decision Recording | Calls | Subplan spawning decisions recorded via `DecisionRecorder` | +| Checkpoint System | Calls | Checkpoints created before and after subplan execution | + +### Error Handling + +- `SubplanSpawnError(parent_plan_id, reason)` — failed to create subplan +- `SubplanExecutionError(subplan_id, reason)` — subplan execution failed +- `MaxParallelExceededError(plan_id, requested, max)` — concurrency limit exceeded +- `SubplanDepthLimitError(plan_id, depth, max_depth)` — hierarchical depth limit exceeded (max: 8) + +### Cross-Cutting Concerns + +- **Observability**: Subplan count, depth, and completion rate tracked as metrics +- **Logging**: All subplan lifecycle events logged at INFO level +- **Cancellation**: Parent plan cancellation propagates to all running subplans +- **Timeout**: Each subplan has a configurable timeout (default: 30 minutes) + +--- +**Automated by CleverAgents Bot** +Supervisor: Architecture | Agent: architecture-pool-supervisor +Worker: [AUTO-ARCH-6]