feat(webapp) backend changes and backfill for dev branches#4010
feat(webapp) backend changes and backfill for dev branches#4010carderne wants to merge 2 commits into
Conversation
…ents Dev environments are now branchable. Backfill all existing non-archived DEVELOPMENT runtime environments so isBranchableEnvironment is true. TRI-8726
Extend branch support to DEVELOPMENT environments alongside PREVIEW.
- UpsertBranchRequestBody / branches API accept env "development" as well
as "preview"; the upsert service resolves the parent env by slug
("preview" or "dev") and scopes dev branches per org member.
- checkBranchLimit applies a separate "branchesDev" limit and filters dev
branches by the owning org member.
- API-key and JWT auth resolve branch child environments for both PREVIEW
and DEVELOPMENT parents; findEnvironmentByApiKey returns the dev branch
child when a non-default branch is requested.
- archiveBranch refuses to archive the default dev branch and reports the
branch type so callers can route appropriately.
- Presenters and presence are env/branch aware.
Backwards compatible with the existing CLI: requests that send
env "preview" (or no dev branch) behave exactly as before.
TRI-8726
🦋 Changeset detectedLatest commit: 5b6142d The changes in this PR will be included in the next version bump. This PR includes changesets to release 27 packages
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 |
WalkthroughThis PR extends the branch system to support DEVELOPMENT environments in addition to PREVIEW environments. A 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
apps/webapp/app/services/upsertBranch.server.ts (1)
84-93: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueInvariant message may confuse debugging.
The invariant
invariant(env === "preview", "No default dev runtime environment setup")throws whenparentEnvironmentis null andenv !== "preview". The message indicates a dev setup issue, but the invariant assertsenv === "preview". Consider clarifying or inverting the logic for clearer error attribution:- invariant(env === "preview", "No default dev runtime environment setup"); + if (env !== "preview") { + throw new Error("No default dev runtime environment setup"); + }Or keep the invariant but clarify the message reflects what's expected vs. what failed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ec387772-9be8-44a6-b224-f4a80f5e02c2
📒 Files selected for processing (25)
.changeset/dev-branch-default-sentinel.mdapps/webapp/app/models/member.server.tsapps/webapp/app/models/project.server.tsapps/webapp/app/models/runtimeEnvironment.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/services/apiAuth.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsinternal-packages/database/prisma/migrations/20260618090554_backfill_branchable_dev_env/migration.sqlinternal-packages/rbac/src/fallback.tspackages/core/src/v3/apiClientManager/index.tspackages/core/src/v3/schemas/api.tspackages/core/src/v3/utils/gitBranch.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (39)
- GitHub Check: internal / 🧪 Unit Tests: Internal (9, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (12, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (7, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (8, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (6, 12)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 10)
- GitHub Check: internal / 🧪 Unit Tests: Internal (5, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (10, 12)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 10)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 10)
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: internal / 🧪 Unit Tests: Internal (11, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (3, 12)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 10)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 10)
- GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
- GitHub Check: internal / 🧪 Unit Tests: Internal (1, 12)
- GitHub Check: internal / 🧪 Unit Tests: Internal (2, 12)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 10)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 10)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 10)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 10)
- GitHub Check: internal / 🧪 Unit Tests: Internal (4, 12)
- GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 10)
- 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 / Bun Runtime
- GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
- GitHub Check: typecheck / typecheck
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp
- GitHub Check: 🛡️ E2E Auth Tests (full)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Build and publish previews
🧰 Additional context used
📓 Path-based instructions (9)
internal-packages/database/**/prisma/migrations/*/*.sql
📄 CodeRabbit inference engine (internal-packages/database/CLAUDE.md)
internal-packages/database/**/prisma/migrations/*/*.sql: Clean up generated Prisma migrations by removing extraneous lines for junction tables (_BackgroundWorkerToBackgroundWorkerFile,_BackgroundWorkerToTaskQueue,_TaskRunToTaskRunTag,_WaitpointRunConnections,_completedWaitpoints) and indexes (SecretStore_key_idx, variousTaskRunindexes) unless explicitly added
When adding indexes to existing tables, useCREATE INDEX CONCURRENTLY IF NOT EXISTSto avoid table locks in production, and place each concurrent index in its own separate migration file
Indexes on newly created tables can useCREATE INDEXwithout CONCURRENTLY and can be combined in the same migration file as theCREATE TABLEstatement
When adding an index on a new column in an existing table, use two separate migrations: first forALTER TABLE ... ADD COLUMN IF NOT EXISTS ..., then forCREATE INDEX CONCURRENTLY IF NOT EXISTS ...in its own file
Files:
internal-packages/database/prisma/migrations/20260618090554_backfill_branchable_dev_env/migration.sql
**/*.{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 insteadImport from
@trigger.dev/sdkwhen writing Trigger.dev tasks. Never use@trigger.dev/sdk/v3or deprecatedclient.defineJob
Files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.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/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
**/*.{ts,tsx,js,jsx}: Prefer static imports over dynamic imports. Only use dynamicimport()when circular dependencies cannot be resolved, code splitting is needed for performance, or the module must be loaded conditionally at runtime
Import subpaths only frompackages/core(@trigger.dev/core), never import from the root
Files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.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/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: Access environment variables through theenvexport ofenv.server.tsinstead of directly accessingprocess.env
Use subpath exports from@trigger.dev/corepackage instead of importing from the root@trigger.dev/corepathUse named constants for sentinel/placeholder values (e.g.
const UNSET_VALUE = '__unset__') instead of raw string literals scattered across comparisons
Files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
apps/webapp/**/*.server.ts
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
apps/webapp/**/*.server.ts: Never userequest.signalfor detecting client disconnects. UsegetRequestAbortSignal()fromapp/services/httpAsyncStorage.server.tsinstead, which is wired directly to Expressres.on('close')and fires reliably
Access environment variables viaenvexport fromapp/env.server.ts. Never useprocess.envdirectly
Always usefindFirstinstead offindUniquein Prisma queries.findUniquehas an implicit DataLoader that batches concurrent calls and has active bugs even in Prisma 6.x (uppercase UUIDs returning null, composite key SQL correctness issues, 5-10x worse performance).findFirstis never batched and avoids this entire class of issues
Files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
**/*.{js,ts,tsx,jsx,css,json,md}
📄 CodeRabbit inference engine (AGENTS.md)
Use Prettier for code formatting and run
pnpm run formatbefore committing
Files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
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/api.tspackages/core/src/v3/utils/gitBranch.tspackages/core/src/v3/apiClientManager/index.ts
🧠 Learnings (21)
📚 Learning: 2026-02-03T18:48:31.790Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: internal-packages/database/prisma/migrations/20260129162810_add_integration_deployment/migration.sql:14-18
Timestamp: 2026-02-03T18:48:31.790Z
Learning: For Prisma migrations targeting PostgreSQL: - When adding indexes to existing tables, create the index in a separate migration file and include CONCURRENTLY to avoid locking the table. - For indexes on newly created tables (in CREATE TABLE statements), you can create the index in the same migration file without CONCURRENTLY. This reduces rollout complexity for new objects while protecting uptime for existing structures.
Applied to files:
internal-packages/database/prisma/migrations/20260618090554_backfill_branchable_dev_env/migration.sql
📚 Learning: 2026-03-22T13:49:20.068Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: internal-packages/database/prisma/migrations/20260318114244_add_prompt_friendly_id/migration.sql:5-5
Timestamp: 2026-03-22T13:49:20.068Z
Learning: For Prisma migration SQL files under `internal-packages/database/prisma/migrations/`, it is acceptable to create indexes with `CREATE INDEX` / `CREATE UNIQUE INDEX` (i.e., without `CONCURRENTLY`) when the parent table is introduced in the same PR and has no existing production rows yet. Only require `CREATE INDEX CONCURRENTLY` (or otherwise account for existing production data/locks) when the table already exists in production with data.
Applied to files:
internal-packages/database/prisma/migrations/20260618090554_backfill_branchable_dev_env/migration.sql
📚 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/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.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/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-05-18T08:21:27.694Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3632
File: apps/webapp/sentry.server.ts:4-21
Timestamp: 2026-05-18T08:21:27.694Z
Learning: When handling Prisma error P1001 ("Can't reach database server") in TypeScript, don’t assume a single error shape. Prisma can surface P1001 via two different error classes/fields: `PrismaClientKnownRequestError` exposes it as `err.code === "P1001"` (common during mid-query connection drops), while `PrismaClientInitializationError` exposes it as `err.errorCode === "P1001"` (common on client startup failure). Therefore, predicates should use `err.code === "P1001" || err.errorCode === "P1001"`. Do not flag `err.code === "P1001"` as “unreachable/never matches,” as it is expected in production.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-05-18T08:21:27.694Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3632
File: apps/webapp/sentry.server.ts:4-21
Timestamp: 2026-05-18T08:21:27.694Z
Learning: When handling Prisma errors for P1001 ("Can't reach database server"), do not assume it only appears under a single property name. Prisma may surface P1001 via either `PrismaClientKnownRequestError` (`err.code === "P1001"`, e.g., mid-query connection drops) or `PrismaClientInitializationError` (`err.errorCode === "P1001"`, e.g., client startup connection failure). To reliably detect the condition, check `err.code === "P1001" || err.errorCode === "P1001"`, and avoid review rules that would incorrectly flag `err.code === "P1001"` as unreachable/never-matching.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-06-13T19:53:13.759Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3937
File: packages/trigger-sdk/skills/realtime-and-frontend/SKILL.md:258-260
Timestamp: 2026-06-13T19:53:13.759Z
Learning: When reviewing code that uses `trigger.dev/react-hooks`’s `useRealtimeRun`, preserve the call signature where the first argument is the full realtime handle object (not `handle.id`). This is intentional to maintain type-safety and is consistent with the official docs; do not suggest changing the first argument from the handle object to `handle.id`.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-06-17T17:13:49.929Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3948
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.bulk-actions.$bulkActionParam/route.tsx:48-62
Timestamp: 2026-06-17T17:13:49.929Z
Learning: In triggerdotdev/trigger.dev, within `dashboardLoader`/`dashboardAction` (or similar context resolver code) whenever you resolve an organization ID from an organization slug for RBAC/enterprise authorization scope, always read from the primary Prisma client (`prisma`), not `$replica`. Using `$replica` can hit replica-lag and cause the RBAC lookup/authorization to run without the correct org scope (bypassing intended role enforcement). Implement the slug→org lookup with `prisma.organization.findFirst(...)` (or equivalent primary-client query) and add an inline comment documenting why the primary client is required (replica lag could lead to unscoped RBAC checks).
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-05-05T09:38:02.512Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3523
File: apps/webapp/app/routes/api.v3.batches.ts:178-181
Timestamp: 2026-05-05T09:38:02.512Z
Learning: When reviewing code that catches `ServiceValidationError` in `*.server.ts` files, do not blindly forward `error.status` to HTTP responses, because SVEs may be thrown with non-default statuses (e.g., 400/500) and forwarding them can cause client-visible behavioral regressions (e.g., surfacing 500s to clients). Prefer a safe default response status of `error.status ?? 422`, but only after confirming via the reachable call graph that the caught `ServiceValidationError` instances are expected to carry those non-default statuses; otherwise, normalize to `422` to avoid unexpected client-visible 5xx behavior.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-05-12T21:04:05.815Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3542
File: apps/webapp/app/components/sessions/v1/SessionStatus.tsx:1-3
Timestamp: 2026-05-12T21:04:05.815Z
Learning: In this Remix + TypeScript codebase, do not flag a server/client boundary violation when a file imports only types from a module matching `*.server`.
Specifically, it’s safe to import types using `import type { Foo } from "*.server"` or `import { type Foo } from "*.server"` because TypeScript erases type-only imports at compile time and they emit no JavaScript, so they won’t cross the Remix server/client bundle boundary.
Only raise the boundary concern for value imports (e.g., `import { Foo }` without `type`, or `import Foo`), since those produce JavaScript output.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-06-04T18:16:35.386Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3836
File: apps/supervisor/src/backpressure/backpressureMonitor.ts:3-5
Timestamp: 2026-06-04T18:16:35.386Z
Learning: When reviewing TypeScript in this repo, apply the rule “prefer type aliases over interfaces” only to data/object shapes and union/intersection type modeling. If an interface is being used as a behavioral contract for collaborators to implement (e.g., method-shape interfaces that define required behavior, such as `BackpressureLogger` / `BackpressureSignalSource` in `apps/supervisor/src/backpressure/backpressureMonitor.ts`), keep it as an `interface` and do not flag it as a type-alias-vs-interface violation.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-06-09T17:58:04.699Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 3879
File: apps/webapp/app/models/vercelIntegration.server.ts:619-630
Timestamp: 2026-06-09T17:58:04.699Z
Learning: In this codebase, outbound raw `fetch` calls should typically rely on Node/undici’s default request timeout (about ~300s) rather than adding a per-call `AbortController` + `setTimeout` wrapper inside individual functions (e.g. in files like `apps/webapp/app/models/vercelIntegration.server.ts`). During code review, do not flag the absence of a per-call timeout on a single `fetch` as an issue; if per-call timeouts are needed, they should be implemented via a codebase-wide convention (e.g., a shared fetch wrapper or documented pattern) rather than ad-hoc per-function changes.
Applied to files:
apps/webapp/app/models/project.server.tsapps/webapp/app/models/member.server.tsapps/webapp/app/services/archiveBranch.server.tsapps/webapp/app/routes/resources.taskruns.$runParam.replay.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tspackages/core/src/v3/schemas/api.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.tsapps/webapp/app/routes/engine.v1.dev.presence.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.tsapps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tspackages/core/src/v3/utils/gitBranch.tsapps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/models/runtimeEnvironment.server.tspackages/core/src/v3/apiClientManager/index.tsapps/webapp/app/routes/api.v1.projects.$projectRef.branches.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsinternal-packages/rbac/src/fallback.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.tsapps/webapp/app/services/apiAuth.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/archiveBranch.server.tsapps/webapp/app/services/upsertBranch.server.tsapps/webapp/app/services/apiAuth.server.ts
📚 Learning: 2026-06-09T16:27:28.750Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 3878
File: apps/webapp/app/routes/api.v1.workers.ts:0-0
Timestamp: 2026-06-09T16:27:28.750Z
Learning: When computing the `isDefault` field for worker groups in API responses (e.g., `api.v1.*workers*.ts`), always reuse `WorkerGroupService.getDefaultWorkerGroupForProject` (the same resolver used by the trigger/run-routing path) instead of resolving feature flags or defaults inline. This keeps the API’s `isDefault` value exactly aligned with the routing behavior.
If you need org-level override behavior for the default region/instance group, centralize that override logic in `getGlobalDefaultWorkerGroup` so all code paths (API presenter/resolver and routing) stay consistent. Avoid introducing org override handling only in `getGlobalDefaultWorkerGroup` call sites like `RegionsPresenter` unless it’s part of the centralized logic.
Applied to files:
apps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.ts
📚 Learning: 2026-02-06T19:53:38.843Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts:233-237
Timestamp: 2026-02-06T19:53:38.843Z
Learning: When constructing Vercel dashboard URLs from deployment IDs, always strip the dpl_ prefix from the ID. Implement this by transforming the ID with .replace(/^dpl_/, "") before concatenating into the URL: https://vercel.com/${teamSlug}/${projectName}/${cleanedDeploymentId}. Consider centralizing this logic in a small helper (e.g., getVercelDeploymentId(id) or a URL builder) and add tests to verify both prefixed and non-prefixed inputs.
Applied to files:
apps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.ts
📚 Learning: 2026-06-21T05:35:23.468Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4005
File: apps/webapp/app/presenters/v3/ApiErrorListPresenter.server.ts:29-30
Timestamp: 2026-06-21T05:35:23.468Z
Learning: For triggerdotdev/trigger.dev list endpoints (and their presenters/handlers that implement list pagination), it is an established shared convention to allow both cursor query params `page[after]` and `page[before]` to be provided at the same time. When both are present, `page[before]` must take precedence (i.e., it should be used/wins). During code review, do NOT flag missing per-endpoint mutual-exclusion validation between `page[after]` and `page[before]` as a problem; if stricter enforcement is ever desired, it should be implemented as a codebase-wide shared convention (not individually per endpoint).
Applied to files:
apps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.tsapps/webapp/app/presenters/v3/EditSchedulePresenter.server.tsapps/webapp/app/presenters/OrganizationsPresenter.server.tsapps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.tsapps/webapp/app/presenters/v3/DevPresence.server.tsapps/webapp/app/presenters/v3/BranchesPresenter.server.ts
📚 Learning: 2026-03-29T19:16:28.864Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3291
File: apps/webapp/app/v3/featureFlags.ts:53-65
Timestamp: 2026-03-29T19:16:28.864Z
Learning: When reviewing TypeScript code that uses Zod v3, treat `z.coerce.*()` schemas as their direct Zod type (e.g., `z.coerce.boolean()` returns a `ZodBoolean` with `_def.typeName === "ZodBoolean"`) rather than a `ZodEffects`. Only `.preprocess()`, `.refine()`/`.superRefine()`, and `.transform()` are expected to wrap schemas in `ZodEffects`. Therefore, in reviewers’ logic like `getFlagControlType`, do not flag/unblock failures that require unwrapping `ZodEffects` when the input schema is a `z.coerce.*` schema.
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts
📚 Learning: 2026-06-09T16:27:26.195Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 3878
File: apps/webapp/app/v3/services/computeTemplateCreation.server.ts:0-0
Timestamp: 2026-06-09T16:27:26.195Z
Learning: When working in triggerdotdev/trigger.dev code related to worker-group/region default resolution (e.g., defaultWorkerInstanceGroupId handling used by getGlobalDefaultWorkerGroup, getDefaultWorkerGroupForProject, and RegionsPresenter), do NOT add org-level featureFlags overrides in only one resolution site. That can cause template creation routing/decisions to diverge from actual run routing. If org-level override of the default region/worker group is required, it must be centralized in getGlobalDefaultWorkerGroup so every resolution path remains aligned.
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts
📚 Learning: 2026-05-14T08:21:07.614Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3614
File: apps/webapp/app/v3/mollifier/mollifierGate.server.ts:48-52
Timestamp: 2026-05-14T08:21:07.614Z
Learning: When using Trigger.dev v3 feature flags in the webapp, prefer the existing per-org gating mechanism supported by `flag()` via the `overrides` argument. Pass `Organization.featureFlags` (from `environment.organization.featureFlags`) as the `overrides` value; overrides must take precedence over the global `featureFlag` row. Do not require schema changes or add an `orgId` field to `FlagsOptions` for per-org gating—use the overrides pattern consistently (e.g., in gate flows like `resolveOrgFlag` and any server code that threads `environment.organization.featureFlags` into the gate call).
Applied to files:
apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts
📚 Learning: 2026-05-01T15:45:05.096Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3499
File: internal-packages/rbac/src/fallback.ts:34-107
Timestamp: 2026-05-01T15:45:05.096Z
Learning: When reviewing triggerdotdev/trigger.dev RBAC auth code, do not treat missing Personal Access Token (PAT) handling inside `authenticateBearer` as a bug. `authenticateBearer` is intentionally scoped to runtime environment API keys and Public JWTs only; PAT auth is handled via the separate PAT route builder (e.g., `createLoaderPATApiRoute`) which calls `authenticateApiRequestWithPersonalAccessToken` directly. Ensure that reviewers compare auth behavior against these distinct architectural paths (OSS fallback and cloud plugin) before flagging an issue.
Applied to files:
internal-packages/rbac/src/fallback.ts
📚 Learning: 2026-05-09T08:07:24.612Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3499
File: internal-packages/rbac/src/fallback.ts:271-277
Timestamp: 2026-05-09T08:07:24.612Z
Learning: When reviewing RBAC/auth code that looks up or validates `PersonalAccessToken` (PAT), do not flag missing `expiresAt`/expiration checks: the PAT model has no `expiresAt` column and is treated as perpetual until manually revoked via `revokedAt`. Only require/enforce expiration logic when the code is dealing with `OrganizationAccessToken`, which does have an `expiresAt` field (and should be checked accordingly).
Applied to files:
internal-packages/rbac/src/fallback.ts
🔇 Additional comments (37)
apps/webapp/app/presenters/v3/DevPresence.server.ts (4)
4-8: LGTM!
23-39: LGTM!
41-51: LGTM!
53-62: LGTM!apps/webapp/app/models/runtimeEnvironment.server.ts (3)
7-7: LGTM!
103-107: LGTM!
166-184: LGTM!apps/webapp/app/routes/engine.v1.dev.presence.ts (2)
2-2: LGTM!
16-26: LGTM!Also applies to: 37-41
apps/webapp/app/presenters/v3/EditSchedulePresenter.server.ts (2)
54-54: LGTM!
91-98: LGTM!apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts (1)
1117-1128: LGTM!apps/webapp/app/presenters/v3/BranchesPresenter.server.ts (1)
1-12: LGTM!Also applies to: 65-72, 95-104
apps/webapp/app/presenters/OrganizationsPresenter.server.ts (1)
250-253: LGTM!apps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.ts (1)
4-4: LGTM!Also applies to: 143-143, 156-157
packages/core/src/v3/utils/gitBranch.ts (1)
1-20: LGTM!packages/core/src/v3/schemas/api.ts (1)
608-608: LGTM!packages/core/src/v3/apiClientManager/index.ts (1)
19-19: LGTM!Also applies to: 59-61, 70-72, 89-90
.changeset/dev-branch-default-sentinel.md (1)
1-7: LGTM!apps/webapp/app/models/project.server.ts (1)
124-131: LGTM!apps/webapp/app/models/member.server.ts (1)
213-221: LGTM!apps/webapp/app/presenters/v3/environmentVariablesEnvironments.server.ts (1)
5-11: LGTM!Also applies to: 45-52, 69-75
apps/webapp/app/services/apiAuth.server.ts (4)
28-28: LGTM!Also applies to: 285-294
323-342: LGTM!Also applies to: 355-363
465-470: LGTM!Also applies to: 519-531, 542-562
592-592: LGTM!Also applies to: 608-618
internal-packages/rbac/src/fallback.ts (2)
149-149: LGTM!
199-234: LGTM!apps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.ts (1)
8-8: LGTM!Also applies to: 73-73
apps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.ts (1)
11-11: LGTM!Also applies to: 42-42
apps/webapp/app/services/upsertBranch.server.ts (4)
8-13: LGTM!Also applies to: 30-35
54-65: LGTM!
98-105: LGTM!Also applies to: 147-149
176-206: Verify thataddOns.branches.purchasedintentionally applies to both preview and dev branch limits.The code applies the same
addOns?.branches?.purchasedto both preview and dev environments (line 208), but uses different limit names ("branches"vs"branchesDev") for the base limits (line 205). No separateaddOns?.branchesDev?.purchasedproperty exists in the codebase. Confirm whether this unified add-on approach is intentional or if dev branches require a separate add-on purchase path.apps/webapp/app/services/archiveBranch.server.ts (1)
59-70: LGTM!apps/webapp/app/routes/api.v1.projects.$projectRef.branches.ts (1)
52-57: LGTM!Also applies to: 74-101
apps/webapp/app/routes/api.v1.projects.$projectRef.branches.archive.ts (1)
14-15: LGTM!Also applies to: 54-56
| import { devPresence } from "./v3/DevPresence.server"; | ||
| import { hydrateEnvsWithActivity } from "./v3/BranchesPresenter.server"; |
There was a problem hiding this comment.
Remove the duplicate getRecentBranchIds read.
Line 107 performs a Redis read that is never used, and Line 112 immediately calls hydrateEnvsWithActivity() which already performs the same read internally. This adds unnecessary network I/O on every request.
💡 Suggested fix
-import { devPresence } from "./v3/DevPresence.server";
import { hydrateEnvsWithActivity } from "./v3/BranchesPresenter.server";
@@
- const recentDevBranchIds = await devPresence.getRecentBranchIds(user.id, fullProject.id);
-
const environments = fullProject.
environments.filter((env) => env.type !== "DEVELOPMENT" || env.orgMember?.userId === user.id);Also applies to: 107-113
| // The default DEV branch has no branchName (it's the root dev env, stored | ||
| // with branchName: null), so searching for it by name wouldn't display it. | ||
| // Hacky way around that: always include the null-branchName root env. | ||
| const branchNameWhere = envType === "DEVELOPMENT" ? | ||
| search | ||
| ? { OR: [{ contains: search, mode: "insensitive" as const }, { is: null }] } | ||
| : {} : | ||
| search | ||
| ? { contains: search, mode: "insensitive" as const } | ||
| : { not: null }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/webapp/app/presenters/v3/BranchesPresenter.server.ts | sed -n '130,190p'Repository: triggerdotdev/trigger.dev
Length of output: 2676
🏁 Script executed:
find . -name "*.prisma" -type f | head -20Repository: triggerdotdev/trigger.dev
Length of output: 119
🏁 Script executed:
grep -A 50 "model RuntimeEnvironment" ./internal-packages/database/prisma/schema.prisma | head -80Repository: triggerdotdev/trigger.dev
Length of output: 2821
🏁 Script executed:
head -50 apps/webapp/app/presenters/v3/BranchesPresenter.server.tsRepository: triggerdotdev/trigger.dev
Length of output: 1999
🏁 Script executed:
rg "Prisma.*WhereInput" apps/webapp/app/presenters/v3/BranchesPresenter.server.ts -A 3 -B 3Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
cat -n apps/webapp/app/presenters/v3/BranchesPresenter.server.ts | sed -n '1,30p'Repository: triggerdotdev/trigger.dev
Length of output: 1585
🏁 Script executed:
web_search
Prisma scalar nullable field OR filter syntax StringNullableFilterRepository: triggerdotdev/trigger.dev
Length of output: 165
🌐 Web query:
Prisma StringNullableFilter OR operator scalar field syntax
💡 Result:
In Prisma, there is no specialized "StringNullableFilter OR" operator. Instead, you use the standard, composable OR operator at the level of your where clause, allowing you to combine any valid filter conditions, including those for nullable scalar fields [1][2][3]. To filter on a nullable string field within an OR condition, you simply place your conditions inside an array assigned to the OR key [1][4]. Example Syntax: const users = await prisma.user.findMany({ where: { OR: [ { name: { equals: "Alice" } }, { bio: { equals: null } }, // Targeting a StringNullableFilter { bio: { contains: "developer" } } ], }, }); Key Considerations: 1. Composition: The OR operator accepts an array of filter objects [1][3]. Each object in the array represents a condition, and at least one must be true for the record to be returned [3]. 2. Nullable Fields: When a field is nullable (StringNullableFilter), you can explicitly filter for null or use string operators like contains, startsWith, or endsWith, which are available on the StringNullableFilter type [5]. 3. Handling Undefined: Be cautious when building dynamic queries. If you pass undefined to a filter, Prisma ignores that specific filter [6]. If all elements of an OR array are omitted due to being undefined, it can lead to unexpected query behavior, such as generating an empty result or a 1=0 SQL clause [7][6]. 4. Nesting: The OR, AND, and NOT operators are fully nestable, allowing you to create complex boolean logic as needed [2][3]. For nullable fields, you can check for null values by setting the field to null directly or using the { equals: null } syntax [8].
Citations:
- 1: https://www.prisma.io/docs/orm/prisma-client/queries/filtering-and-sorting
- 2: https://www.prisma.io/docs/orm/v6/prisma-client/queries/filtering-and-sorting
- 3: https://sanjewa.com/blogs/prisma-filtering-sorting-pagination-mysql-typescript-2027/
- 4: https://www.prisma.io/docs/orm/v6/prisma-client/queries/crud
- 5: Does `?` type modifier generates ambiguous types? MichalLytek/typegraphql-prisma#264
- 6: https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/null-and-undefined
- 7: "OR" filter generates "AND 1=0" when fields are undefined prisma/prisma#25534
- 8: https://stackoverflow.com/questions/75010221/what-is-the-correct-way-to-query-a-column-with-is-not-null-with-prisma
🏁 Script executed:
cat -n apps/webapp/app/presenters/v3/BranchesPresenter.server.ts | sed -n '130,195p'Repository: triggerdotdev/trigger.dev
Length of output: 2892
🏁 Script executed:
cat -n apps/webapp/app/presenters/v3/BranchesPresenter.server.ts | sed -n '195,210p'Repository: triggerdotdev/trigger.dev
Length of output: 619
🏁 Script executed:
cat > /tmp/test_prisma_types.ts << 'EOF'
// Testing Prisma filter types
import { type Prisma } from "`@prisma/client`";
// What the current code is trying to do:
const invalidWhere: Prisma.RuntimeEnvironmentWhereInput = {
branchName: {
OR: [{ contains: "test", mode: "insensitive" as const }, { is: null }]
}
};
// What the correct approach should be:
const correctWhere: Prisma.RuntimeEnvironmentWhereInput = {
OR: [
{ branchName: { contains: "test", mode: "insensitive" } },
{ branchName: null }
]
};
// Or using spread approach from the suggested fix:
const correctWhereSpread: Prisma.RuntimeEnvironmentWhereInput = {
branchName: { contains: "test", mode: "insensitive" }
};
export { invalidWhere, correctWhere, correctWhereSpread };
EOF
cat /tmp/test_prisma_types.tsRepository: triggerdotdev/trigger.dev
Length of output: 789
Fix Prisma filter composition for branchName scalar field.
Lines 135-141 build an invalid filter structure. The OR operator belongs at the RuntimeEnvironmentWhereInput level, not nested under the branchName scalar field. When this filter is passed as branchName: branchNameWhere at lines 149 and 182, it violates Prisma's StringNullableFilter type contract, which does not accept an OR property.
💡 Suggested fix
- const branchNameWhere = envType === "DEVELOPMENT" ?
- search
- ? { OR: [{ contains: search, mode: "insensitive" as const }, { is: null }] }
- : {} :
- search
- ? { contains: search, mode: "insensitive" as const }
- : { not: null };
+ const whereForBranchSearch: Prisma.RuntimeEnvironmentWhereInput =
+ envType === "DEVELOPMENT"
+ ? search
+ ? {
+ OR: [
+ { branchName: { contains: search, mode: "insensitive" } },
+ { branchName: null },
+ ],
+ }
+ : {}
+ : search
+ ? { branchName: { contains: search, mode: "insensitive" } }
+ : { branchName: { not: null } };
@@
- branchName: branchNameWhere,
+ ...whereForBranchSearch,
@@
- branchName: branchNameWhere,
+ ...whereForBranchSearch,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // The default DEV branch has no branchName (it's the root dev env, stored | |
| // with branchName: null), so searching for it by name wouldn't display it. | |
| // Hacky way around that: always include the null-branchName root env. | |
| const branchNameWhere = envType === "DEVELOPMENT" ? | |
| search | |
| ? { OR: [{ contains: search, mode: "insensitive" as const }, { is: null }] } | |
| : {} : | |
| search | |
| ? { contains: search, mode: "insensitive" as const } | |
| : { not: null }; | |
| const whereForBranchSearch: Prisma.RuntimeEnvironmentWhereInput = | |
| envType === "DEVELOPMENT" | |
| ? search | |
| ? { | |
| OR: [ | |
| { branchName: { contains: search, mode: "insensitive" } }, | |
| { branchName: null }, | |
| ], | |
| } | |
| : {} | |
| : search | |
| ? { branchName: { contains: search, mode: "insensitive" } } | |
| : { branchName: { not: null } }; |
| members: { | ||
| some: { | ||
| userId: authenticationResult.result.userId, | ||
| }, | ||
| }, | ||
| }, | ||
| project: { | ||
| externalRef: projectRef, | ||
| }, | ||
| branchName: parsed.data.branch, | ||
| type: environmentType, | ||
| branchName: branch, | ||
| }, |
There was a problem hiding this comment.
Dev branch archival may need orgMember filtering for proper user scoping.
For DEVELOPMENT branches, the query filters only by type and branchName without constraining by orgMemberId. Since dev branches are user-scoped, User A could potentially archive User B's dev branch if they share the same project and branch name. Consider adding orgMember filtering when environmentType === "DEVELOPMENT" and the auth is a PAT:
where: {
organization: ...,
project: { externalRef: projectRef },
type: environmentType,
branchName: branch,
+ ...(environmentType === "DEVELOPMENT" && authenticationResult.type === "personalAccessToken"
+ ? { orgMember: { userId: authenticationResult.result.userId } }
+ : {}),
},| import { DEFAULT_DEV_BRANCH, isDefaultDevBranch } from "@trigger.dev/core/v3/utils/gitBranch"; | ||
| import invariant from "tiny-invariant"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify invariant usage in this file
rg -n "invariant" "apps/webapp/app/routes/api.v1.projects.\$projectRef.branches.ts"Repository: triggerdotdev/trigger.dev
Length of output: 111
🏁 Script executed:
cat "apps/webapp/app/routes/api.v1.projects.\$projectRef.branches.ts"Repository: triggerdotdev/trigger.dev
Length of output: 5212
Remove unused import: invariant.
This import is not used anywhere in the file and should be removed.
| type: true, | ||
| slug: true, | ||
| branchName: true, | ||
| parentEnvironmentId: true, |
There was a problem hiding this comment.
Missing parentEnvironmentId in mollifier fallback query causes filtering inconsistency.
The main query (line 74) now selects parentEnvironmentId, but the mollifier fallback query at lines 143-159 does not include it. When the filter at line 252 runs against environments from the fallback path, env.parentEnvironmentId will be undefined, causing all PREVIEW environments to be incorrectly included (since undefined !== null is true).
🐛 Proposed fix: Add parentEnvironmentId to fallback query
select: {
slug: true,
environments: {
select: {
id: true,
type: true,
slug: true,
branchName: true,
+ parentEnvironmentId: true,
orgMember: { select: { user: true } },
},Also applies to: 252-252
| if ( | ||
| auth.result.environment.slug !== slug && | ||
| auth.result.environment.branchName !== resolvedBranch | ||
| ) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how this condition was used before this PR to understand the intended behavior
rg -nC5 "Invalid environment slug for this API key" apps/webapp/app/services/apiAuth.server.tsRepository: triggerdotdev/trigger.dev
Length of output: 424
🏁 Script executed:
# Get broader context around the condition
cat -n apps/webapp/app/services/apiAuth.server.ts | sed -n '470,510p'Repository: triggerdotdev/trigger.dev
Length of output: 1493
🏁 Script executed:
# Search for how environment validation is handled elsewhere in the file
rg -n "branchName\|slug" apps/webapp/app/services/apiAuth.server.ts | head -20Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Look for comments or documentation about environment types (root vs branch)
rg -nC10 "root.*env|branch.*env" apps/webapp/app/services/apiAuth.server.ts | head -40Repository: triggerdotdev/trigger.dev
Length of output: 841
🏁 Script executed:
# Look for environment type definition and how branchName is set
rg -n "type.*Environment|branchName" apps/webapp/app/services/apiAuth.server.ts | head -30Repository: triggerdotdev/trigger.dev
Length of output: 1799
🏁 Script executed:
# Search for the Environment interface/type definition
fd -e ts -e tsx | xargs grep -l "interface.*Environment\|type.*Environment" | head -5Repository: triggerdotdev/trigger.dev
Length of output: 323
🏁 Script executed:
# Look for how environments are queried/created to understand branchName nullability
rg -B5 "branchName" apps/webapp/prisma/schema.prismaRepository: triggerdotdev/trigger.dev
Length of output: 142
🏁 Script executed:
# Find and examine the AuthenticatedEnvironment type
find . -path "*/v3/auth/environment*" -type fRepository: triggerdotdev/trigger.dev
Length of output: 112
🏁 Script executed:
# Look at findEnvironmentByApiKey implementation
rg -n "function findEnvironmentByApiKey|const findEnvironmentByApiKey" apps/webapp/app/services/apiAuth.server.ts -A 20Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Find prisma schema
find . -name "schema.prisma" -type fRepository: triggerdotdev/trigger.dev
Length of output: 119
🏁 Script executed:
# Look at AuthenticatedEnvironment type definition
cat packages/core/src/v3/auth/environment.tsRepository: triggerdotdev/trigger.dev
Length of output: 4353
🏁 Script executed:
# Search for findEnvironmentByApiKey implementation in the file
rg -n "findEnvironmentByApiKey|findEnvironmentByPublicApiKey" apps/webapp/app/services/apiAuth.server.ts -B 2 -A 15Repository: triggerdotdev/trigger.dev
Length of output: 3372
🏁 Script executed:
# Check Environment schema in prisma
rg -n "model Environment\|branchName" internal-packages/database/prisma/schema.prisma -A 20 | head -50Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Find runtimeEnvironment.server.ts to see the implementation
find . -name "runtimeEnvironment.server.ts" -type fRepository: triggerdotdev/trigger.dev
Length of output: 123
🏁 Script executed:
# Look at the Environment model in prisma schema
rg -n "model RuntimeEnvironment" internal-packages/database/prisma/schema.prisma -A 30Repository: triggerdotdev/trigger.dev
Length of output: 2344
🏁 Script executed:
# Now look at findEnvironmentByApiKey implementation to understand how it selects environments
rg -n "export.*function findEnvironmentByApiKey|export.*const findEnvironmentByApiKey" apps/webapp/app/models/runtimeEnvironment.server.ts -A 30Repository: triggerdotdev/trigger.dev
Length of output: 976
🏁 Script executed:
# Let me understand better: what does the validation function receive as parameters?
# Look at where validateEnvironmentForAuthentication is called
rg -n "validateEnvironmentForAuthentication" apps/webapp/app/services/apiAuth.server.ts -B 5 | head -40Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Check if there's type checking in the validation logic
rg -n "isBranchableEnvironment|type.*DEVELOPMENT|type.*PREVIEW" apps/webapp/app/services/apiAuth.server.tsRepository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Let me see the full context of validateEnvironmentForAuthentication function
cat -n apps/webapp/app/services/apiAuth.server.ts | sed -n '450,530p'Repository: triggerdotdev/trigger.dev
Length of output: 2921
🏁 Script executed:
# Look for similar pattern validation elsewhere in codebase to understand intended behavior
rg -n "branchName.*null|parentEnvironmentId" apps/webapp/app/services/apiAuth.server.ts -B 2 -A 2Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Check how the function is called to understand what slug and branch are being validated
rg -n "validateEnvironmentForAuthentication\|authenticatedEnvironmentForAuthentication" apps/webapp/app --type ts | head -20Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Let me check how environments are structured - do branch envs have the same slug as parent?
rg -n "parentEnvironment|childEnvironments" apps/webapp/app/models/runtimeEnvironment.server.ts -B 2 -A 2 | head -40Repository: triggerdotdev/trigger.dev
Length of output: 1423
🏁 Script executed:
# Check if there's a field that indicates whether env is a root or branch env
rg -n "isBranchableEnvironment" apps/webapp/app --type ts | head -10Repository: triggerdotdev/trigger.dev
Length of output: 979
🏁 Script executed:
# Let me trace through a concrete example - look for test files or actual usage
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "authenticatedEnvironmentForAuthentication\|validateEnvironmentForAuthentication" 2>/dev/null | head -3Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Let me trace through a concrete scenario - look at how environments are created
# to understand the slug and branchName relationship
rg -n "slug.*branchName\|branchName.*slug" apps/webapp/app --type ts -B 2 -A 2 | head -60Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Check if there's documentation or comments about validation logic for branch envs
rg -n "branch env|root env|parent.*child" apps/webapp/app/services/apiAuth.server.ts -B 1 -A 1Repository: triggerdotdev/trigger.dev
Length of output: 51
🏁 Script executed:
# Let me understand the complete flow - when a branch is requested but doesn't exist,
# what does findEnvironmentByApiKey return?
cat -n apps/webapp/app/models/runtimeEnvironment.server.ts | sed -n '95,175p'Repository: triggerdotdev/trigger.dev
Length of output: 2804
Change && to || in the environment validation condition.
The condition currently throws only when both slug and branchName fail to match, but should throw when either one fails. For example:
- API key for
devenvironment + request forpreview(no branch) → should fail since"dev" !== "preview", but doesn't becausenull === null - API key for branch
dev/feature+ request for different branchdev/other→ should fail, but doesn't
Replace with || so validation passes only when the slug matches (root env) OR the branchName matches (branch env).
| UPDATE "RuntimeEnvironment" | ||
| SET "isBranchableEnvironment" = true | ||
| WHERE "type" = 'DEVELOPMENT' | ||
| AND "isBranchableEnvironment" = false | ||
| AND "archivedAt" IS NULL; |
There was a problem hiding this comment.
Backfill as a single migration UPDATE risks production lock contention.
Line 2–6 updates every matching RuntimeEnvironment row in one transaction. On a large table, this can become long-running and impact write latency/availability during deploy. Please switch to a batched backfill strategy (e.g., out-of-band job or chunked updates) before merge.
Adds environment branches (like Preview branches) to development environment.
Allows running and working on multiple local development environments simultaneously.
This PR sets the groundwork for the feature with the core and API features needed.
Todos:
TRIGGER_PREVIEW_BRANCHenv var injection (check that child tasks get correct env var)