From e7136920959323756644d2f10a358b154fc7771f Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 11 Sep 2026 16:47:47 -0500 Subject: [PATCH] fix(runner-e2e): retain branch-only suites and chat screenshots in reports --- doc/plans/2026-09-10-agent-chat.md | 17 +- tests/runner-e2e/README.md | 10 + tests/runner-e2e/dashboard.ts | 35 ++-- tests/runner-e2e/evidence.ts | 2 + tests/runner-e2e/history-publish.ts | 10 +- tests/runner-e2e/history.test.ts | 16 ++ tests/runner-e2e/history.ts | 36 +++- tests/runner-e2e/public-summary-image.ts | 17 +- tests/runner-e2e/report-catalog.test.ts | 242 +++++++++++++++++++++++ tests/runner-e2e/report-catalog.ts | 130 ++++++++++++ tests/runner-e2e/report.ts | 12 +- tests/runner-e2e/support.test.ts | 17 ++ 12 files changed, 511 insertions(+), 33 deletions(-) create mode 100644 tests/runner-e2e/report-catalog.test.ts create mode 100644 tests/runner-e2e/report-catalog.ts diff --git a/doc/plans/2026-09-10-agent-chat.md b/doc/plans/2026-09-10-agent-chat.md index bb8bf2b1f9..0caac953b0 100644 --- a/doc/plans/2026-09-10-agent-chat.md +++ b/doc/plans/2026-09-10-agent-chat.md @@ -275,9 +275,20 @@ requested three-hour repair window. No acceptance assertions were disabled. - [Exact campaign results](https://d1p6rlowie26tp.cloudfront.net/runner-e2e/campaigns/gha-34648511170-1/summary.md) - [GitHub run and retained evidence](https://github.com/paperclipai/paperclip/actions/runs/34648511170) -Use the Markdown results for this branch-only suite: the trusted default-branch -HTML dashboard still uses its older catalog, while the normalized results and -Markdown report include all 24 chat cells. +The [HTML dashboard](https://d1p6rlowie26tp.cloudfront.net/runner-e2e/campaigns/gha-34648511170-1/index.html?report=agent-chat#suite-agent-chat) +was repaired from retained evidence after its older trusted catalog omitted the +branch-only suite. It now includes the chat suite and 32 screenshots, including +eight draft/revised plan captures recovered from their original Playwright +attachments. No paid cells were rerun; result records, tested SHA, timestamps, +usage, billing, attempts, and cleanup outcomes remain unchanged. + +Reporting now discovers validated display-only entries for unknown selected +execution IDs, and publication rejects missing declared screenshots. The exact +chat plan filenames are included in packaged evidence. All 165 runner unit tests +and runner TypeScript checks passed. Browser verification covered suite +filtering, restored plan images, and gallery navigation. This explicitly +authorized repair replaces only this campaign's report objects; normal +immutable-publication protections remain unchanged. The published summary and normalized results were verified after publication: exactly 24 unique expected cells, all passed on attempt 1, all cleanup checks diff --git a/tests/runner-e2e/README.md b/tests/runner-e2e/README.md index 7e617cc6ee..c3a63a27eb 100644 --- a/tests/runner-e2e/README.md +++ b/tests/runner-e2e/README.md @@ -256,6 +256,12 @@ usage is labeled `unavailable` or `unpriced`; it is never presented as zero cost. The CI report job stages the same portable site at `normalized/index.html` inside the access-controlled merged report artifact. +The trusted publisher discovers display-only entries for selected execution IDs +absent from its local catalog, so branch-only suites remain visible in the +dashboard, filters, gallery, and summary image. It validates execution identity +and escapes display text without loading target-branch executable code. Unknown +suite cardinality is not treated as proof of full-suite coverage. + Permanent publication uses two explicit bundles. Both retain only normalized result PNG files with the explicit `public-runner-fixture` publication marker, including marked `failure.png` captures, so every campaign dashboard has its @@ -274,6 +280,10 @@ retains allowlisted inert per-attempt evidence (`.json`, `.log`, `.md`, and redaction. The GitHub Pages bundle is regenerated separately with the same declared-screenshot boundary. +Publication fails if any declared public screenshot is missing from the bundle. +The evidence packager explicitly retains `chat-plan-draft.png` and +`chat-plan-revised.png`; arbitrary chat-prefixed files remain excluded. + Both public bundles exclude video, archives, raw/unallowlisted logs, SVG or other active content, generated Playwright/blob/HTML report trees, and undeclared PNG files, and per-attempt XML. The root `junit.xml` remains public diff --git a/tests/runner-e2e/dashboard.ts b/tests/runner-e2e/dashboard.ts index 06a72bd9cb..a775909a06 100644 --- a/tests/runner-e2e/dashboard.ts +++ b/tests/runner-e2e/dashboard.ts @@ -1,5 +1,8 @@ +import { + discoverReportCatalog, + type ReportExecution, +} from "./report-catalog.js"; import type { - MatrixExecution, RunnerE2ECampaign, RunnerE2EHistoryIndex, RunnerE2EResult, @@ -22,7 +25,7 @@ export interface RunnerDashboardInput { title: string; generatedAt: string; expected: readonly string[]; - catalog: readonly MatrixExecution[]; + catalog: readonly ReportExecution[]; entries: readonly RunnerDashboardEntry[]; campaign?: RunnerE2ECampaign; history?: RunnerE2EHistoryIndex; @@ -135,7 +138,7 @@ function resolveScreenshots( } function renderCase( - execution: MatrixExecution, + execution: ReportExecution, expected: ReadonlySet, entryById: ReadonlyMap, ) { @@ -151,7 +154,11 @@ function renderCase( const label = state.replace("-", " "); const detail = entry?.errors.join("; ") || - (entry?.valid ? "All invariants passed" : "Not selected"); + (entry?.valid + ? "All invariants passed" + : selected + ? "No result artifact was uploaded" + : "Not selected"); const screenshots = resolveScreenshots(entry); const billing = entry ? summarizeExecutionBilling(entry.result) : null; const matcherResults = entry?.result.matcherResults ?? []; @@ -508,7 +515,7 @@ function renderHistory(history: RunnerE2EHistoryIndex | undefined) { } function renderSuiteMatrix(input: { - suiteCatalog: readonly MatrixExecution[]; + suiteCatalog: readonly ReportExecution[]; expected: ReadonlySet; entryById: ReadonlyMap; summary?: RunnerE2ESuiteSummary; @@ -580,6 +587,11 @@ function renderSuiteMatrix(input: { } export function renderRunnerE2EDashboard(input: RunnerDashboardInput) { + const catalog = discoverReportCatalog({ + catalog: input.catalog, + expected: input.expected, + results: input.entries.map((entry) => entry.result), + }); const expected = new Set(input.expected); const entryById = new Map( input.entries.map((entry) => [entry.result.executionId, entry]), @@ -602,13 +614,13 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) { ); const suites = [ ...new Map( - input.catalog.map((execution) => [execution.suite.id, execution.suite]), + catalog.map((execution) => [execution.suite.id, execution.suite]), ).values(), ]; const suiteSections = suites .map((suite) => renderSuiteMatrix({ - suiteCatalog: input.catalog.filter( + suiteCatalog: catalog.filter( (execution) => execution.suite.id === suite.id, ), expected, @@ -625,15 +637,12 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) { ); const filterProfiles = [ ...new Map( - input.catalog.map((execution) => [ - execution.profile.id, - execution.profile, - ]), + catalog.map((execution) => [execution.profile.id, execution.profile]), ).values(), ]; const filterEnvironments = [ ...new Map( - input.catalog.map((execution) => [ + catalog.map((execution) => [ execution.environment.id, execution.environment, ]), @@ -1034,7 +1043,7 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) { ${suiteSections} ${historySection} -
Generated ${html(input.generatedAt)}${input.catalog.length} catalog executions · Declared screenshots and sanitized structured evidence published
+
Generated ${html(input.generatedAt)}${catalog.length} catalog executions · Declared screenshots and sanitized structured evidence published