Valid and empty
Two generated UI specs passed json-render's validation and showed nothing. The root pointed at an element that was never written, and the check that said so was a different function.
Date: 2026-11-09. Context: the generative UI spike (spike/genui, journal ch. 42).
What happened
The spike asked the same free model, three times with each prompt, for the same view: a business
account's balance, money in and out, two failed direct debits, three transactions. Our renderer's
three answers rendered. json-render's first two came back from catalog.validate with
success: true, and rendered as an empty string.
The JSONL told why. The model began with {"op":"add","path":"/root","value":"main"}, the name
used in the example of json-render's prompt, then built its view under another name,
mainContainer. The root pointed at nothing. catalog.validate checks each element's type and
props, not the references between them; validateSpec, a second function, does report "Root
element "main" not found"; autoFixSpec does not repair it. The third answer rendered, with every
amount written as a $template string ("48,210.55 EUR") instead of a number in a Stat.
What we learned
- A valid spec can be an empty view. Props that pass their schemas say nothing about whether the tree holds together, or whether anything reaches the screen.
- A prompt teaches what it describes. json-render's prompt explains state,
repeat, dynamic expressions and templates at length, for interactive UIs. Asked for a read-only overview, the model used all of them, and the more machinery a spec has, the more ways it can come apart. Ours describes ten components and one shape. - Formatting moves to wherever the spec lets it. Given a template string, the model formatted
the amount itself, past
FormatNumber: the same mistake the design system exists to prevent, made by a model instead of an agent writing code.
What it means for Vektos
It is the benchmark's own lesson in a new place. A page that compiles is not a page that works,
so the benchmark checks mustShow in Chrome; a spec that validates is not a view that works, so
generated UI must be judged by what it renders too. The chat evals of GenUI-2 will count what a
generated view shows (the figures asked for, formatted), not only whether it parsed.
Follow-ups
- In
packages/genui, a renderer that reports a node it leaves out, so a view never fails silently. - A repair step: send the issues back to the model once, as json-render's
formatSpecIssuesdoes. - Retry the comparison with a paid model when there is a budget for it: a stronger model may follow json-render's longer prompt better. Three tries on one free model is a small sample.
The toast that took every page down
A design-system module that created its toaster at import time crashed every page of the dashboard, including the ones without a toast. Every check passed; only the Next build and a browser saw it.
The view written as a string
Asked for a view as a tool argument, the model wrote it as JSON inside a string, a third of the time, and sometimes with a brace missing. The shape of the schema decided it.