paperclipai/paperclip · error

Expected five documented annotations for screen

Error message

Expected five documented annotations for screen ${screen.id}

What it means

Spec-validation guard in the wireframe generator: every screen definition in uiSurfaceSpec must carry exactly five documented annotations (the viewer renders a numbered annotation list per screen). This sentinel throws during generation when a screen's annotations array is missing, empty, or does not have length 5 — it is an authoring-data error in the script's screen spec, not a runtime condition.

Solutions

  1. Count the annotations array for the screen named in the message and pad or trim it to exactly five entries.
  2. Check for a recently edited screen spec where annotations were removed or the array was renamed so it resolved to undefined.
  3. Replace an empty annotations array with five meaningful annotation strings covering the screen's key behaviors.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at doc/plans/chat-adapters/generate-wireframes.mjs:197 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/30e976be0f547875. Report an issue: GitHub.

Appendix: source

Thrown at doc/plans/chat-adapters/generate-wireframes.mjs:197

      ["Comments and messaging", "GitHub · Linear · Notion · Telegram", "Token/app + resource allowlist", "Thread and mention rules vary"],
      ["Phone, social, and email", "WhatsApp · Twilio · X · Resend · iMessage", "Sender identity + webhook", "Media, window, and rate limits vary"],
    ],
    notes: ["Provider taxonomy drives setup fields without cloning the wizard.", "Capability rows name supported, fallback, and unavailable behavior.", "Shared states cover loading, empty, degraded, denied, rate-limited, revoked, and dead letter."],
  },
];

const uiSurfaceSpec = readFileSync(join(root, "2026-09-03-chat-adapters-ui-surfaces.md"), "utf8");
const annotationMap = new Map(
  [...uiSurfaceSpec.matchAll(/### (\d{2})[^\n]*\n\nPurpose:[^\n]*\n\n((?:\d+\.[^\n]*\n){5})/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 !== 5) {
    throw new Error(`Expected five documented annotations for screen ${screen.id}`);
  }
}

const esc = (value) => String(value)
  .replaceAll("&", "&")
  .replaceAll("<", "&lt;")
  .replaceAll(">", "&gt;")
  .replaceAll('"', "&quot;");

const text = (x, y, value, size = 14, fill = "#000", extra = "") =>
  `<text x="${x}" y="${y}" font-size="${size}" stroke="none" fill="${fill}" ${extra}>${esc(value)}</text>`;

const multiline = (x, y, lines, size = 14, fill = "#666", gap = 24) =>
  lines.map((line, index) => text(x, y + index * gap, line, size, fill)).join("\n");

function wrapWords(value, maxCharacters = 48) {
  const lines = [];
  let current = "";

View on GitHub (pinned to 3f1d897a7c)