Add onboarding status to Project model and implement related database…#1246
Add onboarding status to Project model and implement related database…#1246
Conversation
… migrations - Introduced `onboardingStatus` field to the Project model with a default value of "completed". - Created migration scripts to add the new column and validate its values against predefined statuses. - Updated relevant queries and components to handle the new onboarding status in project creation and updates. - Enhanced tests to ensure proper functionality of the onboarding status feature.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Project onboardingStatus field end-to-end: DB migrations (with CHECK constraint and validation), Prisma schema, backend read/write mappings, SDK/types, dashboard onboarding wizard and list UI updates, tests, and shared schemas/types. Changes
Sequence DiagramsequenceDiagram
participant User as User
participant Dashboard as Dashboard UI
participant Backend as Backend API
participant DB as Database
User->>Dashboard: Open new-project or reopen incomplete project
Dashboard->>Backend: POST /projects or PATCH /projects/{id} (include onboarding_status)
Backend->>DB: INSERT/UPDATE "Project" (onboardingStatus)
DB-->>Backend: Persisted/Updated
Backend-->>Dashboard: Project payload (includes onboarding_status)
Dashboard->>User: Render wizard step based on onboarding_status
loop Each onboarding step
User->>Dashboard: Complete step → Next
Dashboard->>Backend: PATCH /projects/{id} (update onboarding_status)
Backend->>DB: UPDATE "Project" (onboardingStatus)
DB-->>Backend: Updated
Backend-->>Dashboard: Confirmed status
end
User->>Dashboard: Finish final step
Dashboard->>Backend: PATCH /projects/{id} (onboarding_status: "completed")
Backend->>DB: UPDATE "Project"
DB-->>Backend: Completed
Backend-->>Dashboard: Completed
Dashboard->>User: Redirect to project page
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds a persisted onboardingStatus to Project (DB + shared schemas) and uses it in the dashboard to support resumable onboarding and “setup incomplete” routing/badging.
Changes:
- Introduces
onboarding_statusas a validated field across shared schemas/CRUD + template SDK typing/mapping. - Adds Prisma column + CHECK constraint (NOT VALID) and a follow-up migration to VALIDATE the constraint, with migration tests.
- Updates dashboard project list + new-project onboarding flow to read/write onboarding status via
/internal/projects.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/template/src/lib/stack-app/projects/index.ts | Adds onboardingStatus to template SDK project types + update mapping. |
| packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts | Maps onboarding_status from CRUD read into AdminProject. |
| packages/stack-shared/src/schema-fields.ts | Defines canonical onboarding status values/type/schema. |
| packages/stack-shared/src/interface/crud/projects.ts | Adds onboarding_status to admin read/update/create schemas. |
| apps/backend/prisma/schema.prisma | Adds onboardingStatus column with default completed. |
| apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/migration.sql | Adds column + NOT VALID CHECK constraint. |
| apps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/migration.sql | Validates the CHECK constraint. |
| apps/backend/prisma/migrations/**/tests/*.ts | Verifies default + constraint behavior + validation. |
| apps/backend/src/lib/projects.tsx | Plumbs onboarding status through project read + create/update code paths. |
| apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx | Fetches onboarding statuses to route/badge incomplete projects. |
| apps/dashboard/src/components/project-card.tsx | Updates UI to show “Setup incomplete” styling/badge and configurable href. |
| apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx | Replaces simple create flow with resumable onboarding wizard + status persistence. |
| apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts | Updates expectations to include onboarding_status. |
| claude/CLAUDE-KNOWLEDGE.md | Adds internal guidance notes related to onboarding/status usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
Greptile SummaryThis PR adds a multi-step project onboarding wizard by introducing an The migration strategy (add column with Key issues found:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User (Browser)
participant NP as /new-project page
participant API as /internal/projects API
participant DB as Postgres (Project table)
U->>NP: Visit /new-project
NP->>API: GET /internal/projects (client token)
API->>DB: SELECT id, onboardingStatus FROM Project
DB-->>API: rows
API-->>NP: { items: [{ id, onboarding_status }] }
NP->>NP: Build projectStatuses Map<id, status>
alt No project_id in URL
NP->>U: Show "Create Project" dialog
U->>NP: Submit name + team
NP->>API: POST /internal/projects (create)
API->>DB: INSERT Project (onboardingStatus='completed')
DB-->>API: new project
API-->>NP: newProject
NP->>API: PATCH /internal/projects/current { onboarding_status: 'config_choice' } (admin token)
API->>DB: UPDATE Project SET onboardingStatus='config_choice'
NP->>NP: updateSearchParams({ project_id })
end
alt project_id in URL & status != 'completed'
NP->>U: Render ProjectOnboardingWizard
loop Each onboarding step
U->>NP: Click "Next"
NP->>API: PATCH /internal/projects/current { onboarding_status: <next_step> }
API->>DB: UPDATE Project SET onboardingStatus=<next_step>
end
NP->>U: Navigate to /projects/:id on 'completed'
end
alt project_id in URL & status == 'completed'
NP->>U: Redirect to /projects/:id (via useEffect)
end
Last reviewed commit: bc87ba6 |
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts (1)
61-404:⚠️ Potential issue | 🟡 MinorAdd a round-trip E2E for non-default
onboarding_status.All of the new assertions here still only cover the default
"completed"path. Since the create/PATCH contract now acceptsonboarding_status, add at least one test that writes a non-default value such as"auth_setup"and reads it back, plus an invalid-value case if the endpoint is expected to reject bad statuses before the DB constraint fires.Based on learnings
Applies to **/apps/e2e/**/*.{ts,tsx} : ALWAYS add new E2E tests when you change the API or SDK interface. Generally, err on the side of creating too many tests.Also applies to: 457-663
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts` around lines 61 - 404, Add an E2E round-trip test that sets a non-default onboarding_status when creating a project and asserts the created project's body returns that same status (use the existing Project.create / Project.createAndGetAdminToken helpers used in these tests), and also add a negative test that posts an invalid onboarding_status value and expects a validation rejection (HTTP 4xx) before DB constraints run; place these next to the existing "creates a new project" and "creates a new project with different configurations" tests, reusing the same backendContext/Auth setup so the test flow mirrors Project.create/Project.createAndGetAdminToken and asserts on response.body.onboarding_status for the positive and the response status/body for the invalid-case.
🧹 Nitpick comments (2)
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx (2)
1161-1166: Inconsistent indentation in else block.Line 1165 has extra indentation that makes the code structure unclear. Both statements are in the else block, but the indentation suggests otherwise.
🧹 Fix indentation
if (includePayments) { await props.setStatus("payments_setup"); } else { await props.setStatus("completed"); - props.onComplete(); + props.onComplete(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 1161 - 1166, The else block after the includePayments check has inconsistent indentation: align both statements inside the else so they clearly belong together; specifically, in the block that calls await props.setStatus("completed"); and props.onComplete(); remove the extra leading spaces before props.onComplete() (and keep the same indentation level as the await line) so both lines are uniformly indented within the else branch (referencing includePayments, props.setStatus and props.onComplete to locate the code).
73-83: Duplicate constant definition — import from shared schema instead.
PROJECT_ONBOARDING_STATUSESduplicatesprojectOnboardingStatusValuesdefined inpackages/stack-shared/src/schema-fields.ts(see context snippet 3). Maintaining two identical lists risks drift and violates DRY.♻️ Proposed fix: import from shared package
-const PROJECT_ONBOARDING_STATUSES = [ - "config_choice", - "apps_selection", - "auth_setup", - "domain_setup", - "email_theme_setup", - "payments_setup", - "completed", -] as const; - -type ProjectOnboardingStatus = (typeof PROJECT_ONBOARDING_STATUSES)[number]; +import { projectOnboardingStatusValues, type ProjectOnboardingStatus } from "@stackframe/stack-shared/dist/schema-fields"; + +const PROJECT_ONBOARDING_STATUSES = projectOnboardingStatusValues;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 73 - 83, PROJECT_ONBOARDING_STATUSES and ProjectOnboardingStatus duplicate the canonical list projectOnboardingStatusValues in the shared package; remove the local constant and type and import projectOnboardingStatusValues from packages/stack-shared/src/schema-fields.ts, then recreate the type where needed as (typeof projectOnboardingStatusValues)[number] (or reference an exported type if available) and update any local usages to use the imported symbol instead of PROJECT_ONBOARDING_STATUSES.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.ts`:
- Around line 14-50: Add an UPDATE test in postMigration that attempts to set
the existing row (ctx.projectId) onboardingStatus to "invalid_status" and
asserts it rejects with the same constraint error
(Project_onboardingStatus_valid); locate the postMigration function and after
the existing SELECT/asserts, run an UPDATE on the row with id ctx.projectId to
set onboardingStatus = 'invalid_status' and expect the SQL promise to reject
with /Project_onboardingStatus_valid/ to prove update-time enforcement.
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx:
- Around line 188-214: deriveInitialSignInMethods currently seeds the Set with
default methods so disabled options can never be removed; change it to start
with an empty Set<SignInMethod> and only add methods when their corresponding
config flags are true (check config.credentialEnabled -> add "credential",
config.magicLinkEnabled -> add "magicLink", config.passkeyEnabled -> add
"passkey"), and still iterate config.oauthProviders to add provider.id for
"google", "github", "microsoft"; keep returning the resulting Set from the same
function.
- Around line 1431-1439: The loop building statusMap silently defaults unknown
item.onboarding_status values to "completed"; update the code around statusMap /
isProjectOnboardingStatus to either (A) add a clear comment explaining this
fallback is intentional for forward-compatibility, referencing onboarding_status
and isProjectOnboardingStatus, or (B) make it defensive by logging a warning
(e.g., via your existing logger) when onboarding_status is present but not
recognized and then decide whether to set a safe default or skip the entry;
implement one of these options so the rationale or the warning is explicit.
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/projects/page-client.tsx:
- Around line 25-35: Replace the local
PROJECT_ONBOARDING_STATUSES/ProjectOnboardingStatus with the canonical union
exported from `@stackframe/stack-shared` (import it and remove the hard-coded
array), then validate every project status you receive against that union
instead of coercing/ defaulting to "completed"; in the project list rendering
and the status fetch/loading handler (the page-client component and any
ProjectCard rendering code), treat unknown/missing statuses as loading/error: do
not render the project as completed or link to /projects/${id} until the status
is confirmed, and surface or throw an explicit error (or render an error state)
for any payload that does not match the shared ProjectOnboardingStatus union so
we fail loud rather than silently falling back.
---
Outside diff comments:
In `@apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts`:
- Around line 61-404: Add an E2E round-trip test that sets a non-default
onboarding_status when creating a project and asserts the created project's body
returns that same status (use the existing Project.create /
Project.createAndGetAdminToken helpers used in these tests), and also add a
negative test that posts an invalid onboarding_status value and expects a
validation rejection (HTTP 4xx) before DB constraints run; place these next to
the existing "creates a new project" and "creates a new project with different
configurations" tests, reusing the same backendContext/Auth setup so the test
flow mirrors Project.create/Project.createAndGetAdminToken and asserts on
response.body.onboarding_status for the positive and the response status/body
for the invalid-case.
---
Nitpick comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx:
- Around line 1161-1166: The else block after the includePayments check has
inconsistent indentation: align both statements inside the else so they clearly
belong together; specifically, in the block that calls await
props.setStatus("completed"); and props.onComplete(); remove the extra leading
spaces before props.onComplete() (and keep the same indentation level as the
await line) so both lines are uniformly indented within the else branch
(referencing includePayments, props.setStatus and props.onComplete to locate the
code).
- Around line 73-83: PROJECT_ONBOARDING_STATUSES and ProjectOnboardingStatus
duplicate the canonical list projectOnboardingStatusValues in the shared
package; remove the local constant and type and import
projectOnboardingStatusValues from packages/stack-shared/src/schema-fields.ts,
then recreate the type where needed as (typeof
projectOnboardingStatusValues)[number] (or reference an exported type if
available) and update any local usages to use the imported symbol instead of
PROJECT_ONBOARDING_STATUSES.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f4b7df96-3f26-442c-ade8-4bb15fe4f951
📒 Files selected for processing (15)
apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/migration.sqlapps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.tsapps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/migration.sqlapps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/tests/constraint-is-validated.tsapps/backend/prisma/schema.prismaapps/backend/src/lib/projects.tsxapps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsxapps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsxapps/dashboard/src/components/project-card.tsxapps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.tsclaude/CLAUDE-KNOWLEDGE.mdpackages/stack-shared/src/interface/crud/projects.tspackages/stack-shared/src/schema-fields.tspackages/template/src/lib/stack-app/apps/implementations/admin-app-impl.tspackages/template/src/lib/stack-app/projects/index.ts
...isma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.ts
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Show resolved
Hide resolved
…te status transitions - Removed the "Domains" step from the onboarding timeline. - Enhanced error handling for missing base URL in trusted domains. - Updated status transitions to skip directly from "domain_setup" to "email_theme_setup". - Refactored UI components for better readability and maintainability.
- Introduced error capturing for the "link-existing" mode in the new project onboarding flow. - Added a reference to prevent multiple captures of the same error. - Enhanced the useEffect hook to trigger error capture only when the mode changes to "link-existing".
There was a problem hiding this comment.
♻️ Duplicate comments (4)
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx (4)
188-195:⚠️ Potential issue | 🟠 Major
deriveInitialSignInMethodscurrently ignores disabled config flags.Line 190 pre-seeds methods (
credential,magicLink,github), so those remain enabled even when config says otherwise.🐛 Proposed fix
function deriveInitialSignInMethods(project: AdminOwnedProject): Set<SignInMethod> { const config = project.config; - const methods = new Set<SignInMethod>([ - "credential", - "magicLink", - "google", - "github", - ]); + const methods = new Set<SignInMethod>(); if (config.credentialEnabled) { methods.add("credential"); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 188 - 195, deriveInitialSignInMethods currently pre-populates the Set with all methods and ignores project.config, so disabled sign-in options stay enabled; update deriveInitialSignInMethods to read the project's config (project.config on AdminOwnedProject) and only add each SignInMethod ("credential", "magicLink", "google", "github") to the Set when the corresponding enable flag in config is true (or present), e.g., check the config properties that control credentials, magic link, Google, and GitHub before calling add on the Set.
1355-1357:⚠️ Potential issue | 🟡 MinorAvoid silently coercing unknown
onboarding_statusto"completed".Line 1356 hides unexpected backend values and can incorrectly route users as fully onboarded.
🛡️ Proposed fix
const onboardingStatus = "onboarding_status" in item ? item.onboarding_status : undefined; - statusMap.set(item.id, isProjectOnboardingStatus(onboardingStatus) ? onboardingStatus : "completed"); + if (onboardingStatus === undefined || onboardingStatus === null) { + statusMap.set(item.id, "completed"); + } else if (isProjectOnboardingStatus(onboardingStatus)) { + statusMap.set(item.id, onboardingStatus); + } else { + throw new Error(`Unexpected onboarding_status "${String(onboardingStatus)}" for project ${item.id}`); + }As per coding guidelines, "NEVER silently use fallback values when type errors occur... Fail early, fail loud."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 1355 - 1357, The code silently coerces unexpected onboarding_status values to "completed"; instead, validate onboardingStatus using isProjectOnboardingStatus and if it returns false, do not default to "completed" — either set statusMap for item.id to undefined or an explicit "unknown" sentinel and emit/log an error (or throw) so failures are loud; update the block around onboardingStatus, isProjectOnboardingStatus, and statusMap.set(item.id, ...) to fail fast and surface the invalid backend value (include item.id and the raw onboardingStatus in the log/exception).
1162-1165:⚠️ Potential issue | 🟠 Major"Do This Later" should not call
setupPayments().Line 1163 creates Stripe setup artifacts even when the user explicitly chooses to skip for now.
⚙️ Proposed fix
<DesignButton className="rounded-xl" variant="outline" loading={saving} onClick={() => runAsynchronouslyWithAlert(() => runWithSaving(async () => { - await props.project.app.setupPayments(); await finalizeOnboarding(); }))} > Do This Later </DesignButton>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 1162 - 1165, The "Do This Later" button is incorrectly invoking props.project.app.setupPayments() which creates Stripe artifacts; update the click handler used by runAsynchronouslyWithAlert -> runWithSaving so it does not call props.project.app.setupPayments() and only calls finalizeOnboarding() for this path (i.e., remove or conditionally skip the setupPayments() invocation in the onClick callback where finalizeOnboarding() is called). Ensure you only change the handler referenced by runAsynchronouslyWithAlert and runWithSaving so other flows that should perform setupPayments() remain unchanged.
446-467:⚠️ Potential issue | 🔴 Critical
iframe srcDocis missingsandbox, enabling script execution risk.Line 463 renders untrusted/templated HTML via
srcDocwithout sandboxing. That can execute scripts in a sensitive dashboard context.🔒 Proposed fix
- const inertPreviewHtml = previewHtml ? `${previewHtml} - <script> - document.addEventListener('click', function(e) { - var target = e.target; - while (target && target.tagName !== 'A') { - target = target.parentNode; - } - if (target && target.tagName === 'A') { - e.preventDefault(); - e.stopPropagation(); - } - }, true); - </script> - ` : previewHtml; + const inertPreviewHtml = previewHtml; return ( <iframe srcDoc={inertPreviewHtml} + sandbox="" className="pointer-events-none h-full w-full border-0" title="Email theme preview" /> );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 446 - 467, The iframe is rendering untrusted srcDoc (inertPreviewHtml) without sandboxing; add a sandbox attribute to the iframe element (e.g., sandbox="" to block scripts and same-origin) to prevent script execution and origin access, or if specific features are required, explicitly whitelist only the minimal flags (e.g., sandbox="allow-popups allow-forms") rather than allowing scripts or same-origin; update the JSX for the iframe that uses srcDoc={inertPreviewHtml} to include the appropriate sandbox attribute.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx:
- Around line 188-195: deriveInitialSignInMethods currently pre-populates the
Set with all methods and ignores project.config, so disabled sign-in options
stay enabled; update deriveInitialSignInMethods to read the project's config
(project.config on AdminOwnedProject) and only add each SignInMethod
("credential", "magicLink", "google", "github") to the Set when the
corresponding enable flag in config is true (or present), e.g., check the config
properties that control credentials, magic link, Google, and GitHub before
calling add on the Set.
- Around line 1355-1357: The code silently coerces unexpected onboarding_status
values to "completed"; instead, validate onboardingStatus using
isProjectOnboardingStatus and if it returns false, do not default to "completed"
— either set statusMap for item.id to undefined or an explicit "unknown"
sentinel and emit/log an error (or throw) so failures are loud; update the block
around onboardingStatus, isProjectOnboardingStatus, and statusMap.set(item.id,
...) to fail fast and surface the invalid backend value (include item.id and the
raw onboardingStatus in the log/exception).
- Around line 1162-1165: The "Do This Later" button is incorrectly invoking
props.project.app.setupPayments() which creates Stripe artifacts; update the
click handler used by runAsynchronouslyWithAlert -> runWithSaving so it does not
call props.project.app.setupPayments() and only calls finalizeOnboarding() for
this path (i.e., remove or conditionally skip the setupPayments() invocation in
the onClick callback where finalizeOnboarding() is called). Ensure you only
change the handler referenced by runAsynchronouslyWithAlert and runWithSaving so
other flows that should perform setupPayments() remain unchanged.
- Around line 446-467: The iframe is rendering untrusted srcDoc
(inertPreviewHtml) without sandboxing; add a sandbox attribute to the iframe
element (e.g., sandbox="" to block scripts and same-origin) to prevent script
execution and origin access, or if specific features are required, explicitly
whitelist only the minimal flags (e.g., sandbox="allow-popups allow-forms")
rather than allowing scripts or same-origin; update the JSX for the iframe that
uses srcDoc={inertPreviewHtml} to include the appropriate sandbox attribute.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e0932e46-dddb-42b2-b65f-26dee03d4ff7
📒 Files selected for processing (1)
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
- Updated the Project model to include onboarding status handling in various components. - Implemented validation for onboarding status to ensure only valid statuses are accepted, throwing errors for invalid or missing values. - Refactored related components to utilize the new onboarding status values from shared schema fields. - Enhanced tests to cover the new onboarding status logic and error handling.
- Updated the `deriveInitialSignInMethods` function to accept onboarding status, improving the logic for determining available sign-in methods based on project configuration and status. - Adjusted state management in the `ProjectOnboardingWizard` component to reflect the new function signature, ensuring correct initialization of sign-in methods. - Cleaned up import statements for better organization and readability. - Enhanced the onboarding flow by adding conditions for default authentication methods based on the project's early onboarding status.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts (1)
102-102: Snapshot updates look correct.The
onboarding_status: "completed"field is properly added to all project response snapshots, reflecting the new default value from the database schema.However, consider adding an E2E test that explicitly creates or updates a project with a non-default
onboarding_statusvalue (e.g.,"auth_setup") to verify the API accepts and returns valid status values correctly.🧪 Suggested test addition
it("updates project onboarding_status", async ({ expect }) => { await Project.createAndSwitch(); const response = await niceBackendFetch("/api/v1/internal/projects/current", { method: "PATCH", accessType: "admin", body: { onboarding_status: "auth_setup" } }); expect(response.status).toBe(200); expect(response.body.onboarding_status).toBe("auth_setup"); });Based on learnings
Applies to **/apps/e2e/**/*.{ts,tsx} : ALWAYS add new E2E tests when you change the API or SDK interface. Generally, err on the side of creating too many tests.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts` at line 102, Add an E2E test that verifies non-default onboarding_status values are accepted and returned by the API: create or switch to a project using Project.createAndSwitch(), PATCH /api/v1/internal/projects/current via niceBackendFetch with body { onboarding_status: "auth_setup" }, assert response.status is 200 and response.body.onboarding_status equals "auth_setup" (e.g., add a test case named "updates project onboarding_status" in apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts).apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx (1)
1645-1656: Verify fallback to"config_choice"is intentional.Line 1649 passes
selectedProjectStatus ?? "config_choice"to the wizard. WhileselectedProjectStatusshould be non-null at this point (due to the guard at Line 1468-1470), this fallback could silently mask a bug if the status is unexpectedly null.Consider using a more defensive approach:
🛡️ Suggested change
<ProjectOnboardingWizard project={selectedProject} - status={selectedProjectStatus ?? "config_choice"} + status={selectedProjectStatus ?? (() => { throw new Error(`Invariant: status should exist for project ${selectedProject.id}`); })()} mode={mode}Or extract to a guarded variable:
const wizardStatus = selectedProjectStatus ?? (() => { throw new Error(`Invariant: status should exist for project ${selectedProject.id}`); })();As per coding guidelines
Prefer ?? throwErr(...) over non-null assertions with good error messages explicitly stating the assumption.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx around lines 1645 - 1656, The code currently silently falls back to "config_choice" when passing status to ProjectOnboardingWizard; replace that silent fallback with an explicit invariant check so null/undefined status fails loudly: compute a guarded wizardStatus from selectedProjectStatus and if it is null/undefined throw an Error containing the project id (use selectedProject.id) and a clear message, then pass that wizardStatus into ProjectOnboardingWizard (referencing ProjectOnboardingWizard and selectedProjectStatus to locate the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx:
- Around line 1645-1656: The code currently silently falls back to
"config_choice" when passing status to ProjectOnboardingWizard; replace that
silent fallback with an explicit invariant check so null/undefined status fails
loudly: compute a guarded wizardStatus from selectedProjectStatus and if it is
null/undefined throw an Error containing the project id (use selectedProject.id)
and a clear message, then pass that wizardStatus into ProjectOnboardingWizard
(referencing ProjectOnboardingWizard and selectedProjectStatus to locate the
change).
In `@apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts`:
- Line 102: Add an E2E test that verifies non-default onboarding_status values
are accepted and returned by the API: create or switch to a project using
Project.createAndSwitch(), PATCH /api/v1/internal/projects/current via
niceBackendFetch with body { onboarding_status: "auth_setup" }, assert
response.status is 200 and response.body.onboarding_status equals "auth_setup"
(e.g., add a test case named "updates project onboarding_status" in
apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 50d54710-81e0-405c-b8c0-2f03efbf2605
📒 Files selected for processing (6)
apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.tsapps/backend/src/lib/projects.tsxapps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsxapps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsxapps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.tsclaude/CLAUDE-KNOWLEDGE.md
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/backend/src/lib/projects.tsx
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/e2e/tests/backend/endpoints/api/v1/projects.test.ts (1)
65-113:⚠️ Potential issue | 🟠 MajorAdd behavioral E2E coverage for
onboarding_statuswrites and validation.The new assertions only check default read shape (
"completed"). They don’t verify that PATCH/CREATE can persist non-default onboarding stages or reject invalid values end-to-end.Proposed test additions
+it("updates onboarding_status and persists it", async ({ expect }) => { + await Auth.fastSignUp(); + const { adminAccessToken } = await Project.createAndGetAdminToken(); + + const { updateProjectResponse } = await Project.updateCurrent(adminAccessToken, { + onboarding_status: "auth_setup", + }); + expect(updateProjectResponse.status).toBe(200); + expect(updateProjectResponse.body.onboarding_status).toBe("auth_setup"); + + const getResponse = await niceBackendFetch(`/api/v1/internal/projects/current`, { + accessType: "admin", + method: "GET", + headers: { "x-stack-admin-access-token": adminAccessToken }, + }); + expect(getResponse.status).toBe(200); + expect(getResponse.body.onboarding_status).toBe("auth_setup"); +}); + +it("rejects invalid onboarding_status values", async ({ expect }) => { + await Auth.fastSignUp(); + const { adminAccessToken } = await Project.createAndGetAdminToken(); + + const response = await niceBackendFetch(`/api/v1/internal/projects/current`, { + accessType: "admin", + method: "PATCH", + headers: { "x-stack-admin-access-token": adminAccessToken }, + body: { onboarding_status: "not_a_valid_stage" }, + }); + + expect(response.status).toBe(400); + expect(response.headers.get("x-stack-known-error")).toBe("SCHEMA_ERROR"); +});As per coding guidelines,
**/apps/e2e/**/*.{ts,tsx}: ALWAYS add new E2E tests when you change the API or SDK interface. Generally, err on the side of creating too many tests.Also applies to: 115-166
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/e2e/tests/backend/endpoints/api/v1/projects.test.ts` around lines 65 - 113, The test only asserts the default read value for onboarding_status and lacks end-to-end coverage for writes and validation; update the E2E test(s) around Project.createAndGetAdminToken and Project.updateCurrent to (1) create or patch a project setting onboarding_status to a non-default valid stage and assert the response and subsequent GET reflect that value, and (2) attempt to PATCH/CREATE with invalid onboarding_status values and assert the API returns a 4xx validation error; use the existing helper methods (Auth.fastSignUp, Project.createAndGetAdminToken, Project.updateCurrent and any Project.getCurrent helper) and assert both persistence and rejection behavior for onboarding_status.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@apps/e2e/tests/backend/endpoints/api/v1/projects.test.ts`:
- Around line 65-113: The test only asserts the default read value for
onboarding_status and lacks end-to-end coverage for writes and validation;
update the E2E test(s) around Project.createAndGetAdminToken and
Project.updateCurrent to (1) create or patch a project setting onboarding_status
to a non-default valid stage and assert the response and subsequent GET reflect
that value, and (2) attempt to PATCH/CREATE with invalid onboarding_status
values and assert the API returns a 4xx validation error; use the existing
helper methods (Auth.fastSignUp, Project.createAndGetAdminToken,
Project.updateCurrent and any Project.getCurrent helper) and assert both
persistence and rejection behavior for onboarding_status.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 44b99555-df90-46cf-b3f5-662f1fbcd196
📒 Files selected for processing (8)
apps/e2e/tests/backend/endpoints/api/v1/integrations/custom/projects/provision.test.tsapps/e2e/tests/backend/endpoints/api/v1/integrations/neon/oauth-providers.test.tsapps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/current.test.tsapps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/provision.test.tsapps/e2e/tests/backend/endpoints/api/v1/project-permissions.test.tsapps/e2e/tests/backend/endpoints/api/v1/projects.test.tsapps/e2e/tests/backend/endpoints/api/v1/team-permissions.test.tsclaude/CLAUDE-KNOWLEDGE.md
🚧 Files skipped from review as they are similar to previous changes (1)
- claude/CLAUDE-KNOWLEDGE.md
…ProjectWithLegacyConfig - Added a check to determine if the onboardingStatus column exists in the Project table before writing onboardingStatus during project creation and updates. - Refactored the data structure for project creation and update to improve clarity and maintainability. - Updated documentation to reflect the new handling of onboardingStatus for forward-compatibility with older schemas.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/backend/src/lib/projects.tsx (2)
131-133: Avoid silently dropping caller-providedonboarding_statuswhen schema is old.Right now, if a caller sends
onboarding_statusand the column is missing, the value is ignored without signal. For rollout safety, either throw a clear error or emit a structured warning/metric so this path is visible.Based on learnings: not used. As per coding guidelines: "Fail early, fail loud. Fail fast with an error instead of silently continuing."
Also applies to: 166-168
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/lib/projects.tsx` around lines 131 - 133, The code silently drops caller-provided onboarding_status when onboardingStatusColumnExists is false; update the branches that set createData.onboardingStatus (the block using onboardingStatusColumnExists and options.data.onboarding_status, and the similar block at the other occurrence) to instead fail loudly: if options.data.onboarding_status is provided but onboardingStatusColumnExists is false, throw a clear Error (or call a central validation error helper) naming the missing column and the caller field (e.g., "onboarding_status provided but column missing"), so callers see an explicit failure rather than silently losing their value.
105-114: Consider memoizing the column-existence probe to avoid repeating metadata queries.This check runs on every create/update transaction. Since schema shape changes rarely, a small cache/helper would reduce overhead on hot paths.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/backend/src/lib/projects.tsx` around lines 105 - 114, The metadata probe for onboardingStatus currently performs tx.$queryRaw each time (see onboardingStatusColumnExistsRows and onboardingStatusColumnExists) which is expensive; introduce a module-level memoized helper (e.g., getOnboardingStatusColumnExists) that accepts the Prisma client/transaction (prisma or tx), returns a cached boolean or runs the query once and caches the Promise<boolean> for subsequent calls, and replace the direct tx.$queryRaw usage with calls to that helper; ensure the helper caches per-process and uses the same query logic as the existing SELECT ... EXISTS, and use the cached boolean where onboardingStatusColumnExists is currently used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/backend/src/lib/projects.tsx`:
- Around line 131-133: The code silently drops caller-provided onboarding_status
when onboardingStatusColumnExists is false; update the branches that set
createData.onboardingStatus (the block using onboardingStatusColumnExists and
options.data.onboarding_status, and the similar block at the other occurrence)
to instead fail loudly: if options.data.onboarding_status is provided but
onboardingStatusColumnExists is false, throw a clear Error (or call a central
validation error helper) naming the missing column and the caller field (e.g.,
"onboarding_status provided but column missing"), so callers see an explicit
failure rather than silently losing their value.
- Around line 105-114: The metadata probe for onboardingStatus currently
performs tx.$queryRaw each time (see onboardingStatusColumnExistsRows and
onboardingStatusColumnExists) which is expensive; introduce a module-level
memoized helper (e.g., getOnboardingStatusColumnExists) that accepts the Prisma
client/transaction (prisma or tx), returns a cached boolean or runs the query
once and caches the Promise<boolean> for subsequent calls, and replace the
direct tx.$queryRaw usage with calls to that helper; ensure the helper caches
per-process and uses the same query logic as the existing SELECT ... EXISTS, and
use the cached boolean where onboardingStatusColumnExists is currently used.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 87d7bc6f-8857-436e-a348-09d229cbeac1
📒 Files selected for processing (2)
apps/backend/src/lib/projects.tsxclaude/CLAUDE-KNOWLEDGE.md
… migrations
onboardingStatusfield to the Project model with a default value of "completed".Summary by CodeRabbit
New Features
API Changes
Validation
Tests & Docs