From d3752dca63c73efc05d4a7eabf589c440c68095b Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Thu, 22 Oct 2026 11:26:29 -0500 Subject: [PATCH 1/2] fix: show submission ID on run detail --- .../portal/src/components/ShortId.stories.tsx | 36 +++---------------- apps/portal/src/pages/RunDetail.tsx | 7 ++++ docs/architecture/app-design.md | 3 +- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/apps/portal/src/components/ShortId.stories.tsx b/apps/portal/src/components/ShortId.stories.tsx index 55d0563c0..a9a2eed27 100644 --- a/apps/portal/src/components/ShortId.stories.tsx +++ b/apps/portal/src/components/ShortId.stories.tsx @@ -2,45 +2,19 @@ // Licensed under the MIT License. import type { Meta, StoryObj } from "@storybook/react-vite"; -import { expect, screen, userEvent } from "storybook/test"; import { ShortId } from "./ShortId"; -const FULL_ID = "abcdef1234567890fedcba"; - const meta = { + title: "components/ShortId", component: ShortId, - tags: ["ai-generated", "needs-work"], args: { - id: FULL_ID, + id: "89a92ad2-4250-4ebc-9784-b53c731cb402", + label: "submission ID", }, } satisfies Meta; export default meta; type Story = StoryObj; -export const Default: Story = { - play: async ({ canvas }) => { - // The trigger shows only the first 8 characters by default. - await expect(canvas.getByText("abcdef12")).toBeVisible(); - }, -}; - -export const CustomLength: Story = { - args: { length: 4 }, - play: async ({ canvas }) => { - await expect(canvas.getByText("abcd")).toBeVisible(); - }, -}; - -export const HoverRevealsFullIdAndCopy: Story = { - play: async ({ canvas }) => { - await userEvent.hover(canvas.getByText("abcdef12")); - // The tooltip content is portaled to the document body (so it is never - // clipped by an overflow ancestor such as a table), so query it via - // `screen` rather than the container-scoped `canvas`. Radix renders the - // content twice (visible + a11y copy), so match all and take the first. - const [button] = await screen.findAllByRole("button", { name: /copy/i }); - await expect(button).toBeVisible(); - await expect(screen.getAllByText(FULL_ID).length).toBeGreaterThan(0); - }, -}; +/** Run detail exposes the full submission ID and a copy action on hover. */ +export const SubmissionId: Story = {}; diff --git a/apps/portal/src/pages/RunDetail.tsx b/apps/portal/src/pages/RunDetail.tsx index 77ac6b0fa..b0e28c955 100644 --- a/apps/portal/src/pages/RunDetail.tsx +++ b/apps/portal/src/pages/RunDetail.tsx @@ -31,6 +31,7 @@ import { ReportThumbnail } from "@/components/ReportThumbnail"; import { CriteriaBadge } from "@/components/CriteriaBadge"; import { TaskPromptBadge } from "@/components/TaskPromptBadge"; import { AgentBadge } from "@/components/AgentBadge"; +import { ShortId } from "@/components/ShortId"; import { SkillRevisionLinks } from "@/components/SkillRevisionLinks"; import { AlertTriangle, ArrowLeft, Copy, Check, Sparkles, CheckCircle2, XCircle, MinusCircle, FileText, Plus, Download, Loader2, Archive, Video, LayoutGrid, List, Puzzle, RotateCcw, ChevronDown, Clock, Pause, Play, ArrowUpDown, X, Boxes } from "lucide-react"; import { formatDate, formatId, formatDuration, cn } from "@/lib/utils"; @@ -674,6 +675,12 @@ export function RunDetail() { {/* Tier 2 — labeled configuration + metrics */}
+ {run.submissionId && ( + } + /> + )} } diff --git a/docs/architecture/app-design.md b/docs/architecture/app-design.md index f1a243262..bcafbdb30 100644 --- a/docs/architecture/app-design.md +++ b/docs/architecture/app-design.md @@ -678,7 +678,8 @@ Both follow the same rules: A sibling affordance, `components/ShortId.tsx`, applies the same hoverable-tooltip pattern to **identifiers**: the Runs list renders run and submission IDs truncated to 8 chars -(`formatId`), and on hover the tooltip reveals the full ID plus a copy-to-clipboard button. The +(`formatId`), and Run detail shows the parent submission ID in its metadata. On +hover the tooltip reveals the full ID plus a copy-to-clipboard button. The trigger stays an inline `` (not a link) so the row click still navigates to the run; the copy button calls `e.stopPropagation()` so copying never triggers row navigation. From 6cee0b2a5eb0e02f1a4f2139b78c9e993ceb20ba Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Thu, 22 Oct 2026 11:27:37 -0500 Subject: [PATCH 2/2] test: retain ShortId stories and cover submission label --- .../portal/src/components/ShortId.stories.tsx | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/apps/portal/src/components/ShortId.stories.tsx b/apps/portal/src/components/ShortId.stories.tsx index a9a2eed27..6e7e7e1f3 100644 --- a/apps/portal/src/components/ShortId.stories.tsx +++ b/apps/portal/src/components/ShortId.stories.tsx @@ -2,19 +2,55 @@ // Licensed under the MIT License. import type { Meta, StoryObj } from "@storybook/react-vite"; +import { expect, screen, userEvent } from "storybook/test"; import { ShortId } from "./ShortId"; +const FULL_ID = "abcdef1234567890fedcba"; + const meta = { - title: "components/ShortId", component: ShortId, + tags: ["ai-generated", "needs-work"], args: { - id: "89a92ad2-4250-4ebc-9784-b53c731cb402", - label: "submission ID", + id: FULL_ID, }, } satisfies Meta; export default meta; type Story = StoryObj; -/** Run detail exposes the full submission ID and a copy action on hover. */ -export const SubmissionId: Story = {}; +export const Default: Story = { + play: async ({ canvas }) => { + // The trigger shows only the first 8 characters by default. + await expect(canvas.getByText("abcdef12")).toBeVisible(); + }, +}; + +export const CustomLength: Story = { + args: { length: 4 }, + play: async ({ canvas }) => { + await expect(canvas.getByText("abcd")).toBeVisible(); + }, +}; + +export const HoverRevealsFullIdAndCopy: Story = { + play: async ({ canvas }) => { + await userEvent.hover(canvas.getByText("abcdef12")); + // The tooltip content is portaled to the document body (so it is never + // clipped by an overflow ancestor such as a table), so query it via + // `screen` rather than the container-scoped `canvas`. Radix renders the + // content twice (visible + a11y copy), so match all and take the first. + const [button] = await screen.findAllByRole("button", { name: /copy/i }); + await expect(button).toBeVisible(); + await expect(screen.getAllByText(FULL_ID).length).toBeGreaterThan(0); + }, +}; + +/** The run detail metadata labels its parent submission ID explicitly. */ +export const SubmissionId: Story = { + args: { label: "submission ID" }, + play: async ({ canvas }) => { + await userEvent.hover(canvas.getByText("abcdef12")); + const [button] = await screen.findAllByRole("button", { name: "Copy submission ID" }); + await expect(button).toBeVisible(); + }, +};