This entry is for front-end leads and product engineers who use a coding agent on an existing app and want reviewers to click through a feature before anyone builds the backend.
Both skills started inside the SkalX web app, written for our own stack, flag service and folder layout. For this release I took out everything specific to that codebase, so the agent now reads each project's conventions instead of assuming ours.
Prototypes made in a design tool drift away from the real app. Prototypes built straight in the app have the opposite problem: they slowly turn into half-finished production code, with fake data wired into real components and nobody sure what was approved. I wanted both properties at once, a mock that looks exactly like the app and a clean line between "the team is still deciding" and "this is shipping".
So I wrote it down as two Agent Skills and open-sourced them as mockship.
The idea
#UI work gets two phases with a sign-off between them, and each phase has its own skill.
The status line in MOCK.md moves from draft to approved only with a name. Promotion can stop twice: for a missing API operation, and for a visible change, which needs a new sign-off. The flag stays off the whole way.
| Skill | Builds | Leaves out |
|---|---|---|
ui-to-mock | Real routes, real layout shell and theme, fake in-memory data, a flag that defaults off | Tests, translations, API calls, edits to shared code |
mock-to-ui | Real API operations, access checks, shared components, translations, tests | Any change a user can see, unless you approve it |
Building the mock
#The agent reads the project first: framework, router, design system, flag mechanism, data layer, i18n, tests and permissions. Then it lists the screens, the flow between them, the states each one needs, and who sees what. Anything the request leaves open, it asks about before writing code.
Three rules keep the mock cheap and easy to throw away.
The flag comes first. The agent adds a flag the same way the project adds every other flag, with the default off, before the first screen exists. A gate component renders the app's own not-found page when the flag is off, so the mock is invisible in every environment where nobody switched it on. If the project has no flag system, the agent offers a build-time env var, a development-only route or a dev URL switch, and lets you pick. It does not invent a flag system.
All the code lives in one feature folder. Shared code is read-only. When a shared component almost fits, the mock gets a local copy and lists it as a stand-in. The only edits outside the folder are the touchpoints: the flag, one route per page, and an entry point if you ask for one. A script checks that against the git diff.
Everything the mock writes is in the green box. Shared code is imported and never edited, and the touchpoints are the only files outside the folder that change.
Every state has a URL. Reviewers approve the empty, error and loading states too, so each screen reads a query parameter:
/invoices populated
/invoices?scenario=empty empty
/invoices?scenario=error error
/invoices?scenario=loading loading
/invoices?scenario=write-error every write fails
/invoices?role=viewer read-only roleEverything the next phase needs goes into a MOCK.md in the feature folder: status, flag, stack, screens, review links, access, touchpoints, stand-ins, the data the screens assume, decisions and open questions.
Sign-off needs a name
#The agent never marks a mock approved on its own. "Product approved it" is not enough. It keeps the status at draft and asks who approved it and when. Only then does the status line change:
Status: approved by Ana Popescu on 2026-10-27Any visible change after that, such as new copy or a moved button, puts the mock back to draft.
Shipping it
#mock-to-ui starts by refusing to work on a draft. Then it goes through ten steps, each with its own finish line:
| Step | Done when |
|---|---|
| Inventory and sign-off | Approval recorded, every file classified, every touchpoint present |
| Baseline | One screenshot per review link, saved outside the repo |
| Contract | Every read and write maps to a real API operation |
| Access | Every access rule enforced by the app's own checks |
| Reuse | Every stand-in replaced, merged into the shared component, or kept on purpose |
| Structure | Components follow the project's conventions |
| Data and states | Nothing imports mockData; every approved state comes from real conditions |
| Text | Every string goes through i18n, in every locale |
| Tests | Gate, access, states and interactions covered, and passing |
| Parity and hand-off | Screenshots match the baseline, MOCK.md deleted, flag still in place |
Two rules matter most. The mock is the spec: the agent changes how the feature is built, not what users see, and when the real API forces a visible difference it asks you. And a missing API operation stops the work: the agent drafts the contract for the backend owner and waits, instead of hand-writing a request around the API layer.
It also keeps a promotion record in MOCK.md, so a later session picks up where the last one stopped.
How I tested it
#Each skill has eval cases that run a real agent against a small React app, once with the skill and once without, using claude plugin eval. These are the results on version 1.3.0, one run per case:
| Case | With skill | Without |
|---|---|---|
| Builds a flag-gated mock | 1.00 | 0.27 |
| Offers options when there is no flag system | 1.00 | 0.25 |
| Asks who approved before promoting | 1.00 | 0.00 |
| Keeps shared components read-only | 1.00 | 0.75 |
| Stops on missing API operations | 1.00 | 0.83 |
| Promotes an approved mock end to end | 1.00 | 0.89 |
Without the skill, the agent writes good code and skips the process. It leaves mock routes outside the flag gate, adds tests and translation keys to a mock that will be thrown away, builds a flag system nobody asked for, and starts promoting as soon as someone says "it's approved". Those are the steps the skills exist for.
The evals also found problems in my own setup:
- The judge only saw part of the run. A judge model sees the first and last twelve messages of a long run, so it kept failing runs for work done in the middle. I moved those checks to the files a run leaves behind, plus checks on which commands ran.
- The check scripts could pass by accident. In a sandbox that blocks temp files, both scripts read nothing and printed OK. An eval agent noticed it. Both scripts now avoid temp files, and a test runs them with temp files blocked.
When to skip it
#The process costs something. Every mock adds a flag, a feature folder and a review round, and mock-to-ui then rebuilds the data layer, the copy and the tests. That pays off when other people have to agree on a flow before the backend exists. It does not pay off when:
- The change is small. A copy fix, a new column or one extra field is faster to build straight away.
- Nobody else signs off. If you are the only reviewer, the named approval is ceremony. Build it behind a flag and move on.
- The backend and the design are settled. With a final design and every API operation in place, there is nothing left for a mock to decide.
- You are still exploring directions. Comparing five layouts is quicker in a design tool. Mock the one you picked.
The flag also stays after promotion. Turning it on and later deleting it is the product owner's job, so someone has to own that clean-up.
Try it
#In Claude Code:
claude plugin marketplace add marcellefter/mockship
claude plugin install mockship@mockshipOr with any agent that reads Agent Skills:
npx skills add marcellefter/mockshipThen ask for a mock in plain words, for example "Mock an Invoices list and detail page so product can click through it before the backend exists." The code, the sample app and the evals are on GitHub, under the MIT license.