Skip to content

fix(chat): the workflow planner returned a fixed skeleton and auto-started it without approval (#13809) - #17947

Merged
mrveiss merged 9 commits into
mainfrom
issue-13809-chat-workflow-planner
Oct 4, 2026
Merged

mrveiss merged 9 commits into
mainfrom
issue-13809-chat-workflow-planner

Conversation

@mrveiss

@mrveiss mrveiss commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Closes #13809

Thinking Path

/api/workflow-automation/create_from_chat returned the same three placeholder steps for every request: plan_workflow_steps is a fixed skeleton, and every step fell through to echo 'Executing: <action>'. Before #13730 the path failed with a 500; afterwards it succeeded silently, doing nothing.

The issue recommended routing to create_workflow_plan, the canonical LLM planner. Reading that planner's prompt showed the recommendation alone would still have left the endpoint hollow: the prompt asks only for agent tasks {agent, action, inputs: {}}, never a shell command. Rerouting would have produced request-shaped actions whose steps still all echoed. Both points went to the owner, whose ruling is recorded on #13809 before any code:

  1. Chat plans with create_workflow_plan, the one canonical planner (AC4).
  2. An opt-in command contract on the shared planning prompt. Only this path turns it on, so every other caller's prompt is byte-identical.
  3. The server default becomes require_approval=True, auto_start=False (AC3). The frontend's only caller (useWorkflowBuilder.ts) already sends requireApproval=true, so this removes a server/client divergence rather than tightening a default anyone relies on.
  4. Every step whose command the LLM wrote carries requires_confirmation=True, a property of the step, whatever the route defaults are.
  5. The test "user text never reaches a generated command" held only because the endpoint was hollow. It is replaced by the property that holds with a real planner: no generated command runs without human approval.

A plan with no executable command, including the planner's own fallback when the LLM fails, now returns no workflow (a visible failure) rather than an echo-only one.

What Changed

File Change
orchestration/orchestrator_prompts.py build_planning_prompt(shell_commands=False): appends _SHELL_COMMAND_CONTRACT only when asked
orchestrator.py _build_planning_prompt(goal, planning_ctx); create_workflow_plan reads context["shell_commands"]. The file is 6 lines shorter (1114 → 1108).
repo_tests/python_file_size_ratchet_baseline.py, scripts/python_file_size_known_large.py orchestrator.py ceiling lowered to 1108 in both
services/workflow_automation/manager.py chat → create_workflow_plan with the contract on; _steps_from_plan keeps only commanded tasks, all requires_confirmation=True; no command → None
services/workflow_automation/routes.py create_from_chat defaults auto_start=False, require_approval=True, reasoning recorded at the default
tests/services/test_chat_workflow_is_request_shaped.py xfail markers and the fixed-skeleton test removed; 6 tests drive the real prompt → parse → plan chain with only the LLM faked (the fake derives its plan from the Goal line it finds in the prompt)
tests/orchestration/test_planning_caller_wiring.py, takeover_manager_e2e_test.py moved onto the new path

Knowingly left behind, filed as #17910: _extract_command_from_step's keyword fallbacks (e.g. any "install" action → sudo apt install -y git curl wget) and classify_request_complexity_verdict now have no live caller. Neither is deleted.

Verification

  • Tests re-run on the tree rebased onto current main (deployed backend interpreter, python 3.14.6; CI is the authority):
    386 passed across orchestration/, services/workflow_automation/, the chat-workflow file and test_planning_caller_wiring;
    11 passed in takeover_manager_e2e_test.py; 14 passed in the two prompt suites
    (test_learned_prompt_template_wiring, test_ledger_vs_executor); 31 passed in
    python_file_size_ratchet_test.py, which is what proves the two baselines carry both this
    branch's entry and the one that landed meanwhile.
  • Mutations, each reddening a named test:
Mutant Fails
requires_confirmation=False test_a_generated_command_requires_confirmation_on_its_step
contract off (shell_commands: False) test_the_chat_path_asks_the_planner_for_commands
old route defaults test_the_route_starts_nothing_unless_asked
hollow plan accepted test_a_plan_with_no_command_creates_no_workflow
old skeleton planner restored 4 tests, including both former xfails
  • pre-commit run --from-ref origin/main: 0 failed, as recorded pre-rebase. Not re-run after the
    rebase: the 7 code files are byte-identical and the only merged region is the two baseline dicts,
    whose own guard test passes.
  • Reversal check — not empty, and it mattered. git log 53803bddca..origin/main -- <each changed file>
    returns 0 commits for all 7 code files, but 1 for each of the two ratchet baselines:
    the schemas_chat.py ceiling moved 732 -> 702. The pre-rebase tip still carried 732,
    so merging it unrebased would have reversed that. Resolved in favour of the newer value:
    the rebased baselines hold schemas_chat.py: 702 and orchestrator.py: 1108, and
    python_file_size_ratchet_test.py passes (31) against the real tree.
    Every one of the 9 changed files other than those two baselines is byte-identical to the
    pre-rebase commit (blob-hash compared), so nothing else moved.

Model Used

Claude Opus 5.5

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Chat requests now generate workflow plans with executable shell commands based on the request.
    • Plans are presented for approval by default. Workflows start immediately only when approval is explicitly bypassed.
  • Bug Fixes
    • Planned tasks without an executable command are excluded. If no executable commands remain, no workflow is created.
    • Dependencies are mapped between retained tasks, and every generated workflow step requires confirmation before it runs.

…e every generated command, default to approval (#13809)
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Chat-driven workflow creation now uses the orchestrator planner with a shell-command contract. The manager converts tasks with commands into steps that require confirmation. The route defaults to plan approval and returns None when the plan has no executable steps.

Changes

Chat Workflow Planning

Layer / File(s) Summary
Shell-command planning contract
autobot-backend/orchestration/orchestrator_prompts.py, autobot-backend/orchestrator.py
The prompt builder adds the shell-command contract when enabled. The orchestrator passes planning context, including the shell-command option, to the prompt builder.
Convert plans into confirmed workflow steps
autobot-backend/services/workflow_automation/manager.py, autobot-backend/takeover_manager_e2e_test.py, autobot-backend/tests/orchestration/test_planning_caller_wiring.py, autobot-backend/tests/services/test_chat_workflow_is_request_shaped.py
The chat manager uses create_workflow_plan, retains tasks with non-empty commands, maps dependencies between retained tasks, and requires confirmation for each generated step. Tests cover request-shaped commands, missing commands, and task conversion.
Default to plan approval
autobot-backend/services/workflow_automation/routes.py, autobot-backend/tests/services/test_chat_workflow_is_request_shaped.py
The route defaults to auto_start=False and require_approval=True. A route test checks that the plan is presented for approval without starting execution.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🔵 Low · up to b41db

Malformed commands can appear in chat-created workflows. The current execution path limits the impact, but the conversion should be corrected.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to b41db

Approval is safer by default, but generated plans can lose prerequisite relationships during conversion. Real command execution is not demonstrated by the available code, which limits confirmed impact while leaving execution-safety guarantees unresolved.

Retained concerns

  • Medium · architecture · inferred: The new planner-to-chat conversion can silently erase declared completion prerequisites. The response schema requests dependency IDs without defining task IDs; the builder assigns fresh UUID-based IDs while copying dependency strings unchanged, and conversion retains only references matching those generated IDs. Ordinary list-order execution mitigates straightforward plans, but does not restore dependency-completion gates. This newly exposed contract mismatch can undermine failure containment when a prerequisite is needed for safe execution; no concrete privileged-command exploit is established.
Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is authenticated creation of command-bearing workflow state and subsequent owner-keyed secret resolution. Checked control routes restrict workflow actions to the owner or an administrator. Actual host privileges, sandbox boundaries, network reach, and production data-store exposure cannot be derived from the simulated sink.

Security Findings and Attack Paths

  • observed — The existing direct step-approval path checks the current step ID but does not itself require a waiting-approval state, an approved plan, or a non-cancelled workflow. This permits execution-shaped transitions outside the normal plan-first sequence, but still involves an authorized approval request. The behavior predates this PR, and neither cross-owner execution nor real shell execution is established; it is not retained as an introduced authorization finding.

Trust Boundaries and Controls

  • observed — Chat creation records the authenticated caller as owner. Checked plan-approval and control routes use the shared owner-or-admin lookup; ownerless workflows are admin-only. Normal scheduling requests confirmation for generated steps. These controls counter an inference that changing the planner alone enables automatic or cross-user command execution.

Resilience and Maintainability Implications

  • observed — Existing approval records remain addressable after resolution, start_execution has no already-started guard, and step execution lacks an atomic single-consumption check. Cancellation stops normal scheduling, but in-flight finalization does not recheck cancellation. These pre-existing transition properties limit the strength of replay, concurrency, and interruption guarantees; production command consequences remain unverified.

Hardening Proposals

  • proposed — Define planner-visible task IDs, bind dependency references to generated tasks, and reject unresolved or removed prerequisites rather than silently converting them into dependency-free steps.
  • proposed — Before connecting a real command sink, enforce approval and lifecycle state at the execution boundary with atomic, single-use transitions bound to the approved step content. Preserve cancellation and replay guarantees, and establish the production identity, session, credential, and sandbox boundary explicitly.
🚥 Pre-merge checks | ✅ 4 | ❓ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The planner changes, approval defaults and related tests support #13809. The PR description links the file-size baseline edits to the orchestrator.py reduction and a newer main-branch baseline. Howe… Reviewable evidence of the exact edits in repo_tests/python_file_size_ratchet_baseline.py and scripts/python_file_size_known_large.py, or independent evidence establishing their connection to this PR, is needed to decide whether those c…
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the fixed chat workflow plan and the change to approval before execution.
Linked Issues check ✅ Passed Issue #13809 requires request-shaped chat plans, a test that distinguishes requests, a planner decision and a decision on the startup default. The chat path uses create_workflow_plan with the comman…
Docstring Coverage ✅ Passed Docstring coverage is 81.48% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 7 files.
Full details: Out of Scope Changes check

Explanation

The planner changes, approval defaults and related tests support #13809. The PR description links the file-size baseline edits to the orchestrator.py reduction and a newer main-branch baseline. However, repo_tests/python_file_size_ratchet_baseline.py and scripts/python_file_size_known_large.py are excluded from review. The available summary does not represent their contents, so I cannot verify whether those edits stay within scope.

Resolution

Reviewable evidence of the exact edits in repo_tests/python_file_size_ratchet_baseline.py and scripts/python_file_size_known_large.py, or independent evidence establishing their connection to this PR, is needed to decide whether those changes are in scope.

✨ Finishing Touches 💡 1
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No new hardcoded values of either class — ssot and other both block.

Known backlog in pipeline-scripts/hardcoded_values_baseline.txt is suppressed and tracked in #14371.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 15d5f80c-7d66-4a43-b2d9-bcdae6e59a49
📥 Commits

Reviewing files that changed from the base of the PR and between c8c025b and 4afbb0b.

⛔ Files ignored due to path filters (2)
  • repo_tests/python_file_size_ratchet_baseline.py is excluded by !repo_tests/python_file_size_ratchet_baseline.py
  • scripts/python_file_size_known_large.py is excluded by !scripts/python_file_size_known_large.py
📒 Files selected for processing (4)
  • autobot-backend/orchestration/orchestrator_prompts.py
  • autobot-backend/orchestrator.py
  • autobot-backend/services/workflow_automation/manager.py
  • autobot-backend/takeover_manager_e2e_test.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread autobot-backend/services/workflow_automation/manager.py Outdated
Comment thread autobot-backend/services/workflow_automation/manager.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
autobot-backend/services/workflow_automation/manager.py (2)

118-118: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Accept only non-empty string commands.

Line 118 keeps any task whose command is truthy. A list or dict passes this filter, and WorkflowStep.command does not check its type at runtime. Accept a command only when it is a string with non-blank content after stripping.


130-130: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Planner dependency references can be lost.

build_workflow_plan assigns its own task IDs. LLM dependency references such as task_0 therefore do not match step_ids, and line 130 drops them without a warning. A dependent command can then run without its prerequisite. Before conversion, resolve each dependency reference to a generated task ID. If a reference cannot be resolved, log it.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: mrveiss/AutoBot-AI/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 8392976c-beae-490f-9170-8a322cd83492
📥 Commits

Reviewing files that changed from the base of the PR and between 12e945d and b41db2f.

⛔ Files ignored due to path filters (2)
  • repo_tests/python_file_size_ratchet_baseline.py is excluded by !repo_tests/python_file_size_ratchet_baseline.py
  • scripts/python_file_size_known_large.py is excluded by !scripts/python_file_size_known_large.py
📒 Files selected for processing (4)
  • autobot-backend/orchestration/orchestrator_prompts.py
  • autobot-backend/orchestrator.py
  • autobot-backend/services/workflow_automation/manager.py
  • autobot-backend/takeover_manager_e2e_test.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

github-actions Bot and others added 4 commits October 4, 2026 19:16
… in silence (#13809)

Both review threads on #17947, and they are the same shape: a filter that was
right about what to exclude and wrong about doing it quietly.

COMMANDS. `inputs.get("command")` was filtered on truthiness alone, so a
non-empty list, dict or int passed it and travelled into `WorkflowStep.command`
-- declared `str`, not validated at runtime -- and from there into workflow
status and snapshots. A non-dict `inputs` raised `AttributeError` into the
broad handler above and became a plain `return None`, which is the #13730
shape of an error vanishing into a "no workflow" result. `_planned_command`
now requires a non-empty `str` and tolerates a malformed `inputs`.

Measured against the old filter rather than assumed: of the eight rejected
shapes, truthiness admitted five (list, dict, int, whitespace-only, and
non-dict inputs by raising).

DEPENDENCIES. The planner copies the model's `dependencies` through unchanged,
and the model has no id to reference -- the schema asks for `["task_ids"]`
while giving it no id field to assign, and ids are generated server-side after
it answers. So every reference it writes is unresolvable by construction, and
filtering them left steps that look independent: a plan that said "B waits for
A" became indistinguishable from one that stated no ordering. The filter stays,
because an id that resolves to nothing cannot be ordered against, but the
unresolvable ids are now named in the log.

Root cause is in the prompt shared by every planner caller, so it is filed as
#17979 rather than fixed here; this PR makes the symptom visible and points at
it.

VERIFICATION, stated rather than overstated. This test module cannot run on
this machine: it imports `StrEnum`, which needs 3.11+, and no interpreter here
that has it also has pytest. CI (3.14) is the first place the new tests
execute. What I did verify locally, by lifting the logic out of the module and
exercising it directly: `_planned_command` is correct on all nine input shapes,
and the dependency resolution reports exactly the unresolvable id, keeps the
resolvable one, and stays silent when everything resolves.

Refs #13809, #17979
#13809)

`verify-generated-types` is a required check and it was red: this PR changed
the `create_from_chat` route docstring, FastAPI renders a route docstring into
the OpenAPI description, and the generated client types therefore drifted by
exactly that one line.

Applied from the `generated-types-patch` artifact that the drift job publishes
rather than regenerating locally. Regenerating needs the backend's OpenAPI
schema built, which means importing the application -- and the project rule is
not to run codebase code outside CI or the deployment. The artifact is the
sanctioned route and the job exists to provide it (#15362).

The whole diff is the added description line; no type or shape changed.

Refs #13809, #15362
@mrveiss
mrveiss merged commit e2487c3 into main Oct 4, 2026
81 checks passed
@mrveiss
mrveiss deleted the issue-13809-chat-workflow-planner branch October 4, 2026 21:04
@mrveiss
mrveiss restored the issue-13809-chat-workflow-planner branch October 12, 2026 13:41
@mrveiss
mrveiss deleted the issue-13809-chat-workflow-planner branch October 12, 2026 13:58
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.

bug(workflow-automation): chat-driven workflow creation now succeeds but returns three fixed echo steps for every request

1 participant