Skip to content

docs(orchestration): deprecate WorkflowPlanner in place with a test that holds the invariant (#13751) - #13760

Merged
mrveiss merged 4 commits into
Dev_new_guifrom
issue-13751
Aug 8, 2026
Merged

mrveiss merged 4 commits into
Dev_new_guifrom
issue-13751

Conversation

@mrveiss

@mrveiss mrveiss commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Closes #13751

Thinking Path

#13751 offered two branches: wire the two unreachable methods into a request path, or
record why the capability is superseded, naming the superseding code. Investigation settled
it decisively toward the second — and turned up more than the issue described.

The scope is bigger than two methods. create_plan_summary_for_approval also has zero
callers, so every public method on WorkflowPlanner is unreachable. Only __init__ runs,
via orchestrator._step_planner, which is never called on.

There is a precedent, and this is its missed sibling. run_workflow's docstring records
that #5058 removed the orchestration.WorkflowPlanner/WorkflowExecutor path. #12373/#12579
then "declare[d] services WorkflowExecutor canonical, deprecate[d] dead orchestration engine
in place" — the executor half. It never reached the planner half. This PR applies the same
convention to the sibling it missed.

Wiring would be the wrong move. Every capability already exists in wired code:

WorkflowPlanner capability Canonical, wired equivalent
base plan + capability→agent assignment orchestrator.create_workflow_plan + AgentRouter.get_agent_recommendations_scored (via WorkflowRunner)
similar-trajectory priors (GH#7357) orchestrator._fetch_planning_context (#10580/#10581), tenant-scoped by #11015/#11089
plan without executing orchestrator.create_workflow_plan — builds and stores a WorkflowPlan, does not run it
approval presentation services.workflow_automation.executor.WorkflowExecutor.present_plan_for_approval, via WorkflowAutomationManager (#390)

Unlike the executor — which #12579 kept for genuinely unique DAG/checkpoint/sub-workflow
capabilities under #12577 — this module has nothing the canonical path lacks. Wiring it
would recreate a second planning path for the two to drift against, which is precisely what
#13751 was filed to prevent.

This module's status has now surfaced three times. #6820 tracked it as one of four
orphaned modules and closed 2026-05-17 having wired three and left this one. #12579 deprecated
its sibling and skipped it. Both prior passes recorded intent in prose that nothing held —
which is the actual root cause of the recurrence, and why this PR adds a test.

What Changed

orchestration/workflow_planner.py — module and class docstrings record the deprecation
with the per-capability supersession table above, and state explicitly that wiring it in is
not the intended fix. All code retained.

orchestration/__init__.py — package docstring marks workflow_planner deprecated,
mirroring the existing workflow_executor note. Re-export kept, exactly as #12579 kept the
executor's.

orchestration/workflow_planning.py — corrects a stale claim that inverted reality:
it called WorkflowPlanner "the canonical (but currently unwired)" planner. It is not
canonical; StrategyPlanner in that very module is the wired one
(orchestrator._strategy_planner, the create_workflow_plan fallback).

orchestrator.py — the comment on _step_planner implied a live collaborator. Now
records that no method is ever invoked, and why the attribute is retained anyway.

repo_tests/workflow_planner_deprecation_test.py — new; the guard #12579 lacked.

Verification

$ python3 -m pytest repo_tests/workflow_planner_deprecation_test.py -q -p no:randomly
5 passed in 8.33s

Each guard proven to catch its own regression — five deliberate breakages, each failing
in its own distinct test rather than one blanket assertion:

Deliberate breakage Test that caught it
wire self._step_planner.get_plan_summary(...) test_planner_instance_is_constructed_but_never_called
reference plan_workflow_steps_with_agents in production test_unique_planner_methods_have_no_production_call_sites
new production file imports WorkflowPlanner test_only_known_files_reference_the_planner
strip the DEPRECATED notice test_module_and_package_record_the_deprecation
delete estimate_step_duration test_deprecated_code_is_retained_in_full

That last guard matters: it makes "deprecate in place" enforceable, so this can never quietly
become a deletion.

One trap worth naming: the guard deliberately does not scan for the bare name
get_plan_summary. OverseerAgent defines an unrelated method with that name which is
wired via api/overseer_handlers.py, so a name-only scan false-positives. Reachability is
pinned through the instance attribute (AST, distinguishing the assignment from a call) and
the import allow-list instead.

Regression sweep:

$ python3 -m pytest orchestration/ tests/orchestration/ services/workflow_automation/ \
    services/advanced_workflow/ performance_benchmarks_test.py dependency_injection_test.py -q -p no:randomly
598 passed

$ python3 -m pytest repo_tests/ -q -p no:randomly
316 passed

Local Python is 3.10 with backend deps partially absent — CI is the authority.

Decision

Taking the "record superseded" branch of #13751's own acceptance criteria rather than the
"wire it in" branch. Reversible, and flagged rather than buried: if you would rather these
capabilities be wired and the canonical path retired instead, say so and I will invert it —
but that is a larger architecture change than #13751 scoped, and it would need #12577's
consolidation to lead.

AC 3 needs reinterpreting, and I want that visible rather than silently ticked. It asks
for "a test [that] exercises each through its production entry point". Under the supersession
finding there is no production entry point, so that AC is unsatisfiable as written. The
guard test above is what replaces it: it pins the absence of a production path, so the
finding cannot rot. #13730 already added tests that drive both methods directly, so the code
itself is covered.

Model Used

Claude Opus 5 (1M context)

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No hardcoded values detected that have SSOT config equivalents!

mrveiss added 4 commits August 8, 2026 19:40
…supersedes each capability (#13751)

Every public method on orchestration.WorkflowPlanner has zero callers. It is the
planner half of the engine orchestrator.run_workflow stopped using at #5058;
#12373/#12579 deprecated the executor half in place and did not reach this
sibling. Orchestrator still constructs it as self._step_planner but never calls
a method on it.

Unlike the executor, it holds no capability the canonical path lacks, so nothing
here is staged for consolidation. Each capability maps to wired code:
plan_workflow_steps_with_agents -> create_workflow_plan plus
AgentRouter.get_agent_recommendations_scored; trajectory priors ->
_fetch_planning_context (#10580/#10581, tenant-scoped #11015/#11089);
get_plan_summary -> create_workflow_plan, which plans without executing;
create_plan_summary_for_approval -> the wired services WorkflowExecutor
.present_plan_for_approval (#390).

Wiring it in was the alternative and is the wrong move: it would recreate a
second planning path for the two to drift against, which is what #13751 was
filed to avoid.

Also corrects two stale claims. workflow_planning.py called WorkflowPlanner
"the canonical (but currently unwired)" planner, inverting the relationship —
StrategyPlanner there is the wired one. And the orchestrator comment implied
_step_planner was a live collaborator. All code retained; nothing deleted.

Prior tracking was #6820, which closed 2026-05-17 having wired three of its four
orphaned modules and left this one.
…13751)

#12579 recorded the sibling executor's deprecation in docstrings only, with
nothing holding the claim, so it could go stale in either direction — wired
while still documented dead, or the note dropped while still true. That is
partly why the planner half went unnoticed through #6820 and #12579.

Five guards, each verified by deliberate failure:
- no attribute access through orchestrator._step_planner (AST, so the
  construction assignment is distinguished from a call)
- the two uniquely-named deprecated methods appear nowhere in production
- an allow-list of the three files permitted to mention the class, each with its
  reason, failing in both directions
- the module and package docstrings still carry the notice AND name the
  canonical replacements
- the class and all five public methods still exist — deprecation must not
  become quiet deletion

Deliberately does not scan for the bare name get_plan_summary: OverseerAgent
defines an unrelated wired method with the same name, so a name-only scan would
false-positive on api/overseer_handlers.py.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant