EW-617 G4 — Wizard "Generate now" + POST /api/works/quick-create
Sub-task: EW-621. Parent epic: EW-617.
Companion gaps (separate PRs): G6 #752 (merged-ready), G2 #756 (anon auth), G3 #757 (claim-account), G1/G5/G7/G8 planned.
Goal
Replace the wizard's hand-off to /works/new with a single "Generate now"
button that creates a Work and starts AI generation in one API call. When
a prompt is carried over from the landing page (G1), the wizard jumps
straight to this final step.
Functional requirements
- FR-G4-1
OnboardingWizardStateV2gains an optionalprompt: stringfield, persisted server-side alongside the existingai/storage/deploychoices. The patch endpoint accepts it viaOnboardingStatePatchRequest.state.prompt. - FR-G4-2 A new
POST /api/works/quick-createendpoint (authenticated byAuthSessionGuard, throttled 10/min per IP) acceptsQuickCreateWorkDto { slug, name, description, prompt, organization?, owner?, gitProvider?, deployProvider?, storageProvider?, websiteTemplateId?, model?, readmeConfig? }and: 1. CallsWorkLifecycleService.createWorkwith the subset that maps toCreateWorkDto. Provider defaults (storage/deploy/git) are resolved from the user's onboarding state insidecreateWork— the endpoint forwards explicit overrides only. 2. CallsWorkGenerationService.generateItems(workId, { name, prompt, model? }, user, /*awaitCompletion*/ false)to dispatch generation asynchronously. 3. Returns202 Acceptedwith{ status: 'pending', work: { id, slug, name }, generation: { historyId, message } }. - FR-G4-3 When
createWorkreturns a non-success status the endpoint MUST throwBadRequestExceptionand not attempt to start generation. - FR-G4-4 Generation errors after a successful create MUST bubble up
unmodified — the caller can retry generation via
POST /works/:id/generateagainst the new work id. - FR-G4-5
CreateWorkStepMUST render "Generate now" only when a non-emptypromptis available and anonQuickCreatecallback is passed. Otherwise it renders the legacy "Create your first work" link to/works/new. The component MUST be I/O-free; the parent owns the fetch. - FR-G4-6 On wizard mount, the parent MUST read
?prompt=…from the URL query OR from the#prompt=…URL fragment, seedflow.setPrompt(...), jump to thecreate-workstep, and strip the parameter from the URL viahistory.replaceState. Fragment transport is preferred (lands client-side only, never hits server logs).
Non-functional requirements
- NFR-G4-1 Slug + name derivation on the client MUST be deterministic
enough for the server's validation to pass without bouncing: slug is
^[a-z0-9]+(?:-[a-z0-9]+)*$, max 46 chars, and SHOULD include a short randomized suffix to dodge collisions across users typing the same prompt. - NFR-G4-2 The "Generate now" button MUST disable itself while the request is in flight and surface API errors inline (no global toast) so the user can retry without losing wizard context.
- NFR-G4-3 Prompt MUST be bounded to 5000 chars on the wire (matches
CreateItemsGeneratorDto.prompt).
Out of scope (other gaps)
- G1 — landing-page input + signed-token URL handoff (EW-618).
- G3 — claim-account banner shown after a successful quick-create (EW-620, in flight as #757).
- G7 — captcha / global cap (EW-624).
- G8 — funnel telemetry for the quick-create event (EW-625).
Acceptance
- A logged-in (or anonymous, post-G2) user with
promptpopulated on their wizard state can click "Generate now" and receive a 202 with a pending generation history id. PollingGET /works/:id/generation-historyshows the run progressing. - Landing on
app.ever.works/onboarding#prompt=AI%20coding%20assistantsdrops the user on thecreate-workstep with the prompt pre-filled and the URL cleaned toapp.ever.works/onboarding.