Skip to content

feat(webapp,rbac): REQUIRE_PLUGINS=1 fail-fast for required plugin loads [TRI-9852]#3734

Open
matt-aitken wants to merge 5 commits into
mainfrom
feature/require-plugins-fail-fast
Open

feat(webapp,rbac): REQUIRE_PLUGINS=1 fail-fast for required plugin loads [TRI-9852]#3734
matt-aitken wants to merge 5 commits into
mainfrom
feature/require-plugins-fail-fast

Conversation

@matt-aitken
Copy link
Copy Markdown
Member

@matt-aitken matt-aitken commented May 23, 2026

Summary

  • internal-packages/rbac/src/index.ts — in LazyController.load()'s catch block, throw an Error when process.env.REQUIRE_PLUGINS === "1" instead of silently falling back. The throw is captured into the lazy controller's init promise, so it surfaces on the first method call.
  • apps/webapp/app/routes/healthcheck.tsxawait rbac.isUsingPlugin() after the DB ping. With REQUIRE_PLUGINS=1 and a failed plugin load, the throw surfaces here and the healthcheck returns 500 → readiness probe fails → rollout is rolled back. Noop for self-hosters.
  • .server-changes/require-plugins-fail-fast.md — server-changes entry.
  • internal-packages/rbac/src/require-plugins.test.ts — 4 unit tests covering loader branching: unset → fallback, =1 → throw, forceFallback: true wins, only exactly "1" enforces.
  • internal-packages/testcontainers/src/webapp.ts — adds requirePlugins?: boolean to StartWebappOptions. Implies forceRbacFallback: false.
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts — e2e closes the loop: spawns a real webapp, hits /healthcheck via HTTP, asserts 500 with REQUIRE_PLUGINS=1 and 200 without.

Motivation

Today the RBAC plugin loader catches any plugin-load failure (missing module, broken transitive dep, init throw) and silently returns the default fallback implementation. This is the correct behaviour for self-hosters who don't ship the plugin — but it's dangerous in deployments where the plugin is expected to load: an accidentally-missing or broken plugin would silently disable enforcement.

REQUIRE_PLUGINS=1 makes the loader fail loudly in those deployments. The variable name is intentionally plural and generic — future plugin contracts (audit logs, SSO) can read the same flag without renaming.

Closes TRI-9852.

Test plan

  • pnpm run test --filter @trigger.dev/rbac — 38/38 tests pass, including the 4 new loader tests
  • pnpm run typecheck --filter webapp — passes
  • pnpm run typecheck --filter @trigger.dev/rbac --filter @internal/testcontainers — passes
  • e2e test added (healthcheck-require-plugins.e2e.test.ts) — CI runs it via e2e-webapp.yml. Couldn't run locally (no Docker daemon up); CI has Docker provisioned.

🤖 Generated with Claude Code

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 23, 2026

⚠️ No Changeset found

Latest commit: 39fb313

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

This PR includes no changesets

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

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

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

Review Change Stack

Walkthrough

Adds an opt-in fail-fast mode for the RBAC lazy plugin loader controlled by REQUIRE_PLUGINS=1 (import failures throw instead of falling back). The healthcheck route now awaits rbac.isUsingPlugin() so plugin-load failures surface during readiness probes. Adds unit tests and E2E tests, testcontainer wiring to spawn servers with REQUIRE_PLUGINS=1, and documentation describing the change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description comprehensively covers the motivation, implementation details, test plan, and includes proper testing evidence; however, it does not follow the repository's standard template structure with required sections. Consider restructuring the description to follow the template with explicit Checklist, Testing, and Changelog sections for better consistency with repository standards.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main feature: adding REQUIRE_PLUGINS=1 fail-fast mode for plugin loads, which is the primary objective of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feature/require-plugins-fail-fast

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.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

matt-aitken and others added 5 commits May 24, 2026 16:02
Adds a deployment-mode opt-in that hardens the plugin loader against
silent degradation. Today, when the RBAC plugin module fails to load —
whether because it's not installed or because a transitive dep is broken —
the loader catches the error and returns the default fallback
implementation. Correct for self-hosters; dangerous in deployments where
the fallback's permissive auth is not an acceptable degraded state.

- internal-packages/rbac/src/index.ts: in LazyController.load()'s catch
  block, after logging, throw an Error when `process.env.REQUIRE_PLUGINS
  === "1"`. The throw is captured into `this._init` (a rejected promise),
  so it surfaces on the first method call on the lazy controller. The
  forceFallback option (used by tests) still wins — it short-circuits
  before this code path, so tests aren't broken if a test runner happens
  to inherit REQUIRE_PLUGINS from its parent env.
- apps/webapp/app/routes/healthcheck.tsx: call `rbac.isUsingPlugin()`
  after the DB ping. For self-hosters (and any deployment with the
  plugin loading cleanly) this is a noop. With REQUIRE_PLUGINS=1 and a
  failed plugin load, the throw surfaces here — healthcheck returns 500
  and the rollout's readiness probe fails, so the new revision is
  rolled back before traffic shifts.

REQUIRE_PLUGINS is intentionally plural and generic — future plugin
contracts (audit logs, SSO) can read the same flag without renaming.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four tests in require-plugins.test.ts driving the loader's branching:
- REQUIRE_PLUGINS unset + plugin missing → falls back (resolves false)
- REQUIRE_PLUGINS=1 + plugin missing → rejects on first method call
- forceFallback: true beats REQUIRE_PLUGINS=1 (test escape hatch)
- Any non-"1" value of REQUIRE_PLUGINS is treated as unset

The plugin module isn't installed in this OSS repo, so the dynamic import
naturally fails with ERR_MODULE_NOT_FOUND — no module mocking needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the loop on the unit-test loader coverage by spawning a real
webapp + DB + Redis and verifying via HTTP:

- REQUIRE_PLUGINS=1 + plugin missing → GET /healthcheck → 500 (so
  ECS/k8s readiness probes fail and the rollout rolls back).
- REQUIRE_PLUGINS unset + plugin missing → GET /healthcheck → 200
  (baseline — self-hoster behaviour unchanged).

- internal-packages/testcontainers/src/webapp.ts: adds
  `requirePlugins?: boolean` to StartWebappOptions. Implies
  `forceRbacFallback: false` (you can't observe the throw if the
  loader short-circuits to the fallback).
- apps/webapp/test/healthcheck-require-plugins.e2e.test.ts: two
  describes, each with its own webapp instance (~30s spawn each)
  since they need different env. Auto-picked up by vitest.e2e.config.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ly work

Three small fixes uncovered by running the e2e test locally:

- internal-packages/rbac/src/index.ts: attach a no-op .catch() to
  LazyController._init in the constructor. load() runs eagerly but its
  result is only awaited on the first method call. When load() rejects
  (REQUIRE_PLUGINS=1 + plugin missing), Node flags the unawaited
  rejection as unhandledRejection — newer Node versions kill the
  process before any consumer can await _init and convert the throw
  into a 500. The .catch() marks the rejection as handled at the
  global level; the actual error still re-throws when c() awaits _init.
- internal-packages/testcontainers/src/webapp.ts: when requirePlugins
  is set, explicitly override RBAC_FORCE_FALLBACK="0" so a local
  apps/webapp/.env that sets it to "1" doesn't short-circuit the loader
  past the REQUIRE_PLUGINS check. Without this, the test passes in CI
  (no .env file) but fails locally with no obvious error.
- internal-packages/testcontainers/src/webapp.ts: waitForHealthcheck
  now takes an `acceptAnyResponse` flag. When requirePlugins is set,
  /healthcheck is *expected* to return 500 (the whole point of the
  test), so the bootstrap waiter must accept any HTTP response rather
  than only 200 — otherwise startTestServer times out before the test
  can assert.

apps/webapp/test/healthcheck-require-plugins.e2e.test.ts: tidied up
the locally-linked-skip placeholder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…check

Two paths could silently skip the new plugin-load gate, found in review:

- server.ts: when DASHBOARD_AND_API_DISABLED=true, /healthcheck was
  served by a static Express handler (200 OK) that bypassed the Remix
  loader entirely. Forward to createRequestHandler in that branch too
  so the loader's readiness checks (DB ping + rbac.isUsingPlugin())
  run in every deployment mode.
- healthcheck.tsx: rbac.isUsingPlugin() sat after the
  HEALTHCHECK_DATABASE_DISABLED early return, so it never ran when
  that flag was set. The rbac fallback doesn't touch the DB
  (fallback.ts isUsingPlugin returns false unconditionally), so move
  the rbac check above the DB-disabled guard — REQUIRE_PLUGINS
  protection now applies regardless of the DB-healthcheck setting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@matt-aitken matt-aitken force-pushed the feature/require-plugins-fail-fast branch from 74b9042 to 39fb313 Compare May 24, 2026 18:11
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/webapp/test/healthcheck-require-plugins.e2e.test.ts (1)

1-93: ⚡ Quick win

Co-locate this .test.ts file with the source under test.

This new test is under apps/webapp/test/, but the repo rule for *.test.ts requires placement beside the source file it validates. Please move it next to the healthcheck route/module under test.

As per coding guidelines "**/*.test.{ts,tsx,js,jsx}: Test files should be placed next to source files (e.g., MyService.ts -> MyService.test.ts)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/webapp/test/healthcheck-require-plugins.e2e.test.ts` around lines 1 -
93, Move the new end-to-end test out of apps/webapp/test and colocate it beside
the healthcheck route/module it verifies; specifically relocate the file that
defines the top-level describe("/healthcheck with REQUIRE_PLUGINS") (the file
exporting LINKED_PLUGIN_PATH, pluginLocallyLinked, and using
startTestServer/TestServer) into the same directory as the healthcheck source,
update any relative imports (e.g. startTestServer import) as necessary, and run
the test/lint to ensure the repo rule for *.test.ts next-to-source is satisfied.
internal-packages/testcontainers/src/webapp.ts (1)

78-81: 💤 Low value

Consider enforcing the requirePlugins → forceRbacFallback=false constraint at the TypeScript level.

The current implementation allows forceRbacFallback to be set independently from requirePlugins, then overrides the environment variable at line 132. While functionally correct, this creates a mismatch between the TypeScript variable and the actual environment behavior that could confuse maintainers.

♻️ Optional refactor for consistency
 const requirePlugins = options.requirePlugins ?? false;
-const forceRbacFallback = options.forceRbacFallback ?? !requirePlugins;
+const forceRbacFallback = requirePlugins 
+  ? false 
+  : (options.forceRbacFallback ?? true);

This makes the TypeScript variable consistent with the environment variable behavior and removes the need for the override comment at line 129-131.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal-packages/testcontainers/src/webapp.ts` around lines 78 - 81, Ensure
the TypeScript variables reflect the intended constraint by making
forceRbacFallback derived from requirePlugins: compute requirePlugins =
Boolean(options.requirePlugins) and set forceRbacFallback = requirePlugins ?
false : (options.forceRbacFallback ?? true) so that when requirePlugins is true
forceRbacFallback is always false, otherwise fall back to the caller-provided
value or default; update references to use these symbols (requirePlugins,
forceRbacFallback) and remove the separate env-override comment/code that
previously reconciled the mismatch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/webapp/test/healthcheck-require-plugins.e2e.test.ts`:
- Around line 1-93: Move the new end-to-end test out of apps/webapp/test and
colocate it beside the healthcheck route/module it verifies; specifically
relocate the file that defines the top-level describe("/healthcheck with
REQUIRE_PLUGINS") (the file exporting LINKED_PLUGIN_PATH, pluginLocallyLinked,
and using startTestServer/TestServer) into the same directory as the healthcheck
source, update any relative imports (e.g. startTestServer import) as necessary,
and run the test/lint to ensure the repo rule for *.test.ts next-to-source is
satisfied.

In `@internal-packages/testcontainers/src/webapp.ts`:
- Around line 78-81: Ensure the TypeScript variables reflect the intended
constraint by making forceRbacFallback derived from requirePlugins: compute
requirePlugins = Boolean(options.requirePlugins) and set forceRbacFallback =
requirePlugins ? false : (options.forceRbacFallback ?? true) so that when
requirePlugins is true forceRbacFallback is always false, otherwise fall back to
the caller-provided value or default; update references to use these symbols
(requirePlugins, forceRbacFallback) and remove the separate env-override
comment/code that previously reconciled the mismatch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1856c8b6-aaf6-4818-a5b4-740b648837dd

📥 Commits

Reviewing files that changed from the base of the PR and between 74b9042 and 39fb313.

📒 Files selected for processing (7)
  • .server-changes/require-plugins-fail-fast.md
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/server.ts
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • internal-packages/testcontainers/src/webapp.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). (19)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp
  • GitHub Check: typecheck / typecheck
  • GitHub Check: 🛡️ E2E Auth Tests (full)
🧰 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

Files:

  • apps/webapp/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.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/server.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.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 dynamic import() when circular dependencies cannot be resolved otherwise, code splitting is needed for performance, or the module must be loaded conditionally at runtime.
Import from @trigger.dev/core using subpaths only - never import from the root.
When writing Trigger.dev tasks, always import from @trigger.dev/sdk. Never use @trigger.dev/sdk/v3 or deprecated client.defineJob.
Add agentcrumbs markers (// @Crumbs or `#region `@crumbs) as you write code, not just when debugging. They stay on the branch throughout development and are stripped by agentcrumbs strip before merge.

Files:

  • apps/webapp/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.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/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.ts
apps/webapp/**/*.{ts,tsx}

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

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

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

Files:

  • apps/webapp/server.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

Code formatting must be enforced using Prettier before committing

Files:

  • apps/webapp/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.ts
**/*.{test,spec}.{ts,tsx}

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

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptive describe and it blocks
Unit tests should use vitest framework
Tests should avoid mocks or stubs and use helpers from @internal/testcontainers when Redis or Postgres are needed

**/*.test.{ts,tsx,js,jsx}: Never mock anything in tests - use testcontainers instead.
Test files should be placed next to source files (e.g., MyService.ts -> MyService.test.ts).

Files:

  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
apps/webapp/**/*.{tsx,jsx}

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

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

Files:

  • apps/webapp/app/routes/healthcheck.tsx
apps/webapp/**/*.test.{ts,tsx}

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

Do not import env.server.ts directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable

For testable code, never import env.server.ts in test files. Pass configuration as options instead (e.g., realtimeClient.server.ts takes config as constructor arg, realtimeClientGlobal.server.ts creates singleton with env config)

Files:

  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
🧠 Learnings (13)
📚 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/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.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/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.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/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.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/server.ts
  • internal-packages/rbac/src/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
  • internal-packages/testcontainers/src/webapp.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/server.ts
  • apps/webapp/app/routes/healthcheck.tsx
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.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/index.ts
  • internal-packages/rbac/src/require-plugins.test.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/index.ts
  • internal-packages/rbac/src/require-plugins.test.ts
📚 Learning: 2026-05-18T14:40:02.173Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3658
File: packages/core/src/v3/realtimeStreams/manager.test.ts:1-147
Timestamp: 2026-05-18T14:40:02.173Z
Learning: In the triggerdotdev/trigger.dev repo, the policy “Never mock anything — use testcontainers instead” should only be enforced for integration tests that interact with real external services (e.g., Redis, Postgres) via actual infrastructure. For unit tests that exercise pure in-memory logic (e.g., cache semantics) it is OK to stub collaborators such as `ApiClient` using Vitest (`vi.fn()`) to assert call counts or control behavior. Do not flag `vi.fn()`-based `ApiClient` stubs in unit tests as violations of the testcontainers policy.

Applied to files:

  • internal-packages/rbac/src/require-plugins.test.ts
  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
📚 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/healthcheck.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/healthcheck.tsx
📚 Learning: 2026-05-08T21:00:20.973Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 3538
File: apps/webapp/app/components/primitives/Resizable.tsx:60-78
Timestamp: 2026-05-08T21:00:20.973Z
Learning: In the triggerdotdev/trigger.dev codebase, treat Zod as a boundary validation tool (API handlers, request/response validation, and storage/DB read/write validation), not as inline render-time validation inside React components/primitive UI code. For render-time guards, prefer small manual type-narrowing checks (e.g., a short predicate like ~10–20 lines) over importing Zod into UI primitives, to avoid per-render schema-parse overhead and unnecessary abstraction. Use the manual guard approach unless you truly need schema validation at a boundary; only then introduce Zod.

Applied to files:

  • apps/webapp/app/routes/healthcheck.tsx
📚 Learning: 2026-05-07T12:25:18.271Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3531
File: apps/webapp/test/sentryTraceContext.server.test.ts:9-47
Timestamp: 2026-05-07T12:25:18.271Z
Learning: In the triggerdotdev/trigger.dev webapp test suite, it is acceptable to leave `createInMemoryTracing()` calls that register a global `NodeTracerProvider` without `afterEach`/`afterAll` teardown. Do not flag this as a test-ordering risk when the code follows the established pattern used across webapp tests (e.g., replication service/benchmark/backfiller tests). This is considered safe because `trace.getActiveSpan()` when called outside a `context.with(...)` block reads `AsyncLocalStorage.getStore()` (undefined when no `run()` scope exists), so it falls back to `ROOT_CONTEXT` with no attached span—regardless of which provider is registered.

Applied to files:

  • apps/webapp/test/healthcheck-require-plugins.e2e.test.ts
📚 Learning: 2026-05-14T14:54:39.095Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3545
File: .server-changes/agent-view-sessions.md:10-10
Timestamp: 2026-05-14T14:54:39.095Z
Learning: In the `trigger.dev` repository, do not flag inconsistent dot vs slash notation in route/path strings inside `.server-changes/*.md` files. These markdown files are consumed verbatim into the changelog, so the mixed notation (e.g., `resources.orgs.../runs.$runParam/...`) is intentional and should be preserved as-is.

Applied to files:

  • .server-changes/require-plugins-fail-fast.md
🪛 LanguageTool
.server-changes/require-plugins-fail-fast.md

[grammar] ~6-~6: Ensure spelling is correct
Context: ...sing, broken transitive dep, etc.). The webapp's /healthcheck route now resolves the l...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (9)
.server-changes/require-plugins-fail-fast.md (1)

6-6: Clarify exact trigger value in the changelog text.

Line 6 still says “When set”, but the runtime behavior is only enabled when REQUIRE_PLUGINS is exactly "1".

internal-packages/rbac/src/index.ts (1)

56-62: LGTM!

Also applies to: 116-127

internal-packages/rbac/src/require-plugins.test.ts (1)

1-44: LGTM!

apps/webapp/app/routes/healthcheck.tsx (1)

4-15: LGTM!

Also applies to: 21-21

apps/webapp/server.ts (1)

22-25: LGTM!

Also applies to: 191-202, 219-219

internal-packages/testcontainers/src/webapp.ts (4)

23-37: LGTM!


57-67: LGTM!


129-132: LGTM!


168-173: LGTM!

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