Skip to content

Commit 4c40cd9

Browse files
committed
feat(webapp): surface run cell in the run admin panel
1 parent d5f1696 commit 4c40cd9

3 files changed

Lines changed: 46 additions & 3 deletions

File tree

apps/webapp/app/presenters/v3/SpanPresenter.server.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export class SpanPresenter extends BasePresenter {
106106
spanId,
107107
runFriendlyId,
108108
linkedRunId,
109+
isAdmin,
109110
}: {
110111
userId: string;
111112
projectSlug: string;
@@ -117,6 +118,7 @@ export class SpanPresenter extends BasePresenter {
117118
spanId: string;
118119
runFriendlyId: string;
119120
linkedRunId?: string;
121+
isAdmin?: boolean;
120122
}) {
121123
const project = await this._replica.project.findFirst({
122124
where: {
@@ -197,6 +199,7 @@ export class SpanPresenter extends BasePresenter {
197199
eventRepository: repository,
198200
spanId,
199201
linkedRunId,
202+
isAdmin,
200203
createdAt: parentRun.createdAt,
201204
completedAt: parentRun.completedAt,
202205
environmentId: parentRun.runtimeEnvironmentId,
@@ -236,6 +239,7 @@ export class SpanPresenter extends BasePresenter {
236239
eventRepository,
237240
spanId,
238241
linkedRunId,
242+
isAdmin,
239243
createdAt,
240244
completedAt,
241245
}: {
@@ -245,6 +249,7 @@ export class SpanPresenter extends BasePresenter {
245249
eventRepository: IEventRepository;
246250
spanId: string;
247251
linkedRunId?: string;
252+
isAdmin?: boolean;
248253
createdAt: Date;
249254
completedAt: Date | null;
250255
}) {
@@ -382,6 +387,34 @@ export class SpanPresenter extends BasePresenter {
382387
}
383388
: undefined;
384389

390+
// Cell attribution is telemetry-only - a `trigger.cell` resource attribute
391+
// on the run's span, never a DB field. Admin-only + best-effort: the span
392+
// may not be in the event store yet (queued/buffered/ingestion lag) and the
393+
// store may be unreachable, so any failure degrades to undefined rather than
394+
// breaking the panel.
395+
let cell: string | undefined;
396+
if (isAdmin) {
397+
try {
398+
const rootSpan = await eventRepository.getSpan(
399+
eventStore,
400+
environmentId,
401+
run.spanId,
402+
traceId,
403+
createdAt,
404+
completedAt ?? undefined
405+
);
406+
const resource = rootSpan?.resourceProperties as Record<string, any> | undefined;
407+
const value = resource?.trigger?.cell ?? resource?.["trigger.cell"];
408+
cell = typeof value === "string" ? value : undefined;
409+
} catch (error) {
410+
logger.warn("Failed to resolve run cell from telemetry", {
411+
runId: run.id,
412+
spanId: run.spanId,
413+
error: error instanceof Error ? error.message : String(error),
414+
});
415+
}
416+
}
417+
385418
return {
386419
id: run.id,
387420
friendlyId: run.friendlyId,
@@ -453,6 +486,7 @@ export class SpanPresenter extends BasePresenter {
453486
isBuffered: false,
454487
machinePreset: machine?.name,
455488
taskEventStore: run.taskEventStore,
489+
cell,
456490
externalTraceId,
457491
};
458492
}

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ import { useHasAdminAccess } from "~/hooks/useUser";
8484
import { redirectWithErrorMessage } from "~/models/message.server";
8585
import { type Span, SpanPresenter, type SpanRun } from "~/presenters/v3/SpanPresenter.server";
8686
import { logger } from "~/services/logger.server";
87-
import { requireUserId } from "~/services/session.server";
87+
import { requireUser } from "~/services/session.server";
8888
import { cn } from "~/utils/cn";
8989
import { formatCurrencyAccurate } from "~/utils/numberFormatter";
9090
import {
@@ -108,7 +108,10 @@ import { RealtimeStreamViewer } from "../resources.orgs.$organizationSlug.projec
108108
import { CompleteWaitpointForm } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
109109

110110
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
111-
const userId = await requireUserId(request);
111+
const user = await requireUser(request);
112+
// Admin gate for the telemetry-backed "Cell" field — only admins trigger the
113+
// extra event-store read (below), and only admins see the panel.
114+
const isAdmin = user.admin || user.isImpersonating;
112115
const { projectParam, organizationSlug, envParam, runParam, spanParam } =
113116
v3SpanParamsSchema.parse(params);
114117

@@ -123,7 +126,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
123126
envSlug: envParam,
124127
spanId: spanParam,
125128
runFriendlyId: runParam,
126-
userId,
129+
userId: user.id,
130+
isAdmin,
127131
linkedRunId,
128132
});
129133

@@ -1044,6 +1048,10 @@ function RunBody({
10441048
<Property.Label>Task event store</Property.Label>
10451049
<Property.Value>{run.taskEventStore}</Property.Value>
10461050
</Property.Item>
1051+
<Property.Item>
1052+
<Property.Label>Cell</Property.Label>
1053+
<Property.Value>{run.cell ?? "-"}</Property.Value>
1054+
</Property.Item>
10471055
</div>
10481056
)}
10491057
</Property.Table>

apps/webapp/app/v3/mollifier/syntheticSpanRun.server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export async function buildSyntheticSpanRun(args: {
198198
isBuffered: true,
199199
machinePreset: narrowMachinePreset(run.machinePreset),
200200
taskEventStore: "taskEvent",
201+
cell: undefined,
201202
externalTraceId: undefined,
202203
};
203204
}

0 commit comments

Comments
 (0)