Skip to content

Commit 0a00abd

Browse files
committed
fix(webapp): name only demo resources in the gallery's fixtures
1 parent 5ed445e commit 0a00abd

3 files changed

Lines changed: 76 additions & 21 deletions

File tree

apps/webapp/app/components/dashboard-agent/demo/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Must stay free of server imports. `demo.test.ts` asserts that.
2-
export { DEMO_ID_PREFIX, DEMO_MARKER, DEMO_WORLD, demoId, demoReportUri } from "./ids";
2+
export { DEMO_ID_PREFIX, DEMO_MARKER, DEMO_WORLD, demoId, demoReportUri, demoRunsUri } from "./ids";
33

44
export * as demoFixtures from "./fixtures";
55

apps/webapp/app/routes/storybook.agent-ui/fixtures.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from "@internal/dashboard-agent-contracts";
66
import { ErrorId } from "@trigger.dev/core/v3/isomorphic";
77
import { describe, expect, it } from "vitest";
8+
import { DEMO_MARKER } from "~/components/dashboard-agent/demo";
89
import { planDiagnosisActions } from "~/components/dashboard-agent/diagnosis-actions";
910
import { renderableActions } from "~/components/dashboard-agent/view-actions";
1011
import { reportTrust } from "~/presenters/v3/reports/report-layout";
@@ -77,6 +78,55 @@ describe("gallery view blocks", () => {
7778
});
7879
});
7980

81+
/**
82+
* `demo.test.ts` marks the demo layer's ids. These fixtures are hand-written next to the
83+
* route, so the same rule is asserted here: a `view_run` or `navigate` the presenter can
84+
* resolve must land on demo data, never on somebody's environment.
85+
*/
86+
describe("gallery identifiers", () => {
87+
// The digit keeps discriminants like `error_recurrence` out; ids always carry one.
88+
const IDENTIFIER = /^(run|error|watch|queue|proj|env|deployment)_[a-z0-9]*\d|^trigger:\/\//i;
89+
90+
function strings(value: unknown, path = "fixture"): Array<[string, string]> {
91+
if (typeof value === "string") return [[value, path]];
92+
if (Array.isArray(value)) return value.flatMap((item, i) => strings(item, `${path}[${i}]`));
93+
if (value && typeof value === "object") {
94+
return Object.entries(value).flatMap(([key, item]) => strings(item, `${path}.${key}`));
95+
}
96+
return [];
97+
}
98+
99+
const fixtures = {
100+
fullDiagnosis,
101+
externalServiceDiagnosis,
102+
lowConfidenceDiagnosis,
103+
revisedDiagnosisBlocks,
104+
offerActionsBlock,
105+
watchConfirmationBlock,
106+
watchDegradedConfirmationBlock,
107+
watchSatisfiedBlock,
108+
untrustworthyReport,
109+
};
110+
111+
it("names no resource that isn't demo data", () => {
112+
for (const [value, path] of strings(fixtures)) {
113+
if (!IDENTIFIER.test(value)) continue;
114+
expect(value, path).toContain(DEMO_MARKER);
115+
}
116+
});
117+
118+
it("watches only demo subjects, whatever shape their id takes", () => {
119+
for (const action of offerActionsBlock.actions) {
120+
if (action.intent.kind !== "watch") continue;
121+
const subject = Object.entries(action.intent.spec).filter(([key]) =>
122+
["queue", "runId", "fingerprint"].includes(key)
123+
);
124+
expect(subject.length).toBeGreaterThan(0);
125+
for (const [key, value] of subject) expect(String(value), key).toContain(DEMO_MARKER);
126+
}
127+
});
128+
});
129+
80130
describe("gallery watch confirmations", () => {
81131
it("states the external outcome each confirmation claims", () => {
82132
expect(watchConfirmationBlock.followUp).toContain(

apps/webapp/app/routes/storybook.agent-ui/fixtures.ts

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
type ViewBlock,
99
type WatchResultBlock as WatchResultBlockPayload,
1010
} from "@internal/dashboard-agent-contracts";
11-
import { DEMO_WORLD, demoFixtures } from "~/components/dashboard-agent/demo";
11+
import { DEMO_WORLD, demoFixtures, demoId, demoRunsUri } from "~/components/dashboard-agent/demo";
1212
import type { TurnActivity } from "~/components/dashboard-agent/DashboardAgentMessages";
1313
import { watchConfirmationBlockBody, watchOneShotBlockBody } from "~/presenters/v3/dashboardAgent";
1414
import {
@@ -47,6 +47,11 @@ const {
4747
userMessage,
4848
} = demoFixtures;
4949

50+
const DEMO_WATCH_ID = demoId("watch_gallery");
51+
52+
// A stored error_recurrence spec holds the internal id, so the demo one drops its prefix.
53+
const DEMO_FINGERPRINT = DEMO_WORLD.errorFingerprint.replace(/^error_/, "");
54+
5055
/** One transcript the message gallery renders, with the turn state it belongs to. */
5156
export type DemoTranscript = {
5257
messages: UIMessage[];
@@ -182,7 +187,7 @@ export const demoTranscripts = {
182187

183188
export const fullDiagnosis: DiagnosisBlock = {
184189
type: "diagnosis",
185-
runId: "run_a1b2c3d4e5",
190+
runId: DEMO_WORLD.failedRunId,
186191
summary:
187192
"The run failed because processOrder threw on an order with no line items. The payload had an empty items array.",
188193
category: "user_code_error",
@@ -193,7 +198,7 @@ export const fullDiagnosis: DiagnosisBlock = {
193198
{
194199
type: "error",
195200
detail: "TypeError: Cannot read properties of undefined (reading 'sku')",
196-
reference: "run_a1b2c3d4e5",
201+
reference: DEMO_WORLD.failedRunId,
197202
},
198203
{ type: "failed_span", detail: "processOrder attempt 1 failed after 42ms" },
199204
{
@@ -204,7 +209,7 @@ export const fullDiagnosis: DiagnosisBlock = {
204209
{
205210
type: "historical_match",
206211
detail: "14 runs of this task hit the same error in the last 24h.",
207-
reference: "error_emptyorder",
212+
reference: DEMO_WORLD.errorFingerprint,
208213
},
209214
],
210215
impact:
@@ -214,14 +219,14 @@ export const fullDiagnosis: DiagnosisBlock = {
214219
"Validate the payload before triggering so empty orders never reach the task.",
215220
],
216221
actions: [
217-
{ label: "View run", kind: "view_run", target: "run_a1b2c3d4e5" },
222+
{ label: "View run", kind: "view_run", target: DEMO_WORLD.failedRunId },
218223
{ label: "Retries docs", kind: "docs", target: "https://trigger.dev/docs/errors-retrying" },
219224
],
220225
};
221226

222227
export const externalServiceDiagnosis: DiagnosisBlock = {
223228
type: "diagnosis",
224-
runId: "run_f6g7h8i9j0",
229+
runId: DEMO_WORLD.slowRunId,
225230
summary: "chargePayment timed out waiting on the Stripe API after 30 seconds.",
226231
category: "external_service",
227232
likelyCause:
@@ -231,7 +236,7 @@ export const externalServiceDiagnosis: DiagnosisBlock = {
231236
{
232237
type: "error",
233238
detail: "TimeoutError: Stripe API timed out after 30s",
234-
reference: "run_f6g7h8i9j0",
239+
reference: DEMO_WORLD.slowRunId,
235240
},
236241
{ type: "deploy", detail: "First seen on version 20260620.2", reference: "20260620.2" },
237242
],
@@ -240,12 +245,12 @@ export const externalServiceDiagnosis: DiagnosisBlock = {
240245
"Wrap the Stripe call in a retry with backoff.",
241246
"Set an explicit request timeout shorter than the task's max duration.",
242247
],
243-
actions: [{ label: "View run", kind: "view_run", target: "run_f6g7h8i9j0" }],
248+
actions: [{ label: "View run", kind: "view_run", target: DEMO_WORLD.slowRunId }],
244249
};
245250

246251
export const lowConfidenceDiagnosis: DiagnosisBlock = {
247252
type: "diagnosis",
248-
runId: "run_k1l2m3n4o5",
253+
runId: DEMO_WORLD.priorRunId,
249254
summary:
250255
"The run crashed without a captured error, so the cause isn't conclusive from the available signals.",
251256
category: "unknown",
@@ -265,21 +270,21 @@ export const lowConfidenceDiagnosis: DiagnosisBlock = {
265270
export const revisedDiagnosisBlocks: ViewBlock[] = [
266271
{
267272
...lowConfidenceDiagnosis,
268-
id: "diagnosis-run_a1b2c3d4e5",
273+
id: `diagnosis-${DEMO_WORLD.failedRunId}`,
269274
revision: 1,
270275
version: VIEW_BLOCK_VERSION,
271276
summary: "Revision 1 — first guess, before the logs came back. Should not render.",
272277
},
273278
{
274279
...externalServiceDiagnosis,
275-
id: "diagnosis-run_a1b2c3d4e5",
280+
id: `diagnosis-${DEMO_WORLD.failedRunId}`,
276281
revision: 2,
277282
version: VIEW_BLOCK_VERSION,
278283
summary: "Revision 2 — narrowed to the payload, still unconfirmed. Should not render.",
279284
},
280285
{
281286
...fullDiagnosis,
282-
id: "diagnosis-run_a1b2c3d4e5",
287+
id: `diagnosis-${DEMO_WORLD.failedRunId}`,
283288
revision: 3,
284289
version: VIEW_BLOCK_VERSION,
285290
summary:
@@ -299,7 +304,7 @@ export const offerActionsBlock: ViewBlock = {
299304
kind: "watch",
300305
spec: {
301306
kind: "error_recurrence",
302-
fingerprint: "a1b2c3",
307+
fingerprint: DEMO_FINGERPRINT,
303308
checkEveryMinutes: 15,
304309
maxHours: 6,
305310
note: "the TypeError in send-order-receipt",
@@ -308,7 +313,7 @@ export const offerActionsBlock: ViewBlock = {
308313
},
309314
{
310315
label: "See its failed runs",
311-
intent: { kind: "navigate", target: "trigger://proj_abc/env_abc/runs" },
316+
intent: { kind: "navigate", target: demoRunsUri() },
312317
},
313318
],
314319
};
@@ -318,16 +323,16 @@ export const offerActionsBlock: ViewBlock = {
318323
* ------------------------------------------------------------------ */
319324

320325
const WATCH_BLOCK_ENVELOPE = {
321-
id: "watch:watch_demo",
326+
id: `watch:${DEMO_WATCH_ID}`,
322327
revision: 0,
323328
version: VIEW_BLOCK_VERSION,
324329
} as const;
325330

326331
/** Both opt-ins took effect: the happy path a submit with `notifyExternally` produces. */
327332
export const watchConfirmationBlock: WatchResultBlockPayload = {
328333
...watchConfirmationBlockBody({
329-
spec: queueWatchRecommendation("email-sends"),
330-
watchId: "watch_demo",
334+
spec: queueWatchRecommendation(DEMO_WORLD.queue),
335+
watchId: DEMO_WATCH_ID,
331336
followUp: { investigateOnAttention: true, external: { status: "enabled" } },
332337
}),
333338
...WATCH_BLOCK_ENVELOPE,
@@ -339,8 +344,8 @@ export const watchConfirmationBlock: WatchResultBlockPayload = {
339344
*/
340345
export const watchDegradedConfirmationBlock: WatchResultBlockPayload = {
341346
...watchConfirmationBlockBody({
342-
spec: queueWatchRecommendation("email-sends"),
343-
watchId: "watch_demo",
347+
spec: queueWatchRecommendation(DEMO_WORLD.queue),
348+
watchId: DEMO_WATCH_ID,
344349
unavailable: true,
345350
followUp: {
346351
investigateOnAttention: true,
@@ -352,7 +357,7 @@ export const watchDegradedConfirmationBlock: WatchResultBlockPayload = {
352357

353358
export const watchSatisfiedBlock: WatchResultBlockPayload = {
354359
...watchOneShotBlockBody({
355-
spec: runWatchRecommendation("run_a1b2c3d4e5"),
360+
spec: runWatchRecommendation(DEMO_WORLD.failedRunId),
356361
result: "satisfied",
357362
}),
358363
...WATCH_BLOCK_ENVELOPE,

0 commit comments

Comments
 (0)