Skip to content

Conversation

@mpcgrid
Copy link
Collaborator

@mpcgrid mpcgrid commented Jan 15, 2026

Closes #

✅ Checklist

  • I have followed every step in the contributing guide
  • The PR title follows the convention.
  • I ran and tested the code works

Testing

[Describe the steps you took to test this change]


Changelog

[Short description of what has changed]


Screenshots

[Screenshots]

💯

added support for tasks_v1 for logs
cleaned the No logs message
extended LogsPresenter with BasePresenter for spans
@changeset-bot
Copy link

changeset-bot bot commented Jan 15, 2026

⚠️ No Changeset found

Latest commit: c55ab1e

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 15, 2026

Walkthrough

This PR introduces multi-backend event storage support by adding organization-based repository configuration that routes between ClickHouse V1, ClickHouse V2, and PostgreSQL. It renames and versions existing log query builders into V1 and V2 variants, updates the underlying data schema from metadata/attributes fields to a unified attributes_text field, implements error handling for unsupported backends, and gates logs page access through a new feature flag. The changes span presenters, routes, utility functions, and the ClickHouse query builder library.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Detailed notes

The review complexity stems from:

  • Logic density across multiple files: Three presenters (LogsListPresenter, LogDetailPresenter, and route.tsx) each contain conditional store selection and error handling that must be verified for correctness.
  • Heterogeneous changes requiring separate reasoning: UI component simplification, presenter refactoring, route-level error handling, feature flag introduction, and ClickHouse query builder versioning each demand distinct review perspectives.
  • Interconnected schema and API changes: The shift from metadata/attributes to attributes_text propagates across query results, presenters, and error message extraction logic. Query builder renaming and versioning (V1/V2 split) affects multiple surface layers.
  • New feature flag and access control: Introduction of hasLogsPageAccess flag adds runtime branching that must be validated against expected feature flag behavior.
  • Error handling for unsupported backend: PostgreSQL detection and ServiceValidationError throwing introduces new error paths that need validation in both presenter and route contexts.
🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely unmodified from the template and lacks any substantive content; testing, changelog, and changelog sections are unfilled placeholder text. Fill out the PR description with the issue number, testing steps taken, a summary of changes, and any relevant screenshots. Ensure the checklist items are completed and marked.
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% 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 'logs-page-fixes' is vague and generic, using non-descriptive phrasing that doesn't convey meaningful information about the specific changes. Use a more descriptive title that highlights the primary change, such as 'Add feature flag gating for logs page access and support v1/v2 ClickHouse query builders'.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@mpcgrid
Copy link
Collaborator Author

mpcgrid commented Jan 15, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@claude
Copy link

claude bot commented Jan 15, 2026

PR Review: logs-page-fixes

Summary

This PR makes several improvements to the logs page functionality, including:

  • Adding support for ClickHouse v1 table queries (previously only v2 was supported)
  • Removing the EVENT_REPOSITORY_CLICKHOUSE_ROLLOUT_PERCENT environment variable
  • Adding a new hasLogsPageAccess feature flag
  • Fixing attribute handling to use attributes_text consistently
  • Removing unused hasFilters prop from LogsTable

Issues Found

1. Critical: Unused hasLogsPageAccess function in route loader

📍 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx:61-93

The hasLogsPageAccess function is defined but never called in the loader. The loader at line 101-103 still only checks isAdmin:

if (!isAdmin) {
  throw redirect("/");
}

This means the new feature flag will not work as intended - users with the hasLogsPageAccess flag enabled will still be redirected. The function should be called and its result used in the access check.

Suggested fix:

const hasAccess = await hasLogsPageAccess(userId, user.admin, user.isImpersonating, organizationSlug);
if (!hasAccess) {
  throw redirect("/");
}

2. Inconsistency between SideMenu and route access control

📍 apps/webapp/app/components/navigation/SideMenu.tsx:272

The SideMenu shows the Logs link based on featureFlags.hasLogsPageAccess, but the route loader doesn't respect this flag. This creates a broken UX where users see the menu item but get redirected when clicking it.


Potential Issues

3. LogsListPresenter constructor doesn't call super() with arguments

📍 apps/webapp/app/presenters/v3/LogsListPresenter.server.ts:171-176

The presenter now extends BasePresenter but doesn't pass the replica to the parent constructor:

constructor(
  private readonly replica: PrismaClientOrTransaction,
  private readonly clickhouse: ClickHouse
) {
  super();  // Should be super(undefined, replica) or similar
}

While this works because the parent has default values, it's inconsistent - the class has its own replica field AND the parent has _replica. Consider either:

  • Using the parent's _replica instead of declaring a new one
  • Or not extending BasePresenter if the tracing methods aren't being used

4. Inconsistent string quotes in error messages

📍 apps/webapp/app/v3/eventRepository/index.server.ts:41,60

Mixed quote styles:

throw new Error('Organization not found when configuring event repository');
// vs
return { repository: eventRepository, store: 'postgres' };

Consider using double quotes consistently to match the rest of the codebase.


Minor Suggestions

5. Extra blank line in route file

📍 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx:93-95

There's an extra blank line after the hasLogsPageAccess function that could be cleaned up.

6. Double blank line in LogsListPresenter

📍 apps/webapp/app/presenters/v3/LogsListPresenter.server.ts around the imports

There's an extra blank line in the import section that could be removed for consistency.


Code Quality

Positive aspects:

  • Good error handling with ServiceValidationError for store mismatches
  • Clean separation between v1 and v2 query builders
  • Proper use of feature flags for access control (once the bug is fixed)
  • Correct handling of attributes_text field instead of the generic attributes

Test coverage:

  • No tests appear to be added for the new functionality. Consider adding tests for:
    • getConfiguredEventRepository function
    • V1 query builders
    • The hasLogsPageAccess logic

Verdict

The PR has good intent but has a critical bug where the hasLogsPageAccess function is defined but never used in the route loader. This should be fixed before merging, as it will cause confusion when users have the feature flag enabled but still cannot access the logs page.

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: 2

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/presenters/v3/LogDetailPresenter.server.ts (1)

78-94: Remove dead code for log.metadata parsing.

The metadata column was removed from both query builders (getLogDetailQueryBuilderV1 and getLogDetailQueryBuilderV2) in commit c55ab1e. The log.metadata field will always be undefined, making the parsing block on lines 78-84 and the rawMetadata assignment on line 113 dead code. Either restore the metadata column to the queries if it's needed, or remove the unused metadata parsing logic.

🤖 Fix all issues with AI agents
In `@apps/webapp/app/components/logs/LogsTable.tsx`:
- Around line 127-129: The empty-state message currently shown in LogsTable when
logs.length === 0 still mentions filters even though hasFilters was removed;
update the BlankState usage in LogsTable (or the BlankState component itself) to
show a generic message like "No logs found." or to accept a prop that
differentiates filtered vs. empty results; specifically, either restore a
boolean (e.g., hasFilters) passed to BlankState from LogsTable based on current
filter state or change the displayed text in the BlankState call inside
LogsTable to a neutral message so users who haven't applied filters aren't
misled.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx:
- Around line 61-93: The loader currently only checks isAdmin and redirects
non-admins, bypassing the feature flag check; replace that simple admin check
with an awaited call to hasLogsPageAccess(userId, isAdmin, isImpersonating,
organizationSlug) and use its boolean result to decide access; specifically,
remove or replace the if (!isAdmin) { throw redirect("/") } logic with: await
hasLogsPageAccess(...) and if it returns false then throw redirect("/"); ensure
you pass the same userId, isAdmin, isImpersonating, and organizationSlug
variables and keep existing redirect behavior for denied access.
🧹 Nitpick comments (3)
apps/webapp/app/v3/eventRepository/index.server.ts (1)

27-42: Consider using $replica for read operations.

The function queries organization data using prisma directly, but other parts of the codebase (e.g., RunsRepository, presenters) use $replica for read operations to distribute load. Additionally, the error thrown is a generic Error rather than ServiceValidationError used elsewhere in the codebase for validation errors.

♻️ Suggested improvements
+import { $replica } from "~/db.server";
+import { ServiceValidationError } from "~/v3/services/baseService.server";

 export async function getConfiguredEventRepository(
   organizationId: string
 ): Promise<{ repository: IEventRepository; store: string }> {
-  const organization = await prisma.organization.findFirst({
+  const organization = await $replica.organization.findFirst({
     select: {
       id: true,
       featureFlags: true,
     },
     where: {
       id: organizationId,
     },
   });

   if (!organization) {
-    throw new Error('Organization not found when configuring event repository');
+    throw new ServiceValidationError('Organization not found when configuring event repository');
   }
apps/webapp/app/presenters/v3/LogsListPresenter.server.ts (1)

545-555: Use const for the parsed attributes variable.

The attributes variable is never reassigned, so it should be declared with const instead of let.

♻️ Suggested fix
       if (log.status === "ERROR" && log.attributes_text) {
         try {
-          let attributes = JSON.parse(log.attributes_text) as ErrorAttributes;
+          const attributes = JSON.parse(log.attributes_text) as ErrorAttributes;

           if (attributes?.error?.message && typeof attributes.error.message === "string") {
             displayMessage = attributes.error.message;
           }
         } catch {
           // If attributes parsing fails, use the regular message
         }
       }
internal-packages/clickhouse/src/taskEvents.ts (1)

353-375: Consider renaming the result type for clarity.

The V1 detail builder uses LogDetailV2Result as its result type. While functionally correct (same schema), this naming could be confusing. Consider either:

  1. Creating a LogDetailResult type alias that both versions use
  2. Renaming LogDetailV2Result to LogDetailResult since it's shared
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4dc504d and c55ab1e.

📒 Files selected for processing (11)
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/env.server.ts
  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/v3/featureFlags.server.ts
  • internal-packages/clickhouse/src/index.ts
  • internal-packages/clickhouse/src/taskEvents.ts
💤 Files with no reviewable changes (1)
  • apps/webapp/app/env.server.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{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

Every Trigger.dev task must be exported; use task() function with unique id and run async function

Files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • internal-packages/clickhouse/src/taskEvents.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
  • internal-packages/clickhouse/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/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • internal-packages/clickhouse/src/taskEvents.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
  • internal-packages/clickhouse/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

In webapp development, access environment variables via env export from apps/webapp/app/env.server.ts; never use process.env directly

Files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/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

Files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • internal-packages/clickhouse/src/taskEvents.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
  • internal-packages/clickhouse/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:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/v3/featureFlags.server.ts
  • internal-packages/clickhouse/src/taskEvents.ts
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • internal-packages/clickhouse/src/index.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js}: Import from @trigger.dev/core using subpaths only; never import from root of @trigger.dev/core
Always import task definitions from @trigger.dev/sdk, never from @trigger.dev/sdk/v3 or deprecated client.defineJob pattern

Files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/v3/featureFlags.server.ts
  • internal-packages/clickhouse/src/taskEvents.ts
  • apps/webapp/app/components/logs/LogsTable.tsx
  • apps/webapp/app/v3/eventRepository/index.server.ts
  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
  • internal-packages/clickhouse/src/index.ts
🧠 Learnings (19)
📚 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/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes

Applied to files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/presenters/v3/LogsListPresenter.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 : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/services/**/*.server.{ts,tsx} : Organize services in the webapp following the pattern `app/v3/services/*/*.server.ts`

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.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/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
📚 Learning: 2026-01-12T11:01:34.792Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:01:34.792Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : Edit database schema in internal-packages/database/prisma/schema.prisma; remove extraneous lines from migrations related to BackgroundWorker, TaskRun tags, waitpoints, and SecretStore indexes

Applied to files:

  • internal-packages/clickhouse/src/taskEvents.ts
  • internal-packages/clickhouse/src/index.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} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

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

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • internal-packages/clickhouse/src/index.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
📚 Learning: 2026-01-12T11:01:34.792Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:01:34.792Z
Learning: Applies to **/*.{ts,tsx} : Every Trigger.dev task must be exported; use task() function with unique id and run async function

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
📚 Learning: 2026-01-12T11:01:34.792Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-12T11:01:34.792Z
Learning: Applies to **/*.{ts,tsx,js} : Always import task definitions from trigger.dev/sdk, never from trigger.dev/sdk/v3 or deprecated client.defineJob pattern

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • internal-packages/clickhouse/src/index.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} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.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 : Configure Trigger.dev project in `trigger.config.ts` using `defineConfig()` with project ref and task directories

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.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} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • internal-packages/clickhouse/src/index.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} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • apps/webapp/app/presenters/v3/LogsListPresenter.server.ts
  • internal-packages/clickhouse/src/index.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 : Define global lifecycle functions (onStart, onSuccess, onFailure) in trigger.config.ts to apply to all tasks

Applied to files:

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

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.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:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.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} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`

Applied to files:

  • internal-packages/clickhouse/src/index.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} : Use lifecycle functions (init, cleanup, onStart, onSuccess, onFailure, handleError) for task event handling

Applied to files:

  • internal-packages/clickhouse/src/index.ts
🧬 Code graph analysis (4)
apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts (1)
apps/webapp/app/v3/eventRepository/index.server.ts (1)
  • getConfiguredEventRepository (27-61)
internal-packages/clickhouse/src/taskEvents.ts (2)
internal-packages/clickhouse/src/client/types.ts (1)
  • ClickhouseReader (60-199)
internal-packages/clickhouse/src/index.ts (1)
  • ClickHouseSettings (2-2)
apps/webapp/app/v3/eventRepository/index.server.ts (3)
apps/webapp/app/v3/eventRepository/eventRepository.types.ts (1)
  • IEventRepository (314-411)
apps/webapp/app/v3/eventRepository/clickhouseEventRepositoryInstance.server.ts (2)
  • clickhouseEventRepositoryV2 (11-14)
  • clickhouseEventRepository (6-9)
apps/webapp/app/v3/eventRepository/eventRepository.server.ts (1)
  • eventRepository (1441-1441)
internal-packages/clickhouse/src/index.ts (1)
internal-packages/clickhouse/src/taskEvents.ts (4)
  • getLogsListQueryBuilderV1 (329-351)
  • getLogDetailQueryBuilderV1 (353-375)
  • getLogsListQueryBuilderV2 (257-279)
  • getLogDetailQueryBuilderV2 (301-323)
⏰ 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 (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 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 (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
🔇 Additional comments (22)
apps/webapp/app/components/logs/LogsTable.tsx (1)

211-234: LGTM!

The BlankState component is well-structured with proper loading state handling. The colSpan=6 correctly accounts for all table columns including the menu column.

apps/webapp/app/v3/featureFlags.server.ts (1)

9-9: LGTM!

The new hasLogsPageAccess feature flag follows the established pattern, uses z.coerce.boolean() consistent with the hasQueryAccess flag, and integrates properly with the existing feature flag infrastructure.

Also applies to: 17-17

apps/webapp/app/v3/eventRepository/index.server.ts (1)

44-61: LGTM!

The resolution logic is well-documented with clear priority ordering, and the store selection matches the patterns used in getEventRepository and getV3EventRepository.

apps/webapp/app/presenters/v3/LogsListPresenter.server.ts (4)

171-177: LGTM!

The class correctly extends BasePresenter and calls super() in the constructor. This aligns with the presenter pattern documented in learnings for apps/webapp/app/v3/presenters/**/*.server.ts.


368-383: Store-based query builder selection is well-implemented.

The conditional logic correctly routes to the appropriate ClickHouse version. Note that the repository from getConfiguredEventRepository is unused here since the presenter directly uses the ClickHouse query builders.


394-424: LGTM!

The conditional inserted_at PREWHERE clause correctly applies partition pruning only for the V2 table (which is partitioned by inserted_at), while start_time filtering in WHERE is applied for both versions. The inline comments clearly explain the reasoning.


137-152: LGTM!

The levelToKindsAndStatuses function provides a clean mapping from display levels to ClickHouse query constraints. The logic correctly handles the different level types including the special cases for ERROR and CANCELLED which map to statuses.

internal-packages/clickhouse/src/taskEvents.ts (3)

257-279: LGTM!

The V2 query builders are correctly renamed and target the task_events_v2 table. The column selection including attributes_text is consistent across both list and detail builders.

Also applies to: 301-323


329-375: The attributes_text column exists in task_events_v1 and is properly selected.

Verification confirms that attributes_text is a MATERIALIZED column in the task_events_v1 table schema (attributes_text String MATERIALIZED toJSONString(attributes)), and all columns selected by both getLogsListQueryBuilderV1 and getLogDetailQueryBuilderV1 are present in the schema. The queries are correct and will function as intended.


238-253: Schema change from metadata/attributes to attributes_text is properly handled.

The LogsListResult schema has been updated to use attributes_text instead of separate metadata and attributes fields. This is a breaking change, but verification confirms the only consumer—LogsListPresenter—has already been updated to correctly access attributes_text rather than the old fields.

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

26-29: LGTM!

The imports are well-organized, clearly versioned, and properly separate V1 and V2 query builders for the respective task event paths.


209-228: LGTM!

The version alignment is consistent: taskEvents uses V1 builders targeting task_events_v1 table, while taskEventsV2 uses V2 builders targeting task_events_v2 table. Settings are correctly passed through for both paths.

apps/webapp/app/presenters/v3/LogDetailPresenter.server.ts (3)

5-6: LGTM!

Proper imports for organization-based store configuration and error handling.


29-43: LGTM!

The store detection and query builder selection logic is correct:

  • PostgreSQL throws a clear error since logs aren't supported there
  • V2 store uses V2 builder, otherwise falls back to V1 builder

The conditional branching is clean and maintainable.


87-91: LGTM!

Correctly parses attributes_text as the source for attributes data, aligning with the updated schema.

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs.$logId.tsx (2)

10-10: LGTM!

Correct import for error handling.


47-62: LGTM!

The error handling pattern is clean and appropriate:

  • ServiceValidationError maps to HTTP 400 (bad request) with the error message
  • Other errors are re-thrown to preserve stack traces and allow upstream error handling
  • This aligns with the presenter's store validation logic
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx (4)

3-3: LGTM!

Correct import for error handling in the loader.


126-140: LGTM!

The error handling pattern correctly catches ServiceValidationError and transforms it into a structured error object that the UI can handle gracefully. Other errors are re-thrown to preserve error boundaries.


195-215: LGTM!

Clean error-first rendering pattern that checks for the error object and displays a user-friendly callout before attempting to render the logs list.


230-230: LGTM!

Good use of TypeScript's Exclude utility type to ensure type safety by removing the error case from the list prop type, since error cases are handled separately in the parent component.

apps/webapp/app/components/navigation/SideMenu.tsx (1)

272-281: LGTM!

The visibility condition follows the established pattern used for the Query menu item (line 289) and correctly gates the Logs menu item behind:

  • Admin access, or
  • Impersonation, or
  • The hasLogsPageAccess feature flag

This is consistent with the codebase conventions.

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

Comment on lines +127 to 129
{logs.length === 0 ? (
<BlankState isLoading={isLoading} onRefresh={() => window.location.reload()} />
) : (
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

Empty state message may be inaccurate when no filters are applied.

Since hasFilters was removed, the message "No logs match your filters. Try refreshing or modifying your filters." displays even when no filters are applied. This could confuse users who haven't set any filters.

Consider differentiating the message based on whether filters are actually applied, or use a more generic message like "No logs found."

🤖 Prompt for AI Agents
In `@apps/webapp/app/components/logs/LogsTable.tsx` around lines 127 - 129, The
empty-state message currently shown in LogsTable when logs.length === 0 still
mentions filters even though hasFilters was removed; update the BlankState usage
in LogsTable (or the BlankState component itself) to show a generic message like
"No logs found." or to accept a prop that differentiates filtered vs. empty
results; specifically, either restore a boolean (e.g., hasFilters) passed to
BlankState from LogsTable based on current filter state or change the displayed
text in the BlankState call inside LogsTable to a neutral message so users who
haven't applied filters aren't misled.

Comment on lines +61 to +93
async function hasLogsPageAccess(
userId: string,
isAdmin: boolean,
isImpersonating: boolean,
organizationSlug: string
): Promise<boolean> {
if (isAdmin || isImpersonating) {
return true;
}

// Check organization feature flags
const organization = await prisma.organization.findFirst({
where: {
slug: organizationSlug,
members: { some: { userId } },
},
select: {
featureFlags: true,
},
});

if (!organization?.featureFlags) {
return false;
}

const flags = organization.featureFlags as Record<string, unknown>;
const hasLogsPageAccessResult = validateFeatureFlagValue(
FEATURE_FLAG.hasLogsPageAccess,
flags.hasLogsPageAccess
);

return hasLogsPageAccessResult.success && hasLogsPageAccessResult.data === true;
}
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

hasLogsPageAccess is defined but never called.

This helper function is implemented but not used anywhere in the loader. The loader at lines 101-103 still only checks isAdmin and redirects non-admins to /:

if (!isAdmin) {
  throw redirect("/");
}

The feature flag check via hasLogsPageAccess is bypassed entirely. If the intent is to gate access based on the hasLogsPageAccess feature flag (as done in SideMenu.tsx), this function should be called in the loader.

Suggested fix
  const isAdmin = user.admin || user.isImpersonating;

- if (!isAdmin) {
-   throw redirect("/");
- }
+ const hasAccess = await hasLogsPageAccess(
+   userId,
+   user.admin,
+   user.isImpersonating,
+   organizationSlug
+ );
+
+ if (!hasAccess) {
+   throw redirect("/");
+ }
🤖 Prompt for AI Agents
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.logs/route.tsx
around lines 61 - 93, The loader currently only checks isAdmin and redirects
non-admins, bypassing the feature flag check; replace that simple admin check
with an awaited call to hasLogsPageAccess(userId, isAdmin, isImpersonating,
organizationSlug) and use its boolean result to decide access; specifically,
remove or replace the if (!isAdmin) { throw redirect("/") } logic with: await
hasLogsPageAccess(...) and if it returns false then throw redirect("/"); ensure
you pass the same userId, isAdmin, isImpersonating, and organizationSlug
variables and keep existing redirect behavior for denied access.

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