Skip to content

Conversation

@matt-aitken
Copy link
Member

@matt-aitken matt-aitken commented Jan 27, 2026

Summary

  • Query: add time limits, performance improvements, and styling updates

Changes

  • Add ClickHouse output_text and error_text columns with indexes
  • Automatically use _text columns for JSON based on query pattern; support JSON column data prefixes
  • Add idempotency key and scope columns
  • Add enforcedWhereClause for tenant and time restrictions, instead of the old tenant stuff.
  • Implement basic time filter limiting and set default time period based on plan; show message when results are clipped
  • UX: resizable code area (including vertical splits), collapsible sidebar, fix table/chart vertical sizing, max height for chart legend in fullscreen
  • Styling and UI tweaks: improved chart legend styling, more chart colours, thinner line chart stroke, pricing callout color, improved layout for callouts
  • Features: generate and save AI titles

Open with Devin

@changeset-bot
Copy link

changeset-bot bot commented Jan 27, 2026

⚠️ No Changeset found

Latest commit: 1376448

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

This PR includes no changesets

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

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

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

Walkthrough

The changes add structured enforcedWhereClause support across the TSQL library (new condition types, printer/context updates, tests) and update the ClickHouse executor API to accept enforcedWhereClause. They introduce textColumn and dataPrefix for JSON handling, adapt many tests, and change public types. Backend changes include executeQuery returning ExecuteQueryResult with queryId, AIQueryTitleService and an ai-title route, DB migration adding CustomerQuery.title and removing costInCents, schema updates, and environment var removal. Frontend updates add legendScrollable props, AnimatedNumber decimals, Resizable handle orientation, chart/table UI tweaks, time-filter maxPeriodDays enforcement, and title-generation wiring in the query UI.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is a broad, vague summary that references multiple unrelated areas (time limits, performance, styling) without clarifying the primary change or main objective of the changeset. Consider a more specific title focusing on the main change, such as 'Add time period limits and AI query titles with schema/UX improvements' or 'Implement query time restrictions with enforced where clauses and UI enhancements'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description provides a good high-level summary of changes but lacks some required template sections: missing issue reference (#), incomplete checklist, no testing details, and no screenshots where applicable.

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

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
internal-packages/clickhouse/src/tsql.test.ts (1)

952-952: Remove unused variable now.

The variable now is declared but never used in this test. The test only uses usage_duration_ms values without any timestamp-based fields.

🧹 Suggested fix
-    const now = Date.now();
-
     await insert([
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

458-461: Validation block may be unreachable.

Since the Apply button is disabled when exceedsMaxPeriod is true (line 837), this validation branch cannot be reached through normal UI interaction. Consider removing this check or keeping it only as a defensive guard without setting validationError (which would show redundantly alongside the Callout).

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a34c0b and 1376448.

📒 Files selected for processing (2)
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/clickhouse/src/tsql.test.ts
🧰 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}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • internal-packages/clickhouse/src/tsql.test.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • internal-packages/clickhouse/src/tsql.test.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
**/*.{test,spec}.{ts,tsx}

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

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/clickhouse/src/tsql.test.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:

  • internal-packages/clickhouse/src/tsql.test.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • internal-packages/clickhouse/src/tsql.test.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g., MyService.ts) → MyService.test.ts

Files:

  • internal-packages/clickhouse/src/tsql.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers helpers (redisTest, postgresTest, containerTest) from @internal/testcontainers for Redis/PostgreSQL testing instead of mocks

Files:

  • internal-packages/clickhouse/src/tsql.test.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/components/runs/v3/SharedFilters.tsx
apps/webapp/app/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
apps/webapp/**/*.{ts,tsx}

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

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

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
🧠 Learnings (4)
📚 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:

  • internal-packages/clickhouse/src/tsql.test.ts
📚 Learning: 2025-07-21T12:52:44.342Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2284
File: apps/webapp/app/services/realtimeClient.server.ts:111-127
Timestamp: 2025-07-21T12:52:44.342Z
Learning: Electric (the database service used in the realtimeClient) has built-in SQL injection protection and safely handles whereClause parameters passed via URL parameters, so direct string interpolation of runId values into SQL where clauses is safe when using Electric.

Applied to files:

  • internal-packages/clickhouse/src/tsql.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Set default maximum duration in trigger.config.ts using `maxDuration` property

Applied to files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 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:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
⏰ 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). (22)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 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 (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (10)
internal-packages/clickhouse/src/tsql.test.ts (5)

1-85: LGTM!

The imports, schema definitions, and test helpers are well-structured. The taskRunsSchema correctly defines the tenant column mappings, and the createTaskRun helper provides a clean way to create test data with sensible defaults.


109-113: LGTM!

The migration to enforcedWhereClause is consistently applied across all integration tests. The security tests (cross-tenant access prevention at lines 234-279 and SQL injection handling at lines 409-439) properly validate that tenant isolation is enforced regardless of malicious WHERE clauses.

Also applies to: 150-154, 204-208, 265-269, 301-305, 340-344, 364-368, 397-401, 427-431, 461-465, 492-496


557-560: LGTM!

The optional tenant filter tests correctly demonstrate the flexibility of enforcedWhereClause by omitting project_id and/or environment_id while maintaining organization-level isolation. The inline comments (e.g., // project_id and environment_id omitted - query across all) clearly document the intent of each test case.

Also applies to: 619-623, 680-683, 741-744, 788-791


878-882: LGTM!

The virtual column tests comprehensively validate computed/expression columns with various comparison operators and query patterns. The costExpressionSchema at lines 1079-1119 mirrors real-world patterns for cost calculations, making these tests valuable for regression detection.

Also applies to: 930-934, 978-982, 1022-1026, 1060-1064, 1159-1163, 1204-1208, 1245-1249, 1291-1295, 1339-1343, 1387-1391


1454-1458: LGTM!

The field mapping tests thoroughly validate the transformation of values between external references and internal IDs. The test at lines 1566-1613 correctly uses org-only filtering (enforcedWhereClause with just organization_id) to test field mappings across multiple projects.

Also applies to: 1497-1501, 1546-1550, 1597-1599

apps/webapp/app/components/runs/v3/SharedFilters.tsx (5)

799-808: LGTM!

Good UX pattern showing an upgrade callout with a direct billing link when the user's selection exceeds their plan's limits. The conditional rendering properly handles cases where organization context may not be available.


825-840: LGTM!

Disabling the Apply button when the selection exceeds the plan limit provides clear, immediate feedback that the action is blocked, complementing the upgrade callout above.


740-790: LGTM!

Good fix setting explicit end dates (endOfDay, endOfWeek, endOfMonth) for quick date selections instead of leaving toValue undefined. This ensures proper date range bounds and more predictable filtering behavior.


374-401: LGTM!

The SectionType union type follows the coding guideline preference for types over interfaces. The maxPeriodDays prop is well-documented with JSDoc and properly threaded through to TimeDropdown.


126-140: LGTM!

Clean utility functions for converting periods and date ranges to day counts. The dateRangeToDays function correctly calculates the span from a given date to now.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

529-540: Add missing dependencies to applySelection callback.
Line 529-540 references maxPeriodDays and exceedsMaxPeriod, but they’re missing from the dependency list, which can stale the validation/error message when props change.

🧩 Suggested fix
   }, [
     activeSection,
     selectedPeriod,
     isCustomDurationValid,
     customValue,
     customUnit,
     fromValue,
     toValue,
     replace,
     onApply,
     onValueChange,
+    maxPeriodDays,
+    exceedsMaxPeriod,
   ]);
🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/code/TSQLResultsTable.tsx`:
- Line 1142: The tbody in TSQLResultsTable uses "divide-y divide-charcoal-700"
which doesn't show dividers for absolutely positioned/virtualized rows; remove
those utilities from the tbody className and instead add "border-b
border-charcoal-700" to the element that renders each virtualized row (the
per-row container/render function used by the virtualizer — e.g., the row
renderer / row element inside the map or renderRow handler) so each row gets its
own bottom border; update any row className/props (not the tbody) to include the
border utilities and remove the divide-y/divide-charcoal-700 from the tbody.

In `@apps/webapp/app/components/primitives/AnimatedNumber.tsx`:
- Around line 22-47: The decimals value used in the useTransform callback must
be clamped and sanitized before being passed to toLocaleString to avoid runtime
throws from invalid values; update the AnimatedNumber component to compute a
safeDecimals (based on decimalPlaces or getDecimalPlaces(value)) by coercing to
a finite number, rounding to an integer, and clamping into the 0–20 range (e.g.,
use Number.isFinite, Math.round, Math.max/Math.min) and then use safeDecimals
inside the display transformer and any branch that checks for zero; ensure NaN,
negatives, non-integers and >20 are handled by this normalization so
toLocaleString always receives a valid 0–20 integer.

In `@apps/webapp/app/components/runs/v3/SharedFilters.tsx`:
- Around line 441-452: The exceedsMaxPeriod calculation only checks fromValue
for date ranges and thus misses cases where only toValue is set; update the
date-range branch in exceedsMaxPeriod to compute the earliest selected date (min
of fromValue and toValue) and compare its age via dateRangeToDays (or convert
the computed range to days) against maxPeriodDays so "to-only" selections that
exceed maxPeriodDays also return true; locate the exceedsMaxPeriod IIFE and
adjust the date range logic that references activeSection, fromValue, toValue,
dateRangeToDays, and maxPeriodDays accordingly.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx:
- Around line 70-76: The "guide" tab currently has double padding because
ClientTabsContent (value="guide") has className including "p-3" and its inner
div also has "p-3"; remove the outer padding to match the "ai" tab layout.
Locate the ClientTabsContent element with value="guide" and remove the "p-3"
from its className (leave the inner div wrapping TRQLGuideContent with its
"p-3"), ensuring visual alignment with the other tab; references:
ClientTabsContent and TRQLGuideContent.
- Around line 86-92: The "examples" tab duplicates padding because
ClientTabsContent has p-3 and the inner wrapper div also uses p-3; remove the
extra padding by deleting the p-3 on the inner div (the wrapper around
<ExamplesContent onTryExample={onTryExample} />) or adjust it to a neutral class
(e.g., remove padding or use p-0) so only ClientTabsContent provides the
padding; update the wrapper near ClientTabsContent/ExamplesContent to eliminate
the double padding.
- Around line 78-84: The "schema" tab shows double padding because both
ClientTabsContent (value="schema") and the inner div around TableSchemaContent
apply p-3; remove the redundant padding by deleting or changing the inner div's
padding class (the div wrapping TableSchemaContent) to p-0 (or remove p-3) so
only ClientTabsContent provides spacing; update the inner wrapper surrounding
TableSchemaContent to use no padding.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx:
- Around line 849-852: The className passed to ClientTabsContent contains a
typo: replace the incorrect "f-full" token with "h-full" so the height utility
is applied correctly; update the className string in the ClientTabsContent JSX
(the element with value="graph") to use "h-full" instead of "f-full".

In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx:
- Around line 21-27: Wrap the call to request.json() in a try-catch so malformed
JSON returns a 400 instead of throwing; specifically, around the code that calls
request.json() before you call RequestSchema.safeParse, catch any error from
JSON parsing and return json({ success: false, error: "Invalid JSON", title:
null }, { status: 400 }); keep the existing RequestSchema.safeParse logic for
validated bodies and reference the same submission variable and response shape.
- Around line 59-65: The update lets any queryId be changed without verifying it
belongs to the current project/environment; restrict the update by either (A)
replacing prisma.customerQuery.update with prisma.customerQuery.updateMany({
where: { id: queryId, projectId: <currentProjectId>, environmentId:
<currentEnvId> }, data: { title } }) and verifying the returned count > 0, or
(B) first fetch the record with prisma.customerQuery.findUnique/findFirst({
where: { id: queryId } }), confirm its project/environment matches the current
project/env variables, then call prisma.customerQuery.update({ where: { id:
queryId }, data: { title } }); use the existing symbols queryId, title,
prisma.customerQuery.update/updateMany and the current project/env identifiers
in the file to implement this check.

In `@apps/webapp/app/services/queryService.server.ts`:
- Around line 104-112: The tenant inputs (organizationId, projectId,
environmentId) and enforcedWhereClause passed into executeQuery can diverge and
cause mismatched attribution for history/concurrency; update the executeQuery
parameter handling to validate and normalize tenant context by deriving the
enforcedWhereClause and history/concurrency keys from the canonical
organizationId/projectId/environmentId (or reject when inconsistent).
Specifically, in executeQuery (and the similar block around the other usage at
the 153-156 zone) check that any provided enforcedWhereClause matches the
computed clause for the given organizationId/projectId/environmentId (or rebuild
it from those IDs), and use the same canonical IDs to compute history and
customOrgConcurrencyLimit keys so all tenant guards are constructed from the
same source of truth.

In `@internal-packages/tsql/src/query/printer.ts`:
- Around line 2259-2267: The rootColumnSchema lookup currently calls
resolveFieldToColumnSchema([node.chain[0]]) which fails for qualified references
like ['runs','error',...]; change the input to include the table/alias when
present (e.g., use node.chain.length > 1 ? [node.chain[0], node.chain[1]] :
[node.chain[0]]) so resolveFieldToColumnSchema sees the table+column for JSON
columns; this ensures rootColumnSchema is correctly detected (so the .String
type hint is applied when this.queryHasGroupBy &&
!this.isInWhereComparisonContext()).
- Around line 1669-1733: createEnforcedGuard currently builds unqualified guard
expressions which can bind to the wrong table in multi-join queries; update
createConditionExpression to accept an optional tableAlias parameter and, when
provided, construct the Field.chain as [tableAlias, column] (instead of just
[column]) so resolveFieldChain will resolve to the correct table; then call
createConditionExpression(column, condition, tableAlias) from
createEnforcedGuard's loop and ensure existing logic in
createConditionExpression (used for between expressions and CompareOperation
building with createValueExpression and mapConditionOpToCompareOp) uses the new
fieldExpr with the qualified chain.
🧹 Nitpick comments (7)
apps/webapp/app/components/primitives/Callout.tsx (1)

8-14: Remove unused ChartBarIcon import.

The ChartBarIcon import at line 11 is not used anywhere in this file. It was likely replaced by CreditCardIcon in the pricing variant (line 65).

🧹 Proposed cleanup
 import {
   ArrowTopRightOnSquareIcon,
   BookOpenIcon,
-  ChartBarIcon,
   CheckCircleIcon,
   ChevronRightIcon,
 } from "@heroicons/react/24/solid";
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

288-308: Prefer type aliases over interfaces for the TimeFilter types.
Line 288-308 uses interfaces, but the TS guideline prefers type aliases. Consider switching these to type for consistency.

♻️ Suggested refactor
-export interface TimeFilterApplyValues {
+export type TimeFilterApplyValues = {
   period?: string;
   from?: string;
   to?: string;
-}
+};

-export interface TimeFilterProps {
+export type TimeFilterProps = {
   defaultPeriod?: string;
   period?: string;
   from?: string;
   to?: string;
   /** Label name used in the filter display, defaults to "Created" */
   labelName?: string;
   hideLabel?: boolean;
   applyShortcut?: ShortcutDefinition | undefined;
   /** Callback when the user applies a time filter selection, receives the applied values */
   onValueChange?: (values: TimeFilterApplyValues) => void;
   /** When set an upgrade message will be shown if you select a period further back than this number of days */
   maxPeriodDays?: number;
-}
+};

As per coding guidelines, prefer types over interfaces.

apps/webapp/app/components/code/QueryResultsChart.tsx (1)

49-58: Prefer a type alias for QueryResultsChartProps.
This interface was modified; please switch it to a type to align with repo TS style.

♻️ Proposed change
-interface QueryResultsChartProps {
+type QueryResultsChartProps = {
   rows: Record<string, unknown>[];
   columns: OutputColumnMetadata[];
   config: ChartConfiguration;
   fullLegend?: boolean;
   /** Callback when "View all" legend button is clicked */
   onViewAllLegendItems?: () => void;
   /** When true, constrains legend to max 50% height with scrolling */
   legendScrollable?: boolean;
-}
+};

As per coding guidelines.

internal-packages/clickhouse/src/client/tsql.ts (1)

33-104: Prefer a type alias for ExecuteTSQLOptions.
This interface was modified; please switch it to a type to align with repo TS style.

♻️ Proposed change
-export interface ExecuteTSQLOptions<TOut extends z.ZodSchema> {
+export type ExecuteTSQLOptions<TOut extends z.ZodSchema> = {
   /** The name of the operation (for logging/tracing) */
   name: string;
   /** The TSQL query string to execute */
   query: string;
   /** The Zod schema for validating output rows */
   schema: TOut;
   /** Schema registry defining allowed tables and columns */
   tableSchema: TableSchema[];
   /**
    * REQUIRED: Conditions always applied at the table level.
    * Must include tenant columns (e.g., organization_id) for multi-tenant tables.
    * Applied to every table reference including subqueries, CTEs, and JOINs.
    *
    * `@example`
    * ```typescript
    * {
    *   // Tenant isolation
    *   organization_id: { op: "eq", value: "org_123" },
    *   project_id: { op: "eq", value: "proj_456" },
    *   environment_id: { op: "eq", value: "env_789" },
    *   // Plan-based time limit
    *   triggered_at: { op: "gte", value: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
    * }
    * ```
    */
   enforcedWhereClause: Record<string, WhereClauseCondition | undefined>;
   /** Optional ClickHouse query settings */
   clickhouseSettings?: ClickHouseSettings;
   /** Optional TSQL query settings (maxRows, timezone, etc.) */
   querySettings?: Partial<QuerySettings>;
   /**
    * Whether to transform result values using the schema's valueMap
    * When enabled, internal ClickHouse values (e.g., 'COMPLETED_SUCCESSFULLY')
    * are converted to user-friendly display names (e.g., 'Completed')
    * `@default` true
    */
   transformValues?: boolean;
   /**
    * Runtime field mappings for dynamic value translation.
    * Maps internal ClickHouse values to external user-facing values.
    *
    * `@example`
    * ```typescript
    * {
    *   project: { "cm12345": "my-project-ref" },
    * }
    * ```
    */
   fieldMappings?: FieldMappings;
   /**
    * Run EXPLAIN instead of executing the query.
    * Returns the ClickHouse execution plan with index information.
    * Should only be used by admins for debugging query performance.
    * `@default` false
    */
   explain?: boolean;
   /**
    * Fallback WHERE conditions to apply when the user hasn't filtered on a column.
    * Key is the column name, value is the fallback condition.
    * These are applied at the AST level (top-level query only).
    *
    * `@example`
    * ```typescript
    * // Apply triggered_at >= 7 days ago if user doesn't filter on triggered_at
    * whereClauseFallback: {
    *   triggered_at: { op: 'gte', value: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
    * }
    * ```
    */
   whereClauseFallback?: Record<string, WhereClauseCondition>;
-}
+};

As per coding guidelines.

internal-packages/tsql/src/query/printer_context.ts (1)

21-41: Prefer type aliases for new condition types.
Please switch these new interfaces to type aliases to align with the TS guideline. As per coding guidelines, use types over interfaces.

♻️ Proposed refactor
-export interface SimpleComparisonCondition {
+export type SimpleComparisonCondition = {
   /** The comparison operator */
   op: "eq" | "neq" | "gt" | "gte" | "lt" | "lte";
   /** The value to compare against */
   value: Date | string | number;
-}
+};

-export interface BetweenCondition {
+export type BetweenCondition = {
   /** The between operator */
   op: "between";
   /** The low bound of the range */
   low: Date | string | number;
   /** The high bound of the range */
   high: Date | string | number;
-}
+};
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (2)

291-312: Non-null assertion relies on control flow that could be fragile.

The requestedFromDate! assertion at line 311 is safe in the current logic because when we reach the else branch, neither timeFilter.from nor timeFilter.to is set, so requestedFromDate was assigned at line 299. However, this relies on implicit coupling between the two code blocks.

Consider restructuring to make the invariant explicit, or add a defensive check:

♻️ Suggested improvement
   } else {
-    triggeredAtFallback = { op: "gte", value: requestedFromDate! };
+    // requestedFromDate is guaranteed non-null here because neither from nor to was specified,
+    // so we calculated it from the period at line 298-299
+    triggeredAtFallback = { op: "gte", value: requestedFromDate ?? new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) };
   }

618-640: Title generation effect has a potential stale closure issue.

The effect depends on results but also reads from editorRef.current?.getQuery(). If the query in the editor changes between the effect trigger and the getQuery() call, the title generated won't match the results. While this is unlikely in practice due to React's batching, consider capturing the query at submission time instead.

Additionally, the dependency array includes organization.slug, project.slug, environment.slug which are used only to construct the action URL—these are unlikely to change during the component lifecycle, but including them is correct for completeness.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fb9cc3 and 0806d27.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (33)
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/package.json
  • internal-packages/clickhouse/schema/014_add_task_runs_v2_output_error_text.sql
  • internal-packages/clickhouse/schema/015_update_output_error_text_to_extract_data.sql
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/index.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/schema.ts
  • internal-packages/tsql/src/query/security.test.ts
💤 Files with no reviewable changes (1)
  • apps/webapp/app/env.server.ts
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/Callout.tsx
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/tsql/src/query/schema.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/Callout.tsx
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/tsql/src/query/schema.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
apps/webapp/app/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
apps/webapp/**/*.{ts,tsx}

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

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

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/package.json
  • apps/webapp/app/components/primitives/Callout.tsx
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/tsql/src/query/schema.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
internal-packages/database/prisma/migrations/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/database/prisma/migrations/**/*.sql: When editing the Prisma schema, remove extraneous migration lines related to specific tables: _BackgroundWorkerToBackgroundWorkerFile, _BackgroundWorkerToTaskQueue, _TaskRunToTaskRunTag, _WaitpointRunConnections, _completedWaitpoints, SecretStore_key_idx, and unrelated TaskRun indexes
Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file

Files:

  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
**/*.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/presenters/v3/QueryPresenter.server.ts
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/tsql/src/query/schema.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/v3/querySchemas.ts
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/services/queryService.server.ts
internal-packages/clickhouse/schema/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/clickhouse/schema/**/*.sql: ClickHouse migrations must use Goose format with -- +goose Up and -- +goose Down markers
Follow ClickHouse naming conventions: raw_ prefix for input tables, _v1, _v2 suffixes for versioning, _mv_v1 suffix for materialized views

Files:

  • internal-packages/clickhouse/schema/014_add_task_runs_v2_output_error_text.sql
  • internal-packages/clickhouse/schema/015_update_output_error_text_to_extract_data.sql
apps/webapp/app/v3/services/**/*.server.{ts,tsx}

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

Organize services in the webapp following the pattern app/v3/services/*/*.server.ts

Files:

  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
**/*.{test,spec}.{ts,tsx}

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

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g., MyService.ts) → MyService.test.ts

Files:

  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers helpers (redisTest, postgresTest, containerTest) from @internal/testcontainers for Redis/PostgreSQL testing instead of mocks

Files:

  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
apps/webapp/app/services/**/*.server.{ts,tsx}

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

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

Files:

  • apps/webapp/app/services/queryService.server.ts
🧠 Learnings (24)
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes

Applied to files:

  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
  • internal-packages/clickhouse/schema/014_add_task_runs_v2_output_error_text.sql
  • internal-packages/clickhouse/schema/015_update_output_error_text_to_extract_data.sql
  • internal-packages/database/prisma/schema.prisma
📚 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/package.json
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path

Applied to files:

  • apps/webapp/package.json
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: The webapp at apps/webapp is a Remix 2.1 application using Node.js v20

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/webapp/package.json
📚 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} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • apps/webapp/package.json
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx} : Always import tasks from `trigger.dev/sdk`, never use `trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern

Applied to files:

  • apps/webapp/package.json
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Import from `trigger.dev/core` using subpaths only, never import from root

Applied to files:

  • apps/webapp/package.json
📚 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} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

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

Applied to files:

  • apps/webapp/package.json
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to **/*.{ts,tsx} : Every Trigger.dev task must be exported and have a unique `id` property with no timeouts in the run function

Applied to files:

  • apps/webapp/package.json
📚 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} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • apps/webapp/package.json
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • apps/webapp/package.json
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : Follow ClickHouse naming conventions: `raw_` prefix for input tables, `_v1`, `_v2` suffixes for versioning, `_mv_v1` suffix for materialized views

Applied to files:

  • internal-packages/clickhouse/schema/014_add_task_runs_v2_output_error_text.sql
  • internal-packages/clickhouse/schema/015_update_output_error_text_to_extract_data.sql
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : ClickHouse migrations must use Goose format with `-- +goose Up` and `-- +goose Down` markers

Applied to files:

  • internal-packages/clickhouse/schema/014_add_task_runs_v2_output_error_text.sql
  • internal-packages/clickhouse/schema/015_update_output_error_text_to_extract_data.sql
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2025-07-21T12:52:44.342Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2284
File: apps/webapp/app/services/realtimeClient.server.ts:111-127
Timestamp: 2025-07-21T12:52:44.342Z
Learning: Electric (the database service used in the realtimeClient) has built-in SQL injection protection and safely handles whereClause parameters passed via URL parameters, so direct string interpolation of runId values into SQL where clauses is safe when using Electric.

Applied to files:

  • internal-packages/tsql/src/query/security.test.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/v3/querySchemas.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Set default maximum duration in trigger.config.ts using `maxDuration` property

Applied to files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 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:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 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:

  • internal-packages/clickhouse/src/client/tsql.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2026-01-12T17:18:09.451Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2870
File: apps/webapp/app/services/redisConcurrencyLimiter.server.ts:56-66
Timestamp: 2026-01-12T17:18:09.451Z
Learning: In `apps/webapp/app/services/redisConcurrencyLimiter.server.ts`, the query concurrency limiter will not be deployed with Redis Cluster mode, so multi-key operations (keyKey and globalKey in different hash slots) are acceptable and will function correctly in standalone Redis mode.

Applied to files:

  • apps/webapp/app/services/queryService.server.ts
🧬 Code graph analysis (10)
apps/webapp/app/v3/services/aiQueryTitleService.server.ts (1)
apps/webapp/app/env.server.ts (1)
  • env (1320-1320)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx (6)
apps/webapp/app/components/primitives/ClientTabs.tsx (3)
  • ClientTabsList (211-211)
  • ClientTabsTrigger (211-211)
  • ClientTabsContent (211-211)
apps/webapp/app/assets/icons/AISparkleIcon.tsx (1)
  • AISparkleIcon (1-31)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/AITabContent.tsx (1)
  • AITabContent (6-65)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/TRQLGuideContent.tsx (1)
  • TRQLGuideContent (71-1203)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/TableSchemaContent.tsx (1)
  • TableSchemaContent (45-67)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/ExamplesContent.tsx (1)
  • ExamplesContent (60-81)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx (6)
apps/webapp/app/utils/pathBuilder.ts (1)
  • EnvironmentParamSchema (26-28)
packages/core/src/v3/apps/http.ts (1)
  • json (65-75)
apps/webapp/app/models/project.server.ts (1)
  • findProjectBySlug (136-147)
apps/webapp/app/models/runtimeEnvironment.server.ts (1)
  • findEnvironmentBySlug (116-145)
apps/webapp/app/env.server.ts (1)
  • env (1320-1320)
apps/webapp/app/v3/services/aiQueryTitleService.server.ts (1)
  • AIQueryTitleService (15-71)
internal-packages/tsql/src/index.test.ts (2)
internal-packages/tsql/src/index.ts (2)
  • WhereClauseCondition (127-127)
  • compileTSQL (538-566)
internal-packages/tsql/src/query/printer_context.ts (1)
  • WhereClauseCondition (47-47)
apps/webapp/app/v3/querySchemas.ts (1)
internal-packages/tsql/src/query/schema.ts (1)
  • column (354-366)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (3)
apps/webapp/app/components/primitives/Callout.tsx (1)
  • Callout (73-170)
apps/webapp/app/components/primitives/Buttons.tsx (1)
  • LinkButton (335-403)
apps/webapp/app/utils/pathBuilder.ts (1)
  • organizationBillingPath (116-118)
internal-packages/clickhouse/src/client/tsql.ts (3)
internal-packages/tsql/src/index.ts (1)
  • WhereClauseCondition (127-127)
internal-packages/clickhouse/src/index.ts (1)
  • WhereClauseCondition (59-59)
internal-packages/tsql/src/query/printer_context.ts (1)
  • WhereClauseCondition (47-47)
apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (2)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/AnimatedNumber.tsx (1)
  • AnimatedNumber (22-58)
internal-packages/tsql/src/index.ts (4)
internal-packages/tsql/src/query/printer_context.ts (3)
  • SimpleComparisonCondition (24-29)
  • WhereClauseCondition (47-47)
  • createPrinterContext (249-256)
internal-packages/clickhouse/src/client/tsql.ts (1)
  • WhereClauseCondition (28-28)
internal-packages/clickhouse/src/index.ts (1)
  • WhereClauseCondition (59-59)
internal-packages/tsql/src/query/schema.ts (1)
  • createSchemaRegistry (371-387)
apps/webapp/app/services/queryService.server.ts (3)
internal-packages/clickhouse/src/client/tsql.ts (1)
  • TSQLQueryResult (133-133)
apps/webapp/app/services/queryConcurrencyLimiter.server.ts (2)
  • queryConcurrencyLimiter (19-22)
  • GLOBAL_CONCURRENCY_LIMIT (28-28)
internal-packages/tsql/src/query/errors.ts (1)
  • QueryError (43-45)
⏰ 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). (20)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

height: `${rowVirtualizer.getTotalSize()}px`,
position: "relative",
}}
className="bg-background-dimmed divide-y divide-charcoal-700"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check the actual file and code at line 1142
wc -l apps/webapp/app/components/code/TSQLResultsTable.tsx

Repository: triggerdotdev/trigger.dev

Length of output: 126


🏁 Script executed:

# Read the file around line 1142 to understand the context
sed -n '1130,1170p' apps/webapp/app/components/code/TSQLResultsTable.tsx

Repository: triggerdotdev/trigger.dev

Length of output: 1554


🏁 Script executed:

# Search for how virtualization is set up in this file
rg -n "useVirtualizer|rowVirtualizer" apps/webapp/app/components/code/TSQLResultsTable.tsx -B2 -A5

Repository: triggerdotdev/trigger.dev

Length of output: 1386


🏁 Script executed:

# Check if divide-y is used elsewhere with virtualized content
rg -n "divide-y" apps/webapp --type tsx -B5 -A5 | head -100

Repository: triggerdotdev/trigger.dev

Length of output: 96


🏁 Script executed:

# Find ROW_HEIGHT definition
rg -n "ROW_HEIGHT" apps/webapp/app/components/code/TSQLResultsTable.tsx

Repository: triggerdotdev/trigger.dev

Length of output: 168


🏁 Script executed:

# Check if there are other virtualized tables in the codebase
rg -n "useVirtualizer" apps/webapp --type ts --type tsx -l

Repository: triggerdotdev/trigger.dev

Length of output: 96


🏁 Script executed:

# Search for divide-y usage in the codebase
rg -n "divide-y" apps/webapp -B3 -A3 --type ts --type tsx | head -60

Repository: triggerdotdev/trigger.dev

Length of output: 96


🏁 Script executed:

# Check the Grid and absolute positioning behavior - look for similar patterns
rg -n "display.*grid.*position.*relative" apps/webapp --type ts --type tsx -B2 -A10 | head -80

Repository: triggerdotdev/trigger.dev

Length of output: 96


🏁 Script executed:

# Search for virtualized tables without type flags
rg "useVirtualizer" apps/webapp -B2 -A8 | head -100

Repository: triggerdotdev/trigger.dev

Length of output: 11147


🏁 Script executed:

# Look for divide-y usage more broadly
rg "divide-y" apps/webapp | head -20

Repository: triggerdotdev/trigger.dev

Length of output: 50383


🏁 Script executed:

# Check Tailwind documentation patterns - look for how divide-y is actually implemented in the codebase
rg "divide-y|divide-x|divide-" --max-count=30 apps/webapp

Repository: triggerdotdev/trigger.dev

Length of output: 50383


🏁 Script executed:

# Look at actual rendered output or comments about this table
grep -n "divide\|border" apps/webapp/app/components/code/TSQLResultsTable.tsx | head -20

Repository: triggerdotdev/trigger.dev

Length of output: 562


🏁 Script executed:

# Check if there are any visual tests or storybook files for this component
find apps/webapp -name "*TSQLResultsTable*" -o -name "*results*table*" 2>/dev/null

Repository: triggerdotdev/trigger.dev

Length of output: 121


divide-y won't render visible dividers with absolutely positioned virtualized rows.

The divide-y utility applies borders via CSS sibling selectors (> * + *), but with absolutely positioned children in a grid container, borders paint at the natural flow position (top of container) rather than at each row's transformed Y position. This won't produce visible dividers between rows as intended.

Add border-b border-charcoal-700 directly to each row instead:

Suggested fix
         <tr
           key={row.id}
           data-index={virtualRow.index}
-          className="group/row hover:bg-charcoal-800"
+          className="group/row border-b border-charcoal-700 hover:bg-charcoal-800"
           style={{
             display: "flex",
             position: "absolute",
             transform: `translateY(${virtualRow.start}px)`,
             width: "100%",
           }}
         >

Then remove divide-y divide-charcoal-700 from the tbody className.

🤖 Prompt for AI Agents
In `@apps/webapp/app/components/code/TSQLResultsTable.tsx` at line 1142, The tbody
in TSQLResultsTable uses "divide-y divide-charcoal-700" which doesn't show
dividers for absolutely positioned/virtualized rows; remove those utilities from
the tbody className and instead add "border-b border-charcoal-700" to the
element that renders each virtualized row (the per-row container/render function
used by the virtualizer — e.g., the row renderer / row element inside the map or
renderRow handler) so each row gets its own bottom border; update any row
className/props (not the tbody) to include the border utilities and remove the
divide-y/divide-charcoal-700 from the tbody.

Comment on lines +22 to +47
export function AnimatedNumber({
value,
duration = 0.5,
decimalPlaces,
}: {
value: number;
duration?: number;
/** Number of decimal places to display. If not provided, auto-detects based on value. */
decimalPlaces?: number;
}) {
const motionValue = useMotionValue(value);
let display = useTransform(motionValue, (current) => Math.round(current).toLocaleString());

// Determine decimal places - use provided value or auto-detect
const decimals = useMemo(
() => (decimalPlaces !== undefined ? decimalPlaces : getDecimalPlaces(value)),
[decimalPlaces, value]
);

const display = useTransform(motionValue, (current) => {
if (decimals === 0) {
return Math.round(current).toLocaleString();
}
return current.toLocaleString(undefined, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clamp and sanitize decimalPlaces before toLocaleString.

Invalid values (negative, non-integer, >20, NaN) will throw at runtime and break rendering. Guard by normalizing to a finite integer within 0–20.

✅ Suggested fix
-  const decimals = useMemo(
-    () => (decimalPlaces !== undefined ? decimalPlaces : getDecimalPlaces(value)),
-    [decimalPlaces, value]
-  );
+  const decimals = useMemo(() => {
+    const raw =
+      decimalPlaces !== undefined ? decimalPlaces : getDecimalPlaces(value);
+    if (!Number.isFinite(raw)) return 0;
+    return Math.min(20, Math.max(0, Math.round(raw)));
+  }, [decimalPlaces, value]);
🤖 Prompt for AI Agents
In `@apps/webapp/app/components/primitives/AnimatedNumber.tsx` around lines 22 -
47, The decimals value used in the useTransform callback must be clamped and
sanitized before being passed to toLocaleString to avoid runtime throws from
invalid values; update the AnimatedNumber component to compute a safeDecimals
(based on decimalPlaces or getDecimalPlaces(value)) by coercing to a finite
number, rounding to an integer, and clamping into the 0–20 range (e.g., use
Number.isFinite, Math.round, Math.max/Math.min) and then use safeDecimals inside
the display transformer and any branch that checks for zero; ensure NaN,
negatives, non-integers and >20 are handled by this normalization so
toLocaleString always receives a valid 0–20 integer.

Comment on lines +441 to +452
// Calculate if the current selection exceeds maxPeriodDays
const exceedsMaxPeriod = (() => {
if (!maxPeriodDays) return false;

if (activeSection === "duration") {
const periodToCheck = selectedPeriod === "custom" ? `${customValue}${customUnit}` : selectedPeriod;
if (!periodToCheck) return false;
return periodToDays(periodToCheck) > maxPeriodDays;
} else {
// For date range, check if fromValue is further back than maxPeriodDays
return dateRangeToDays(fromValue) > maxPeriodDays;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Enforce maxPeriodDays for “to-only” date ranges.
Line 441-452 only checks fromValue, so selecting just a far‑past “to” date bypasses the cap and upgrade prompt. Consider checking the earliest of fromValue/toValue.

🐛 Suggested fix
-    } else {
-      // For date range, check if fromValue is further back than maxPeriodDays
-      return dateRangeToDays(fromValue) > maxPeriodDays;
-    }
+    } else {
+      // For date range, check earliest bound (from or to)
+      const earliest =
+        fromValue && toValue ? (fromValue < toValue ? fromValue : toValue) : fromValue ?? toValue;
+      return dateRangeToDays(earliest) > maxPeriodDays;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Calculate if the current selection exceeds maxPeriodDays
const exceedsMaxPeriod = (() => {
if (!maxPeriodDays) return false;
if (activeSection === "duration") {
const periodToCheck = selectedPeriod === "custom" ? `${customValue}${customUnit}` : selectedPeriod;
if (!periodToCheck) return false;
return periodToDays(periodToCheck) > maxPeriodDays;
} else {
// For date range, check if fromValue is further back than maxPeriodDays
return dateRangeToDays(fromValue) > maxPeriodDays;
}
// Calculate if the current selection exceeds maxPeriodDays
const exceedsMaxPeriod = (() => {
if (!maxPeriodDays) return false;
if (activeSection === "duration") {
const periodToCheck = selectedPeriod === "custom" ? `${customValue}${customUnit}` : selectedPeriod;
if (!periodToCheck) return false;
return periodToDays(periodToCheck) > maxPeriodDays;
} else {
// For date range, check earliest bound (from or to)
const earliest =
fromValue && toValue ? (fromValue < toValue ? fromValue : toValue) : fromValue ?? toValue;
return dateRangeToDays(earliest) > maxPeriodDays;
}
🤖 Prompt for AI Agents
In `@apps/webapp/app/components/runs/v3/SharedFilters.tsx` around lines 441 - 452,
The exceedsMaxPeriod calculation only checks fromValue for date ranges and thus
misses cases where only toValue is set; update the date-range branch in
exceedsMaxPeriod to compute the earliest selected date (min of fromValue and
toValue) and compare its age via dateRangeToDays (or convert the computed range
to days) against maxPeriodDays so "to-only" selections that exceed maxPeriodDays
also return true; locate the exceedsMaxPeriod IIFE and adjust the date range
logic that references activeSection, fromValue, toValue, dateRangeToDays, and
maxPeriodDays accordingly.

Comment on lines 78 to +84
<ClientTabsContent
value="schema"
className="min-h-0 flex-1 overflow-y-auto p-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
>
<TableSchemaContent />
<div className="min-w-64 p-3">
<TableSchemaContent />
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same double padding issue on "schema" tab.

♻️ Suggested fix
        <ClientTabsContent
          value="schema"
-         className="min-h-0 flex-1 overflow-y-auto p-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
+         className="min-h-0 flex-1 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
        >
          <div className="min-w-64 p-3">
            <TableSchemaContent />
          </div>
        </ClientTabsContent>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ClientTabsContent
value="schema"
className="min-h-0 flex-1 overflow-y-auto p-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
>
<TableSchemaContent />
<div className="min-w-64 p-3">
<TableSchemaContent />
</div>
<ClientTabsContent
value="schema"
className="min-h-0 flex-1 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
>
<div className="min-w-64 p-3">
<TableSchemaContent />
</div>
</ClientTabsContent>
🤖 Prompt for AI Agents
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
around lines 78 - 84, The "schema" tab shows double padding because both
ClientTabsContent (value="schema") and the inner div around TableSchemaContent
apply p-3; remove the redundant padding by deleting or changing the inner div's
padding class (the div wrapping TableSchemaContent) to p-0 (or remove p-3) so
only ClientTabsContent provides spacing; update the inner wrapper surrounding
TableSchemaContent to use no padding.

Comment on lines +21 to +27
// Parse the request body
const data = await request.json();
const submission = RequestSchema.safeParse(data);

if (!submission.success) {
return json({ success: false as const, error: "Invalid request data", title: null }, { status: 400 });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Wrap request.json() in try-catch for malformed JSON.

If the request body isn't valid JSON, request.json() will throw an unhandled exception, resulting in a 500 error instead of a proper 400 response.

🔧 Proposed fix
-  // Parse the request body
-  const data = await request.json();
-  const submission = RequestSchema.safeParse(data);
-
-  if (!submission.success) {
-    return json({ success: false as const, error: "Invalid request data", title: null }, { status: 400 });
-  }
+  // Parse the request body
+  let data: unknown;
+  try {
+    data = await request.json();
+  } catch {
+    return json({ success: false as const, error: "Invalid JSON body", title: null }, { status: 400 });
+  }
+  const submission = RequestSchema.safeParse(data);
+
+  if (!submission.success) {
+    return json({ success: false as const, error: "Invalid request data", title: null }, { status: 400 });
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Parse the request body
const data = await request.json();
const submission = RequestSchema.safeParse(data);
if (!submission.success) {
return json({ success: false as const, error: "Invalid request data", title: null }, { status: 400 });
}
// Parse the request body
let data: unknown;
try {
data = await request.json();
} catch {
return json({ success: false as const, error: "Invalid JSON body", title: null }, { status: 400 });
}
const submission = RequestSchema.safeParse(data);
if (!submission.success) {
return json({ success: false as const, error: "Invalid request data", title: null }, { status: 400 });
}
🤖 Prompt for AI Agents
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
around lines 21 - 27, Wrap the call to request.json() in a try-catch so
malformed JSON returns a 400 instead of throwing; specifically, around the code
that calls request.json() before you call RequestSchema.safeParse, catch any
error from JSON parsing and return json({ success: false, error: "Invalid JSON",
title: null }, { status: 400 }); keep the existing RequestSchema.safeParse logic
for validated bodies and reference the same submission variable and response
shape.

Comment on lines 59 to 65
// If a queryId was provided, update the CustomerQuery record with the title
if (queryId) {
await prisma.customerQuery.update({
where: { id: queryId },
data: { title },
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the Prisma schema file
find . -name "*.prisma" -type f | head -20

Repository: triggerdotdev/trigger.dev

Length of output: 528


🏁 Script executed:

#!/bin/bash
# Check the CustomerQuery model in the Prisma schema
rg -A 30 'model CustomerQuery' --glob '*.prisma'

Repository: triggerdotdev/trigger.dev

Length of output: 2595


🏁 Script executed:

#!/bin/bash
# Read the actual file to understand the full context
cat -n apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx | sed -n '1,100p'

Repository: triggerdotdev/trigger.dev

Length of output: 173


🏁 Script executed:

#!/bin/bash
# Search for the file with the ai-title pattern
fd "ai-title" -e tsx

Repository: triggerdotdev/trigger.dev

Length of output: 180


🏁 Script executed:

#!/bin/bash
# Search for the file and its content using rg
rg "Update the CustomerQuery record with the title" -A 20 -B 20

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

#!/bin/bash
# Read the file using proper shell quoting
file="apps/webapp/app/routes/resources.orgs.\$organizationSlug.projects.\$projectParam.env.\$envParam.query.ai-title.tsx"
cat -n "$file" | head -100

Repository: triggerdotdev/trigger.dev

Length of output: 2994


🏁 Script executed:

#!/bin/bash
# Alternative: use find and xargs
find . -name "*ai-title.tsx" -type f -exec cat -n {} \; | head -100

Repository: triggerdotdev/trigger.dev

Length of output: 2994


Scope the CustomerQuery update to the current project/environment.

The queryId is provided directly from the request body without verifying the record belongs to the authenticated user's environment. A malicious user could update another organization's query by supplying a different queryId, causing unauthorized modification of data across tenant boundaries.

🔒 Proposed fix
   // If a queryId was provided, update the CustomerQuery record with the title
   if (queryId) {
     await prisma.customerQuery.update({
-      where: { id: queryId },
+      where: {
+        id: queryId,
+        environmentId: environment.id,
+      },
       data: { title },
     });
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If a queryId was provided, update the CustomerQuery record with the title
if (queryId) {
await prisma.customerQuery.update({
where: { id: queryId },
data: { title },
});
}
// If a queryId was provided, update the CustomerQuery record with the title
if (queryId) {
await prisma.customerQuery.update({
where: {
id: queryId,
environmentId: environment.id,
},
data: { title },
});
}
🤖 Prompt for AI Agents
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
around lines 59 - 65, The update lets any queryId be changed without verifying
it belongs to the current project/environment; restrict the update by either (A)
replacing prisma.customerQuery.update with prisma.customerQuery.updateMany({
where: { id: queryId, projectId: <currentProjectId>, environmentId:
<currentEnvId> }, data: { title } }) and verifying the returned count > 0, or
(B) first fetch the record with prisma.customerQuery.findUnique/findFirst({
where: { id: queryId } }), confirm its project/environment matches the current
project/env variables, then call prisma.customerQuery.update({ where: { id:
queryId }, data: { title } }); use the existing symbols queryId, title,
prisma.customerQuery.update/updateMany and the current project/env identifiers
in the file to implement this check.

Comment on lines 104 to 112
const {
scope,
organizationId,
projectId,
environmentId,
enforcedWhereClause,
history,
customOrgConcurrencyLimit,
whereClauseFallback,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure tenant guards cannot diverge from organization/project inputs.
executeQuery accepts organizationId/projectId/environmentId and enforcedWhereClause independently; if they diverge, queries can run for one tenant while history/concurrency are attributed to another. Please validate or construct the tenant guards here.

✅ Suggested guard alignment
   const {
     scope,
     organizationId,
     projectId,
     environmentId,
     enforcedWhereClause,
     history,
     customOrgConcurrencyLimit,
     whereClauseFallback,
     ...baseOptions
   } = options;

+  const tenantGuards: Record<string, WhereClauseCondition> = {
+    organization_id: { op: "eq", value: organizationId },
+  };
+  if (scope !== "organization") {
+    if (!projectId) {
+      return [
+        new QueryError("projectId is required for project/environment scope", {
+          query: options.query,
+        }),
+        null,
+        null,
+      ];
+    }
+    tenantGuards.project_id = { op: "eq", value: projectId };
+  }
+  if (scope === "environment") {
+    if (!environmentId) {
+      return [
+        new QueryError("environmentId is required for environment scope", {
+          query: options.query,
+        }),
+        null,
+        null,
+      ];
+    }
+    tenantGuards.environment_id = { op: "eq", value: environmentId };
+  }
+  const effectiveEnforcedWhereClause = { ...enforcedWhereClause, ...tenantGuards };
+
   const result = await executeTSQL(clickhouseClient.reader, {
     ...baseOptions,
-    enforcedWhereClause,
+    enforcedWhereClause: effectiveEnforcedWhereClause,
     fieldMappings,
     whereClauseFallback,
     clickhouseSettings: {
       ...getDefaultClickhouseSettings(),
       ...baseOptions.clickhouseSettings,
     },
   });

Also applies to: 153-156

🤖 Prompt for AI Agents
In `@apps/webapp/app/services/queryService.server.ts` around lines 104 - 112, The
tenant inputs (organizationId, projectId, environmentId) and enforcedWhereClause
passed into executeQuery can diverge and cause mismatched attribution for
history/concurrency; update the executeQuery parameter handling to validate and
normalize tenant context by deriving the enforcedWhereClause and
history/concurrency keys from the canonical
organizationId/projectId/environmentId (or reject when inconsistent).
Specifically, in executeQuery (and the similar block around the other usage at
the 153-156 zone) check that any provided enforcedWhereClause matches the
computed clause for the given organizationId/projectId/environmentId (or rebuild
it from those IDs), and use the same canonical IDs to compute history and
customOrgConcurrencyLimit keys so all tenant guards are constructed from the
same source of truth.

Comment on lines +1669 to +1733
private createConditionExpression(column: string, condition: WhereClauseCondition): Expression {
const fieldExpr: Field = {
expression_type: "field",
chain: [column],
};

if (condition.op === "between") {
const betweenExpr: BetweenExpr = {
expression_type: "between_expr",
expr: fieldExpr,
low: this.createValueExpression(condition.low),
high: this.createValueExpression(condition.high),
};
guards.push(envGuard);
return betweenExpr;
}

// Add required filters from the table schema
// Simple comparison
const compareExpr: CompareOperation = {
expression_type: "compare_operation",
left: fieldExpr,
right: this.createValueExpression(condition.value),
op: this.mapConditionOpToCompareOp(condition.op),
};
return compareExpr;
}

/**
* Create a WHERE clause expression for enforced conditions and required filters.
*
* This method applies:
* 1. All conditions from enforcedWhereClause (tenant isolation + plan limits)
* 2. Required filters from the table schema (e.g., engine = 'V2')
*
* Conditions are applied if the column exists in either:
* - The exposed columns (tableSchema.columns)
* - The tenant columns (tableSchema.tenantColumns)
*
* This ensures the same enforcedWhereClause can be used across different tables.
*
* Note: We use just the column name without table prefix since ClickHouse
* requires the actual table name (task_runs_v2), not the TSQL alias (task_runs)
*/
private createEnforcedGuard(tableSchema: TableSchema, _tableAlias: string): Expression | null {
const { requiredFilters, tenantColumns } = tableSchema;
const guards: Expression[] = [];

// Build a set of valid columns for this table (exposed + tenant columns)
const validColumns = new Set<string>(Object.keys(tableSchema.columns));
if (tenantColumns) {
if (tenantColumns.organizationId) validColumns.add(tenantColumns.organizationId);
if (tenantColumns.projectId) validColumns.add(tenantColumns.projectId);
if (tenantColumns.environmentId) validColumns.add(tenantColumns.environmentId);
}

// Apply all enforced conditions for columns that exist in this table
for (const [column, condition] of Object.entries(this.context.enforcedWhereClause)) {
// Skip undefined/null conditions (allows conditional inclusion like project_id?: condition)
if (condition === undefined || condition === null) {
continue;
}
// Only apply if column exists in this table's schema or is a tenant column
if (validColumns.has(column)) {
guards.push(this.createConditionExpression(column, condition));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 117


🏁 Script executed:

sed -n '1650,1760p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 4091


🏁 Script executed:

# Search for JOIN handling and multi-table query patterns
rg -n "JOIN|join|inProjectionContext|allowedInternalColumns" internal-packages/tsql/src/query/printer.ts | head -30

Repository: triggerdotdev/trigger.dev

Length of output: 1693


🏁 Script executed:

# Look at the full context of createEnforcedGuard and where it's called
rg -n "createEnforcedGuard\|createConditionExpression" internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Search more carefully for the functions
rg -n "createEnforcedGuard\|createConditionExpression" internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at the rest of createEnforcedGuard and how it's used
sed -n '1700,1800p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 3812


🏁 Script executed:

# Search for where createEnforcedGuard is called
rg -B3 -A3 "createEnforcedGuard" internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 764


🏁 Script executed:

# Look at the field resolution logic mentioned in the proposed fix
sed -n '500,700p' internal-packages/tsql/src/query/printer.ts | grep -A20 "resolveFieldChain\|tableSchema"

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look for resolveFieldChain or where fields are resolved
rg -n "resolveFieldChain\|resolveField" internal-packages/tsql/src/query/printer.ts -A 5

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at the visitField implementation to see how fields with chains are handled
rg -n "visitField" internal-packages/tsql/src/query/printer.ts -A 20

Repository: triggerdotdev/trigger.dev

Length of output: 1771


🏁 Script executed:

# Check if there are multi-table JOIN scenarios in tests
find internal-packages/tsql -name "*.test.ts" -o -name "*.spec.ts" | head -5

Repository: triggerdotdev/trigger.dev

Length of output: 311


🏁 Script executed:

# Search for join-related test cases
rg -l "join\|JOIN" internal-packages/tsql --type ts | head -5

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at parser test to see JOIN examples
rg -n "join\|JOIN" internal-packages/tsql/src/query/parser.test.ts -B2 -A5 | head -50

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at the rest of visitField to understand field resolution
sed -n '2197,2320p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 4877


🏁 Script executed:

# Search for contexts where enforcedWhereClause is set and used
rg -n "enforcedWhereClause" internal-packages/tsql/src/query/printer.ts -B2 -A2

Repository: triggerdotdev/trigger.dev

Length of output: 2000


🏁 Script executed:

# Check if there are scenarios with multiple tables in FROM/JOIN
rg -n "visitFromExpr\|visitJoinExpr" internal-packages/tsql/src/query/printer.ts -A 30 | head -80

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at resolveFieldChain to understand how fields are resolved
rg -n "private resolveFieldChain" internal-packages/tsql/src/query/printer.ts -A 40

Repository: triggerdotdev/trigger.dev

Length of output: 3489


🏁 Script executed:

# Search for actual JOIN scenarios and how they handle column references
sed -n '1300,1500p' internal-packages/tsql/src/query/printer.ts | grep -A 20 -B 5 "join\|JOIN"

Repository: triggerdotdev/trigger.dev

Length of output: 2216


🏁 Script executed:

# Look at how the table context is managed when processing JOINs
sed -n '300,450p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 5492


🏁 Script executed:

# Check the context structure and how tables are tracked
rg -n "class.*Printer\|private tableStack\|private currentTable" internal-packages/tsql/src/query/printer.ts | head -20

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look for actual JOIN test cases with multiple tables
find internal-packages -name "*.test.ts" -exec grep -l "join\|JOIN" {} \; | head -3

Repository: triggerdotdev/trigger.dev

Length of output: 337


🏁 Script executed:

# Check if there are any tests that show multi-table scenarios with tenant columns
rg -n "enforcedWhereClause\|createEnforcedGuard" internal-packages/tsql/src -l

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at how the WHERE clause is actually built with enforced guards
sed -n '365,395p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1123


🏁 Script executed:

# Check resolveFieldChain more carefully - does it add table qualification?
sed -n '2574,2590p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 802


🏁 Script executed:

# Check if resolveFieldChain returns qualified results or just column names
sed -n '2547,2595p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 2083


🏁 Script executed:

# Look at what resolveColumnName returns to understand table qualification
rg -n "private resolveColumnName" internal-packages/tsql/src/query/printer.ts -A 20

Repository: triggerdotdev/trigger.dev

Length of output: 997


🏁 Script executed:

# Check if there are actual scenarios in the codebase where multiple tables share column names
rg -n "tenantColumns\|requiredFilters" internal-packages/tsql/src/schema -A 3 | head -40

Repository: triggerdotdev/trigger.dev

Length of output: 142


🏁 Script executed:

# Search for actual schema definitions to see if overlap is possible
find internal-packages/tsql -name "*.ts" -not -path "*.test.ts" | xargs grep -l "TableSchema" | head -3

Repository: triggerdotdev/trigger.dev

Length of output: 202


🏁 Script executed:

# Check the comment in printer.ts that says "Note: We use just the column name without table prefix since ClickHouse requires the actual table name"
sed -n '1708,1712p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 392


🏁 Script executed:

# Look at how resolved chains are actually printed (visitField line 2227)
sed -n '2225,2250p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1125


🏁 Script executed:

# Check if there are any other guard creation patterns in the codebase
rg -n "createConditionExpression\|createEnforcedGuard" internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at the full context around line 2580 to see if table qualification happens elsewhere
sed -n '2575,2610p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1613


🏁 Script executed:

# Look for examples of multi-table joins in tests
rg -n "select_from.*next_join" internal-packages/tsql/src/query/parser.test.ts -B 5 -A 10 | head -60

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Check if tables typically have overlapping tenant columns
rg -n "organizationId\|projectId\|environmentId" internal-packages/tsql/src/query/results.ts -B 2 -A 2 | head -50

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look for actual table schema definitions with tenantColumns
rg -n "tenantColumns:" internal-packages -A 3 | head -40

Repository: triggerdotdev/trigger.dev

Length of output: 2778


🏁 Script executed:

# Verify the iteration order concern - check if Map iteration order is guaranteed
rg -n "tableContexts.values()\|tableContexts.set" internal-packages/tsql/src/query/printer.ts | head -20

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Check how the resolved chain is actually used in SQL
sed -n '2225,2240p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 760


🏁 Script executed:

# Look at a test case to see actual generated SQL
rg -n "expected.*WHERE\|sql.*WHERE" internal-packages/tsql/src/query/results.test.ts -A 3 | head -40

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Check what printIdentifierOrIndex does
rg -n "private printIdentifierOrIndex" internal-packages/tsql/src/query/printer.ts -A 10

Repository: triggerdotdev/trigger.dev

Length of output: 410


🏁 Script executed:

# Look for test cases with JOINs to see generated SQL
rg -n "JOIN\|join" internal-packages/tsql/src/query/results.test.ts -B 5 -A 5 | head -50

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Look at a specific test case to see the actual JOIN and WHERE clause generated
sed -n '1,100p' internal-packages/tsql/src/query/results.test.ts | grep -A 20 "describe\|test.*join"

Repository: triggerdotdev/trigger.dev

Length of output: 1579


🏁 Script executed:

# Search for actual multi-table join tests with WHERE clauses
rg -n "next_join\|JOIN" internal-packages/tsql/src/query/results.test.ts -B 10 -A 10 | head -80

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Check what happens in a query with multiple tables and enforced guards
rg -n "enforcedWhereClause\|createEnforcedGuard" internal-packages/tsql/src/query/results.test.ts -B 5 -A 5

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Understand the comment about ClickHouse requiring actual table names - check if there are alias issues documented
sed -n '1700,1720p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1192


🏁 Script executed:

# See the exact flow: when createEnforcedGuard creates chain: [column], trace how it gets resolved
sed -n '2574,2610p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1669


🏁 Script executed:

# Check the order of operations: when is createEnforcedGuard called relative to tableContexts population?
sed -n '1470,1530p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 2859


🏁 Script executed:

# Let's see if there are actual production tables that could have overlapping column names
rg -n "organizationId.*:\|projectId.*:" internal-packages -A 1 | grep -B 1 ":" | head -40

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Check if there's actual test coverage for multi-join scenarios with enforced guards
rg -n "tenantColumns.*organizationId" internal-packages/tsql/src/query -l

Repository: triggerdotdev/trigger.dev

Length of output: 156


🏁 Script executed:

# Look at the actual Map iteration behavior and when tables get added to tableContexts
sed -n '1458,1495p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1585


🏁 Script executed:

# Let me trace the exact SQL output to see if unqualified columns are actually generated
# Create a simple test to understand the output
cat > /tmp/test_join.js << 'EOF'
// Simulate what happens with two tables having organization_id
const tableContexts = new Map();
tableContexts.set('task_runs', { name: 'task_runs', clickhouseName: 'trigger_dev.task_runs_v2', columns: { organization_id: { name: 'organization_id' }, id: { name: 'id' } } });
tableContexts.set('task_queue', { name: 'task_queue', clickhouseName: 'trigger_dev.task_queue_v2', columns: { organization_id: { name: 'organization_id' }, queue_name: { name: 'queue_name' } } });

// Simulate resolveFieldChain when encountering unqualified "organization_id"
function resolveFieldChain(chain) {
  const firstPart = chain[0];
  
  // Case 2: Unqualified reference
  for (const tableSchema of tableContexts.values()) {
    const columnSchema = tableSchema.columns[firstPart];
    if (columnSchema) {
      console.log(`Found "${firstPart}" in table ${tableSchema.name}`);
      return [columnSchema.clickhouseName || columnSchema.name, ...chain.slice(1)];
    }
  }
  return chain;
}

const resolved = resolveFieldChain(['organization_id']);
console.log('Resolved chain:', resolved);
console.log('SQL would be: WHERE organization_id = value');
console.log('This is AMBIGUOUS in: FROM task_runs_v2 AS task_runs JOIN task_queue_v2 AS task_queue WHERE organization_id = value');
EOF
node /tmp/test_join.js

Repository: triggerdotdev/trigger.dev

Length of output: 309


🏁 Script executed:

# Look at what allowedInternalColumns does - does it help with qualification?
sed -n '2606,2625p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 910


🏁 Script executed:

# Check if there's any protection through validation that prevents actual ambiguity
rg -n "allowedInternalColumns.has\|isAllowedInternalColumn" internal-packages/tsql/src/query/printer.ts -B 2 -A 3

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Now verify if this is actually used in a multi-join scenario - let's check if createEnforcedGuard is called for each joined table
sed -n '1520,1540p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1072


🏁 Script executed:

# Verify: is createEnforcedGuard called once per join or once total?
sed -n '1510,1530p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 997


🏁 Script executed:

# Check if the loop that adds enforced guards handles multiple tables
sed -n '365,390p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1009


🏁 Script executed:

# Check if the guards from createEnforcedGuard are truly added to the WHERE clause with unqualified chains still intact
sed -n '1518,1525p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 620


🏁 Script executed:

# Verify the exact moment when WHERE clause is visited - are tableContexts still populated?
rg -n "this.visit(where)" internal-packages/tsql/src/query/printer.ts -B 5 -A 2

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

# Critical: check if Map iteration order is deterministic for this scenario
sed -n '330,380p' internal-packages/tsql/src/query/printer.ts | grep -A 50 "tableContexts"

Repository: triggerdotdev/trigger.dev

Length of output: 1789


🏁 Script executed:

# Let me confirm: when WHERE is visited, are tableContexts still populated with all joined tables?
sed -n '432,465p' internal-packages/tsql/src/query/printer.ts

Repository: triggerdotdev/trigger.dev

Length of output: 1355


🏁 Script executed:

# Check the exact order: when is WHERE visited relative to when tableContexts is set up with all tables
sed -n '365,445p' internal-packages/tsql/src/query/printer.ts | head -60

Repository: triggerdotdev/trigger.dev

Length of output: 2353


Qualify enforced guards to avoid ambiguous column resolution in multi-table JOINs.

createEnforcedGuard currently emits unqualified columns (e.g., organization_id instead of task_runs.organization_id). When the WHERE clause is evaluated with multiple joined tables sharing tenant column names, resolveFieldChain iterates through tableContexts.values() in insertion order and returns the first match, potentially binding to the wrong table and undermining isolation. Pass the tableAlias parameter to createConditionExpression to qualify guard columns during creation.

🐛 Proposed fix
-  private createConditionExpression(column: string, condition: WhereClauseCondition): Expression {
+  private createConditionExpression(
+    column: string,
+    condition: WhereClauseCondition,
+    tableAlias?: string
+  ): Expression {
     const fieldExpr: Field = {
       expression_type: "field",
-      chain: [column],
+      chain: tableAlias ? [tableAlias, column] : [column],
     };
@@
-  private createEnforcedGuard(tableSchema: TableSchema, _tableAlias: string): Expression | null {
+  private createEnforcedGuard(tableSchema: TableSchema, tableAlias: string): Expression | null {
@@
-        guards.push(this.createConditionExpression(column, condition));
+        guards.push(this.createConditionExpression(column, condition, tableAlias));
🤖 Prompt for AI Agents
In `@internal-packages/tsql/src/query/printer.ts` around lines 1669 - 1733,
createEnforcedGuard currently builds unqualified guard expressions which can
bind to the wrong table in multi-join queries; update createConditionExpression
to accept an optional tableAlias parameter and, when provided, construct the
Field.chain as [tableAlias, column] (instead of just [column]) so
resolveFieldChain will resolve to the correct table; then call
createConditionExpression(column, condition, tableAlias) from
createEnforcedGuard's loop and ensure existing logic in
createConditionExpression (used for between expressions and CompareOperation
building with createValueExpression and mapConditionOpToCompareOp) uses the new
fieldExpr with the qualified chain.

@matt-aitken matt-aitken force-pushed the query-output-error-idempotency branch from 0806d27 to 9837a90 Compare January 27, 2026 17:21
When we store data in JSON columns, if there is data we wrap it in `{
"data": theData }`. We do this because it could be an array, a primitive
or an object. Arrays and primitives can't be stored in a JSON column.

This hides this from the user because it’s an implementation detail and
means their expectation doesn’t match how we store
This includes making the resizable panel work for vertical splits
@matt-aitken matt-aitken force-pushed the query-output-error-idempotency branch from 9837a90 to 87ec51f Compare January 27, 2026 17:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

529-540: Missing maxPeriodDays in dependency array.

The applySelection callback references maxPeriodDays in the error message (line 459) but it's not included in the dependency array. While the current flow may work due to the disabled button preventing calls, this could cause stale values if the callback is invoked programmatically.

Suggested fix
   }, [
     activeSection,
     selectedPeriod,
     isCustomDurationValid,
     customValue,
     customUnit,
     fromValue,
     toValue,
     replace,
     onApply,
     onValueChange,
+    exceedsMaxPeriod,
+    maxPeriodDays,
   ]);
apps/webapp/app/v3/querySchemas.ts (1)

329-354: Add test coverage for NULL checks on JSON subfields with dataPrefix to verify the semantics are correct.

The implementation appears sound: dataPrefix injection and nullValue transformation work together correctly because the NULL comparison is applied after the field reference is visited (which injects the prefix). For example, WHERE output.message IS NULL with dataPrefix: "data" becomes equals(output.data.message, '{}') — correctly checking the wrapped field against empty object.

However, there is no explicit test covering this combined scenario. The test suite includes:

  • NULL checks on bare JSON columns with nullValue (lines 493–544)
  • NULL checks on bare JSON columns with textColumn (lines 800–812)
  • dataPrefix injection in SELECT/WHERE/GROUP BY (lines 901–1005)

But no test combines dataPrefix with NULL checks on subfields (e.g., WHERE output.message IS NULL or WHERE error.code IS NULL). Adding a test case would confirm the behavior is correct and prevent future regressions.

🤖 Fix all issues with AI agents
In `@internal-packages/tsql/src/query/printer_context.ts`:
- Around line 21-41: Replace the exported interfaces SimpleComparisonCondition
and BetweenCondition with exported type aliases that preserve the exact same
object shapes and string literal unions (e.g., export type
SimpleComparisonCondition = { op: "eq" | "neq" | "gt" | "gte" | "lt" | "lte";
value: Date | string | number; } and export type BetweenCondition = { op:
"between"; low: Date | string | number; high: Date | string | number; }); keep
JSDoc comments and exports intact so all callers using SimpleComparisonCondition
and BetweenCondition continue to compile.
🧹 Nitpick comments (4)
internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql (2)

6-15: Consider the repeated toJSONString(output) calls.

The expression calls toJSONString(output) up to 3 times per row (lines 8, 11, and 12 or 13). Since this is a MATERIALIZED column computed at insert time rather than query time, the impact is limited to write performance. However, ClickHouse may not optimize away these redundant calls.

Unfortunately, ClickHouse's MATERIALIZED column syntax doesn't support CTEs or variable bindings within the expression, so there's no clean way to compute it once. If write performance becomes a concern, an alternative approach would be using a separate materialized view, but that adds complexity.

This is acceptable as-is given the tradeoff favors read performance.


2-2: Minor: Comment says "Update" but the action is "ADD COLUMN".

The comment "Update the materialized columns" suggests modifying existing columns, but the migration actually adds new columns. Consider clarifying this if it's intentional (e.g., replacing a previous approach).

-- +goose Up
--- Update the materialized columns to extract the 'data' field if it exists
+-- Add materialized text columns that extract the 'data' field if it exists
 -- This avoids the {"data": ...} wrapper in the text representation
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (2)

618-640: Consider using titleFetcher.state in the dependency array.

The effect depends on titleFetcher object, but only checks titleFetcher.state in the condition. Including the entire fetcher object may trigger unnecessary effect runs since the fetcher reference can change between renders. Using titleFetcher.state would be more precise.

♻️ Suggested refinement
-  }, [results, shouldGenerateTitle, historyTitle, titleFetcher, organization.slug, project.slug, environment.slug]);
+  }, [results, shouldGenerateTitle, historyTitle, titleFetcher.state, organization.slug, project.slug, environment.slug]);

1086-1090: Minor inconsistency: DialogHeader doesn't show loading state.

The main chart header (line 1068) displays a loading spinner during title generation, but the DialogHeader (line 1089) falls back to "Chart" directly. If the dialog is opened while the title is still generating, users see inconsistent UI.

♻️ Suggested fix for consistency
           <DialogHeader>
-            {queryTitle ?? "Chart"}
+            {titleContent}
           </DialogHeader>
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0806d27 and 9837a90.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/package.json
  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/index.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/schema.ts
  • internal-packages/tsql/src/query/security.test.ts
💤 Files with no reviewable changes (1)
  • apps/webapp/app/env.server.ts
🚧 Files skipped from review as they are similar to previous changes (16)
  • internal-packages/tsql/src/query/schema.ts
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • internal-packages/database/prisma/schema.prisma
  • apps/webapp/package.json
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • internal-packages/clickhouse/src/client/tsql.ts
  • apps/webapp/app/components/primitives/Callout.tsx
🧰 Additional context used
📓 Path-based instructions (12)
internal-packages/clickhouse/schema/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/clickhouse/schema/**/*.sql: ClickHouse migrations must use Goose format with -- +goose Up and -- +goose Down markers
Follow ClickHouse naming conventions: raw_ prefix for input tables, _v1, _v2 suffixes for versioning, _mv_v1 suffix for materialized views

Files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
**/*.{ts,tsx}

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

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

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/query/printer.test.ts
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/query/printer.test.ts
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
apps/webapp/app/**/*.{ts,tsx}

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

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

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
apps/webapp/**/*.{ts,tsx}

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

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

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/query/printer.test.ts
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
**/*.ts

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

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

Files:

  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/services/queryService.server.ts
**/*.{test,spec}.{ts,tsx}

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

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g., MyService.ts) → MyService.test.ts

Files:

  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers helpers (redisTest, postgresTest, containerTest) from @internal/testcontainers for Redis/PostgreSQL testing instead of mocks

Files:

  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
apps/webapp/app/services/**/*.server.{ts,tsx}

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

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

Files:

  • apps/webapp/app/services/queryService.server.ts
🧠 Learnings (11)
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : Follow ClickHouse naming conventions: `raw_` prefix for input tables, `_v1`, `_v2` suffixes for versioning, `_mv_v1` suffix for materialized views

Applied to files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : ClickHouse migrations must use Goose format with `-- +goose Up` and `-- +goose Down` markers

Applied to files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes

Applied to files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Set default maximum duration in trigger.config.ts using `maxDuration` property

Applied to files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 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:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2025-07-21T12:52:44.342Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2284
File: apps/webapp/app/services/realtimeClient.server.ts:111-127
Timestamp: 2025-07-21T12:52:44.342Z
Learning: Electric (the database service used in the realtimeClient) has built-in SQL injection protection and safely handles whereClause parameters passed via URL parameters, so direct string interpolation of runId values into SQL where clauses is safe when using Electric.

Applied to files:

  • internal-packages/tsql/src/query/security.test.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/services/queryService.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • apps/webapp/app/v3/querySchemas.ts
📚 Learning: 2025-08-14T10:53:54.526Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2391
File: apps/webapp/app/services/organizationAccessToken.server.ts:50-0
Timestamp: 2025-08-14T10:53:54.526Z
Learning: In the Trigger.dev codebase, token service functions (like revokePersonalAccessToken and revokeOrganizationAccessToken) don't include tenant scoping in their database queries. Instead, authorization and tenant scoping happens at a higher level in the authentication flow (typically in route handlers) before these service functions are called. This is a consistent pattern across both Personal Access Tokens (PATs) and Organization Access Tokens (OATs).

Applied to files:

  • apps/webapp/app/services/queryService.server.ts
📚 Learning: 2026-01-12T17:18:09.451Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2870
File: apps/webapp/app/services/redisConcurrencyLimiter.server.ts:56-66
Timestamp: 2026-01-12T17:18:09.451Z
Learning: In `apps/webapp/app/services/redisConcurrencyLimiter.server.ts`, the query concurrency limiter will not be deployed with Redis Cluster mode, so multi-key operations (keyKey and globalKey in different hash slots) are acceptable and will function correctly in standalone Redis mode.

Applied to files:

  • apps/webapp/app/services/queryService.server.ts
🧬 Code graph analysis (7)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (3)
apps/webapp/app/components/primitives/Callout.tsx (1)
  • Callout (73-170)
apps/webapp/app/components/primitives/Buttons.tsx (1)
  • LinkButton (335-403)
apps/webapp/app/utils/pathBuilder.ts (1)
  • organizationBillingPath (116-118)
internal-packages/tsql/src/index.test.ts (5)
internal-packages/tsql/src/index.ts (4)
  • column (81-81)
  • TableSchema (113-113)
  • WhereClauseCondition (127-127)
  • compileTSQL (538-566)
apps/webapp/app/services/queryService.server.ts (1)
  • TableSchema (21-21)
internal-packages/clickhouse/src/client/tsql.ts (2)
  • TableSchema (28-28)
  • WhereClauseCondition (28-28)
internal-packages/clickhouse/src/index.ts (2)
  • TableSchema (54-54)
  • WhereClauseCondition (59-59)
internal-packages/tsql/src/query/printer_context.ts (1)
  • WhereClauseCondition (47-47)
internal-packages/tsql/src/query/printer_context.ts (2)
internal-packages/tsql/src/index.ts (8)
  • SimpleComparisonCondition (126-126)
  • BetweenCondition (122-122)
  • WhereClauseCondition (127-127)
  • SchemaRegistry (112-112)
  • FieldMappings (109-109)
  • createPrinterContext (119-119)
  • PrinterContextOptions (123-123)
  • PrinterContext (121-121)
internal-packages/tsql/src/query/schema.ts (2)
  • SchemaRegistry (344-349)
  • FieldMappings (270-270)
apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (2)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/AnimatedNumber.tsx (1)
  • AnimatedNumber (22-58)
internal-packages/tsql/src/query/printer.test.ts (1)
internal-packages/tsql/src/query/printer_context.ts (2)
  • createPrinterContext (249-256)
  • PrinterContext (79-214)
apps/webapp/app/v3/querySchemas.ts (1)
internal-packages/tsql/src/query/schema.ts (1)
  • column (354-366)
apps/webapp/app/services/queryService.server.ts (4)
internal-packages/clickhouse/src/client/tsql.ts (1)
  • TSQLQueryResult (133-133)
internal-packages/clickhouse/src/index.ts (2)
  • TSQLQueryResult (55-55)
  • QueryError (64-64)
apps/webapp/app/services/queryConcurrencyLimiter.server.ts (2)
  • queryConcurrencyLimiter (19-22)
  • GLOBAL_CONCURRENCY_LIMIT (28-28)
internal-packages/tsql/src/query/errors.ts (1)
  • QueryError (43-45)
⏰ 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). (22)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 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 (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (48)
apps/webapp/app/components/primitives/Resizable.tsx (3)

28-40: LGTM!

The orientation-aware styling using data-[handle-orientation=vertical] selectors is well-structured. The base horizontal styles with vertical overrides pattern is clean and maintainable.


44-47: LGTM!

The line indicators properly toggle visibility based on orientation using complementary hidden/block classes with the group-data-[handle-orientation=vertical] selector. The hover transitions are smooth and provide good visual feedback.


48-63: LGTM!

The orientation-aware dot rendering is well-implemented. Using index as the key is acceptable here since the array is static, fixed-length, and items never reorder. The visibility logic correctly shows only the relevant orientation's dots while hiding both on hover to reveal the line indicator.

internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql (3)

1-5: Goose format and naming conventions are correct.

The migration properly uses -- +goose Up and -- +goose Down markers, and task_runs_v2 follows the versioning suffix convention. As per coding guidelines and learnings.


29-32: Index configuration looks appropriate for text search.

The ngrambf_v1(3, 131072, 3, 0) bloom filter with 3-character ngrams is well-suited for substring matching on JSON text fields.


34-45: Down migration is correctly structured.

Indexes are dropped before columns, and IF EXISTS guards protect against partial migration states. Good defensive practice.

apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (2)

9-43: Scrollable prop is cleanly added and defaulted.
Good addition to the public API with a sensible default for backward compatibility.


134-216: Scrollable legend layout looks solid.
The max-height constraint, shrink-0 guards, and overflow wrapper are well composed for flex layouts.

apps/webapp/app/components/primitives/charts/ChartRoot.tsx (2)

9-105: Legend scrollability is wired through cleanly.
Defaults and prop forwarding are consistent across the root component.


109-170: Inner wiring to ChartLegendCompound is correct.
scrollable is passed through without side effects.

apps/webapp/app/components/runs/v3/SharedFilters.tsx (6)

14-29: LGTM!

The new imports are well-organized and all are utilized in the component for the upgrade callout feature.


126-140: LGTM!

The utility functions are well-implemented. periodToDays leverages the existing parse-duration library for consistency, and dateRangeToDays uses ceiling to ensure partial days count conservatively.


797-806: LGTM!

The upgrade callout is well-implemented with proper conditional rendering, pluralization via simplur, and correct routing to the billing page using organizationBillingPath.


738-762: LGTM!

The quick date button changes improve semantic correctness. Using endOfDay, endOfWeek, and endOfMonth for the "to" values ensures the selected ranges cover the full intended periods rather than stopping at the current timestamp.


823-838: LGTM!

Disabling the Apply button when exceedsMaxPeriod is true provides clear UX feedback and prevents invalid submissions.


93-109: LGTM!

Adding the "5 days" preset provides users with a useful intermediate option between 3 and 7 days.

apps/webapp/app/v3/querySchemas.ts (1)

170-178: No action needed. The idempotency scope values are correctly defined and match the backend support. All three scopes—"run", "attempt", and "global"—are officially supported throughout the codebase.

internal-packages/tsql/src/query/printer_context.ts (1)

95-199: LGTM: enforcedWhereClause is consistently propagated.

Also applies to: 219-255

internal-packages/tsql/src/index.ts (2)

26-32: LGTM: WhereClauseCondition wiring across helpers is consistent.

Also applies to: 118-128, 316-367, 377-444


450-504: LGTM: enforcedWhereClause sanitization + context creation look solid.

Also applies to: 542-565

internal-packages/tsql/src/query/security.test.ts (2)

55-61: LGTM: default test options migrated to enforcedWhereClause.


417-421: LGTM: optional tenant filter permutations updated for enforcedWhereClause.

Also applies to: 438-443, 459-463, 491-496, 516-520, 535-539, 559-563, 578-582

apps/webapp/app/services/queryService.server.ts (1)

57-87: LGTM: 3‑tuple result + queryId propagation look consistent.

Also applies to: 89-99, 121-134, 164-218

internal-packages/tsql/src/query/printer.test.ts (4)

88-94: LGTM: test contexts updated for enforcedWhereClause.

Also applies to: 155-164, 221-229, 248-258, 481-491, 697-707, 888-898, 1152-1157, 1469-1485, 1585-1604, 1811-1832, 2054-2074, 2201-2227, 2614-2645, 2769-2785, 2899-2921, 3084-3093


611-664: LGTM: JSON subfield type‑hint suppression in WHERE is covered.


666-855: LGTM: textColumn optimization scenarios are well covered.


857-1007: LGTM: dataPrefix injection behavior is well covered.

internal-packages/tsql/src/index.test.ts (3)

8-74: LGTM: scaffolding updated for enforcedWhereClause and new schemas.


168-243: LGTM: fallback tests updated to WhereClauseCondition.


516-828: LGTM: enforcedWhereClause behavior suite is comprehensive.

internal-packages/tsql/src/query/printer.ts (12)

117-118: LGTM!

Good addition of the queryHasGroupBy flag with clear documentation explaining its purpose for JSON subfield type hints in GROUP BY queries.


397-400: LGTM!

Proper state management pattern for queryHasGroupBy - saving before modification and restoring after subquery processing, consistent with the existing savedTableContexts approach.


639-678: LGTM!

Well-structured logic flow for handling different column types:

  1. Text column optimization for bare JSON fields
  2. Virtual column expression aliasing
  3. JSON subfield aliasing (excluding dataPrefix)
  4. ClickHouse name mismatch aliasing

The effectiveOutputName override ensures metadata consistency with the generated alias.


696-710: LGTM!

Correctly handles the case where a user explicitly aliases a JSON column with a textColumn optimization (e.g., SELECT output AS myOutput), using the optimized text column while preserving the user's explicit alias.


853-857: LGTM!

Consistent application of textColumn optimization during SELECT * expansion, with proper aliasing to preserve the user-facing column name.


1479-1480: LGTM!

Good fail-fast validation of required tenant columns early in join processing. This ensures tenant isolation requirements are enforced before query execution.


1590-1604: LGTM!

Proper tenant isolation validation - ensures the required organizationId column is present in enforcedWhereClause. The error message clearly identifies the missing requirement.


1606-1638: LGTM!

Correct date handling for ClickHouse:

  • Uses UTC methods consistently for timezone-safe formatting
  • toDateTime64(..., 3) matches DateTime64(3) column precision
  • Format string matches ClickHouse's expected format

1868-1881: LGTM!

Good optimization to use textColumn for string-based comparisons (Eq, NotEq, Like, ILike, etc.) where the text representation is more efficient. Correctly excludes numeric comparisons (Lt, Gt, etc.) where the text column wouldn't provide correct semantics.


2296-2308: LGTM!

Clean implementation of isInWhereComparisonContext() that correctly identifies WHERE/HAVING comparison contexts without including GROUP BY. This enables proper differentiation for JSON type hint handling.


2351-2419: LGTM!

Well-implemented helper methods for text column and data prefix detection:

  • getTextColumnForField correctly identifies bare JSON fields vs. subfield access
  • getDataPrefixForField properly handles both qualified and unqualified references with correct length checks

2421-2468: LGTM!

Correct implementation of data prefix handling:

  • injectDataPrefix properly transforms chains for both qualified and unqualified references
  • buildAliasWithoutDataPrefix safely operates on a filtered copy, removing the internal data prefix for cleaner user-facing aliases
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (2)

38-46: LGTM!

The normalization logic is clean and improves SQL display by collapsing whitespace per line while preserving newlines for structure. The increased limit to 500 characters provides better context in history items.


122-136: LGTM!

The conditional rendering correctly prioritizes the title when available (with the query shown as secondary context), while maintaining the original query-focused display when no title exists. The styling differentiation is appropriate.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx (4)

173-180: LGTM!

The default period calculation correctly respects plan limits while preferring the ideal 7-day default. The logic handles edge cases appropriately.


291-326: LGTM!

The time filter logic correctly handles all cases (from/to, from-only, to-only, period). The non-null assertion on line 311 is safe because the else branch guarantees requestedFromDate was set. The enforcedWhereClause properly enforces tenant isolation and plan-based time limits.


988-1026: LGTM!

The QueryResultsCallouts component provides clear user feedback about result limitations with an actionable upgrade path. The simplur usage correctly handles day/days pluralization.


1028-1033: LGTM!

Clean helper function that encapsulates the callout visibility logic.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +21 to +41
/**
* A simple comparison condition (e.g., column > value)
*/
export interface SimpleComparisonCondition {
/** The comparison operator */
op: "eq" | "neq" | "gt" | "gte" | "lt" | "lte";
/** The value to compare against */
value: Date | string | number;
}

/**
* A between condition (e.g., column BETWEEN low AND high)
*/
export interface BetweenCondition {
/** The between operator */
op: "between";
/** The low bound of the range */
low: Date | string | number;
/** The high bound of the range */
high: Date | string | number;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n internal-packages/tsql/src/query/printer_context.ts | sed -n '15,50p'

Repository: triggerdotdev/trigger.dev

Length of output: 1330


Use type aliases for condition types (repo style).

SimpleComparisonCondition and BetweenCondition are declared as interfaces, but the TypeScript guideline requires using type aliases instead.

♻️ Proposed change
-export interface SimpleComparisonCondition {
+export type SimpleComparisonCondition = {
   /** The comparison operator */
   op: "eq" | "neq" | "gt" | "gte" | "lt" | "lte";
   /** The value to compare against */
   value: Date | string | number;
-}
+};

-export interface BetweenCondition {
+export type BetweenCondition = {
   /** The between operator */
   op: "between";
   /** The low bound of the range */
   low: Date | string | number;
   /** The high bound of the range */
   high: Date | string | number;
-}
+};
🤖 Prompt for AI Agents
In `@internal-packages/tsql/src/query/printer_context.ts` around lines 21 - 41,
Replace the exported interfaces SimpleComparisonCondition and BetweenCondition
with exported type aliases that preserve the exact same object shapes and string
literal unions (e.g., export type SimpleComparisonCondition = { op: "eq" | "neq"
| "gt" | "gte" | "lt" | "lte"; value: Date | string | number; } and export type
BetweenCondition = { op: "between"; low: Date | string | number; high: Date |
string | number; }); keep JSDoc comments and exports intact so all callers using
SimpleComparisonCondition and BetweenCondition continue to compile.

Copy link

@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 potential issues.

Open in Devin Review

Comment on lines 455 to 465
const applySelection = useCallback(() => {
setValidationError(null);

if (exceedsMaxPeriod) {
setValidationError(`Your plan allows a maximum of ${maxPeriodDays} days. Upgrade for longer retention.`);
return;
}

if (activeSection === "duration") {
// Validate custom duration
if (selectedPeriod === "custom" && !isCustomDurationValid) {

Choose a reason for hiding this comment

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

🟡 TimeDropdown applySelection uses stale exceedsMaxPeriod/maxPeriodDays due to missing hook dependencies

applySelection closes over exceedsMaxPeriod/maxPeriodDays but they are not listed in the useCallback dependency array.

Actual behavior: if maxPeriodDays changes (e.g. plan loads async) or if the user changes inputs that affect exceedsMaxPeriod, the callback may still use the previous value and either:

  • incorrectly block Apply with an outdated validation error, or
  • incorrectly allow Apply and update URL/state even though the selection exceeds the limit.

Expected behavior: the Apply handler should always reflect the current exceedsMaxPeriod and maxPeriodDays.

Click to expand
  • exceedsMaxPeriod is computed from activeSection, selectedPeriod, customValue/customUnit, fromValue, and maxPeriodDays.
  • applySelection checks if (exceedsMaxPeriod) { setValidationError(...${maxPeriodDays}...) ; return; } but the dependency list omits both exceedsMaxPeriod and maxPeriodDays.

Cited code: apps/webapp/app/components/runs/v3/SharedFilters.tsx:455-540.

(Refers to lines 455-540)

Recommendation: Add exceedsMaxPeriod and maxPeriodDays to the dependency array (or compute exceedsMaxPeriod inside the callback).

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

This prevents cross-tenancy updates
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

455-540: Add exceedsMaxPeriod and maxPeriodDays to the useCallback dependencies.

The callback reads both values (line 458–459), but they're missing from the dependency array. Since exceedsMaxPeriod is recalculated on each render and maxPeriodDays is a prop, omitting them causes the callback to close over stale values when these change.

Proposed fix
   ], [
     activeSection,
     selectedPeriod,
     isCustomDurationValid,
     customValue,
     customUnit,
     fromValue,
     toValue,
     replace,
     onApply,
     onValueChange,
+    exceedsMaxPeriod,
+    maxPeriodDays,
   ]);
🧹 Nitpick comments (2)
internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql (1)

7-14: Consider extracting repeated toJSONString() call.

toJSONString(output) and toJSONString(error) are each called up to 3 times within their respective expressions. While ClickHouse may optimize this for MATERIALIZED columns computed at insert time, this could be cleaner. Unfortunately, ClickHouse doesn't support CTEs or variable bindings within MATERIALIZED expressions, so this is likely unavoidable—just noting for awareness.

Also applies to: 19-26

apps/webapp/app/components/runs/v3/SharedFilters.tsx (1)

289-308: Prefer type aliases over interface in TSX.

This file uses interfaces for TimeFilterApplyValues and TimeFilterProps; please align with the project rule to use types instead.
As per coding guidelines, use type aliases over interfaces in TS/TSX.

♻️ Proposed refactor
-export interface TimeFilterApplyValues {
+export type TimeFilterApplyValues = {
   period?: string;
   from?: string;
   to?: string;
-}
+};

-export interface TimeFilterProps {
+export type TimeFilterProps = {
   defaultPeriod?: string;
   period?: string;
   from?: string;
   to?: string;
   /** Label name used in the filter display, defaults to "Created" */
   labelName?: string;
   hideLabel?: boolean;
   applyShortcut?: ShortcutDefinition | undefined;
   /** Callback when the user applies a time filter selection, receives the applied values */
   onValueChange?: (values: TimeFilterApplyValues) => void;
   /** When set an upgrade message will be shown if you select a period further back than this number of days */
   maxPeriodDays?: number;
-}
+};
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9837a90 and 6a34c0b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • apps/webapp/package.json
  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/tsql/src/index.test.ts
  • internal-packages/tsql/src/index.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/query/schema.ts
  • internal-packages/tsql/src/query/security.test.ts
💤 Files with no reviewable changes (1)
  • apps/webapp/app/env.server.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • apps/webapp/package.json
  • apps/webapp/app/v3/services/aiQueryTitleService.server.ts
  • internal-packages/tsql/src/query/schema.ts
  • apps/webapp/app/components/code/QueryResultsChart.tsx
  • internal-packages/clickhouse/src/index.ts
  • apps/webapp/app/components/primitives/AnimatedNumber.tsx
  • apps/webapp/app/presenters/v3/QueryPresenter.server.ts
  • apps/webapp/app/components/primitives/charts/ChartRoot.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHelpSidebar.tsx
  • internal-packages/database/prisma/migrations/20260124203524_customer_query_add_title_remove_cost/migration.sql
  • apps/webapp/app/v3/querySchemas.ts
  • apps/webapp/app/components/code/TSQLResultsTable.tsx
  • apps/webapp/app/components/primitives/Callout.tsx
  • apps/webapp/app/components/primitives/charts/Card.tsx
🧰 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}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/index.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/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/index.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/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
apps/webapp/**/*.{ts,tsx}

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

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

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/components/primitives/charts/ChartLine.tsx
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
  • apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx
  • internal-packages/tsql/src/index.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:

  • internal-packages/tsql/src/query/printer.ts
  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/clickhouse/src/client/tsql.ts
  • internal-packages/tsql/src/query/printer_context.ts
  • internal-packages/tsql/src/index.test.ts
  • apps/webapp/app/services/queryService.server.ts
  • internal-packages/tsql/src/index.ts
**/*.{test,spec}.{ts,tsx}

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

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g., MyService.ts) → MyService.test.ts

Files:

  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers helpers (redisTest, postgresTest, containerTest) from @internal/testcontainers for Redis/PostgreSQL testing instead of mocks

Files:

  • internal-packages/tsql/src/query/security.test.ts
  • internal-packages/tsql/src/query/printer.test.ts
  • internal-packages/tsql/src/index.test.ts
internal-packages/clickhouse/schema/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/clickhouse/schema/**/*.sql: ClickHouse migrations must use Goose format with -- +goose Up and -- +goose Down markers
Follow ClickHouse naming conventions: raw_ prefix for input tables, _v1, _v2 suffixes for versioning, _mv_v1 suffix for materialized views

Files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
apps/webapp/app/services/**/*.server.{ts,tsx}

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

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

Files:

  • apps/webapp/app/services/queryService.server.ts
🧠 Learnings (13)
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes

Applied to files:

  • internal-packages/database/prisma/schema.prisma
  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • internal-packages/tsql/src/query/security.test.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
📚 Learning: 2025-08-14T10:53:54.526Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2391
File: apps/webapp/app/services/organizationAccessToken.server.ts:50-0
Timestamp: 2025-08-14T10:53:54.526Z
Learning: In the Trigger.dev codebase, token service functions (like revokePersonalAccessToken and revokeOrganizationAccessToken) don't include tenant scoping in their database queries. Instead, authorization and tenant scoping happens at a higher level in the authentication flow (typically in route handlers) before these service functions are called. This is a consistent pattern across both Personal Access Tokens (PATs) and Organization Access Tokens (OATs).

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx
  • apps/webapp/app/services/queryService.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Set default maximum duration in trigger.config.ts using `maxDuration` property

Applied to files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 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:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 Learning: 2025-07-21T12:52:44.342Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2284
File: apps/webapp/app/services/realtimeClient.server.ts:111-127
Timestamp: 2025-07-21T12:52:44.342Z
Learning: Electric (the database service used in the realtimeClient) has built-in SQL injection protection and safely handles whereClause parameters passed via URL parameters, so direct string interpolation of runId values into SQL where clauses is safe when using Electric.

Applied to files:

  • internal-packages/tsql/src/query/security.test.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:

  • internal-packages/clickhouse/src/client/tsql.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • internal-packages/tsql/src/query/printer_context.ts
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : Follow ClickHouse naming conventions: `raw_` prefix for input tables, `_v1`, `_v2` suffixes for versioning, `_mv_v1` suffix for materialized views

Applied to files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : ClickHouse migrations must use Goose format with `-- +goose Up` and `-- +goose Down` markers

Applied to files:

  • internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to internal-packages/database/**/*.{ts,tsx} : Use Prisma for database interactions in internal-packages/database with PostgreSQL

Applied to files:

  • apps/webapp/app/services/queryService.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx
📚 Learning: 2026-01-12T17:18:09.451Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2870
File: apps/webapp/app/services/redisConcurrencyLimiter.server.ts:56-66
Timestamp: 2026-01-12T17:18:09.451Z
Learning: In `apps/webapp/app/services/redisConcurrencyLimiter.server.ts`, the query concurrency limiter will not be deployed with Redis Cluster mode, so multi-key operations (keyKey and globalKey in different hash slots) are acceptable and will function correctly in standalone Redis mode.

Applied to files:

  • apps/webapp/app/services/queryService.server.ts
🧬 Code graph analysis (7)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query.ai-title.tsx (5)
apps/webapp/app/utils/pathBuilder.ts (1)
  • EnvironmentParamSchema (26-28)
apps/webapp/app/models/project.server.ts (1)
  • findProjectBySlug (136-147)
apps/webapp/app/models/runtimeEnvironment.server.ts (1)
  • findEnvironmentBySlug (116-145)
apps/webapp/app/env.server.ts (1)
  • env (1320-1320)
apps/webapp/app/v3/services/aiQueryTitleService.server.ts (1)
  • AIQueryTitleService (15-71)
apps/webapp/app/components/runs/v3/SharedFilters.tsx (3)
apps/webapp/app/components/primitives/Callout.tsx (1)
  • Callout (73-170)
apps/webapp/app/components/primitives/Buttons.tsx (1)
  • LinkButton (335-403)
apps/webapp/app/utils/pathBuilder.ts (1)
  • organizationBillingPath (116-118)
internal-packages/tsql/src/query/printer.test.ts (2)
internal-packages/tsql/src/index.ts (5)
  • createPrinterContext (119-119)
  • TableSchema (113-113)
  • column (81-81)
  • createSchemaRegistry (82-82)
  • PrinterContext (121-121)
internal-packages/tsql/src/query/printer_context.ts (2)
  • createPrinterContext (249-256)
  • PrinterContext (79-214)
internal-packages/clickhouse/src/client/tsql.ts (3)
internal-packages/tsql/src/index.ts (1)
  • WhereClauseCondition (127-127)
internal-packages/clickhouse/src/index.ts (1)
  • WhereClauseCondition (59-59)
internal-packages/tsql/src/query/printer_context.ts (1)
  • WhereClauseCondition (47-47)
internal-packages/tsql/src/query/printer_context.ts (1)
internal-packages/tsql/src/query/schema.ts (2)
  • SchemaRegistry (344-349)
  • FieldMappings (270-270)
internal-packages/tsql/src/index.test.ts (3)
internal-packages/tsql/src/index.ts (4)
  • column (81-81)
  • TableSchema (113-113)
  • WhereClauseCondition (127-127)
  • compileTSQL (538-566)
internal-packages/clickhouse/src/index.ts (2)
  • TableSchema (54-54)
  • WhereClauseCondition (59-59)
internal-packages/tsql/src/query/printer_context.ts (1)
  • WhereClauseCondition (47-47)
apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (2)
apps/webapp/app/utils/cn.ts (1)
  • cn (77-79)
apps/webapp/app/components/primitives/AnimatedNumber.tsx (1)
  • AnimatedNumber (22-58)
⏰ 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). (18)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 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 (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
🔇 Additional comments (15)
apps/webapp/app/components/primitives/charts/ChartLegendCompound.tsx (3)

20-22: LGTM!

The scrollable prop is well-documented and defaults to false to maintain backward compatibility.

Also applies to: 42-42


134-158: LGTM!

The scrollable container implementation is correct:

  • min-h-0 on flex children properly allows shrinking below content size.
  • shrink-0 on the total row and separator prevents them from being compressed.
  • flex-1 with overflow-y-auto on the items container enables proper scrolling behavior.

159-216: LGTM!

Legend items rendering is clean with proper hover interactions and active state highlighting preserved within the new scrollable structure.

apps/webapp/app/components/primitives/charts/ChartLine.tsx (1)

175-175: LGTM!

Consistent reduction of strokeWidth from 2 to 1 for both Area and Line chart variants results in a cleaner, thinner visual appearance as intended.

Also applies to: 223-223

internal-packages/clickhouse/schema/014_update_output_error_text_to_extract_data.sql (3)

1-1: Goose format and naming conventions followed correctly.

The migration properly uses -- +goose Up and -- +goose Down markers, and the table task_runs_v2 follows the required _v2 suffix convention. Based on learnings and coding guidelines.

Also applies to: 34-34


34-45: Down migration is correctly ordered.

Dropping indexes before columns is the correct order (ClickHouse would fail otherwise), and IF EXISTS guards provide safe rollback behavior.


6-15: ADD COLUMN is correct—these are new columns, not existing ones to modify.

The columns output_text and error_text do not exist in any previous migration (001–013). This migration is their initial creation, so ADD COLUMN is the appropriate statement. The MODIFY COLUMN concern is not applicable here.

The migration correctly uses Goose format (-- +goose Up and -- +goose Down markers) and includes proper rollback operations.

Likely an incorrect or invalid review comment.

apps/webapp/app/components/primitives/Resizable.tsx (3)

29-38: Verify data-handle-orientation is emitted by PanelResizer.
These selectors depend on the library setting that attribute; if it differs, vertical styling won’t apply. Please confirm the actual attribute name and adjust if needed.


44-47: Orientation-specific line indicators look good.
The separate horizontal/vertical indicators and hover transitions are clear and consistent.


50-61: Dot layout switching per orientation is clear.
The hidden/flex toggles make the handle dots adapt cleanly for each orientation.

internal-packages/database/prisma/schema.prisma (1)

2455-2456: Optional query title field looks good.

This aligns with the new AI title feature without forcing existing rows to migrate.

internal-packages/clickhouse/src/client/tsql.ts (1)

42-103: enforcedWhereClause documentation and examples read well.

The API intent is clear and aligns with the enforced tenant/plan guard behavior.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/QueryHistoryPopover.tsx (1)

122-135: Title + query rendering looks solid.

The two-line layout with expanded clamp and preserved whitespace fits the new title feature nicely.

internal-packages/tsql/src/query/security.test.ts (1)

55-61: Tests updated cleanly for enforcedWhereClause.

The new default options and optional-guard cases still cover tenant isolation paths well.

internal-packages/tsql/src/query/printer.test.ts (1)

666-1006: Great coverage for JSON textColumn/dataPrefix behavior.

These scenarios should catch regressions in SELECT/WHERE/GROUP BY handling.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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.

2 participants