Skip to content

AI-First Starting UX: Build with AI path#4902

Open
lmac-1 wants to merge 18 commits into
4848-ai-first-starting-ux-parentfrom
4868-build-with-ai
Open

AI-First Starting UX: Build with AI path#4902
lmac-1 wants to merge 18 commits into
4848-ai-first-starting-ux-parentfrom
4868-build-with-ai

Conversation

@lmac-1

@lmac-1 lmac-1 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What I need in this review: Focus on the "Build with AI" path of the epic. Any big code organisation problems will be fixed in the clean up issue.

Description

This PR adds the "Build with AI path" of the #4848 AI-First Starting UX epic.

How it works:

  1. User goes to /new and types a prompt in the "Build with AI" textarea
  2. Pressing Enter or clicking "Build it →" dismisses the landing screen and opens the AI panel with the prompt pre
    sent
  3. The canvas stays blank while the AI processes
  4. When the AI returns YAML, it is auto-applied to the canvas and saved to the DB automatically — no "Create" button
  5. The URL transitions from /new to /projects/{id}/w/{id} without a page reload

Key decisions:

  • Auto-save is silent. No "Workflow saved" toast fires on first creation — it would be noise in a flow the user didn't manually trigger.
  • Panel lock while unsaved. The AI panel cannot be closed (⌘K disabled, close button hidden) while the workflow hasn't been saved yet. Closing it would leave the user on a blank canvas with no path forward.
  • Validation errors route to chat, not toasts. If the AI returns invalid YAML, the error is injected as a message into the chat panel so the user can ask the AI to fix it, rather than hitting a dead-end toast.
  • Save failure shows a Retry button. If the DB save fails, a toast fires with a Retry action that retries only the save — the YAML has already been applied to the canvas so re-importing it is unnecessary. Fit-view is skipped on failure so the canvas doesn't zoom into an unpersisted workflow.
  • Streaming dedup via appliedViaStreamingRef. The streaming streamingChanges event and the final new_message both carry the same YAML. The ref is set before the streaming apply and cleared by the auto-apply effect after skipping, preventing a double import. The ref is also reset on apply or save failure so the new_message path can retry if streaming failed.
  • URL params are blocked on /new. Params like ?panel=run, ?panel=editor, ?panel=settings, and ?chat=true could open panels or bypass the landing screen before the user has done anything. The store ignores all URL params at init time when isNewWorkflow=true, and WorkflowEditor guards its URL→panel sync effect with the same flag. This prevents e.g. someone sharing a /new?panel=run link that skips straight to the run panel on a blank workflow.

Closes #4868

Validation steps

  1. Go to /new, type a prompt, press Enter — confirm landing screen dismisses, AI panel opens with prompt pre-sent
  2. Wait for AI to return YAML — confirm workflow appears on canvas and URL updates to /w/{id} without a reload
  3. While AI is processing, ⌘K — should do nothing
  4. After creation, confirm panel can be freely opened and closed
  5. Test with a prompt that produces invalid YAML (might be hard to do - might have to hack it with Claude) — confirm error appears in the chat panel, canvas stays blank, panel stays locked

Additional notes for the reviewer

  1. What do you think about the toolbar appearing? Is the effect very jumpy and jarring? What could be the alternative? A completely read-only toolbar before save?
  2. We are still working out how the disclaimer will work in this new flow. To be addressed in follow-up work.
  3. There are still details to be ironed out. For example, you can still "View conversations" in AI tab before you have created a workflow. Still need to iron out other areas that you shouldn't be able to "get to" before a workflow is created. This can be done in follow-up work.
  4. I am not satisfied with the error handling flow, and might need help to tidy this up in some follow-on work for the AI assistant in general - from a Claude review: the long tail of fix(useAIWorkflowApplications): ... commits (9+) is real evidence of implicit state churn, not just normal bug fixing. appliedViaStreamingRef alone is now reset from four different call sites for four different failure paths, alongside appliedMessageIdsRef and hasLoadedSessionRef tracking overlapping concerns. This is the classic shape of state that wants to be an explicit state machine (idle | streaming-applied | applying | save-failed) instead of booleans mutated ad hoc — worth a follow-up ticket before more logic piles on top.
  5. When reviewing, make it clear that there is still some legacy code from the old "Create a new workflow" route using the side panel. This will be cleaned up in a follow up issue.
  6. Landing screen gate is reactive, not static. createUIStore is created once inside useState — on a slow connection the channel hasn't joined yet, so isNewWorkflow is false at init time. Initialising showLandingScreen: true always and moving the isNewWorkflow guard into useShowLandingScreen (which reads from the reactive SessionContextStore) means the landing screen correctly appears once the channel joins, even on slow connections.

AI Usage

Please disclose whether you've used AI anywhere in this PR (it's cool, we just
want to know!):

  • I have used Claude Code
  • I have used another model
  • I have not used AI

You can read more details in our
Responsible AI Policy

Pre-submission checklist

  • I have performed an AI review of my code (we recommend using /review
    with Claude Code)
  • I have implemented and tested all related authorization policies.
    (e.g., :owner, :admin, :editor, :viewer)
  • I have updated the changelog.
  • I have ticked a box in "AI usage" in this PR

lmac-1 added 5 commits June 26, 2026 13:51
…ew workflows

When YAML validation fails (syntax, ID format, schema) during handleApplyWorkflow
and the workflow is new, inject the error as an assistant message into the AI chat
thread instead of showing a toast. Save failures keep the existing toast behaviour.
Add tests for the six untested behaviours introduced in #4868:
- auto-save called after importWorkflow when isNewWorkflow
- validation errors routed to onValidationError callback, not toast
- save failures show toast and do not invoke onValidationError
- close button absent when onClose is undefined
- empty-canvas placeholder suppressed when AI panel is open

Also adds saveWorkflow to mockWorkflowActions and isNewWorkflow: false
to all existing handleApplyWorkflow renderHook calls to match the
updated hook interface.
…e fails

When saveWorkflow rejects on a new workflow, fit-view was incorrectly
dispatched after doneApplyingWorkflow, implying the workflow was
successfully persisted when it wasn't. Add a saveSucceeded flag and
skip fit-view when save fails.

Also wires up the Retry action on the save-failure toast so the user
can re-attempt the full apply+save without losing their canvas state.
@github-project-automation github-project-automation Bot moved this to New Issues in Core Jun 26, 2026
lmac-1 added 13 commits June 29, 2026 15:18
…ies workflow

The workflow was being applied twice — once via streaming and again when the
final new_message arrived — causing a transient dirty state (unsaved red dot)
between the second import and save.

Consolidate the deduplication flag into useAIWorkflowApplications so the
"mark as applied + skip" logic happens atomically in one place rather than
split across two separate useEffect passes in the wrapper.

Test changes: delete a duplicate test identical to an existing case, tighten
the streaming-skip assertion to also verify saveWorkflow is not called.
On /new, URL params like ?chat=true, ?method=template, ?panel=run,
?panel=editor, and ?panel=settings could bypass the landing screen or
open panels that shouldn't be reachable before the user takes an action.

- createUIStore: return clean defaults when isNewWorkflow=true so the
  store ignores all URL params at init time
- WorkflowEditor: guard the URL->panel sync effect with isNewWorkflow
  so ?panel=run can't open the run panel
- WorkflowEditor: short-circuit isIDEOpen and showInspector on new
  workflows so FullScreenIDE (z-50) and Inspector can't render above
  the landing screen via URL params
- Add TODO-AI-FIRST annotations on the method URL sync and old
  placeholder block for cleanup when the left-panel flow is removed
…g YAML

When the initial save fails after AI applies a workflow, the Retry button
was re-running the full handleApplyWorkflow (importWorkflow + save). Add a
saveWorkflowRef that points at the latest saveWorkflow callback so Retry
only calls save, skipping the already-successful canvas apply.

Also set duration: Infinity on the save-failure toast so it persists until
the user explicitly retries or navigates away.
…ntent

The error rendering for assistant messages changed: non-empty error content
now renders inline in a styled red box (ai-validation-error) rather than
showing the hardcoded 'Failed to send message' banner. Split the single
test into two cases — one for non-empty content (styled box) and one for
empty content (banner).
…ations test fixtures

Both test files were missing the now-required saveWorkflow field in
mockWorkflowActions and the required isNewWorkflow boolean at every
renderHook call site, causing TypeScript compile errors.
… failure

When streaming fires mid-conversation and handleApplyWorkflow fails
(e.g. invalid YAML, importWorkflow throws), appliedViaStreamingRef was
left as true. The session-load guard only runs once per session, so
the next real new_message would hit the early-return and the workflow
would silently never be applied.

Reset the ref in the catch block so a failed streaming apply doesn't
block the subsequent settled-message auto-apply.
…toast

The save-failure Retry onClick used void to discard the promise, so a
second save failure was silently swallowed with no user feedback.

Chain a .catch on the retry promise to show another alert if the
retry also fails.
… channel join

createUIStore initialises once inside useState — if the channel hasn't
joined by mount time, isNewWorkflow is false and showLandingScreen was
permanently frozen as false.

Initialize showLandingScreen to true and move the isNewWorkflow gate into
useShowLandingScreen, where it reads from the reactive SessionContextStore.
On slow connections the landing screen now correctly appears once the
channel joins and delivers isNewWorkflow=true.
…on disconnect

saveWorkflow returns null (not throws) when the WebSocket is disconnected.
The null return was silently treated as success — saveSucceeded stayed true,
fit-view fired, and the landing screen stayed dismissed.

Check the return value and throw on null so the existing catch path handles
it: shows the save-failure toast with a Retry button and sets saveSucceeded
to false. The retry path is also fixed to handle null via an async IIFE.
…failure after streaming apply

When importWorkflow succeeded but saveWorkflow subsequently failed, the
inner catch set saveSucceeded=false but never reset appliedViaStreamingRef.
The outer catch (which does reset it) was never reached, leaving the ref
true. The next confirmed message from the server was then silently skipped
by the auto-apply effect.

Reset the ref in the inner save-failure catch, mirroring the existing reset
in the outer catch. Covers both thrown errors and null returns (disconnect).
…wRef

The ref was assigned but never called — the Retry toast uses saveWorkflowRef
and callers receive handleApplyWorkflow directly from the hook return value.
@lmac-1 lmac-1 marked this pull request as ready for review June 30, 2026 11:13
@github-actions

Copy link
Copy Markdown

All 15 changed files are frontend-only (TypeScript/React in assets/js/collaborative-editor/ and matching tests). No Elixir, Ecto queries, web-layer endpoints, policies, or Repo writes are touched, so none of S0/S1/S2 are in scope.

Security Review ✅

  • S0 (project scoping): N/A — all changes are frontend React/TypeScript in assets/js/collaborative-editor/ with no new queries or web-layer entrypoints.
  • S1 (authorization): N/A — no new controllers, LiveView events, or policy modules; existing useWorkflowReadOnly/isNewWorkflow gating is unchanged.
  • S2 (audit trail): N/A — no Repo.insert/update/delete on config resources; saveWorkflow continues to flow through the existing server-side persistence path.

@lmac-1 lmac-1 requested a review from elias-ba June 30, 2026 11:40
@nintyboy

nintyboy commented Jul 1, 2026

Copy link
Copy Markdown

Hey @lmac-1 popped together some looms for design review:

No Ai : https://www.loom.com/share/6d76a5aea6e54a0bab8a577b6d432d26
With AI: https://www.loom.com/share/3c53229a24324705aa288b6135bd9e8f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New Issues

Development

Successfully merging this pull request may close these issues.

2 participants