UAT: Child plan spawning does not propagate parent plan's effective invariant set — spec requires inheritance #2095

Open
opened 2026-04-03 04:02:16 +00:00 by freemo · 1 comment
Owner

Metadata

  • Branch: fix/subplan-inherit-parent-invariants
  • Commit Message: fix(subplan): propagate parent plan invariants to child plans during spawn
  • Milestone: v3.7.0
  • Parent Epic: #936

Description

The specification (docs/specification.md line 19623) states:

"When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan. Child plans do not re-run reconciliation — they inherit the parent's resolved view."

ADR-016 (docs/adr/ADR-016-invariant-system.md) also states:

"When a parent plan spawns child plans, the child inherits the parent's effective invariant set."

However, the current implementation in src/cleveragents/application/services/subplan_service.py creates child Plan objects without copying the parent plan's invariants field. The Plan domain model has an invariants: list[PlanInvariant] field, but the SubplanService.spawn() method (lines 259–283) does not pass invariants=list(parent_plan.invariants) when constructing child plans.

Current Behavior:

  • When a parent plan with invariants spawns child plans via SubplanService.spawn(), the child plans are created with an empty invariants list (the default).
  • Child plans have no knowledge of the parent's enforced invariant constraints.

Expected Behavior (per spec):

  • Child plans must inherit the parent plan's effective invariant set.
  • The SubplanService.spawn() method must copy invariants=list(parent_plan.invariants) to each child plan.
  • Child plans should NOT re-run reconciliation — they use the already-reconciled parent invariants.

Code Location: src/cleveragents/application/services/subplan_service.py, spawn() method, lines 259–283. The child_plan: Plan = Plan(...) constructor call copies many parent fields (strategy_actor, execution_actor, project_links, definition_of_done, etc.) but omits invariants.

Steps to Reproduce:

  1. Create a parent plan with invariants (e.g., agents invariant add --plan <PLAN_ID> "Use Python 3.13 only")
  2. Run the plan through Strategize so invariants are reconciled
  3. Spawn child plans via SubplanService
  4. Inspect child plan's invariants field
  5. Observe: child plan has empty invariants list

Spec References:

  • docs/specification.md line 19623: "When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan."
  • docs/adr/ADR-016-invariant-system.md: "When a parent plan spawns child plans, the child inherits the parent's effective invariant set."

Subtasks

  • Reproduce the bug with a failing Behave scenario in features/ that asserts child plan inherits parent invariants after spawn
  • Inspect SubplanService.spawn() (lines 259–283) and identify all Plan(...) constructor call sites that omit invariants
  • Update SubplanService.spawn() to pass invariants=list(parent_plan.invariants) when constructing each child Plan
  • Ensure child plans do NOT re-run invariant reconciliation (they must use the already-reconciled parent invariant set as-is)
  • Add/update type annotations and argument validation in SubplanService.spawn() to enforce the invariant propagation contract
  • Run nox -e typecheck to confirm no Pyright errors introduced
  • Run nox -e unit_tests to confirm all Behave scenarios pass including the new regression scenario
  • Run nox -e integration_tests to confirm Robot Framework integration tests pass
  • Run nox -e coverage_report to confirm coverage remains ≥ 97%

Definition of Done

  • A Behave scenario in features/ reproduces the bug and passes after the fix
  • SubplanService.spawn() passes invariants=list(parent_plan.invariants) to every child Plan constructor call
  • Child plans do not re-run invariant reconciliation — they inherit the parent's already-reconciled invariant set
  • No # type: ignore suppressions introduced; nox -e typecheck passes clean
  • All nox stages pass (nox -e lint, nox -e typecheck, nox -e unit_tests, nox -e integration_tests)
  • Coverage >= 97%
  • Commit fix(subplan): propagate parent plan invariants to child plans during spawn pushed to branch fix/subplan-inherit-parent-invariants with footer ISSUES CLOSED: #<this issue>
  • Pull request merged, linked to this issue, and marked as blocking this issue in Forgejo dependency tracking

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-new-issue-creator

## Metadata - **Branch**: `fix/subplan-inherit-parent-invariants` - **Commit Message**: `fix(subplan): propagate parent plan invariants to child plans during spawn` - **Milestone**: v3.7.0 - **Parent Epic**: #936 ## Description The specification (`docs/specification.md` line 19623) states: > "When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan. Child plans do not re-run reconciliation — they inherit the parent's resolved view." ADR-016 (`docs/adr/ADR-016-invariant-system.md`) also states: > "When a parent plan spawns child plans, the child inherits the parent's effective invariant set." However, the current implementation in `src/cleveragents/application/services/subplan_service.py` creates child `Plan` objects without copying the parent plan's `invariants` field. The `Plan` domain model has an `invariants: list[PlanInvariant]` field, but the `SubplanService.spawn()` method (lines 259–283) does not pass `invariants=list(parent_plan.invariants)` when constructing child plans. **Current Behavior:** - When a parent plan with invariants spawns child plans via `SubplanService.spawn()`, the child plans are created with an empty `invariants` list (the default). - Child plans have no knowledge of the parent's enforced invariant constraints. **Expected Behavior (per spec):** - Child plans must inherit the parent plan's effective invariant set. - The `SubplanService.spawn()` method must copy `invariants=list(parent_plan.invariants)` to each child plan. - Child plans should NOT re-run reconciliation — they use the already-reconciled parent invariants. **Code Location:** `src/cleveragents/application/services/subplan_service.py`, `spawn()` method, lines 259–283. The `child_plan: Plan = Plan(...)` constructor call copies many parent fields (`strategy_actor`, `execution_actor`, `project_links`, `definition_of_done`, etc.) but omits `invariants`. **Steps to Reproduce:** 1. Create a parent plan with invariants (e.g., `agents invariant add --plan <PLAN_ID> "Use Python 3.13 only"`) 2. Run the plan through Strategize so invariants are reconciled 3. Spawn child plans via `SubplanService` 4. Inspect child plan's `invariants` field 5. Observe: child plan has empty `invariants` list **Spec References:** - `docs/specification.md` line 19623: "When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan." - `docs/adr/ADR-016-invariant-system.md`: "When a parent plan spawns child plans, the child inherits the parent's effective invariant set." ## Subtasks - [ ] Reproduce the bug with a failing Behave scenario in `features/` that asserts child plan inherits parent invariants after spawn - [ ] Inspect `SubplanService.spawn()` (lines 259–283) and identify all `Plan(...)` constructor call sites that omit `invariants` - [ ] Update `SubplanService.spawn()` to pass `invariants=list(parent_plan.invariants)` when constructing each child `Plan` - [ ] Ensure child plans do NOT re-run invariant reconciliation (they must use the already-reconciled parent invariant set as-is) - [ ] Add/update type annotations and argument validation in `SubplanService.spawn()` to enforce the invariant propagation contract - [ ] Run `nox -e typecheck` to confirm no Pyright errors introduced - [ ] Run `nox -e unit_tests` to confirm all Behave scenarios pass including the new regression scenario - [ ] Run `nox -e integration_tests` to confirm Robot Framework integration tests pass - [ ] Run `nox -e coverage_report` to confirm coverage remains ≥ 97% ## Definition of Done - [ ] A Behave scenario in `features/` reproduces the bug and passes after the fix - [ ] `SubplanService.spawn()` passes `invariants=list(parent_plan.invariants)` to every child `Plan` constructor call - [ ] Child plans do not re-run invariant reconciliation — they inherit the parent's already-reconciled invariant set - [ ] No `# type: ignore` suppressions introduced; `nox -e typecheck` passes clean - [ ] All nox stages pass (`nox -e lint`, `nox -e typecheck`, `nox -e unit_tests`, `nox -e integration_tests`) - [ ] Coverage >= 97% - [ ] Commit `fix(subplan): propagate parent plan invariants to child plans during spawn` pushed to branch `fix/subplan-inherit-parent-invariants` with footer `ISSUES CLOSED: #<this issue>` - [ ] Pull request merged, linked to this issue, and marked as blocking this issue in Forgejo dependency tracking --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
freemo added this to the v3.7.0 milestone 2026-04-03 04:02:21 +00:00
freemo self-assigned this 2026-04-03 16:58:08 +00:00
Author
Owner

MoSCoW classification: Should Have

Rationale: This issue addresses a spec requirement or important quality improvement. It should be included in the milestone if possible.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: ca-project-owner

MoSCoW classification: **Should Have** Rationale: This issue addresses a spec requirement or important quality improvement. It should be included in the milestone if possible. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: ca-project-owner
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core#2095
No description provided.