Skip to content

feat: deployment runtime logs#4910

Merged
chronark merged 42 commits intomainfrom
deployment-runtime-logs
Feb 4, 2026
Merged

feat: deployment runtime logs#4910
chronark merged 42 commits intomainfrom
deployment-runtime-logs

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Feb 3, 2026

What does this PR do?

  • All the major UI issues are fixed
  • Runtime logs are available both on project overview and deployment overview. Its lacking the "Live view", i'll add this later when I refactor /sentinel-logs page
  • Table resizes properly for laptop screens/smaller screens
  • Sidebar now goes one level deeper and shows deployments items too -> Network, runtime logs, settings etc...
  • Sometimes domains were not loading after a fresh deploy, thats fixed now

How should this be tested?

  • Run make dev
  • Run go run . dev seed local
  • Make a deployment
  • Run go run . dev seed sentinel --deployment-id="d_xxxx"
  • Make some automated requests as well to test RPS on network tab, coz it only fetches last 15 minutes AVG.
Screen.Recording.2026-02-03.at.19.10.43.mov

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

Adds per-column cell styling support to virtual tables and applies it across table skeletons; introduces a full runtime-logs feature (UI, hooks, TRPC query, ClickHouse integration, and LLM-backed search); adds region flag components; and refactors deployment detail navigation/layout and related UI/formatters.

Changes

Cohort / File(s) Summary
Virtual Table Core
web/apps/dashboard/components/virtual-table/index.tsx, web/apps/dashboard/components/virtual-table/types.ts
Added cellClassName to Column and tableLayout config; implemented colgroup-based widths and propagate column.cellClassName to header, body and skeleton cells.
Table Consumers (skeleton styling)
web/apps/dashboard/app/(app)/.../keys-list.tsx, .../permissions-list.tsx, .../roles-list.tsx, .../identities-list.tsx, .../root-keys-list.tsx, .../deployments-list.tsx
Applied column.cellClassName to skeleton row td elements across multiple table components; deployments-list also updated widths, responsive visibility, and CPU/memory formatting usage.
Runtime Logs — UI & Context
.../runtime-logs/page.tsx, .../types.ts, .../context/runtime-logs-provider.tsx
Added runtime logs page, provider/context for selected log state, and centralized type exports.
Runtime Logs — Hooks & Query
.../hooks/use-runtime-logs-filters.ts, .../table/hooks/use-runtime-logs-query.ts, .../hooks/use-runtime-logs.ts
New hooks: URL-driven filters management (severity/message/time) and infinite-scrolling runtime logs query (TRPC) with periodic refetch.
Runtime Logs — Controls & Search (LLM)
.../controls/**, .../controls/components/runtime-logs-search/index.tsx
New controls: search (LLM-backed mutation), filters, datetime selector, refresh, and ControlCloud integration for managing filters and searches.
Runtime Logs — Table & Details UI
.../table/runtime-logs-table.tsx, .../table/runtime-log-details/**
Virtualized runtime logs table with severity styling, selection, load-more, and a details panel for selected log.
Runtime Logs — Server & ClickHouse
web/apps/dashboard/lib/trpc/routers/deploy/runtime-logs/query.ts, .../llm-search/*, web/internal/clickhouse/src/runtime-logs.ts, web/internal/clickhouse/src/index.ts
Added TRPC query and LLM mutation endpoints, LLM prompt/response utils, server-side filter transform, and ClickHouse runtime logs query module exposed via ClickHouse.runtimeLogs.
LLM Search Utilities
.../llm-search/utils.ts
New logic to construct system prompt, call OpenAI, parse & validate structured filter output, and surface errors as TRPCError.
Deployment Detail Layout & Navigation
.../deployments/.../layout.tsx, .../deployment-navbar.tsx, .../use-deployment-breadcrumb-config.ts
New DeploymentLayout and DeploymentNavbar components and hook for breadcrumb config and quick-nav integration.
Region Flags & Formatting
.../components/region-flag.tsx, .../components/region-flags.tsx, web/apps/dashboard/lib/utils/deployment-formatters.ts, web/apps/dashboard/lib/utils/metric-formatters.ts
Added RegionFlag/RegionFlags components and formatters (CPU, memory, latency); replaced inline flag/format code with these utilities.
Deployment & Project UI Adjustments
.../deployment-info-section.tsx, .../latency-badge.tsx, projects/.../page.tsx, layout.tsx, project-navigation.tsx
Refactored deployment info rendering to use RegionFlags/formatters, simplified status badge and navbar behavior, and adjusted page-level wiring (liveDeploymentId, navigation visibility).
Active Deployment Card & Logs refactor
.../active-deployment-card-logs/**, .../hooks/use-deployment-logs.tsx, .../providers/deployment-logs-provider.tsx
Removed build-steps path, added logType state (sentinel/runtime), rewired provider and hooks, and added RuntimeLogsContent and UI controls to select log type.
Project Details / Empty States / UI tweaks
.../custom-domains-section/**, .../env-variables-section/**, .../project-details-expandables/sections.tsx
Enhanced empty states, button styling, and replaced hard-coded regions with RegionFlags and formatters.
Navigation / Sidebar
components/navigation/.../nested-nav-item.tsx, .../use-projects-navigation.tsx
Increased default maxDepth and injected deployment-detail sub-items (Overview, Runtime Logs, Network) into project Deployments nav when viewing a deployment detail. Removed OpenAPI Diff/Settings top-level items.
Schemas & Types
web/apps/dashboard/lib/schemas/runtime-logs.filter.schema.ts, web/apps/dashboard/lib/schemas/runtime-logs.schema.ts
New Zod schemas and types for runtime-log requests/responses and filter config (fields, operators, color mapping).
Backend Data & Mapping
web/apps/dashboard/lib/trpc/routers/deploy/deployment/list.ts, web/apps/dashboard/lib/trpc/routers/deploy/network/utils.ts, web/apps/dashboard/lib/collections/deploy/deployments.ts
Added instance.region selection and flagCode mapping; exported flagCodes and derived FlagCode type; deployment collection schema extended to include region/flagCode.
Log Types
web/apps/dashboard/components/logs/details/log-details/index.tsx
Expanded SupportedLogTypes union to include RuntimeLog.

Sequence Diagrams

sequenceDiagram
    participant User
    participant UI as Runtime Logs UI
    participant Hooks as Filters & Query Hooks
    participant TRPC as TRPC Client
    participant Server as Runtime Logs Server
    participant CH as ClickHouse
    User->>UI: Apply filter / LLM search / open page
    UI->>Hooks: updateFilters / trigger search / mount
    Hooks->>TRPC: call deploy.runtimeLogs.query (filters, cursor)
    TRPC->>Server: validated request
    Server->>Server: transformFilters
    Server->>CH: SQL queries (logs + total)
    CH-->>Server: rows + total
    Server-->>TRPC: response (logs, hasMore, nextCursor)
    TRPC-->>Hooks: update cache
    Hooks->>UI: provide logs and pagination
    UI-->>User: render table, load-more, details
Loading
sequenceDiagram
    participant User
    participant UI as LLM Search UI
    participant TRPCmut as TRPC Mutation
    participant Server as Runtime Logs Server
    participant OpenAI as OpenAI API
    participant Schema as Filter Schema
    User->>UI: Submit natural-language query
    UI->>TRPCmut: deploy.runtimeLogs.llmSearch {query, timestamp}
    TRPCmut->>Server: validated input
    Server->>Server: build system prompt (field/operator docs)
    Server->>OpenAI: chat.completions(system+user)
    OpenAI-->>Server: JSON response
    Server->>Schema: validate against runtimeLogsFilterOutputSchema
    Schema-->>Server: validated structured filters
    Server-->>TRPCmut: return structured filters
    TRPCmut-->>UI: structured result
    UI->>Hooks: updateFilters(structured filters)
    UI-->>User: show applied filters / results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • refactor: deployment overview page v2 #4804: Touches the virtual-table implementation and types — likely related to the cellClassName and layout changes.
  • feat: key details #3242: Modifies virtual-table (index/types) to add row event callbacks — overlaps with table core changes and may conflict/coordinate with this PR's layout and props changes.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: deployment runtime logs' clearly and concisely describes the main feature added in this changeset—runtime logs for deployments—and is directly supported by the raw summary showing extensive additions of runtime logs functionality.
Description check ✅ Passed The PR description covers the main changes (UI fixes, runtime logs on project/deployment overviews, table resizing, sidebar improvements, domain loading fix) and includes testing steps with specific commands and context, though it does not follow the template structure precisely and omits some checkbox items.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch deployment-runtime-logs

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


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

@vercel
Copy link

vercel bot commented Feb 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview, Comment Feb 4, 2026 3:05pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
engineering Ignored Ignored Preview Feb 4, 2026 3:05pm

Request Review

@vercel vercel bot temporarily deployed to Preview – dashboard February 3, 2026 16:28 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard February 3, 2026 16:43 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard February 4, 2026 10:22 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard February 4, 2026 10:27 Inactive
@vercel vercel bot temporarily deployed to Preview – engineering February 4, 2026 10:28 Inactive
@ogzhanolguncu ogzhanolguncu marked this pull request as ready for review February 4, 2026 10:40
@vercel vercel bot temporarily deployed to Preview – dashboard February 4, 2026 10:43 Inactive
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: 18

Caution

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

⚠️ Outside diff range comments (7)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/navigations/project-navigation.tsx (1)

118-136: ⚠️ Potential issue | 🟡 Minor

Inconsistent height class between loading and loaded states may cause layout shift.

The loaded Navbar at Line 145 includes className="h-[65px]", but the loading state Navbar at Line 119 does not. Since the base Navbar component uses min-h-[65px], the loading state could have a variable height while the loaded state is fixed, potentially causing a visual jump when loading completes.

🔧 Proposed fix to add consistent height to loading state
     return (
-      <Navbar>
+      <Navbar className="h-[65px]">
         <Navbar.Breadcrumbs icon={<Cube />}>

Also applies to: 145-145

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/deployment-status-badge.tsx (1)

39-47: ⚠️ Potential issue | 🟠 Major

Type definition contradicts runtime behavior.

The status prop is typed as optional (status?: DeploymentStatus), but the component throws an error when status is falsy. This creates a type/runtime mismatch—callers can legally pass undefined according to TypeScript, yet that causes a runtime exception.

If status is required, remove the ? to make this a compile-time error instead of a runtime one. As per coding guidelines: "Make illegal states unrepresentable."

🛠️ Proposed fix
 type Props = {
-  status?: DeploymentStatus;
+  status: DeploymentStatus;
   className?: string;
 };

-export const DeploymentStatusBadge = ({ status, className }: Props) => {
-  if (!status) {
-    throw new Error(`Invalid deployment status: ${status}`);
-  }
-
+export const DeploymentStatusBadge = ({ status, className }: Props) => {
   const config = STATUS_CONFIG[status];
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/active-deployment-card-logs/hooks/use-deployment-logs.tsx (2)

172-177: ⚠️ Potential issue | 🟡 Minor

Auto-expand effect may cause unwanted expansion after user collapses.

If a user manually collapses the logs panel, this effect will re-expand it whenever logs.length changes (e.g., on the next poll). Consider tracking whether the user has manually collapsed to avoid overriding their preference.

Possible approach
+ const [userCollapsed, setUserCollapsed] = useState(false);

  // Auto-expand when logs are available
  useEffect(() => {
-   if (logs.length > 0) {
+   if (logs.length > 0 && !userCollapsed) {
      setIsExpanded(true);
    }
- }, [logs.length]);
+ }, [logs.length, userCollapsed]);

  const setExpanded = (expanded: boolean) => {
    setIsExpanded(expanded);
+   if (!expanded) {
+     setUserCollapsed(true);
+   }
    if (!expanded) {
      setTimeout(resetScroll, SCROLL_RESET_DELAY);
    }
  };

50-52: ⚠️ Potential issue | 🟠 Major

deploymentId parameter is accepted but never used.

The hook accepts deploymentId but the sentinel query at lines 72-89 doesn't filter by it. The queryLogs endpoint doesn't support deployment-level filtering—the input schema (logsRequestSchema) lacks a deploymentId field and only supports workspace-level log queries. Either remove the unused parameter or extend the endpoint to support deployment filtering.

web/apps/dashboard/lib/trpc/routers/deploy/network/utils.ts (1)

45-55: ⚠️ Potential issue | 🟠 Major

Remove as FlagCode to comply with type safety guidelines.

The as const marker on the prefixMap provides sufficient type information for TypeScript to infer FlagCode correctly without an explicit type assertion. The optional chaining (?.[1]) combined with the nullish coalesce operator (?? "us") already returns the correct FlagCode union type.

♻️ Minimal fix
export function mapRegionToFlag(region: string): FlagCode {
  const prefixMap = [
    ["ap-southeast", "au"],
    ["ap-northeast", "jp"],
    ["ap-south", "in"],
    ["us-", "us"],
    ["eu-", "de"],
    ["sa-", "br"],
  ] as const;

-  return (prefixMap.find(([prefix]) => region.startsWith(prefix))?.[1] as FlagCode) ?? "us";
+  const match = prefixMap.find(([prefix]) => region.startsWith(prefix));
+  return match ? match[1] : "us";
}

Per coding guidelines: "Never compromise type safety: No any, no ! (non-null assertion), no as Type."

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/(overview)/components/sections/deployment-info-section.tsx (2)

28-75: ⚠️ Potential issue | 🟠 Major

Guard DeploymentStatusBadge against undefined status.

DeploymentStatusBadge throws when status is falsy. If the live query hasn’t resolved yet, this will crash the page. Add a null guard or fallback UI.

🛠️ Proposed fix
-        statusBadge={<DeploymentStatusBadge status={deploymentStatus} />}
+        statusBadge={
+          deploymentStatus ? <DeploymentStatusBadge status={deploymentStatus} /> : null
+        }

16-18: ⚠️ Potential issue | 🟠 Major

Remove the as string assertion and guard against undefined deploymentId.

Line 17 uses a type assertion which violates the coding guideline: "Never compromise type safety: No any, no !, no as Type". This can mask missing params. Use a typed useParams call with an explicit guard instead.

Additionally, deploymentStatus may be undefined during data loading (line 29), which will cause DeploymentStatusBadge to throw an error (it requires a valid status value). Ensure either the status is always defined before rendering the badge, or the badge component handles the undefined case gracefully.

🛠️ Proposed fix
-  const params = useParams();
-  const deploymentId = params?.deploymentId as string;
+  const { deploymentId } = useParams<{ deploymentId: string }>();
+  if (!deploymentId) {
+    return null;
+  }

Also consider adding a guard before rendering DeploymentStatusBadge:

-        statusBadge={<DeploymentStatusBadge status={deploymentStatus} />}
+        statusBadge={deploymentStatus ? <DeploymentStatusBadge status={deploymentStatus} /> : null}
🤖 Fix all issues with AI agents
In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/(overview)/navigations/use-deployment-breadcrumb-config.ts:
- Around line 27-29: The variables workspaceSlug, projectId, and deploymentId
currently use unsafe type assertions (as string); replace these with
runtime-safe extraction from useParams() by checking each param is a string (and
not an array/undefined) or by using a small type-safe helper (e.g.,
getStringParam) inside the useDeploymentBreadcrumbConfig hook: validate typeof
param === 'string' and handle or throw a clear error/redirect when it's missing
or an array, ensuring no `as` or non-null assertions are used and preserving
explicit error handling for invalid params.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/components/controls/components/runtime-logs-search/index.tsx:
- Around line 11-36: The code uses ad-hoc TypeScript assertions (e.g., casting
data to typedData and casting the result of transformStructuredOutputToFilters
to typeof filters) which violates the "no `as`" rule; instead, update the
repository linting and boundary validation: add/enable the relevant
`@typescript-eslint` rules (e.g., `@typescript-eslint/no-unnecessary-type-assertion`
and/or no-explicit-any) in web/apps/dashboard/eslint.config.mjs to catch these
patterns globally, and implement a schema validation layer (use zod or
equivalent) at the LLM/output boundary that validates/parses the incoming data
into a safe typed object before usage so you can remove casts around variables
like data/typedData and the transformStructuredOutputToFilters result.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/components/table/hooks/use-runtime-logs-query.ts:
- Around line 18-20: Remove the "as const" assertion on the operator literal and
instead give the mapped array an explicit type; specifically, in the declaration
of severityFilters (which maps from filters), remove the "as const" and add a
type annotation such as Array<{ operator: 'is'; value: string }> (or the
equivalent generic/type alias used in this module) to the severityFilters
variable so the operator is typed as the literal "is" without using a type
assertion.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/components/table/runtime-log-details/runtime-log-header.tsx:
- Around line 28-29: The icon-only close Button in runtime-log-header.tsx lacks
an accessible label; update the Button (the element with props size="icon"
variant="ghost" onClick={onClose}) to include an accessible name—e.g., add
aria-label="Close" or aria-label={`Close ${something}`} (or include a
visually-hidden <span> describing the action) so screen readers can announce the
control; ensure the XMark icon remains decorative (aria-hidden) if you add an
aria-label on the Button.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/components/table/runtime-logs-table.tsx:
- Around line 68-86: getLogKey currently builds keys from time-region-message
which can collide; change it to include a stable sequence id (preferred) or
fallback to the row index to make keys unique. Update getLogKey to accept a
second parameter (e.g., seq or index) and include it in the returned string,
then update all uses: where logs are mapped/rendered (Component that renders
rows), where selectedLogKey is computed (RuntimeLogsTable using selectedLog ->
use the same seq/index or store the seq alongside selectedLog), and any handlers
that call setSelectedLog to set or compare the augmented key; ensure
getSelectedClassName still uses the log object but selection comparisons use the
augmented key consistent with useRuntimeLogsQuery-produced sequence IDs or the
array index.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/hooks/use-runtime-logs-filters.ts:
- Around line 37-45: The effect that sets a default since value uses
searchParams and setSearchParams but has an empty dependency array, which can
cause stale closures; update the dependency array to include searchParams and
setSearchParams (e.g., useEffect(() => { if (searchParams.since === null &&
searchParams.startTime === null && searchParams.endTime === null)
setSearchParams({ since: "6h" }); }, [searchParams, setSearchParams])) so the
check runs when params change, ensuring you only call setSearchParams when all
three fields are null to avoid loops and stale values; reference the existing
useEffect, searchParams, and setSearchParams identifiers when making the change.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/active-deployment-card-logs/components/deployment-logs-trigger.tsx:
- Around line 13-27: The two log-type toggle buttons (the ones calling
setLogType with "sentinel" and "runtime" and reading logType) lack an
aria-pressed state for assistive tech; update both button elements in
deployment-logs-trigger.tsx so each includes aria-pressed set to a boolean
expression (e.g., aria-pressed={logType === "sentinel"} for the sentinel button
and aria-pressed={logType === "runtime"} for the runtime button) so screen
readers can announce the active toggle.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/active-deployment-card-logs/components/runtime-logs-content.tsx:
- Around line 66-99: The code uses a type assertion on log.severity (as keyof
typeof SEVERITY_STYLES); replace that with a runtime type guard: derive a
normalized string (e.g. const severityRaw = String(log.severity).toUpperCase()),
then check if severityRaw is a key in SEVERITY_STYLES (if (severityRaw in
SEVERITY_STYLES) ...) and set a typed variable (e.g. severityKey = severityRaw)
or fallback to "INFO"; use that severityKey for SEVERITY_STYLES[severityKey] and
SEVERITY_ABBR[severityKey] instead of the asserted severity to preserve type
safety.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/active-deployment-card-logs/hooks/use-deployment-logs.tsx:
- Line 58: The ref declaration uses an unsafe type assertion; replace the cast
with a type-safe ref by initializing useRef<HTMLDivElement | null>(null) (remove
"as React.MutableRefObject<HTMLDivElement>"), change the exported/returned type
to React.RefObject<HTMLDivElement | null>, and update usages in handleScroll and
resetScroll to use optional chaining (e.g. scrollRef.current?....) so you don't
rely on non-null assertions; this keeps type safety in use-deployment-logs.tsx
for scrollRef, handleScroll, and resetScroll.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/active-deployment-card-logs/hooks/use-runtime-logs.tsx:
- Around line 25-35: The runtime logs query is passing redundant startTime and
endTime alongside the relative `since` parameter; update the trpc call in
use-runtime-logs.tsx (trpc.deploy.runtimeLogs.query.useQuery) to omit
`startTime` and `endTime` when using `RUNTIME_LOGS_SINCE`—keep `projectId`,
`deploymentId`, `limit: RUNTIME_LOGS_LIMIT`, `since: RUNTIME_LOGS_SINCE`, and
any other filters (`severity`, `message`) intact, and remove the `startTime:
timestamp` and `endTime: timestamp` entries to avoid sending conflicting
parameters.

In
`@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/components/region-flags.tsx:
- Around line 8-12: The container in the RegionFlags component uses the CSS
class cursor-pointer but has no click handler or accessible role; either remove
the cursor-pointer class from the div in RegionFlags to avoid suggesting
interactivity, or make the element interactive by adding an onClick handler and
appropriate accessibility attributes (e.g., role="button" and keyboard handlers)
and update RegionFlags' props to accept the callback; locate RegionFlags and the
surrounding div that renders RegionFlag instances to apply the change.

In `@web/apps/dashboard/app/`(app)/[workspaceSlug]/projects/[projectId]/page.tsx:
- Around line 55-60: The DeploymentLogsTrigger is being rendered unconditionally
which allows interaction when no live deployment exists; update the rendering so
DeploymentLogsTrigger is only rendered when project?.liveDeploymentId is truthy
(the same condition used for DeploymentLogsContent) — locate the trailingContent
and expandableContent usage around DeploymentLogsTrigger and
DeploymentLogsContent and wrap or replace the DeploymentLogsTrigger with a
conditional check (project?.liveDeploymentId ? <DeploymentLogsTrigger /> : null)
so the trigger is hidden/disabled when no liveDeploymentId is present.

In
`@web/apps/dashboard/components/navigation/sidebar/app-sidebar/hooks/use-projects-navigation.tsx`:
- Line 41: The line in use-projects-navigation.tsx uses an unnecessary type
assertion on the result of segments.at(deploymentIdIndex); update the
declaration for deploymentId (used in this hook) to remove the `as string |
undefined` assertion and rely on TypeScript's inferred `string | undefined` from
segments.at(...), ensuring no type-assertion or non-null operators are
introduced.

In `@web/apps/dashboard/components/virtual-table/index.tsx`:
- Around line 88-93: The current tableClassName computation unconditionally
applies a "!table-auto" override via the expression config.tableLayout ===
"fixed" ? "!table-fixed" : "!table-auto", which cancels the responsive
"xl:table-fixed"; update the logic so the override is only added when
config.tableLayout is explicitly provided (e.g., use a conditional that checks
config.tableLayout truthiness and then chooses "!table-fixed" or "!table-auto"),
leaving the class list empty when tableLayout is unset to preserve the default
"xl:table-fixed" behavior; modify the code that builds tableClassName (the cn
call) to reference config.tableLayout accordingly.

In `@web/apps/dashboard/lib/trpc/routers/deploy/runtime-logs/llm-search/utils.ts`:
- Around line 58-78: Remove all "as Type" assertions in the catch block and stop
logging raw userSearchMsg; instead sanitize inputs before logging (e.g., redact
or log only non-sensitive fields). Replace direct casts for error and
error.response with type-safe guards: first check if error instanceof TRPCError
and rethrow; then narrow unknown error by checking typeof error === "object" &&
error !== null and safely parse error shapes (e.g., use zod.safeParse or manual
property checks) before accessing response?.status to detect 429 and throw a
TOO_MANY_REQUESTS TRPCError; finally throw a generic INTERNAL_SERVER_ERROR
TRPCError. Apply the same change to the catch blocks in the other llm-search
utils files listed.

In `@web/apps/dashboard/lib/trpc/routers/deploy/runtime-logs/query.ts`:
- Around line 74-80: The code assumes countResult.val[0] exists when building
the RuntimeLogsResponseSchema; update the construction of total in the object
returned by the function that sets logs and nextCursor (the block using
logsResult and countResult) to guard against an empty countResult.val by
defaulting total to 0 if countResult.val is empty or missing (e.g., check
countResult.val && countResult.val.length > 0 and use
countResult.val[0].total_count, otherwise 0). Ensure you update the symbol
references in that same block where logs, hasMore, total, and nextCursor are
assembled.

In `@web/internal/clickhouse/src/runtime-logs.ts`:
- Around line 6-17: The schema runtimeLogsRequestSchema allows negative integers
for timestamps which will fail when bound to ClickHouse UInt64; update the
startTime and endTime validators to use non-negative integers and make
cursorTime a nullable non-negative integer (e.g., replace z.int() with
z.int().nonnegative() for startTime and endTime, and
z.int().nullable().nonnegative() or the equivalent for cursorTime) so the schema
enforces the UInt64 boundary at validation time.
- Around line 41-45: The nullable severity binding fails because ClickHouse
doesn't support Nullable(Array(String)); update the SQL CASE that checks
severity to use an empty-array sentinel: replace the length({severity:
Array(String)}) check with empty({severity: Array(String)}) and use the OR
short-circuit (empty({severity:Array(String)}) OR severity IN
{severity:Array(String)}), and ensure the caller binds an empty array [] when no
filter is intended; alternatively, pre-normalize the severity parameter to []
before invoking the query or add a separate nullable flag parameter (as
suggested by the message pattern) if you must distinguish null from empty.
🧹 Nitpick comments (14)
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/components/active-deployment-card/components/skeleton.tsx (1)

58-65: Consider using skeleton placeholders or disabled buttons for consistency.

This is a skeleton/loading state component, but these buttons appear interactive with real text labels. The rest of the component uses animate-pulse divs as placeholders. Using plain HTML <button> elements without handlers in a skeleton creates a confusing UX where buttons look clickable but do nothing.

Consider either:

  1. Replacing with skeleton placeholders to match the pattern used elsewhere in this component, or
  2. Using the design system's Button component with disabled state for consistency with line 65.
Option 1: Skeleton placeholders
         <div className="flex items-center gap-1.5">
-          <button className="text-xs text-grayA-9" type="button">
-            Sentinel logs
-          </button>
-          <span className="text-grayA-6">|</span>
-          <button className="text-xs text-grayA-9" type="button">
-            Runtime logs
-          </button>
+          <div className="h-2.5 w-16 bg-grayA-3 rounded animate-pulse" />
+          <span className="text-grayA-6">|</span>
+          <div className="h-2.5 w-16 bg-grayA-3 rounded animate-pulse" />
           <Button size="icon" variant="ghost">
Option 2: Disabled design system buttons
         <div className="flex items-center gap-1.5">
-          <button className="text-xs text-grayA-9" type="button">
+          <Button variant="ghost" size="sm" disabled className="text-xs text-grayA-9">
             Sentinel logs
-          </button>
+          </Button>
           <span className="text-grayA-6">|</span>
-          <button className="text-xs text-grayA-9" type="button">
+          <Button variant="ghost" size="sm" disabled className="text-xs text-grayA-9">
             Runtime logs
-          </button>
+          </Button>
           <Button size="icon" variant="ghost">
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/custom-domains-section/add-custom-domain.tsx (1)

117-120: Empty catch block silently swallows exceptions.

While toast.promise handles displaying errors to the user, the empty catch block obscures intent. Consider adding a comment to clarify this is intentional, or explicitly ignoring the error:

♻️ Suggested clarification
     try {
       await mutation;
       onSuccess();
-    } catch {}
+    } catch {
+      // Error already displayed by toast.promise above
+    }
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/(overview)/navigations/use-deployment-breadcrumb-config.ts (1)

10-21: Consider importing BreadcrumbItem type instead of duplicating it.

This type definition is identical to the one in use-breadcrumb-config.ts (lines 15-26 in relevant snippets). Importing from a shared location would reduce duplication and ensure consistency.

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/layout.tsx (1)

32-34: Magic number and fragile path detection.

The >= 5 check relies on a specific URL structure that could break if routes change. Similar logic exists in use-projects-navigation.tsx (lines 38-40) using segment indices, creating potential for inconsistency between the two detection approaches.

Consider extracting this into a shared utility or constant to ensure both locations stay in sync:

Suggested approach
// In a shared utils file
export function isDeploymentDetailPath(pathname: string | null): boolean {
  if (!pathname) return false;
  const segments = pathname.split("/").filter(Boolean);
  // Expected: workspace/projects/projectId/deployments/deploymentId/...
  const deploymentsIndex = segments.indexOf("deployments");
  return deploymentsIndex !== -1 && segments.length > deploymentsIndex + 1;
}
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/details/active-deployment-card-logs/hooks/use-deployment-logs.tsx (2)

6-6: Remove commented-out code instead of leaving it in.

The commented-out build steps logic (lines 6, 61-70, 91-105) should be removed rather than left in the codebase. Version control preserves the history if this code needs to be restored later.

Proposed removal
- // const BUILD_STEPS_REFETCH_INTERVAL = 500;
  const GATEWAY_LOGS_REFETCH_INTERVAL = 2000;
- // const { data: buildData, isLoading: buildLoading } = trpc.deploy.deployment.buildSteps.useQuery(
- //   {
- //     // without this check TS yells at us
- //     deploymentId: deploymentId ?? "",
- //   },
- //   {
- //     enabled: showBuildSteps && isExpanded && Boolean(deploymentId),
- //     refetchInterval: BUILD_STEPS_REFETCH_INTERVAL,
- //   },
- // );
- // // Update stored logs when build data changes
- // useEffect(() => {
- //   if (showBuildSteps && buildData?.logs) {
- //     const logMap = new Map<string, LogEntry>();
- //     buildData.logs.forEach((log) => {
- //       logMap.set(log.id, {
- //         type: "build",
- //         id: log.id,
- //         timestamp: log.timestamp,
- //         message: log.message,
- //       });
- //     });
- //     setStoredLogs(logMap);
- //   }
- // }, [showBuildSteps, buildData]);

Also applies to: 61-70, 91-105


15-21: Consider removing the hardcoded type field if only one type exists.

Since LogEntry.type now only supports "sentinel", and the type is always set to "sentinel" on line 122, the field may be unnecessary. However, if you plan to reintroduce other log types later, keeping it for forward compatibility is reasonable.

web/apps/dashboard/lib/utils/deployment-formatters.ts (1)

22-22: Consider clarifying the millicores format string.

The format ${millicores}m vCPU combines millicores notation (m) with vCPU which may be redundant or confusing. Standard notation is typically just ${millicores}m (e.g., "500m") or expressed as a fraction/decimal of vCPU.

💡 Suggested alternatives
-  return `${millicores}m vCPU`;
+  return `${millicores}m`;

Or convert to decimal vCPU:

-  return `${millicores}m vCPU`;
+  return `${(millicores / 1024).toFixed(2)} vCPU`;
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/components/controls/components/runtime-logs-datetime/index.tsx (1)

20-28: Avoid type assertion; type the accumulator explicitly.

The as Record<string, string | number> cast on line 27 violates the coding guideline to avoid as Type. The accumulator can be typed directly in the reduce call.

♻️ Proposed fix
   const timeValues = filters
     .filter((f) => ["startTime", "endTime", "since"].includes(f.field))
     .reduce(
-      (acc, f) => {
+      (acc: Record<string, string | number>, f) => {
         acc[f.field] = f.value;
         return acc;
       },
-      {} as Record<string, string | number>,
+      {},
     );

As per coding guidelines: "Never compromise type safety: No any, no ! (non-null assertion), no as Type"

web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/components/controls/components/runtime-logs-filters/runtime-logs-severity-filter.tsx (1)

32-39: Minor: Inconsistent field ordering in options array.

The "info" option has display before severity (lines 34-35) while other options have severity before display. This doesn't affect functionality but slightly reduces readability.

💅 Suggested fix for consistency
   {
     id: 3,
-    display: "INFO",
     severity: "info",
+    display: "INFO",
     label: "Info",
     color: "bg-info-9",
     checked: false,
   },
web/apps/dashboard/lib/trpc/routers/deploy/runtime-logs/utils.ts (1)

13-17: Consider handling potential error from getTimestampFromRelative.

The getTimestampFromRelative function throws an Error with message about invalid format (per the relevant snippet showing regex validation). If params.since contains an invalid format, this will throw uncaught.

Depending on where transformFilters is called, you may want to either:

  1. Validate since format upstream before calling this function
  2. Wrap the call with error handling using the fault library per coding guidelines
Example with fault library wrapping
import { fault } from "fault"; // or appropriate import path

// In transformFilters:
if (hasRelativeTime) {
  try {
    startTime = getTimestampFromRelative(params.since);
    endTime = Date.now();
  } catch (e) {
    throw fault("InvalidRelativeTime", {
      message: `Invalid relative time format: ${params.since}`,
      cause: e,
    });
  }
}
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/context/runtime-logs-provider.tsx (1)

23-28: Consider using fault library for the invariant error.

Per coding guidelines, the fault library should be used for error handling. While this is a standard React context invariant pattern, using fault would maintain consistency across the codebase.

Optional: Use fault library
+import { fault } from "fault";
+
 export function useRuntimeLogs() {
   const context = useContext(RuntimeLogsContext);
   if (!context) {
-    throw new Error("useRuntimeLogs must be used within RuntimeLogsProvider");
+    throw fault("ContextMisuse", {
+      message: "useRuntimeLogs must be used within RuntimeLogsProvider",
+    });
   }
   return context;
 }
web/apps/dashboard/app/(app)/[workspaceSlug]/projects/[projectId]/(overview)/deployments/[deploymentId]/runtime-logs/hooks/use-runtime-logs-filters.ts (3)

56-60: Type assertion as string violates coding guidelines.

The as string assertion bypasses type safety. Consider using a type guard or narrowing the type properly.

♻️ Proposed fix
         metadata: {
-          colorClass: runtimeLogsFilterFieldConfig.severity.getColorClass?.(
-            severity.value as string,
-          ),
+          colorClass:
+            typeof severity.value === "string"
+              ? runtimeLogsFilterFieldConfig.severity.getColorClass?.(severity.value)
+              : undefined,
         },

As per coding guidelines: Never compromise type safety: No any, no ! (non-null assertion), no as Type.


73-83: Multiple type assertions violate coding guidelines.

Lines 74, 78, and 80 use as type assertions. These can be refactored to use proper type narrowing.

♻️ Proposed fix using type-safe iteration
-    ["startTime", "endTime", "since"].forEach((field) => {
-      const value = searchParams[field as keyof RuntimeLogsQuerySearchParams];
-      if (value !== null && value !== undefined) {
-        activeFilters.push({
-          id: crypto.randomUUID(),
-          field: field as RuntimeLogsFilterField,
-          operator: "is",
-          value: value as string | number,
-        });
-      }
-    });
+    const timeFields = ["startTime", "endTime", "since"] as const;
+    for (const field of timeFields) {
+      const value = searchParams[field];
+      if (value !== null && value !== undefined) {
+        activeFilters.push({
+          id: crypto.randomUUID(),
+          field,
+          operator: "is",
+          value,
+        });
+      }
+    }

As per coding guidelines: Never compromise type safety: No any, no ! (non-null assertion), no as Type.


116-122: Type assertions as number and as string violate coding guidelines.

Consider using type guards or discriminated unions to narrow types safely.

♻️ Proposed fix
           case "startTime":
           case "endTime":
-            newParams[filter.field] = filter.value as number;
+            if (typeof filter.value === "number") {
+              newParams[filter.field] = filter.value;
+            }
             break;
           case "since":
-            newParams.since = filter.value as string;
+            if (typeof filter.value === "string") {
+              newParams.since = filter.value;
+            }
             break;

As per coding guidelines: Never compromise type safety: No any, no ! (non-null assertion), no as Type.

@vercel vercel bot temporarily deployed to Preview – dashboard February 4, 2026 15:05 Inactive
@chronark chronark merged commit fea7588 into main Feb 4, 2026
16 of 17 checks passed
@chronark chronark deleted the deployment-runtime-logs branch February 4, 2026 20:34
Flo4604 pushed a commit that referenced this pull request Feb 5, 2026
* fix: overlapping in deployment list

* chore: delete state change

* feat: add actual network elements

* refactor: replace rest of the regions with sentinel

* chore: tidy up

* feat: add RPS CH queries

* feat: add proper formatting for metrics

* feat: add proper foramtting

* feat: add RPS chart

* feat: add latency chart

* refactor: fix AVG aggregation

* chore: update logs querry

* refactor: reorganize componetns

* chore: tidy

* chore: explanation line

* feat: add disabled for navbar-popover

* fix: type issues

* feat: update sidebar nesting

* feat: add tRPC for runtime-lgos

* feat: add rough version of runtime-logs

* chore: fmt

* refactor: styling of runtime logs

* refactor: use the same schema

* feat: add message filter

* refactor: use proper filters schema

* feat: add AI search

* feat: add proper resizing for smaller screens and switch to table-auto instead of  fixed

* chore: allow overriding table layout

* refactor: improve ui

* fix: ui inconsistenties

* chore: improve styling of custom domains

* feat: add runtime-logs to active deployment card

* refactor: custom domain styless

* fix: formatting

* chore: improvemetns

* refactor: use shared component for flags

* chore: tidyup

* fix: network status bar overlapping
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants