{"record":{"id":"f1f0f4f6fc5f6932","repo":"paperclipai/paperclip","slug":"published-history-requires-the-same-run-runner-lab","errorCode":null,"errorMessage":"Published history requires the same-run Runner Lab site theme","messagePattern":"Published history requires the same-run Runner Lab site theme","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs","lineNumber":461,"sourceCode":"    resolve(reportRoot, \"campaign.json\"),\n    cacheControl,\n  );\n}\n\nexport async function publishProtocolEvalHistory({\n  reportRoot,\n  destination,\n  viewerRoot,\n}) {\n  const validatedDestination =\n    validateProtocolEvalHistoryDestination(destination);\n  const { campaign } = await validatePublicProtocolEvalReport(reportRoot, {\n    viewerRoot,\n  });\n  const viewer = await trustedViewerFiles(viewerRoot);\n  const stylesheet = viewer.index.match(/<link rel=\"stylesheet\" crossorigin href=\"\\.\\/(assets\\/[A-Za-z0-9._-]+\\.css)\">/)?.[1];\n  if (!stylesheet || !viewer.files.get(`viewer/${stylesheet}`)?.includes(\".evalbook-site\"))\n    throw new Error(\"Published history requires the same-run Runner Lab site theme\");\n  const stylesheetHref = `campaigns/${campaign.campaignId}/viewer/${stylesheet}`;\n  const manifest = await createProtocolEvalBundleManifest(\n    reportRoot,\n    campaign.campaignId,\n    { viewerRoot },\n  );\n  const temporary = await mkdtemp(\n    join(tmpdir(), \"runner-protocol-eval-history-\"),\n  );\n  const historyKey = `${validatedDestination.prefix}/history.json`;\n  const mergedHistory = mergeProtocolEvalHistory(\n    (await downloadJson(\n      validatedDestination.bucket,\n      historyKey,\n      join(temporary, \"history.json\"),\n    )) ?? emptyProtocolEvalHistory(),\n    protocolEvalHistoryRecord(\n      campaign,","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs#L443-L479","documentation":"publishProtocolEvalHistory renders the public history index using the same stylesheet as the Runner Lab viewer build for the current run. It extracts the hashed CSS href from the trusted viewer's index.html and requires the CSS to contain the .evalbook-site theme class. A missing link tag, missing stylesheet file, or unthemed build means the published history page would not match the campaign pages, so publication is refused.","triggerScenarios":"viewerRoot pointing at a stale or wrong build whose index.html lacks the expected <link rel=\"stylesheet\" crossorigin href=\"./assets/*.css\"> tag; a viewer build without the .evalbook-site theme class in its CSS; viewerRoot undefined so trustedViewerFiles cannot resolve the run's viewer.","commonSituations":"Setting PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR to an older viewer output; viewer rebuilt between report generation and publish so the hashed asset name changed; a non-Runner Lab viewer variant used by mistake.","solutions":["Point PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR at the viewer build produced by the same run as the report","Rebuild the Runner Lab viewer so index.html contains the hashed stylesheet link and the CSS includes .evalbook-site","Verify with: grep -o 'assets/[A-Za-z0-9._-]*\\.css' <viewerRoot>/index.html and grep .evalbook-site <viewerRoot>/assets/*.css","Regenerate both the report and viewer together so their revisions match"],"exampleFix":"// before\nPAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR=./old-viewer-dist pnpm publish-eval-history\n// after\npnpm --filter @paperclipai/runner build:viewer  # emits viewer/ with .evalbook-site CSS\nPAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR=./viewer pnpm publish-eval-history","handlingStrategy":"validation","validationCode":"const viewer = await trustedViewerFiles(viewerRoot);\nconst css = viewer.index.match(/<link rel=\"stylesheet\" crossorigin href=\"\\.\\/(assets\\/[A-Za-z0-9._-]+\\.css)\">/)?.[1];\nif (!css || !viewer.files.get(`viewer/${css}`)?.includes(\".evalbook-site\"))\n  throw new Error(\"viewer build lacks the Runner Lab theme; rebuild it\");","typeGuard":"const hasLabTheme = (viewer) => {\n  const css = viewer.index.match(/\\.\\/(assets\\/[A-Za-z0-9._-]+\\.css)/)?.[1];\n  return !!css && viewer.files.get(`viewer/${css}`)?.includes(\".evalbook-site\") === true;\n};","tryCatchPattern":"try {\n  await publishProtocolEvalHistory({ reportRoot, destination, viewerRoot });\n} catch (e) {\n  if (String(e.message).includes(\"same-run Runner Lab site theme\")) {\n    console.error(\"rebuild the viewer for this run and point viewerRoot at its output\");\n  }\n  throw e;\n}","preventionTips":["Build the viewer and the report in the same workflow run and pass that exact viewerRoot","Set PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR explicitly per run; never reuse a stale dist","CI-check that the viewer CSS contains .evalbook-site before publishing"],"tags":["publishing","theme","build-artifacts"],"backgroundTag":"invalid-config-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}