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

  1. 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.
  2. If a fifth note exists, remove it so the count is exactly 4.
  3. Verify the screen id in the screens array matches the id used in the annotation block (typo check).
  4. 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 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


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("<", "&lt;")
  .replaceAll(">", "&gt;")
  .replaceAll('"', "&quot;");

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)