name: clone-app
description: Given a target Android app — an APK path, a running emulator serial, or an installed package id — scope every screen and build a high-fidelity cloned RN/Expo app forked from app-template, filled with original mock content. Runs a 6-stage Acquire→Scope→Scaffold→Build→Parity→Deliver pipeline with 3 hard gates. Triggers: "clone app", "clone this APK", "clone for me", "high-fidelity RN clone", "make a clone of this emulator app", /clone-app.
argument-hint: " [--name ] [--keep-branding] [--base <path=app-template>]"
level: 4
clone-app turns a target Android app into a high-fidelity, functional **RN/Expo clone** forked from `app-template`, filled with **original mock content**. When invoked it runs a 6-stage pipeline — **Acquire → Scope → Scaffold → Build → Parity → Deliver** — that decompiles + live-drives the target, maps every screen, forks the template into a new monorepo app dir, prunes the modules the target doesn't need, rewrites the always-on core screens, builds every scoped screen via a foundation-contract + parallel per-screen Workflow, iterates on-device until a reviewed visual-parity pass, and opens a PR with durable artifacts. It is **autonomous within a loop budget**, hard-stopping at 3 gates and at every outbound/irreversible action.
This skill produces app code (a new clone app). It does not reproduce the target's copyrighted assets or text — only its structure and UX, re-skinned with original branding and mock content.
<Use_When>
- User wants a working clone of a mobile app they can point you at as an APK, a running emulator, or a package id — "clone this app", "clone this APK", "build me a high-fidelity clone of ".
- User wants the clone built on
app-template as a new app directory in the monorepo.
- User wants the clone to look and navigate like the target but with original (non-infringing) content.
</Use_When>
<Do_Not_Use_When>
- User wants to reproduce the target's actual content, audio, imagery, or wordmark (this skill refuses — original stand-ins only;
--keep-branding is the single, flagged, personal-study exception).
- User wants a real backend / auth / payments clone (this skill ships mock data only).
- User wants pixel-perfect numeric parity as a gate (parity here is a human-reviewed pass, not a metric).
- User wants to clone non-RN native code as native code (the clone is always RN/Expo on
app-template).
- The target is not an Android app reachable as an APK, an emulator serial, or an installed package id.
</Do_Not_Use_When>
<Why_This_Exists>
Cloning an app by hand means manually walking every screen, re-deriving the theme, rebuilding navigation, and pruning a skeleton you forked — slow, error-prone, and easy to (a) leak the target's copyrighted content, (b) accidentally dirty the shared base/sibling apps, or (c) ship a half-de-skinned clone that still boots into the template's original (Calm) screens. This skill encodes the proven Calm-clone run as a repeatable pipeline whose guardrails are structural (enforced by mechanism, not by a prose "remember to…"): a locked read-only reference emulator serial, git status --porcelain 0-diff checks on the base+siblings at three points, a zero-orphan scan that catches dead screens typecheck cannot, an edit-only-your-own-file fan-out, and a brand-gate scan for IP.
</Why_This_Exists>
<Golden_Rules>
Read these before any action. They are structural invariants — each is enforced by a named mechanism later in this file, not by memory.
- Original content only (IP boundary). Clone structure & UX only. GENERATE original titles, narration text, audio, imagery, narrator/persona names. NEVER copy the target's assets or copy. Default to original branding/wordmark; keep the target's wordmark ONLY if the user passed
--keep-branding, and then flag the clone as personal-study-only. Enforced by: per-agent "generate original" instruction in every screen prompt + the brand-gate scan (scripts/brand-gate-scan.mjs in the forked app) run before Deliver.
- The base + sibling apps end at 0 git changes.
app-template and every other monorepo app must be byte-identical to HEAD when you finish. Enforced by: git worktree isolation (all work happens in the worktree) + a git -C <repo> status --porcelain -- app-template <sibling-apps> check run at 3 points (after Scaffold, after Build, before PR). Nonzero ⇒ STOP, revert, investigate (the monorepo cwd-at-root gotcha makes contamination easy — see Gotchas).
- No outbound without approval. Never
git push, open a PR, merge, or deploy (EAS/Vercel/Convex/expo prebuild) without explicit user approval. Enforced by: Gate 3 wraps every outbound/irreversible action.
- Worktree isolation. All edits happen inside a git worktree for the clone branch. NEVER edit
app-template in place. Enforced by: Stage 3 creates/enters the worktree before any edit; every spawned agent gets absolute worktree paths and cds to the app root for shell commands.
- Mock data only. No real auth/payments/services. Module code imports only
../../core/* seams (useData, useAuth). Enforced by: the per-screen HARD RULES block (no native deps, no backend SDKs) + explicitly striking new-app.sh step 4 ("implement DataClient / swap AuthProvider").
- The reference emulator serial is locked and read-only. Recorded at Stage 1; the parity loop reads it and hard-fails if asked to drive or screencap-for-edit it. Enforced by: a serial parameter on every device action that throws on the reference serial — a mechanism, not a prose "NEVER drive the reference emulator" (the source run flipped 5554↔5556 across its own artifacts).
</Golden_Rules>
```
/clone-app [--name ] [--keep-branding] [--base ]
```
<target> (required) — one of:
- an APK path (e.g.
~/Downloads/target.apk) → static branch + optional install on a fresh emulator;
- an emulator serial (e.g.
emulator-5554) → attach live; recover the package id;
- an installed package id (e.g.
com.calm.android) → find the running/installed instance.
--name <clone> — the clone's display name (drives the new app dir, bundle id, scheme). If omitted, propose one at Preflight and confirm at Gate 1.
--keep-branding — keep the target's wordmark/branding (personal-study only; flagged in artifacts and the PR). Default = original branding.
--base <path> — the template to fork. Default = <path-to-your-app-template>.
Resolve <target>:
- APK path:
apktool d / jadx for the static branch; adb install on a fresh emulator for the dynamic branch.
- Serial:
adb -s <serial> shell pm path <guess> / adb -s <serial> shell dumpsys window | grep mCurrentFocus to recover the foreground package id.
- Package id: locate the installed/running instance on an available emulator.
Before Stage 1:
1. **Locate the base** — resolve `--base` (default `app-template`); confirm it exists and is clean (`git -C status --porcelain -- ` empty).
2. **Pick clone identity** — display name (`--name` or proposed), bundle id (`com..`), scheme, and the new monorepo app dir path (sibling of the other apps). Defer final confirm to Gate 1.
3. **Pick non-colliding device + Metro config** — choose distinct emulator serials for **reference vs clone** and distinct Metro ports; record the `adb` path.
4. **Record-and-LOCK the reference serial** — write the reference emulator serial to `RESUME.md` and `_-build/reference_serial.txt`. This value is **read-only** for the whole run; the parity loop consumes it to refuse driving the reference device.
5. **Worktree base = local HEAD** — commit any base state you need carried into the worktree *before* creating it (uncommitted work does not transfer). If a worktree is missing per-app `node_modules`, symlink it from the main checkout before trusting any `tsc`/`lint` gate (monorepo gotcha).
Write the initial RESUME.md + create the _<name>-build/ sibling dir now so the run survives a fresh session.
<Stage_1_Acquire>
Entry: target resolved at Preflight. Goal: confirm the package id + reference serial, and mine static hints.
- Lock the reference serial (if not already): write it to
RESUME.md + _<name>-build/reference_serial.txt. Read-only henceforth.
- Static branch (1 background agent,
opus): apktool d / jadx the APK → mine res/values/{strings,colors,styles}, drawable/asset names, layout-xml hints, launcher label/package. Write a target-static.md digest. These are hints only — never content to copy (Golden Rule 1).
- Dynamic branch (the QA/driver session): confirm the app launches on the emulator; capture the launcher + first screen.
- Orchestration: static decompile = 1 background agent (
opus); dynamic = the QA/driver session — run in parallel; converge on a confirmed package id + asset/string inventory + the locked reference serial.
Artifacts: target-static.md, reference_serial.txt, launcher + first-screen captures.
Exit: package id confirmed, reference serial locked. (No gate — flows into Scope.)
</Stage_1_Acquire>
<Stage_2_Scope>
Entry: package id + locked reference serial. Goal: an exhaustive screen map + reference screenshots → Gate 1.
- Walk (QA-tester driver): drive every reachable screen/flow on the locked reference serial; screencap each with
adb -s <ref-serial> exec-out screencap -p > <file>.png. Build a screen map in the proven format (a navigation graph — onboarding chain, tab bar, modals — plus a per-screen entry with screenshot filename + element inventory).
- Gates inside scope (best-effort + flag): on a login/paywall/premium gate → STOP and ask the user to unlock on the emulator if they want that area covered; otherwise proceed and explicitly flag the screen unverified/gated. Use the static decompile to recover gated structure where useful.
- Re-scope trigger (named re-entry point): if a gated/premium area is discovered or unlocked after Gate 1 (as in the Calm run, where the user signed into premium mid-build), re-enter Scope for just that area, then re-run the foundation-contract delta + the affected per-screen build + parity for those screens. This is an explicit named re-entry, not an ad-hoc restart.
- Orchestration: explore/QA driver session on the reference serial.
Artifacts: _<name>-build/<name>_screen_map.md + _<name>-build/<name>_screens/NNN_*.png.
Exit → Gate 1: present scope + the module KEEP/PRUNE proposal + the core-rewrite set; STOP for confirmation.
</Stage_2_Scope>
<Stage_3_Scaffold>
Entry: Gate 1 confirmed (scope + module selection + core-rewrite set). Goal: a forked, pruned, rebranded, worktree-isolated clone that is typecheck-green AND zero-orphan-clean.
- Fork:
app-template/new-app.sh "<Display Name>" <bundle-id> <dest> where <dest> = the new monorepo app dir. (new-app.sh rsyncs — excluding node_modules/.expo/dist/.git — and rewrites identity in app.json: name/slug/scheme/ios.bundleIdentifier/android.package + package.json name.)
- Follow the script's "Next" checklist steps 1–3 (Install → Run → Trim/delete unwanted modules).
- EXPLICITLY STRIKE / OVERRIDE step 4 ("implement DataClient (
src/core/data) and swap AuthProvider (src/core/auth) for your backend") — it conflicts with the mock-data-only guardrail. The clone keeps the local mock seams.
- Worktree isolation: create/enter a git worktree for the clone branch (
EnterWorktree, or git worktree add), and do ALL subsequent work there. Never edit app-template in place. Symlink per-app node_modules if missing.
- Two-mode module prune (apply the Gate-1 table — see Module_Map_Reference):
- Self-contained module:
rm -rf src/modules/<id> + delete its import { <x>Module } from './<id>/manifest' line and its <x>Module entry in the MODULES array of src/modules/index.ts.
- Core-backed module: the above PLUS delete/replace the named
src/core/screens/... files it owned PLUS remove any now-orphaned sibling component (e.g. drop SleepTimerSheet/CreatePlaylistSheet from src/core/components/calm-app/index.ts if nothing else imports it).
- Acid test:
pnpm typecheck exits 0 AND the zero-orphan scan reports no unreferenced files under src/core/screens/**. typecheck-green ALONE is INSUFFICIENT — TypeScript does not flag a valid-but-unreachable file, so for core-backed modules it certifies a clone that still ships the dead Calm screens (see Module_Map_Reference for the scan + the dependency note).
- Plan core-screen rewrites: record which always-on core screens (
OnboardingFlow, HomeScreen, ProfileScreen, SettingsScreen, PlayerScreen) are in scope for rewrite for this target — they are real Calm screens, not stubs, and a clone that prunes modules but keeps core still boots into Calm Home/Player/onboarding unless these are rewritten. Net-new feature areas are stubbed as new module folders here (filled in Build).
- Rebrand: retune
src/core/theme/tokens.ts (colors/spacing/type) toward the scoped palette; swap assets/ (icon/splash) for originals. NOTE: app-template also carries src/core/theme/calmTokens.ts — Calm-specific tokens consumed by the core Calm screens; rebrand must cover whichever token module the in-scope core screens actually import.
- 0-diff check (point 1 of 3): run
git -C <repo> status --porcelain -- app-template <sibling-apps>; must be empty. Then commit the scaffold on the worktree branch.
- Orchestration: mostly direct (deterministic script + deletes) + a verifier pass (judgment tier) for the 0-diff + typecheck-green + zero-orphan acid test.
Artifacts: the forked app dir; the scaffold commit; the pruned MODULES.
Exit: acid test passes (typecheck-green AND zero-orphan-clean), base/siblings 0-diff. (No gate — flows into Build.)
</Stage_3_Scaffold>
<Stage_4_Build>
Entry: clean scaffold. Goal: every scoped screen (module and in-scope core) built, wired, tsc+lint green.
- Foundation contract (1 judgment-tier agent — inherits session model): from the scoped design, derive and WRITE
FOUNDATION-CONTRACT.md (mirror the shape of the Calm _calm-build/FOUNDATION-CONTRACT.md, adapted to the module layout):
- exact theme tokens; content model + ~N original mock catalog items; navigation param lists + a
useOpenContent-style router; shared-components inventory;
- the explicit list of in-scope core-screen rewrites (
HomeScreen/PlayerScreen/ProfileScreen/SettingsScreen/OnboardingFlow);
- the list of module/net-new screens each per-screen agent will build.
State each screen's true starting state (core screens = fully-built Calm screens to rewrite; module screens = scaffolds). Do NOT call core screens "stubs."
This freezes the contract the fan-out codes against. It must be committed before the parallel Workflow launches (strict ordering — no foundation/fan-out race).
- Per-screen parallel Workflow (mechanical seats — may pin
opus): one agent per scoped screen — covering BOTH src/modules/<id>/screens/ screens AND the in-scope src/core/screens/calm-app/ core screens — each editing only its own screen file. Embed the HARD RULES block below.
- Integrate pass (1 agent): the cross-cutting wiring the per-screen agents were forbidden to touch — for
app-template this means src/core/navigation/RootNavigator.tsx (routes, mini-player mount, onboarding gate) and, only if net-new modules were added, src/modules/index.ts + new manifest.tsx. Then drive tsc+lint green.
- Verify:
pnpm typecheck && pnpm lint (respect the per-app --max-warnings ceiling — don't chase zero) + the 0-diff base/siblings check (point 2 of 3) + the zero-orphan scan. Commit.
- Orchestration shape:
pipeline( foundation-contract → parallel(module_screens + core_rewrites) → integrate → verify ).
Per-screen HARD RULES block (paste into every per-screen + fix agent prompt — written for app-template's real wiring, NOT lifted from the Calm flat spec):
HARD RULES — edit ONLY your own screen file.
- You own EXACTLY: <list the agent's screen file(s)> under
src/core/screens/<area>/ or src/modules/<id>/screens/ . Edit nothing else.
- NEVER touch any of these (they are owned by the integrate pass / are shells):
src/core/navigation/RootNavigator.tsx (routing — integrate pass only)
src/modules/index.ts (MODULES registry — integrate pass only)
any src/modules/<id>/manifest.tsx (module wiring)
src/core/theme/tokens.ts (theme — frozen by the foundation contract)
App.tsx (provider shell — "edit MODULES, not this file")
- cd to the CLONE APP ROOT for every shell command; use ABSOLUTE worktree paths
(spawned agents run with cwd = monorepo root, NOT the worktree — relative paths
contaminate base/siblings).
- Installed libraries ONLY. NO new native deps. If a screen needs one, REPORT it
(do not add it) — a native dep forces a prebuild and breaks the Expo Go run.
- If a route/param is missing, REPORT it — do NOT add it yourself.
- Generate ORIGINAL content (titles/text/imagery placeholders) — never copy the target's.
- After editing, the gate must stay green: `npx tsc --noEmit; echo EXIT=$?` → EXIT=0.
Artifacts: FOUNDATION-CONTRACT.md (+ a build spec mirroring _calm-build/PHASE2-BUILD-SPEC.md); per-screen commits.
Exit: tsc+lint green, zero-orphan clean, base/siblings 0-diff. (No gate — flows into Parity.)
</Stage_4_Build>
<Stage_5_Parity>
Entry: green build. Goal: a reviewed on-device visual parity pass → Gate 2.
- Run on-device: start Metro in tmux on the clone's port;
adb -s <clone-serial> reverse tcp:<port> tcp:<port>; launch exp://… on the clone's serial (Expo Go). Read the locked reference serial (Stage 1) and assert <clone-serial> != <reference-serial>; refuse to start if they match.
- Diff: screencap each built screen on the clone serial (
adb -s <clone-serial> exec-out screencap -p), compare against its _<name>-build/<name>_screens/ reference; record a per-screen punch list.
- Context hygiene (HARD RULE): captures are taken and judged INSIDE subagents (
visual-verdict seats) — screenshots are NEVER pasted into the main session thread; only per-screen verdicts + the punch list come back. Coordinate remaps (the "multiply by 1.11" dance) are computed inside the capture script, not narrated. (One real clone run inlined dozens of captures → a 415MB transcript that force-compacted 4×, each compaction costing a hand-written resume.)
- Reference-serial guard (mechanism, not prose): every screencap/launch step takes the serial as a parameter and hard-fails if handed the reference serial. Do NOT rely on a prose "never drive the reference emulator" — the source run flipped 5554↔5556 across its own artifacts.
- Fix agents: dispatch one fix agent per failing screen (same edit-only-your-file HARD RULES); re-run; re-capture into
_<name>-build/loopN/.
- Gotchas (required handling):
- Stale Metro bundle → press
r in Metro / restart with --clear before trusting a capture.
- Host overload (high load avg, emulator goes
offline) → host-contention backpressure: defer/retry when load drops (named sub-procedure); don't fight a wedged emulator.
- Shared-emulator discipline → distinct serial + port; bring the app to the foreground before screencap; never drive the reference emulator.
- Loop budget: cap iterations (the Calm run took ~5 visual loops + a premium re-scope cycle). On hitting the budget without a clean pass, surface "remaining nits (cosmetic, not blockers)" to the user at Gate 2 rather than looping unbounded.
- Orchestration: a
/loop driven by the main session with parallel per-screen fix agents per iteration (judgment-tier parity review; mechanical-tier fix agents).
Artifacts: _<name>-build/loopN/ captures + per-loop punch lists.
Exit → Gate 2: present captures + punch-list status; a reviewed parity pass (judgment, NOT a pixel number). Loop until pass or budget, then STOP for the checkpoint.
</Stage_5_Parity>
<Stage_6_Deliver>
Entry: Gate 2 passed. Goal: green build + reviewed parity + PR + artifacts.
- Run the Done-bar checklist (see Verification_Checklist) FIRST — includes zero-orphan scan clean + all in-scope core screens rewritten + base/siblings 0-diff.
- Finalize artifacts: update
RESUME.md; ensure screen map / foundation contract / build spec / loop captures / locked reference serial are all in _<name>-build/.
- 0-diff check (point 3 of 3):
git -C <repo> status --porcelain -- app-template <sibling-apps> empty.
- Brand-gate scan: run
scripts/brand-gate-scan.mjs in the forked app; resolve any flagged copies of the target's branding/text (unless --keep-branding, in which case flag the PR as personal-study-only).
- Outbound = Gate 3: STOP and ask before
git push + opening the PR to main. NEVER merge/deploy without a second explicit approval. Use the repo PR workflow — never push to main directly (it auto-deploys cut-over Vercel apps).
Artifacts: PR to monorepo main; final RESUME.md + _<name>-build/ durable set.
Exit: PR opened (after Gate 3 approval), artifacts written.
</Stage_6_Deliver>
Three gate types. Each is an explicit **STOP-and-ask** — do not proceed past one without the user's word.
Gate 1 — Scope + module-selection + core-rewrite confirm (end of Stage 2, before any deletion).
STOP. "Here is the screen map (N screens), the proposed module KEEP/PRUNE table, and the core-screen rewrite set (Home/Player/Profile/Settings/Onboarding as applicable). Confirm before I fork and delete anything." Nothing is forked, pruned, or rewritten until the user confirms.
Gate 2 — Parity-review checkpoint (end of Stage 5).
STOP. "Here are the on-device captures vs references and the remaining punch list. Is this parity acceptable, or should I keep iterating?" A reviewed (human-judged) pass — not a pixel metric.
Gate 3 — Every outbound / irreversible action (Stage 6 and anywhere outbound).
STOP before ANY of: PR open · merge · git push · eas (build/update) · convex deploy · expo prebuild / expo run · anything touching money or secrets. Each requires explicit approval. Merge/deploy require a separate approval from PR-open.
<Module_Map_Reference>
The default mapping the skill ships: target feature-signal → keep module, each classified by prune mode. Anything the target shows that has no row ⇒ net-new module/screen.
app-template module | Prune mode | Owns (must also remove when pruned) | Keep when the target has … |
|---|
art | self-contained | src/modules/art/screens/ArtScreen | generative art / visual creative surface |
calls | self-contained | src/modules/calls/screens/CallsScreen | voice/video calls or call history |
care | self-contained | src/modules/care/screens/CareScreen | caregiving / B2B care / patient mgmt |
family | self-contained | src/modules/family/screens/FamilyScreen | family plan / multi-member / shared accounts |
health | self-contained | src/modules/health/screens/HealthScreen | health metrics / HealthKit-style dashboards |
journal | self-contained | src/modules/journal/screens/JournalScreen | journaling / reflections / notes |
medicine | self-contained | src/modules/medicine/screens/MedicineScreen | medication reminders / health regimen |
mood | self-contained | src/modules/mood/screens/MoodScreen | mood check-in / feeling tracker |
persona | self-contained | src/modules/persona/screens/PersonaScreen | AI persona / companion / character chat |
breathe | core-backed | src/core/screens/BreatheScreen | breathing exercises / breath timers |
meditation | core-backed | src/core/screens/MeditationScreen | guided meditation / sessions / courses |
sleep | core-backed | src/core/screens/calm-app/SleepScreen + SleepTimerSheet (src/core/components/calm-app) | sleep content / sleep tab / bedtime stories |
paywall | core-backed | src/core/screens/calm-app/{PremiumScreen,ContentPaywallModal} | premium/subscription/upgrade gating |
browse | core-backed | src/core/screens/calm-app/{DiscoverScreen,AlbumDetailScreen,CollectionScreen,DailyArchiveScreen,SeeAllScreen,CategoryScreen,MusicScreen,SoundscapesScreen} + CreatePlaylistSheet | a content/discover/explore browse feed |
Verified 2026-06-28 by reading every src/modules/<id>/manifest.tsx and src/modules/index.ts (14 modules registered): the 9 self-contained modules import their screen from ./screens/; the 5 core-backed modules import their screen(s) from ../../core/screens/.... rm -rf src/modules/<id> removes only the manifest wrapper for the 5 core-backed ones, leaving the core screen(s) orphaned.
Always-on core surface (NOT modules — ALWAYS present, MUST be rewritten for a non-Calm target):
OnboardingFlow, HomeScreen, ProfileScreen, SettingsScreen, PlayerScreen — all in src/core/screens/calm-app/, imported directly by src/core/navigation/RootNavigator.tsx:26-30. These are full Calm implementations (e.g. PlayerScreen.tsx is 750+ lines), independent of MODULES. A clone that keeps core and prunes modules still boots into a Calm Home/Player/onboarding unless these are rewritten — so they are explicit Stage-4 build targets, enumerated in the foundation contract.
Two-mode prune mechanic (exact):
- Self-contained:
rm -rf src/modules/<id> + delete its import { <x>Module } from './<id>/manifest' line and its <x>Module entry in the MODULES array of src/modules/index.ts.
- Core-backed: the above PLUS
rm/replace the src/core/screens/... files in the "Owns" column PLUS remove any now-orphaned sibling component (e.g. drop the SleepTimerSheet/CreatePlaylistSheet export from src/core/components/calm-app/index.ts if nothing else imports it).
Acid test (exact) — typecheck-green ALONE is INSUFFICIENT:
pnpm typecheck exits 0 AND a zero-orphan scan reports no unreferenced files under src/core/screens/**. TypeScript does not flag a valid-but-unreachable file, so for core-backed modules typecheck-green certifies a clone that still contains the dead Calm screens — the exact "ships dead features" failure this skill forbids. Tooling (pick one; NOTE: both are NEW dependencies — neither is in app-template/package.json today, so call out the install before running the acid test):
- preferred:
npx knip (reports unused files/exports), or
- lighter:
npx ts-prune + a grep that every file under src/core/screens/calm-app/ is reachable from RootNavigator.tsx or a surviving manifest.tsx.
Net-new mechanic (exact): create src/modules/<newid>/ with manifest.tsx exporting a ModuleManifest ({ id, title, tab? | entry?, screens? }), screen files under screens/, and register it in MODULES. A module imports only from ../../core/* (seams: useData, useAuth) — never another module, never a backend SDK. Primary features get a tab; secondary surface via an entry (a param-free hub row in "More").
Decision authority: the proposed keep/prune/net-new table and the core-screen rewrite set are emitted to the user at Gate 1 and are not acted on until confirmed.
</Module_Map_Reference>
<Guardrails_As_Mechanisms>
Each guardrail and the mechanism that enforces it (prose alone is not a guardrail):
- Original content / IP → per-agent "GENERATE original" instruction in every screen prompt + original mock catalog in the foundation contract +
scripts/brand-gate-scan.mjs run before Deliver. --keep-branding is opt-in and flagged personal-study-only.
- Base + siblings 0 git changes → git worktree isolation +
git -C <repo> status --porcelain -- app-template <sibling-apps> at 3 points (after Scaffold, after Build, before PR). Nonzero ⇒ STOP + revert. (The monorepo cwd-at-root gotcha makes this a real, not theoretical, risk.)
- No outbound without approval → Gate 3 wraps PR/push/merge/eas/convex/expo-prebuild/money/secrets.
- Worktree isolation → Stage 3 enters the worktree before any edit; every agent gets absolute worktree paths +
cds to the app root.
- No new native deps → per-screen HARD RULES ("installed libs only; if needed, report — don't add").
- Mock data only → module imports stay
../../core/*; new-app.sh step 4 explicitly struck.
- Reference serial locked & read-only → recorded at Stage 1; a serial parameter on every device action hard-fails on the reference serial.
- No dead screens (zero-orphan) → the acid test = typecheck-green AND zero-orphan scan; typecheck-green alone declared insufficient.
</Guardrails_As_Mechanisms>
<Artifacts_And_Resume>
Write/maintain a RESUME.md (mirror the Calm worktree RESUME) with: Goal · Hard constraints · Architecture · target reference (what to match) · locked reference serial + clone serial + ports · Progress (per-stage commits) · How-to-run recipe · Resume pointer. Keep durable specs/screens in a _<name>-build/ sibling of the app dir so a fresh session can resume.
Durable artifact set the run must leave:
RESUME.md (app dir)
_<name>-build/<name>_screen_map.md + _<name>-build/<name>_screens/NNN_*.png (reference shots)
_<name>-build/reference_serial.txt (the locked reference serial)
_<name>-build/target-static.md (decompile digest)
_<name>-build/FOUNDATION-CONTRACT.md + the build spec
_<name>-build/loopN/ (per-loop parity captures + punch lists)
</Artifacts_And_Resume>
<Gotchas_And_Runbook>
Proven recipes + gotchas from the Calm run.
On-device run/verify recipe (clone, Expo Go):
# distinct port + serial for the CLONE — never the reference serial
npx expo start --port <clone-port> # in tmux; press r to reload, --clear to bust a stale bundle
adb -s <clone-serial> reverse tcp:<clone-port> tcp:<clone-port>
adb -s <clone-serial> shell am start -a android.intent.action.VIEW -d "exp://127.0.0.1:<clone-port>"
# capture (clone only — screencap/launch HARD-FAILS on the reference serial):
adb -s <clone-serial> exec-out screencap -p > _<name>-build/loopN/<screen>.png
Gotchas (required handling):
- Stale Metro bundle — a capture can reflect old code. Press
r in Metro / restart npx expo start --clear before trusting a screenshot.
- Host overload — high load avg makes the emulator go
offline. Apply host-contention backpressure: defer/retry when load drops; don't fight a wedged emulator.
- Shared-emulator discipline — pin distinct serials + ports for reference vs clone; bring the app to the foreground before
screencap; never drive the reference emulator (enforced by the serial guard, not just this note).
- Spawned-agent cwd = repo root — agents run with cwd = the monorepo main checkout, not the worktree; relative paths silently contaminate base/siblings. Always pass absolute worktree paths and
cd the app root in every shell command; verify with the porcelain 0-diff check.
- Worktree base = local HEAD — uncommitted base changes don't transfer into the worktree; commit them first. Symlink per-app
node_modules if the worktree is missing them (else tsc is "not found" or falsely green against main).
Model routing per seat (no haiku/sonnet pins):
- Judgment seats (scope synthesis, foundation contract, parity review, integrate pass, the zero-orphan/0-diff verifier) → inherit the session model (omit the override).
- Mechanical seats (per-screen builders, per-screen fix agents, static decompile mining) → may pin
opus.
</Gotchas_And_Runbook>
<Verification_Checklist>
The Done-bar — runs verbatim as the final gate before Deliver (Stage 6). All must be true: