From c52ba0f4ec58512704322d77a67fae78e8af12d2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 6 Sep 2026 18:19:11 -0500 Subject: [PATCH] test(evals): execute generated chat reports before publication Verify real viewer rendering, read-only controls, tool expansion, navigation, reload and responsive visibility for both report projections. Co-Authored-By: Paperclip --- .../workflows/runner-protocol-live-evals.yml | 12 ++ .../docs/runner-protocol-live-evals.md | 10 ++ ...r-protocol-eval-workflow-security.test.mjs | 8 + .../scripts/verify-runner-evalbook-viewer.mjs | 151 ++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs diff --git a/.github/workflows/runner-protocol-live-evals.yml b/.github/workflows/runner-protocol-live-evals.yml index a33abf393d..f6ace60c6b 100644 --- a/.github/workflows/runner-protocol-live-evals.yml +++ b/.github/workflows/runner-protocol-live-evals.yml @@ -614,6 +614,18 @@ jobs: --coverage-matrix .paperclip-evals/evals/paperclip-runner/coverage-matrix.json cp runner-protocol-merged/campaign.json runner-protocol-merged/public-report/campaign.json + - name: Set up report browser verification + uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6 + with: + version: 9.15.4 + + - name: Verify the actual chat viewer before publication + run: | + pnpm install --frozen-lockfile --ignore-scripts + pnpm --filter @paperclipai/paperclip-runner exec playwright install --with-deps chromium + node packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs --report-root runner-protocol-merged/public-report --screenshots runner-protocol-merged/viewer-proof + node packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs --report-root runner-protocol-merged/report + - name: Enforce the static public allowlist id: public_report run: | diff --git a/packages/paperclip-runner/docs/runner-protocol-live-evals.md b/packages/paperclip-runner/docs/runner-protocol-live-evals.md index 9904217445..0a0643f643 100644 --- a/packages/paperclip-runner/docs/runner-protocol-live-evals.md +++ b/packages/paperclip-runner/docs/runner-protocol-live-evals.md @@ -23,6 +23,16 @@ The CSP prohibits network calls, forms and external resources. Supply `PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR` to the publisher. The workflow sends a viewer-only artifact to that job; raw attempts and provider secrets stay out. +Before uploading, the report job runs the actual built application in Chromium +against representative passing, failing and missing-recording pages in both +reports. It checks initial rendering, tool expansion, read-only controls, +navigation, reload, a narrow viewport and the public no-API-request boundary. +Run the same check locally with +`node packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs --report-root /path/to/report`. +Add `--screenshots /path/to/proof` for visual evidence. Public screenshots are +retained in the aggregate Actions artifact, not added to the public report's +closed file allowlist. + ### Refresh a completed report without calling models Download the aggregate Actions artifact, then: diff --git a/packages/paperclip-runner/scripts/runner-protocol-eval-workflow-security.test.mjs b/packages/paperclip-runner/scripts/runner-protocol-eval-workflow-security.test.mjs index a73e6639b5..3b1f73c85c 100644 --- a/packages/paperclip-runner/scripts/runner-protocol-eval-workflow-security.test.mjs +++ b/packages/paperclip-runner/scripts/runner-protocol-eval-workflow-security.test.mjs @@ -136,6 +136,14 @@ test("publishes only the separately sanitized Evalbook through trusted OIDC code /Upload access-controlled canonical Evalbook and raw attempts/u, ); assert.match(report, /Upload publisher-only sanitized Evalbook/u); + assert.match( + report, + /verify-runner-evalbook-viewer\.mjs --report-root runner-protocol-merged\/public-report/u, + ); + assert.match( + report, + /verify-runner-evalbook-viewer\.mjs --report-root runner-protocol-merged\/report/u, + ); assert.match( report, /--viewer-root runner-protocol-build\/extracted\/dist-issue-thread\s*\\\n\s*--public-viewer/u, diff --git a/packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs b/packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs new file mode 100644 index 0000000000..5e5b01e30e --- /dev/null +++ b/packages/paperclip-runner/scripts/verify-runner-evalbook-viewer.mjs @@ -0,0 +1,151 @@ +#!/usr/bin/env node +// Execute the built viewer against actual generated pages, without a runner API. +import assert from "node:assert/strict"; +import { createServer } from "node:http"; +import { readFile, readdir, mkdir } from "node:fs/promises"; +import { resolve, join, extname, sep } from "node:path"; +import { chromium } from "@playwright/test"; + +const arg = (name) => process.argv[process.argv.indexOf(name) + 1]; +if (!process.argv.includes("--report-root")) + throw new Error("--report-root is required"); +const root = resolve(arg("--report-root")); +const screenshots = process.argv.includes("--screenshots") + ? resolve(arg("--screenshots")) + : null; +const samples = new Map(); +for (const entry of await readdir(join(root, "attempts"), { + withFileTypes: true, +})) { + if (!entry.isDirectory()) continue; + const route = `attempts/${entry.name}/index.html`; + const html = await readFile(join(root, route), "utf8"); + const encoded = + html.match( + /