paperclipai/paperclip · error
Expected four documented annotations for screen
Error message
Expected four documented annotations for screen ${screen.id} What it means
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.
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.
Example fix
// before
screens.push({ id: "25", title: "New screen" }); // no annotations
// after
// add to annotation source:
// 1. Header marks the step
// 2. Primary action invites the bot
// 3. Fallback link opens advanced mode
// 4. Footer explains what Paperclip creates
screens.push({ id: "25", title: "New screen" }); Defensive patterns
Strategy: validation
Validate before calling
for (const screen of screens) {
const a = annotationMap.get(screen.id) ?? [];
if (a.length !== 4) console.warn(`screen ${screen.id} has ${a.length} annotations, expected 4`);
} Type guard
const hasFourAnnotations = (screen) => Array.isArray(screen.annotations) && screen.annotations.length === 4;
Try / catch
try {
generate();
} catch (err) {
if (err.message.startsWith("Expected four documented annotations")) {
const id = err.message.match(/screen (\S+)/)?.[1];
console.error(`Add exactly four numbered annotation lines for screen ${id}.`);
process.exit(1);
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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`.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- ACPX verified runtime descriptor is misplaced
- : capture has no states
- Could not load wireframe viewer styles
- Could not load wireframe viewer styles
- Could not load wireframe viewer styles
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/90d4cf346a47d69e.
Report an issue: GitHub.
Appendix: source
Thrown at doc/plans/chat-adapters/generate-wireframes-v2.mjs:35
{ 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." },
{ 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." },
{ 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." },
{ 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." },
{ 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." },
{ 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." },
];
const spec = readFileSync(join(root, "2026-09-04-chat-adapters-ui-surfaces-v2.md"), "utf8");
const annotationMap = new Map(
[...spec.matchAll(/### (\d{2})[^\n]*\n\nPurpose:[^\n]*\n\n((?:\d+\.[^\n]*\n){4})/g)].map((match) => [
match[1],
match[2].trim().split("\n").map((line) => line.replace(/^\d+\.\s*/, "")),
]),
);
for (const screen of screens) {
screen.annotations = annotationMap.get(screen.id);
if (!screen.annotations || screen.annotations.length !== 4) {
throw new Error(`Expected four documented annotations for screen ${screen.id}`);
}
}
const esc = (value) => String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """);
const tx = (x, y, value, size = 14, fill = "#000", extra = "") =>
`<text x="${x}" y="${y}" font-size="${size}" fill="${fill}" stroke="none" ${extra}>${esc(value)}</text>`;
const ln = (x1, y1, x2, y2, extra = "") => `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${extra}/>`;
const rc = (x, y, w, h, extra = "") => `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="6" ${extra}/>`;
const circle = (x, y, r, extra = "") => `<circle cx="${x}" cy="${y}" r="${r}" ${extra}/>`;
function wrap(value, width = 48, max = 2) {
const lines = [];
let current = "";View on GitHub (pinned to 3f1d897a7c)