{"record":{"id":"22bc9839fc3a469b","repo":"paperclipai/paperclip","slug":"public-attempt-must-contain-the-read-only-public-c","errorCode":null,"errorMessage":"Public attempt must contain the read-only public chat projection","messagePattern":"Public attempt must contain the read-only public chat projection","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/public-eval-viewer.mjs","lineNumber":67,"sourceCode":"    );\n  }\n  if (\n    ![...files.keys()].some((name) => name.endsWith(\".js\")) ||\n    !index.includes('<script type=\"module\"')\n  )\n    throw new Error(\"Incomplete trusted viewer build\");\n  return { index, files };\n}\n\nexport function validatePublicChatPayload(payload) {\n  if (\n    payload?.publication?.schema !== PUBLIC_CHAT_SCHEMA ||\n    payload.view?.sessionId !== \"public-report\" ||\n    payload.view?.composer?.state !== \"disabled\" ||\n    payload.view?.connection?.state !== \"closed\" ||\n    payload.devtools !== null\n  )\n    throw new Error(\n      \"Public attempt must contain the read-only public chat projection\",\n    );\n  const allowed = new Set([\n    \"attemptId\",\n    \"caseId\",\n    \"disposition\",\n    \"passed\",\n    \"checks\",\n    \"view\",\n    \"devtools\",\n    \"navigation\",\n    \"run\",\n    \"publication\",\n  ]);\n  if (Object.keys(payload).some((key) => !allowed.has(key)))\n    throw new Error(\"Unknown public chat payload field\");\n  const fields = (value, names) => {\n    if (","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/public-eval-viewer.mjs#L49-L85","documentation":"validatePublicChatPayload() first asserts that the payload really is the sanitized, read-only public projection: the publication schema constant matches PUBLIC_CHAT_SCHEMA, the view sessionId is exactly \"public-report\", the composer is \"disabled\", the connection is \"closed\", and devtools is null. Any miss means an unsanitized or wrong payload is about to be embedded in the public viewer, so \"Public attempt must contain the read-only public chat projection\" is thrown.","triggerScenarios":"Calling validatePublicChatPayload with: a payload produced by an older generator using a different PUBLIC_CHAT_SCHEMA value; a raw internal replay payload (real sessionId, live composer/connection states, non-null devtools); a null/undefined payload; or a hand-built object missing the publication/view fields.","commonSituations":"Generator and viewer versions out of sync after the schema constant was bumped; accidentally passing the internal run payload instead of the public projection; tests constructing partial fixtures that skip the publication block.","solutions":["Regenerate the public payload with the current generator so publication.schema matches PUBLIC_CHAT_SCHEMA in public-eval-chat.mjs.","Pass the projected public payload (output of the public chat projection step), not the internal replay/run payload.","Ensure the generator sets view.sessionId=\"public-report\", composer.state=\"disabled\", connection.state=\"closed\", and devtools=null.","Sync packages/paperclip-runner so the generator and this validator use the same PUBLIC_CHAT_SCHEMA constant version."],"exampleFix":"// before\nvalidatePublicChatPayload(internalReplayPayload);\n// after\nconst publicPayload = buildPublicChatProjection(internalReplayPayload);\nvalidatePublicChatPayload(publicPayload);","handlingStrategy":"type-guard","validationCode":"const isPublicProjection = (p) =>\n  p?.publication?.schema === PUBLIC_CHAT_SCHEMA &&\n  p?.view?.sessionId === \"public-report\" &&\n  p?.view?.composer?.state === \"disabled\" &&\n  p?.view?.connection?.state === \"closed\" &&\n  p?.devtools === null;","typeGuard":"const looksPublic = (p) => p != null && typeof p === \"object\" && p.publication?.schema === PUBLIC_CHAT_SCHEMA;","tryCatchPattern":"try {\n  validatePublicChatPayload(payload);\n} catch (err) {\n  if (err.message.startsWith(\"Public attempt must contain\")) {\n    throw new Error(\"Internal payload passed where public projection required; run the projection step first\");\n  }\n  throw err;\n}","preventionTips":["Always run the internal payload through the public projection step before validation","Keep generator and validator on the same PUBLIC_CHAT_SCHEMA constant; bump both together","Add a unit test that internal payloads fail validation and projected ones pass","Never construct public payloads by hand; use the projector API"],"tags":["validation","schema","security"],"backgroundTag":"schema-validation-failed","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}