Repository navigation
Conversation
A spec under mockups/<name>/spec.json is parsed at its boundary, validated against the live block registry with JSON-pointer issues, its fixture: references rewritten, TableView built through TableView.fromSource and sketches checked like dms-ai's. A watched mockup streams its state and annotations over SSE. /mockup renders DmsMockupViewer, dev only.
buildMockupSchema describes spec.json with one node branch per block type, plus the sketch and componentName nodes. Each preview adapter now carries both directions, so the schema accepts the format the compiler validates: a block option given as a child, a form's section fieldIds. scripts/mockup-schema.mjs writes data/mockup-spec.schema.json, and its --check mode runs in CI next to docs:catalog:check.
The example spec and every non-controller docs example compile with no issue; a bad option, an unknown type, a missing fixture and a forbidden sketch are reported at their pointers; a block registered at runtime validates. The store keeps the last good pages on a JSON error, reaches a subscriber within a second of a save, and keeps every annotation of concurrent creates. The rows route sorts, searches and pages, and stopping the engine ends its event streams. src/mockup ranks with routes and dev.
A reference page for skill authors: the file layout, the spec envelope and page options, nodes and their preview-format exceptions, fixtures, controller blocks, sketches and their refused markup, the compiled state, the HTTP API and its events, annotations, and the viewer.
…dren GET /api/mockup answered 404: a bare @get() does not match a controller's root, @get("/") does. The compiler now reports a child its parent does not accept (Grid takes only GridRow; a block that is no container takes only the children its block options name), the example spec wraps its cards in a GridRow, and the name 'schema' is reserved for the static schema route.
…eview The render message carries an optional padding flag, kept from the previous state when absent; the mockup viewer sends each page's, padded by default.
…docs example dms-control map failed on the unmapped src/mockup files. The mockups feature covers them with a journey a run's built backend can drive: every mockup route answers 404 outside development. The docs example of MockupState declares the PreviewNode it names, which the docs check compiles.
…tory, refuse slash-separated URLs - The events route ends its stream when the request's socket closes: the API only pipes the stream into the response, so a closed tab left its stream, listener and watcher behind. - One recursive watcher covers mockups/ while anyone subscribes, so a mockup directory deleted and made again keeps reaching its subscribers instead of freezing on its last state. - The sketch check refuses src and href written after a slash or a quote (<a/href=...>), which HTML parses as an attribute. - A spec nested deeper than 100 JSON levels is refused with an issue instead of overflowing the compiler's stack.
…ng nodes, 404 a missing mockup's events - The rows route sorts strings with a locale comparison, so "apple" comes before "apple pie", and rows missing the field come last both ways. - Two siblings with one id are an issue at the second id: the preview gives both the same layout path, so one could not be traced back to the spec. - GET :name/events answers 404 for a missing spec instead of an empty 200 stream, and POST :name/annotations answers 404 unless page and path name a node of the spec now. - The sketch check refuses image() and image-set() of a remote URL and an at-rule written with an escape.
…d read sketch CSS with escapes decoded - The events route listens for the request's close before it reads the spec, and serves nothing to a client already gone. - The sketch check decodes CSS escapes before matching, refuses any quoted remote URL in CSS (every image-set candidate), checks the CSS of style elements and attributes, and skips quoted attribute values when it looks for a tag's attributes (<a title=">" href=...>).
…es its block's place
…s in one total order, refuse entities in style attributes - The store watches mockups/ for its own entries and each subscribed mockup directory on its own, re-arming one that is made again: a recursive watch read every sibling, and one unreadable directory failed every stream. A directory that cannot be watched is logged, not thrown. - Numbers and numeric text sort by value before other text, so mixed rows sort in one order whatever their input order, and "1.25" < "1.5". - A sketch's style attribute may not hold character references (().
…er references " ' & < and > are ordinary HTML escaping: they are read as the characters they stand for before the CSS checks, so image-set("https://...") is still refused, and any other reference ((, () still is.
Upd4ting
added a commit
that referenced
this pull request
Nov 11, 2026
Upd4ting
marked this pull request as ready for review
November 11, 2026 22:48
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⚠ sensitive: more than 400 non-test lines, and dev-only routes without a login that write under mockups/.
Why
Iterating on a visual must be much faster as a mockup than through the builder, whose save typechecks and reloads (several seconds). A mockup is a JSON spec drawn by the real block renderer (
DmsBlockPreviewon/dms-preview), so it always matches what the DMS can draw. Task: ANT-2041.For the
dms-mockupskills (ANT-2042)packages/dms/data/mockup-spec.schema.json, generated from the block registry withpnpm --dir packages/dms mockup:schema. CI fails when it is stale.GET /api/mockup/schemaserves the live registry's schema, which includes installed modules' blocks.packages/dms/data/mockup-example/spec.json.packages/dms/docs/08.ai-and-claude-code/9.mockups.md.mockup_annotations,mockup_resolve_annotation,mockup_write_specandmockup_patch_specare in feat(mcp): mockup tools to write and patch a spec and read and resolve its annotations #859, stacked on feat(dms): serve the builder engine and its MCP tools to coding agents in development #857.What changes
All of it is development only: nothing is registered outside
ajs project run.Spec.
mockups/<name>/spec.jsonholds pages in theDmsBlockPreviewtree format, page options (theme,locale,padding),fixturesper page, andsketchnodes.Validation runs against
ListBlockTypes()at runtime, so blocks from installed modules count:ValidateBlockOptionscontainerandallowedChildrenEach issue is a JSON pointer into
spec.json. Compilation is lenient: an invalid node is still drawn, and its issues are listed.Fixtures. A string option
fixture:<key>points at a page fixture through/api/mockup/:name/pages/:page/fixtures/:key.TableViewis built with the realTableView.fromSourceover a fixture array, so blocks that need a backend render from fixtures instead of theneeds_backendnotice.Sketches. A
sketchnode is static HTML and CSS. It renders in<iframe sandbox="">with a CSP that loads onlydata:URLs, drawn hatched and outlined with a "Custom" label.Live preview. The store watches each subscribed mockup directory and sends the compiled state over SSE.
/mockup(DmsMockupViewer) posts each state to the/dms-previewiframe with the existingrendermessage: no typecheck and no reload. It has page tabs, desktop and mobile widths, and the issues list.Annotations. In Annotate mode, a click on a block writes
{ page, path, text, status }tomockups/<name>/annotations.jsonthroughGET/POST/PATCH /api/mockup/:name/annotations. The preview gainsinspectandselectmessages for that click.Verify map. A new
mockupsfeature has a journey proving that every mockup route answers 404 on a built backend.Proof
postMessageand the Vue render.pnpm run checks:Checks: 11 passed, 0 failed.compile,store,routes):35 passing.@Get()→@Get("/"))url(,image-setcandidates, entities instyleAttention
/api/mockup/*routes and the/mockuppage are development only and need no login, like/dms/dev. That includes the annotation writes. Every name, page id, fixture key and annotation id is checked against its pattern before a path is built, so no request reaches outsidemockups/<name>/.mockups/directory while a viewer is open stops its updates until a reload. The docs say so.