Files
cleveragents-core/docs/reference/subplan_service.md
T
freemo c43e1f8260
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / quality (pull_request) Successful in 19s
CI / build (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 31s
CI / security (pull_request) Successful in 32s
CI / unit_tests (pull_request) Successful in 1m52s
CI / docker (pull_request) Successful in 42s
CI / integration_tests (pull_request) Successful in 2m59s
CI / coverage (pull_request) Successful in 4m14s
CI / lint (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 30s
CI / typecheck (push) Successful in 1m3s
CI / build (push) Successful in 18s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m27s
CI / integration_tests (push) Successful in 3m0s
CI / docker (push) Successful in 50s
CI / coverage (push) Successful in 5m8s
CI / benchmark-publish (push) Has been cancelled
CI / benchmark-regression (pull_request) Successful in 29m24s
feat(service): add subplan service and spawn workflow
Add SubplanService for building child plans from DecisionService spawn
entries and SubplanConfig. The service validates resource scopes, merge
strategies, and max_parallel bounds before spawning.

Key additions:
- SubplanService: Orchestrates child plan creation from spawn entries
- SpawnMetadata: Persisted metadata (spawn_decision_id, parent/root
  plan IDs, execution mode) for status output
- Spawn validation: Checks resource scopes, merge strategy, and
  parallelism bounds before spawning
- Documentation: subplan_service.md with spawn workflow and lifecycle

ISSUES CLOSED: #197
2026-03-02 09:41:25 -05:00

5.6 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, SpawnValidationError is raised with 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
SpawnValidationError Validation checks fail (resource scope,
merge strategy, max_parallel, action name,
or decision type)

SpawnValidationError inherits from ValidationError and includes a validation_errors list with detailed messages for each failure.