# mock-to-ui

Adopt this skill: https://proskills.md/skills/other/marcellefter-mockship-skills-mock-to-ui

Page: https://proskills.md/skills/other/marcellefter-mockship-skills-mock-to-ui
This document is the public skill bundle for that page. It contains SKILL.md and every helper file the catalog carries. No login is required.

## File: skills/mock-to-ui/SKILL.md
size: 9286
sha: 3c589b4b64bb609902b9ecf2633909bf21152f09
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/SKILL.md

```text
---
name: mock-to-ui
description: Promote an approved UI mock built with ui-to-mock into production code (real API data, access checks, translations, tests, shared components and the project's conventions) and prove it still looks and flows like the approved mock. Use when a mock has been signed off and must ship, or to resume a promotion recorded in a MOCK.md. Not for changing a mock that is still in review (Status draft); that is ui-to-mock.
license: MIT
metadata:
  author: marcellefter
  version: "1.3.1"
---

# Mock to UI

The input is a feature folder built by the `ui-to-mock` skill. It is clickable and
flag-gated, uses fake data with states you can open from a URL, has plain-text copy and no
tests, and comes with a `MOCK.md` listing everything it touched. The output is the same
feature as production code.

**The mock is the spec.** Layout, flow, copy and every state behind a review link were
approved as they are. The steps below change how the feature is built, not what a user sees.
When a production constraint forces a visible change (the API cannot sort that column, or a
state the mock never showed), show it to the user and let them decide.

**The flag stays.** The feature keeps shipping dark behind the mock's flag and gate. Turning it
on, and later removing it, is a rollout decision for the product owner, outside this skill.

Each step defers to the project's own conventions for its subject: `AGENTS.md`, `CLAUDE.md`,
`CONTRIBUTING.md`, and any installed skill that covers it (API usage, components, i18n,
testing, design review). _Stack_ in `MOCK.md` names the libraries. This skill sets the order
and the finish line for each step.

## Keep a record

Promotion stops for decisions and can span sessions, so keep _Promotion_ in `MOCK.md` current.
Write one line per step (`done`, `waiting: <what>` or `skipped: <why>`), where the baseline
screenshots are, and every gap you drafted with its status. When **resuming**, read it first
and carry on from the first step that is not done. Don't redo a finished step from memory. If
the baseline folder is gone, say so. Step 10 then becomes a walkthrough against the mock's
review links, as long as the mock is still in git history.

## 1. Inventory and sign-off

Read `MOCK.md`, then every file in the feature folder.

- **Not a promotion?** When the user asks for a change to the mock itself (copy, a moved
  button, a new state), that is mock work. Make it the way `ui-to-mock` does, in the feature
  folder with literals and fixtures. A visible change to an approved mock puts _Status_ back
  to `draft`, which needs sign-off again before promotion continues.
- **Sign-off.** _Status_ must read `approved by <who> on <YYYY-MM-DD>`. If it still reads
  `draft`, ask the user who approved the mock and when, and record it before changing any
  code. "It's approved" without a name is not enough. Never assume approval.
- **No `MOCK.md`** (a mock built without `ui-to-mock`): rebuild one from the diff against the
  branch's merge base with these sections: Status, Flag, Stack, Screens and flow, Review
  links, Review, Access, Touchpoints, Local stand-ins, Data the screens assume, Decisions,
  Open questions, Promotion. Then confirm the gaps in it with the user.
- **A development-only switch** (a route or URL switch that only exists in development
  builds) cannot ship. Ask the user which shippable switch replaces it, a build-time env var
  or a flag service, and make the swap part of this work.

**Done when** the mock is recorded as approved, the flag can ship, every file is classed as
page, component, local stand-in, data, review helper or gate, and every touchpoint in
`MOCK.md` is confirmed present in the code.

## 2. Baseline

With the flag on, open every row of _Review links_ and save a screenshot of each (every
screen in every state and role) to a folder **outside the repository**, such as a temp or
scratch directory. Use whatever browser automation you have: a browser tool, Playwright, or
headless Chrome (`--headless --screenshot`). Step 10 compares the result against these.

**Done when** there is one screenshot per review link. If you have no way to take screenshots,
say so now. Step 10 then becomes a walkthrough the user does against the mock, and the report
must say the comparison was manual.

## 3. Contract

For every entry under _Data the screens assume_ and every write the screens perform, find the
real operation the frontend will call: a generated API client operation, an existing service
function, a GraphQL query or mutation.

**Done when** each read and write maps to a named operation. Any that does not is a **gap**.
For each gap, draft the missing contract from the mock: the operation, a suggested method and
path, request fields and response fields taken from the `mockData` types, and the screen that
needs it. Give the drafts to the user for the API owner, and **stop** until the user says how
to proceed. Step 4 (Access) may still run while you wait. Steps 5 onward wait, because the
answers can change what ships. Only the backend closes a gap. Don't hand-write a request
around the project's API layer or edit a generated client.

## 4. Access

Map every line under _Access_ onto the project's permission mechanism (route guards, a
`can()` helper, role checks) so each screen and action shows exactly for the roles the mock
showed it to. A role or permission the app does not have is a gap: report it as in step 3 and
stop. When _Access_ says everyone sees everything, confirm that against the routes the feature
sits under and move on.

**Done when** every _Access_ line is enforced by real checks, and the mock's `role` URL
parameter is gone.

## 5. Reuse

Walk _Local stand-ins_ and then every component in the folder, and resolve each one:

- **An existing shared or app component already does it.** Use that and delete the local one.
- **It is a variant of a shared component.** Extend the shared component in place, keep its
  current callers unchanged, and add the stories, docs or tests the shared code expects.
- **It is new.** Keep it in the feature. It moves to shared code only when a second feature
  or app needs it.

**Done when** every stand-in in `MOCK.md` has one of those three outcomes, and no feature file
duplicates rules from a shared component or the theme.

## 6. Structure

Apply the project's component conventions in full: when to split a component, where hooks and
utilities are extracted to, barrel files only where something outside the folder imports, and
its rules for state, effects and memoization.

**Done when** every component reads as markup: it calls hooks (or the stack's equivalent) and
renders.

## 7. Data and states

Replace the in-memory state with the project's data layer on the operations from step 3. Use
its data-fetching library, cache invalidation and API error display the way neighbouring
features do. The loading, empty and error states now come from real conditions, and each
must look like the state reviewers approved behind its review link. A state the mock never
showed (a partial failure, a stale-data banner) is a visible change, so show it to the user
first.

**Done when** nothing imports the `mockData` module, it and the `scenario` URL parameter are
deleted, and every approved state is reachable from real conditions.

## 8. Text

Move every literal into the project's i18n system, in every locale the app ships. The wording
approved in the mock is the primary-language translation. If the project has no i18n, skip
this step and say so in the report.

**Done when** every user-visible string in the folder, including `aria-label`, `title` and
placeholder props, goes through the translation function.

## 9. Tests

Follow the project's testing conventions. When the project has no test runner, ask the user
before adding one. Cover what the mock proved matters: the flag gate (off renders not-found,
on renders the page), the access rules from step 4, the utilities and hooks extracted in
step 6, the approved states, and the interactions the reviewers clicked through. Run the test
files you wrote.

**Done when** those files pass and each test would fail if the behaviour it names broke.

## 10. Parity, review and hand-off

1. **Parity.** Take the same screenshots as step 2 from the production code, reaching each
   state through real conditions, test data or a mocked network in the dev setup, and compare
   each pair. Fix every difference, or show it to the user and record their decision. With no
   screenshots, give the user the list of screens and states to walk through against the mock.
2. **Review.** Review the feature folder against the project's design and code-review
   standards and fix what the review finds.
3. **Hand-off.** Delete `MOCK.md`. Move its _Decisions_ and _Open questions_ into the pull
   request description, along with who approved the mock. At commit time the project's quality
   gates apply (lint, type-check, tests, PR checklist).

**Done when** the project's type-check and tests pass, `MOCK.md`, the `mockData` module and
both review parameters are gone, the flag and gate are still in place, every screenshot pair
matches or has a recorded decision, and the report names any gap from steps 3 and 4 and any
visible change the user approved.
```

## File: skills/mock-to-ui/evals/asks-for-sign-off-when-draft/case.yaml
size: 290
sha: 507dc7c9b9cbed12a6c60ef4fc82a085e5cd053d
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/asks-for-sign-off-when-draft/case.yaml

```text
schema_version: "1.1"
name: asks-for-sign-off-when-draft
expected_outcome: |
  MOCK.md says Status draft and the user names no approver. The run asks who approved the mock
  and when before changing any code, and does not mark it approved itself.
context:
  scaffold_script: place-files.sh
```

## File: skills/mock-to-ui/evals/asks-for-sign-off-when-draft/graders/01-asks-who-approved-and-when.md
size: 149
sha: 30db762cbe66467edec5ba35d3f6eddef4cc9d7e
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/asks-for-sign-off-when-draft/graders/01-asks-who-approved-and-when.md

```text
---
type: llm
focus: last_message
weight: 1
---

The response asks the user who approved the mock and when (a name and a date) before going further.
```

## File: skills/mock-to-ui/evals/asks-for-sign-off-when-draft/graders/02-no-code-changed-before-answer.md
size: 124
sha: 118d2f7fae44d3872e22c08d91281b3bcb021a57
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/asks-for-sign-off-when-draft/graders/02-no-code-changed-before-answer.md

```text
---
type: llm
focus: trace
weight: 1
---

No file outside src/features/invoices/MOCK.md is changed before the user answers.
```

## File: skills/mock-to-ui/evals/asks-for-sign-off-when-draft/graders/03-status-not-marked-approved.md
size: 116
sha: 00907b0eb8bdd4ab3b523aced76a80d268cc9e69
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/asks-for-sign-off-when-draft/graders/03-status-not-marked-approved.md

```text
---
type: regex
pattern: '^Status: draft'
flags: m
target:
  source: file
  path: src/features/invoices/MOCK.md
---
```

## File: skills/mock-to-ui/evals/asks-for-sign-off-when-draft/place-files.sh
size: 191
sha: bf06f00136f29e117f2f9e717a0341b1eb5e455b
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/asks-for-sign-off-when-draft/place-files.sh

```text
#!/usr/bin/env bash
set -eu
root=$(cd "$(dirname "$0")/../../../.." && pwd)
bash "$root/examples/scaffold.sh" - "$root/examples/overlays/invoices-mock" "$root/examples/overlays/status-draft"
```

## File: skills/mock-to-ui/evals/asks-for-sign-off-when-draft/prompt.md
size: 227
sha: 7f79a2271fa5cfe703ef70c8d7635c8777bcafff
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/asks-for-sign-off-when-draft/prompt.md

```text
---
tags: [mockship, mock-to-ui]
max_turns: 80
timeout_seconds: 1800
allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
---

Product loved the invoices mock, so it's approved — go ahead and make it production-ready.
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/case.yaml
size: 399
sha: 32e497d1d4391f0dbfe674d7a9a653c73e28a5a6
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/case.yaml

```text
schema_version: "1.1"
name: edits-draft-mock-without-promoting
expected_outcome: |
  The mock is still a draft and the user asks for a copy change, not a promotion. The run
  changes the subtitle as a plain literal in the mock and does not start promoting: no API
  wiring, no translation keys, mockData and the review parameters stay, Status stays draft.
context:
  scaffold_script: place-files.sh
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/01-subtitle-changed.md
size: 146
sha: 3e55f0ff3619f761015b21f0e4c201d2e712cd2d
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/01-subtitle-changed.md

```text
---
type: regex
pattern: 'All invoices you have sent'
flags: m
target:
  source: file
  path: src/features/invoices/InvoiceListPage/index.tsx
---
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/02-status-stays-draft.md
size: 116
sha: 00907b0eb8bdd4ab3b523aced76a80d268cc9e69
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/02-status-stays-draft.md

```text
---
type: regex
pattern: '^Status: draft'
flags: m
target:
  source: file
  path: src/features/invoices/MOCK.md
---
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/03-no-translation-keys.md
size: 125
sha: 88a2c2bc95f65fc60a4a9c85119a636a5006dde9
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/03-no-translation-keys.md

```text
---
type: regex
pattern: 'invoices'
flags: m
match: not_contains
target:
  source: file
  path: src/i18n/locales/en.json
---
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/04-mock-not-promoted.md
size: 174
sha: a7637cd274912d3bb1e6a1abf723723f4a586abb
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/graders/04-mock-not-promoted.md

```text
---
type: llm
focus: trace
weight: 1
---

The run does not delete mockData.ts, mockStore.ts or reviewParams.ts, does not wire the screens to src/api, and does not add tests.
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/place-files.sh
size: 191
sha: bf06f00136f29e117f2f9e717a0341b1eb5e455b
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/place-files.sh

```text
#!/usr/bin/env bash
set -eu
root=$(cd "$(dirname "$0")/../../../.." && pwd)
bash "$root/examples/scaffold.sh" - "$root/examples/overlays/invoices-mock" "$root/examples/overlays/status-draft"
```

## File: skills/mock-to-ui/evals/edits-draft-mock-without-promoting/prompt.md
size: 219
sha: b456b608c237a83711ddcf9bb5e8c11c276cbc27
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/edits-draft-mock-without-promoting/prompt.md

```text
---
tags: [mockship, mock-to-ui]
max_turns: 80
timeout_seconds: 1800
allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
---

In the invoices mock, change the list page subtitle to "All invoices you have sent".
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/case.yaml
size: 431
sha: c7372f0391b980934947d795eac2453499799c6d
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/case.yaml

```text
schema_version: "1.1"
name: extends-shared-badge-for-stand-in
expected_outcome: |
  The local status badge stops carrying its own styles: the shared StatusBadge gains an accent
  tone (existing tones and callers unchanged, test extended) that On hold uses; SelectField is
  kept as genuinely new or moved deliberately; the page-width literals get a theme token or an
  explicit decision.
context:
  scaffold_script: place-files.sh
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/01-shared-badge-gains-accent-tone.md
size: 172
sha: b7d4782514ed7fc85d3c994f1e7ebe326985d2ac
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/01-shared-badge-gains-accent-tone.md

```text
---
type: regex
pattern: '^\.accent\s*\{[^}]*var\(--color-accent-bg\)'
flags: m
target:
  source: file
  path: src/shared/components/StatusBadge/StatusBadge.module.css
---
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/02-badge-test-covers-new-tone.md
size: 133
sha: e81372875c0a2c583440ec11dd206923e59e4b75
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/02-badge-test-covers-new-tone.md

```text
---
type: regex
pattern: 'accent'
flags: m
target:
  source: file
  path: src/shared/components/StatusBadge/StatusBadge.test.tsx
---
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/03-local-badge-has-no-own-styles.md
size: 251
sha: a78aea5c3a39b440e5c59021d343ecf61b54b0c5
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/03-local-badge-has-no-own-styles.md

```text
---
type: llm
focus: trace
weight: 1
---

On hold renders through the shared StatusBadge's new tone; the feature's InvoiceStatusBadge no longer has its own stylesheet (a thin component that only maps an invoice status to a tone and label may remain).
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/04-every-stand-in-resolved.md
size: 372
sha: 13363fcb1517408f4a507cf2ec23cb30fce9221c
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/04-every-stand-in-resolved.md

```text
---
type: llm
focus: trace
weight: 1
---

Every component and styling stand-in listed in MOCK.md (the status badge, SelectField, the page widths) is resolved as one of: replaced by an existing component, an extension of shared code, or kept as genuinely new — and the response says which for each. The in-memory store may stay, because the prompt defers the API wiring.
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/05-tests-pass.md
size: 118
sha: d9de6270efa112ca61457e79c16595b697b10d3e
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/graders/05-tests-pass.md

```text
---
type: llm
focus: trace
weight: 1
---

The run ran the project tests (npm test) after its changes and they passed.
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/place-files.sh
size: 152
sha: 4da24617e868ea2379d1fd24f185bab8d988f0cb
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/place-files.sh

```text
#!/usr/bin/env bash
set -eu
root=$(cd "$(dirname "$0")/../../../.." && pwd)
bash "$root/examples/scaffold.sh" - "$root/examples/overlays/invoices-mock"
```

## File: skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/prompt.md
size: 324
sha: 64a03100df75920b69e8d4096f335ad4f06f31d1
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/extends-shared-badge-for-stand-in/prompt.md

```text
---
tags: [mockship, mock-to-ui]
max_turns: 80
timeout_seconds: 1800
allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
---

The Invoices mock is approved. The backend adds delete and the On hold status next sprint, so leave the API wiring for later — for now just clean up the local stand-ins listed in MOCK.md.
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/case.yaml
size: 441
sha: 8e879b6c5b3c164f464ed666908bd26411e9c4d1
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/case.yaml

```text
schema_version: "1.1"
name: promotes-approved-mock-end-to-end
expected_outcome: |
  The backend already has every operation the mock needs. The run completes the promotion:
  real data through the API client, access through useCan, the shared badge extended, copy in
  both locales, tests for the gate, access and states, mock files removed, flag and gate kept,
  and type-check and tests passing.
context:
  scaffold_script: place-files.sh
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/01-flag-still-off.md
size: 109
sha: 44cd165c2230c72a56b8191c33b57700e7dd2852
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/01-flag-still-off.md

```text
---
type: regex
pattern: '''invoices-beta'': false'
flags: m
target:
  source: file
  path: src/flags.ts
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/02-shared-badge-gains-accent-tone.md
size: 143
sha: e93c205126b7606cdbdd42912f03e7616aa6e588
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/02-shared-badge-gains-accent-tone.md

```text
---
type: regex
pattern: '^\.accent\s*\{'
flags: m
target:
  source: file
  path: src/shared/components/StatusBadge/StatusBadge.module.css
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/03-romanian-copy-added.md
size: 118
sha: 2c1b2247d54310f3d12ff0c3367a21c9cc634924
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/03-romanian-copy-added.md

```text
---
type: regex
pattern: '"[a-z]*\.?invoices?\.'
flags: m
target:
  source: file
  path: src/i18n/locales/ro.json
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/04-english-copy-added.md
size: 118
sha: 6741dc6f905a6379dd56639d0c0e3367f43ccecf
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/04-english-copy-added.md

```text
---
type: regex
pattern: '"[a-z]*\.?invoices?\.'
flags: m
target:
  source: file
  path: src/i18n/locales/en.json
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/05-tests-created.md
size: 70
sha: 864bb60dc6d6b770267f74355c7cd823d794bc27
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/05-tests-created.md

```text
---
type: file_exists
path: 'src/features/invoices/**/*.test.ts*'
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/06-mock-md-deleted.md
size: 98
sha: dff9afdd785551401810cdfd3a7fab5dd9853d43
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/06-mock-md-deleted.md

```text
---
type: tool_used
tool: Bash
input_match: '(?:\\n|\b)rm\s+[^;&|]*MOCK\.md'
min: 1
arm: both
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/07-real-data-and-access.md
size: 286
sha: 9dd71c3494cf93c7a9960de80a89297bc44397e1
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/07-real-data-and-access.md

```text
---
type: llm
focus:
  source: file
  path: src/features/invoices/InvoiceDetailPage/index.tsx
weight: 1
---

The detail page reads and writes through the API client via data-fetching hooks (not mock data), and shows Change status and Delete only when useCan('invoices:manage') is true.
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/08-typecheck-and-tests-pass.md
size: 119
sha: 96c59315bf7c0cbeebdd929d60700df0361d3cc1
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/08-typecheck-and-tests-pass.md

```text
---
type: llm
focus: trace
weight: 1
---

Near the end of the run, npm run typecheck and npm test both ran and passed.
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/09-mock-data-deleted.md
size: 98
sha: e0d25878f32af2ec6b1dd295f5f6f31e63a734ad
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/graders/09-mock-data-deleted.md

```text
---
type: tool_used
tool: Bash
input_match: '(?:\\n|\b)rm\s+[^;&|]*mockData'
min: 1
arm: both
---
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/place-files.sh
size: 189
sha: 5b40629c28204dc13cf0acd320d356537f071bb1
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/place-files.sh

```text
#!/usr/bin/env bash
set -eu
root=$(cd "$(dirname "$0")/../../../.." && pwd)
bash "$root/examples/scaffold.sh" "$root/examples/overlays/api-complete" "$root/examples/overlays/invoices-mock"
```

## File: skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/prompt.md
size: 406
sha: 2ff957bc57c4fc80d7547c320e044479ec8c874a
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/promotes-approved-mock-end-to-end/prompt.md

```text
---
tags: [mockship, mock-to-ui]
max_turns: 200
timeout_seconds: 3600
allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
---

The Invoices mock is approved — make it production-ready. I'm away until tomorrow: where the real API forces a visible difference from the mock, pick the option closest to the approved mock, record it under Decisions, and carry on to the end instead of waiting for me.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/case.yaml
size: 359
sha: 1cd59238cf0fb19df2f4c9258d4e627605fbf8b0
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/case.yaml

```text
schema_version: "1.1"
name: stops-on-missing-api-operations
expected_outcome: |
  The API client has no delete-invoice operation and no On hold status. The run maps the other reads and writes, reports both as gaps with a drafted contract each, and stops for the user's decision without working around the API layer.
context:
  scaffold_script: place-files.sh
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/01-the-response-names-the-missing-delete-in.md
size: 108
sha: 0b133d1beed91a3467f6d74b8b606368045dcfde
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/01-the-response-names-the-missing-delete-in.md

```text
---
type: llm
focus: trace
weight: 1
---

The response names the missing delete-invoice operation as a gap.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/02-the-response-names-the-missing-on-hold-s.md
size: 109
sha: 97576c8f84e41605a95ac5abf4ed7bdc9119e77b
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/02-the-response-names-the-missing-on-hold-s.md

```text
---
type: llm
focus: trace
weight: 1
---

The response names the missing On hold status in the API as a gap.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/03-for-each-gap-the-response-drafts-a-contr.md
size: 188
sha: 18f58d27bf3a25dc5af3f84f40210aa4f26a5b7b
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/03-for-each-gap-the-response-drafts-a-contr.md

```text
---
type: llm
focus: trace
weight: 1
---

For each gap the response drafts a contract: an operation with a suggested method and path (or enum change), and its request and response fields.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/04-src-api-client-ts-and-src-api-server-ts.md
size: 99
sha: a86cd0384294c82818c7733c3bc47451d74b600d
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/04-src-api-client-ts-and-src-api-server-ts.md

```text
---
type: llm
focus: trace
weight: 1
---

src/api/client.ts and src/api/server.ts are not changed.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/05-the-response-stops-and-asks-the-user-how.md
size: 144
sha: 99e2182b42253bbae4f40344bd9255fa634ddd21
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/05-the-response-stops-and-asks-the-user-how.md

```text
---
type: llm
focus: trace
weight: 1
---

The response stops and asks the user how to proceed with the gaps instead of continuing to wire data.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/06-the-invoices-beta-flag-in-src-flags-ts-i.md
size: 109
sha: 62fbf4d47933a7558e0d13bd01d1cd0a22ad6097
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/graders/06-the-invoices-beta-flag-in-src-flags-ts-i.md

```text
---
type: llm
focus: trace
weight: 1
---

The invoices-beta flag in src/flags.ts is still present and false.
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/place-files.sh
size: 152
sha: 4da24617e868ea2379d1fd24f185bab8d988f0cb
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/place-files.sh

```text
#!/usr/bin/env bash
set -eu
root=$(cd "$(dirname "$0")/../../../.." && pwd)
bash "$root/examples/scaffold.sh" - "$root/examples/overlays/invoices-mock"
```

## File: skills/mock-to-ui/evals/stops-on-missing-api-operations/prompt.md
size: 194
sha: 190274278ddceb31ba5bb8fcbb136ae75e35dc9f
upstream: https://github.com/marcellefter/mockship/blob/main/skills/mock-to-ui/evals/stops-on-missing-api-operations/prompt.md

```text
---
tags: [mockship, mock-to-ui]
max_turns: 80
timeout_seconds: 1800
allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
---

The Invoices mock is approved — make it production-ready.
```

## File: README.md
upstream: https://github.com/marcellefter/mockship/blob/main/README.md

````text
# mockship

![ui-to-mock builds a flag-gated clickable mock in your real app; after the team approves it, mock-to-ui ships it as production code with the same look and flow](assets/hero.png)

Two [Agent Skills](https://agentskills.io) for UI work in an existing app. The first builds a
quick mock inside the app so people can click through it and approve it. The second turns the
approved mock into production code.

- [`ui-to-mock`](skills/ui-to-mock/SKILL.md) builds a clickable mock of a screen or flow inside your existing app, using its real routes, layout and theme with fake in-memory data, behind a feature flag that defaults off. It writes no tests, translations or API calls, and leaves a `MOCK.md` hand-off.
- [`mock-to-ui`](skills/mock-to-ui/SKILL.md) takes an approved mock (a feature folder with a `MOCK.md`) and turns it into production code: real API operations, shared components, translations, loading, empty and error states, and tests. What the user approved stays the same.

They work with any frontend stack that has routes, a component library or theme, and ideally a
feature-flag system. The agent reads your project's own conventions (`AGENTS.md`, `CLAUDE.md`,
`CONTRIBUTING.md`, other installed skills) and follows them. The skills only add the rules
that make a mock a mock.

## Why

A prototype built in a separate design tool drifts away from the real app. A prototype built
straight in the app tends to turn into half-finished production code. mockship keeps the two
phases apart:

- The mock is cheap and contained. All its code lives in one feature folder. Shared code is
  read-only, and anything that nearly fits is copied into the folder and listed as a _stand-in_.
  The only edits outside the folder are the flag, the routes, and an entry point if you ask for
  one.
- The mock ships dark. A feature flag that defaults off gates every mock page, so you can merge
  a mock and demo it in any environment without users seeing it.
- The mock is the spec. `mock-to-ui` changes how the feature is built, not what a user sees.
  When a production constraint would change the UI, the agent stops and asks.
- Missing API operations stop the work. If the approved screens need an operation the backend
  does not have, `mock-to-ui` lists it and waits. It does not hand-write a request around your
  API layer.

## Install

### Claude Code (plugin marketplace)

```bash
claude plugin marketplace add marcellefter/mockship
```

```bash
claude plugin install mockship@mockship
```

Or, inside a session: `/plugin marketplace add marcellefter/mockship`, then `/plugin install
mockship@mockship`. The skills are then available as `/mockship:ui-to-mock` and
`/mockship:mock-to-ui`, and the agent also uses them on its own when a request matches.

### Any agent that supports Agent Skills (skills CLI)

```bash
npx skills add marcellefter/mockship
```

### Manually

Copy the folders under [`skills/`](skills) into your agent's skills directory. For Claude Code
that is `.claude/skills/` in a project, or `~/.claude/skills/` for every project. Some other
agents read `.agents/skills/`.

```bash
git clone https://github.com/marcellefter/mockship.git
```

```bash
cp -r mockship/skills/ui-to-mock mockship/skills/mock-to-ui .claude/skills/
```

## Usage

### 1. Build the mock

Ask for it in plain words, or hand over a design file or screenshot:

> Mock an Invoices list and an invoice detail page so product can click through it before the
> backend exists.

The agent then:

1. Works out the stack (framework, router, design system, flags, data layer, i18n, tests,
   permissions) and lists the screens, the flow between them, the states each one needs and
   who sees what. It asks about anything the request leaves open before building.
2. Adds the flag first, the same way your project declares other flags, defaulting to off. If
   your project has no flag mechanism, it offers a build-time env var, a development-only route
   or a development URL switch and lets you pick.
3. Builds the screens in one feature folder. Each page is a real route; tabs, modals, filters
   and form submits work; and writes change in-memory data seeded from a `mockData` module.
   Styling comes from your component library and theme tokens, and the layout matches the
   closest existing page in the app.
4. Makes every state reachable from a URL (`?scenario=empty`, `?scenario=error`,
   `?scenario=loading`, and `?role=viewer` when access differs), so reviewers sign off on
   those too.
5. Keeps `MOCK.md` up to date in the feature folder: status, flag, stack, screens and flow, a
   review link per screen and state, access, touchpoints, local stand-ins, assumed data,
   decisions and open questions.
6. Runs the project's type-check and two scripts. One checks that only the feature folder and
   the listed touchpoints changed. The other checks for literal colours, sizes and raw palette
   classes; token idioms such as `hsl(var(--x))`, breakpoints and 1px borders are allowed. It
   then reports the review links.

Changing an approved mock puts it back to draft. Abandoning a mock undoes every touchpoint it
listed. Several mocks can be in progress at once. With a file-based router (Next.js,
SvelteKit, Nuxt, Remix), the route files stay thin and import from the feature folder.

### 2. Review and sign off

The agent asks how reviewers will reach the mock (a preview deployment with the flag on, flag
targeting for named accounts, or a walkthrough) and records the answer. Feedback goes into the
mock and the _Decisions_ log. Only you can mark it approved, and you have to say who approved
it:

> Ana Popescu from product signed off on the Invoices mock today.

The agent records that as `Status: approved by Ana Popescu on 2026-10-27`.

### 3. Ship it

> Ana approved the Invoices mock. Make it production-ready.

The agent works through ten steps, each with its own finish line:

1. Confirm the sign-off and list every file in the feature folder.
2. Screenshot every review link as a baseline.
3. Map every read and write to a real API operation.
4. Map _Access_ onto your permission checks.
5. Resolve each stand-in against your shared components.
6. Restructure the code to your conventions.
7. Swap the in-memory data for your data layer.
8. Move the copy into i18n.
9. Write and run tests.
10. Compare screenshots against the baseline and hand off.

It keeps a _Promotion_ record in `MOCK.md`, so a later session can pick up where this one
stopped. When an API operation or permission is missing, it stops and gives you a drafted
contract for the API owner. At the end `MOCK.md`, `mockData` and the review parameters are
deleted. The flag and gate stay, so turning the feature on remains the product owner's call.

## Repository layout

```
mockship/
├── .claude-plugin/marketplace.json   # Claude Code marketplace + plugin definition
├── skills/
│   ├── ui-to-mock/
│   │   ├── SKILL.md
│   │   ├── assets/MOCK.template.md
│   │   ├── references/               # stack discovery, flag options and gates per framework
│   │   ├── scripts/                  # check-boundary.sh, check-tokens.sh
│   │   └── evals/                    # test cases (claude plugin eval format)
│   └── mock-to-ui/
│       ├── SKILL.md
│       └── evals/
├── examples/
│   ├── sample-app/                   # React + Vite app the evals run against
│   ├── overlays/                     # layers on the sample app: no flags, a finished mock, draft status
│   ├── scaffold.sh                   # sets up the sample app for an eval run
│   ├── test-scripts.sh               # tests for the check scripts
│   └── test-fixtures.sh              # sets up every eval case and checks it
├── assets/                           # README image and its source
├── .github/workflows/ci.yml
├── CHANGELOG.md
└── LICENSE
```

Each skill follows the [Agent Skills specification](https://agentskills.io/specification): a
folder whose `SKILL.md` has `name` and `description` frontmatter, with longer material in
`r
````
