| name | rn-harness-screenshot |
| description | Phase 9 โ Automated store screenshots via Maestro on real simulator/emulator. Hides ads during capture. |
| allowed-tools | ["Read","Write","Edit","Bash","Glob","Grep","AskUserQuestion"] |
rn-harness-screenshot โ Phase 9: Store Screenshots
Capture store screenshots by running the app on a real simulator/emulator with Maestro. Ads are hidden during capture via environment variable.
How It Works
This is NOT image generation. Maestro physically controls the simulator:
- Launches the app on a real iOS Simulator or Android Emulator
- Taps buttons, scrolls, navigates between screens
- Takes PNG screenshots at each screen
- You can watch it happen live on the simulator
Trigger
Called by the orchestrator as Phase 9.
Input
docs/harness/plans/YYYY-MM-DD-prd.md (screen structure)
- Actual project code
- Development build installed on simulator/emulator
Prerequisites
- Maestro CLI (
curl -Ls "https://get.maestro.mobile.dev" | bash)
- iOS Simulator or Android Emulator running
- Development build installed (from Phase 8)
Process
Step 0: Hide Ads for Screenshots
Store screenshots must NOT show ads. Before capturing, ensure the ad-hiding mechanism is in place.
0a. Verify AdBanner has EXPO_PUBLIC_HIDE_ADS support
Check src/features/ads/ui/AdBanner.tsx:
export function AdBanner({ size = BannerAdSize.ANCHORED_ADAPTIVE_BANNER }: IAdBannerProps) {
if (process.env.EXPO_PUBLIC_HIDE_ADS === 'true') return null;
return (
<BannerAd
unitId={getAdUnitId('BANNER')}
size={size}
requestOptions={{ requestNonPersonalizedAdsOnly: true }}
/>
);
}
If not present, add the EXPO_PUBLIC_HIDE_ADS check to:
AdBanner.tsx โ banner component
use-interstitial.ts โ interstitial hook (skip show())
use-rewarded.ts โ rewarded hook (skip show())
use-app-open-ad.ts โ app open ad hook (skip)
0b. Start app with ads hidden
EXPO_PUBLIC_HIDE_ADS=true npx expo start --dev-client
Or if using expo run:*:
EXPO_PUBLIC_HIDE_ADS=true npx expo run:ios
EXPO_PUBLIC_HIDE_ADS=true npx expo run:android
Step 1: Choose Screenshot Screens
Select 5-8 key screens from PRD for store listing:
- Onboarding / Login (or splash)
- Main home screen
- Core feature 1
- Core feature 2
- Search / explore
- Profile / settings
- (Optional) Dark mode
- (Optional) Unique/differentiating feature
Step 2: Environment Detection
which maestro && echo "MAESTRO=yes" || echo "MAESTRO=no"
xcrun simctl list devices 2>/dev/null | grep "Booted" || true
adb devices 2>/dev/null | grep -v "List" | grep "device" || true
If Maestro is NOT available:
โ Skip to Step 6 (Manual Fallback)
Step 3: Write Maestro Flow
maestro/screenshots.yaml:
appId: com.company.app
---
- waitForAnimationToEnd
- takeScreenshot: docs/harness/store-assets/ios/01-first
- tapOn: "Home"
- waitForAnimationToEnd
- takeScreenshot: docs/harness/store-assets/ios/02-home
- tapOn: "[Feature Button]"
- waitForAnimationToEnd
- takeScreenshot: docs/harness/store-assets/ios/03-feature
- tapOn:
index: 0
- waitForAnimationToEnd
- takeScreenshot: docs/harness/store-assets/ios/04-detail
- tapOn: "Profile"
- waitForAnimationToEnd
- takeScreenshot: docs/harness/store-assets/ios/05-profile
Customize the flow based on actual app screens and navigation. Use assertVisible to verify correct screen before capturing.
Step 4: Capture Screenshots
iOS (App Store requirements)
maestro --device "iPhone 15 Pro Max" test maestro/screenshots.yaml
iPad screenshots NOT needed (supportsTablet: false in app.config.ts).
Android (Google Play requirements)
maestro test maestro/screenshots.yaml
Step 5: Organize Screenshots
docs/harness/store-assets/
โโโ ios/
โ โโโ 01-first.png
โ โโโ 02-home.png
โ โโโ 03-feature.png
โ โโโ 04-detail.png
โ โโโ 05-profile.png
โโโ android/
โ โโโ phone/
โ โโโ 01-first.png
โ โโโ 02-home.png
โ โโโ ...
โโโ icon.png # 512x512 (for Google Play)
โโโ feature_graphic.png # 1024x500 (for Google Play)
โโโ metadata/
โโโ ko-KR/
โโโ title.txt
โโโ short_description.txt
โโโ full_description.txt
โโโ release_notes.txt
Step 6: Manual Fallback (No Maestro)
If Maestro is not installed:
AskUserQuestion:
Maestro is not installed, so automatic screenshot capture is unavailable.
Please capture screenshots manually:
1. Run the app: EXPO_PUBLIC_HIDE_ADS=true npx expo start --dev-client
2. Navigate to each key screen
3. Take screenshots (Cmd+S in iOS Simulator, or device screenshot)
4. Save to docs/harness/store-assets/ios/ and android/phone/
Screens to capture:
1. [screen 1]
2. [screen 2]
...
Or install Maestro to automate:
curl -Ls "https://get.maestro.mobile.dev" | bash
Press Enter when screenshots are ready, or type "install" to install Maestro.
Step 7: Generate Store Metadata
Create metadata files from PRD:
docs/harness/store-assets/metadata/ko-KR/title.txt:
[์ฑ ์ด๋ฆ] (30 chars max)
docs/harness/store-assets/metadata/ko-KR/subtitle.txt (iOS only):
[๋ถ์ ] (30 chars max)
docs/harness/store-assets/metadata/ko-KR/short_description.txt (Android):
[์งง์ ์ค๋ช
] (80 chars max)
docs/harness/store-assets/metadata/ko-KR/full_description.txt:
[์ ์ฒด ์ค๋ช
] (4000 chars max, includes key features, value proposition)
docs/harness/store-assets/metadata/ko-KR/keywords.txt (iOS only):
CRITICAL: iOS App Store keywords field has a 100-character limit INCLUDING commas. This field is one of the most important ASO (App Store Optimization) factors. Fill it to the MAXIMUM โ generate keywords until the total exceeds 100 characters, then trim down.
Rules:
- Comma-separated, NO spaces after commas (to save characters)
- Do NOT include the app name (already indexed)
- Do NOT include the app category name (already indexed)
- Do NOT duplicate words from the title/subtitle
- Each word counts individually โ "์ปคํผ,๊ตฌ๋
,๊ฐ๊ณ๋ถ" searches for each separately
- Mix Korean + English keywords for broader reach
- Prefer single words over phrases (unless phrase is a common search term)
Process:
- Generate 20-30 candidate keywords from app features/target users/synonyms
- Join with commas (no spaces):
k1,k2,k3,...
- Count total characters
- If < 100: add MORE keywords (synonyms, English variants, related concepts)
- If > 100: trim least relevant keywords
- Target: 95-100 characters (use the full budget, leave 0-5 char safety margin)
Example (coffee subscription tracker app):
๊ตฌ๋
๊ด๋ฆฌ,์ปคํผ,์ ๊ธฐ๋ฐฐ์ก,์ง์ถ๊ด๋ฆฌ,๊ฐ๊ณ๋ถ,์๋ฆผ,ํต๊ณ,์ง์ถ๋ถ์,์์ฐ,subscription,coffee,tracker,monthly,bill,reminder,budget,spending
Count: 97 characters โ (uses full budget)
Bad example (too short):
์ปคํผ,๊ตฌ๋
,๊ด๋ฆฌ
Count: 9 characters โ (wastes 91 characters of search opportunity)
docs/harness/store-assets/metadata/ko-KR/release_notes.txt:
[์ถ์ ๋
ธํธ]
docs/harness/store-assets/metadata/ko-KR/screenshot_captions.md:
Screenshot caption copy for store listings. These are the short marketing headlines overlaid on each screenshot. Written based on what each screenshot actually shows. 5-8 captions, matched 1:1 with the captured screenshot order.
Rules:
- Headline: 1 line, 15-25 characters (mobile-readable at small thumbnail size)
- Subheadline: 1 line, 25-40 characters (supporting detail)
- First caption = brand/value proposition (splash/logo context)
- Middle captions = one feature per screenshot, benefit-focused not feature-focused
- Last caption = aspirational / call to action
- Korean uses shorter phrases (15-20 chars); English 20-30
- Use emoji sparingly (0-1 per caption) for scannability
- Match the screenshot order from Step 1
Format template:
# Screenshot Captions โ [App Name]
## 01 โ First screen (splash / login / onboarding)
**Headline:** ํ๋ฃจ ํ ๋ฒ, ๋ ๊ด๋ฆฌ
**Subheadline:** 3์ด ๊ฐ๊ณ๋ถ๋ก ์ถฉ๋ถํด์
**Matches:** docs/harness/store-assets/ios/01-first.png
## 02 โ Home / main screen
**Headline:** ์ด๋ฒ ๋ฌ ์ผ๋ง ์ผ์ง?
**Subheadline:** ์นดํ
๊ณ ๋ฆฌ๋ณ ์ง์ถ ํ๋์ ๋ณด๊ธฐ
**Matches:** docs/harness/store-assets/ios/02-home.png
## 03 โ Core feature (ex: quick entry)
**Headline:** ์๊ฐ๋ฝ ํ ๋ฒ์ผ๋ก ๊ธฐ๋ก
**Subheadline:** ์ปคํผ ํ ์, ํ์๋น, ๋ฐ๋ก ์
๋ ฅ
**Matches:** docs/harness/store-assets/ios/03-feature.png
## 04 โ Secondary feature (ex: statistics)
**Headline:** ๋ด ์๋น ํจํด์ด ๋ณด์ฌ์
**Subheadline:** ์ฃผ๊ฐยท์๊ฐ ๋ฆฌํฌํธ ์๋ ์์ฑ
**Matches:** docs/harness/store-assets/ios/04-detail.png
## 05 โ Last screen (premium / settings / achievement)
**Headline:** ๋ชฉํ ๋ฌ์ฑ๊น์ง D-23
**Subheadline:** ๋งค์ผ ์๋ฆผ์ผ๋ก ์์ง ์๊ธฐ
**Matches:** docs/harness/store-assets/ios/05-profile.png
Process:
- Read PRD for feature priorities and value proposition
- For each screenshot captured (Step 1 list), write a caption
- Headline emphasizes user benefit (not feature name)
- Bad: "Charts screen" / "์ฐจํธ ํ๋ฉด"
- Good: "See where your money goes" / "๋ด ์๋น ํจํด์ด ๋ณด์ฌ์"
- Subheadline adds specific detail
- Save the file, then the user can use it with Figma/Canva/Sketch to overlay text on screenshots
Why this matters: Screenshots on the store are viewed at thumbnail size. A user scrolling decides to install in 2-3 seconds based on these captions. A screenshot without a clear headline wastes that opportunity.
Step 8: User Review
AskUserQuestion:
Screenshots and metadata are ready. Please review:
Screenshots: docs/harness/store-assets/
Metadata: docs/harness/store-assets/metadata/ko-KR/
Checklist:
- [ ] Screenshots look clean (no ads visible)
- [ ] App name correct
- [ ] Description accurate
- [ ] Privacy policy URL: [URL from config]
- [ ] Screenshot captions match what's actually on screen (screenshot_captions.md)
- [ ] iOS keywords.txt uses 95-100 chars
Any changes needed? (Press Enter to proceed, or describe what to change)
Output
docs/harness/store-assets/ โ screenshots + metadata
maestro/screenshots.yaml โ Maestro flow
State Update
current_phase: submit
next_role: rn-harness-submit
HARD GATES
- Minimum 5 screenshots
- iOS 6.7" screenshots required (if on macOS)
- Metadata files (title, descriptions) required
- Privacy policy URL required (from config.md)
- Ads MUST be hidden during screenshot capture (EXPO_PUBLIC_HIDE_ADS=true)
- If Maestro unavailable โ manual fallback with clear instructions
- Screenshots must be reviewed by user before proceeding to submission
- iOS keywords.txt MUST use 95-100 characters (total length including commas). Under-utilizing this field hurts ASO. Generate extra keywords if short, trim if over 100.
- screenshot_captions.md MUST include one caption per screenshot, with headline + subheadline, matched to the actual on-screen content. Benefit-focused, not feature-focused.