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.
# Conflicts: # .agents/skills/verify-dms/features/README.md
Upd4ting
marked this pull request as ready for review
November 11, 2026 23:47
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: it stacks #858 and #857, so its diff shows both until they land.
Why
Iterating on a visual must be much faster as a mockup than through the builder. #858 adds the mockup engine:
mockups/<name>/spec.json, drawn live on/mockup, with annotations a person leaves on it. An agent in Safe mode, the default in the dashboard chat, cannot write files. These four MCP tools let it write and patch a spec and read and resolve those annotations through the core MCP server. Task: ANT-2041. Thedms-mockupskills (ANT-2042) use these names.Stack
Base:
feat/builder-engine-mcp-core(#857, the MCP server). The branch also mergesfeat/mockup-engine(#858), because the tools call its compiler and annotation store. Until #858 lands, this diff shows #858's files too.Review only these commits:
4a0eb9befeat(mcp): apply RFC 6902 JSON Patch operations to a JSON documentbb571aa1feat(mcp): add the mockup tools0b2a6f60,14ebda55refactor(mcp): import the engine through its entry, and declare the tools as constants tested throughMCP_TOOLSa67986f2,9c28b558test(mcp): the engine's depth and duplicate-id checks throughmockup_write_spec, and tests on a unique mockup under the working directory.dependency-cruiser/backend.cjsadds"mockup"to the tier under["mcp"], somcpmay importmockup.Once #857 and #858 merge, rebase this branch onto
main. It then shrinks to those commits.Tools
mockup_annotationsread{ mockup, status?: "open" | "resolved", page? }{ annotations }, filteredmockup_resolve_annotationbuilder{ mockup, id }annotations.jsonmockup_write_specbuilder{ mockup, spec }{ ok, errors: [{ path, message }] }mockup_patch_specbuilder{ mockup, patch: RFC 6902 operations }mockup_write_specmockup_write_specvalidates exactly as the watcher does (parseSpec, thencompileSpecagainstListBlockTypes()). It writesmockups/<mockup>/spec.jsonatomically, pretty-printed, only whenerrorsis empty, so a refused spec leaves the file untouched. Eachpathis a JSON pointer into the spec.mockup_patch_specapplies the patch to the current spec on a copy, then validates and writes likemockup_write_spec. A failedtestoperation or a bad pointer is an error naming the operation index.ok: falseanswer. A bad name, a failed patch operation or a missing mockup or annotation is anisErrorresult.mockupwithparseMockupName, so no tool reaches a path outsidemockups/<name>/. Safe mode serves all four, because none isproject.Proof
All run on the committed head after a clean
pnpm --dir packages/dms run build:pnpm run checks:Checks: 11 passed, 0 failed.pnpm --dir packages/dms typecheck: passes.dist/test/unit/mcp/mockup-*.test.js,dist/test/unit/mockup/*.test.js):67 passing.pnpm --dir packages/dms test:unitatb2c15792:2634 passing (15m). The later merge of feat(mockup): live JSON mockups rendered by the real block preview #858's final fixes (0f4b2d60) touches only the sketch check, a frontend type and their tests; the mockup and MCP tests above ran on it.The tests each name the wrong implementation they catch, among them:
parseMockupName: eight traversal names are refused by all four tools and nothing outsidemockups/<name>/is createdproject: Safe mode'stools/listmust list all fourtestoperation