NF-UI-10: operator creates new flow from canvas #370

Closed
opened 2026-04-25 10:43:59 +00:00 by code-lead · 0 comments
Collaborator

As an operator, I want to create a new flow from a blank canvas — pick a trigger, drop nodes, save — without leaving the editor surface, so that authoring a custom routing policy mirrors the edit experience instead of being a separate wizard.

ComfyUI-style: land on /app/flows/new, get a canvas seeded with one source node + an unset trigger; toolbar surfaces a trigger dropdown + id input + Save / Discard. First save POSTs to /flows (NF-7 endpoint), subsequent saves PATCH normally.

Acceptance criteria

Route + initial state

  • /app/flows/new replaces the stub. Renders the canvas + toolbar with mode: "create".
  • Initial graph: one source node ({ id: "src", type: "source" }), no edges. Source node is undeletable in create mode (matches edit-mode rule).
  • Trigger starts unset (empty string); id starts empty.

Toolbar (create mode)

  • Trigger picker — dropdown over TRIGGER_KINDS (from @claude-hooks/shared). Required.
  • ID input — text, validates ^[a-z0-9-]+$. Required.
  • Description textarea — optional.
  • Priority + mutex_group fields work the same as edit mode.
  • Save button disabled until: trigger set + id valid + graph passes validateGraph.
  • Discard button: confirms when dirty, routes to /app/flows.

Save flow

  • First save calls flowsApi.createFlow(graph)POST /flows.
  • On 201: navigate to /app/flows/:newId. Subsequent saves on that route use the existing PATCH path.
  • On 409 (id already exists): surface inline error on the id input ("already exists, pick another"); no navigation; canvas stays in create mode.
  • On 422 (validation failed): highlight offending nodes via data-invalid (reuse the edit-mode FlowValidationError flow).
  • Dirty tracking flips on: drop / delete node, connect / remove edge, toolbar field change. beforeunload warning matches edit mode.

Architecture

  • flowsApi.createFlow(graph): Promise<FlowSummary> — new client function (POSTs full body). Returns the server summary mapped through toClientSummary.
  • FlowCanvas accepts three new props:
    • mode?: "edit" | "create" — default "edit".
    • initialDetail?: FlowDetailResponse — used in create mode instead of useQuery.
    • onCreate?: (graph: Graph) => Promise<{ id: string }> — used instead of updateFlow on first save.
  • Edit-mode behavior unchanged (no regressions in existing 18 FlowCanvasEditing tests).

Tests (target ≥12)

  • Toolbar renders trigger dropdown populated from TRIGGER_KINDS.
  • Toolbar renders id input with format validation (rejects Foo!, accepts my-flow).
  • Save disabled until trigger + id valid + graph valid.
  • Dropping a node trips the dirty bit.
  • Save with valid body calls createFlow with the expected shape.
  • 201 response navigates to /app/flows/:newId.
  • 409 conflict surfaces inline id-input error, stays on /app/flows/new.
  • 422 validation highlights offending nodes via data-invalid (mirrors edit mode).
  • Discard with dirty prompts confirmation; cleanly cancels.
  • Source node auto-seeded + undeletable in create mode.
  • Empty trigger → save disabled even with otherwise-valid graph.
  • beforeunload warning fires when dirty (mirrors edit mode).

Server side

  • Zero changes. POST /flows shipped with NF-7 (#363) including 409 / 422 envelopes + audit log row with action: "create".

Out of scope

  • Changing trigger / id after save (post-create immutability is fine for v1).
  • localStorage draft persistence (refresh-during-edit loses state — acceptable v1, tracked as follow-up).
  • Templates / preset graphs.
  • Operator-authored fixtures from the new-flow page.

References

  • Spec: specs/node-flows-ui.md § Stories — currently has no NF-UI-10; this issue is the first story past NF-UI-9 in the UI sequence.
  • apps/web/src/features/flows/FlowCanvas.tsx — reference for the edit-mode toolbar + save handler the create mode mirrors.
  • apps/server/src/http/flows-routes.ts::handleFlowCreate — the POST endpoint already shipped.
  • apps/web/src/features/flows/flowsApi.ts::updateFlow — sibling shape to copy for createFlow.
  • packages/shared/src/trigger-event.ts::TRIGGER_KINDS — runtime catalog the trigger picker reads.

Dependencies

  • NF-7 (#328, merged via #363) — server POST endpoint.
  • NF-UI-3 (#333, merged via #351) — edit canvas the create canvas reuses.
  • #366 (merged via #368) — flowsApi swapped to real fetch; createFlow is the same pattern.
As an operator, I want to create a new flow from a blank canvas — pick a trigger, drop nodes, save — without leaving the editor surface, so that authoring a custom routing policy mirrors the edit experience instead of being a separate wizard. ComfyUI-style: land on `/app/flows/new`, get a canvas seeded with one source node + an unset trigger; toolbar surfaces a trigger dropdown + id input + Save / Discard. First save POSTs to `/flows` (NF-7 endpoint), subsequent saves PATCH normally. ## Acceptance criteria ### Route + initial state - [ ] `/app/flows/new` replaces the stub. Renders the canvas + toolbar with `mode: "create"`. - [ ] Initial graph: one source node (`{ id: "src", type: "source" }`), no edges. Source node is undeletable in create mode (matches edit-mode rule). - [ ] Trigger starts unset (empty string); id starts empty. ### Toolbar (create mode) - [ ] Trigger picker — dropdown over `TRIGGER_KINDS` (from `@claude-hooks/shared`). Required. - [ ] ID input — text, validates `^[a-z0-9-]+$`. Required. - [ ] Description textarea — optional. - [ ] Priority + mutex_group fields work the same as edit mode. - [ ] Save button disabled until: trigger set + id valid + graph passes `validateGraph`. - [ ] Discard button: confirms when dirty, routes to `/app/flows`. ### Save flow - [ ] First save calls `flowsApi.createFlow(graph)` → `POST /flows`. - [ ] On 201: navigate to `/app/flows/:newId`. Subsequent saves on that route use the existing PATCH path. - [ ] On 409 (id already exists): surface inline error on the id input ("already exists, pick another"); no navigation; canvas stays in create mode. - [ ] On 422 (validation failed): highlight offending nodes via `data-invalid` (reuse the edit-mode `FlowValidationError` flow). - [ ] Dirty tracking flips on: drop / delete node, connect / remove edge, toolbar field change. `beforeunload` warning matches edit mode. ### Architecture - [ ] `flowsApi.createFlow(graph): Promise<FlowSummary>` — new client function (POSTs full body). Returns the server summary mapped through `toClientSummary`. - [ ] `FlowCanvas` accepts three new props: - `mode?: "edit" | "create"` — default `"edit"`. - `initialDetail?: FlowDetailResponse` — used in create mode instead of `useQuery`. - `onCreate?: (graph: Graph) => Promise<{ id: string }>` — used instead of `updateFlow` on first save. - [ ] Edit-mode behavior unchanged (no regressions in existing 18 FlowCanvasEditing tests). ### Tests (target ≥12) - [ ] Toolbar renders trigger dropdown populated from `TRIGGER_KINDS`. - [ ] Toolbar renders id input with format validation (rejects `Foo!`, accepts `my-flow`). - [ ] Save disabled until trigger + id valid + graph valid. - [ ] Dropping a node trips the dirty bit. - [ ] Save with valid body calls `createFlow` with the expected shape. - [ ] 201 response navigates to `/app/flows/:newId`. - [ ] 409 conflict surfaces inline id-input error, stays on `/app/flows/new`. - [ ] 422 validation highlights offending nodes via `data-invalid` (mirrors edit mode). - [ ] Discard with dirty prompts confirmation; cleanly cancels. - [ ] Source node auto-seeded + undeletable in create mode. - [ ] Empty trigger → save disabled even with otherwise-valid graph. - [ ] beforeunload warning fires when dirty (mirrors edit mode). ### Server side - Zero changes. `POST /flows` shipped with NF-7 (#363) including 409 / 422 envelopes + audit log row with `action: "create"`. ## Out of scope - Changing trigger / id after save (post-create immutability is fine for v1). - localStorage draft persistence (refresh-during-edit loses state — acceptable v1, tracked as follow-up). - Templates / preset graphs. - Operator-authored fixtures from the new-flow page. ## References - Spec: `specs/node-flows-ui.md` § Stories — currently has no NF-UI-10; this issue is the first story past NF-UI-9 in the UI sequence. - `apps/web/src/features/flows/FlowCanvas.tsx` — reference for the edit-mode toolbar + save handler the create mode mirrors. - `apps/server/src/http/flows-routes.ts::handleFlowCreate` — the POST endpoint already shipped. - `apps/web/src/features/flows/flowsApi.ts::updateFlow` — sibling shape to copy for `createFlow`. - `packages/shared/src/trigger-event.ts::TRIGGER_KINDS` — runtime catalog the trigger picker reads. ## Dependencies - NF-7 (#328, merged via #363) — server POST endpoint. - NF-UI-3 (#333, merged via #351) — edit canvas the create canvas reuses. - #366 (merged via #368) — flowsApi swapped to real fetch; `createFlow` is the same pattern.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#370
No description provided.