[Frontend feat] Workflow creation unification#4274
[Frontend feat] Workflow creation unification#4274ardaerzin wants to merge 23 commits intorelease/v0.99.3from
Conversation
…reate-unification
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughImplements 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. ChangesEphemeral app-create flow (templates → drawer)
Validation helpers & small UX adjustments
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 winUse 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_FIELDSAlso applies to: 236-245
web/oss/src/components/pages/prompts/PromptsPage.tsx (1)
410-423:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't overwrite failed retries with
"success".Both branches in
catchset"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 winUse 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
📒 Files selected for processing (27)
web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsxweb/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsxweb/oss/src/components/pages/app-management/components/ApplicationManagementSection.tsxweb/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsxweb/oss/src/components/pages/app-management/components/EmptyAppView.tsxweb/oss/src/components/pages/app-management/components/appWorkflowColumns.tsxweb/oss/src/components/pages/app-management/index.tsxweb/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/assets/styles.tsweb/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/components/AddAppFromTemplateModalContent.tsxweb/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/index.tsxweb/oss/src/components/pages/app-management/modals/AddAppFromTemplateModal/types.tsweb/oss/src/components/pages/app-management/store/appWorkflowStore.tsweb/oss/src/components/pages/app-management/store/index.tsweb/oss/src/components/pages/prompts/PromptsPage.tsxweb/oss/src/components/pages/prompts/components/PromptsBreadcrumb.tsxweb/oss/src/components/pages/prompts/components/PromptsTableSection.tsxweb/oss/tests/playwright/acceptance/app/index.tsweb/oss/tests/playwright/acceptance/app/test.tsweb/packages/agenta-entities/src/workflow/index.tsweb/packages/agenta-entities/src/workflow/state/appUtils.tsweb/packages/agenta-entities/src/workflow/state/index.tsweb/packages/agenta-entities/src/workflow/state/store.tsweb/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerContent.tsxweb/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/DrawerHeader.tsxweb/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/WorkflowRevisionDrawer.tsxweb/packages/agenta-playground-ui/src/components/WorkflowRevisionDrawer/index.tsweb/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
Railway Preview Environment
|
|
Thanks @ardaerzin for the PR:
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. |
| 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) |
There was a problem hiding this comment.
🔴 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.
| 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 | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
| drawerCallbackRef.current?.({ | ||
| newAppId, | ||
| newRevisionId, | ||
| }) | ||
|
|
||
| if (newAppId && newRevisionId) { | ||
| routerRef.current.push( | ||
| `${baseAppURLRef.current}/${newAppId}/playground?revisions=${newRevisionId}`, | ||
| ) | ||
| } | ||
| closeDrawerRef.current() |
There was a problem hiding this comment.
🔴 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 onWorkflowCreated — CreateAppDropdown (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.
Was this helpful? React with 👍 or 👎 to provide feedback.
| message.success( | ||
| isEvaluatorCreate | ||
| ? "Evaluator created successfully" | ||
| : "Evaluator committed successfully", | ||
| isAppCreate | ||
| ? "App created successfully" | ||
| : isEvaluatorCreate | ||
| ? "Evaluator created successfully" | ||
| : "Evaluator committed successfully", | ||
| ) |
There was a problem hiding this comment.
🟡 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.handleSubmit → createFromEphemeral → invokeWorkflowCommitCallbacks → useDrawerCreateCommitCallback'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.
Was this helpful? React with 👍 or 👎 to provide feedback.
- 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".
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsx (1)
236-240: 💤 Low valueConsider a more generic fallback label for unknown ephemeral entity types.
The fallback to
EVALUATOR_CREATE_FIELDSwhen neitherisEvaluatornorisApplicationis 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_FIELDSweb/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsx (1)
458-487: 💤 Low valueNavigation occurs before drawer close for
app-createcontext.For
app-create, the code callsrouter.push()(line 483-485) beforecloseDrawerRef.current()(line 487). This differs fromevaluator-createwhere 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 valueUse
EnhancedModalfrom@agenta/uiinstead of local import.The coding guidelines specify: "Use
EnhancedModalfrom@agenta/uifor 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
EnhancedModalfrom@agenta/uifor all new modals."
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e3a94862-5ba0-458c-b8ac-235b33ccebac
📒 Files selected for processing (8)
web/oss/src/components/Playground/Components/Modals/CommitVariantChangesModal/index.tsxweb/oss/src/components/WorkflowRevisionDrawerWrapper/index.tsxweb/oss/src/components/pages/app-management/components/CreateAppDropdown/index.tsxweb/oss/src/components/pages/app-management/index.tsxweb/oss/src/components/pages/app-management/modals/CreateAppTypeModal/index.tsxweb/oss/src/components/pages/prompts/PromptsPage.tsxweb/packages/agenta-entities/src/workflow/state/appUtils.tsweb/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
| useEffect(() => { | ||
| setLocalValue(currentName ?? "") | ||
| }, [currentName]) |
There was a problem hiding this comment.
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.
| 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} | |
| /> | |
| ) | |
| }) |
- 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.
Add "/prompts" to TRACE_ENABLED_PATH_MATCHERS so trace-related features are available when navigating to the prompts page.



Summary
this PR aligns app creation flow with the evaluator creation flow, in order to provide a few UX wins for our users:
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
Contributor Resources