fix(cli): share PlanLifecycleService instance between CLI handler and PlanExecutor #1027
Merged
CoreRasurae
merged 1 commits from 2026-03-17 12:29:39 +00:00
test/e2e-m6-acceptance into master
No Reviewers
Labels
Clear labels
auto/needs-reevaluation
controller-managed
overdue
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Needs Feedback
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
Controller deferred this PR; awaiting Phase 6+ scope-evaluator or operator re-enablement.
Auto-agents controller manages this PR/issue (see tools/controller/deploy/RUNBOOK.md). Remove this label to abandon controller management.
PR blocked by an open issue dependency. Operator must close the dep (or remove the dependency link) before the merge driver can act. Auto-cleared by merge_drive when no open deps remain.
Most recent merge cycle hit CI timeout. Driver excludes this PR while last merge_cycle row is < 30 min old; label persists thereafter as visible history.
Currently being processed by an implementer worker.
Currently being processed by the merge driver.
Currently being processed by a reviewer worker.
Merge driver heartbeat stale; pipeline halted. Closed automatically on next clean tick.
Detected master commit violating the strict merge invariant. Tracked as an issue (not a PR label); kept here for label completeness.
In-cycle escalation: most recent attempt ran at the Tier 0 slot (`tier-0`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 1 slot (`tier-1`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 2 slot (`tier-2`). Slot's model defined in .opencode/models/tiers.yaml. Gated behind IMPLEMENTER_ESCALATION_TIER2_ENABLED.
In-cycle escalation: most recent attempt ran at the Tier -1 slot (`tier-min`). Slot's model defined in .opencode/models/tiers.yaml. Suffix is ``-min`` (not ``--1``) so the Forgejo UI reads naturally.
Tracking issues used by the AI Automation system for agents to communicate and report.
Rebase conflict needs LLM conflict-resolver.
Failing CI needs implementer attention.
Documenting a driver incident or rollback.
Reviewer has APPROVED this PR and no later REQUEST_CHANGES is outstanding. The merge driver requires this label to even consider a PR for merging. Set by the reviewer worker on APPROVE; cleared on REQUEST_CHANGES.
Train repeatedly lost master-tempo races. Driver excludes via merge_cycle until cooldown elapses; label persists as visible history.
Revert PR backing out an invariant violation. Fast-tracked through the merge driver.
Sentinel PR duplicated from upstream into a personal fork by tools/duplicate_prs_to_fork.py for pipeline testing. Lives only in the fork; the canonical pipeline never sees it.
No implementer activity for N days. Flagged for human review. Auto-cleared on next push to head branch.
Repeatedly fails on current master (>= 3 ci-fail-on-rebased-sha releases in 12 h). Excluded from driver until human triage.
A ticket in a blocked state and unable to complete until some other task is completed first.
Bounty
$100
A bounty of $100 for any open-source contributor who provides a MR that solves this issue
Bounty
$1000
A bounty of $1000 for any open-source contributor who provides a MR that solves this issue
Bounty
$10000
A bounty of $10000 for any open-source contributor who provides a MR that solves this issue
Bounty
$20
A bounty of $20 for any open-source contributor who provides a MR that solves this issue
Bounty
$2000
A bounty of $2000 for any open-source contributor who provides a MR that solves this issue
Bounty
$250
A bounty of $250 for any open-source contributor who provides a MR that solves this issue
Bounty
$50
A bounty of $50 for any open-source contributor who provides a MR that solves this issue
Bounty
$500
A bounty of $500 for any open-source contributor who provides a MR that solves this issue
Bounty
$5000
A bounty of $5000 for any open-source contributor who provides a MR that solves this issue
Bounty
$750
A bounty of $750 for any open-source contributor who provides a MR that solves this issue
MoSCoW
Could have
Could have feature in order to satisfy the epic/legendary.
MoSCoW
Must have
Must have feature in order to satisfy the epic/legendary.
MoSCoW
Should have
Should have feature in order to satisfy the epic/legendary.
There are questions in the ticket that can not be completed until the project owner provides clarity.
Points
1
1 man-hours worth of work for an expert with no learning curve.
Points
13
13 man-hours worth of work for an expert with no learning curve.
Points
2
2 man-hours worth of work for an expert with no learning curve.
Points
21
21 man-hours worth of work for an expert with no learning curve.
Points
3
3 man-hours worth of work for an expert with no learning curve.
Points
34
34 man-hours worth of work for an expert with no learning curve.
Points
5
5 man-hours worth of work for an expert with no learning curve.
Points
55
55 man-hours worth of work for an expert with no learning curve.
Points
8
8 man-hours worth of work for an expert with no learning curve.
Points
88
88 man-hours worth of work for an expert with no learning curve.
Priority
Backlog
This ticket has backlogged priority and is not to be worked on yet
Priority
CI Blocker
Critical priority issue that blocks CI/CD pipeline and prevents PR merges
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
A ticket for learning a tool or technology that is needed to be able to do future planning and design.
State
Completed
The ticket has been fully implemented, completed, and merged with the source code. This label should only be applied once a ticket is closed.
State
Duplicate
A ticket that represents the same content as an existing ticket.
State
In Progress
A ticket that is actively being developed.
State
In Review
A ticket that has had some code completed to implement but is waiting to pass peer review and is not yet merged in.
State
Paused
This ticket's work started but wasn't finished. It's on hold (likely in a feature branch) and will be resumed later, either due to a blocker or a delay.
State
Unverified
All new tickets start in this state. A developer may set it to show the ticket is unverified. This means we haven't agreed to work on it. It will either move to a verified state or be closed as wontdo.
State
Verified
The issue has been verified by a developer as legitimate. It will be worked on and verified tickets are now considered part of the backlog.
State
Wont Do
This ticket has been decided it wont be done. This may mean the bug has been determined to not be real (cant verify) or the feature is one we have decided we dont want to adopt.
Type
Automation
Any edits or discussion about the AI automated coding system.
Type
Bug
Something that doesnt work as intended.
Type
Discussion
Anytime a ticket represents a discussion about a subject and doesnt fall into one of the other categories.
Type
Documentation
An error or improvement needed in the documentation.
Type
Epic
Any first tier epic. That is, an epic which contains only issues as children and will not have sub-epics.
Type
Feature
Some new functionality not present.
Type
Legendary
A type of Epic which will contain other Epics.
Type
Refactor
A code change that restructures existing code without changing its external behavior.
Type
Support
Someone needs help using the project.
Type
Task
A generic task that doesnt fit into the other type categories.
Type
Testing
Work exclusively focusing on fixing or expanding testing.
No Label
Type
Bug
Projects
Clear projects
No project
Assignees
aditya (Aditya Chhabra)
aleenaumair (Aleena Umair)
brent.edwards (Brent Edwards)
CoreRasurae (Luis Mendes)
drew (Drew Morris)
eugen.thaci (Eugen Thaci)
freemo (Jeffrey Phillips Freeman)
HAL9000 (HAL 9000)
HAL9001 (HAL9001)
hamza.khyari (Hamza Khyari)
hurui200320 (Rui Hu)
justin.morris
khird (Kyle Hird)
org.cleveragents
Clear assignees
No Assignees
CoreRasurae
Notifications
Due Date
No due date set.
Blocks
#746 test(e2e): E2E acceptance criteria for M6 (v3.5.0) — autonomy hardening
cleveragents/cleveragents-core
#1026 fix: Failing E2E tests on master after plan execute cache-sharing fix
cleveragents/cleveragents-core
Reference: cleveragents/cleveragents-core#1027
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "test/e2e-m6-acceptance"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The
plan executeCLI command failed with "Plan is not in an executable state (current: strategize/queued)" after the strategize phase completed successfully. The root cause was that_get_plan_executor()created a secondPlanLifecycleServiceFactory instance with its own in-memory_planscache. After the executor'srun_strategize()advanced the plan toexecute/queued(viaauto_progress), the CLI handler's separate service instance returned stalestrategize/queuedstate from its cache.Changes
_get_plan_executor()now accepts an optionallifecycle_serviceparameter. Theplan executehandler passes its own service instance so the CLI handler andPlanExecutorshare the same cache, eliminating stale-state reads after phase transitions.lifecycle_serviceparameter is typed asPlanLifecycleService | Noneinstead ofAny | None, enforcing correct usage at the call site via static analysis (Pyright).Plan execute shares lifecycle service instance with executor) that asserts_get_plan_executoris called with the same lifecycle service object the CLI handler created, preventing silent regressions.docs/reference/di.md(cache-sharing caveat),docs/reference/plan_cli.md(internal wiring section), anddocs/reference/plan_execute.md(CLI executor wiring detail and typed signature).Verification
All default
noxsessions pass:lint,format,typecheck,unit_tests,docs,build,security_scan,dead_code.Closes #746
Closes #1026
PR Review: !1027 (No corresponding ticket — urgent pipeline fix)
Verdict: Approve ✅
The code fix is technically correct, minimal, and well-documented. It properly resolves the stale-cache bug by sharing a single
PlanLifecycleServicefactory instance. No functional, security, or performance concerns. Process and test coverage observations are noted below for follow-up but are non-blocking given the urgency.Critical Issues
None.
Major Issues
None.
Minor Issues (non-blocking observations)
1. No tests protect the fix (follow-up recommended)
_get_plan_executor(lifecycle_service=service)is recommended.2. Type safety:
Anyinstead of concrete typesrc/cleveragents/cli/commands/plan.py, line 1204 —lifecycle_service: Any | Noneshould ideally bePlanLifecycleService | None. Pre-existing pattern in this function.3. Commit message body describes changes not in the diff
4. Branch name / PR process conventions
test/e2e-m6-acceptancedoesn't followbugfix/convention. PR body is empty. Milestone is v3.2.0 but references issue #746 (v3.5.0).Nits
5. Doc table inconsistency —
plan_cli.mdsummary table still says "Transition to Execute phase" but the detail section was updated.6. Doc snippet missing type annotations —
plan_execute.mdcode example omits annotations present in real code.Summary
The fix correctly shares a single
PlanLifecycleServiceinstance between the CLI handler andPlanExecutor, eliminating the stale in-memory cache that caused "Plan is not in an executable state" errors. The change is surgical (+10/−3 lines of code), well-documented (CHANGELOG + 3 reference docs), and introduces no risks. Approved for merge as an urgent fix. Process and test gaps should be addressed in follow-up work.PR #1027 Review Response Report
Reviewer: @hurui200320 (Rui Hu)
Review verdict: Approved ✅
Review date: 2026-03-17T11:44:48Z
Items Addressed
1. Minor Issue #1 — No tests protect the fix
Reviewer statement:
Justification for addressing:
The reviewer's observation is correct and is directly supported by two project policies:
The fix introduced a new behavioral contract —
_get_plan_executor()must receive the caller'sPlanLifecycleServiceinstance to avoid stale-cache reads — but no test enforced this contract. If thelifecycle_service=serviceargument were accidentally removed in a future refactoring, all existing tests would still pass, silently reintroducing the bug.What was done:
Plan execute shares lifecycle service instance with executortofeatures/plan_lifecycle_cli_coverage.feature(the existing feature file that already coversplan executeCLI behavior, per CONTRIBUTING.md § BDD Test Organization: "Group new steps with related ones").features/steps/plan_lifecycle_cli_steps.py:step_plan_execute_verify_sharing— runs the CLI handler with_get_plan_executorpatched as a spy, capturing call arguments.step_executor_shares_lifecycle_service— asserts that_get_plan_executorwas called withlifecycle_service=pointing to the same object returned by_get_lifecycle_service().nox -s unit_tests).2. Minor Issue #2 — Type safety:
Anyinstead of concrete typeReviewer statement:
Justification for addressing:
The reviewer's observation is correct and is directly mandated by project policy:
Using
Anyfor a parameter that is always expected to bePlanLifecycleService | Noneeffectively bypasses the static type checker's ability to catch incorrect usage at the call site. The file already usesfrom __future__ import annotations(line 32), so the annotation is a string at runtime — meaning the import can safely go insideTYPE_CHECKINGwith zero runtime cost or circular-import risk.What was done:
PlanLifecycleServiceto the existingif TYPE_CHECKING:block atplan.py:83.lifecycle_service: Any | None = Nonetolifecycle_service: PlanLifecycleService | None = None.docs/reference/plan_execute.mdto reflect the new typed signature.nox -s typecheck(Pyright): 0 errors, 1 pre-existing warning (unrelated).3. Minor Issue #3 — Commit message body describes changes not in the diff
Reviewer statement:
Justification for addressing:
The reviewer's observation is correct. The original commit message body stated:
However, the diff only contained the
lifecycle_servicesharing fix (+10/−3 lines inplan.py, plus docs and changelog). The other three claimed changes are not present in the diff. This is directly against:A misleading commit message makes
git log,git bisect, and code archaeology unreliable.What was done:
fix(cli): share PlanLifecycleService instance between CLI handler and PlanExecutor), and whose body accurately describes only the changes present in the diff: the stale-cache root cause, the fix, and the review-feedback improvements (type safety, regression test, doc update). TheISSUES CLOSED: #746footer was preserved.Items Not Addressed
4. Minor Issue #4 — Branch name / PR process conventions
Reviewer statement:
Justification for not addressing:
This comment concerns PR metadata and branch-naming conventions — process and administrative items that are outside the scope of code fixes.
test/e2e-m6-acceptanceas the head ref). Renaming would require closing and re-creating the PR.Branch: test/e2e-m6-acceptance), which was set by a project owner. Changing it unilaterally would contradict the issue specification.5. Nit #5 — Doc table inconsistency
Reviewer statement:
Justification for not addressing:
The reviewer themselves labelled this as a Nit, indicating it is a non-blocking cosmetic observation. The inconsistency between the summary table header text and the expanded detail section in
docs/reference/plan_cli.mddoes not affect code behavior, type safety, or test correctness. It can be addressed in a future documentation cleanup pass.6. Nit #6 — Doc snippet missing type annotations
Reviewer statement:
Justification for not addressing:
The reviewer themselves labelled this as a Nit. The code snippet in the documentation is a simplified illustration; omitting some annotations is a common documentation practice for readability. Note that the specific snippet the reviewer refers to (the general
_get_plan_executorsignature) was updated as part of Fix #2 to reflect thePlanLifecycleService | Nonetype change, but the broader annotation gaps in other doc snippets throughout the file were not addressed since they are pre-existing and marked as a nit.Verification Summary
lintformattypecheckunit_tests(pertinent feature)docsbuildsecurity_scandead_codeFiles Changed
src/cleveragents/cli/commands/plan.pyTYPE_CHECKINGimportfeatures/plan_lifecycle_cli_coverage.featurefeatures/steps/plan_lifecycle_cli_steps.pydocs/reference/plan_execute.mdCHANGELOG.md2098491fb5toff2d824f17New commits pushed, approval review dismissed automatically according to repository settings