Skip to content

Add onboarding status to Project model and implement related database…#1246

Merged
N2D4 merged 12 commits intodevfrom
codex/add-multistep-onboarding-flow
Mar 13, 2026
Merged

Add onboarding status to Project model and implement related database…#1246
N2D4 merged 12 commits intodevfrom
codex/add-multistep-onboarding-flow

Conversation

@Developing-Gamer
Copy link
Contributor

@Developing-Gamer Developing-Gamer commented Mar 13, 2026

… 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.

Summary by CodeRabbit

  • New Features

    • Multi-step Project Onboarding wizard and onboarding-driven navigation; projects show a "Setup incomplete" badge and route to onboarding when unfinished.
  • API Changes

    • Project responses now include onboarding_status (read/write surface) so UI and SDKs can reflect setup state.
  • Validation

    • Database-level constraint for onboarding status added and validated; API/schema values strictly enforced.
  • Tests & Docs

    • Added migration/constraint tests, updated e2e snapshots, and onboarding guidance.

… 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.
@Developing-Gamer Developing-Gamer self-assigned this Mar 13, 2026
Copilot AI review requested due to automatic review settings March 13, 2026 03:03
@vercel
Copy link

vercel bot commented Mar 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Mar 13, 2026 6:59pm
stack-backend Ready Ready Preview, Comment Mar 13, 2026 6:59pm
stack-dashboard Ready Ready Preview, Comment Mar 13, 2026 6:59pm
stack-demo Ready Ready Preview, Comment Mar 13, 2026 6:59pm
stack-docs Ready Ready Preview, Comment Mar 13, 2026 6:59pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
DB migrations & tests
apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/migration.sql, apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.ts, apps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/migration.sql, apps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/tests/constraint-is-validated.ts
Adds onboardingStatus TEXT NOT NULL DEFAULT 'completed' and a CHECK constraint limiting allowed values; includes tests and a follow-up migration that VALIDATEs the constraint.
Prisma schema
apps/backend/prisma/schema.prisma
Adds onboardingStatus String @default("completed") to Project model.
Backend project logic & tests
apps/backend/src/lib/projects.tsx, apps/e2e/tests/backend/.../*.test.ts, apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts
Maps onboarding_status in query post-processing, conditionally writes onboardingStatus in create/update when column exists, and updates many backend tests/snapshots to include onboarding_status: "completed".
Dashboard onboarding UI (wizard)
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
Implements a multi-step Project Onboarding Wizard, step components, state management, server sync, and guarded navigation based on per-project onboarding status.
Dashboard projects list & card
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx, apps/dashboard/src/components/project-card.tsx
Fetches per-project onboarding_status, routes incomplete projects to the wizard, shows "Setup incomplete" badge, and updates ProjectCard props/styling and link behavior.
Shared types, schemas & SDK mappings
packages/stack-shared/src/schema-fields.ts, packages/stack-shared/src/interface/crud/projects.ts, packages/template/src/lib/stack-app/projects/index.ts, packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
Adds projectOnboardingStatusValues/type/Yup schema, surfaces onboarding_status in CRUD schemas, maps field into AdminProject and update payloads, and exposes SDK typing for onboardingStatus.
E2E / integration test snapshots
apps/e2e/tests/backend/endpoints/.../*.test.ts
Updated many snapshot expectations to include onboarding_status: "completed" in project payloads.
Docs / knowledge
claude/CLAUDE-KNOWLEDGE.md
Documents onboarding retrieval/update patterns, redirect handling, loading UX, snapshot guidance, and forward-compatible writes when column may be absent.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Email templates and project logos #852: Modifies the same backend project read/write plumbing (getProjectQuery / createOrUpdateProjectWithLegacyConfig) where onboardingStatus is surfaced/persisted.
  • project owner team #835: Edits Project model plumbing and backend helpers overlapping mapping and SDK conversion logic.
  • Project logo upload #817: Updates backend project helpers and mappings for additional Project fields, touching similar areas for field exposure/persistence.

Suggested reviewers

  • N2D4

Poem

🐰 I hop through steps with nimble, friendly cheer,

I track each stage until the path is clear.
From config choice to payments, one by one,
I nudge each setup till the onboarding's done.
Tiny paws, big hopes — now every project runs!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding onboarding status to the Project model and implementing database migrations.
Description check ✅ Passed The description covers the main changes and objectives, but lacks detail about specific implementation aspects and testing strategy.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/add-multistep-onboarding-flow
📝 Coding Plan
  • Generate coding plan for human review comments

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_status as 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.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR adds a multi-step project onboarding wizard by introducing an onboardingStatus column to the Project table and wiring it through the full stack — Prisma schema, migration, CRUD schema, SDK types, backend query, and a completely rewritten new-project dashboard page.

The migration strategy (add column with NOT VALID, then a second migration to VALIDATE CONSTRAINT) is correct for zero-downtime deployment on a populated table. The shared constant projectOnboardingStatusValues is properly centralised in schema-fields.ts and the API schema validates it via yup.

Key issues found:

  • Unvalidated Stripe redirect: window.location.href = setup.url uses a URL from the API response without any validation; the URL should be checked to be an https:// URL before the redirect executes.
  • Duplicated PROJECT_ONBOARDING_STATUSES constant: The constant and its derived type are re-declared identically in both new-project/page-client.tsx and projects/page-client.tsx instead of importing projectOnboardingStatusValues from schema-fields.ts.
  • Object.fromEntries with dynamic keys: The apps-selection step builds a config-update object with dynamic keys via Object.fromEntries; per the project style guide Map<A, B> should be used for dynamic-key collections.
  • Silent error swallowing: projects/page-client.tsx uses runAsynchronously (not runAsynchronouslyWithAlert) for the project-status fetch, so failures are silently discarded and in-progress projects are incorrectly shown as fully set up.
  • Extra DB round-trip on onboarding_status writes: onboardingStatus is defined in the Prisma schema but the backend passes it via a separate $executeRaw UPDATE inside the transaction rather than including it directly in the Prisma create/update call.

Confidence Score: 3/5

  • The migration and schema changes are safe, but the unvalidated window.location.href redirect and the silent failure path for onboarding-status loading should be addressed before merging.
  • The database migration is correctly structured (NOT VALID + separate VALIDATE step). The backend logic is functionally correct. The main concern is the unvalidated redirect URL in the Stripe payments flow (window.location.href = setup.url) which violates the project's redirect-URL security policy. Additionally, the runAsynchronously silent failure in projects/page-client.tsx means in-progress projects could silently appear as completed, which is a UX regression. Together these lower confidence from a passing score.
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx (unvalidated redirect, duplicate constant, dynamic-key object) and apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx (silent error swallowing) need the most attention.

Important Files Changed

Filename Overview
apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/migration.sql Adds onboardingStatus TEXT NOT NULL DEFAULT 'completed' with a NOT VALID CHECK constraint — correct zero-downtime pattern for a populated table.
apps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/migration.sql Runs VALIDATE CONSTRAINT as a separate migration step to backfill constraint validation safely; proper two-phase migration approach.
apps/backend/src/lib/projects.tsx Exposes onboarding_status in getProjectQuery and handles updates via $executeRaw inside both create and update branches — works correctly but the field should be passed directly through Prisma to avoid an extra DB round-trip.
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx Large rewrite adding a multi-step onboarding wizard; three issues: unvalidated window.location.href = setup.url redirect, duplicate PROJECT_ONBOARDING_STATUSES constant (should import from schema-fields), and Object.fromEntries with dynamic keys.
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx Fetches onboarding statuses and routes incomplete projects back to the wizard; uses runAsynchronously (silent failures) instead of runAsynchronouslyWithAlert for the status fetch, and duplicates the PROJECT_ONBOARDING_STATUSES constant.
packages/stack-shared/src/schema-fields.ts Adds projectOnboardingStatusValues, ProjectOnboardingStatus, and projectOnboardingStatusSchema — canonical definitions that should be imported by dashboard files rather than re-declared.
packages/stack-shared/src/interface/crud/projects.ts Correctly adds onboarding_status to both the admin read schema (.defined()) and the admin update schema (.optional()), with proper yup validation via the shared schema field.

Sequence Diagram

sequenceDiagram
    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
Loading

Last reviewed commit: bc87ba6

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Add 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 accepts onboarding_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_STATUSES duplicates projectOnboardingStatusValues defined in packages/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

📥 Commits

Reviewing files that changed from the base of the PR and between 612cb71 and bc87ba6.

📒 Files selected for processing (15)
  • apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/migration.sql
  • apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.ts
  • apps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/migration.sql
  • apps/backend/prisma/migrations/20260312000001_validate_project_onboarding_status_constraint/tests/constraint-is-validated.ts
  • apps/backend/prisma/schema.prisma
  • apps/backend/src/lib/projects.tsx
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
  • apps/dashboard/src/components/project-card.tsx
  • apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts
  • claude/CLAUDE-KNOWLEDGE.md
  • packages/stack-shared/src/interface/crud/projects.ts
  • packages/stack-shared/src/schema-fields.ts
  • packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts
  • packages/template/src/lib/stack-app/projects/index.ts

…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".
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (4)
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx (4)

188-195: ⚠️ Potential issue | 🟠 Major

deriveInitialSignInMethods currently ignores disabled config flags.

Line 190 pre-seeds methods (credential, magicLink, google, 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 | 🟡 Minor

Avoid silently coercing unknown onboarding_status to "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 srcDoc is missing sandbox, enabling script execution risk.

Line 463 renders untrusted/templated HTML via srcDoc without 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

📥 Commits

Reviewing files that changed from the base of the PR and between bc87ba6 and b804a27.

📒 Files selected for processing (1)
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx

@Developing-Gamer Developing-Gamer requested a review from N2D4 March 13, 2026 05:28
Developing-Gamer and others added 3 commits March 13, 2026 09:38
- 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.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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_status value (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. While selectedProjectStatus should 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

📥 Commits

Reviewing files that changed from the base of the PR and between b804a27 and ab53a32.

📒 Files selected for processing (6)
  • apps/backend/prisma/migrations/20260312000000_add_project_onboarding_status/tests/default-and-constraint.ts
  • apps/backend/src/lib/projects.tsx
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client.tsx
  • apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx
  • apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts
  • claude/CLAUDE-KNOWLEDGE.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/src/lib/projects.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

Add behavioral E2E coverage for onboarding_status writes 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

📥 Commits

Reviewing files that changed from the base of the PR and between ab53a32 and 2f62bcf.

📒 Files selected for processing (8)
  • apps/e2e/tests/backend/endpoints/api/v1/integrations/custom/projects/provision.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/oauth-providers.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/current.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/provision.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/project-permissions.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/projects.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/team-permissions.test.ts
  • claude/CLAUDE-KNOWLEDGE.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • claude/CLAUDE-KNOWLEDGE.md

@Developing-Gamer Developing-Gamer enabled auto-merge (squash) March 13, 2026 17:32
…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.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/backend/src/lib/projects.tsx (2)

131-133: Avoid silently dropping caller-provided onboarding_status when schema is old.

Right now, if a caller sends onboarding_status and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2f62bcf and 4d7036d.

📒 Files selected for processing (2)
  • apps/backend/src/lib/projects.tsx
  • claude/CLAUDE-KNOWLEDGE.md

@N2D4 N2D4 disabled auto-merge March 13, 2026 19:00
@N2D4 N2D4 merged commit b5d5e5c into dev Mar 13, 2026
28 checks passed
@N2D4 N2D4 deleted the codex/add-multistep-onboarding-flow branch March 13, 2026 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants