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
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
141 lines
5.6 KiB
Markdown
141 lines
5.6 KiB
Markdown
# 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
|
|
|
|
```python
|
|
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.
|