Skip to content

feat(private-link): Add private links UI#3264

Draft
0ski wants to merge 3 commits intomainfrom
oskar/feat-private-link
Draft

feat(private-link): Add private links UI#3264
0ski wants to merge 3 commits intomainfrom
oskar/feat-private-link

Conversation

@0ski
Copy link
Collaborator

@0ski 0ski commented Mar 24, 2026

No description provided.

@changeset-bot
Copy link

changeset-bot bot commented Mar 24, 2026

🦋 Changeset detected

Latest commit: d8d5c89

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 29 packages
Name Type
@trigger.dev/core Patch
@trigger.dev/build Patch
trigger.dev Patch
@trigger.dev/python Patch
@trigger.dev/redis-worker Patch
@trigger.dev/schema-to-json Patch
@trigger.dev/sdk Patch
@internal/cache Patch
@internal/clickhouse Patch
@internal/llm-model-catalog Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/schedule-engine Patch
@internal/testcontainers Patch
@internal/tracing Patch
@internal/tsql Patch
@internal/zod-worker Patch
d3-chat Patch
references-d3-openai-agents Patch
references-nextjs-realtime Patch
references-realtime-hooks-test Patch
references-realtime-streams Patch
references-telemetry Patch
@internal/sdk-compat-tests Patch
@trigger.dev/react-hooks Patch
@trigger.dev/rsc Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 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

Introduces optional private networking support end-to-end. Billing and run-engine types now include hasPrivateLink, dequeue messages and schemas carry it, supervisor workload creation and Kubernetes labels may include privatelink. Webapp adds feature flag hasPrivateConnections, environment variables, two new Remix routes for listing/creating private connections, platform service functions (CRUD + regions), UI navigation items, path helpers, and a Changeset for @trigger.dev/core.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is completely empty, providing no information about the change, testing, or changelog as required by the template. Add a complete description following the template: include issue closure, checklist confirmation, testing details, changelog summary, and any relevant screenshots.
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 (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(private-link): Add private links UI' is concise and directly summarizes the main change—adding a private links user interface to the webapp.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch oskar/feat-private-link

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

@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

🧹 Nitpick comments (4)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx (2)

188-378: Consider extracting shared port entry logic to reduce duplication.

TerraformWizard and AIPromptWizard have nearly identical state management and UI for hostname, ports, and region selection. Consider extracting a shared hook or component to reduce duplication.

Example: Shared hook
function useResourceConfig() {
  const [hostname, setHostname] = useState("");
  const [ports, setPorts] = useState<PortEntry[]>([{ port: "5432", protocol: "TCP" }]);
  const [region, setRegion] = useState("us-east-1");

  const addPort = () => setPorts([...ports, { port: "", protocol: "TCP" }]);
  const removePort = (index: number) => setPorts(ports.filter((_, i) => i !== index));
  const updatePort = (index: number, field: keyof PortEntry, value: string) =>
    setPorts(ports.map((p, i) => (i === index ? { ...p, [field]: value } : p)));

  const validPorts = ports.filter((p) => p.port !== "");

  return { hostname, setHostname, ports, addPort, removePort, updatePort, validPorts, region, setRegion };
}

Also applies to: 380-523

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections.new/route.tsx
around lines 188 - 378, TerraformWizard and AIPromptWizard duplicate
hostname/ports/region state and UI; extract that shared logic into a hook (e.g.,
useResourceConfig) or a shared PortList component to centralize state (hostname,
ports, region), handlers (addPort, removePort, updatePort), and derived values
(validPorts), then replace the local state/handlers in both TerraformWizard and
AIPromptWizard to use the hook/component so the Terraform-specific script
generation remains in TerraformWizard while UI/state logic is reused.

260-269: Terraform script hardcodes supported_regions which may drift from actual availability.

The Terraform configuration hardcodes supported_regions = ["us-east-1", "eu-central-1"] (line 264) while the form's availableRegions is fetched dynamically from the API. This could cause confusion if the regions diverge.

Consider passing availableRegions to TerraformWizard and using it in the generated script, or adding a comment noting this is a simplification.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections.new/route.tsx
around lines 260 - 269, The Terraform snippet hardcodes supported_regions which
may diverge from the UI's dynamic availableRegions; update the flow so
TerraformWizard receives the component's availableRegions and uses it when
generating the aws_vpc_endpoint_service block (replace the literal
supported_regions value with the passed availableRegions list), ensuring the
resource aws_vpc_endpoint_service "trigger_privatelink" reflects the runtime
availableRegions used by the form; alternatively, if you prefer a simpler
change, add a clear comment in the Terraform output noting supported_regions is
a simplified static list and may not match availableRegions from the UI.
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx (2)

152-152: Consider adding DELETING to TERMINAL_STATUSES for polling purposes.

Currently TERMINAL_STATUSES only includes ["ACTIVE", "ERROR"], so connections in DELETING status will trigger continuous polling. If no further state updates are expected for DELETING connections until they disappear, adding it to terminal statuses would reduce unnecessary revalidations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx
at line 152, TERMINAL_STATUSES currently lists only "ACTIVE" and "ERROR",
causing connections in "DELETING" to keep polling; update the TERMINAL_STATUSES
constant (type PrivateLinkConnectionStatus) to include "DELETING" so polling
logic treats deletions as terminal and stops revalidations for connections in
that state (update the array assigned to TERMINAL_STATUSES).

175-175: hasPrivateNetworking is hardcoded to true.

This variable is set to true unconditionally but is used to conditionally render UI. Since the route already gates access via hasPrivateConnections in the loader, this appears intentional but could be confusing. Consider removing this variable and the related conditional, or adding a comment explaining the intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx
at line 175, The constant hasPrivateNetworking is hardcoded to true and
duplicates the loader gate hasPrivateConnections; remove the unused
hasPrivateNetworking variable and any conditional branches that check it (or, if
the intent is to make a permanent true toggle, add a clear comment above
hasPrivateNetworking explaining why it's hardcoded and reference
hasPrivateConnections in the loader). Locate usages of hasPrivateNetworking in
this route (search for hasPrivateNetworking in the file) and either delete the
variable and simplify the conditional rendering to rely on
hasPrivateConnections, or keep the variable but replace the hardcoded assignment
with a documented constant explaining its purpose.
🤖 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/webapp/app/features.server.ts`:
- Around line 17-22: Replace direct process.env access in hasPrivateConnections
with the exported env object from env.server.ts: import and use
env.PRIVATE_CONNECTIONS_ENABLED in hasPrivateConnections (the function name is
hasPrivateConnections) instead of process.env.PRIVATE_CONNECTIONS_ENABLED, and
ensure the PRIVATE_CONNECTIONS_ENABLED key is added to the env schema/export in
env.server.ts so the property exists and types are correct; keep the existing
isManagedCloud(host) call and return logic unchanged.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx:
- Around line 98-105: The call site uses tryCatch(deletePrivateLink(...))
expecting exceptions, but deletePrivateLink in platform.v3.server.ts currently
returns void and swallows errors; either change deletePrivateLink to throw on
failure (propagate the caught error) so tryCatch can catch it, or change its
signature to return a success/failure result (e.g., boolean or { ok, error })
and update this route to check that result instead of relying on tryCatch;
specifically update deletePrivateLink to rethrow or return a result, and adjust
the code around tryCatch/redirectWithErrorMessage/v3PrivateConnectionsPath in
this route to handle the new return or thrown error accordingly.
- Around line 59-62: Replace the console.error call in the tryCatch block that
calls getPrivateLinks with the project's structured logger: import the project's
logger (e.g., logger) and call logger.error with the error as structured data
and a clear message (for example logger.error({ error }, "Error loading private
link connections")). Update the top-of-file imports to include the logger and
remove the console.error usage so error reporting uses the project's logging
facility.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections.new/route.tsx:
- Line 72: Replace the direct process.env access for
PRIVATE_CONNECTIONS_AWS_ACCOUNT_IDS with the env export from app/env.server.ts:
read env.PRIVATE_CONNECTIONS_AWS_ACCOUNT_IDS, split by "," and filter(Boolean)
to populate awsAccountIds (the variable currently declared as awsAccountIds),
and remove any use of process.env.PRIVATE_CONNECTIONS_AWS_ACCOUNT_IDS; also add
PRIVATE_CONNECTIONS_AWS_ACCOUNT_IDS to the env schema in env.server.ts so the
type and runtime validation include this variable.

---

Nitpick comments:
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx:
- Line 152: TERMINAL_STATUSES currently lists only "ACTIVE" and "ERROR", causing
connections in "DELETING" to keep polling; update the TERMINAL_STATUSES constant
(type PrivateLinkConnectionStatus) to include "DELETING" so polling logic treats
deletions as terminal and stops revalidations for connections in that state
(update the array assigned to TERMINAL_STATUSES).
- Line 175: The constant hasPrivateNetworking is hardcoded to true and
duplicates the loader gate hasPrivateConnections; remove the unused
hasPrivateNetworking variable and any conditional branches that check it (or, if
the intent is to make a permanent true toggle, add a clear comment above
hasPrivateNetworking explaining why it's hardcoded and reference
hasPrivateConnections in the loader). Locate usages of hasPrivateNetworking in
this route (search for hasPrivateNetworking in the file) and either delete the
variable and simplify the conditional rendering to rely on
hasPrivateConnections, or keep the variable but replace the hardcoded assignment
with a documented constant explaining its purpose.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections.new/route.tsx:
- Around line 188-378: TerraformWizard and AIPromptWizard duplicate
hostname/ports/region state and UI; extract that shared logic into a hook (e.g.,
useResourceConfig) or a shared PortList component to centralize state (hostname,
ports, region), handlers (addPort, removePort, updatePort), and derived values
(validPorts), then replace the local state/handlers in both TerraformWizard and
AIPromptWizard to use the hook/component so the Terraform-specific script
generation remains in TerraformWizard while UI/state logic is reused.
- Around line 260-269: The Terraform snippet hardcodes supported_regions which
may diverge from the UI's dynamic availableRegions; update the flow so
TerraformWizard receives the component's availableRegions and uses it when
generating the aws_vpc_endpoint_service block (replace the literal
supported_regions value with the passed availableRegions list), ensuring the
resource aws_vpc_endpoint_service "trigger_privatelink" reflects the runtime
availableRegions used by the form; alternatively, if you prefer a simpler
change, add a clear comment in the Terraform output noting supported_regions is
a simplified static list and may not match availableRegions from the UI.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d2e8fb2a-352f-4cd1-8550-7beb778499c9

📥 Commits

Reviewing files that changed from the base of the PR and between c00dae0 and bd1484d.

📒 Files selected for processing (16)
  • .changeset/private-networking-dequeue.md
  • .server-changes/private-networking.md
  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/supervisor/src/workloadManager/types.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • packages/core/src/v3/schemas/runEngine.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{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

**/*.{ts,tsx}: For apps and internal packages (apps/*, internal-packages/*), use pnpm run typecheck --filter <package> for verification, never use build as it proves almost nothing about correctness
Use testcontainers helpers (redisTest, postgresTest, containerTest from @internal/testcontainers) for integration tests with Redis and PostgreSQL instead of mocking
When writing Trigger.dev tasks, always import from @trigger.dev/sdk - never use @trigger.dev/sdk/v3 or deprecated client.defineJob

Files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

**/*.{ts,tsx,js,jsx}: Use pnpm for package management in this monorepo (version 10.23.0) with Turborepo for orchestration - run commands from root with pnpm run
Add crumbs as you write code for debug tracing using // @Crumbs comments or `// `#region` `@crumbs blocks - they stay on the branch throughout development and are stripped via agentcrumbs strip before merge

Files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/services/platform.v3.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset

Files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/supervisor/src/workloadManager/**/*.{js,ts}

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

Container orchestration abstraction (Docker or Kubernetes) should be implemented in src/workloadManager/

Files:

  • apps/supervisor/src/workloadManager/types.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

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

Files:

  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
packages/core/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/core/CLAUDE.md)

Never import the root package (@trigger.dev/core). Always use subpath imports such as @trigger.dev/core/v3, @trigger.dev/core/v3/utils, @trigger.dev/core/logger, or @trigger.dev/core/schemas

Files:

  • packages/core/src/v3/schemas/runEngine.ts
packages/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/**/*.{ts,tsx,js,jsx}: For public packages (packages/*), use pnpm run build --filter <package> for verification
Add a changeset via pnpm run changeset:add when modifying any public package (packages/* or integrations/*) - default to patch for bug fixes and minor changes

Files:

  • packages/core/src/v3/schemas/runEngine.ts
packages/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

In the core package (@trigger.dev/core), import subpaths only - never import from root

Files:

  • packages/core/src/v3/schemas/runEngine.ts
apps/webapp/app/**/*.{ts,tsx}

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

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/**/*.{ts,tsx}

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

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/app/**/*.{ts,tsx,server.ts}

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

Access environment variables via env export from app/env.server.ts. Never use process.env directly

Files:

  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/app/v3/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

In the webapp v3 directory, only modify V2 code paths when encountering V1/V2 branching in services - all new work uses Run Engine 2.0 (@internal/run-engine) and redis-worker, not legacy V1 engine code

Files:

  • apps/webapp/app/v3/runEngine.server.ts
internal-packages/run-engine/src/engine/systems/**/*.ts

📄 CodeRabbit inference engine (internal-packages/run-engine/CLAUDE.md)

Integrate OpenTelemetry tracer and meter instrumentation in RunEngine systems for observability

Files:

  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
apps/webapp/app/services/**/*.server.{ts,tsx}

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

Separate testable services from configuration files; follow the pattern of realtimeClient.server.ts (testable service) and realtimeClientGlobal.server.ts (configuration) in the webapp

Files:

  • apps/webapp/app/services/platform.v3.server.ts
🧠 Learnings (22)
📚 Learning: 2026-03-02T12:42:47.652Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/supervisor/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:47.652Z
Learning: Applies to apps/supervisor/src/workloadManager/**/*.{js,ts} : Container orchestration abstraction (Docker or Kubernetes) should be implemented in `src/workloadManager/`

Applied to files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
📚 Learning: 2026-03-02T12:42:47.652Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/supervisor/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:47.652Z
Learning: Applies to apps/supervisor/src/workloadServer/**/*.{js,ts} : HTTP server for workload communication (heartbeats, snapshots) should be implemented in `src/workloadServer/`

Applied to files:

  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
📚 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/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.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/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/types.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/features.server.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-22T13:45:36.346Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/navigation/SideMenu.tsx:460-489
Timestamp: 2026-03-22T13:45:36.346Z
Learning: In triggerdotdev/trigger.dev, sidebar navigation items (SideMenu.tsx) are intentionally NOT gated behind feature-flag or permission checks at the nav level. Authorization is enforced at the route/loader level instead. Hiding nav items based on access checks is considered confusing UX. This applies to items like "AI Metrics" (v3BuiltInDashboardPath) and other dashboard links — they are always rendered in the sidebar regardless of hasQueryAccess or similar flags.

Applied to files:

  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
📚 Learning: 2026-02-03T18:27:49.039Z
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:49.039Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (like the Edit button with PencilSquareIcon) intentionally have no text labels - only icons are shown in the TableCellMenu. This is a deliberate UI design pattern for compact icon-only menu items.

Applied to files:

  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.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/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:25.029Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.029Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.ts : Only modify V2 code paths when editing services that branch on `RunEngineVersion` to support both V1 and V2 (e.g., `cancelTaskRun.server.ts`, `batchTriggerV3.server.ts`)

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to apps/webapp/app/v3/**/*.{ts,tsx} : In the webapp v3 directory, only modify V2 code paths when encountering V1/V2 branching in services - all new work uses Run Engine 2.0 (`internal/run-engine`) and redis-worker, not legacy V1 engine code

Applied to files:

  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`

Applied to files:

  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2026-03-02T12:43:43.173Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/redis-worker/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:43.173Z
Learning: Applies to packages/redis-worker/**/redis-worker/src/queue.ts : Job queue abstraction should be Redis-backed in src/queue.ts

Applied to files:

  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
📚 Learning: 2026-03-03T13:08:03.862Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3166
File: packages/redis-worker/src/fair-queue/index.ts:1114-1121
Timestamp: 2026-03-03T13:08:03.862Z
Learning: In packages/redis-worker/src/fair-queue/index.ts, it's acceptable for the worker queue depth cap check to allow overshooting by up to batchClaimSize messages per iteration, as the next iteration will recheck and prevent sustained growth beyond the limit.

Applied to files:

  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
📚 Learning: 2026-03-14T10:24:37.515Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3219
File: internal-packages/run-engine/src/run-queue/index.ts:774-777
Timestamp: 2026-03-14T10:24:37.515Z
Learning: In `internal-packages/run-engine/src/run-queue/index.ts`, checking `message.concurrencyKey` to branch CK vs non-CK logic is safe and intentional. The `concurrencyKey` field and the `:ck:` suffix in `message.queue` are always set atomically during the same enqueue call (`queueKey = this.keys.queueKey(env, message.queue, concurrencyKey)`). In `enqueueMessage`, `concurrencyKey` is the source of truth that creates the `:ck:` queue name (checking the queue name would be circular). In `#callAcknowledgeMessage`, `#callNackMessage`, and `#callMoveToDeadLetterQueue`, the `OutputPayload` is a single serialized JSON blob containing both fields — a message with `:ck:` in the queue name but a missing `concurrencyKey` cannot exist. Do not suggest replacing `message.concurrencyKey` checks with queue-name-derived predicates.

Applied to files:

  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
📚 Learning: 2026-02-19T18:09:23.944Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3095
File: apps/webapp/app/components/navigation/DashboardDialogs.tsx:47-47
Timestamp: 2026-02-19T18:09:23.944Z
Learning: In the triggerdotdev/trigger.dev codebase, the pattern `isPaying === false` is used consistently to explicitly check for free plan users. This is a project convention that distinguishes between `isPaying === true` (paying), `isPaying === false` (free), and `isPaying === undefined` (no subscription data). Do not suggest changing this to negation pattern.
```
<!-- <review_comment_addressed>

Applied to files:

  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-13T13:42:25.092Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/routes/admin.llm-models.new.tsx:65-91
Timestamp: 2026-03-13T13:42:25.092Z
Learning: In `apps/webapp/app/routes/admin.llm-models.new.tsx`, sequential Prisma writes for model/tier creation are intentionally not wrapped in a transaction. The form is admin-only with low concurrency risk, and the blast radius is considered minimal for admin tooling.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
📚 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.private-connections.new/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:25.029Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.029Z
Learning: Applies to apps/webapp/app/routes/**/*.ts : Use Remix flat-file route convention with dot-separated segments where `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
🔇 Additional comments (14)
internal-packages/run-engine/src/engine/billingCache.ts (1)

20-24: LGTM!

The BillingPlan type extension with optional hasPrivateLink is appropriate. The caching logic correctly propagates the field from the upstream billing callback, and the default free plan correctly omits the field (falsy by default).

Also applies to: 67-81

apps/webapp/app/utils/pathBuilder.ts (1)

641-647: LGTM!

The new path builder functions follow the established pattern of other organization settings paths in this file. Naming and implementation are consistent with existing conventions.

apps/webapp/app/hooks/useFeatures.ts (1)

5-8: LGTM!

The default fallback now correctly includes both TriggerFeatures fields (isManagedCloud and hasPrivateConnections), ensuring the hook always returns a complete object matching the type definition. Defaulting hasPrivateConnections to false is the appropriate safe default.

apps/supervisor/src/index.ts (1)

270-270: LGTM!

The hasPrivateLink flag is correctly extracted from message.organization.hasPrivateLink and passed to the workload manager. The optional nature of the field in both the message schema and WorkloadManagerCreateOptions ensures type compatibility.

apps/supervisor/src/workloadManager/types.ts (1)

38-39: LGTM!

The optional hasPrivateLink field is appropriately added to the workload creation options. The comment provides helpful context for the field's purpose.

packages/core/src/v3/schemas/runEngine.ts (1)

267-270: LGTM!

The hasPrivateLink field is correctly added as an optional boolean to the organization object in the DequeuedMessage schema. This is the appropriate place to define the field since it propagates through the dequeue pipeline to the supervisor. A changeset for this change already exists in the repository.

apps/webapp/app/services/platform.v3.server.ts (1)

646-724: Implementation follows established patterns.

The four new private link functions (getPrivateLinks, createPrivateLink, deletePrivateLink, getPrivateLinkRegions) correctly follow the error handling pattern used throughout this file:

  • Early return when client is unavailable
  • tryCatch wrapper for async client calls
  • Logging on both thrown errors and !result.success responses
  • Silent failure (return undefined/void) rather than throwing

The functions are already in use across the codebase (in private-connections route files) without issues, confirming that the BillingClient type from @trigger.dev/platform includes these methods.

apps/supervisor/src/workloadManager/kubernetes.ts (1)

338-353: Implementation is correct for CiliumNetworkPolicy matching.

The conditional privatelink label addition with orgId as the value enables network policy selectors to match pods by organization. The truthy check on opts.hasPrivateLink correctly handles both undefined and false cases. Organization IDs use CUID format, which generates alphanumeric strings well under the 63-character Kubernetes label limit and fully comply with the K8s label value pattern.

.changeset/private-networking-dequeue.md (1)

1-5: LGTM!

Changeset correctly documents a patch-level change for the optional hasPrivateLink field addition.

apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx (1)

108-116: LGTM!

The Private Connections menu item follows the existing pattern used for managed cloud features (e.g., Usage, Billing), with proper feature flag gating, icon styling, and data-action attribute.

.server-changes/private-networking.md (1)

1-6: LGTM!

Server-changes documentation correctly describes the webapp feature addition with appropriate metadata.

internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (2)

498-518: LGTM!

The hasPrivateLink field is correctly typed as boolean | undefined and properly propagated from the billing cache result. When billing fails, it gracefully falls back to undefined, which downstream consumers can handle appropriately.


585-588: LGTM!

The hasPrivateLink field is correctly added to the organization object in the dequeued message, matching the schema extension in packages/core/src/v3/schemas/runEngine.ts.

apps/webapp/app/v3/runEngine.server.ts (1)

163-168: No changes needed. The code correctly accesses plan.v3Subscription.plan.limits after confirming plan.v3Subscription.plan exists at line 155. When the plan exists, the limits property is guaranteed to be present by design—the billing client uses fail-fast behavior to throw rather than return undefined limits. This matches the established pattern in platform.v3.server.ts (lines 283–295).

			> Likely an incorrect or invalid review comment.

@0ski 0ski force-pushed the oskar/feat-private-link branch from bd1484d to a9d6231 Compare March 26, 2026 11:45
Copy link
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 4 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

@0ski
Copy link
Collaborator Author

0ski commented Mar 26, 2026

Code review

Found 1 issue:

  1. Direct process.env usage in webapp code (apps/webapp/CLAUDE.md says "Access via env export from app/env.server.ts. Never use process.env directly.")

Both PRIVATE_CONNECTIONS_ENABLED and PRIVATE_CONNECTIONS_AWS_ACCOUNT_IDS are accessed via process.env directly instead of through the env export from app/env.server.ts. These env vars should be declared in env.server.ts and accessed through the typed env singleton.

function hasPrivateConnections(host: string): boolean {
if (process.env.PRIVATE_CONNECTIONS_ENABLED === "1") {
return isManagedCloud(host);
}
return false;

const awsAccountIds = process.env.PRIVATE_CONNECTIONS_AWS_ACCOUNT_IDS?.split(",").filter(Boolean) ?? [];

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

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: 1

🧹 Nitpick comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx (2)

2-2: Remove unused imports.

useFetcher and Badge are imported but not used in this file.

Proposed fix
-import { Form, useFetcher, useRevalidator, type MetaFunction } from "@remix-run/react";
+import { Form, useRevalidator, type MetaFunction } from "@remix-run/react";
-import { Badge } from "~/components/primitives/Badge";

Also applies to: 11-11

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx
at line 2, The import statement currently includes unused symbols—remove
useFetcher from the Remix import list and remove the unused Badge import
wherever it is imported (referenced as Badge) so the module only imports symbols
actually used in this file; update the import lines that contain useFetcher and
Badge to eliminate them and run the linter/typecheck to confirm no remaining
references to useFetcher or Badge (e.g., adjust the import at the top that reads
Form, useFetcher, useRevalidator, type MetaFunction and any import that brings
in Badge).

176-176: Hardcoded hasPrivateNetworking creates unreachable code.

hasPrivateNetworking is hardcoded to true, making the "requires upgrade" UI block (lines 204-209) dead code. If this is intentional because the feature flag gates access at the route level, consider removing the unreachable branch. Otherwise, derive this value from the plan data.

Option A: Remove dead code if gating happens at route level
-  const hasPrivateNetworking = true;
   const limit = plan?.v3Subscription?.plan?.limits?.privateLinkConnectionLimit ?? 2;
   const canAdd = connections.filter((c) => c.status !== "DELETING").length < limit;

   return (
     <PageContainer>
       ...
-            {!hasPrivateNetworking ? (
-              <div className="rounded-lg border border-grid-dimmed p-6 text-center">
-                <Paragraph variant="small" className="text-text-dimmed">
-                  Private Connections require upgrading to Pro or an Enterprise plan.
-                </Paragraph>
-              </div>
-            ) : connections.length === 0 ? (
+            {connections.length === 0 ? (
Option B: Derive from plan if plan-based gating is intended
-  const hasPrivateNetworking = true;
+  const hasPrivateNetworking = Boolean(plan?.v3Subscription?.plan?.limits?.privateLinkConnectionLimit);

Also applies to: 204-209

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx
at line 176, hasPrivateNetworking is hardcoded to true which makes the "requires
upgrade" UI branch unreachable; either remove that dead branch or compute
hasPrivateNetworking from the organization's plan data. Update the code that
defines hasPrivateNetworking (replace the constant) to derive its value from the
plan/planData object used in this route (e.g., check plan.features, plan.tier,
or plan.privateNetworking flag) so the UI branch that renders the "requires
upgrade" message executes correctly when the plan lacks private networking, or
delete the unreachable UI block if access is already gated at route-level.
🤖 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/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx:
- Line 4: Replace the root import of tryCatch from "@trigger.dev/core" with the
subpath export "@trigger.dev/core/v3": update the import statement that
currently imports tryCatch to import it from "@trigger.dev/core/v3" so the
module follows the webapp subpath export guideline and uses the v3 entrypoint.

---

Nitpick comments:
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx:
- Line 2: The import statement currently includes unused symbols—remove
useFetcher from the Remix import list and remove the unused Badge import
wherever it is imported (referenced as Badge) so the module only imports symbols
actually used in this file; update the import lines that contain useFetcher and
Badge to eliminate them and run the linter/typecheck to confirm no remaining
references to useFetcher or Badge (e.g., adjust the import at the top that reads
Form, useFetcher, useRevalidator, type MetaFunction and any import that brings
in Badge).
- Line 176: hasPrivateNetworking is hardcoded to true which makes the "requires
upgrade" UI branch unreachable; either remove that dead branch or compute
hasPrivateNetworking from the organization's plan data. Update the code that
defines hasPrivateNetworking (replace the constant) to derive its value from the
plan/planData object used in this route (e.g., check plan.features, plan.tier,
or plan.privateNetworking flag) so the UI branch that renders the "requires
upgrade" message executes correctly when the plan lacks private networking, or
delete the unreachable UI block if access is already gated at route-level.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f52ae241-a7bb-4ef9-89dd-e8157c090854

📥 Commits

Reviewing files that changed from the base of the PR and between 1716e03 and d8d5c89.

📒 Files selected for processing (5)
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/services/platform.v3.server.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{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

**/*.{ts,tsx}: For apps and internal packages (apps/*, internal-packages/*), use pnpm run typecheck --filter <package> for verification, never use build as it proves almost nothing about correctness
Use testcontainers helpers (redisTest, postgresTest, containerTest from @internal/testcontainers) for integration tests with Redis and PostgreSQL instead of mocking
When writing Trigger.dev tasks, always import from @trigger.dev/sdk - never use @trigger.dev/sdk/v3 or deprecated client.defineJob

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.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/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

**/*.{ts,tsx,js,jsx}: Use pnpm for package management in this monorepo (version 10.23.0) with Turborepo for orchestration - run commands from root with pnpm run
Add crumbs as you write code for debug tracing using // @Crumbs comments or `// `#region` `@crumbs blocks - they stay on the branch throughout development and are stripped via agentcrumbs strip before merge

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/app/**/*.{ts,tsx}

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

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/**/*.{ts,tsx}

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

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/app/**/*.{ts,tsx,server.ts}

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

Access environment variables via env export from app/env.server.ts. Never use process.env directly

Files:

  • apps/webapp/app/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
apps/webapp/app/services/**/*.server.{ts,tsx}

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

Separate testable services from configuration files; follow the pattern of realtimeClient.server.ts (testable service) and realtimeClientGlobal.server.ts (configuration) in the webapp

Files:

  • apps/webapp/app/services/platform.v3.server.ts
🧠 Learnings (25)
📚 Learning: 2026-03-23T06:24:25.028Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.028Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx,server.ts} : Access environment variables via `env` export from `app/env.server.ts`. Never use `process.env` directly

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2026-03-02T12:42:47.652Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/supervisor/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:47.652Z
Learning: Applies to apps/supervisor/src/env.ts : Environment configuration should be defined in `src/env.ts`

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2026-03-23T06:24:25.028Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.028Z
Learning: Applies to apps/webapp/**/*.{test,spec}.{ts,tsx} : For testable code, never import `env.server.ts` in test files. Pass configuration as options/constructor arguments instead

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2026-03-13T13:42:59.104Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/routes/admin.api.v1.llm-models.$modelId.ts:40-43
Timestamp: 2026-03-13T13:42:59.104Z
Learning: In `apps/webapp/app/routes/admin.api.v1.llm-models.$modelId.ts` and `apps/webapp/app/routes/admin.api.v1.llm-models.ts`, the `startDate` field in `UpdateModelSchema` and `CreateModelSchema` intentionally uses `z.string().optional()` (or `.nullable().optional()`) without strict ISO datetime validation. Invalid date strings are rejected at the Prisma/DB layer. This is acceptable because these are admin-only API routes protected by Personal Access Token (PAT) authentication and are not user-facing.

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-10T09:09:07.399Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2663
File: apps/webapp/app/env.server.ts:1205-1206
Timestamp: 2025-11-10T09:09:07.399Z
Learning: In the trigger.dev webapp, S2_ACCESS_TOKEN and S2_DEPLOYMENT_LOGS_BASIN_NAME environment variables must remain optional until an OSS version of S2 is available, to avoid breaking environments that don't have S2 provisioned.

Applied to files:

  • apps/webapp/app/env.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.test.{ts,tsx} : Test files should only import classes and functions from `app/**/*.ts` files and should not import `env.server.ts` directly or indirectly; pass configuration through options instead

Applied to files:

  • apps/webapp/app/env.server.ts
📚 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/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.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/env.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:25.029Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.029Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.ts : Only modify V2 code paths when editing services that branch on `RunEngineVersion` to support both V1 and V2 (e.g., `cancelTaskRun.server.ts`, `batchTriggerV3.server.ts`)

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to apps/webapp/app/v3/**/*.{ts,tsx} : In the webapp v3 directory, only modify V2 code paths when encountering V1/V2 branching in services - all new work uses Run Engine 2.0 (`internal/run-engine`) and redis-worker, not legacy V1 engine code

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-03-26T10:02:22.373Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 3254
File: apps/webapp/app/services/platformNotifications.server.ts:363-385
Timestamp: 2026-03-26T10:02:22.373Z
Learning: In `triggerdotdev/trigger.dev`, the `getNextCliNotification` fallback in `apps/webapp/app/services/platformNotifications.server.ts` intentionally uses `prisma.orgMember.findFirst` (single org) when no `projectRef` is provided. This is acceptable for v1 because the CLI (`dev` and `login` commands) always passes `projectRef` in normal usage, making the fallback a rare edge case. Do not flag the single-org fallback as a multi-org correctness bug in this file.

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-26T09:02:07.973Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 3274
File: apps/webapp/app/services/runsReplicationService.server.ts:922-924
Timestamp: 2026-03-26T09:02:07.973Z
Learning: When parsing Trigger.dev task run annotations in server-side services, keep `TaskRun.annotations` strictly conforming to the `RunAnnotations` schema from `trigger.dev/core/v3`. If the code already uses `RunAnnotations.safeParse` (e.g., in a `#parseAnnotations` helper), treat that as intentional/necessary for atomic, schema-accurate annotation handling. Do not recommend relaxing the annotation payload schema or using a permissive “passthrough” parse path, since the annotations are expected to be written atomically in one operation and should not contain partial/legacy payloads that would require a looser parser.

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:25.029Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.029Z
Learning: Applies to apps/webapp/app/routes/**/*.ts : Use Remix flat-file route convention with dot-separated segments where `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 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.private-connections._index/route.tsx
📚 Learning: 2026-03-13T13:45:39.411Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/routes/admin.llm-models.missing.$model.tsx:19-21
Timestamp: 2026-03-13T13:45:39.411Z
Learning: In `apps/webapp/app/routes/admin.llm-models.missing.$model.tsx`, the `decodeURIComponent(params.model ?? "")` call is intentionally unguarded. Remix route params are decoded at the routing layer before reaching the loader, so malformed percent-encoding is rejected upstream. The page is also admin-only, so the risk is minimal and no try-catch is warranted.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-13T13:42:25.092Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/routes/admin.llm-models.new.tsx:65-91
Timestamp: 2026-03-13T13:42:25.092Z
Learning: In `apps/webapp/app/routes/admin.llm-models.new.tsx`, sequential Prisma writes for model/tier creation are intentionally not wrapped in a transaction. The form is admin-only with low concurrency risk, and the blast radius is considered minimal for admin tooling.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-25T15:29:25.853Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2026-03-25T15:29:25.853Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `logger.debug()`, `logger.log()`, `logger.info()`, `logger.warn()`, and `logger.error()` for logging with key-value data

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-02-19T18:09:23.944Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3095
File: apps/webapp/app/components/navigation/DashboardDialogs.tsx:47-47
Timestamp: 2026-02-19T18:09:23.944Z
Learning: In the triggerdotdev/trigger.dev codebase, the pattern `isPaying === false` is used consistently to explicitly check for free plan users. This is a project convention that distinguishes between `isPaying === true` (paying), `isPaying === false` (free), and `isPaying === undefined` (no subscription data). Do not suggest changing this to negation pattern.
```
<!-- <review_comment_addressed>

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-22T13:45:36.346Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/navigation/SideMenu.tsx:460-489
Timestamp: 2026-03-22T13:45:36.346Z
Learning: In triggerdotdev/trigger.dev, sidebar navigation items (SideMenu.tsx) are intentionally NOT gated behind feature-flag or permission checks at the nav level. Authorization is enforced at the route/loader level instead. Hiding nav items based on access checks is considered confusing UX. This applies to items like "AI Metrics" (v3BuiltInDashboardPath) and other dashboard links — they are always rendered in the sidebar regardless of hasQueryAccess or similar flags.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/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.private-connections._index/route.tsx
🔇 Additional comments (9)
apps/webapp/app/env.server.ts (1)

1389-1391: Good env centralization for private-connections flags.

Declaring these variables in EnvironmentSchema keeps them typed and routed via the env singleton, which aligns with the expected webapp pattern.

As per coding guidelines, "Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp."

apps/webapp/app/services/platform.v3.server.ts (4)

646-664: LGTM - Read operation follows established patterns.

The getPrivateLinks function correctly returns undefined when the client is unavailable or on error, consistent with other read operations in this file like getCurrentPlan, getLimits, and getUsage.


666-685: LGTM - Write operation correctly throws on failure.

The createPrivateLink function appropriately throws errors when the client is unavailable or when the operation fails, allowing callers to handle failures via tryCatch. This is consistent with other mutating operations like setBillingAlert and enqueueBuild.


687-704: LGTM - Delete operation correctly throws on failure.

The deletePrivateLink function now throws on all error paths (missing client, caught error, unsuccessful result), which addresses the previous review feedback and allows proper error handling at call sites.


706-724: LGTM - Read operation follows established patterns.

The getPrivateLinkRegions function correctly returns undefined on errors, consistent with other read operations in this file.

apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx (4)

43-69: LGTM - Loader implementation is well-structured.

The loader correctly gates access via the feature flag, handles errors gracefully by returning an empty connections list, and uses the structured logger as recommended in the previous review.


71-113: LGTM - Action correctly handles delete operations.

The action properly validates the request, uses tryCatch with deletePrivateLink (which now throws on error), and provides appropriate success/error feedback via flash messages.


155-174: LGTM - Polling logic is well-implemented.

The interval-based revalidation correctly polls only when there are in-progress connections (non-terminal statuses), and respects the revalidator's idle state to prevent request piling.


220-293: LGTM - Connection list UI is clean and functional.

The connection cards display relevant information with copy buttons, status badges, and conditional delete controls. The delete confirmation prompt provides good UX safety.

@0ski 0ski force-pushed the oskar/feat-private-link branch from d8d5c89 to 73db571 Compare March 26, 2026 17:14
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: 1

🧹 Nitpick comments (1)
apps/webapp/app/v3/runEngine.server.ts (1)

167-167: Keep getCurrentPlan return shape consistent across all branches.

At Line 167, hasPrivateLink is set only in the normal path. Consider also returning hasPrivateLink: false in fallback branches so downstream consumers always receive a boolean.

Suggested patch
         if (!plan) {
           logger.warn("engine.getCurrentPlan: no plan", { orgId });
           return {
             isPaying: true,
             type: "paid", // default to paid
+            hasPrivateLink: false,
           };
         }

         // This shouldn't happen
         if (!plan.v3Subscription) {
           logger.warn("engine.getCurrentPlan: no v3 subscription", { orgId });
           return {
             isPaying: false,
             type: "free",
+            hasPrivateLink: false,
           };
         }

         // Neither should this
         if (!plan.v3Subscription.plan) {
           logger.warn("engine.getCurrentPlan: no v3 subscription plan", { orgId });
           return {
             isPaying: plan.v3Subscription.isPaying,
             type: plan.v3Subscription.isPaying ? "paid" : "free",
+            hasPrivateLink: false,
           };
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/v3/runEngine.server.ts` at line 167, The getCurrentPlan
return shape is inconsistent: hasPrivateLink is set from
plan.v3Subscription.plan.limits.hasPrivateNetworking in the normal branch but
omitted in fallback branches; update all return paths inside getCurrentPlan to
include hasPrivateLink (set to false in fallbacks) so consumers always receive a
boolean, e.g., add hasPrivateLink: false to the fallback/else return objects
that currently omit it (refer to getCurrentPlan and the
plan.v3Subscription.plan.limits.hasPrivateNetworking usage).
🤖 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/webapp/app/routes/_app.orgs`.$organizationSlug.settings.private-connections._index/route.tsx:
- Line 176: The variable hasPrivateNetworking is hardcoded to true, making the
conditional rendering block that shows the upgrade messaging (the branch guarded
by hasPrivateNetworking) unreachable; either remove the dead branch and delete
the unused hasPrivateNetworking variable, or wire hasPrivateNetworking to real
plan/organization data returned by the route loader (e.g., derive from
loaderData.organization.plan or similar) so the conditional reflects actual
entitlement before rendering the upgrade messaging in the route component.

---

Nitpick comments:
In `@apps/webapp/app/v3/runEngine.server.ts`:
- Line 167: The getCurrentPlan return shape is inconsistent: hasPrivateLink is
set from plan.v3Subscription.plan.limits.hasPrivateNetworking in the normal
branch but omitted in fallback branches; update all return paths inside
getCurrentPlan to include hasPrivateLink (set to false in fallbacks) so
consumers always receive a boolean, e.g., add hasPrivateLink: false to the
fallback/else return objects that currently omit it (refer to getCurrentPlan and
the plan.v3Subscription.plan.limits.hasPrivateNetworking usage).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 011443d9-112e-480e-a458-97e2f93ef3db

📥 Commits

Reviewing files that changed from the base of the PR and between d8d5c89 and 73db571.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • .changeset/private-networking-dequeue.md
  • .server-changes/private-networking.md
  • apps/supervisor/src/index.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/supervisor/src/workloadManager/types.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/features.server.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
  • apps/webapp/app/services/platform.v3.server.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/package.json
  • internal-packages/run-engine/src/engine/billingCache.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • packages/core/src/v3/schemas/runEngine.ts
✅ Files skipped from review due to trivial changes (9)
  • apps/supervisor/src/workloadManager/types.ts
  • apps/webapp/package.json
  • .changeset/private-networking-dequeue.md
  • .server-changes/private-networking.md
  • apps/supervisor/src/workloadManager/kubernetes.ts
  • apps/webapp/app/hooks/useFeatures.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • internal-packages/run-engine/src/engine/billingCache.ts
  • apps/webapp/app/env.server.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/supervisor/src/index.ts
  • packages/core/src/v3/schemas/runEngine.ts
  • apps/webapp/app/components/navigation/OrganizationSettingsSideMenu.tsx
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: typecheck / typecheck
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{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

**/*.{ts,tsx}: For apps and internal packages (apps/*, internal-packages/*), use pnpm run typecheck --filter <package> for verification, never use build as it proves almost nothing about correctness
Use testcontainers helpers (redisTest, postgresTest, containerTest from @internal/testcontainers) for integration tests with Redis and PostgreSQL instead of mocking
When writing Trigger.dev tasks, always import from @trigger.dev/sdk - never use @trigger.dev/sdk/v3 or deprecated client.defineJob

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
{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/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

**/*.{ts,tsx,js,jsx}: Use pnpm for package management in this monorepo (version 10.23.0) with Turborepo for orchestration - run commands from root with pnpm run
Add crumbs as you write code for debug tracing using // @Crumbs comments or `// `#region` `@crumbs blocks - they stay on the branch throughout development and are stripped via agentcrumbs strip before merge

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
apps/webapp/app/**/*.{ts,tsx}

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

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
apps/webapp/**/*.{ts,tsx}

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

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
apps/webapp/app/**/*.{ts,tsx,server.ts}

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

Access environment variables via env export from app/env.server.ts. Never use process.env directly

Files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
apps/webapp/app/v3/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

In the webapp v3 directory, only modify V2 code paths when encountering V1/V2 branching in services - all new work uses Run Engine 2.0 (@internal/run-engine) and redis-worker, not legacy V1 engine code

Files:

  • apps/webapp/app/v3/runEngine.server.ts
apps/webapp/app/services/**/*.server.{ts,tsx}

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

Separate testable services from configuration files; follow the pattern of realtimeClient.server.ts (testable service) and realtimeClientGlobal.server.ts (configuration) in the webapp

Files:

  • apps/webapp/app/services/platform.v3.server.ts
🧠 Learnings (31)
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx} : Access all environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env` in the Trigger.dev webapp

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:25.028Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.028Z
Learning: Applies to apps/webapp/app/**/*.{ts,tsx,server.ts} : Access environment variables via `env` export from `app/env.server.ts`. Never use `process.env` directly

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2026-03-23T06:24:25.028Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.028Z
Learning: Applies to apps/webapp/**/*.{test,spec}.{ts,tsx} : For testable code, never import `env.server.ts` in test files. Pass configuration as options/constructor arguments instead

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.test.{ts,tsx} : Test files should only import classes and functions from `app/**/*.ts` files and should not import `env.server.ts` directly or indirectly; pass configuration through options instead

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2026-03-02T12:42:47.652Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/supervisor/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:42:47.652Z
Learning: Applies to apps/supervisor/src/env.ts : Environment configuration should be defined in `src/env.ts`

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2026-03-23T06:24:25.029Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.029Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.ts : Only modify V2 code paths when editing services that branch on `RunEngineVersion` to support both V1 and V2 (e.g., `cancelTaskRun.server.ts`, `batchTriggerV3.server.ts`)

Applied to files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-08-14T18:35:44.370Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2390
File: apps/webapp/app/env.server.ts:764-765
Timestamp: 2025-08-14T18:35:44.370Z
Learning: The BoolEnv helper in apps/webapp/app/utils/boolEnv.ts uses z.preprocess with inconsistent default value types across the codebase - some usages pass boolean defaults (correct) while others pass string defaults (incorrect), leading to type confusion. The helper should enforce boolean-only defaults or have clearer documentation.

Applied to files:

  • apps/webapp/app/features.server.ts
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to apps/webapp/app/v3/**/*.{ts,tsx} : In the webapp v3 directory, only modify V2 code paths when encountering V1/V2 branching in services - all new work uses Run Engine 2.0 (`internal/run-engine`) and redis-worker, not legacy V1 engine code

Applied to files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp

Applied to files:

  • apps/webapp/app/features.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-03-02T12:43:17.177Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: internal-packages/database/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:17.177Z
Learning: Applies to internal-packages/database/**/{app,src,webapp}/**/*.{ts,tsx,js,jsx} : Use `$replica` from `~/db.server` for read-heavy queries in the webapp instead of the primary database connection

Applied to files:

  • apps/webapp/app/features.server.ts
📚 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/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 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/features.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-02-04T16:34:48.876Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/vercel.connect.tsx:13-27
Timestamp: 2026-02-04T16:34:48.876Z
Learning: In apps/webapp/app/routes/vercel.connect.tsx, configurationId may be absent for "dashboard" flows but must be present for "marketplace" flows. Enforce this with a Zod superRefine and pass installationId to repository methods only when configurationId is defined (omit the field otherwise).

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 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.private-connections._index/route.tsx
📚 Learning: 2026-03-13T13:45:39.411Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/routes/admin.llm-models.missing.$model.tsx:19-21
Timestamp: 2026-03-13T13:45:39.411Z
Learning: In `apps/webapp/app/routes/admin.llm-models.missing.$model.tsx`, the `decodeURIComponent(params.model ?? "")` call is intentionally unguarded. Remix route params are decoded at the routing layer before reaching the loader, so malformed percent-encoding is rejected upstream. The page is also admin-only, so the risk is minimal and no try-catch is warranted.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-26T10:02:22.373Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 3254
File: apps/webapp/app/services/platformNotifications.server.ts:363-385
Timestamp: 2026-03-26T10:02:22.373Z
Learning: In `triggerdotdev/trigger.dev`, the `getNextCliNotification` fallback in `apps/webapp/app/services/platformNotifications.server.ts` intentionally uses `prisma.orgMember.findFirst` (single org) when no `projectRef` is provided. This is acceptable for v1 because the CLI (`dev` and `login` commands) always passes `projectRef` in normal usage, making the fallback a rare edge case. Do not flag the single-org fallback as a multi-org correctness bug in this file.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-03-25T15:29:25.853Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2026-03-25T15:29:25.853Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `logger.debug()`, `logger.log()`, `logger.info()`, `logger.warn()`, and `logger.error()` for logging with key-value data

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-02-19T18:09:23.944Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3095
File: apps/webapp/app/components/navigation/DashboardDialogs.tsx:47-47
Timestamp: 2026-02-19T18:09:23.944Z
Learning: In the triggerdotdev/trigger.dev codebase, the pattern `isPaying === false` is used consistently to explicitly check for free plan users. This is a project convention that distinguishes between `isPaying === true` (paying), `isPaying === false` (free), and `isPaying === undefined` (no subscription data). Do not suggest changing this to negation pattern.
```
<!-- <review_comment_addressed>

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
  • apps/webapp/app/v3/runEngine.server.ts
📚 Learning: 2026-03-22T13:45:36.346Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/navigation/SideMenu.tsx:460-489
Timestamp: 2026-03-22T13:45:36.346Z
Learning: In triggerdotdev/trigger.dev, sidebar navigation items (SideMenu.tsx) are intentionally NOT gated behind feature-flag or permission checks at the nav level. Authorization is enforced at the route/loader level instead. Hiding nav items based on access checks is considered confusing UX. This applies to items like "AI Metrics" (v3BuiltInDashboardPath) and other dashboard links — they are always rendered in the sidebar regardless of hasQueryAccess or similar flags.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-02T12:43:37.906Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/core/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:37.906Z
Learning: Applies to packages/core/**/*.{ts,tsx,js,jsx} : Never import the root package (trigger.dev/core). Always use subpath imports such as trigger.dev/core/v3, trigger.dev/core/v3/utils, trigger.dev/core/logger, or trigger.dev/core/schemas

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to packages/core/**/*.{ts,tsx} : In the core package (`trigger.dev/core`), import subpaths only - never import from root

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-02T12:43:48.124Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/trigger-sdk/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:48.124Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx,js,jsx} : Always import from `trigger.dev/sdk`. Never use `trigger.dev/sdk/v3` (deprecated path alias)

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-25T15:29:25.853Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2026-03-25T15:29:25.853Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Import from `trigger.dev/sdk` (NEVER from `trigger.dev/sdk/v3`)

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to **/*.{ts,tsx} : When writing Trigger.dev tasks, always import from `trigger.dev/sdk` - never use `trigger.dev/sdk/v3` or deprecated `client.defineJob`

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx
📚 Learning: 2026-03-02T12:43:02.539Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: docs/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:02.539Z
Learning: Applies to docs/**/*.mdx : Always import from `trigger.dev/sdk` in code examples (never from `trigger.dev/sdk/v3`)

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/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.private-connections._index/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
📚 Learning: 2026-03-26T09:02:07.973Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 3274
File: apps/webapp/app/services/runsReplicationService.server.ts:922-924
Timestamp: 2026-03-26T09:02:07.973Z
Learning: When parsing Trigger.dev task run annotations in server-side services, keep `TaskRun.annotations` strictly conforming to the `RunAnnotations` schema from `trigger.dev/core/v3`. If the code already uses `RunAnnotations.safeParse` (e.g., in a `#parseAnnotations` helper), treat that as intentional/necessary for atomic, schema-accurate annotation handling. Do not recommend relaxing the annotation payload schema or using a permissive “passthrough” parse path, since the annotations are expected to be written atomically in one operation and should not contain partial/legacy payloads that would require a looser parser.

Applied to files:

  • apps/webapp/app/services/platform.v3.server.ts
🔇 Additional comments (5)
apps/webapp/app/features.server.ts (1)

1-1: LGTM!

The hasPrivateConnections function correctly uses the env export from env.server.ts instead of process.env directly, following the project's coding guidelines. The implementation correctly gates private connections behind both the feature flag and managed cloud check.

Also applies to: 18-23

apps/webapp/app/services/platform.v3.server.ts (1)

646-724: LGTM!

The new private link service functions follow established patterns in this file:

  • Read operations (getPrivateLinks, getPrivateLinkRegions) return undefined on failure, consistent with other read functions like getPlans and getUsage.
  • Write operations (createPrivateLink, deletePrivateLink) throw on failure, consistent with setBillingAlert and enqueueBuild.
  • Proper error logging with structured context (organizationId, connectionId).
  • Consistent use of tryCatch pattern with appropriate error handling.
apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections._index/route.tsx (3)

43-69: LGTM!

The loader and action implementations are well-structured:

  • Proper authentication via requireUserId
  • Feature gating with redirect for unauthorized access
  • Organization membership validation
  • Appropriate error handling with tryCatch and structured logging
  • Correct method validation (DELETE/POST) and form data validation in action
  • Good UX with success/error flash messages

Also applies to: 71-113


153-178: LGTM!

The polling logic is well-implemented:

  • 3-second interval is reasonable for status updates
  • Correctly identifies non-terminal statuses (PENDING, PROVISIONING, DELETING) for polling
  • Disables polling when all connections are in terminal states (ACTIVE, ERROR)
  • Connection limit check correctly excludes DELETING connections from the count

4-4: No changes needed—the tryCatch import is correctly using the subpath export.

The import @trigger.dev/core/utils is a valid subpath export (verified in package.json exports) and complies with the coding guideline requiring subpath exports in the webapp. Both @trigger.dev/core/utils and @trigger.dev/core/v3 export tryCatch, but they are different implementations—the root utils version is appropriate for general webapp use, while the v3 version is for SDK v3 flows. The current import is correct.

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