HelpKoder
Blog

hidden states

What a screenshot hides from a coding agent

A UI screenshot never shows auth, empty, error, loading, or the API. A spec has to name those states or mark them out of scope, or the agent will invent them.

AudienconUpdated August 14, 2026

A screenshot shows one happy frame. It does not show login, empty, error, loading, permissions, keyboard focus, or the API behind the table. If your spec stays silent, a coding agent will invent those states or skip them. HelpKoder writes a “what the screenshot hides” section so the gap is explicit: infer it, upload another frame, or mark it out of scope.

I used to bury this under “accessibility insights.” That was the wrong heading. Accessibility is part of it (contrast, hit targets, labels). The larger failure is missing product states. An agent that builds only the Dribbble frame ships a demo, not an app.

Why do agents hallucinate states the mock never shows?

Models are trained to be helpful. A table implies pagination, search, and a row action. A form implies validation. A lock icon implies auth. None of that is in the pixels. The model fills the blank with a generic SaaS, which is how you get a purple shadcn dashboard that is not your mock.

The honest spec lists inferences separately from observations. Observation: 12px radius, #1f1610 ink, a three-column grid. Inference: email/password login because there is an avatar in the corner. Out of scope: billing, because nothing in the frame or the extra shots mentions it.

HelpKoder’s verdict uses this. YES means the hidden list is short. KINDA means the happy path is clear and the failure path is not. NOT REALLY means you cannot even name the product states. Can you vibe this screenshot? is the grading rubric.

Which states should every spec name?

I name six if the UI is an app interior:

  1. Authenticated vs signed out.
  2. Empty (zero rows, zero projects, first-run).
  3. Loading.
  4. Error (network, 403, 422).
  5. Partial permission (viewer vs editor).
  6. Small viewport, if the frame is desktop only.

If the screenshot is marketing, the list shrinks: mobile nav, form success, form error. Do not invent an app shell.

When I have the extra screen, I attach it. HelpKoder takes up to six frames in one spec. A settings sheet, a confirm dialog, and an empty table are worth more than another paragraph of prose.

What about accessibility that you cannot see?

Contrast is partly visible. Label-to-input pairing is not, if the mock uses placeholder-as-label. Focus rings never appear in a static PNG. Keyboard order never appears. Screen reader names never appear.

The spec should require: visible labels, a focus ring that uses the accent token, hit targets that are not 12px icons, and alt on any image that is not decorative. It should not dump a WCAG checklist. Agents ignore checklists. They follow a short invariant in AGENTS.md or CLAUDE.md. Which file the agent reads matters here. Put the invariant in the file the tool loads, not in a blog-shaped appendix.

How do APIs and data models get inferred?

A table of “Projects” implies a Project record. A composer implies a Message. A price implies a Plan you may not want in v1. I let HelpKoder sketch Prisma models from nouns on the screen, then I chat them down. “No billing.” “Messages are local.” “Use Neon.”

The danger is a schema that is larger than the UI. The agent then builds admin pages you never mocked. The spec’s job is to cap the model at what the frames need. Routes follow the same cap.

What should you leave out on purpose?

Motion, unless the screenshot is a video frame or you wrote it in chat. Illustration style, unless you will supply assets. Real customer data. Microcopy you have not approved. A second color system “for dark mode” if the mock is light only.

Leaving things out is a product decision. The spec should say “out of scope” in a sentence, not omit the topic and hope. Omission is how agents “add polish.”

How does this change the kickoff you paste?

The kickoff tells the agent the screenshot is canonical for what is visible, and the spec is canonical for what is not. That split is the whole method. Pixels win arguments about color and layout. The hidden-state list wins arguments about empty and error.

Pipeline detail is in how a screenshot becomes a spec. The product wrapper is HelpKoder. Then open the studio and attach the frame you actually have, not the one you wish you had.

Drop a screenshot. Leave with a spec.

Stream an agent-ready document for Cursor, Claude Code, or v0. Copy the kickoff. Attach the frame.

Open the studio