Task Breakdown: Onboarding Wizard v2
Ordered, granular tasks derived from
plan.md. Each task is small enough to land in a single commit (and ideally tested) per Constitution Principle VI.
Feature ID: onboarding-wizard-v2
Plan: ./plan.md
Status: Draft
Last updated: 2026-05-11
How to use
- Phases are sequential. Within a phase, tasks marked
(parallel)can run alongside their predecessor. - Each task names explicit file paths so an implementer can pick it up cold.
- Use the checkbox to track progress as commits/PRs land.
- Add new tasks at the bottom rather than renumbering.
Phase 1 — Plugin scaffolding (commit: chore(plugins))
- T1. Scaffold
packages/plugins/grok/by copyingpackages/plugins/groq/and adjustingpackage.jsonmetadata (id: 'grok', name "Grok (xAI)",uiHints.includeInOnboarding: true,onboardingPriority: 3, brand icon). - T2. Implement
packages/plugins/grok/src/grok.plugin.tsextendingBaseAiProviderwithproviderType: 'openai',baseURL: 'https://api.x.ai/v1/', settings schema (apiKeyx-secret,defaultModel: 'grok-2-latest', tiered models,temperature,maxTokens),getCapabilities(),validateConnection()and an xAI-flavoured readme. - T3. Vitest spec at
packages/plugins/grok/src/__tests__/grok.plugin.spec.tscovering manifest shape, schema, connection validation happy + sad path with mockedfetch. - T4. Register
grokin the plugin loader / build pipeline (anywheregroqis referenced as a built-in). - T5. Add
apiKeytopackages/plugins/gemini/src/gemini.plugin.ts(x-secret,x-envVar: GEMINI_API_KEY,x-scope: 'user'). Update existing Vitest spec to assert presence and to assert CLI-mode still works whenapiKeyis empty. - T6. Flip
packages/plugins/k8s/package.jsoneverworks.plugin.uiHints.includeInOnboardingfromfalsetotrue; addonboardingPriority: 4andonboardingDescription. Confirm the existing k8s test suite still passes. - T7. Copy tweak in
claude-codeconfig wizard (packages/plugins/claude-code/...) clarifying "Use OAuth token for Claude Pro/Max subscription (no per-token cost); otherwise paste an Anthropic API key." - T8. Lint + type-check the four touched plugin packages; commit as
chore(plugins): add grok, gemini api key, k8s onboarding, claude-code copy.
Phase 2 — Agent provider work (commit: feat(agent))
Data model
- T9. Add
storageProvideranddeployProvidercolumns to theWorkentity inpackages/agent/src/entities/work.entity.ts. Defaults'user-github'/'vercel'. Update entity tests inpackages/agent/src/entities/__tests__/. - T10. Hand-written TypeORM migration at
apps/api/src/migrations/{nextTs}-AddWorkStorageAndDeployProvider.tsadding both columns plus the partial indexidx_works_user_deploy_active. - T11. Update
packages/agent/src/dto/create-work.dto.ts+ any other Work DTOs to optionally acceptstorageProvider/deployProvider. Validate enums.
Ever Works Git provider
- T12. Env config: extend
apps/api/src/config/withSTORAGE_EVER_WORKS_GIT_ENABLED,EVER_WORKS_CUSTOMERS_GITHUB_ORG(default'ever-works-cloud'),EVER_WORKS_CUSTOMERS_GITHUB_PAT,EVER_WORKS_CUSTOMERS_GITHUB_VISIBILITY. Document each in.env.example. - T13. New
EverWorksGitProvideratpackages/agent/src/git/ever-works-git.provider.tsimplementing theGitProvidershape:createRepository(work)→ OctokitPOST /orgs/{org}/repos.push(work, content)anddelete(work)(soft).- Each call wraps
ActivityLogService.record({ actorKind: 'platform', userId, action, resource }).
- T14. Branch in
packages/agent/src/facades/git.facade.tscreateRepository(): switch onwork.storageProvider. Existinguser-githubpath stays the default. - T15. Reject
storageProvider = 'ever-works-git'with typedstorage_provider_disablederror inWorksServicewhen the env flag is off. - T16. Jest tests in
packages/agent/src/git/__tests__/ever-works-git.provider.spec.tsmocking Octokit: happy path, collision retry with-{shortId}, PAT-missing failure, activity-log written.
Ever Works Deploy provider
- T17. Env config:
DEPLOY_EVER_WORKS_ENABLED,EVER_WORKS_DEPLOY_KUBECONFIG/_PATH,EVER_WORKS_DEPLOY_NAMESPACE,EVER_WORKS_DEPLOY_INGRESS_HOST_TEMPLATE,EVER_WORKS_DEPLOY_INGRESS_CLASS,EVER_WORKS_DEPLOY_TLS_ISSUER,EVER_WORKS_DEPLOY_REGISTRY,EVER_WORKS_DEPLOY_MAX_WORKS_PER_USER(default3). Documented in.env.example. - T18. New
EverWorksK8sDeployProvideratpackages/agent/src/deployment/ever-works-k8s.provider.ts:getDeployConfig(user)builds ak8splugin config from env vars (substitute{slug}in ingress host template).ensureNamespace(user)— idempotent.deploy(work)— calls the existing k8s plugin deploy primitive.
- T19. Quota service:
checkQuota(userId)queriesworksfiltered by user + provider + active status. Throws typedQuotaExceededError. Wired intoWorksService.createand any redeploy path. - T20. Branch in deployment façade (the existing
apps/api/src/plugins-capabilities/deploy/...or equivalent): whenwork.deployProvider === 'ever-works', route throughEverWorksK8sDeployProvider; otherwise unchanged. - T21. Jest tests in
packages/agent/src/deployment/__tests__/ever-works-k8s.provider.spec.ts: env-derived config, namespace creation idempotency, quota under/over scenarios, env-flag-off rejection.
WorksService glue
- T22. In
WorksService.create(and wherever defaults flow), readusers.onboarding_stateand seedwork.storageProvider/work.deployProviderfrom the user's choices, falling back to entity defaults. - T23. Run quota check before creating a Work with
deployProvider = 'ever-works'. Surface as429 quota_exceededto the controller.
Commit
- T24.
pnpm testinpackages/agent+apps/api(the touched modules). Commit asfeat(agent): ever-works-git + ever-works-k8s providers, work provider columns, quota.
Phase 3 — Onboarding API + user state (commit: feat(api))
- T25. Add
onboardingCompletedAt,onboardingDismissedAt,onboardingStatecolumns topackages/agent/src/entities/user.entity.ts. DefaultsNULL. Update user entity tests. - T26. Hand-written migration at
apps/api/src/migrations/{nextTs+1}-AddUserOnboardingState.tsadding the three columns. Forward-only, additive. - T27. Zod schema for
onboarding_statepayload atpackages/contracts/src/api/onboarding/state.ts(version, choices, lastStep, skippedSteps, pluginsReviewed). Re-export from the API barrel. - T28.
OnboardingStateControlleratapps/api/src/onboarding/onboarding-state.controller.tsexposing:GET /api/onboarding/state— returns row, normalised to v2 shape.PATCH /api/onboarding/state— partial update, class-validator + Zod, validates enum membership.POST /api/onboarding/complete— setsonboardingCompletedAt = now().POST /api/onboarding/dismiss— setsonboardingDismissedAt = now().
- T29.
OnboardingStateServiceatapps/api/src/onboarding/onboarding-state.service.tswith the corresponding repository methods. - T30.
OnboardingCatalogControlleratapps/api/src/onboarding/onboarding-catalog.controller.tswithGET /api/onboarding/catalog. Service reads env flags and the plugin manifests to produce the AI / Storage / Deploy / Plugins lists. - T31. Auto-complete hook: when
WorksService.createsucceeds AND all chosen vendors have credentials, callOnboardingStateService.markCompletedIfReady(userId). - T32. Jest controller + service specs at
apps/api/src/onboarding/__tests__/onboarding-state.controller.spec.tsand.service.spec.ts. Cover auth, validation, idempotency, env-flag-off catalog filtering, auto-complete trigger. - T33. Wire
OnboardingModuleintoapps/api/src/api.module.ts. - T34.
pnpm testinapps/api. Commit asfeat(api): onboarding state + catalog endpoints, user columns.
Phase 4 — Web wizard (commit: feat(web))
Hook + state sync
- T35.
apps/web/src/components/onboarding/useOnboardingFlow.ts—useReducerstep machine. Inputs: server state + catalog. Outputs:step,canGoBack,canSkip,goNext / goBack / skip / refresh, and a sync side-effect that PATCHes the server on every transition (debounced 300 ms). - T36. Refactor
apps/web/src/components/onboarding/use-onboarding-state.tsto thin SWR-style hook over/api/onboarding/state. Keep localStorage only as offline-cache.
Reusable components
- T37.
ChoiceCard.tsxandChoiceCardGrid.tsxatapps/web/src/components/onboarding/. Props: logo, title, description, badges (Default / BYOK / Planned),disabled,selected,onSelect. - T38.
PluginsCatalogGrid.tsx+ inline-expand behaviour, embedding existingOnboardingPluginStep. - T39.
WizardFooter.tsxwith Back / Skip / Refresh / Next, hidden/disabled per props.
Steps
- T40.
WelcomeStep.tsx— extracted from the existing welcome JSX inEverWorksOnboardingWizard.tsx. - T41.
AIChoiceStep.tsx— six cards from the AI catalog: ever-works (default), openrouter, claude-code, codex, gemini, grok. - T42.
AIConfigStep.tsx— whenchoice !== 'ever-works', renders the chosen plugin's existingOnboardingPluginStep. - T43.
StorageChoiceStep.tsx— four cards: ever-works-git (default, Planned-when-disabled), user-github, user-gitlab (Planned), user-git (Planned). - T44.
StorageConfigStep.tsx— whenchoice === 'user-github', render the existing GitHub OAuth/Connect flow; otherwise auto-skip. - T45.
DeployChoiceStep.tsx— three cards: ever-works (default, Planned-when-disabled), vercel, k8s. - T46.
DeployConfigStep.tsx— whenchoice === 'vercel'orchoice === 'k8s', render the chosen plugin'sOnboardingPluginStep; otherwise auto-skip. - T47.
PluginsCatalogStep.tsx— renders cards formake,sim-ai,zapier,activepieces(driven by the catalog endpoint). "Skip — set up later" primary button. Inline expand per card. - T48.
CreateWorkStep.tsx— extracted from the existing work-step JSX.
Driver + integration
- T49. Rewrite
apps/web/src/components/onboarding/EverWorksOnboardingWizard.tsxto drive the new step machine; keep the dialog chrome, progress bar, and sidebar. - T50.
apps/web/src/app/[locale]/(dashboard)/layout.tsx— server-side fetch/api/onboarding/stateand/api/onboarding/catalog; pass into client. - T51. Update
apps/web/src/app/[locale]/(dashboard)/layout-client.tsxto consume the new props and render the rewritten wizard.
Telemetry
- T52.
apps/web/src/app/actions/onboarding/track.ts—'use server'action callingAnalyticsService.track. Validate event name against a whitelist. - T53. Wire events listed in
plan.md§8 inside the wizard hook + footer + cards.
Static assets
- T54. Vendor SVG logos under
apps/web/public/logos/{openrouter,anthropic,xai,google,vercel,github,gitlab,kubernetes,everworks}.svg. Note licence / source comment per file.
Tests
- T55. Vitest unit tests for
useOnboardingFlowcovering every (ai, storage, deploy) combo + skip + back + refresh + planned-card click. - T56. Playwright e2e at
apps/web/e2e/onboarding-wizard-v2.spec.ts:- Full-defaults happy path → create Work.
- OpenRouter BYOK path → create Work with
ai.choice = 'openrouter'. - Claude Code subscription path → uses
oauthToken. - Back / Skip / Refresh on a config step.
- Reopen after clearing cookies restores server state.
- Quota-block: with
EVER_WORKS_DEPLOY_MAX_WORKS_PER_USER=1and one existing Work, creating a second ever-works-deploy Work shows the quota error.
Commit + PR
- T57.
pnpm lint,pnpm type-check, web build clean. Commit asfeat(web): onboarding wizard v2 with choice-driven flow, plugins catalog, telemetry. - T58. Open PR
feat/ew-onboarding-wizard-v2→develop. Body links this spec; tracks Jira ticket[EW-XXX](added by owner when ticket is created). - T59. Follow develop → stage → main after CI green, per release-flow memory.
Phase 5 — Post-merge follow-ups
The original "deferred" set landed in the same PR after the user pulled
them forward (apps/web Vitest runner, Playwright wizard spec, brand SVGs,
WorksService.create wire-up). Items still requiring ops action are
documented in ./deployment.md:
- T60. Create the
ever-works-cloudGitHub PAT (manual web-UI step — GitHub disallows API creation; see deployment.md §1.3); store in k8s + GH-Actions secrets; flipSTORAGE_EVER_WORKS_GIT_ENABLED=true. Shipped 2026-05-12 via PR #721. - T61. Push the
k8s-workskubeconfig into the API deployment secret asEVER_WORKS_DEPLOY_KUBECONFIG; flipDEPLOY_EVER_WORKS_ENABLED=true(see deployment.md §2). Blocked onk8s-workscluster + SSL termination being owner-provisioned. - T62. Wire
EverWorksGitProviderintoWorkLifecycleService.createWorkso picking "Ever Works Git" actually publishes toever-works-cloudvia the platform PAT (not the user's OAuth). Shipped 2026-05-13 via EW-614 — see./ew-614-storage-wireup.md. - T63. Admin override for the 3-Work cap (lifts the limit for a specific user — useful for internal Ever Works staff demoing the flow).
- T64. Cost reporting / per-user usage panel for Ever Works Deploy.
- T65. Migrate
EverWorksGitProviderto the GitHub App installation-token path so we don't rely on a long-lived PAT (the App is already installed for user GitHub flows). Tracked as a follow-up on EW-614. - T66. Orphan-repo cleanup cron over
ever-works-cloudrepos with no matching Work row (handles the rare slug-collision rollback case where the GitHub repo was created but the DB insert failed — see EW-614 wire-up doc §2).