validate

Every forbidden pairing in surface.

This reads the adjacency list rather than the rendered tree, and the difference is not a shortcut. dev.ynagai.a2ui.core.surface.walk follows every path, so it emits a component once per route that reaches it — n layers each referencing the same two children produce 2^n instances from 2n components, and a template's subtree is instantiated once per row of a list the agent sends. Checking there would report one forbidden pairing as many times as the payload happens to reach it, and would cost the same. Composition is a property of the edges: a Menu may not contain a Label once, whoever renders it and however often.

A component the surface has not received yet is skipped rather than reported: the specification requires a renderer to keep drawing while a surface arrives, so an id naming nothing is a component still in flight, not a composition error. A component whose catalog this validator does not hold is skipped for the same reason the resolver skips it — CatalogValidator is what reports that.

Components no route reaches are checked too. They are in the surface, the next updateComponents may mount them, and reporting the pairing now is what lets an agent fix it before it is drawn.

Throws

if resolver refuses a component — CatalogChildResolver does that rather than return a shortened list of children, and a composition verdict over children that were quietly dropped would be worth nothing.