flame-harness-admob
Phase 7 — analyze the game, decide a rewarded-ad strategy, guide manual AdMob ad-unit creation, and inject google_mobile_ads + ATT/UMP consent code.
Phase 8 — bootstrap signing credentials, generate fastlane config from templates, and build + upload signed IPA (TestFlight) and AAB (internal track).
$ npx -y skills add tjdrhs90/flutter-flame-harness --skill flame-harness-build --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/flame-harness-buildContext preview
The summary Claude sees to decide when to auto-load this skill.
Phase 8 — bootstrap signing credentials, generate fastlane config from templates, and build + upload signed IPA (TestFlight) and AAB (internal track).
name: flame-harness-build description: Phase 8 — bootstrap signing credentials, generate fastlane config from templates, and build + upload signed IPA (TestFlight) and AAB (internal track). argument-hint: "" allowed-tools: [Read, Write, Edit, Bash, Glob]
Phase 8 of the flutter-flame-harness pipeline. Bootstraps signing credentials into the generated game, generates fastlane config from the Task 3 templates, then builds and uploads a signed IPA to TestFlight and a signed AAB to the Play Store internal track.
All file schemas (`config.md`, `state.md`, `pipeline-log.md`) and the phase transition table are defined in `docs/harness-protocol.md` — that document is the single source of truth (§1 for `config.md` including `credentials_dir`, `ios`, and `android` blocks; §2 for `state.md`; §6 for log schemas; §7 for the `build → screenshot` transition and the `status: running` rule). Do not redefine schemas here.
**Prerequisites:** An App Store Connect app record and a Google Play Console app must already exist (created once manually). The upload lanes will fail without them. Fastlane and Ruby must be installed on the developer's macOS machine (Xcode and the flutter CLI are assumed present).
---
Before any action, load:
1. `docs/harness/config.md` — extract `app_slug`, `bundle_id`, `app_name`, and `credentials_dir` (per protocol §1). Also read `ios.asc_key_id` and `android.key_alias`. 2. `docs/harness/state.md` — confirm `next_role: build` (per protocol §2).
Derive the game root path: `<projects-dir>/<app_slug>/`.
---
Copy signing credentials from the shared `credentials_dir` (per protocol §1 — skills must not hard-code credential paths; always read `credentials_dir` from `config.md`) into the game's platform directories.
mkdir -p <game>/ios/fastlane/certs cp <credentials_dir>/AuthKey_<asc-key-id>.p8 <game>/ios/fastlane/
The `.p8` filename encodes the ASC key ID from `config.md` `ios.asc_key_id`. The `certs/` directory holds the downloaded distribution certificate and provisioning profile (written by `fastlane certs`).
mkdir -p <game>/android/fastlane cp <credentials_dir>/play-store-key.json <game>/android/fastlane/
Check whether `<credentials_dir>/upload-keystore.jks` exists.
cp <credentials_dir>/upload-keystore.jks <game>/android/upload-keystore.jks
defined in `config.md` `android.key_alias` (default: `upload`). Store and key passwords are both `<keystore-password>`.
keytool -genkey -v \
-keystore <game>/android/upload-keystore.jks \
-alias upload \
-keyalg RSA -keysize 2048 -validity 10000 \
-storepass <keystore-password> -keypass <keystore-password> \
-dname "CN=<company>, OU=Dev, O=<company>, L=Seoul, S=Seoul, C=KR"Write `<game>/android/key.properties` by substituting the `templates/key.properties.template` values (placeholders are already concrete — the template uses literal values matching the keystore generated above):
storePassword=<keystore-password> keyPassword=<keystore-password> keyAlias=upload storeFile=upload-keystore.jks
The `storeFile` path is relative to the `android/` directory, which is where Gradle reads it.
---
Generate fastlane `Appfile` and `Fastfile` for both platforms by substituting the following placeholders in the templates located at `templates/fastlane/`:
| Placeholder | Value | |---|---| | `__APP_ID__` | `bundle_id` from `config.md` | | `__PACKAGE__` | `bundle_id` from `config.md` | | `__IPA_NAME__` | `<app_name>.ipa` (spaces allowed; use the value of `app_name` verbatim) | | `__PROFILE_NAME__` | `<bundle_id> AppStore` (e.g. `com.<company>.mygame AppStore`) |
Note: `__PROFILE_NAME__` is used both as the Xcode code-signing profile name and as the `.mobileprovision` filename (with `.mobileprovision` appended by fastlane). Spaces are acceptable.
mkdir -p <game>/ios/fastlane/certs
# Write Appfile
sed -e 's/__APP_ID__/<bundle_id>/g' \
templates/fastlane/ios-Appfile.template \
> <game>/ios/fastlane/Appfile
# Write Fastfile
sed -e 's/__APP_ID__/<bundle_id>/g' \
-e 's/__IPA_NAME__/<app_name>.ipa/g' \
-e 's/__PROFILE_NAME__/<bundle_id> AppStore/g' \
templates/fastlane/ios-Fastfile.template \
> <game>/ios/fastlane/Fastfilemkdir -p <game>/android/fastlane
# Write Appfile
sed -e 's/__PACKAGE__/<bundle_id>/g' \
templates/fastlane/android-Appfile.template \
> <game>/android/fastlane/Appfile
# Write Fastfile
sed -e 's/__PACKAGE__/<bundle_id>/g' \
templates/fastlane/android-Fastfile.template \
> <game>/android/fastlane/FastfileAfter substitution, verify with `grep '__' <game>/ios/fastlane/Appfile <game>/ios/fastlane/Fastfile <game>/android/fastlane/Appfile <game>/android/fastlane/Fastfile` — output must be empty (no unresolved placeholders).
---
Run all build commands from the game root (`<projects-dir>/<app_slug>/`).
Before building, ensure the platform items in `docs/game-gotchas.md` (Build/platform + Store rejections) are in place — these are submit-blockers if missing: iOS `PrivacyInfo.xcprivacy` registered (iOS 17+), Android `minSdk = 23` + core-library desugaring, iOS `Podfile` platform bumped, `Info.plist` ATT + `SKAdNetworkItems`, and `ITSAppUsesNonExemptEncryption = false` (skip the export-compliance prompt). **Bump the build number on every upload** — App Store Connect rejects a duplicate build number, so increment it (don't reuse `1`).
**Re-run / resume safety (store uploads are NOT idempotent):** if this phase is re-entered after a pa
A Claude Code plugin that takes a Flutter/Flame game from raw idea all the way to the app stores.
Phase 7 — analyze the game, decide a rewarded-ad strategy, guide manual AdMob ad-unit creation, and inject google_mobile_ads + ATT/UMP consent code.
Phase 4 — propose verifiable completion criteria and mandatory hard gates; reach AGREED (1-pass default, multi-round negotiation in --strict).
Phase 3 — define the Flutter design_tokens.dart spec (palette, typography, spacing), the game's art/visual concept, and the asset/audio sourcing plan.
Phase 6 — skeptical QA. Run the game, watch it, then judge against the contract. Default = functional check; --strict adds quality and edge-case passes.
Phase 5 — build the Flame game in 3 gated sub-phases (core loop → systems+components → UI+content), then self-evaluate against the contract.
Phase 2 — write the game PRD in the project's default_language (core loop, mechanics, content metrics, win/lose, scope guard), map the lib/ structure, and…