Skip to content

feat(mcp): add get_span_details tool#3255

Merged
ericallam merged 5 commits intomainfrom
feat/mcp-get-span-details
Mar 24, 2026
Merged

feat(mcp): add get_span_details tool#3255
ericallam merged 5 commits intomainfrom
feat/mcp-get-span-details

Conversation

@ericallam
Copy link
Member

Returns the fully detailed span with attributes and AI enrichment data

Returns the fully detailed span with attributes and AI enrichment data
@changeset-bot
Copy link

changeset-bot bot commented Mar 24, 2026

🦋 Changeset detected

Latest commit: 6d9e386

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

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

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

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

devin-ai-integration[bot]

This comment was marked as resolved.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new API endpoint GET /api/v1/runs/:runId/spans/:spanId (Remix loader) that validates path params, enforces JWT auth and scoped authorization, resolves the run, selects the event repository/table, and returns a detailed span payload (core identifiers/status, timing with durationMs, optional properties/events/entityType, AI enrichment fields, and triggered child runs) or HTTP 404. Adds RetrieveSpanDetailResponseBody Zod schema and ApiClient.retrieveSpan(). Adds MCP tool get_span_details with input schema, formatter, and handler; updates CLI formatters and schemas to render and validate span detail output.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is minimal and incomplete. It lacks required sections from the template including issue reference, checklist, testing steps, and detailed changelog information. Add the missing template sections: issue reference (Closes #...), completed checklist items, testing steps, and more detailed changelog describing the new endpoint and tool.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a new MCP tool called get_span_details, which aligns with the substantial implementation across multiple files.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mcp-get-span-details

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts (1)

67-79: Add orderBy for deterministic pagination.

The query limits results to 50 but lacks an orderBy clause. If more than 50 triggered runs exist for this span, the returned subset will be non-deterministic across calls.

♻️ Suggested fix
     const triggeredRuns = await $replica.taskRun.findMany({
       take: 50,
+      orderBy: { createdAt: "asc" },
       select: {
         friendlyId: true,
         taskIdentifier: true,
         status: true,
         createdAt: true,
       },
       where: {
         runtimeEnvironmentId: authentication.environment.id,
         parentSpanId: params.spanId,
       },
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/routes/api.v1.runs`.$runId.spans.$spanId.ts around lines 67 -
79, The findMany query that populates triggeredRuns (call to
$replica.taskRun.findMany) limits to 50 but has no orderBy, causing
non-deterministic pagination; add an explicit orderBy (e.g., { createdAt: 'desc'
} or another deterministic key like friendlyId) to the query so results are
returned in a stable order when using take: 50 and when filtering by
parentSpanId and authentication.environment.id.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/webapp/app/routes/api.v1.runs`.$runId.spans.$spanId.ts:
- Around line 67-79: The findMany query that populates triggeredRuns (call to
$replica.taskRun.findMany) limits to 50 but has no orderBy, causing
non-deterministic pagination; add an explicit orderBy (e.g., { createdAt: 'desc'
} or another deterministic key like friendlyId) to the query so results are
returned in a stable order when using take: 50 and when filtering by
parentSpanId and authentication.environment.id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8aa5219a-04b0-4b2d-9673-8a1e174db16d

📥 Commits

Reviewing files that changed from the base of the PR and between c2fdff7 and 922c34f.

📒 Files selected for processing (1)
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx}

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

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/app/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
**/*.ts

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/app/**/*.{ts,tsx,server.ts}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/app/routes/**/*.ts

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

Use Remix flat-file route convention with dot-separated segments where api.v1.tasks.$taskId.trigger.ts maps to /api/v1/tasks/:taskId/trigger

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
🧠 Learnings (17)
📚 Learning: 2026-03-23T06:24:25.016Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.016Z
Learning: Applies to apps/webapp/app/routes/**/*.ts : Use Remix flat-file route convention with dot-separated segments where `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-24T10:42:41.009Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3255
File: apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts:100-100
Timestamp: 2026-03-24T10:42:41.009Z
Learning: In `apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts` (and related span-handling code in trigger.dev), `span.entity` is a required (non-optional) field on the `SpanDetail` type and is always present. Do not flag `span.entity.type` as a potential null pointer / suggest optional chaining (`span.entity?.type`) in this context.

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-13T13:43:06.471Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/components/runs/v3/ai/extractAISpanData.ts:52-52
Timestamp: 2026-03-13T13:43:06.471Z
Learning: In the trigger.dev codebase (PR `#3213`), `extractAISpanData.ts` (`apps/webapp/app/components/runs/v3/ai/extractAISpanData.ts`) is a read-side UI helper that reads already-enriched `trigger.llm.*` span attributes for display. The actual LLM cost computation and gateway/OpenRouter cost fallback logic lives in `enrichCreatableEvents.server.ts` (`apps/webapp/app/v3/utils/enrichCreatableEvents.server.ts`) via `extractProviderCost()`. The `gatewayCost` parsed in `extractAISpanData` is for UI display purposes only, not for cost calculation.

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-21T21:23:31.557Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts:149-150
Timestamp: 2026-03-21T21:23:31.557Z
Learning: In `apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts` (and related AI span extraction files in `apps/webapp/app/components/runs/v3/ai/`), manual JSON.parse with typeof guards and type assertions is intentional. These functions are on the hot path for span rendering, so Zod validation is deliberately avoided for performance reasons. Do not suggest replacing manual JSON parsing with Zod schemas in these files.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T13:50:18.205Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts:15-34
Timestamp: 2026-03-22T13:50:18.205Z
Learning: In `apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts`, the `rec()` helper always returns `{}` for non-object values and `str()` returns `undefined` for non-strings. The truthiness guard on `ai.operationId` (Line 15) is intentionally loose — a non-string truthy value simply results in `str()` returning `undefined` and `operationName` defaulting to `""`. The real early-exit guard is the model check (`if (!model) return undefined`). An empty `operationName` is considered harmless in this code path.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T13:51:20.866Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/presenters/v3/PromptPresenter.server.ts:100-141
Timestamp: 2026-03-22T13:51:20.866Z
Learning: In the triggerdotdev/trigger.dev codebase, the ClickHouse server is configured with UTC timezone. Therefore, `toStartOfHour(start_time)` (without an explicit timezone argument) in ClickHouse queries returns UTC-formatted strings, which correctly align with JavaScript `toISOString()`-derived UTC bucket keys. Do not flag this pattern as a timezone mismatch bug.

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T19:32:14.433Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx:82-151
Timestamp: 2026-03-22T19:32:14.433Z
Learning: In `apps/webapp/app/v3/services/alerts/createAlertChannel.server.ts` and `apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx`, the `errorAlertConfig` field on `ProjectAlertChannel` is intentionally `Json?` (nullable). The `ErrorAlertEvaluator.computeMinInterval()` in `apps/webapp/app/v3/services/alerts/errorAlertEvaluator.server.ts` uses `ErrorAlertConfig.safeParse(ch.errorAlertConfig)` and falls back to `DEFAULT_INTERVAL_MS = 300_000` when `errorAlertConfig` is null. No UI currently collects this value — it is scaffolding for a future per-channel evaluation interval feature. Do not flag the absence of `errorAlertConfig` in `CreateAlertChannelOptions` or the action handler as a bug; null configs are safe and expected.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-23T06:24:14.555Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.555Z
Learning: Do not flag `// crumbs` markers, `#region crumbs` blocks, or agentcrumbs imports in PR reviews - these are temporary debug instrumentation stripped before merge

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-07-12T18:06:04.133Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2264
File: apps/webapp/app/services/runsRepository.server.ts:172-174
Timestamp: 2025-07-12T18:06:04.133Z
Learning: In apps/webapp/app/services/runsRepository.server.ts, the in-memory status filtering after fetching runs from Prisma is intentionally used as a workaround for ClickHouse data delays. This approach is acceptable because the result set is limited to a maximum of 100 runs due to pagination, making the performance impact negligible.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
🔇 Additional comments (4)
apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts (4)

1-13: LGTM!

Imports correctly use subpath export from @trigger.dev/core/v3/isomorphic as per guidelines, and parameter validation uses Zod appropriately.


15-39: LGTM!

Route configuration properly scopes findResource to the authenticated environment and sets up appropriate authorization with read action and super-scopes.


40-60: LGTM!

Duration conversion correctly handles the difference between Postgres (returns ms) and ClickHouse (returns nanoseconds). The context snippets from eventRepository.server.ts and clickhouseEventRepository.server.ts confirm this behavior.


81-135: LGTM!

Response construction cleanly omits empty/undefined fields. The direct access to span.entity.type on line 102 is correct since entity is a required field on SpanDetail. Based on learnings from previous review.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Member

@matt-aitken matt-aitken left a comment

Choose a reason for hiding this comment

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

@ericallam a couple of outstanding devin review comments worth looking at

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts (2)

65-77: Consider adding ordering to the triggered runs query.

The query lacks an orderBy clause, so result order is undefined and may vary between calls. Adding orderBy: { createdAt: "desc" } would provide consistent, predictable results.

📝 Suggested improvement
     const triggeredRuns = await $replica.taskRun.findMany({
       take: 50,
       select: {
         friendlyId: true,
         taskIdentifier: true,
         status: true,
         createdAt: true,
       },
+      orderBy: { createdAt: "desc" },
       where: {
         runtimeEnvironmentId: authentication.environment.id,
         parentSpanId: params.spanId,
       },
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/routes/api.v1.runs`.$runId.spans.$spanId.ts around lines 65 -
77, The query that populates triggeredRuns using $replica.taskRun.findMany has
no ordering and returns results in an undefined order; update the findMany call
for triggeredRuns to include an orderBy clause (e.g., orderBy: { createdAt:
"desc" }) so results are deterministic and newest runs appear first, ensuring
you modify the options passed to $replica.taskRun.findMany where triggeredRuns
is defined.

57-58: Duration unit assumption documented but carries risk for Postgres store users.

The comment correctly notes this assumes nanoseconds from ClickHouse. Per context snippet 1, the Postgres EventRepository.getSpan returns duration already converted to milliseconds via nanosecondsToMilliseconds(). If any environments still use the Postgres store, durationMs would be off by a factor of 1,000,000.

Given the Postgres store is deprecated per commit message, this is acceptable, but consider adding a more prominent warning or removing Postgres store support entirely if it's no longer tested.

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

In `@apps/webapp/app/routes/api.v1.runs`.$runId.spans.$spanId.ts around lines 57 -
58, The code assumes span.duration is nanoseconds and divides by 1_000_000 to
get durationMs, but Postgres-backed EventRepository.getSpan already converts to
milliseconds via nanosecondsToMilliseconds(), risking a 1,000x error; update the
data contract so EventRepository.getSpan always returns milliseconds (apply
nanosecondsToMilliseconds inside EventRepository.getSpan for ClickHouse paths)
and then simplify the route to treat span.duration as milliseconds (remove the
nanoseconds conversion) and update the comment accordingly; reference
EventRepository.getSpan, nanosecondsToMilliseconds, and the
span.duration/durationMs variable to locate the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/webapp/app/routes/api.v1.runs`.$runId.spans.$spanId.ts:
- Around line 65-77: The query that populates triggeredRuns using
$replica.taskRun.findMany has no ordering and returns results in an undefined
order; update the findMany call for triggeredRuns to include an orderBy clause
(e.g., orderBy: { createdAt: "desc" }) so results are deterministic and newest
runs appear first, ensuring you modify the options passed to
$replica.taskRun.findMany where triggeredRuns is defined.
- Around line 57-58: The code assumes span.duration is nanoseconds and divides
by 1_000_000 to get durationMs, but Postgres-backed EventRepository.getSpan
already converts to milliseconds via nanosecondsToMilliseconds(), risking a
1,000x error; update the data contract so EventRepository.getSpan always returns
milliseconds (apply nanosecondsToMilliseconds inside EventRepository.getSpan for
ClickHouse paths) and then simplify the route to treat span.duration as
milliseconds (remove the nanoseconds conversion) and update the comment
accordingly; reference EventRepository.getSpan, nanosecondsToMilliseconds, and
the span.duration/durationMs variable to locate the changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0b656882-d7dd-448c-b8ab-8bf900df8b6b

📥 Commits

Reviewing files that changed from the base of the PR and between 9a5e7c0 and 6d9e386.

📒 Files selected for processing (2)
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
  • packages/cli-v3/src/mcp/formatters.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (28)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx}

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

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

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

Files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

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

Files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
**/*.ts

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

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

Files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
packages/cli-v3/src/mcp/**/*

📄 CodeRabbit inference engine (packages/cli-v3/CLAUDE.md)

Provide an MCP server implementation in src/mcp/ for AI-assisted task development

Files:

  • packages/cli-v3/src/mcp/formatters.ts
packages/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • packages/cli-v3/src/mcp/formatters.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/app/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/app/**/*.{ts,tsx,server.ts}

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

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

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
apps/webapp/app/routes/**/*.ts

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

Use Remix flat-file route convention with dot-separated segments where api.v1.tasks.$taskId.trigger.ts maps to /api/v1/tasks/:taskId/trigger

Files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
🧠 Learnings (21)
📓 Common learnings
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3255
File: apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts:100-100
Timestamp: 2026-03-24T10:42:41.009Z
Learning: In `apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts` (and related span-handling code in trigger.dev), `span.entity` is a required (non-optional) field on the `SpanDetail` type and is always present. Do not flag `span.entity.type` as a potential null pointer / suggest optional chaining (`span.entity?.type`) in this context.
📚 Learning: 2026-03-24T10:42:41.009Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3255
File: apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts:100-100
Timestamp: 2026-03-24T10:42:41.009Z
Learning: In `apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts` (and related span-handling code in trigger.dev), `span.entity` is a required (non-optional) field on the `SpanDetail` type and is always present. Do not flag `span.entity.type` as a potential null pointer / suggest optional chaining (`span.entity?.type`) in this context.

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T13:51:20.866Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/presenters/v3/PromptPresenter.server.ts:100-141
Timestamp: 2026-03-22T13:51:20.866Z
Learning: In the triggerdotdev/trigger.dev codebase, the ClickHouse server is configured with UTC timezone. Therefore, `toStartOfHour(start_time)` (without an explicit timezone argument) in ClickHouse queries returns UTC-formatted strings, which correctly align with JavaScript `toISOString()`-derived UTC bucket keys. Do not flag this pattern as a timezone mismatch bug.

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T13:51:20.866Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/presenters/v3/PromptPresenter.server.ts:100-141
Timestamp: 2026-03-22T13:51:20.866Z
Learning: In the triggerdotdev/trigger.dev codebase, the ClickHouse server is configured with UTC as its timezone. Therefore, `toStartOfHour(start_time)` (without an explicit timezone argument) in ClickHouse queries correctly returns UTC-formatted strings that align with JavaScript `toISOString()`-derived UTC bucket keys (e.g., in `apps/webapp/app/presenters/v3/PromptPresenter.server.ts`). Do not flag this as a timezone mismatch bug.

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Limit task duration using the `maxDuration` property (in seconds)

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
📚 Learning: 2026-03-13T13:43:06.471Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/components/runs/v3/ai/extractAISpanData.ts:52-52
Timestamp: 2026-03-13T13:43:06.471Z
Learning: In the trigger.dev codebase (PR `#3213`), `extractAISpanData.ts` (`apps/webapp/app/components/runs/v3/ai/extractAISpanData.ts`) is a read-side UI helper that reads already-enriched `trigger.llm.*` span attributes for display. The actual LLM cost computation and gateway/OpenRouter cost fallback logic lives in `enrichCreatableEvents.server.ts` (`apps/webapp/app/v3/utils/enrichCreatableEvents.server.ts`) via `extractProviderCost()`. The `gatewayCost` parsed in `extractAISpanData` is for UI display purposes only, not for cost calculation.

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-09-27T21:10:34.211Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2567
File: internal-packages/clickhouse/src/taskEvents.ts:15-16
Timestamp: 2025-09-27T21:10:34.211Z
Learning: In the ClickHouse implementation, parent_span_id is stored as an empty string ("") for root spans, not NULL. This means z.string() validation is correct and doesn't need to be nullable.

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

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

Applied to files:

  • packages/cli-v3/src/mcp/formatters.ts
  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-23T06:24:25.016Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:25.016Z
Learning: Applies to apps/webapp/app/routes/**/*.ts : Use Remix flat-file route convention with dot-separated segments where `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-21T21:23:31.557Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts:149-150
Timestamp: 2026-03-21T21:23:31.557Z
Learning: In `apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts` (and related AI span extraction files in `apps/webapp/app/components/runs/v3/ai/`), manual JSON.parse with typeof guards and type assertions is intentional. These functions are on the hot path for span rendering, so Zod validation is deliberately avoided for performance reasons. Do not suggest replacing manual JSON parsing with Zod schemas in these files.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T13:50:18.205Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts:15-34
Timestamp: 2026-03-22T13:50:18.205Z
Learning: In `apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts`, the `rec()` helper always returns `{}` for non-object values and `str()` returns `undefined` for non-strings. The truthiness guard on `ai.operationId` (Line 15) is intentionally loose — a non-string truthy value simply results in `str()` returning `undefined` and `operationName` defaulting to `""`. The real early-exit guard is the model check (`if (!model) return undefined`). An empty `operationName` is considered harmless in this code path.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-22T19:32:14.433Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx:82-151
Timestamp: 2026-03-22T19:32:14.433Z
Learning: In `apps/webapp/app/v3/services/alerts/createAlertChannel.server.ts` and `apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx`, the `errorAlertConfig` field on `ProjectAlertChannel` is intentionally `Json?` (nullable). The `ErrorAlertEvaluator.computeMinInterval()` in `apps/webapp/app/v3/services/alerts/errorAlertEvaluator.server.ts` uses `ErrorAlertConfig.safeParse(ch.errorAlertConfig)` and falls back to `DEFAULT_INTERVAL_MS = 300_000` when `errorAlertConfig` is null. No UI currently collects this value — it is scaffolding for a future per-channel evaluation interval feature. Do not flag the absence of `errorAlertConfig` in `CreateAlertChannelOptions` or the action handler as a bug; null configs are safe and expected.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-03-23T06:24:14.555Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.555Z
Learning: Do not flag `// crumbs` markers, `#region crumbs` blocks, or agentcrumbs imports in PR reviews - these are temporary debug instrumentation stripped before merge

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
📚 Learning: 2025-07-12T18:06:04.133Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2264
File: apps/webapp/app/services/runsRepository.server.ts:172-174
Timestamp: 2025-07-12T18:06:04.133Z
Learning: In apps/webapp/app/services/runsRepository.server.ts, the in-memory status filtering after fetching runs from Prisma is intentionally used as a workaround for ClickHouse data delays. This approach is acceptable because the result set is limited to a maximum of 100 runs due to pagination, making the performance impact negligible.

Applied to files:

  • apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts
🔇 Additional comments (4)
apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts (2)

15-38: LGTM on the authorization setup.

The JWT authentication, CORS strategy, resource finding with environment scoping, and authorization with appropriate action and superScopes are all correctly implemented.


86-132: LGTM on the response payload construction.

The response correctly maps span data, conditionally includes optional fields (properties, events, entityType, ai, triggeredRuns), and the AI data selection matches the RetrieveSpanDetailResponseBody schema.

packages/cli-v3/src/mcp/formatters.ts (2)

239-243: LGTM on the duration and span ID changes.

The nanosecond-to-millisecond conversion is consistent with the API route's handling, and including the span ID in the header improves traceability.


465-554: Well-structured span detail formatter.

The function handles all optional fields correctly, formats AI details comprehensively, and provides appropriate truncation for events (10 max).

Minor observation: formatSpanDetail uses [IN PROGRESS] for partial spans (line 473) while getStatusIndicator uses [PARTIAL] (line 332). This is likely intentional for user-friendly MCP output, but worth noting for consistency if the terminology should be unified.

@ericallam ericallam merged commit c00dae0 into main Mar 24, 2026
46 checks passed
@ericallam ericallam deleted the feat/mcp-get-span-details branch March 24, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants