Move SubplanSpawnError from local subplan_service definition to centralized cleveragents.core.exceptions alongside four new spec-defined error types: SubplanExecutionError, MaxParallelExceededError, and SubplanDepthLimitError. Per the v3.3.0 specification (AUTO-ARCH-6), all subplan-related errors are defined in exceptions.py with proper inheritance hierarchy under DomainError/ PlanError/BusinessRuleViolation. The old local SpawnValidationError class has been replaced with SubplanSpawnError(PlanError) with a simplified constructor API (message string instead of validation_errors list). Updates: - exceptions.py: Added 4 subplan error classes + __all__ entries - subplan_service.py: Remove local SpawnValidationError, import SubplanSpawnError from exceptions - services/__init__.py: Update TYPE_CHECKING stub and _LAZY_IMPORTS for new location - vulture_whitelist.py: Replace old entry with new error class names - docs/reference/subplan_service.md: Update to reference SubplanSpawnError (v3.3.0) - features/*.feature + steps: Update test references from SpawnValidationError to SubplanSpawnError
5.7 KiB
Subplan Service: Spawn Workflow and Lifecycle
The SubplanService coordinates the spawning of child plans from
DecisionService spawn entries and SubplanConfig. It validates
resource scopes, merge strategies, and parallelism bounds before
creating child plan statuses.
Overview
When a parent plan decides to decompose work into child plans (via
subplan_spawn or subplan_parallel_spawn decisions during Strategize),
the SubplanService handles the spawn workflow:
- Extract spawn decisions from the
DecisionService - Build
SpawnEntryobjects from those decisions - Validate the spawn request
- Create
SubplanStatusandSpawnMetadatafor each entry - Return the result for the caller to persist on the parent plan
Spawn Workflow
DecisionService SubplanService Parent Plan
| | |
| get_spawn_decisions | |
|<-----------------------| |
| [Decision, ...] | |
|----------------------->| |
| | build_spawn_entries |
| | validate_spawn |
| | spawn |
| |------------------------->|
| | SpawnResult |
| | (statuses + metadata) |
Key Types
SpawnMetadata
Persisted alongside each child plan for status output and provenance:
| Field | Type | Description |
|---|---|---|
spawn_decision_id |
str |
ULID of the decision that triggered spawn |
parent_plan_id |
str |
ULID of the parent plan |
root_plan_id |
str |
ULID of the root plan in the hierarchy |
execution_mode |
str |
How the subplan should be executed |
SpawnEntry
A single spawn request derived from a decision:
| Field | Type | Description |
|---|---|---|
decision |
Decision |
The decision that triggered spawn |
action_name |
str |
Namespaced action name for child plan |
target_resources |
list[str] |
Resource IDs the child plan uses |
description |
str |
Description/prompt for the child plan |
SpawnResult
Result of spawning child plans:
| Field | Type | Description |
|---|---|---|
spawned_statuses |
list[SubplanStatus] |
Status for each child plan |
metadata |
dict[str, SpawnMetadata] |
Metadata keyed by subplan_id |
total_spawned |
int |
Number of child plans created |
execution_mode |
str |
Execution mode from config |
Spawn Validation
Before any child plan is created, validate_spawn checks:
-
Resource scopes resolved: All
target_resourcesin each entry exist in theavailable_resourcesset. Unresolved resources produce a validation error. -
Merge strategy defined: The
SubplanConfig.merge_strategymust not beNone. Without a merge strategy, child plan results cannot be combined. -
max_parallel bounds: In
PARALLELexecution mode, the number of spawn entries must not exceedconfig.max_parallel. -
Valid action names: Each entry must have a non-empty
action_name. -
Correct decision types: Each entry's decision must be either
subplan_spawnorsubplan_parallel_spawn.
If any check fails, SubplanSpawnError is raised with a message listing all errors.
Integration with Other Services
- DecisionService: Provides spawn-type decisions via
list_by_type(plan_id, "subplan_spawn"). - SubplanExecutionService: Executes the spawned child plans using
the
SubplanStatusobjects fromSpawnResult. - SubplanMergeService: Merges child plan outputs using the strategy
from
SubplanConfig. - PlanLifecycleService: Manages the parent plan's lifecycle,
persisting the updated
subplan_statusesandsubplan_config.
Example Usage
from cleveragents.application.services.subplan_service import (
SubplanService,
)
# Get spawn decisions for the parent plan
decisions = subplan_service.get_spawn_decisions(parent_plan.identity.plan_id)
# Build spawn entries from decisions
entries = subplan_service.build_spawn_entries(decisions)
# Spawn child plans (validates first)
result = subplan_service.spawn(
parent_plan=parent_plan,
config=parent_plan.subplan_config,
spawn_entries=entries,
available_resources=frozenset(known_resource_ids),
)
# Update the parent plan with spawned statuses
parent_plan.subplan_statuses = result.spawned_statuses
Error Handling
| Error | When |
|---|---|
ValueError |
None or empty required arguments |
SubplanSpawnError (v3.3.0) |
Validation checks fail (resource scope, |
| merge strategy, max_parallel, action name, | |
| or decision type) |
SubplanSpawnError inherits from PlanError and carries a descriptive message
listing all validation failures. The error is centralized in
cleveragents.core.exceptions (moved from the local subplan_service module in v3.3.0).