Skip to content

chore(webapp): upgrade @conform-to to v1#4044

Open
carderne wants to merge 8 commits into
mainfrom
chore/conform-v1
Open

chore(webapp): upgrade @conform-to to v1#4044
carderne wants to merge 8 commits into
mainfrom
chore/conform-v1

Conversation

@carderne

@carderne carderne commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Upgrades the dashboard form layer from @conform-to 0.9 to 1.x. No behaviour change is intended; this is the conform API migration only.

conform 1.x peer-depends on zod ^3.21 || ^4, so it runs on the current zod 3 and is a prerequisite for upgrading the repo to zod 4: conform 0.9 imports ZodNativeEnum, ZodEffects, and ZodPipeline, all removed in zod 4, so the webapp cannot build against zod 4 until conform is on 1.x. Landing this first (on zod 3) keeps the zod 4 PR focused on zod alone.

Testing

Did a bunch of local smoke tests and E2E playwright tests, several rounds of different reviewers, all clean.

@changeset-bot

changeset-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: c28f8ff

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

The pull request upgrades @conform-to/react and @conform-to/zod from 0.9.2 to ^1.2.2. Across the webapp, form validation switches to parseWithZod, rendered forms switch to getFormProps and field prop helpers, dynamic lists switch to fields.*.getFieldList() with form.insert and form.remove, and many server handlers now return submission.reply() or submission.reply({ fieldErrors, formErrors }). Field error rendering changes from .error to .errors, and the server changes note records the upgrade.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: upgrading @conform-to in the webapp.
Description check ✅ Passed The description covers summary and testing, but omits the issue reference, checklist, changelog, and screenshots sections from the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/conform-v1

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.

coderabbitai[bot]

This comment was marked as resolved.

@carderne carderne marked this pull request as ready for review June 25, 2026 16:25
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

Open in Devin Review

@carderne carderne mentioned this pull request Jun 25, 2026
2 tasks
@pkg-pr-new

pkg-pr-new Bot commented Jun 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@trigger.dev/build

npm i https://pkg.pr.new/@trigger.dev/build@e762338

trigger.dev

npm i https://pkg.pr.new/trigger.dev@e762338

@trigger.dev/core

npm i https://pkg.pr.new/@trigger.dev/core@e762338

@trigger.dev/python

npm i https://pkg.pr.new/@trigger.dev/python@e762338

@trigger.dev/react-hooks

npm i https://pkg.pr.new/@trigger.dev/react-hooks@e762338

@trigger.dev/redis-worker

npm i https://pkg.pr.new/@trigger.dev/redis-worker@e762338

@trigger.dev/rsc

npm i https://pkg.pr.new/@trigger.dev/rsc@e762338

@trigger.dev/schema-to-json

npm i https://pkg.pr.new/@trigger.dev/schema-to-json@e762338

@trigger.dev/sdk

npm i https://pkg.pr.new/@trigger.dev/sdk@e762338

commit: e762338

devin-ai-integration[bot]

This comment was marked as resolved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx (2)

142-157: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return blocked purchase errors in the same Conform shape the modal consumes.

PurchaseBranchesModal only forwards fetcher payloads with a status field into lastResult (Lines 625-627) and only renders amount.errors / form.errors (Lines 714-717). The plain { ok: false, error } responses here are therefore invisible in the UI, so “plan unavailable” and “managed billing” failures become silent no-ops for the user.

Possible fix
-    if (purchaseBlockReason === "plan_unavailable") {
-      return json(
-        { ok: false, error: "Unable to verify billing status. Please try again." } as const,
-        { status: 503 }
-      );
-    }
-    if (purchaseBlockReason === "managed_billing") {
-      return json({ ok: false, error: "Contact us to request more branches." } as const, {
-        status: 403,
-      });
-    }
-
     const submission = parseWithZod(formData, { schema: PurchaseSchema });
 
     if (submission.status !== "success") {
       return json(submission.reply());
     }
+
+    if (purchaseBlockReason === "plan_unavailable") {
+      return json(
+        submission.reply({
+          formErrors: ["Unable to verify billing status. Please try again."],
+        }),
+        { status: 503 }
+      );
+    }
+    if (purchaseBlockReason === "managed_billing") {
+      return json(
+        submission.reply({
+          formErrors: ["Contact us to request more branches."],
+        }),
+        { status: 403 }
+      );
+    }

706-711: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Suppress Conform’s injected default value here. InputNumberStepper is controlled via value={amountValue}, so getInputProps(amount, { type: "number", value: false }) should be used to avoid Conform adding a defaultValue alongside the controlled props.

Source: Learnings

🧹 Nitpick comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx (1)

4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a subpath export for tryCatch here.

This route is under apps/webapp, so importing tryCatch from the @trigger.dev/core root regresses the repo’s package-boundary rule. Please switch this to the appropriate subpath export used elsewhere in webapp. As per coding guidelines, apps/webapp/**/*.{ts,tsx} should “Use subpath exports from @trigger.dev/core package instead of importing from the root @trigger.dev/core path”.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 39b5f328-d5fb-47ba-a21d-decdcfecff07

📥 Commits

Reviewing files that changed from the base of the PR and between e5d7cff and f37daf8.

📒 Files selected for processing (25)
  • .server-changes/conform-v1.md
  • apps/webapp/app/components/Feedback.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
  • apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx
  • apps/webapp/app/components/runs/v3/ReplayRunDialog.tsx
  • apps/webapp/app/components/schedules/PurchaseSchedulesModal.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
✅ Files skipped from review due to trivial changes (1)
  • .server-changes/conform-v1.md
🚧 Files skipped from review as they are similar to previous changes (19)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/components/Feedback.tsx
  • apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
  • apps/webapp/app/components/schedules/PurchaseSchedulesModal.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx
  • apps/webapp/app/components/runs/v3/ReplayRunDialog.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (38)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (4, 12)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 10)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 10)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 10)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 10)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 10)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 10)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (12, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (2, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (11, 12)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 10)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 10)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (10, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (3, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (8, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (9, 12)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 10)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (7, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (1, 12)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 10)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (5, 12)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (6, 12)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Build and publish previews
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: Access environment variables through the env export of env.server.ts instead of directly accessing process.env
Use subpath exports from @trigger.dev/core package instead of importing from the root @trigger.dev/core path

Use named constants for sentinel/placeholder values (e.g. const UNSET_VALUE = '__unset__') instead of raw string literals scattered across comparisons

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
apps/webapp/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Only use useCallback/useMemo for context provider values, expensive derived data that is a dependency elsewhere, or stable refs required by a dependency array. Don't wrap ordinary event handlers or trivial computations

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}: Use pnpm run typecheck for changes in apps (apps/*) and internal packages (internal-packages/*), and never use build to verify those changes.
Use Vitest for tests, and never mock anything; use testcontainers instead.
Prefer static imports over dynamic import(), and only use dynamic imports for unresolved circular dependencies, genuine code-splitting needs, or conditional runtime loading.

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,md,mdx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from @trigger.dev/sdk when writing Trigger.dev tasks; never use @trigger.dev/sdk/v3 or deprecated client.defineJob.

Files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
🧠 Learnings (17)
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-03-22T19:24:14.403Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts:200-204
Timestamp: 2026-03-22T19:24:14.403Z
Learning: In the triggerdotdev/trigger.dev codebase, webhook URLs are not expected to contain embedded credentials/secrets (e.g., fields like `ProjectAlertWebhookProperties` should only hold credential-free webhook endpoints). During code review, if you see logging or inclusion of raw webhook URLs in error messages, do not automatically treat it as a credential-leak/secrets-in-logs issue by default—first verify the URL does not contain embedded credentials (for example, no username/password in the URL, no obvious secret/token query params or fragments). If the URL is credential-free per this project’s conventions, allow the logging.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-05-18T08:21:27.694Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3632
File: apps/webapp/sentry.server.ts:4-21
Timestamp: 2026-05-18T08:21:27.694Z
Learning: When handling Prisma error P1001 ("Can't reach database server") in TypeScript, don’t assume a single error shape. Prisma can surface P1001 via two different error classes/fields: `PrismaClientKnownRequestError` exposes it as `err.code === "P1001"` (common during mid-query connection drops), while `PrismaClientInitializationError` exposes it as `err.errorCode === "P1001"` (common on client startup failure). Therefore, predicates should use `err.code === "P1001" || err.errorCode === "P1001"`. Do not flag `err.code === "P1001"` as “unreachable/never matches,” as it is expected in production.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-05-18T08:21:27.694Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3632
File: apps/webapp/sentry.server.ts:4-21
Timestamp: 2026-05-18T08:21:27.694Z
Learning: When handling Prisma errors for P1001 ("Can't reach database server"), do not assume it only appears under a single property name. Prisma may surface P1001 via either `PrismaClientKnownRequestError` (`err.code === "P1001"`, e.g., mid-query connection drops) or `PrismaClientInitializationError` (`err.errorCode === "P1001"`, e.g., client startup connection failure). To reliably detect the condition, check `err.code === "P1001" || err.errorCode === "P1001"`, and avoid review rules that would incorrectly flag `err.code === "P1001"` as unreachable/never-matching.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-06-13T19:53:13.759Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3937
File: packages/trigger-sdk/skills/realtime-and-frontend/SKILL.md:258-260
Timestamp: 2026-06-13T19:53:13.759Z
Learning: When reviewing code that uses `trigger.dev/react-hooks`’s `useRealtimeRun`, preserve the call signature where the first argument is the full realtime handle object (not `handle.id`). This is intentional to maintain type-safety and is consistent with the official docs; do not suggest changing the first argument from the handle object to `handle.id`.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-06-17T17:13:49.929Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3948
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.bulk-actions.$bulkActionParam/route.tsx:48-62
Timestamp: 2026-06-17T17:13:49.929Z
Learning: In triggerdotdev/trigger.dev, within `dashboardLoader`/`dashboardAction` (or similar context resolver code) whenever you resolve an organization ID from an organization slug for RBAC/enterprise authorization scope, always read from the primary Prisma client (`prisma`), not `$replica`. Using `$replica` can hit replica-lag and cause the RBAC lookup/authorization to run without the correct org scope (bypassing intended role enforcement). Implement the slug→org lookup with `prisma.organization.findFirst(...)` (or equivalent primary-client query) and add an inline comment documenting why the primary client is required (replica lag could lead to unscoped RBAC checks).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-06-23T13:04:21.413Z
Learnt from: carderne
Repo: triggerdotdev/trigger.dev PR: 4023
File: apps/webapp/app/services/upsertBranch.server.ts:14-18
Timestamp: 2026-06-23T13:04:21.413Z
Learning: In TypeScript, it’s valid to `import { type X }` and then use `typeof X` in a type-only position, e.g. `type Alias = z.infer<typeof X>`. The `type` modifier suppresses the runtime import, but the type checker still has the full exported type so `z.infer<typeof X>` can resolve correctly. In code reviews, don’t flag this as a TypeScript compile error as long as `typeof X` is used in a type context (e.g., with `z.infer`, `type` aliases, generics), not as a runtime value.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-04-02T19:18:26.255Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3319
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.bulk-actions/route.tsx:179-189
Timestamp: 2026-04-02T19:18:26.255Z
Learning: In this repo’s route components that render the Inspector `ResizablePanelGroup` panels, it’s acceptable to pass `collapsed={!isShowingInspector}` together with a no-op `onCollapseChange={() => {}}` when panel visibility is intentionally controlled only by route parameters (e.g., `*Param` search/route params) rather than user drag/collapse interactions. Do not flag an empty/no-op `onCollapseChange` as “missing wiring” in these cases; only flag it when collapse state is expected to change based on user interaction.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2026-05-12T21:04:00.184Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.sessions._index/route.tsx:40-42
Timestamp: 2026-05-12T21:04:00.184Z
Learning: In triggerdotdev/trigger.dev route loader implementations (Remix `route.tsx` files under `apps/webapp/app/routes/**`), follow the existing convention for missing/unauthorized environment lookups: when `findEnvironmentBySlug` (or the equivalent env resolver) returns a falsy value, handle it by throwing `new Error("Environment not found")` rather than returning a `404` `Response` (i.e., do not flag this as “missing 404 response”). Changing the error-to-404 convention is a cross-cutting refactor and should be left out of individual PRs unless the PR explicitly addresses that broader migration.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
📚 Learning: 2026-05-08T21:00:20.973Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3538
File: apps/webapp/app/components/primitives/Resizable.tsx:60-78
Timestamp: 2026-05-08T21:00:20.973Z
Learning: In the triggerdotdev/trigger.dev codebase, treat Zod as a boundary validation tool (API handlers, request/response validation, and storage/DB read/write validation), not as inline render-time validation inside React components/primitive UI code. For render-time guards, prefer small manual type-narrowing checks (e.g., a short predicate like ~10–20 lines) over importing Zod into UI primitives, to avoid per-render schema-parse overhead and unnecessary abstraction. Use the manual guard approach unless you truly need schema validation at a boundary; only then introduce Zod.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-06-25T18:21:55.847Z
Learnt from: carderne
Repo: triggerdotdev/trigger.dev PR: 4039
File: apps/webapp/app/routes/invite-resend.tsx:0-0
Timestamp: 2026-06-25T18:21:55.847Z
Learning: In the triggerdotdev/trigger.dev Zod 4 migration, avoid importing from the root package `conform-to/zod` in webapp code. It can resolve to the Zod 3 build and may crash at module load under Zod 4. When reviewing TypeScript/TSX files in `apps/webapp`, prefer importing from the Zod 4 subpath `conform-to/zod/v4` for Zod 4-compatible schemas/types.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-05-12T21:04:05.815Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/components/sessions/v1/SessionStatus.tsx:1-3
Timestamp: 2026-05-12T21:04:05.815Z
Learning: In this Remix + TypeScript codebase, do not flag a server/client boundary violation when a file imports only types from a module matching `*.server`.

Specifically, it’s safe to import types using `import type { Foo } from "*.server"` or `import { type Foo } from "*.server"` because TypeScript erases type-only imports at compile time and they emit no JavaScript, so they won’t cross the Remix server/client bundle boundary.

Only raise the boundary concern for value imports (e.g., `import { Foo }` without `type`, or `import Foo`), since those produce JavaScript output.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-06-25T18:21:51.905Z
Learnt from: carderne
Repo: triggerdotdev/trigger.dev PR: 4039
File: apps/webapp/app/routes/invite-revoke.tsx:0-0
Timestamp: 2026-06-25T18:21:51.905Z
Learning: During the Zod v4 migration in the triggerdotdev/trigger.dev webapp, ensure any imports from `conform-to/zod` use the Zod-4 subpath: `conform-to/zod/v4` (e.g., `import { parseWithZod } from "conform-to/zod/v4"`). Do not import from the package root `conform-to/zod`, because it is the Zod 3 implementation and may load Zod-3-only symbols (e.g., `ZodBranded`, `ZodEffects`), which can throw at module load (notably with `zod4.4.3`). This should be enforced across `apps/webapp/**/*` where helpers like `parseWithZod` and `conformZodMessage` are used.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-06-25T18:21:54.729Z
Learnt from: carderne
Repo: triggerdotdev/trigger.dev PR: 4039
File: apps/webapp/app/routes/confirm-basic-details.tsx:0-0
Timestamp: 2026-06-25T18:21:54.729Z
Learning: For Remix + TypeScript files that use Conform v1 (conform-to/react) and its getInputProps helper, when you intend to suppress the helper-provided default value for non-checkbox/non-radio inputs (e.g., hidden inputs managed via an explicit value prop), use the Conform v1 option key `value: false`. Do not recommend `defaultValue: false` here, because `defaultValue` is not a valid option key for these input types in Conform v1 typings.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
📚 Learning: 2026-04-16T14:21:15.229Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3368
File: apps/webapp/app/components/logs/LogsTaskFilter.tsx:135-163
Timestamp: 2026-04-16T14:21:15.229Z
Learning: When rendering lists of task registry items in apps/webapp (e.g., <SelectItem /> rows) and using `key={item.slug}`, do not flag it as potentially non-unique. In trigger.dev’s `TaskIdentifier` table, the DB constraint `@unique([runtimeEnvironmentId, slug])` guarantees `slug` is unique within a given runtime environment, so `item.slug` is safe as the React key as long as the list is derived from that registry/constraint (and not from a legacy query that could produce duplicate slugs).

Applied to files:

  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
🔇 Additional comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx (1)

90-92: 🔒 Security & Privacy

Verify the auth-scope org lookup stays on primary Prisma.

These dashboardLoader / dashboardAction context resolvers feed RBAC scope from resolveOrgIdFromSlug(...), but the provided file does not show whether that helper reads via primary prisma or a replica. Please confirm it uses the primary client and consider an inline comment here documenting the replica-lag risk, since a stale slug→org lookup can leave authorization unscoped. Based on learnings, org-slug resolution inside dashboardLoader/dashboardAction must use primary prisma for RBAC scope because replica lag can bypass intended role enforcement.

Also applies to: 205-207

Source: Learnings

apps/webapp/app/components/billing/BillingLimitConfigSection.tsx (1)

123-127: 🎯 Functional Correctness

No issue here. CheckboxWithLabel syncs its internal checked state from defaultChecked, so this checkbox follows cancelInProgressRuns updates.

			> Likely an incorrect or invalid review comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx (2)

142-157: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return blocked purchase errors in the same Conform shape the modal consumes.

PurchaseBranchesModal only forwards fetcher payloads with a status field into lastResult (Lines 625-627) and only renders amount.errors / form.errors (Lines 714-717). The plain { ok: false, error } responses here are therefore invisible in the UI, so “plan unavailable” and “managed billing” failures become silent no-ops for the user.

Possible fix
-    if (purchaseBlockReason === "plan_unavailable") {
-      return json(
-        { ok: false, error: "Unable to verify billing status. Please try again." } as const,
-        { status: 503 }
-      );
-    }
-    if (purchaseBlockReason === "managed_billing") {
-      return json({ ok: false, error: "Contact us to request more branches." } as const, {
-        status: 403,
-      });
-    }
-
     const submission = parseWithZod(formData, { schema: PurchaseSchema });
 
     if (submission.status !== "success") {
       return json(submission.reply());
     }
+
+    if (purchaseBlockReason === "plan_unavailable") {
+      return json(
+        submission.reply({
+          formErrors: ["Unable to verify billing status. Please try again."],
+        }),
+        { status: 503 }
+      );
+    }
+    if (purchaseBlockReason === "managed_billing") {
+      return json(
+        submission.reply({
+          formErrors: ["Contact us to request more branches."],
+        }),
+        { status: 403 }
+      );
+    }

706-711: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Suppress Conform’s injected default value here. InputNumberStepper is controlled via value={amountValue}, so getInputProps(amount, { type: "number", value: false }) should be used to avoid Conform adding a defaultValue alongside the controlled props.

Source: Learnings

🧹 Nitpick comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx (1)

4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a subpath export for tryCatch here.

This route is under apps/webapp, so importing tryCatch from the @trigger.dev/core root regresses the repo’s package-boundary rule. Please switch this to the appropriate subpath export used elsewhere in webapp. As per coding guidelines, apps/webapp/**/*.{ts,tsx} should “Use subpath exports from @trigger.dev/core package instead of importing from the root @trigger.dev/core path”.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 39b5f328-d5fb-47ba-a21d-decdcfecff07

📥 Commits

Reviewing files that changed from the base of the PR and between e5d7cff and f37daf8.

📒 Files selected for processing (25)
  • .server-changes/conform-v1.md
  • apps/webapp/app/components/Feedback.tsx
  • apps/webapp/app/components/billing/BillingAlertsSection.tsx
  • apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
  • apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx
  • apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx
  • apps/webapp/app/components/runs/v3/ReplayRunDialog.tsx
  • apps/webapp/app/components/schedules/PurchaseSchedulesModal.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
✅ Files skipped from review due to trivial changes (1)
  • .server-changes/conform-v1.md
🚧 Files skipped from review as they are similar to previous changes (19)
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/components/Feedback.tsx
  • apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx
  • apps/webapp/app/components/schedules/PurchaseSchedulesModal.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx
  • apps/webapp/app/components/runs/v3/ReplayRunDialog.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx
📜 Review details
🔇 Additional comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx (1)

90-92: 🔒 Security & Privacy

Verify the auth-scope org lookup stays on primary Prisma.

These dashboardLoader / dashboardAction context resolvers feed RBAC scope from resolveOrgIdFromSlug(...), but the provided file does not show whether that helper reads via primary prisma or a replica. Please confirm it uses the primary client and consider an inline comment here documenting the replica-lag risk, since a stale slug→org lookup can leave authorization unscoped. Based on learnings, org-slug resolution inside dashboardLoader/dashboardAction must use primary prisma for RBAC scope because replica lag can bypass intended role enforcement.

Also applies to: 205-207

Source: Learnings

apps/webapp/app/components/billing/BillingLimitConfigSection.tsx (1)

123-127: 🎯 Functional Correctness

No issue here. CheckboxWithLabel syncs its internal checked state from defaultChecked, so this checkbox follows cancelInProgressRuns updates.

			> Likely an incorrect or invalid review comment.
🛑 Comments failed to post (2)
apps/webapp/app/components/billing/BillingAlertsSection.tsx (1)

171-190: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='apps/webapp/app/components/billing/BillingAlertsSection.tsx'

echo '--- outline ---'
ast-grep outline "$file" --view expanded || true

echo '--- relevant lines around form setup ---'
sed -n '150,230p' "$file"

echo '--- relevant lines around render of email fields ---'
sed -n '320,390p' "$file"

echo '--- search for form reset / keying / reinit ---'
rg -n 'reset|reinit|reinitialize|key=' "$file"

Repository: triggerdotdev/trigger.dev

Length of output: 6629


🌐 Web query:

Conform useForm defaultValue change rerender field list reset insert placeholder rows

💡 Result:

In React Hook Form, resetting a form containing a useFieldArray after changing defaultValues involves careful management of form state to ensure field lists update and placeholders or new rows render correctly. To correctly reset a field array with new default values, you should invoke the reset method provided by the useForm hook [1][2]. When you call reset with a new object, React Hook Form updates the internal form state, which triggers a re-render of the field array [2]. Key considerations for this process include: 1. Use Reset for Default Value Changes: The reset API is the recommended way to update the form's default values [1][3]. When using useFieldArray, calling reset({ fieldArrayName: newValues }) ensures the library re-initializes the field array with the new data [2]. This process effectively replaces the current field list and ensures that any mapped components (such as rows with inputs or placeholders) reflect the new state [2]. 2. Handling Controlled Components: If you are using Controller components, it is essential to ensure that defaultValues are either provided at the form level in useForm or passed explicitly to the reset call [1][2]. Failing to provide consistent default values can lead to stale data remaining in the inputs after a reset, particularly with field arrays [4]. 3. Managing Rerenders and Field Lists: Invoking reset will cause a full re-render of the form state [2]. If you notice performance issues or unnecessary re-renders in larger forms, verify that your field array components are properly memoized and that the field array names (e.g., test.${index}.firstName) remain stable [5][6]. 4. Avoiding Manual Manipulation: Avoid trying to mutate the defaultValues object directly, as this can lead to unpredictable behavior where the form state and the UI get out of sync [4][7]. Instead, treat the form state as immutable and rely on the reset method to apply updates [3][2]. If you are inserting placeholder rows dynamically, you can use the append or prepend methods from useFieldArray to add items [5]. If you need to wipe existing data and start fresh with a specific set of rows, reset is the appropriate tool, as it clears the existing subscriptions and re-registers inputs based on the new array provided [2]. If specific inputs are failing to clear, verify that you are not encountering design limitations related to shouldUnregister, which can sometimes interfere with how fields are re-registered during a reset [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- search for useForm + reset/key patterns in webapp ---'
rg -n 'useForm<|getFormProps\(form\)|form\.reset\(|useForm\(' apps/webapp -g '*.{ts,tsx}' | sed -n '1,200p'

echo '--- search for conform package references ---'
rg -n '`@conform-to/react`|`@conform-to/zod`|parseWithZod' apps/webapp -g '*.{ts,tsx}' | sed -n '1,200p'

Repository: triggerdotdev/trigger.dev

Length of output: 36882


🌐 Web query:

Conform getFieldList useForm field array defaultValue reset insert remove documentation

💡 Result:

In React Hook Form, the useFieldArray hook is the standard tool for managing dynamic lists of form fields [1][2]. There is no official function called getFieldList; if you need to access field states, the getFieldState method from the useForm hook is the appropriate API [3]. Key Concepts and APIs: 1. useFieldArray: This hook accepts a control object (from useForm) and a name (the path to the array in your form values) [1][4]. It returns a fields array (containing your data plus a unique id for each item) and mutation methods [1][4]. 2. Array Mutation Methods: - append: Adds one or multiple items to the end of the array [4][2]. - insert: Inserts one or multiple items at a specific index [4][2]. - remove: Removes an item at a specific index, or all items if no index is provided [1][4]. - prepend, swap, move, update, and replace are also available for comprehensive array manipulation [4][2]. 3. Default Values and Resetting: - Initial values are set via the defaultValues property in useForm [5][6]. - The reset method from useForm can be used to update or reset the entire form state, including field arrays [6][7]. When resetting a form with a field array, it is recommended to provide the full structure of the default values to ensure all fields are correctly synchronized [6][7]. - Note: Be cautious when using append or insert with empty objects if default values are defined; React Hook Form treats undefined values as a fallback to your configured defaultValues [8]. 4. Accessing State: - Use getFieldState(name) to retrieve the state (isDirty, isTouched, invalid, error) of a specific field [3]. For implementation, always use the item.id generated by useFieldArray as the React key for your field components to prevent re-render issues [4][9]. Top results: [1], [4], [5], [6]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx'
sed -n '220,270p' "$file"

Repository: triggerdotdev/trigger.dev

Length of output: 1640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- BillingAlertsSection props and submit flow ---'
sed -n '1,140p' apps/webapp/app/components/billing/BillingAlertsSection.tsx
echo
sed -n '230,430p' apps/webapp/app/components/billing/BillingAlertsSection.tsx

echo '--- who renders BillingAlertsSection ---'
rg -n 'BillingAlertsSection' apps/webapp -g '*.{ts,tsx}'

echo '--- billing alerts route action that changes saved emails ---'
sed -n '200,280p' apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx

Repository: triggerdotdev/trigger.dev

Length of output: 12170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- billing components with reset or prop-driven reinit ---'
rg -n 'form\.reset\(|defaultValue:|useEffect\(\(\) => \{|useEffect\([^)]*\[.*saved|alertsResetRequested' apps/webapp/app/components/billing -g '*.{ts,tsx}'

echo '--- BillingLimitConfigSection relevant slice ---'
sed -n '140,230p' apps/webapp/app/components/billing/BillingLimitConfigSection.tsx

echo '--- BillingLimitRecoveryPanel relevant slice ---'
sed -n '60,170p' apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx

Repository: triggerdotdev/trigger.dev

Length of output: 9456


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx'

echo '--- usage of BillingAlertsSection ---'
rg -n 'BillingAlertsSection|alertsResetRequested|alerts=' "$file"

echo '--- surrounding render block ---'
sed -n '520,590p' "$file"

echo '--- action/loader slices around billing alerts save ---'
sed -n '200,260p' "$file"
sed -n '300,360p' "$file"

Repository: triggerdotdev/trigger.dev

Length of output: 6511


🌐 Web query:

@conform-to/react useForm defaultValue change field list reset getFieldList Conform

💡 Result:

In the Conform library for React, changing the defaultValue of the useForm hook does not automatically reset existing field lists because Conform maintains its internal state independent of external changes to props [1]. To update the form and reset field lists effectively when defaultValue changes, you must ensure that Conform recognizes the update or remounts the components [1]. Key methods to handle this include: 1. Changing the form id or key: You can pass a unique id or key to the useForm hook [2][3]. When this value changes, Conform will automatically reset the form to its initial state, which is the most reliable way to handle a full reset when dependencies like defaultValue update [2][3]. 2. Using the reset intent: You can trigger a reset programmatically using the reset intent provided by Conform [4]. This can be done via a button or by calling form.reset [4][5]. If you need to reset a specific field list, you can pass the name of that field to the reset intent [4]. 3. React 19/Next.js 15 Considerations: In newer versions of React, form elements may automatically reset after server actions, which can conflict with Conform's state management [6][5][7]. If your form is unexpectedly resetting, you may need to prevent the native browser reset event using an event listener [6]: useEffect( => { const handleReset = (event: Event) => { if (event.target === document.forms.namedItem(form.id)) { event.preventDefault; } }; document.addEventListener('reset', handleReset, true); return => document.removeEventListener('reset', handleReset, true); }, [form.id]); To access and manage list fields, use the getFieldList method on the field metadata [8][9]. This returns an array of field objects that you can map over [8]. Each object contains metadata (like key, name, and initialValue) that should be used to correctly bind the input fields to Conform [8][4]. If you find that the list itself is not re-rendering after an update, explicitly using the key property provided by each item in getFieldList (e.g., key={item.key}) is essential for React to correctly remount the updated inputs [3][1].

Citations:


Reset the Conform email list when savedEmails changes. useForm keeps the existing field list until the form is reset or remounted, so updating only emailValues leaves emails.getFieldList() at the old length. After removing an address and saving, the UI can keep rendering stale placeholder rows. Reset the form/list on saved-email changes, or key the form by the saved email set.

apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx (1)

99-100: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file list ==\n'
git ls-files 'apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx' \
  'apps/webapp/app/**' | sed -n '1,120p'

printf '\n== locate related billing action/route files ==\n'
fd -a 'billing.*(route|action)|BillingLimitRecoveryPanel' apps/webapp/app

printf '\n== outline target component ==\n'
ast-grep outline apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx --view expanded

printf '\n== read target component slice ==\n'
sed -n '1,280p' apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx

printf '\n== search for intent handling ==\n'
rg -n '"billing-limit-resolve"|"billing-limit"|"billing-alerts"|intent' apps/webapp/app -g '!**/*.map'

printf '\n== search for useNavigation / navigation.state in nearby billing files ==\n'
rg -n 'useNavigation\(|navigation\.state|formData\?\.get\("intent"\)' apps/webapp/app/components apps/webapp/app -g '*Billing*' -g '*billing*'

Repository: triggerdotdev/trigger.dev

Length of output: 33798


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== billing-limits route outline ==\n'
ast-grep outline apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx --view expanded

printf '\n== route slices around action handling ==\n'
sed -n '200,540p' apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx

printf '\n== billing-limits page composition ==\n'
sed -n '1,260p' apps/webapp/app/components/billing/BillingLimitConfigSection.tsx
printf '\n---\n'
sed -n '1,280p' apps/webapp/app/components/billing/BillingAlertsSection.tsx

Repository: triggerdotdev/trigger.dev

Length of output: 248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

route='apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx'
config='apps/webapp/app/components/billing/BillingLimitConfigSection.tsx'
alerts='apps/webapp/app/components/billing/BillingAlertsSection.tsx'

printf '\n== billing-limits route outline ==\n'
ast-grep outline "$route" --view expanded

printf '\n== route slices around action handling ==\n'
sed -n '200,540p' "$route"

printf '\n== billing-limits config section ==\n'
sed -n '1,280p' "$config"

printf '\n== billing-alerts section ==\n'
sed -n '1,320p' "$alerts"

Repository: triggerdotdev/trigger.dev

Length of output: 34859


Scope isSubmitting to the resolve form intent.

useNavigation() is shared across the billing-limits route, so other submits on this page can disable this button and swap its label.

Suggested fix
-  const isSubmitting = navigation.state === "submitting";
+  const isSubmitting =
+    navigation.state === "submitting" &&
+    navigation.formData?.get("intent") === "billing-limit-resolve";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  const navigation = useNavigation();
  const isSubmitting =
    navigation.state === "submitting" &&
    navigation.formData?.get("intent") === "billing-limit-resolve";

carderne added 5 commits June 29, 2026 14:37
Migrate the dashboard form layer from @conform-to 0.9 to the 1.x API.
conform 1.x peer-depends on zod ^3.21 || ^4, so it runs on the current
zod 3 and unblocks the zod 4 upgrade (conform 0.9 cannot bundle against
zod 4, which removed ZodNativeEnum, ZodEffects, and ZodPipeline).
Address CodeRabbit feedback on the conform 1.x migration: use defaultValue
(not value) on hidden inputs so they stay uncontrolled, return the env-var
project-lookup failure as a form-level error, join multi-message field
errors when rendering, and clone alerts.emails before using it as mutable
field-list state.
The schedules and branches add-on purchase modals detected conform
responses with "intent" in fetcher.data, but conform v1's
submission.reply() returns a status field, not intent, so lastResult was
always undefined and server-side errors never rendered. Switch the guard
to "status" in fetcher.data, matching the rest of the migration.
Resolves conform v1 type errors surfaced once the @conform-to v1 upgrade
was rebased onto main's newer billing-limits / dev-branches code:

- billing config/recovery panels: FormMetadata has no `.ref` in v1; use a
  local form ref for the manual input-event dispatch
- billing alerts: pass the schema generic to useForm so array fields infer
  string[] defaults instead of string
- test task form: drop redundant explicit type="hidden" (getInputProps
  sets it), move the region Select `key` after the spread, add the missing
  getInputProps options arg, and use getFormProps for the delete form
- branches purchase modal: render field.errors (v1) instead of the removed
  .error / .initialError
@carderne carderne enabled auto-merge (squash) June 29, 2026 13:41
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

Open in Devin Review

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.

1 participant