Skip to content

[Frontend feat] Workflow creation unification#4274

Open
ardaerzin wants to merge 23 commits intorelease/v0.99.3from
frontend-feat/workflow-create-unification
Open

[Frontend feat] Workflow creation unification#4274
ardaerzin wants to merge 23 commits intorelease/v0.99.3from
frontend-feat/workflow-create-unification

Conversation

@ardaerzin
Copy link
Copy Markdown
Contributor

@ardaerzin ardaerzin commented May 6, 2026

Summary

this PR aligns app creation flow with the evaluator creation flow, in order to provide a few UX wins for our users:

  • playground in the drawer without any redirects
  • runnable without creating the app first

also fixes AGE-3754

Testing

Verified locally

app creation works

QA follow-up

go over all places where we can create apps from, and make sure they are aligned with this implementation.

Checklist

  • I have included a video or screen recording for UI changes, or marked Demo as N/A
  • Relevant tests pass locally
  • Relevant linting and formatting pass locally
  • I have signed the CLA, or I will sign it when the bot prompts me

Contributor Resources

…e selection to new prompt flow

- Remove "Custom workflow" option from CreateAppDropdown; custom workflows now only accessible via "Set up workflow" in prompts page
- Add Chat/Completion submenu to "New prompt" action in breadcrumb and table section menus
- Update CreateAppDropdown layout to include app type icons and improve visual alignment
- Enable mask-based close for app-create drawer context (blurred backdrop)
- Fix callback
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error May 7, 2026 2:29pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 696955c7-3355-4663-b15d-88b5fdb073f9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Implements an ephemeral app creation flow from templates (Chat/Completion) that mints local workflows, opens a new "app-create" drawer context, adds template atoms and a createEphemeralAppFromTemplate factory, introduces cleanup for local ephemeral data, replaces modal UIs with CreateAppDropdown/CreateAppTypeModal, and updates prompts, app-management, drawer store/UI, tests, and validation helpers.

Changes

Ephemeral app-create flow (templates → drawer)

Layer / File(s) Summary
Data Shape & Atoms
web/packages/agenta-entities/src/workflow/state/appUtils.ts, .../state/index.ts, .../index.ts, .../store.ts
Adds AppType (`"chat"
Core factory & helpers
web/packages/agenta-entities/src/workflow/state/appUtils.ts
Implements template matching, schema resolution/inspect, local-id generation, and persistence of ephemeral workflow to local server data.
UI: Create trigger & modal
web/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsx, web/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsx
Adds CreateAppDropdown (popover trigger) and CreateAppTypeModal (selection UI) that prefetch templates, race-guard with AbortController/useTransition, call createEphemeralAppFromTemplate, and open the drawer with app-create context.
Prompts / App management wiring
web/oss/src/components/pages/prompts/PromptsPage.tsx, .../PromptsBreadcrumb.tsx, .../PromptsTableSection.tsx, web/oss/src/components/pages/app-management/index.tsx, .../ApplicationManagementSection.tsx, .../EmptyAppView.tsx
Prompts UI exposes AppType selection (Chat/Completion), prefetches templates, and routes creation through the new dropdown/modal/drawer flow; app-management replaces old modal wiring with new dropdown/modal and simplifies props.
Drawer store & contract
web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/store.ts, .../index.ts
Adds "app-create" DrawerContext, isCreateContext() helper, extends OpenDrawerParams with onWorkflowCreated (result object), and updates the drawer callback atom to accept result payloads (bridges legacy callbacks).
Drawer UI & runtime wiring
web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerHeader.tsx, .../DrawerContent.tsx, .../WorkflowRevisionDrawer.tsx, web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx
Adds inline AppCreateNameInput, centralizes create-context checks via isCreateContext(), introduces showBlurredMask, wires new open/callback behavior, and adds useDrawerCreateCommitCallback, useDrawerCloseCleanup (calls discardLocalServerDataAtom), and useUnsavedDrawerWarning.
Commit modal
web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx
Adds APP_CREATE_FIELDS, computes isApplication, and passes dynamic createFields and successMessage for ephemeral-create flows.
Tests
web/oss/tests/playwright/acceptance/app/test.ts, .../index.ts
Updates fixtures to drawer-based createNewApp(appName, AppType) flow and adds a test asserting no POST to /workflows when closing the create-app drawer without committing.
Cleanup: removed modal
web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/*
Deletes old AddAppFromTemplateModal and its props/types in favor of new CreateAppTypeModal/CreateAppDropdown.

Validation helpers & small UX adjustments

Layer / File(s) Summary
Helpers
web/oss/src/lib/helpers/utils.ts
Adds isSlugInputValid(input) (URL-safe [a-zA-Z0-9_-]) and tightens isAppNameInputValid to require non-empty trimmed input; keeps isVariantNameInputValid on URL-safe pattern.
Validation usage updates
web/oss/src/components/ModelRegistry/..., web/oss/src/components/SharedDrawers/AnnotateDrawer/..., web/oss/src/components/pages/app-management/modals/CustomWorkflowModal/*, web/oss/src/components/pages/app-management/modals/EditAppModal/index.tsx
Replaces isAppNameInputValid with isSlugInputValid where slug/identifier validation is required; removes character-validity warnings in some app-name UIs (relying on non-empty/duplicate checks).
App workflow store (archived)
web/oss/src/components/pages/app-management/store/appWorkflowStore.ts, .../store/index.ts
Adds archived paginated store using EnrichedWorkflow, integrates invalidation for archived queries, and re-exports getAppWorkflowTableState, appWorkflowPaginatedStore, workflowPaginatedStore from the store index.
Minor UI typing
web/oss/src/components/pages/app-management/components/appWorkflowColumns.tsx
Tightens TypeScript literal typings for archived columns (as const).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as CreateAppDropdown / CreateAppTypeModal
    participant Templates as AppTemplatesAtom
    participant Ephemeral as createEphemeralAppFromTemplate
    participant Drawer as WorkflowRevisionDrawer
    participant API as /workflows
    participant Nav as Navigation

    User->>UI: Click "Create New Prompt" and choose Chat/Completion
    UI->>Templates: Ensure/prefetch templates
    UI->>Ephemeral: createEphemeralAppFromTemplate(AppType)
    Ephemeral->>Ephemeral: Resolve schemas, build ephemeral workflow, store local-* data
    Ephemeral-->>UI: Return localId
    UI->>Drawer: Open drawer with context "app-create" and localId
    Drawer-->>User: Show inline AppCreateNameInput
    User->>Drawer: Edit name and commit
    Drawer->>API: POST /workflows
    API-->>Drawer: Return {configId,newAppId,newRevisionId}
    Drawer->>Nav: Navigate to playground for newAppId
    Nav-->>User: Playground loads
Loading
sequenceDiagram
    participant User
    participant DrawerWrapper
    participant Cleanup as discardLocalServerDataAtom
    participant State as LocalAtomFamily

    User->>DrawerWrapper: Open app-create drawer
    User->>DrawerWrapper: Close drawer without committing
    DrawerWrapper->>Cleanup: discardLocalServerDataAtom(localId)
    Cleanup->>State: Clear local-* server data & draft overlays
    DrawerWrapper-->>User: Drawer closes, ephemeral data removed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title '[Frontend feat] Workflow creation unification' directly describes the main objective of the PR—aligning app and evaluator creation flows into a unified workflow. It is concise, clear, and accurately reflects the primary change.
Description check ✅ Passed The description is well-structured and directly related to the changeset. It explains the motivation (playground in drawer without redirects, runnable without app creation first), references a fixed issue (AGE-3754), covers testing and QA follow-up, and includes a checklist. It is clearly relevant to the changes and provides meaningful context.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch frontend-feat/workflow-create-unification

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

@ardaerzin ardaerzin requested review from ashrafchowdury, bekossy and mmabrouk and removed request for ashrafchowdury and bekossy May 6, 2026 09:20
@ardaerzin ardaerzin marked this pull request as ready for review May 6, 2026 09:46
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request Frontend UX labels May 6, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx (1)

25-30: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a neutral fallback label for non-app ephemeral flows.

The fallback branch still renders Evaluator name, so any future non-evaluator ephemeral flow will get mislabeled even though the success copy is generic.

Suggested tweak
 const EVALUATOR_CREATE_FIELDS: CommitCreateFieldsConfig = {nameLabel: "Evaluator name"}
 const APP_CREATE_FIELDS: CommitCreateFieldsConfig = {nameLabel: "App name"}
+const DEFAULT_CREATE_FIELDS: CommitCreateFieldsConfig = {nameLabel: "Workflow name"}
 const VARIANT_CREATE_FIELDS: CommitCreateFieldsConfig = {
     modes: ["variant"],
     nameLabel: "Variant name",
 }
@@
         const createFields = isEvaluator
             ? EVALUATOR_CREATE_FIELDS
             : isApplication
               ? APP_CREATE_FIELDS
-              : EVALUATOR_CREATE_FIELDS
+              : DEFAULT_CREATE_FIELDS

Also applies to: 236-245

web/oss/src/components/pages/prompts/PromptsPage.tsx (1)

410-423: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't overwrite failed retries with "success".

Both branches in catch set "timeout"/"error", but execution falls through and immediately flips the modal to "success" anyway. That makes failed retries look successful and triggers a workflow refetch on failure.

💡 Suggested fix
     const onTimeoutRetry = useCallback(async () => {
         if (!statusData.appId) return
         setStatusData((prev) => ({...prev, status: "configuring_app", details: undefined}))
         try {
             await waitForAppToStart({appId: statusData.appId, timeout})
+            setStatusData((prev) => ({...prev, status: "success", details: undefined}))
+            refetchWorkflows()
         } catch (error: any) {
             if (error.message === "timeout") {
                 setStatusData((prev) => ({...prev, status: "timeout", details: undefined}))
             } else {
                 setStatusData((prev) => ({...prev, status: "error", details: error}))
             }
+            return
         }
-        setStatusData((prev) => ({...prev, status: "success", details: undefined}))
-        refetchWorkflows()
     }, [refetchWorkflows, setStatusData, statusData.appId])
🧹 Nitpick comments (1)
web/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsx (1)

18-18: ⚡ Quick win

Use the OSS alias for this shared helper import.

This helper is now shared across page modules, so the long relative path is brittle and harder to move safely.

As per coding guidelines, Prefer '@/oss/*' import alias for shared utilities, helpers, types, hooks, and state that work the same in both EE and OSS.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 22337dc0-05bf-48f5-8a86-1507dcaae31c

📥 Commits

Reviewing files that changed from the base of the PR and between 3174f53 and 214be97.

📒 Files selected for processing (27)
  • web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx
  • web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx
  • web/oss/src/components/pages/app-management/components/ApplicationManagementSection.tsx
  • web/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsx
  • web/oss/src/components/pages/app-management/components/EmptyAppView.tsx
  • web/oss/src/components/pages/app-management/components/appWorkflowColumns.tsx
  • web/oss/src/components/pages/app-management/index.tsx
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/assets/styles.ts
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/components/AddAppFromTemplateModalContent.tsx
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/index.tsx
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/types.ts
  • web/oss/src/components/pages/app-management/store/appWorkflowStore.ts
  • web/oss/src/components/pages/app-management/store/index.ts
  • web/oss/src/components/pages/prompts/PromptsPage.tsx
  • web/oss/src/components/pages/prompts/components/PromptsBreadcrumb.tsx
  • web/oss/src/components/pages/prompts/components/PromptsTableSection.tsx
  • web/oss/tests/playwright/acceptance/app/index.ts
  • web/oss/tests/playwright/acceptance/app/test.ts
  • web/packages/agenta-entities/src/workflow/index.ts
  • web/packages/agenta-entities/src/workflow/state/appUtils.ts
  • web/packages/agenta-entities/src/workflow/state/index.ts
  • web/packages/agenta-entities/src/workflow/state/store.ts
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerContent.tsx
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerHeader.tsx
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/WorkflowRevisionDrawer.tsx
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/index.ts
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/store.ts
💤 Files with no reviewable changes (4)
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/components/AddAppFromTemplateModalContent.tsx
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/index.tsx
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/assets/styles.ts
  • web/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/types.ts

Comment thread web/oss/src/components/pages/app-management/index.tsx Outdated
Comment thread web/oss/src/components/pages/prompts/PromptsPage.tsx Outdated
Comment thread web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx
Comment thread web/packages/agenta-entities/src/workflow/state/appUtils.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Railway Preview Environment

Preview URL https://gateway-production-4a92.up.railway.app/w
Project agenta-oss-pr-4274
Image tag pr-4274-7f5ee2d
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-05-07T14:31:22.576Z

@mmabrouk
Copy link
Copy Markdown
Member

mmabrouk commented May 6, 2026

Thanks @ardaerzin for the PR:

CleanShot 2026-05-06 at 12 44 30@2x

Clicking this opens a chat application per default. Given that this is the default path new users creates a new application, this obfuscates the fact that we have two types. I would add for this onboarding flow a modal with two big button chat or completion.

@mmabrouk
Copy link
Copy Markdown
Member

mmabrouk commented May 6, 2026

CleanShot 2026-05-06 at 12 46 03@2x CleanShot 2026-05-06 at 12 46 14@2x

Maybe completion prompt and chat prompt as default names?

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +543 to +558
const entityIdRef = useRef<string | null>(null)
const entityId = useAtomValue(workflowRevisionDrawerEntityIdAtom)
entityIdRef.current = entityId

const discard = useSetAtom(discardLocalServerDataAtom)
const discardRef = useRef(discard)
discardRef.current = discard

const prevOpenRef = useRef(isOpen)
useEffect(() => {
if (prevOpenRef.current && !isOpen) {
// Drawer just closed — release the entity that was open.
// Read from the ref captured BEFORE the close (since close
// resets entityId to null).
const id = entityIdRef.current
if (id) discardRef.current(id)
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.

🔴 useDrawerCloseCleanup never discards local- entity data because entityIdRef is clobbered to null before effect fires*

When closeWorkflowRevisionDrawerAtom is dispatched (store.ts:165-174), it atomically sets both workflowRevisionDrawerOpenAtom to false and workflowRevisionDrawerEntityIdAtom to RESET (null) in the same Jotai write. React 18 batches these into a single re-render where both values are updated simultaneously. During the render phase, entityIdRef.current = entityId (line 545) sets the ref to null — the new value. Then in the commit phase, the useEffect fires and reads entityIdRef.current, which is already null, so the if (id) guard on line 558 fails and discard is never called. This means orphan local-* entities from app-create / evaluator-create flows are never released from the atom family when the user closes the drawer without committing — a state/memory leak.

Suggested change
const entityIdRef = useRef<string | null>(null)
const entityId = useAtomValue(workflowRevisionDrawerEntityIdAtom)
entityIdRef.current = entityId
const discard = useSetAtom(discardLocalServerDataAtom)
const discardRef = useRef(discard)
discardRef.current = discard
const prevOpenRef = useRef(isOpen)
useEffect(() => {
if (prevOpenRef.current && !isOpen) {
// Drawer just closed — release the entity that was open.
// Read from the ref captured BEFORE the close (since close
// resets entityId to null).
const id = entityIdRef.current
if (id) discardRef.current(id)
const entityIdRef = useRef<string | null>(null)
const entityId = useAtomValue(workflowRevisionDrawerEntityIdAtom)
// Only update the ref when entityId is truthy. When closeDrawer()
// resets both isOpen and entityId atomically, the ref retains the
// last real entity ID so the effect can still discard it.
if (entityId) entityIdRef.current = entityId
const discard = useSetAtom(discardLocalServerDataAtom)
const discardRef = useRef(discard)
discardRef.current = discard
const prevOpenRef = useRef(isOpen)
useEffect(() => {
if (prevOpenRef.current && !isOpen) {
// Drawer just closed — release the entity that was open.
const id = entityIdRef.current
if (id) {
discardRef.current(id)
entityIdRef.current = null
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +477 to 487
drawerCallbackRef.current?.({
newAppId,
newRevisionId,
})

if (newAppId && newRevisionId) {
routerRef.current.push(
`${baseAppURLRef.current}/${newAppId}/playground?revisions=${newRevisionId}`,
)
}
closeDrawerRef.current()
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.

🔴 Double router.push navigation on app-create commit: wrapper navigates AND caller's onWorkflowCreated callback navigates

In useDrawerCreateCommitCallback, the isAppCreate branch both invokes drawerCallbackRef.current?.({newAppId, newRevisionId}) (line 477-480) AND performs its own routerRef.current.push(...) (line 483-485). All callers that pass onWorkflowCreatedCreateAppDropdown (CreateAppDropdown/index.tsx:115-118), AppManagement (index.tsx:75), and PromptsPage (PromptsPage.tsx:388) — also call router.push() to the same playground URL inside their callback. This results in two router.push calls to the same URL on every successful app creation, which can cause duplicate browser history entries, double renders, or race conditions during the Next.js page transition.

Wrapper comment vs. actual behavior

The wrapper's comment on line 472-476 says "owning the routing here keeps the contract simple for callers", implying callers should NOT navigate. But every caller does navigate. Either remove the wrapper's router.push (and let the callback own it) or remove the router.push from all onWorkflowCreated callbacks.

Prompt for agents
The wrapper's onNewRevision handler for isAppCreate does two things that conflict: (1) it invokes drawerCallbackRef.current which the callers use to router.push, and (2) it also calls routerRef.current.push itself. This causes double navigation.

Option A (recommended based on the wrapper's comments about 'owning the routing'): Keep the wrapper's router.push and remove the router.push from all onWorkflowCreated callbacks in CreateAppDropdown/index.tsx (lines 114-119), AppManagement index.tsx (line 75), and PromptsPage.tsx (line 388). The callers' callbacks should only do analytics/hooks, not navigation.

Option B: Remove lines 482-486 from the wrapper (the routerRef.current.push block) and let each caller's onWorkflowCreated callback handle its own navigation. This is simpler but means the wrapper doesn't own routing.

Either way, pick one owner for the navigation and remove the duplicate.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 498 to 504
message.success(
isEvaluatorCreate
? "Evaluator created successfully"
: "Evaluator committed successfully",
isAppCreate
? "App created successfully"
: isEvaluatorCreate
? "Evaluator created successfully"
: "Evaluator committed successfully",
)
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.

🟡 Double success toast on app-create commit: both commit callback and EntityCommitModal show 'App created successfully'

When an ephemeral app is committed, the flow is: CommitVariantChangesModal.handleSubmitcreateFromEphemeralinvokeWorkflowCommitCallbacksuseDrawerCreateCommitCallback's onNewRevision. The onNewRevision handler shows message.success("App created successfully") (line 499-500). Then control returns to handleSubmit which returns {success: true}. EntityCommitModal then also calls message.success(successMessage) where successMessage is "App created successfully" (set at CommitVariantChangesModal/index.tsx:243-245, displayed at EntityCommitModal.tsx:408-409). The user sees two identical toast notifications.

Prompt for agents
There are two sources of the success toast for app-create:
1. useDrawerCreateCommitCallback's onNewRevision handler at WorkflowRevisionDrawerWrapper/index.tsx:498-504 calls message.success
2. EntityCommitModal at agenta-entity-ui/src/modals/commit/components/EntityCommitModal.tsx:408-409 calls message.success(successMessage) after handleSubmit returns successfully

Fix: either pass successMessage={null} in CommitVariantChangesModal for app-create ephemeral entities (so the modal doesn't toast), or remove the message.success from the wrapper's onNewRevision handler for isAppCreate. The simplest fix is probably to set successMessage to null for app-create in CommitVariantChangesModal/index.tsx around line 241-245, since the wrapper already handles the toast.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 6, 2026
- Make CreateAppDropdown options keyboard-accessible (button + focus ring).
- Drop unused MaxAppModal state on the app-management page.
- Stop double-navigating on app-create commit: drawer wrapper owns the
  router.push, callers no longer pass an onWorkflowCreated handler.
- Capture the last open entity id before close clears it so
  useDrawerCloseCleanup actually discards orphan local-* entities.
- Build the fallback workflow URI from the requested type rather than
  the raw catalog key (avoids invalid SERVICE:* builtin URIs).
- Persist cleared app-create names instead of silently keeping the old
  one when the user blanks the input.
- Suppress the duplicate "App/Evaluator created successfully" toast in
  EntityCommitModal for ephemeral flows; the drawer wrapper already
  toasts on onNewRevision.

UX:
- Onboarding "Create a prompt" now opens a CreateAppTypeModal with
  Chat / Completion as equal-weight choices instead of defaulting to
  Chat. Apps-table dropdown remains compact for repeat users.
- Default names changed to "Chat prompt" / "Completion prompt".
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx (1)

236-240: 💤 Low value

Consider a more generic fallback label for unknown ephemeral entity types.

The fallback to EVALUATOR_CREATE_FIELDS when neither isEvaluator nor isApplication is true would display "Evaluator name" for an unrecognized ephemeral entity type. This could be confusing if such a case ever occurs in practice.

A more neutral default like {nameLabel: "Name"} or {nameLabel: "Workflow name"} might be clearer if new ephemeral entity types are introduced.

💡 Optional: Use a generic fallback
+const DEFAULT_CREATE_FIELDS: CommitCreateFieldsConfig = {nameLabel: "Name"}
+
 const createFields = isEvaluator
     ? EVALUATOR_CREATE_FIELDS
     : isApplication
       ? APP_CREATE_FIELDS
-      : EVALUATOR_CREATE_FIELDS
+      : DEFAULT_CREATE_FIELDS
web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx (1)

458-487: 💤 Low value

Navigation occurs before drawer close for app-create context.

For app-create, the code calls router.push() (line 483-485) before closeDrawerRef.current() (line 487). This differs from evaluator-create where close happens before navigation isn't needed (it just fires the callback).

However, the docstring at lines 411-415 states: "Order: close → navigate (avoids drawer flicker on destination page during Next.js async transition)."

The actual implementation does navigate → close, which contradicts the documented intent. If flicker is a concern, consider swapping the order to match the documented behavior.

💡 Suggested fix to match documented order
                     if (newAppId && newRevisionId) {
+                        closeDrawerRef.current()
                         routerRef.current.push(
                             `${baseAppURLRef.current}/${newAppId}/playground?revisions=${newRevisionId}`,
                         )
                     }
-                    closeDrawerRef.current()
web/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsx (1)

28-28: 💤 Low value

Use EnhancedModal from @agenta/ui instead of local import.

The coding guidelines specify: "Use EnhancedModal from @agenta/ui for all new modals instead of raw antd Modal with proper ModalContent and ModalFooter components."

The current import uses the local OSS path. Consider updating to use the shared package import for consistency.

💡 Suggested fix
-import EnhancedModal from "@/oss/components/EnhancedUIs/Modal"
+import {EnhancedModal} from "@agenta/ui"

As per coding guidelines: "Use EnhancedModal from @agenta/ui for all new modals."


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e3a94862-5ba0-458c-b8ac-235b33ccebac

📥 Commits

Reviewing files that changed from the base of the PR and between 214be97 and 6d8c4b8.

📒 Files selected for processing (8)
  • web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx
  • web/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx
  • web/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsx
  • web/oss/src/components/pages/app-management/index.tsx
  • web/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsx
  • web/oss/src/components/pages/prompts/PromptsPage.tsx
  • web/packages/agenta-entities/src/workflow/state/appUtils.ts
  • web/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerHeader.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/packages/agenta-entities/src/workflow/state/appUtils.ts

Comment on lines +157 to +159
useEffect(() => {
setLocalValue(currentName ?? "")
}, [currentName])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent external name sync from clobbering in-progress typing.

useEffect always resets localValue when currentName changes. If hydration/background updates land while the user is editing, typed input can be overwritten. Guard sync while editing/focused.
Line 157 currently contradicts the comment about not clobbering active edits.

💡 Suggested fix
 const AppCreateNameInput = memo(({entityId}: {entityId: string}) => {
     const currentName = useAtomValue(workflowMolecule.selectors.name(entityId))
     const updateWorkflow = useSetAtom(workflowMolecule.actions.update)
     const [localValue, setLocalValue] = useState(currentName ?? "")
+    const [isEditing, setIsEditing] = useState(false)

     // Keep input in sync if the entity's name changes externally (e.g. on
     // initial entity hydration). Don't clobber while the user is typing.
     useEffect(() => {
-        setLocalValue(currentName ?? "")
-    }, [currentName])
+        if (!isEditing) setLocalValue(currentName ?? "")
+    }, [currentName, isEditing])

+    const handleFocus = useCallback(() => {
+        setIsEditing(true)
+    }, [])
+
     const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
         setLocalValue(e.target.value)
     }, [])

     const handleBlur = useCallback(() => {
         const trimmed = localValue.trim()
         // Persist the trimmed value whenever it differs from the current
         // (also-trimmed) name — including the empty-string case. Without
         // this, deleting the name and blurring would leave the workflow
         // entity holding the previous name silently while the input
         // appears blank.
         if (trimmed !== (currentName ?? "").trim()) {
             updateWorkflow(entityId, {name: trimmed})
         }
+        setIsEditing(false)
     }, [localValue, currentName, updateWorkflow, entityId])

     return (
         <Input
             value={localValue}
+            onFocus={handleFocus}
             onChange={handleChange}
             onBlur={handleBlur}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
setLocalValue(currentName ?? "")
}, [currentName])
const AppCreateNameInput = memo(({entityId}: {entityId: string}) => {
const currentName = useAtomValue(workflowMolecule.selectors.name(entityId))
const updateWorkflow = useSetAtom(workflowMolecule.actions.update)
const [localValue, setLocalValue] = useState(currentName ?? "")
const [isEditing, setIsEditing] = useState(false)
// Keep input in sync if the entity's name changes externally (e.g. on
// initial entity hydration). Don't clobber while the user is typing.
useEffect(() => {
if (!isEditing) setLocalValue(currentName ?? "")
}, [currentName, isEditing])
const handleFocus = useCallback(() => {
setIsEditing(true)
}, [])
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setLocalValue(e.target.value)
}, [])
const handleBlur = useCallback(() => {
const trimmed = localValue.trim()
// Persist the trimmed value whenever it differs from the current
// (also-trimmed) name — including the empty-string case. Without
// this, deleting the name and blurring would leave the workflow
// entity holding the previous name silently while the input
// appears blank.
if (trimmed !== (currentName ?? "").trim()) {
updateWorkflow(entityId, {name: trimmed})
}
setIsEditing(false)
}, [localValue, currentName, updateWorkflow, entityId])
return (
<Input
value={localValue}
onFocus={handleFocus}
onChange={handleChange}
onBlur={handleBlur}
/>
)
})

ardaerzin added 2 commits May 6, 2026 17:06
- Drop isAppNameInputValid checks from CustomWorkflowModal (content + footer),
  EditAppModal, and related error messages/disabled states.
- Update isAppNameInputValid helper to only reject empty/whitespace-only input
  instead of enforcing [a-zA-Z0-9_-] pattern (per AGE-3754: app names are
  free-form display labels).
- Add isSlugInputValid helper for URL-safe validation on slug fields.
- Remove "must contain only letters, numbers, underscore
Replace isAppNameInputValid with isSlugInputValid in ConfigureProviderDrawer
and CreateEvaluator components where slug/name fields require URL-safe
characters ([a-zA-Z0-9_-] pattern). Aligns with AGE-3754 separation of
display names from URL-safe identifiers.
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 6, 2026

Add "/prompts" to TRACE_ENABLED_PATH_MATCHERS so trace-related features
are available when navigating to the prompts page.
@ardaerzin ardaerzin changed the base branch from main to release/v0.99.3 May 6, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Frontend lgtm This PR has been approved by a maintainer size:XXL This PR changes 1000+ lines, ignoring generated files. UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants