Wire the self-improving UI harness into this Compose Multiplatform project — scaffold the harness/ ledger + Python tools, install Tier1Assertions + the Roborazzi screenshot-testing convention plugin + an inspection-test template (package-parameterized), and print the build wiring. Short interview fills project slots (base package, target module). Trigger phrases: "set up the ui harness", "install ui-harness", "wire the compose ui harness", "scaffold the screenshot harness".
Scanned 9/6/2026
Install to Claude Code
npx -y skills add ShipWithAI/shipwithai-plugins --skill setup --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Setup?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/shipwithai-setup-shipwithai-plugins)More formats (shields.io, HTML) on the badges page.
---
name: setup
description: >
Wire the self-improving UI harness into this Compose Multiplatform project — scaffold the
harness/ ledger + Python tools, install Tier1Assertions + the Roborazzi screenshot-testing
convention plugin + an inspection-test template (package-parameterized), and print the build
wiring. Short interview fills project slots (base package, target module).
Trigger phrases: "set up the ui harness", "install ui-harness", "wire the compose ui harness",
"scaffold the screenshot harness".
argument-hint: "[<base.package>] [--module path/to/feature/impl]"
---
# UI Harness — Setup
Installs the **per-repo half** of the harness so it is committed and shared with the team: the
ledger, the Python tools beside it, and the Kotlin substrate. The agentic half (the `mobile-design`
skill, the `mobile-design-evaluator` agent, and `/ui-iterate` · `/ui-distill` · `/ui-metrics`)
ships with the plugin and is already available.
Bundled sources live under `${CLAUDE_PLUGIN_ROOT}/assets/`.
## 0. Prerequisite probe + interview
- Confirm this is **Compose Multiplatform + Roborazzi + Robolectric** (the substrate's
assumptions). If not, stop and point at `${CLAUDE_PLUGIN_ROOT}/assets/INTEGRATION.md`.
- **Base package** = `$1`, else infer it from an existing `package` declaration and confirm.
- **Target module** = the `--module` value, else `Glob` for a feature `impl` module and ask which
one. Derive its `androidHostTest` source root.
## 1. Lay down the ledger + tools (verbatim)
```bash
mkdir -p harness/bin harness/ledger
cp "${CLAUDE_PLUGIN_ROOT}/assets/harness/bin/"*.py harness/bin/
cp "${CLAUDE_PLUGIN_ROOT}/assets/harness/ledger/README.md" harness/ledger/README.md
cp "${CLAUDE_PLUGIN_ROOT}/assets/harness/ledger/screen-aliases.json" harness/ledger/screen-aliases.json
: > harness/ledger/findings.jsonl # committed, append-only; starts empty
```
Confirm the tools run here: `python3 harness/bin/test_aggregate_ledger.py` (and the two sibling
`test_*.py`) should all pass.
## 2. Install the deterministic asserter
Copy `Tier1Assertions.kt.tmpl` into the target module's `androidHostTest` `testing` package,
replacing the token with the real package. **Substitute the §0 interview values into the shell
vars below and validate them first** — never splice an unvalidated package/path into the shell,
and don't feed the package to `sed` (a `/` in it breaks the `s///` delimiter):
```bash
BASE_PKG="<base.package>" # from §0, e.g. com.acme.app
MODULE="<module>" # from §0, e.g. feature/foo/impl
MODULE_SUFFIX="<module-suffix>" # package tail under BASE_PKG, e.g. feature.foo.impl
# reject anything that isn't a plain Java package / module path
case "$BASE_PKG" in *[!A-Za-z0-9._]*|"") echo "invalid base package"; exit 1;; esac
case "$MODULE_SUFFIX" in *[!A-Za-z0-9._]*|"") echo "invalid module suffix"; exit 1;; esac
case "$MODULE" in *[!A-Za-z0-9._/-]*|/*|*..*) echo "invalid module path"; exit 1;; esac
TESTING_PKG="${BASE_PKG}.${MODULE_SUFFIX}.testing"
DEST="$MODULE/src/androidHostTest/kotlin/${TESTING_PKG//.//}" # pure-shell '.'→'/', no sed
mkdir -p "$DEST"
# literal token replacement — TESTING_PKG never enters a regex
TESTING_PKG="$TESTING_PKG" python3 - \
"${CLAUDE_PLUGIN_ROOT}/assets/substrate/Tier1Assertions.kt.tmpl" "$DEST/Tier1Assertions.kt" <<'PY'
import os, re, sys
src, dst = sys.argv[1], sys.argv[2]
text = open(src, encoding="utf-8").read()
open(dst, "w", encoding="utf-8").write(re.sub(r"__TESTING_PACKAGE__.*", os.environ["TESTING_PKG"], text))
PY
```
## 3. Install the convention plugin
Copy `ComposeScreenshotTestingConventionPlugin.kt.tmpl` into `build-logic` (replacing
`__BUILDLOGIC_PACKAGE__`), then register it and apply `id("<prefix>.compose-screenshot-testing")`
in each module that renders screens. See `${CLAUDE_PLUGIN_ROOT}/assets/INTEGRATION.md` for the exact
`build-logic` registration block and the version-catalog aliases it expects.
## 4. Seed one inspection test
Copy `${CLAUDE_PLUGIN_ROOT}/assets/substrate/ScreenInspection.kt.tmpl` next to `Tier1Assertions`,
replace `__PACKAGE__` / `__TESTING_PACKAGE__` / `__SCREEN__` / `__screen__` (the lowercase
single-token screen id), and wire the `TODO`s to render your stateless screen content. Add a
`"<ComposableName>": "<screen-id>"` row to `harness/ledger/screen-aliases.json`.
## 5. Wire the build + gitignore
Follow `${CLAUDE_PLUGIN_ROOT}/assets/INTEGRATION.md` §"Build wiring" and §"gitignore": add the
Roborazzi plugin + version-catalog aliases, register the convention plugin, and gitignore
`harness/ledger/promotions.md`, `harness/ledger/metrics.md`, and the
`build/outputs/roborazzi/_inspect_*.png` / `*_findings.jsonl` sidecars (keep
`harness/ledger/findings.jsonl` **committed**).
## 6. Smoke-test the loop
Run `/ui-iterate <YourScreen>` once. It should render the matrix, run Tier-1, dispatch the
evaluator in a fresh lane, and fold findings into `harness/ledger/findings.jsonl`. If it produces a
`_verdict.md`, the harness is live.
> The six coupling points (package, module coords, ledger root, sidecar glob, catalog aliases,
> screen-alias map) are all touched above. Everything else is project-agnostic.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!