Skip to content

fix(api): let an app move from a folder back to the top level - #8077

Merged
mmabrouk merged 2 commits into
release/v0.128.7from
fix/workflow-move-to-root
Nov 4, 2026
Merged

mmabrouk merged 2 commits into
release/v0.128.7from
fix/workflow-move-to-root

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Nov 3, 2026

Copy link
Copy Markdown
Member

API change. Not for v0.128.5; next release.

Context

An app moved into a folder cannot be moved back out. The /m Move dialog lists only folders, and /w has no top-level choice either. The API also had no way to clear a folder. edit_workflow dumps the edit with exclude_none=True, so a folder_id: null in the body was dropped and the app stayed in its folder.

Changes

API:

  • edit_workflow keeps a folder_id that the caller sent, including null. A null moves the item to the top level. An edit without folder_id leaves the folder as it is.
  • edit_application and edit_evaluator build a WorkflowEdit from a full dump of their own edit model. With the change above, that dump would send folder_id: null on every rename. They now pass folder_id on only when their caller sent it.

/m:

  • The Move dialog starts with an "All prompts" row (the word the breadcrumb uses). For a prompt inside a folder, it sends {"workflow": {"id": "<id>", "folder_id": null}}.
  • Folders still cannot move to the top level, because the folders API keeps the parent on a null parent_id. The row is greyed out for a folder.

Shared package (@agenta/entities): isValidMoveTarget accepts a null destination for a prompt in a folder, and moveWorkflowToFolder accepts a null folder id. Only /m calls them, and the /w type check passes.

Callers checked

Before this change, folder_id: null in an edit body did nothing. After it, a null clears the folder. I checked every caller that could send one:

  • API services. The simple edits (/simple/workflows, /simple/applications, /simple/evaluators) and the skills import build their edit without folder_id, so it is not sent. The PUT /applications/{id} and PUT /evaluators/{id} routes now pass folder_id on only when the request body has it.
  • /w frontend. updateAppName and the shared updateWorkflow send no folder_id. updateAppFolder sends one, but the /w prompts page calls it only with a folder id and refuses an empty destination. No /w code calls the application or evaluator edit routes.
  • /m and the shared packages. moveWorkflowToFolder is the only caller that sends folder_id, and it sends null only from the new "All prompts" choice.
  • Python SDK. The managers call the /simple/... routes, which have no folder_id. The agent rename_agent op calls PUT /workflows/{id}, but its input schema allows only name and description. The generated client folder is empty, so it makes no calls.

No caller sends back a full object it read, so none clears a folder by accident.

Tests

  • API unit tests in api/oss/tests/pytest/unit/workflows/test_edit_workflow_folder.py parse the JSON body the way the router does. They check that a null folder_id moves the item to the top level, that a folder_id moves it into that folder, and that a rename without folder_id keeps the folder for a workflow, an application and an evaluator. The null test fails on the old service code. The workflows, applications and evaluators unit suites pass (860 tests) through run-tests.py.
  • /m: the prompts screen test runs the real package code with only fetch mocked and asserts the exact PUT /workflows/{id} body. Dialog and package tests cover the new row and the move rules.
  • /m and /w type checks pass. ruff format and ruff check are clean.

What to QA

  • In /m Prompts (developer view prompts), move an app into a folder. Then open Move again and pick All prompts. The app shows at the top level, also after a reload.
  • In /w, rename an app that sits in a folder. It stays in the folder.
  • Rename an evaluator in /w. It keeps its folder, if it has one.

An app moved into a folder could not be moved back out. The workflow edit
dropped a null folder_id (exclude_none), so there was no way to clear it.
edit_workflow now keeps a folder_id the caller sent, including null.
edit_application and edit_evaluator pass folder_id on only when their caller
sent it, so a rename never moves an app or an evaluator.

/m: the Move dialog gets an All prompts row that sends folder_id null.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 29 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 8 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 1d7a73c and 1d7a73c.

Files selected for processing: 12
  • api/oss/src/core/applications/service.py
  • api/oss/src/core/evaluators/service.py
  • api/oss/src/core/workflows/service.py
  • api/oss/tests/pytest/unit/workflows/test_edit_workflow_folder.py
  • web/mobile/src/features/prompts/dialogs/MoveToFolderDialog.tsx
  • web/mobile/src/features/prompts/usePromptFolderActions.ts
  • web/mobile/tests/unit/promptDialogs.render.test.tsx
  • web/mobile/tests/unit/promptsScreen.render.test.tsx
  • web/packages/agenta-entities/src/workflow/folder/api.ts
  • web/packages/agenta-entities/src/workflow/folder/tree.ts
  • web/packages/agenta-entities/tests/unit/workflowFolderApi.test.ts
  • web/packages/agenta-entities/tests/unit/workflowFolderTree.test.ts

No actionable comments were generated in the recent review.

Summary

Summary by CodeRabbit

  • New Features
    • Prompts can now be moved from a folder to the top-level “All prompts” location by selecting it as the destination.
  • Bug Fixes
    • Moving workflows, applications, and evaluators to the top level now correctly clears their folder assignment.
    • Edits that omit a folder destination leave the current folder unchanged; explicitly choosing the top level clears it.
    • Prompts already at the top level cannot be moved to the same location.

The prompt move flow now supports moving a prompt from a folder to the top level. The dialog passes a null folder ID, and the edit services preserve explicitly supplied null values.

Changes: Top-level prompt move

Layer / File(s) Summary
Top-level destination contract
web/packages/agenta-entities/src/workflow/folder/tree.ts, web/packages/agenta-entities/src/workflow/folder/api.ts, web/packages/agenta-entities/tests/unit/workflowFolderApi.test.ts, web/packages/agenta-entities/tests/unit/workflowFolderTree.test.ts
isValidMoveTarget allows a prompt in a folder to move to the top level. moveWorkflowToFolder accepts and forwards a null folder ID. Tests cover destination eligibility and the null request value.
Dialog selection and move request
web/mobile/src/features/prompts/dialogs/MoveToFolderDialog.tsx, web/mobile/src/features/prompts/usePromptFolderActions.ts, web/mobile/tests/unit/promptDialogs.render.test.tsx, web/mobile/tests/unit/promptsScreen.render.test.tsx
The dialog displays the top-level destination and passes null when it is selected. The action sends the destination to target validation. Tests cover selecting the root and sending a request with folder_id: null.
Preserve explicit null folder edits
api/oss/src/core/applications/service.py, api/oss/src/core/evaluators/service.py, api/oss/src/core/workflows/service.py, api/oss/tests/pytest/unit/workflows/test_edit_workflow_folder.py
Application, evaluator, and workflow edit services preserve folder_id when callers explicitly set it to null. Tests check null, non-null, and omitted folder IDs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MoveToFolderDialog
  participant usePromptFolderActions
  participant moveWorkflowToFolder
  participant WorkflowsService
  User->>MoveToFolderDialog: Select top-level destination and confirm
  MoveToFolderDialog->>usePromptFolderActions: Pass null destination
  usePromptFolderActions->>moveWorkflowToFolder: Move prompt to top level
  moveWorkflowToFolder->>WorkflowsService: Send edit with folder_id null
Loading
Merge Risk: Low · up to 1d7a7

The move-to-top-level behavior is implemented correctly; only a narrow regression-test gap remains for application and evaluator wrappers.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: allowing an application to move from a folder to the top level.
Description check Passed The description directly explains the API, dialog, validation, tests, and QA changes for moving prompts to the top level.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 11 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · 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
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between daf88ee and 1d7a73c.

Files selected for processing: 3
  • api/oss/src/core/applications/service.py
  • api/oss/src/core/evaluators/service.py
  • api/oss/src/core/workflows/service.py

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

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Website preview

Preview URL: https://pr-8077-agenta-website-preview.mahmoud-637.workers.dev

Built from 1d7a73c839665bde18afabd7911dec6184dc4878. This comment updates in place on every push.

@mmabrouk

mmabrouk commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

🤖 The AI agent says:

Review loop result for head daf88eea32 (base d6ef7781e5), one round on the full diff.

Lens Final rating
Correctness (Codex) Low, no findings
Naming (Codex) Low, no findings

Correctness checked that an explicit folder_id: null reaches the database write and clears the folder, and that edits without folder_id (including application and evaluator edits) keep the folder. Naming checked that folderId maps to folder_id in the request, the DTO and the stored artifact, and that the dialog reuses the existing "All prompts" label.

Fixed: nothing to fix.
Left on purpose: folders still cannot move to the top level, because the folders API keeps the parent on a null parent_id.

@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.

Nitpick comments: 1
api/oss/tests/pytest/unit/workflows/test_edit_workflow_folder.py: 1

68-104: Trivial · Maintainability & Code Quality · Quick win

Cover explicit folder_id=None through both wrapper services.

The application and evaluator tests cover only an omitted folder_id. They do not protect the explicit-null forwarding path. Add one test for each wrapper that asserts folder_id remains present and has value None.

Suggested fix
+@pytest.mark.asyncio
+async def test_an_application_null_folder_id_moves_the_application_to_the_top_level(service):
+    workflows_service, workflow_id = service
+
+    await ApplicationsService(workflows_service=workflows_service).edit_application(
+        project_id=uuid4(),
+        user_id=uuid4(),
+        application_edit=ApplicationEdit(id=workflow_id, folder_id=None),
+    )
+
+    artifact_edit = _artifact_edit(workflows_service)
+    assert "folder_id" in artifact_edit.model_fields_set
+    assert artifact_edit.folder_id is None
+
+
+@pytest.mark.asyncio
+async def test_an_evaluator_null_folder_id_moves_the_evaluator_to_the_top_level(service):
+    workflows_service, workflow_id = service
+
+    await EvaluatorsService(workflows_service=workflows_service).edit_evaluator(
+        project_id=uuid4(),
+        user_id=uuid4(),
+        evaluator_edit=EvaluatorEdit(id=workflow_id, folder_id=None),
+    )
+
+    artifact_edit = _artifact_edit(workflows_service)
+    assert "folder_id" in artifact_edit.model_fields_set
+    assert artifact_edit.folder_id is None
+
+
 @pytest.mark.asyncio
 async def test_an_application_rename_keeps_the_folder(service):
Review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between d6ef778 and daf88ee.

Files selected for processing: 12
  • api/oss/src/core/applications/service.py
  • api/oss/src/core/evaluators/service.py
  • api/oss/src/core/workflows/service.py
  • api/oss/tests/pytest/unit/workflows/test_edit_workflow_folder.py
  • web/mobile/src/features/prompts/dialogs/MoveToFolderDialog.tsx
  • web/mobile/src/features/prompts/usePromptFolderActions.ts
  • web/mobile/tests/unit/promptDialogs.render.test.tsx
  • web/mobile/tests/unit/promptsScreen.render.test.tsx
  • web/packages/agenta-entities/src/workflow/folder/api.ts
  • web/packages/agenta-entities/src/workflow/folder/tree.ts
  • web/packages/agenta-entities/tests/unit/workflowFolderApi.test.ts
  • web/packages/agenta-entities/tests/unit/workflowFolderTree.test.ts
Files with no reviewable changes: 1
  • web/mobile/src/features/prompts/usePromptFolderActions.ts

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

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Item Value
Status stopped
Commit 1d7a73c839665bde18afabd7911dec6184dc4878
Manual expiry (UTC) Not granted
CI deadline (UTC) Not granted
Workflow https://github.com/Agenta-AI/agenta/actions/runs/41852183103

Dependent tests finished. Post /preview for manual review. Environment absence verified; retained storage requires separate provider accounting.

Post /preview as a new comment to request a one-hour preview. Repeating it while active does not extend the hour.
Preview data is disposable. Expiry is enforced by the next successful cleanup sweep, not an exact timer.
Environment deletion stops its compute. Provider-retained storage and permanent services may still be billed.

@mmabrouk

mmabrouk commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

🤖 The AI agent says: Live QA passed on head 1d7a73c (release/v0.128.5 merged in, no conflicts).

Setup: one fresh OSS dev stack built from release/v0.128.5 with #8075, #8076, #8077 and #8086 merged in, developer views playground,prompts,variants,endpoints,observability-app-filter on, a new account, Chromium at 1440 px and 390 px. The API ran this batch's code.

  • /m: I moved qa-completion into qa-folder and reloaded. It is in the folder. I renamed it inside the folder and reloaded. It stays in the folder. I moved it to "All prompts" and reloaded. It is back at the top level, and the folder is empty.
  • API: PUT /workflows/{id} with folder_id: null moves the app to the top level.
  • /w: Prompts > Move between qa-folder and qa-folder-2, both ways, still works after a reload. The request sends the new folder_id.

Not from this PR: in /w, the Move dialog logs "Maximum call stack size exceeded" (a focus loop) when you open it inside a folder. The move still works. The dialog is /w code that this PR does not change.

Checks on this head: API unit tests (10,118 pass, 145 skipped because no Postgres is available, 0 fail), ruff format and ruff check (clean), /m types, /w types, pnpm run lint in web/mobile (0 errors), the PR's mobile and entities tests, and speed:check all pass. Needed CI is green. Only the web test jobs fail, and they do not gate.

The screen recording (MP4) is saved with the QA evidence. I could not attach it here because the browser upload was not available in this session.

@mmabrouk
mmabrouk changed the base branch from release/v0.128.5 to release/v0.128.7 November 4, 2026 20:07
@mmabrouk
mmabrouk merged commit 6ceae22 into release/v0.128.7 Nov 4, 2026
83 of 88 checks passed
@mmabrouk mmabrouk mentioned this pull request Nov 4, 2026
7 tasks done
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