The schema-validated app generator
blueprints defined a small, closed document format for describing a page as data: a tree of typed blocks, each field either a literal or a template reference into a query or mutation, and every query or mutation bound to a real backend operation by its stable operation ID rather than a URL. One validator checked a document against every rule the format could express structurally and every rule it couldn’t — id uniqueness, variable scoping, an operation id that actually existed — and it never stopped at the first problem: it collected every finding in one pass, each with an exact path to where it lived, so anything reading the result got the complete picture at once. The same expression language that resolved those references ran identically on the server, inside an embedded JS interpreter, and in the browser, off one shared implementation — zero drift between the two hosts by construction.
On top of that sat a generator: a plain-English description went in, a model drafted a page document, the validator’s complete diagnostics fed straight back as the repair signal, and the loop closed — bounded to a handful of rounds — on a document proven valid against a real target API. A step further, the same loop could synthesize the backend operations themselves and merge them safely into an existing API surface, so a page and the endpoints it needed could both come from one description. Underneath both, a human’s edit was sacred: a single explicit marker froze exactly the node a person had touched, so the next regeneration rebuilt everything around it without ever clobbering what they’d changed. It rendered its own marketing site’s landing page as one such document.
A support-ticket triage board is one such document: a query-bound table, two dialog forms, every read and write reaching the backend through an operation id instead of a URL — this is the whole page, nothing else runs:
{
"version": "page/v1",
"type": "page",
"blocks": [
{ "type": "headline", "label": "Support ticket triage", "level": "h1" },
{
"type": "text",
"content": "A tiny app declared as a page/v1 blueprint: a table fed by a query, triage through two dialog forms, every read and write bound by operationId."
},
{
"type": "query-provider",
"queries": [
{ "id": "openTickets", "operationId": "ListTickets" },
{ "id": "agents", "operationId": "ListAgents" }
],
"blocks": [
{
"type": "card",
"blocks": [
{
"type": "headline",
"label": "Open tickets ({{query.openTickets.total}})",
"level": "h2"
},
{
"type": "table",
"id": "ticketRow",
"data": "query.openTickets.items",
"columns": [
{ "type": "table-column", "label": "Id", "content": "{{loop.ticketRow.id}}" },
{ "type": "table-column", "label": "Subject", "content": "{{loop.ticketRow.subject}}" },
{ "type": "table-column", "label": "Status", "content": "{{loop.ticketRow.status}}" },
{ "type": "table-column", "label": "Assignee", "content": "{{loop.ticketRow.assignee}}" }
]
}
]
},
{
"type": "dialog",
"trigger": { "type": "button", "label": "File a ticket" },
"blocks": [
{ "type": "headline", "label": "File a new ticket", "level": "h3" },
{
"type": "form",
"id": "fileTicket",
"mutation": {
"operationId": "CreateTicket",
"body": [
{ "key": "subject", "value": { "kind": "variable", "value": "this.subject" } },
{ "key": "priority", "value": { "kind": "variable", "value": "this.priority" } }
]
},
"blocks": [
{ "type": "form-string-input", "id": "subject", "label": "Subject", "description": "What's broken?" },
{
"type": "form-select",
"id": "priority",
"label": "Priority",
"options": [
{ "label": "Low", "value": "low" },
{ "label": "Medium", "value": "medium" },
{ "label": "High", "value": "high" }
],
"defaultValue": "medium"
},
{ "type": "button", "label": "Submit" }
]
}
]
},
{
"type": "dialog",
"trigger": { "type": "button", "label": "Update status" },
"blocks": [
{ "type": "headline", "label": "Update ticket status", "level": "h3" },
{
"type": "form",
"id": "updateStatus",
"mutation": {
"operationId": "UpdateTicketStatus",
"params": { "id": "this.ticketId" },
"body": [
{ "key": "status", "value": { "kind": "variable", "value": "this.status" } }
]
},
"blocks": [
{ "type": "form-string-input", "id": "ticketId", "label": "Ticket id" },
{
"type": "form-select",
"id": "status",
"label": "New status",
"options": [
{ "label": "Open", "value": "open" },
{ "label": "In progress", "value": "in-progress" },
{ "label": "Resolved", "value": "resolved" }
],
"defaultValue": "open"
},
{
"type": "form-checkbox",
"id": "notifyAgent",
"label": "Notify",
"description": "Email the assigned agent about this status change",
"defaultValue": true
},
{ "type": "button", "label": "Save" }
]
}
]
}
]
},
{ "type": "link", "label": "Back to the lab", "href": "/rnd/blueprints" }
]
}
Rendered against a canned client bound to those same operation ids, it was a real page, not a mockup of one:

Filing a ticket opened a real dialog over a real form — the form-string-input and form-select blocks from the document above, nothing hand-coded:

Problems it solved
- Plain English to a valid page. Bounding the generation loop to a few draft-and-repair rounds against a strict, complete-diagnostics validator produced pages that validated against a real API.
- Edit-preserving regeneration. A single explicit per-node marker kept a human’s hand-tuned change through every later AI pass over the same document.
- Two hosts, one evaluator. One expression-language implementation, shared between the server and the browser, removed drift between them.
- Extending the target backend. Synthesized operations merged into a live API surface under the same validation the hand-written ones got.
Related: the same edit-is-sacred instinct underlies the runtime’s human-in-the-loop model, where an approval is never something the next turn quietly redoes.