From 641dbd406c67e3fbe848c9edbfd65eade34ba496 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 20 Aug 2026 11:34:11 -0500 Subject: [PATCH] feat(runner): make evidence a devtools tab --- .../devtools/issue-thread/issue-thread.spec.ts | 11 +++++++++++ .../devtools/issue-thread/src/DevtoolsInspector.tsx | 10 +++++----- .../devtools/issue-thread/src/EvidencePanel.tsx | 7 +++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/paperclip-runner/devtools/issue-thread/issue-thread.spec.ts b/packages/paperclip-runner/devtools/issue-thread/issue-thread.spec.ts index 51373476c98..7cdb4f8660c 100644 --- a/packages/paperclip-runner/devtools/issue-thread/issue-thread.spec.ts +++ b/packages/paperclip-runner/devtools/issue-thread/issue-thread.spec.ts @@ -238,6 +238,7 @@ test.describe("Capability issue thread", () => { ).toBeVisible(); await expect(panel.getByText("Agent tool — always")).toBeVisible(); await expect(panel.getByText("Agent tool — granted")).toBeVisible(); + }); test("tool exposure groups are deduplicated, foldable, and open catalog details", async ({ page }) => { @@ -716,6 +717,16 @@ test.describe("Capability clean-room chat", () => { }); await openCleanRoom(page); await page.getByTestId("evidence-toggle").click(); + const panel = page.getByTestId("evidence-panel"); + const tabs = panel.getByRole("tab"); + await expect(tabs.first()).toHaveText(/Evidence/); + await expect(tabs.nth(1)).toHaveText(/Timeline/); + await expect(tabs.first()).toHaveAttribute("aria-selected", "true"); + await expect(panel.locator("#evidence-turn-selector")).toBeVisible(); + await panel.getByRole("tab", { name: /Timeline/ }).click(); + await expect(panel.locator("#evidence-turn-selector")).toHaveCount(0); + await panel.getByRole("tab", { name: /Evidence/ }).click(); + await expect(panel.locator("#evidence-turn-selector")).toBeVisible(); await page.getByRole("tab", { name: /Documents/ }).click(); await expect(page.getByRole("heading", { name: "Runner plan" })).toBeVisible(); await expect(page.getByText("Inspectable from DevTools.")).toBeVisible(); diff --git a/packages/paperclip-runner/devtools/issue-thread/src/DevtoolsInspector.tsx b/packages/paperclip-runner/devtools/issue-thread/src/DevtoolsInspector.tsx index 2ad3a771cfb..4d52d20fee1 100644 --- a/packages/paperclip-runner/devtools/issue-thread/src/DevtoolsInspector.tsx +++ b/packages/paperclip-runner/devtools/issue-thread/src/DevtoolsInspector.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from "react"; import type { CapabilityDevtoolsSnapshot } from "../../../src/devtools"; import { Icon } from "./Icons"; -type Tab = "timeline" | "state" | "diff" | "documents" | "protocol" | "runtime" | "authority"; +export type CapabilityDevtoolsTab = "evidence" | "timeline" | "state" | "diff" | "documents" | "protocol" | "runtime" | "authority"; type Json = null | boolean | number | string | Json[] | { [key: string]: Json }; function JsonTree({ value, name = "root" }: { value: Json; name?: string }) { @@ -55,7 +55,8 @@ function download(snapshot: CapabilityDevtoolsSnapshot) { URL.revokeObjectURL(url); } -const TABS: ReadonlyArray<{ id: Tab; glyph: string; label: string }> = [ +const TABS: ReadonlyArray<{ id: CapabilityDevtoolsTab; glyph: string; label: string }> = [ + { id: "evidence", glyph: "◎", label: "Evidence" }, { id: "timeline", glyph: "◫", label: "Timeline" }, { id: "state", glyph: "{}", label: "State" }, { id: "diff", glyph: "±", label: "Diff" }, @@ -85,8 +86,7 @@ function documentsOf(state: Json): Array<{ id: string; key: string; title: strin }); } -export function DevtoolsInspector({ snapshot, onFork }: { snapshot: CapabilityDevtoolsSnapshot; onFork: (revision: number) => void }) { - const [tab, setTab] = useState("timeline"); +export function DevtoolsInspector({ snapshot, onFork, tab, onTabChange }: { snapshot: CapabilityDevtoolsSnapshot; onFork: (revision: number) => void; tab: CapabilityDevtoolsTab; onTabChange: (tab: CapabilityDevtoolsTab) => void }) { const [query, setQuery] = useState(""); const latest = snapshot.revisions.at(-1)!; const [revision, setRevision] = useState(latest.revision); @@ -125,7 +125,7 @@ export function DevtoolsInspector({ snapshot, onFork }: { snapshot: CapabilityDe
{TABS.map(({ id, glyph, label }) => ( - + ))}
{tab === "timeline" ? ( diff --git a/packages/paperclip-runner/devtools/issue-thread/src/EvidencePanel.tsx b/packages/paperclip-runner/devtools/issue-thread/src/EvidencePanel.tsx index 74d0d0b416d..c8e5a05ff98 100644 --- a/packages/paperclip-runner/devtools/issue-thread/src/EvidencePanel.tsx +++ b/packages/paperclip-runner/devtools/issue-thread/src/EvidencePanel.tsx @@ -9,7 +9,7 @@ import type { CapabilityToolDisposition, } from "../../../src/issue-thread/types"; import type { CapabilityDevtoolsSnapshot } from "../../../src/devtools"; -import { DevtoolsInspector } from "./DevtoolsInspector"; +import { DevtoolsInspector, type CapabilityDevtoolsTab } from "./DevtoolsInspector"; import { Icon } from "./Icons"; import { capabilitySemanticToolDescriptor } from "../../../src/semantic-tools/catalog"; import { @@ -271,6 +271,7 @@ function Section({ } export function EvidencePanel(props: EvidencePanelProps) { + const [devtoolsTab, setDevtoolsTab] = useState("evidence"); const { snapshot, devtools, @@ -331,11 +332,12 @@ export function EvidencePanel(props: EvidencePanelProps) { {devtools === null ? (

Loading company state…

) : ( - + )} ) : null} + {devtoolsTab === "evidence" ?