{"record":{"id":"90d4cf346a47d69e","repo":"paperclipai/paperclip","slug":"expected-four-documented-annotations-for-screen-screen-id","errorCode":null,"errorMessage":"Expected four documented annotations for screen ${screen.id}","messagePattern":"Expected four documented annotations for screen (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"doc/plans/chat-adapters/generate-wireframes-v2.mjs","lineNumber":35,"sourceCode":"  { id: \"07\", slug: \"access\", title: \"Access\", subtitle: \"Control who people act as in Paperclip.\", group: \"Manage\", kind: \"detail\", active: \"Access\", rationale: \"Identity linking and restricted guest authority are important but should never block initial connection.\" },\n  { id: \"08\", slug: \"behavior\", title: \"Behavior\", subtitle: \"Adjust what Maya sends and how messages are handled.\", group: \"Manage\", kind: \"detail\", active: \"Behavior\", rationale: \"One compact page exposes reviewed defaults and capability fallbacks; advanced routing stays collapsed.\" },\n  { id: \"09\", slug: \"conversations\", title: \"Conversations\", subtitle: \"See the Paperclip issue behind every external thread.\", group: \"Manage\", kind: \"detail\", active: \"Conversations\", rationale: \"This is the operator view of the one-thread/one-issue invariant and detach lifecycle.\" },\n  { id: \"10\", slug: \"activity\", title: \"Activity\", subtitle: \"Inspect deliveries, retries, and provider health.\", group: \"Manage\", kind: \"detail\", active: \"Activity\", rationale: \"Diagnostics stay out of setup and appear only when an operator needs them.\" },\n  { id: \"11\", slug: \"bound-task\", title: \"Refund workflow is failing\", subtitle: \"PAP-1842 · Created from Slack\", group: \"Related\", kind: \"task\", rationale: \"Externally created work remains a normal Paperclip task with explicit publication and assignment-lock affordances.\" },\n  { id: \"12\", slug: \"agent-channels\", title: \"Maya · Channels\", subtitle: \"Every place people can reach this agent.\", group: \"Related\", kind: \"agentChannels\", rationale: \"Agent detail summarizes endpoints and recent tasks, while connector administration remains in Apps.\" },\n];\n\nconst spec = readFileSync(join(root, \"2026-09-04-chat-adapters-ui-surfaces-v2.md\"), \"utf8\");\nconst annotationMap = new Map(\n  [...spec.matchAll(/### (\\d{2})[^\\n]*\\n\\nPurpose:[^\\n]*\\n\\n((?:\\d+\\.[^\\n]*\\n){4})/g)].map((match) => [\n    match[1],\n    match[2].trim().split(\"\\n\").map((line) => line.replace(/^\\d+\\.\\s*/, \"\")),\n  ]),\n);\nfor (const screen of screens) {\n  screen.annotations = annotationMap.get(screen.id);\n  if (!screen.annotations || screen.annotations.length !== 4) {\n    throw new Error(`Expected four documented annotations for screen ${screen.id}`);\n  }\n}\n\nconst esc = (value) => String(value)\n  .replaceAll(\"&\", \"&amp;\")\n  .replaceAll(\"<\", \"&lt;\")\n  .replaceAll(\">\", \"&gt;\")\n  .replaceAll('\"', \"&quot;\");\n\nconst tx = (x, y, value, size = 14, fill = \"#000\", extra = \"\") =>\n  `<text x=\"${x}\" y=\"${y}\" font-size=\"${size}\" fill=\"${fill}\" stroke=\"none\" ${extra}>${esc(value)}</text>`;\nconst ln = (x1, y1, x2, y2, extra = \"\") => `<line x1=\"${x1}\" y1=\"${y1}\" x2=\"${x2}\" y2=\"${y2}\" ${extra}/>`;\nconst rc = (x, y, w, h, extra = \"\") => `<rect x=\"${x}\" y=\"${y}\" width=\"${w}\" height=\"${h}\" rx=\"6\" ${extra}/>`;\nconst circle = (x, y, r, extra = \"\") => `<circle cx=\"${x}\" cy=\"${y}\" r=\"${r}\" ${extra}/>`;\n\nfunction wrap(value, width = 48, max = 2) {\n  const lines = [];\n  let current = \"\";","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/doc/plans/chat-adapters/generate-wireframes-v2.mjs#L17-L53","documentation":"The wireframes-v2 generator script enforces a documentation contract: every screen must have exactly four annotation notes parsed from the wireframe spec markdown (annotationMap built from numbered lines per screen id). When a screen has no entry, or an entry with a count other than 4, the script aborts so an under-documented screen never reaches the generated viewer.","triggerScenarios":"Editing `doc/plans/chat-adapters/generate-wireframes-v2.mjs` (or its annotation source block) to add/rename a screen id without adding exactly four numbered annotation lines for it, or accidentally deleting one of the four numbered lines, or adding a fifth. Runs via `node doc/plans/chat-adapters/generate-wireframes-v2.mjs`.","commonSituations":"Adding a new screen to the screens array without documenting it; a typo in the screen id so annotationMap.get(id) returns undefined; markdown cleanup that stripped the `1. ` numbered prefix; renumbering annotations after merging provider changes.","solutions":["Open the generator, find the screen whose id is named in the error, and add exactly four numbered annotation lines (`1. ...` through `4. ...`) for that id in the annotations source.","If a fifth note exists, remove it so the count is exactly 4.","Verify the screen id in the screens array matches the id used in the annotation block (typo check).","Re-run `node doc/plans/chat-adapters/generate-wireframes-v2.mjs` to confirm it passes."],"exampleFix":"// before\nscreens.push({ id: \"25\", title: \"New screen\" }); // no annotations\n// after\n// add to annotation source:\n// 1. Header marks the step\n// 2. Primary action invites the bot\n// 3. Fallback link opens advanced mode\n// 4. Footer explains what Paperclip creates\nscreens.push({ id: \"25\", title: \"New screen\" });","handlingStrategy":"validation","validationCode":"for (const screen of screens) {\n  const a = annotationMap.get(screen.id) ?? [];\n  if (a.length !== 4) console.warn(`screen ${screen.id} has ${a.length} annotations, expected 4`);\n}","typeGuard":"const hasFourAnnotations = (screen) => Array.isArray(screen.annotations) && screen.annotations.length === 4;","tryCatchPattern":"try {\n  generate();\n} catch (err) {\n  if (err.message.startsWith(\"Expected four documented annotations\")) {\n    const id = err.message.match(/screen (\\S+)/)?.[1];\n    console.error(`Add exactly four numbered annotation lines for screen ${id}.`);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["When adding a screen id, immediately add its four numbered annotations in the same change.","Keep screen ids and annotation keys in one data structure so they cannot desync.","Add a check script that counts annotations per screen before running the generator."],"tags":["build-script","documentation","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}