feat(webapp): deterministic grounding facts and org-wide tokens for the dashboard agent - #4796
feat(webapp): deterministic grounding facts and org-wide tokens for the dashboard agent#4796kathiekiwi wants to merge 45 commits into
Conversation
…text Stops the assistant chat crashing to a full-screen error when the code-highlighting chunk fails to load. Retries twice with backoff, then renders plain text instead of throwing.
…d errors Extracts the lazy chunk-load factory as loadStreamdownRenderer so the plain-text fallback path is covered by a test. The fallback still re-raises the original error as an unhandled rejection so the deploy-skew asset-recovery reload can pick it up.
…s their organization
One read-only Lua script reads the base concurrency sets, every CK variant in ckIndex and the runningCounter together, so the run ids behind a queue's running count come with counts from the same snapshot.
Adds slotHolders and slotHolderFacts to the retrieved queue: which runs hold the queue's slots, their phase, and whether Redis membership matches the run's status. Both the Redis and Postgres reads degrade instead of failing.
The holder list can never claim completeness for a CK queue, so the contract now reports only what is provable: truncated when the cap was hit and unlistedRunning for dequeued holders that exist but aren't listed.
Pass slotHolders/holderResolution through from the queue live row when present, and ground the model on how to read them: name the holder when consistent, call out scheduler/run-state mismatches without saying leaked or stale, and never assert an executing run from runningNow alone.
Grounding block now covers partial resolution and unresolved holders, the none branch no longer asserts usage as fact, and the runningNow-mismatch clause fires only when holderResolution is complete.
Adds slotHolderFacts to withLiveState with the same independent gating as slotHolders/holderResolution. Grounding block covers admitted-vs- dequeued phase and prefers slotHolderFacts over comparing runningNow manually.
…ists slotHolders is never exhaustive by contract for per-key concurrency queues, so no field claims completeness. slotHolderFacts gains truncated/unlistedRunning as proof of unlisted holders, and its consistency can be unresolved (counts then unusable). Grounding rules and tests updated to match.
get_repo_info and read_file now surface the run-pinned deployment's dirty flag (built from a tree with uncommitted changes), and the source read ledger tracks it per-sha so evidence canonicalization can caveat citations instead of asserting an exact commit match.
A dirty run-pinned deploy and the clean tracked branch can share a sha. dirtyForSha was last-write-wins, so a later clean read of that sha erased the dirty caveat. Fixed to OR instead of overwrite.
…oundary The dashboard agent's delegated token now carries the organization alongside the environment. For a token with an organization, the request may name any environment in that org — re-authorized against the org and the user's membership — and the token's own environment is only the default. Tokens without one stay env-pinned.
…h alert The unsubscribe route read the environment off the token alone, so an org-wide token could subscribe an alert in a sibling environment but not remove it. It now resolves the environment the same way the other agent routes do, checked against the token's organization, which resolveAgentAlertContext requires its caller to pass.
Curated runs expose a computed wait (queued vs created basis, reliability flag) instead of raw timestamps the model had to subtract itself, mirroring dashboardAgentWatchRunChecks' queue-wait semantics. The webapp run presenter now selects queuedAt and derives queueWaitReliable from the raw status. Trace spans carry their spanId, and span evidence is validated against a per-turn span ledger (mirroring the source-read ledger) so a citation must come from this turn's trace read. Source evidence also gets a code-stamped dirty flag from the same ledger, fed by run-pinned/default snapshot dirtiness. Error groups expose a computed recurredSinceResolve instead of leaving the model to compare resolvedAt/lastSeen dates.
…akdown on get_queue The queue can show headroom while the environment is saturated, so the binding constraint may not be the queue itself. Add envConcurrency (limit, current) to QueueRetrievePresenter, guarded like slotHolders, and pass it through the dashboard agent's get_queue tool alongside the concurrency override breakdown (base/override/overriddenBy/overriddenAt) the route already returns but the tool was dropping.
… not the plain limit current >= limit is not the dequeue gate; it's current >= limit * burstFactor (burstFactor defaults to 2). Add burstFactor to EnvConcurrency and reword the get_queue description so the model reasons from the real gate instead of assuming current == limit means the environment is saturated.
Add optional scope/queueName/limit/current fields so the model knows which queue or env is saturated, instead of guessing from the page. Populated by the webapp from data already graded (no new queries), carried through verbatim by the dashboard-agent tool.
The bare-evidence test asserted the pre-validation contract without ever reading a trace. Now it drives get_run_trace for real before citing the span, plus a new case for a span id no trace read returned this turn.
Dedupe get_queue grounding between the tool description and the system prompt, and tighten verbose investigation/watch phrasing, to bring both prompt.chars ceilings back under budget without dropping any grounding rule.
Fabricates PG+Redis fixtures for S1-S6 and S10 of the dashboard-agent UAT scenarios in the local References/hello-world dev environment.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
🚧 Files skipped from review as they are similar to previous changes (18)
Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review. 📜 Recent review details⏰ Context from checks skipped due to timeout. (38)
WalkthroughThe changes add organization-wide dashboard-agent token scope with membership and organization-boundary validation. Queue APIs now expose slot holders, concurrency limits, consistency metadata, and reliable wait times. Agent evidence tracks trace reads and dirty source snapshots. API tools support cross-project reads. Dashboard-agent chat now has bounded waits, and Streamdown loading retries with a plain-text fallback. Tests cover authorization, queue diagnostics, evidence, repository state, cross-project reads, timeout handling, and renderer recovery. A UAT script creates and cleans scenario fixtures. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Description checkExplanation The description gives a detailed and relevant summary of the implementation, but it omits the required issue reference, checklist, testing section, changelog section, and screenshots section from the repository template. Resolution Update the description to follow the repository template. Add the issue reference, complete the checklist, document the testing steps and results, add a short changelog entry, and include screenshots or state that screenshots are not applicable. ✨ Finishing Touches 💡 1📝 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 |
…StreamdownRenderer
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/plugins/src/rbac.ts (1)
302-377: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAdd crumb markers for the changed authorization paths.
packages/plugins/src/rbac.ts#L302-L377: mark claim signing and verification.apps/webapp/app/services/dashboardAgentTokenScope.ts#L22-L47: mark scope resolution.apps/webapp/test/dashboardAgentTokenScope.test.ts#L4-L49: mark scope test flow.apps/webapp/app/routes/api.v1.dashboard-agent.alerts.ts#L44-L142: mark token and alert-context resolution.apps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.ts#L52-L97: mark delete-scope resolution.apps/webapp/app/routes/api.v1.dashboard-agent.watches.ts#L62-L98: mark watch authorization.apps/webapp/app/services/dashboardAgentAlertContext.server.ts#L18-L54: mark environment and organization validation.apps/webapp/test/uatEnvironmentClaim.test.ts#L475-L507: mark organization-scope cases.apps/webapp/test/userActorOrgWideEnvironmentScope.test.ts#L75-L117: mark integration authorization cases.As per coding guidelines: “Add crumbs as you write code — not just when debugging.”
Source: Coding guidelines
🧹 Nitpick comments (4)
internal-packages/dashboard-agent/src/tool-curation.ts (1)
29-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd crumbs for the new code paths.
Add
//@Crumbsmarkers or `#region `@crumbsblocks with an approved namespace. Do not create a namespace if no approved namespace applies.
internal-packages/dashboard-agent/src/tool-curation.ts#L29-L75: add crumbs for the run-wait calculation path.internal-packages/dashboard-agent/src/tool-curation.test.ts#L139-L240: add crumbs for the new test paths.As per coding guidelines, “Add crumbs as you write code — not just when debugging.”
Source: Coding guidelines
internal-packages/dashboard-agent/src/tool-api.ts (1)
299-304: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd crumbs for the new runtime transitions.
Add
//@Crumbsmarkers or `// `#region` `@crumbsblocks for the new trace-ledger and live queue-state behavior.
internal-packages/dashboard-agent/src/tool-api.ts#L299-L304: mark the trace-span recording transition.internal-packages/dashboard-agent/src/tool-api.ts#L187-L193: mark the optional live queue-field propagation.internal-packages/dashboard-agent/src/tools.ts#L42-L42: mark the shared-ledger handoff to API tools.As per coding guidelines: “Add crumbs as you write code.”
Source: Coding guidelines
apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts (1)
168-174: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRun the three diagnostic reads concurrently.
#slotHoldersand#envConcurrencyeach await sequentially after the existingPromise.all. Both are independent of the queue-count reads, so the endpoint pays three serial round trips. Both helpers already swallow their own failures, soPromise.allcannot reject here.♻️ Proposed refactor
- const results = await Promise.all([ - engine.lengthOfQueues(environment, [queue.name]), - engine.currentConcurrencyOfQueues(environment, [queue.name]), - ]); - - const { slotHolders, slotHolderFacts } = await this.#slotHolders(environment, queue.name); - const envConcurrency = await this.#envConcurrency(environment); + const [lengths, concurrencies, { slotHolders, slotHolderFacts }, envConcurrency] = + await Promise.all([ + engine.lengthOfQueues(environment, [queue.name]), + engine.currentConcurrencyOfQueues(environment, [queue.name]), + this.#slotHolders(environment, queue.name), + this.#envConcurrency(environment), + ]);Then read
concurrencies?.[queue.name]andlengths?.[queue.name]intoQueueItem.internal-packages/dashboard-agent/src/repo-tools.ts (1)
232-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd required crumbs for the dirty-snapshot flow.
Add
//@Crumbsmarkers or a `#region `@crumbsblock at each changed dirty-state boundary.
internal-packages/dashboard-agent/src/repo-tools.ts#L232-L245: mark snapshot dirty-state propagation into the loaded workspace.internal-packages/dashboard-agent/src/repo-tools.test.ts#L34-L44: mark dirty-snapshot fixture setup.internal-packages/dashboard-agent/src/agent-runtime.ts#L325-L333: mark client snapshot dirty-state validation.internal-packages/dashboard-agent-contracts/src/evidence.ts#L5-L13: mark dirty source-evidence contract propagation.internal-packages/dashboard-agent/src/tool-schemas.ts#L597-L603: mark dirty-source guidance.As per coding guidelines: “Add crumbs as you write code — not just when debugging.”
Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 21637dfc-c891-41bf-bbed-ea3e306ec06f
⛔ Files ignored due to path filters (1)
internal-packages/dashboard-agent/src/__snapshots__/prompt-prefix.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (58)
.server-changes/agent-chat-code-renderer-fallback.md.server-changes/agent-grounded-queue-answers.mdapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/app/components/code/StreamdownRenderer.tsxapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/services/dashboardAgentAlertContext.server.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/test/dashboardAgentTokenScope.test.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/test/uatEnvironmentClaim.test.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsapps/webapp/vitest.config.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.tsinternal-packages/dashboard-agent-contracts/src/evidence.tsinternal-packages/dashboard-agent-contracts/src/page-context.tsinternal-packages/dashboard-agent/src/agent-runtime.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsinternal-packages/dashboard-agent/src/repo-tools.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsinternal-packages/dashboard-agent/src/tool-api.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsinternal-packages/dashboard-agent/src/tool-curation.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsinternal-packages/dashboard-agent/src/tool-evidence.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsinternal-packages/dashboard-agent/src/tool-schemas.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsinternal-packages/dashboard-agent/src/tool-source-ledger.tsinternal-packages/dashboard-agent/src/tools.tsinternal-packages/run-engine/src/engine/index.tsinternal-packages/run-engine/src/run-queue/index.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tspackages/plugins/src/rbac.tsscripts/seed-dashboard-agent-uat.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (32)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
- GitHub Check: internal / 🧪 Unit Tests: Internal
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
- GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: 🛡️ E2E Auth Tests (full)
🧰 Additional context used
📓 Path-based instructions (14)
Use Remix flat-file route conventions with dot-separated segments; for example, `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.ts
Never use `request.signal` to detect client disconnects. Use `getRequestAbortSignal()` from `app/services/httpAsyncStorage.server.ts`, which is wired to Express response close events.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.ts
Test files must not import `app/env.server.ts`; pass configuration as options instead.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/test/dashboardAgentTokenScope.test.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/test/uatEnvironmentClaim.test.ts
For dashboard changes, visually verify the running Remix app with Chrome DevTools MCP, using snapshots, screenshots, interaction, and console-message checks as appropriate.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsapps/webapp/app/components/code/StreamdownRenderer.tsxapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsinternal-packages/dashboard-agent/src/tools.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsinternal-packages/run-engine/src/engine/index.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent-contracts/src/page-context.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxinternal-packages/dashboard-agent/src/agent-runtime.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsinternal-packages/dashboard-agent/src/tool-evidence.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsinternal-packages/dashboard-agent-contracts/src/evidence.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/code/StreamdownRenderer.tsxinternal-packages/dashboard-agent/src/tool-source-ledger.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsinternal-packages/dashboard-agent/src/tool-curation.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsinternal-packages/run-engine/src/run-queue/index.tsinternal-packages/dashboard-agent/src/repo-tools.tsscripts/seed-dashboard-agent-uat.tsinternal-packages/dashboard-agent/src/tool-schemas.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.tspackages/plugins/src/rbac.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
Add crumbs as you write code — not just when debugging. Mark lines with
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsinternal-packages/dashboard-agent/src/tools.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsinternal-packages/run-engine/src/engine/index.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent-contracts/src/page-context.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxinternal-packages/dashboard-agent/src/agent-runtime.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsinternal-packages/dashboard-agent/src/tool-evidence.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsinternal-packages/dashboard-agent-contracts/src/evidence.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/code/StreamdownRenderer.tsxinternal-packages/dashboard-agent/src/tool-source-ledger.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsinternal-packages/dashboard-agent/src/tool-curation.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsinternal-packages/run-engine/src/run-queue/index.tsinternal-packages/dashboard-agent/src/repo-tools.tsscripts/seed-dashboard-agent-uat.tsinternal-packages/dashboard-agent/src/tool-schemas.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.tspackages/plugins/src/rbac.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
Use zod for validation in packages/core and apps/webapp
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsapps/webapp/app/components/code/StreamdownRenderer.tsxapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsapps/webapp/test/uatEnvironmentClaim.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
Do not import `env.server.ts` directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/test/dashboardAgentTokenScope.test.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/test/uatEnvironmentClaim.test.ts
Access environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env`
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsapps/webapp/app/components/code/StreamdownRenderer.tsxapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsapps/webapp/test/uatEnvironmentClaim.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
Use vitest for all tests in the Trigger.dev repository
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.ts
Use function declarations instead of default exports
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsinternal-packages/dashboard-agent/src/tools.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsinternal-packages/run-engine/src/engine/index.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent-contracts/src/page-context.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxinternal-packages/dashboard-agent/src/agent-runtime.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsinternal-packages/dashboard-agent/src/tool-evidence.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsinternal-packages/dashboard-agent-contracts/src/evidence.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/code/StreamdownRenderer.tsxinternal-packages/dashboard-agent/src/tool-source-ledger.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsinternal-packages/dashboard-agent/src/tool-curation.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsinternal-packages/run-engine/src/run-queue/index.tsinternal-packages/dashboard-agent/src/repo-tools.tsscripts/seed-dashboard-agent-uat.tsinternal-packages/dashboard-agent/src/tool-schemas.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.tspackages/plugins/src/rbac.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
Use types over interfaces for TypeScript
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsxapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsinternal-packages/dashboard-agent/src/tools.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsinternal-packages/run-engine/src/engine/index.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent-contracts/src/page-context.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsxinternal-packages/dashboard-agent/src/agent-runtime.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsinternal-packages/dashboard-agent/src/tool-evidence.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsinternal-packages/dashboard-agent-contracts/src/evidence.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/code/StreamdownRenderer.tsxinternal-packages/dashboard-agent/src/tool-source-ledger.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsinternal-packages/dashboard-agent/src/tool-curation.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsinternal-packages/run-engine/src/run-queue/index.tsinternal-packages/dashboard-agent/src/repo-tools.tsscripts/seed-dashboard-agent-uat.tsinternal-packages/dashboard-agent/src/tool-schemas.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.tspackages/plugins/src/rbac.tsapps/webapp/app/services/dashboardAgentAlertContext.server.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
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
Files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.in.$.tsapps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.tsinternal-packages/dashboard-agent/src/tools.tsapps/webapp/app/services/dashboardAgentWatchInvestigate.server.tsinternal-packages/run-engine/src/engine/index.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.jwt.tsapps/webapp/test/dashboardAgentTokenScope.test.tsinternal-packages/dashboard-agent-contracts/src/page-context.tsinternal-packages/dashboard-agent/src/tool-api-transport.test.tsapps/webapp/test/helpers/dashboardAgentWatchesTestHelpers.tsapps/webapp/app/services/dashboardAgent.server.tsinternal-packages/dashboard-agent/src/agent-runtime.tsinternal-packages/dashboard-agent/src/tool-evidence.test.tsapps/webapp/app/services/dashboardAgentTokenScope.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.tsinternal-packages/dashboard-agent/src/tool-source-ledger.test.tsinternal-packages/dashboard-agent/src/tool-evidence.tsapps/webapp/vitest.config.tsapps/webapp/test/userActorOrgWideEnvironmentScope.test.tsinternal-packages/dashboard-agent/src/tool-api-branch.test.tsinternal-packages/dashboard-agent/src/tool-query-retry-cap.test.tsinternal-packages/dashboard-agent/src/tool-api-paths.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsinternal-packages/dashboard-agent-contracts/src/evidence.tsapps/webapp/test/dashboardAgentUserActorToken.test.tsinternal-packages/dashboard-agent/src/tool-ask-support.test.tsapps/webapp/app/services/dashboardAgentWatchRunChecks.tsinternal-packages/dashboard-agent/src/tool-curation.test.tsapps/webapp/app/services/userActorEnvironment.server.tsapps/webapp/app/components/code/StreamdownRenderer.test.tsinternal-packages/dashboard-agent/src/tool-queue.test.tsapps/webapp/test/dashboardAgentWatches.lifecycle.test.tsinternal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.tsapps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.tsapps/webapp/app/routes/api.v1.dashboard-agent.watches.tsinternal-packages/dashboard-agent/src/tool-api.tsinternal-packages/dashboard-agent/src/tool-source-ledger.tsapps/webapp/app/routes/api.v1.dashboard-agent.alerts.$channelId.tsinternal-packages/dashboard-agent/src/tool-curation.tsinternal-packages/dashboard-agent/src/repo-tools.test.tsapps/webapp/test/uatEnvironmentClaim.test.tsinternal-packages/dashboard-agent/src/dashboard-agent.test.tsapps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsinternal-packages/run-engine/src/run-queue/index.tsinternal-packages/dashboard-agent/src/repo-tools.tsscripts/seed-dashboard-agent-uat.tsinternal-packages/dashboard-agent/src/tool-schemas.tsinternal-packages/dashboard-agent-contracts/src/contracts.test.tspackages/plugins/src/rbac.tsapps/webapp/app/services/dashboardAgentAlertContext.server.ts
🧠 Learnings (5)
📚 Learning: 2026-07-26T13:14:02.968Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4378
File: .server-changes/realtime-run-reads-from-primary.md:0-0
Timestamp: 2026-07-26T13:14:02.968Z
Learning: For files in the .server-changes directory, the body text is published verbatim as dashboard-facing user release notes. Write entries in terms of user-visible behavior (what users can do/see), and avoid implementation-oriented details such as environment-variable names, internal mechanisms, or configuration knobs. If you need to include operational/configuration specifics, put those details in the PR description instead of the .server-changes entry.
Applied to files:
.server-changes/agent-chat-code-renderer-fallback.md.server-changes/agent-grounded-queue-answers.md
📚 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/services/dashboardAgentTokenScope.tsapps/webapp/app/presenters/v3/QueueRetrievePresenter.server.tsinternal-packages/run-engine/src/run-queue/index.ts
📚 Learning: 2026-05-01T15:45:08.099Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3499
File: packages/plugins/tsup.config.ts:3-3
Timestamp: 2026-05-01T15:45:08.099Z
Learning: In build/tool configuration files (e.g., tsup.config.ts, vite.config.ts, vitest.config.ts), follow the tool’s documented export pattern and use `export default defineConfig(...)` (or the equivalent documented default export). The repo-wide guideline “use named exports instead of default exports” should apply only to application code (*.{ts,tsx,js,jsx}), not to these build/tool config files—so do not flag `export default defineConfig(...)` in these config files as a violation.
Applied to files:
apps/webapp/vitest.config.ts
📚 Learning: 2026-06-16T09:19:47.637Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3960
File: apps/webapp/test/prismaInfrastructureErrorCapture.test.ts:0-0
Timestamp: 2026-06-16T09:19:47.637Z
Learning: In this repo’s Vitest setup, `vitest.config.ts` uses `globals: true`, so identifiers like `vi`, `describe`, `it`, and `expect` are available as globals in Vitest test files. During code review, do not flag missing `vi`/`describe`/`it`/`expect` imports as a runtime error or correctness issue when they’re used in `*.test.ts/tsx` or `*.spec.ts/tsx` files. Explicit imports are still preferred for consistency, but they’re not required for runtime behavior.
Applied to files:
internal-packages/run-engine/src/run-queue/tests/slotHolders.test.tsapps/webapp/test/uatEnvironmentClaim.test.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/presenters/v3/QueueRetrievePresenter.server.ts
🪛 ast-grep (0.45.2)
internal-packages/dashboard-agent/src/repo-tools.test.ts
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFileSync } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
internal-packages/dashboard-agent/src/repo-tools.ts
[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(detect-child-process-typescript)
🪛 GitHub Check: code-quality / code-quality
apps/webapp/app/components/code/StreamdownRenderer.tsx
[failure] 67-67: typescript(consistent-type-imports)
apps/webapp/app/components/code/StreamdownRenderer.tsx:67:77: import() type annotations are forbidden.
[failure] 67-67: typescript(consistent-type-imports)
apps/webapp/app/components/code/StreamdownRenderer.tsx:67:42: import() type annotations are forbidden.
[failure] 67-67: typescript(consistent-type-imports)
apps/webapp/app/components/code/StreamdownRenderer.tsx:67:13: import() type annotations are forbidden.
🔇 Additional comments (14)
scripts/seed-dashboard-agent-uat.ts (1)
61-149: LGTM!Also applies to: 155-238, 244-341, 357-431, 437-483, 489-568, 635-708, 714-837
apps/webapp/app/components/dashboard-agent/InvestigationCard.tsx (1)
211-216: LGTM!apps/webapp/app/components/dashboard-agent/InvestigationCard.render.test.ts (1)
69-79: LGTM!apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (1)
1185-1195: LGTM!internal-packages/dashboard-agent/src/tool-curation.ts (1)
50-63: 🎯 Functional CorrectnessKeep the fallback aligned with
describeRunWait.describeRunWaitexplicitly usesrun.startedAt ?? nowfor stalequeuedAtvalues, andcomputeRunWaitmatches that contract..server-changes/agent-chat-code-renderer-fallback.md (1)
1-6: LGTM!apps/webapp/app/components/code/StreamdownRenderer.tsx (1)
65-68: 📐 Maintainability & Code QualityNo change needed.
StreamdownRenderer.tsxalready uses type-only namespace imports in the tuple annotation.apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts (1)
41-41: LGTM!Also applies to: 697-698
apps/webapp/app/services/dashboardAgentWatchRunChecks.ts (1)
34-42: LGTM!internal-packages/run-engine/src/run-queue/index.ts (1)
715-730: LGTM!internal-packages/run-engine/src/engine/index.ts (1)
1736-1742: LGTM!internal-packages/run-engine/src/run-queue/tests/slotHolders.test.ts (1)
75-265: LGTM!apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts (1)
227-295: LGTM!.server-changes/agent-grounded-queue-answers.md (1)
1-6: LGTM!
| const importer = vi.fn().mockResolvedValue("ok"); | ||
| await expect(retryImport(importer, [0, 0])).resolves.toBe("ok"); | ||
| expect(importer).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it("retries after failures then succeeds", async () => { | ||
| const importer = vi | ||
| .fn() | ||
| .mockRejectedValueOnce(new Error("fail1")) | ||
| .mockRejectedValueOnce(new Error("fail2")) | ||
| .mockResolvedValue("ok"); | ||
| await expect(retryImport(importer, [0, 0])).resolves.toBe("ok"); | ||
| expect(importer).toHaveBeenCalledTimes(3); | ||
| }); | ||
|
|
||
| it("throws after exhausting retries", async () => { | ||
| const importer = vi.fn().mockRejectedValue(new Error("always fails")); | ||
| await expect(retryImport(importer, [0, 0])).rejects.toThrow("always fails"); | ||
| expect(importer).toHaveBeenCalledTimes(3); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Remove the vi.fn() mocks from these tests.
Use deterministic importer closures with local attempt counters. This tests retryImport without mocks.
As per coding guidelines: “We use vitest exclusively. Never mock anything - use testcontainers instead.”
Source: Coding guidelines
| const priorListeners = process.listeners("unhandledRejection"); | ||
| process.removeAllListeners("unhandledRejection"); | ||
| const caught = new Promise<Error>((resolve) => { | ||
| process.once("unhandledRejection", (err) => resolve(err as Error)); | ||
| }); | ||
|
|
||
| const mod = await loadStreamdownRenderer(() => Promise.reject(new Error("boom")), [0, 0]); | ||
| const html = renderToStaticMarkup(createElement(mod.default, null, "hello **world**")); | ||
| expect(html).toContain("hello"); | ||
|
|
||
| const dispatched = await caught; | ||
| expect(dispatched.message).toMatch(/boom/); | ||
|
|
||
| for (const listener of priorListeners) { | ||
| process.on("unhandledRejection", listener as NodeJS.UnhandledRejectionListener); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Restore process listeners in a finally block.
If loadStreamdownRenderer, rendering, or an assertion fails, the loop at Line 139 does not run. Later tests then run without the original unhandledRejection listeners.
Wrap the test body after Line 127 in try/finally and restore the listeners in finally.
| signals.push({ | ||
| kind: "concurrency_saturation", | ||
| severity: queued >= limit ? "crit" : "warn", | ||
| scope: "env", | ||
| limit, | ||
| current: running, | ||
| }); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
printf '%s\n' '--- applicable repository conventions ---'
find /tmp/coderabbit-repo-knowledge/triggerdotdev-trigger-dev-0bdd0019 -type f -name '*.md' -print
printf '%s\n' '--- changed files and hunks ---'
git diff --stat
git diff -- apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.ts \
apps/webapp/app/presenters/v3/QueueRetrievePresenter.test.ts \
apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts \
internal-packages/dashboard-agent-contracts/src/page-context.ts \
internal-packages/dashboard-agent-contracts/src/contracts.test.ts \
apps/webapp/vitest.config.ts
printf '%s\n' '--- crumb policy references ---'
rg -n -C 3 'crumb|namespace|agentcrumbs' /tmp/coderabbit-repo-knowledge/triggerdotdev-trigger-dev-0bdd0019 \
--glob '*.md'Repository: triggerdotdev/trigger.dev
Length of output: 8339
🏁 Script executed:
printf '%s\n' '--- crumb convention and namespace table ---'
sed -n '80,115p' /tmp/coderabbit-repo-knowledge/triggerdotdev-trigger-dev-0bdd0019/conventions/repo-wide.md
printf '%s\n' '--- repository state ---'
git status --short
git diff --name-only
printf '%s\n' '--- current crumb markers in the listed files ---'
rg -n '`@crumbs`|agentcrumbs' \
apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.ts \
apps/webapp/app/presenters/v3/QueueRetrievePresenter.test.ts \
apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts \
internal-packages/dashboard-agent-contracts/src/page-context.ts \
internal-packages/dashboard-agent-contracts/src/contracts.test.ts \
apps/webapp/vitest.config.ts || true
printf '%s\n' '--- exact listed source ranges ---'
for spec in \
'apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.ts:150:165' \
'apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.ts:218:244' \
'apps/webapp/app/presenters/v3/QueueRetrievePresenter.test.ts:1:68' \
'apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts:232:265' \
'apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts:306:323' \
'apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts:413:438' \
'internal-packages/dashboard-agent-contracts/src/page-context.ts:138:155' \
'internal-packages/dashboard-agent-contracts/src/contracts.test.ts:105:130' \
'apps/webapp/vitest.config.ts:22:31'; do
file=${spec%%:*}; range=${spec#*:}; start=${range%%:*}; end=${range##*:}
printf '%s\n' "--- $file:$start-$end ---"
sed -n "${start},${end}p" "$file"
doneRepository: triggerdotdev/trigger.dev
Length of output: 9684
Add approved crumbs to the changed blocks before merge.
The repository convention requires crumbs during development and strips them before merge. None of the listed blocks contains a crumb marker. Use an existing approved namespace; do not invent one.
📍 Affects 6 files
apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.ts#L155-L161(this comment)apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.ts#L223-L239apps/webapp/app/presenters/v3/QueueRetrievePresenter.test.ts#L4-L64apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts#L237-L261apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts#L311-L320apps/webapp/app/components/dashboard-agent/suggested-prompts/page-mappers.test.ts#L418-L434internal-packages/dashboard-agent-contracts/src/page-context.ts#L143-L151internal-packages/dashboard-agent-contracts/src/contracts.test.ts#L110-L126apps/webapp/vitest.config.ts#L27-L27
Source: Coding guidelines
| queuedAt: run.queuedAt ?? undefined, | ||
| // Mirrors dashboardAgentWatchRunChecks.describeRunWait: a resumed/retried/paused run's | ||
| // queuedAt is a leftover from an earlier enqueue, not this attempt's wait. | ||
| queueWaitReliable: run.queuedAt !== null && !STALE_QUEUED_AT_STATUSES.has(run.status), |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
# Locate the run-retrieve response schema/type and check for the new fields.
fd -t f -e ts . packages/core/src | xargs rg -n --type=ts -C4 '\bRetrieveRunResponse\b|\bqueueWaitReliable\b|\bqueuedAt\b' | head -100Repository: triggerdotdev/trigger.dev
Length of output: 6684
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- applicable repository conventions ---'
find /tmp/coderabbit-repo-knowledge/triggerdotdev-trigger-dev-0bdd0019 -type f -name '*.md' -print \
| while read -r f; do
case "$f" in
*/apps-webapp/*|*/packages-core/*) head -80 "$f";;
esac
done
printf '%s\n' '--- RetrieveRun schemas and shared fields ---'
sed -n '1240,1345p' packages/core/src/v3/schemas/api.ts
sed -n '1535,1590p' packages/core/src/v3/schemas/api.ts
printf '%s\n' '--- queueWaitReliable declarations/usages ---'
rg -n -C3 --glob '*.{ts,tsx}' '\bqueueWaitReliable\b' apps/webapp packages/coreRepository: triggerdotdev/trigger.dev
Length of output: 9990
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- presenter payload construction ---'
sed -n '500,590p' apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
rg -n -C8 'createCommonRunStructure|relatedRuns' apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
printf '%s\n' '--- zodfetch binding and Zod version ---'
rg -n -C5 'function zodfetch|const zodfetch|export .*zodfetch|zodfetch' packages/core/src/v3 packages/core/package.json package.json
rg -n '"zod"|zod@' package.json packages/core/package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null | head -20Repository: triggerdotdev/trigger.dev
Length of output: 50381
Add queuedAt and queueWaitReliable to RetrieveRunCommandFields.
createCommonRunStructure returns both fields for the main run and relatedRuns. RetrieveRunResponse and RelatedRunDetails declare neither field. ApiClient.retrieveRun parses responses with RetrieveRunResponse; Zod strips undeclared keys, so SDK consumers also lack both fields in the inferred types.
| @@ -1,15 +1,93 @@ | |||
| import { formatTriggerUri } from "@internal/dashboard-agent-contracts"; | |||
| import { assertExhaustive } from "@trigger.dev/core"; | |||
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Import assertExhaustive from a @trigger.dev/core subpath.
This file imports from the root @trigger.dev/core path. Use the subpath export that owns the symbol.
As per coding guidelines: "Use subpath exports from @trigger.dev/core package instead of importing from the root @trigger.dev/core path".
Source: Coding guidelines
| ctx, | ||
| client: createApiClient(ctx), | ||
| renderInvestigations: (() => []) as any, | ||
| spanLedger: { recordTraceSpans: () => {} }, |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Replace the no-op span-ledger test doubles.
Each new spanLedger: { recordTraceSpans: () => {} } fixture is a mock. Use a real SourceReadLedger fixture instead.
internal-packages/dashboard-agent/src/tool-api-branch.test.ts#L86-L86: pass a real source-read ledger.internal-packages/dashboard-agent/src/tool-api-paths.test.ts#L35-L35: pass a real source-read ledger.internal-packages/dashboard-agent/src/tool-api-transport.test.ts#L25-L25: pass a real source-read ledger.internal-packages/dashboard-agent/src/tool-query-retry-cap.test.ts#L24-L24: pass a real source-read ledger.internal-packages/dashboard-agent/src/tool-queue.test.ts#L182-L182: pass a real source-read ledger.internal-packages/dashboard-agent/src/tool-queue.test.ts#L248-L248: pass a real source-read ledger.internal-packages/dashboard-agent/src/tool-queue.test.ts#L324-L324: pass a real source-read ledger.
As per coding guidelines: “Never mock anything - use testcontainers instead.”
📍 Affects 5 files
internal-packages/dashboard-agent/src/tool-api-branch.test.ts#L86-L86(this comment)internal-packages/dashboard-agent/src/tool-api-paths.test.ts#L35-L35internal-packages/dashboard-agent/src/tool-api-transport.test.ts#L25-L25internal-packages/dashboard-agent/src/tool-query-retry-cap.test.ts#L24-L24internal-packages/dashboard-agent/src/tool-queue.test.ts#L182-L182internal-packages/dashboard-agent/src/tool-queue.test.ts#L248-L248internal-packages/dashboard-agent/src/tool-queue.test.ts#L324-L324
Source: Coding guidelines
| function stubFetch(liveRow: Record<string, unknown>) { | ||
| vi.stubGlobal( | ||
| "fetch", | ||
| vi.fn(async (input: any) => { | ||
| const url = typeof input === "string" ? input : input.url; | ||
| if (url.endsWith("/jwt")) { | ||
| return new Response(JSON.stringify({ token: "env-jwt" }), { status: 200 }); | ||
| } | ||
| if (url.includes("/metrics")) { | ||
| return new Response(JSON.stringify({ peakQueued: 4800, startedCount: 12 }), { | ||
| status: 200, | ||
| }); | ||
| } | ||
| return new Response(JSON.stringify({ type: "custom", paused: false, ...liveRow }), { | ||
| status: 200, | ||
| }); | ||
| }) | ||
| ); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Replace the mocked fetch transport.
The added vi.stubGlobal and vi.fn calls mock the JWT and queue API responses. Use a Testcontainers-backed service for these new queue integration tests.
As per coding guidelines: “Never mock anything - use testcontainers instead.”
Source: Coding guidelines
| function fakeRepoTools(path: string): ToolSet { | ||
| return { | ||
| read_file: { | ||
| execute: async () => ({ path, content: "..." }), | ||
| }, | ||
| } as unknown as ToolSet; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- applicable convention files ---'
find /tmp/coderabbit-repo-knowledge/triggerdotdev-trigger-dev-0bdd0019 -type f -name '*.md' -print | sort
echo '--- convention excerpts for dashboard-agent/tests ---'
for f in $(find /tmp/coderabbit-repo-knowledge/triggerdotdev-trigger-dev-0bdd0019 -type f -name '*.md' | sort); do
if grep -qEi 'mock|stub|fake|testcontainer|dashboard-agent|test' "$f"; then
echo "### $f"
cat "$f"
fi
done
echo '--- changed test sites ---'
for spec in \
internal-packages/dashboard-agent/src/tool-source-ledger.test.ts \
internal-packages/dashboard-agent/src/tool-evidence.test.ts \
internal-packages/dashboard-agent/src/dashboard-agent.test.ts \
internal-packages/dashboard-agent/src/tool-ask-support.test.ts
do
echo "### $spec"
wc -l "$spec"
case "$spec" in
*tool-source-ledger.test.ts) sed -n '1,105p' "$spec" ;;
*tool-evidence.test.ts) sed -n '1,45p' "$spec" ;;
*dashboard-agent.test.ts) sed -n '1365,1430p' "$spec" ;;
*tool-ask-support.test.ts) sed -n '1,40p' "$spec" ;;
esac
done
echo '--- nearby test infrastructure and direct definitions ---'
rg -n --glob '*.{ts,tsx,js,jsx}' \
'function (fakeRepoTools|fakeReads|stubFetch)|const (fetchMock|fetchStub)|stubFetch|recordTraceSpans|SourceReadLookup|ToolSet|vi\.(fn|stubGlobal|mock)|testcontainers|GenericContainer|Testcontainers' \
internal-packages/dashboard-agent
git ls-files '*test*' '*container*' | head -80Repository: triggerdotdev/trigger.dev
Length of output: 50381
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- source-read ledger contract and implementation ---'
sed -n '1,180p' internal-packages/dashboard-agent/src/tool-source-ledger.ts
echo '--- repository tool construction and read tool contract ---'
sed -n '1,80p' internal-packages/dashboard-agent/src/repo-tools.ts
sed -n '180,255p' internal-packages/dashboard-agent/src/repo-tools.ts
echo '--- API tool contract and span-ledger call site ---'
sed -n '185,320p' internal-packages/dashboard-agent/src/tool-api.ts
echo '--- ask-support test remainder ---'
sed -n '1,90p' internal-packages/dashboard-agent/src/tool-ask-support.test.ts
echo '--- dashboard-agent local fetch stub definition and cited test ---'
sed -n '1385,1435p' internal-packages/dashboard-agent/src/dashboard-agent.test.ts
sed -n '1970,2015p' internal-packages/dashboard-agent/src/dashboard-agent.test.ts
echo '--- source-read ledger tests remainder ---'
sed -n '90,125p' internal-packages/dashboard-agent/src/tool-source-ledger.test.ts
echo '--- testcontainers patterns in this package ---'
sed -n '1,80p' internal-packages/dashboard-agent/src/maintenance.test.ts
sed -n '1,70p' internal-packages/dashboard-agent/src/investigation-sweep.test.tsRepository: triggerdotdev/trigger.dev
Length of output: 31263
🏁 Script executed:
#!/bin/bash
set -eu
echo '--- API client fetch binding ---'
sed -n '1,220p' internal-packages/dashboard-agent/src/tool-api-client.ts
echo '--- ask_support implementation ---'
rg -n -A55 -B15 'ask_support|SUPPORT_ASK_URL|SUPPORT_ASK_SECRET' internal-packages/dashboard-agent/src/tool-api.ts
echo '--- dashboard tool assembly and ledger binding ---'
sed -n '1,80p' internal-packages/dashboard-agent/src/tools.ts
rg -n -A45 -B15 'createSourceReadLedger|withReadTracking|buildRepoTools|buildApiTools' internal-packages/dashboard-agent/src/agent-runtime.ts internal-packages/dashboard-agent/src/tools.tsRepository: triggerdotdev/trigger.dev
Length of output: 19580
Replace the test doubles with real collaborators.
The repository rule for *.test.ts prohibits mocks and requires Testcontainers. Use concrete collaborators for fakeRepoTools, fakeReads, and the no-op spanLedger. Replace both vi.stubGlobal("fetch", ...) implementations with a Testcontainers-backed HTTP endpoint so these tests exercise the production paths.
📍 Affects 4 files
internal-packages/dashboard-agent/src/tool-source-ledger.test.ts#L21-L27(this comment)internal-packages/dashboard-agent/src/tool-source-ledger.test.ts#L77-L81internal-packages/dashboard-agent/src/tool-evidence.test.ts#L7-L15internal-packages/dashboard-agent/src/dashboard-agent.test.ts#L1400-L1406internal-packages/dashboard-agent/src/tool-ask-support.test.ts#L18-L18
Source: Coding guidelines
| collect(baseQueueKey, '') | ||
|
|
||
| local variants = redis.call('ZRANGE', ckIndexKey, 0, -1) | ||
| for _, v in ipairs(variants) do | ||
| collect(keyPrefix .. v, v) | ||
| end | ||
|
|
||
| local baseDequeued = redis.call('SCARD', baseQueueKey .. ':currentDequeued') | ||
| local ckRunning = tonumber(redis.call('GET', runningCounterKey) or '0') or 0 | ||
|
|
||
| return { | ||
| admittedCount, | ||
| dequeuedCount, | ||
| baseDequeued + ckRunning, | ||
| orphanCount, | ||
| truncated, | ||
| holders, | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Cap the CK variant scan, not only the holder output.
ZRANGE ckIndexKey 0 -1 returns every concurrency-key variant, and collect then issues two SMEMBERS calls per variant. maxHolders bounds only the returned list, so a base queue with many concurrency keys makes this script read every variant set inside one blocking Redis invocation. Redis executes scripts single-threaded, so the whole run-queue instance stalls for the duration. concurrencyKeyBreakdown already bounds its variant fan-out to a caller limit for this reason.
The cost is request-driven: QueueRetrievePresenter.#slotHolders calls slotHoldersOfQueue on every queue-retrieve request (see apps/webapp/app/presenters/v3/QueueRetrievePresenter.server.ts Line 245), so a single hot CK queue is enough to trigger it.
Bound the variant scan and report the remainder, so the payload stays honest about incompleteness.
⚡ Sketch: bound the scan and report skipped variants
local keyPrefix = ARGV[1]
local maxHolders = tonumber(ARGV[2])
+local maxVariants = tonumber(ARGV[3])
@@
-local variants = redis.call('ZRANGE', ckIndexKey, 0, -1)
-for _, v in ipairs(variants) do
- collect(keyPrefix .. v, v)
-end
+local totalVariants = redis.call('ZCARD', ckIndexKey)
+local variants = redis.call('ZRANGE', ckIndexKey, 0, maxVariants - 1)
+for _, v in ipairs(variants) do
+ collect(keyPrefix .. v, v)
+end
+local skippedVariants = math.max(0, totalVariants - `#variants`)Return skippedVariants on the reply, surface it on QueueSlotHolders, and treat a non-zero value as another reason the holder list is incomplete.
| const errorJson = JSON.stringify({ | ||
| data: { | ||
| type: "Error", | ||
| message: "uat recurred fixture error", | ||
| stack: "Error: uat recurred fixture error\n at uatFixture (uat.ts:1:1)", | ||
| }, | ||
| }).replace(/'/g, "''"); | ||
|
|
||
| console.log("\nS10: Postgres side done. ClickHouse errors_v1 is a materialized view over"); | ||
| console.log("task_runs_v2 - run this manually to make the error 'recur' after resolvedAt:\n"); | ||
| console.log( | ||
| `clickhouse-client --query "INSERT INTO trigger_dev.task_runs_v2 ` + | ||
| `(environment_id, organization_id, project_id, run_id, friendly_id, environment_type, ` + | ||
| `engine, status, task_identifier, queue, task_version, error, created_at, updated_at, _version) ` + | ||
| `VALUES ('${ctx.devEnv.id}', '${ctx.orgId}', '${ctx.projectId}', 'uat-recurred-run', ` + | ||
| `'run_uatrecurred', 'DEVELOPMENT', 'V2', 'COMPLETED_WITH_ERRORS', '${taskIdentifier}', ` + | ||
| `'uat-recurred-task', 'uat', '${errorJson}', '${formatChDateTime(lastSeen)}', ` + | ||
| `'${formatChDateTime(lastSeen)}', ${version})"\n` | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the shell quoting in the printed ClickHouse command.
errorJson comes from JSON.stringify, so it contains double quotes. The printed command wraps the query in double quotes (--query "..."). A copy-paste of that command ends the quoted string early and the shell fails or runs a truncated query. Escaping only single quotes is not sufficient here.
Wrap the query in single quotes instead. The SQL-level '' escaping already prevents single quotes from appearing in errorJson.
🐛 Proposed fix for the printed command
console.log(
- `clickhouse-client --query "INSERT INTO trigger_dev.task_runs_v2 ` +
+ `clickhouse-client --query 'INSERT INTO trigger_dev.task_runs_v2 ` +
`(environment_id, organization_id, project_id, run_id, friendly_id, environment_type, ` +
`engine, status, task_identifier, queue, task_version, error, created_at, updated_at, _version) ` +
`VALUES ('${ctx.devEnv.id}', '${ctx.orgId}', '${ctx.projectId}', 'uat-recurred-run', ` +
`'run_uatrecurred', 'DEVELOPMENT', 'V2', 'COMPLETED_WITH_ERRORS', '${taskIdentifier}', ` +
`'uat-recurred-task', 'uat', '${errorJson}', '${formatChDateTime(lastSeen)}', ` +
- `'${formatChDateTime(lastSeen)}', ${version})"\n`
+ `'${formatChDateTime(lastSeen)}', ${version})'\n`
);Note that the SQL string literals inside the query use single quotes too, so a single-quoted shell string also needs those inner quotes handled. A robust alternative is to pipe the query on stdin:
console.log("Save the statement below to /tmp/uat-recurred.sql, then run:");
console.log(" clickhouse-client --queries-file /tmp/uat-recurred.sql\n");
console.log(query);DEVELOPMENT envs are per-user; without --user the script picked the project's first dev env, seeding data into the wrong tester's env. clean now sweeps every env in the project regardless of --user.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/seed-dashboard-agent-uat.ts (1)
141-188: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAdd required
@crumbsmarkers to the new fixture lifecycle.Add
//@Crumbsmarkers or `// `#region` `@crumbsblocks around project resolution, target selection, fixture seeding, and cleanup. This script mutates Postgres and Redis state. Crumbs make incorrect target selection and incomplete cleanup easier to diagnose during branch development.As per coding guidelines, “Add crumbs as you write code — not just when debugging.”
Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 904a8509-2e6d-4567-a12e-43bf07fdb6b0
📒 Files selected for processing (2)
apps/webapp/app/components/code/StreamdownRenderer.tsxscripts/seed-dashboard-agent-uat.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/webapp/app/components/code/StreamdownRenderer.tsx
Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (37)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
- GitHub Check: fk-cascade-guard / fk-cascade-guard
- GitHub Check: internal / 🧪 Unit Tests: Internal
- GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
- GitHub Check: obsmap / 🧪 Unit Tests: Observability Map
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: typecheck / typecheck
- GitHub Check: runops-guard / runops-guard
- GitHub Check: code-quality / code-quality
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: 🛡️ E2E Auth Tests (full)
🧰 Additional context used
📓 Path-based instructions (5)
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:
📄 CodeRabbit inference engine (AGENTS.md)
Files:
scripts/seed-dashboard-agent-uat.ts
Add crumbs as you write code — not just when debugging. Mark lines with
📄 CodeRabbit inference engine (AGENTS.md)
Files:
scripts/seed-dashboard-agent-uat.ts
Use function declarations instead of default exports
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
scripts/seed-dashboard-agent-uat.ts
Use types over interfaces for TypeScript
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
scripts/seed-dashboard-agent-uat.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
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
Files:
scripts/seed-dashboard-agent-uat.ts
🧠 Learnings (1)
📚 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:
scripts/seed-dashboard-agent-uat.ts
…d script Confirmed QueueRetrievePresenter's type=custom lookup matches TaskQueue.name exactly (no prefix, unlike type=task). Seeded queue names already satisfy this; the earlier 404 was the wrong-env queue, now fixed. Documents the contract so future scenarios don't regress it.
Add client-side deadlines for the first stream event (45s) and a single pending tool call (120s), surfacing the existing turn-error callout instead of hanging forever. Wrap the chat-creation head-start and session-start awaits with a 20s timeout so a stuck trigger fails the request instead of hanging it.
Gate the tool-pending deadline by turn status so a dangling tool part on an idle chat never arms. Reset both deadlines' keys on retry and dismiss so a re-fired condition re-arms. Reword the route comment for the timeout path. Add wiring tests for both fixes.
A retry can resend under status "submitted" again, the same value the failed turn already left it in, so the first-event effect never re-ran. Bump an attempt counter in retry/dismissError to force it. Reword the deadline tests to state they prove the extracted predicate and reset, not full component wiring.
Only retry bumps the attempt counter; dismiss just stops the callout. Drop the inert eslint-disable, keep the explanatory comment.
…nment Add a per-call project/environment override on list_runs, get_run, get_run_trace, get_error, and get_queue, so a not-found lookup can be retried elsewhere in the org. The env-JWT exchange targets and caches per override; the default (no-override) path is unchanged. System prompt gains the grounding rule to retry and name where it was found.
…r scoping Resolve the list_projects prompt contradiction, drop preview from the environment override (branches aren't targetable that way), and make envUnavailableError name the overridden project/environment instead of "the current environment". Add a branch-retention test for the default (no-override) path.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx (1)
12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the unnecessary
useMemo.
effectiveErroris a small derived value passed directly toDashboardAgentMessages. It is not a context value, an expensive computation, or a stable dependency reference.Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3276f8a7-c01a-494c-a043-87c6cdf4f8cc
⛔ Files ignored due to path filters (1)
internal-packages/dashboard-agent/src/__snapshots__/prompt-prefix.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (11)
.server-changes/dashboard-agent-bounded-wait-errors.mdapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsxapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/utils/withTimeout.server.tsinternal-packages/dashboard-agent/src/tool-api-client.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.tsinternal-packages/dashboard-agent/src/tool-api.tsinternal-packages/dashboard-agent/src/tool-schemas.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (22)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: internal / 🧪 Unit Tests: Internal
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (14)
Use Remix flat-file route conventions with dot-separated segments; for example, `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.ts
Never use `request.signal` to detect client disconnects. Use `getRequestAbortSignal()` from `app/services/httpAsyncStorage.server.ts`, which is wired to Express response close events.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/utils/withTimeout.server.test.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.ts
Test files must not import `app/env.server.ts`; pass configuration as options instead.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/utils/withTimeout.server.test.ts
For dashboard changes, visually verify the running Remix app with Chrome DevTools MCP, using snapshots, screenshots, interaction, and console-message checks as appropriate.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsinternal-packages/dashboard-agent/src/tool-api-client.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsxinternal-packages/dashboard-agent/src/tool-schemas.ts
Add crumbs as you write code — not just when debugging. Mark lines with
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsinternal-packages/dashboard-agent/src/tool-api-client.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsxinternal-packages/dashboard-agent/src/tool-schemas.ts
Use zod for validation in packages/core and apps/webapp
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx
Do not import `env.server.ts` directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/utils/withTimeout.server.test.ts
Access environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env`
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/app/utils/withTimeout.server.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx
Use vitest for all tests in the Trigger.dev repository
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/utils/withTimeout.server.test.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.ts
Use function declarations instead of default exports
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsinternal-packages/dashboard-agent/src/tool-api-client.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsxinternal-packages/dashboard-agent/src/tool-schemas.ts
Use types over interfaces for TypeScript
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/app/utils/withTimeout.server.tsinternal-packages/dashboard-agent/src/tool-api-client.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.tsinternal-packages/dashboard-agent/src/tool-api.tsapps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsxinternal-packages/dashboard-agent/src/tool-schemas.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
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
Files:
apps/webapp/app/utils/withTimeout.server.tsinternal-packages/dashboard-agent/src/tool-api-client.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsapps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboard-agent.tsapps/webapp/app/utils/withTimeout.server.test.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.tsinternal-packages/dashboard-agent/src/tool-api-cross-project.test.tsinternal-packages/dashboard-agent/src/tool-api.tsinternal-packages/dashboard-agent/src/tool-schemas.ts
🧠 Learnings (4)
📚 Learning: 2026-07-26T13:14:02.968Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4378
File: .server-changes/realtime-run-reads-from-primary.md:0-0
Timestamp: 2026-07-26T13:14:02.968Z
Learning: For files in the .server-changes directory, the body text is published verbatim as dashboard-facing user release notes. Write entries in terms of user-visible behavior (what users can do/see), and avoid implementation-oriented details such as environment-variable names, internal mechanisms, or configuration knobs. If you need to include operational/configuration specifics, put those details in the PR description instead of the .server-changes entry.
Applied to files:
.server-changes/dashboard-agent-bounded-wait-errors.md
📚 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:
internal-packages/dashboard-agent/src/tool-api-client.tsapps/webapp/app/components/dashboard-agent/turn-deadlines.ts
📚 Learning: 2026-06-16T09:19:47.637Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3960
File: apps/webapp/test/prismaInfrastructureErrorCapture.test.ts:0-0
Timestamp: 2026-06-16T09:19:47.637Z
Learning: In this repo’s Vitest setup, `vitest.config.ts` uses `globals: true`, so identifiers like `vi`, `describe`, `it`, and `expect` are available as globals in Vitest test files. During code review, do not flag missing `vi`/`describe`/`it`/`expect` imports as a runtime error or correctness issue when they’re used in `*.test.ts/tsx` or `*.spec.ts/tsx` files. Explicit imports are still preferred for consistency, but they’re not required for runtime behavior.
Applied to files:
internal-packages/dashboard-agent/src/tool-api-cross-project.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/dashboard-agent/src/tool-api-cross-project.test.ts
| toolPendingDeadline.sync(activeToolPendingKey(status, inFlightToolName(messages))); | ||
| }, [messages, status, toolPendingDeadline]); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
printf '%s\n' '--- changed component ---'
sed -n '1,330p' apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx
printf '%s\n' '--- direct dependency ---'
sed -n '70,125p' apps/webapp/app/components/dashboard-agent/progress-line.ts
printf '%s\n' '--- related symbols ---'
rg -n -C 5 'activeToolPendingKey|toolPendingDeadline|inFlightToolName|function use.*Deadline|sync\(' apps/webapp/app/components/dashboard-agentRepository: triggerdotdev/trigger.dev
Length of output: 50381
🏁 Script executed:
#!/bin/bash
printf '%s\n' '--- deadline implementation ---'
cat -n apps/webapp/app/components/dashboard-agent/turn-deadlines.ts
printf '%s\n' '--- progress definitions and tool states ---'
sed -n '1,115p' apps/webapp/app/components/dashboard-agent/progress-line.ts
printf '%s\n' '--- deadline tests around key behavior ---'
sed -n '45,100p' apps/webapp/app/components/dashboard-agent/turn-deadlines.test.tsRepository: triggerdotdev/trigger.dev
Length of output: 9983
Use a per-invocation key for the tool deadline.
inFlightToolName(messages) returns only the tool name, and createKeyedDeadline.sync() ignores an unchanged key. Consecutive calls to the same tool can therefore reuse the previous timer and show an early or stale timeout. Include a stable tool-call identifier in the deadline key and add a regression test without an intermediate null key.
| beforeEach(() => { | ||
| vi.useFakeTimers(); | ||
| }); | ||
| afterEach(() => { | ||
| vi.useRealTimers(); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Remove fake timers from these tests.
vi.useFakeTimers() mocks the timer runtime. The test policy prohibits mocks. Use short real deadlines and real timer scheduling instead.
As per coding guidelines: “We use vitest exclusively. Never mock anything.”
Also applies to: 132-137
Source: Coding guidelines
| function getEnvJwt(refresh = false, target?: ApiTarget): Promise<EnvJwt> { | ||
| // An override drops the branch: it names another project/environment, which the | ||
| // current branch can't be assumed to apply to. A field left off the override still | ||
| // falls back to ctx's own value. | ||
| const ref = target?.projectRef ?? projectRef; | ||
| const env = target?.environmentName ?? environmentName; | ||
| const branch = target ? undefined : environmentBranch; | ||
| if (!hasAuth || !ref || !env) return Promise.resolve(MISSING_ENV); | ||
| const key = `${ref}/${env}/${branch ?? ""}`; | ||
| if (refresh) envJwts.delete(key); | ||
| let pending = envJwts.get(key); | ||
| if (!pending) { | ||
| // A failed exchange is not cached: a 403 or a 5xx would otherwise pin the whole turn. | ||
| pending = exchangeEnvJwt( | ||
| origin, | ||
| userActorToken!, | ||
| projectRef, | ||
| environmentName, | ||
| environmentBranch | ||
| ).then((result) => { | ||
| pending = exchangeEnvJwt(origin, userActorToken!, ref, env, branch).then((result) => { |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add @crumbs markers to the newly added paths.
Mark the cross-target resolution and forwarding logic, override-field behavior, deadline lifecycle, displayed deadline errors, startup timeout cleanup, and timeout settlement with // @crumbs`` or an approved @crumbs region.
📍 Affects 2 files
internal-packages/dashboard-agent/src/tool-api-client.ts#L145-L158(this comment)apps/webapp/app/components/dashboard-agent/turn-deadlines.ts#L58-L91
Source: Coding guidelines
| createdAt: r.createdAt, | ||
| startedAt: r.startedAt, | ||
| finishedAt: r.finishedAt, | ||
| wait: computeRunWait(r), | ||
| durationMs: r.durationMs, | ||
| tags: r.tags, | ||
| })), |
There was a problem hiding this comment.
🔍 Run wait measured differently between get_run and list_runs
computeRunWait measures from queuedAt only when queueWaitReliable === true. ApiRetrieveRunPresenter.createCommonRunStructure emits that field, but the list-runs payload does not, so curateRuns list items always fall back to measuredFrom: "created". The same run can therefore show a different wait label via list_runs than via get_run.
(Refers to this code)
Was this helpful? React with 👍 or 👎 to provide feedback.
The not-found fallback only checked siblings' same-name env, then told the user to check the environment switcher for scopes the agent can already read. Now it also sweeps the current project's other environments and each sibling's matching one, and the final wording names exactly what was checked instead of hedging.
The not-found rule could be satisfied by a past turn's sweep restated as if done now. Require scopes named to be checked THIS turn, with a past sweep cited as past. Trimmed verbose passages elsewhere in the prompt and the shared project/environment describes to hold budget.
…ing line The post-verdict close only banned list-form repeats of remediation/ checkNext, so a reworded restatement of the card's findings still passed. Now it must contain only what the card doesn't. Also fixes projectOverrideField's describe, broken by an earlier trim.
…e only concluded/inconclusive instructions describe CARD content and were read as contradicting the closing-line ban; prefix them "On the card:". Also restores the antecedent sentence an earlier trim dropped.
There was a problem hiding this comment.
🔍 Bounded-wait retry can resend during a live turn
effectiveError in DashboardAgentChat.tsx surfaces a deadline error while the SDK status is still submitted/streaming and error is undefined. The retry callout then calls retry(), which can sendMessage/regenerate while the previous turn's stream is still open server-side. This is the intended bounded-wait behavior, but overlapping sends depend on how useChat handles a resend mid-turn; worth confirming it does not duplicate turns.
(Refers to this code)
Was this helpful? React with 👍 or 👎 to provide feedback.
| public async slotHoldersOfQueue( | ||
| env: MinimalAuthenticatedEnvironment, | ||
| queue: string, | ||
| options?: { limit?: number } | ||
| ): Promise<QueueSlotHolders> { | ||
| const limit = options?.limit ?? DEFAULT_SLOT_HOLDER_LIMIT; | ||
| const baseQueueKey = this.keys.queueKey(env, queue); | ||
|
|
||
| const [admittedCount, dequeuedCount, runningReported, orphanCount, truncated, rawHolders] = | ||
| await this.redis.slotHoldersOfQueue( | ||
| baseQueueKey, | ||
| this.keys.ckIndexKeyFromQueue(baseQueueKey), | ||
| this.keys.queueRunningCounterKey(env, queue), | ||
| this.options.redis.keyPrefix ?? "", | ||
| String(limit) | ||
| ); | ||
|
|
||
| const holders = rawHolders.map(([runId, variant, phase]) => ({ | ||
| runId, | ||
| concurrencyKey: variant ? (this.#concurrencyKeyFromQueue(variant) ?? null) : null, | ||
| phase: phase === "dequeued" ? ("dequeued" as const) : ("admitted" as const), | ||
| })); | ||
|
|
||
| return { | ||
| holders, | ||
| admittedCount, | ||
| dequeuedCount, | ||
| runningReported, | ||
| truncated: truncated === 1, | ||
| unlistedRunning: Math.max(0, runningReported - dequeuedCount), | ||
| consistency: | ||
| dequeuedCount === runningReported && orphanCount === 0 ? "consistent" : "mismatch", | ||
| }; | ||
| } |
There was a problem hiding this comment.
🔍 Queue retrieve enumerates every concurrency-key variant per call
slotHoldersOfQueue (index.ts) runs a Lua script doing ZRANGE ckIndexKey 0 -1 plus two SMEMBERS per backlogged concurrency-key variant, now invoked on every /api/v1/queues/:queue retrieve via QueueRetrievePresenter.#slotHolders. The holder list caps at 20, but the counts force full enumeration, so a queue with many concurrency-key variants triggers an O(N) blocking Lua on single-threaded Redis. Existing concurrencyKeyBreakdown uses a similar scan, so this may be acceptable, but it is worth confirming for high-cardinality CK queues.
Was this helpful? React with 👍 or 👎 to provide feedback.
…omaly Diagnostic/causal questions always get the investigation card, even when the verdict is healthy (concluded, severity info, no remediation) — lookups/navigation/how-to never do. The card schema already supports a healthy verdict without remediation, so no gap. Trimmed other bullets to hold the budget.
…n collisions Health questions route through get_report as the gather step, so the ALWAYS-investigate rule no longer duplicates that procedure. concluded now names the healthy-verdict case explicitly, plus an overclaim guard. Trimmed several unrelated bullets to hold the prompt budget.
| await withTimeout( | ||
| startDashboardAgentHeadStart({ | ||
| chatId, | ||
| messages: [firstMessage], | ||
| mode: repoSnapshot ? "code" : "assistant", | ||
| metadata: headStartMetadata, | ||
| }), | ||
| CHAT_CREATE_TIMEOUT_MS, | ||
| "Dashboard agent head start" | ||
| ); | ||
| } else { | ||
| // Cold start: the client sends the first message through the `in` proxy, which | ||
| // injects the token. | ||
| // Same server-owned identity the head-start path injects; the `in` proxy adds the | ||
| // delegated token on the first turn. | ||
| await startDashboardAgentSession({ | ||
| chatId, | ||
| clientData: { | ||
| ...clientContext, | ||
| organizationId: project.organizationId, | ||
| userId, | ||
| projectId: project.id, | ||
| environmentId: runtimeEnv.id, | ||
| ...environmentAddress, | ||
| }, | ||
| }); | ||
| await withTimeout( | ||
| startDashboardAgentSession({ | ||
| chatId, | ||
| clientData: { | ||
| ...clientContext, | ||
| organizationId: project.organizationId, | ||
| userId, | ||
| projectId: project.id, | ||
| environmentId: runtimeEnv.id, | ||
| ...environmentAddress, | ||
| }, | ||
| }), | ||
| CHAT_CREATE_TIMEOUT_MS, | ||
| "Dashboard agent session start" | ||
| ); | ||
| } | ||
| } catch (error) { | ||
| // Both starts are one create-session-and-trigger round trip, so a rejection means no | ||
| // handover was dispatched and no message was sent: a session the call did create in | ||
| // spite of the error idles out having done nothing. The empty row is all there is to undo. | ||
| // Swallowed so the start's own error is what surfaces and gets logged. | ||
| // A rejection usually means nothing was dispatched, except `withTimeout`'s trigger | ||
| // can still land after the soft-delete below; the `in` proxy just treats it as missing. | ||
| await softDeleteChat(dashboardAgentDb, { |
There was a problem hiding this comment.
🔍 Head-start timeout skips quota accounting for a message that may still send
When withTimeout around startDashboardAgentHeadStart fires, the chat is soft-deleted and the error rethrown before recordAgentMessageSent runs. The code comment notes the trigger can still land after the soft-delete, so a head-started first message can reach the agent while never counting against the org's message quota.
Was this helpful? React with 👍 or 👎 to provide feedback.
Grounds the dashboard agent's answers in deterministic platform facts instead of LLM inference, and widens its token to the organization.