Files
cleveragents-core/docs/reference/subplan_service.md
T
HAL9000 5e89f1016c refactor(subplans): Centralize subplan errors per v3.3.0 spec (#8725)
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
2026-06-02 19:50:41 -04:00

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:

  1. Extract spawn decisions from the DecisionService
  2. Build SpawnEntry objects from those decisions
  3. Validate the spawn request
  4. Create SubplanStatus and SpawnMetadata for each entry
  5. 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:

  1. Resource scopes resolved: All target_resources in each entry exist in the available_resources set. Unresolved resources produce a validation error.

  2. Merge strategy defined: The SubplanConfig.merge_strategy must not be None. Without a merge strategy, child plan results cannot be combined.

  3. max_parallel bounds: In PARALLEL execution mode, the number of spawn entries must not exceed config.max_parallel.

  4. Valid action names: Each entry must have a non-empty action_name.

  5. Correct decision types: Each entry's decision must be either subplan_spawn or subplan_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 SubplanStatus objects from SpawnResult.
  • SubplanMergeService: Merges child plan outputs using the strategy from SubplanConfig.
  • PlanLifecycleService: Manages the parent plan's lifecycle, persisting the updated subplan_statuses and subplan_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).