Plan & Prior Art

Part of One User Interface — what shipped, what remains open, and why the window is real.

Where this stands at 1.8.0. Phases 0–3 shipped: the surface, the contracts, the drawn map, and the browser bridge (registration and invocation verified in Chrome Canary; haltija ships a native tier that consumes describe() directly). Phases 4 and 5 are not built — they are recorded here as direction, and the pages say so. Every "shipped" below happened in a shipping browser or a real test lane, not on paper.

The prior-art survey further down is the part worth reading even if you never use the agent surface: it is the only written comparison of the WebMCP landscape we know of, and it explains why a framework-native map is a different thing from an automation adapter.

The plan

phase status
0 — prove it ✅ live demos on every page of this family
1 — the surface ✅ shipped in 1.8.0 (tosijs/agent)
2 — manifests + contracts ✅ shipped — app, component and inline element contracts
3 — the bridge ✅ shipped — WebMCP auto-registration; registration and invocation verified in Chrome Canary 153; haltija ships a native tier. Open: haltija#16 bridge design, which 1.8.0 unblocks
4 — distance ⬜ not built — design recorded below
5 — headless embodiment ⬜ not built — see Headless Embodiment for what is real

Sequencing note: phases 0–1 are cheap and demonstrable now; 2 is where the schematic/2.0 work and this plan become the same work; 3–4 are ecosystem plays (file, don't fix — designs land as issues on haltija/tjs-lang/lukko); 5 is where the plan and tosijs 2.0 converge entirely.

The empirical lane: LLM-in-the-loop tests (cross-cutting)

Every legibility claim in this plan — flat records beat nested, arrows beat booleans, PNG schematic beats screenshot beats SVG source, glance-then-zoom beats full-dump — is currently an argument. All of them are measurable: give a model a task battery (find the affordance for X; which action changes path Y; complete this flow) against each encoding of the same app (JSON map / schematic PNG / raw screenshot / a11y tree), and score task success × token cost × turns. Once tjs makes model calls stupidly easy, this becomes a test lane like any other — sampled and budgeted rather than per-commit (models are slow, priced, and flaky), but regression-shaped: a change to describe()'s shape gets a measured delta, not vibes. The earlier design reviews kept ending at "the remaining questions are empirical" — this is the lane where they get answered. It doubles as the marketing evidence: "agents complete tasks N× more reliably against the map than against pixels" is a claim with a number in it.

First number (haltija, 2026-08-02): on a trivial page JSON wins outright (an image's fixed cost dominates); on a 60-control app the JSON map is ~1,567 tokens — right at the crossover with an image's ~1,000–1,600 vision tokens. Above that density the rasterized schematic wins, exactly the DeepSeek-OCR curve. So the encoding choice is itself density-dependent, which the eval lane should treat as a variable, not a constant.

Open questions

Prior art & the window — all facts below are as of 2026-08-21

This section is a snapshot, not a maintained page. Everything in it — browser versions, who has shipped what, which competitor exists — is external state we do not control and cannot generate, so it is stamped rather than promised. Do not read it as current; read it as dated. If the date is old and the answer matters, re-survey and re-stamp. It has already moved twice: the first survey was 2026-07-28, three and a half weeks before this one, and one of the five claims below had to be rewritten in that gap.

What remains unclaimed — the tosijs delta:

  1. State, not just tools. The spec confirms it: WebMCP is tools-only, with no MCP resources and no readable/writable state. Its own open questions list multimodal I/O, outputSchema and service workers — state is not among them. Nobody exposes a path-addressable, writable, observable model.
  2. Derived from records you already hold — not declared, annotated, packed or crawled. The blunt version of this claim ("everyone hand-registers") died between the two surveys, and the replacement is sharper. The question is where the knowledge comes from:
    • platform packs (Shopify, Cloudflare) know their domain, not your app;
    • the declarative API and Angular's Signal Forms derive from a declaration written for that purpose — an attribute, an opt-in flag — and only for forms;
    • crawlers and a11y-tree agents reconstruct intent from rendered output: a guess, however good, that decays as markup does;
    • tosijs reads records it already holds, because it created the bindings. Not a guess, not an annotation, not a pack — and still nobody else's claim, because no other mainstream framework has a wiring record to read.
  3. One-truth propagation. An agent write updates the human's UI because both observe the same registry. Every integration surveyed routes through a tool execute() and relies on the author to remember to sync.
  4. Push observation. The only event in the API is toolchange — the tool list changed, not the state. observe(path) remains a channel nobody offers.
  5. Embodiment independence. Headless app + vended UI has no equivalent in the agent-web space.

On the curb-cut framing itself: it is no longer novel — the a11y tree as the thing agents read is now a mainstream observation, down to a W3C CG document disambiguating "WAI-ARIA" from "Agent-ARIA". Claim the mechanism, not the metaphor. What is ours is that the surface is derived from the framework's own records and therefore prosecutes a11y defects rather than absorbing them — auditAccessibility over the same map, contract.role and description materializing as real ARIA. An integration absorbs discrepancies; an intrinsic surface prosecutes them.

The axis that actually separates these approaches: is the agent surface a SECOND ARTIFACT, or a facet of the one you already maintain?

Ordering the field by where the knowledge comes from is useful but incomplete — it invites an argument about whose intent counts. The sharper question is what the surface costs to keep true, and it sorts everything cleanly:

where knowledge comes from what it costs to keep true
a11y-tree agents, crawlers (webmcp-core) reconstructed from rendered output nothing to maintain, because it is a guess — and it decays as the markup does
platform packs (Cloudflare) a pre-built library, bolted on at the edge maintained by the platform, knows nothing about your app
platform-as-API-consumer (Shopify) the platform's own domain model — real knowledge, well used a separate thing, built and maintained beside the app
declarative form API, Angular Signal Forms a declaration you wrote for that purpose a second declaration per form, kept in step by hand
tosijs records the framework already holds, because it created the bindings nothing — it is a projection of the one truth

Shopify is the instructive case, and the one that would be easy to mis-characterise. It is not post-hoc scraping: they are a leaf-node API consumer exposing their own commerce surface, and they have the information to do that part well — better names, better descriptions, more stable across a merchant's refactors than anything derived could be. For a fixed, known domain, a curated pack beats a derived surface, and it is worth conceding that plainly.

What it is not is a discipline — nothing about it generalises to the next app, because the next app is not a storefront. And it is not a free facet of a single source of truth: it is a second artifact, so it can drift from the app it describes, and it covers exactly what somebody decided to cover. A customised storefront whose checkout no longer behaves like the pack says still publishes the pack. Better than post-hoc, and a long way from a surface that cannot lie about the wiring because it is the wiring.

This is the documentation problem wearing different clothes. The house rule for docs is: don't promise to keep a second copy in step — delete it and generate it from the first (see tosijs-coding-practicesdocumentation-surface.md). An agent surface is the same shape. Every approach above except the last maintains a second description of the app, by hand or by platform, and inherits the drift that guarantees. Ours is generated from the wiring, which means the failure mode is not "the description went stale" but "the app stopped working" — and that is a failure someone notices.

Two consequences follow structurally, not from being ahead:

Strategic consequence. Targeting WebMCP first was right and is now clearly right — a real origin trial with default-on deployments, not a proposal. But the window narrowed: "derived agent surface" is being approached from the platform layer, the markup layer and the crawler layer at once. None of them reach the app's own records, and that is the defensible ground — so the framing to ship is derived from what the framework already knows, contrasted explicitly against packs, annotations and crawls. The parts nobody is near — state, propagation, observation, embodiment — are the durable delta, and they are where the next phase should spend.