tjdrhs90/flutter-flame-harness/flame-harness-submit
Claude Code and agent skill by tjdrhs90/flutter-flame-harness
- community
Checking that the SKILL.md file is still in the repository.
Phase 10 — upload store text metadata + categories via fastlane, then pause with exact manual steps for the final iOS review submission and Android production promotion.
Install
npx vskill@latest install tjdrhs90/flutter-flame-harness/flame-harness-submitSKILL.md
flame-harness-submit
Phase 10 of the flutter-flame-harness pipeline. Uploads store text metadata and app categories via fastlane for both iOS and Android, then pauses the pipeline with exact manual steps for the final review submission / production promotion (see Boundary below for what is genuinely manual and what is a deliberate checkpoint).
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; §2 for state.md keys and pause_reason values; §6 for pipeline-log.md schema; §7 for the submit → metadata-done → paused transition and the (paused) → resume → retro transition). Do not redefine schemas here.
Boundary: Text metadata upload is automated. The final "Submit for Review" on iOS and the "Promote to production" action on Android are manual in this pipeline — deliberately, so a human sees the assembled listing once before it goes out, and because the Play questionnaires (Content Rating, Data Safety, Target Audience) have no API.
Note the iOS half is automatable if you want it: reviewSubmissions will submit a version end-to-end (see iOS submission over the API below). What is genuinely not automatable is the Play side and the age-rating questionnaire. Keep the pause unless you have a reason not to — the review is the point of it.
Prerequisites: Phase 9 (flame-harness-screenshot) completed; screenshots and ASO metadata already uploaded; state.md shows next_role: submit.
Input — Read Inputs
Before any action, load:
docs/harness/config.md— extractapp_slug,bundle_id,app_name, anddefault_language
(per protocol §1).
docs/harness/state.md— confirmnext_role: submit(per protocol §2).
Derive the game project root for <app_slug> from config.md / the harness working directory (the games live under the same parent as the harness, e.g. …/AndroidStudioProjects/<app_slug>/).
Phase 0 — Generate store-info files from config
Before uploading, write the store-listing + App-Review files from the developer block in config.md (sourced from credentials/store-metadata.md) so they actually get uploaded — these are otherwise empty and the listing/review info would be missing.
iOS (deliver picks these up automatically):
ios/fastlane/metadata/copyright.txt←developer.copyright- per locale (
ko,en-US):support_url.txt←developer.homepage,marketing_url.txt←
developer.homepage, privacy_url.txt ← developer.privacy
ios/fastlane/metadata/review_information/:first_name.txt,last_name.txt,phone_number.txt,
email_address.txt ← developer.*; notes.txt ← reviewer notes (e.g. the ATT screen-recording note for an ads build).
Android (contact email + website have no `supply` field — set via the Publisher API):
- copy
templates/set_contact_details.rb.template→android/fastlane/set_contact_details.rb,
fill __PACKAGE__=bundle_id, __EMAIL__=developer.email, __WEBSITE__=developer.homepage, and run cd android && ruby fastlane/set_contact_details.rb.
- The privacy policy URL and Data Safety / Content Rating / Target Audience are set in the manual
step below (Play console).
Phase 1 — Metadata Upload
Run the fastlane metadata and categories lanes for both platforms. These lanes push text metadata (localized titles, descriptions, release notes) and app category assignments to the stores without uploading a binary.
iOS metadata upload
fastlane metadatapushes all locale text files underios/fastlane/metadata/to App Store
Connect via deliver (titles, subtitles, descriptions, keywords, promotional text, release notes).
fastlane categoriessets the primary and secondary App Store category. Thecategorieslane
must already exist in the generated ios/fastlane/Fastfile (written by Phase 8 flame-harness-build).
Android metadata upload
fastlane metadatapushes all locale text files underandroid/fastlane/metadata/android/to
Google Play (titles, short descriptions, full descriptions) via upload_to_play_store with skip_upload_apk: true and skip_upload_aab: true.
fastlane release_notespushes thechangelogs/<version-code>.txtfiles for the current
release.
If any fastlane lane exits non-zero, do NOT proceed to the pause step. Instead write docs/harness/state.md with status: paused, pause_reason: manual_action, and next_role: submit (retry), then explain the error and stop.
Phase 2 — Manual Steps + Pause
After both upload sequences complete successfully, the pipeline must pause so the developer can perform the final submission actions that cannot be automated.
Pre-submit rejection checklist (print this with the manual steps — these are the rejections already hit; see docs/game-gotchas.md → Store rejections):
- Age rating declarations complete — especially `advertising` = Yes. Apple added
userGeneratedContent, messagingAndChat, ageAssurance, gunsOrOtherWeapons, advertising, healthOrWellnessTopics, parentalControls in 2026. An app record that only answered the old questionnaire is blocked before human review. Any AdMob build (banners count) must answer `advertising` = Yes, or Apple's automated analysis bounces it. ageRatingDeclarations is write-only over the API (GET → 403) — verify in the Connect UI, there is no other way.
- Android: check the *merged* manifest, not the one you wrote. Dependencies merge their own
permissions into the bundle and Play Console demands a declaration for them under App content → Needs attention. FOREGROUND_SERVICE_* in particular requires a form with a mandatory demo video link (unlisted YouTube; private is invisible to reviewers) showing the feature working while the app is backgrounded. Past the deadline this blocks updates while the live listing stays up. Check: grep uses-permission android/app/build/intermediates/merged_manifests/release/AndroidManifest.xml
- iPad: `TARGETED_DEVICE_FAMILY` matches reality. Declaring iPad (
"1,2") obliges the game to
support every iPad orientation — a portrait-only phone game shipped with the template default gets rejected under 4.0. Phone-only games use "1".
- Version string not already released. A new build under an already-released version fails with
"Invalid Pre-Release Train … closed for new build submissions." Bump the name in pubspec.yaml and leave +N (the Android versionCode) alone.
- ATT (2.1): attach a screen recording from a physical device to App Review Information →
Notes, showing fresh-install → ATT prompt appears before any tracking → following flow. (The app must use the wait-for-resumed ATT pattern, or the prompt won't show on the latest iOS.)
- Localized permission strings: every configured App Store locale's
ios/Runner/<locale>.lproj/InfoPlist.strings must carry a NSUserTrackingUsageDescription (and any other NS*UsageDescription the app declares) — a permission string left in only one language is a repeat localization rejection (the reviewer sees a foreign-language prompt). Verify: for d in ios/Runner/*.lproj; do grep -q NSUserTrackingUsageDescription "$d/InfoPlist.strings" || echo "MISSING: $d"; done (no output = OK). Wording must match the App Privacy / tracking declaration.
- No alpha in the iOS icon or any screenshot.
- Build number incremented (ASC rejects duplicates).
- Export compliance set (
ITSAppUsesNonExemptEncryption=false) so no per-upload prompt. - iOS App Privacy (nutrition label): in App Store Connect → App Privacy, declare data types
accurately (this is separate from PrivacyInfo.xcprivacy, which is a bundled manifest — the label is a Connect form). A missing/empty label blocks release. Use the Data-collection profile below.
- Privacy:
PrivacyInfo.xcprivacypresent in the bundle; tracking matches the ATT prompt. - Android: privacy policy URL set (
developer.privacy); Content Rating (IARC), Data Safety, and
Target Audience questionnaires completed with the Data-collection profile below. (Contact email/website were set in Phase 0 via the API script.)
- COPPA / kids: if the game is child-directed (see the profile), AdMob must be "Made for kids"
with tagForChildDirectedTreatment, and Play Target Audience must include children — keep all three consistent or expect a rejection / FTC exposure.
Pre-pause state write
Per docs/harness-protocol.md §7 (submit → metadata-done event row) and §7 rule 3 (when status is paused, pause_reason must be non-empty), write docs/harness/state.md atomically with exactly these field changes (leave created_at, current_round, resume_attempts unchanged):
Important: next_role: retro is set at pause time because a paused state stores the role to run after resume. flame-harness-resume reads next_role directly from state.md (it does NOT set next_role itself) and dispatches Skill("flame-harness-<next_role>"). Setting next_role: retro here is what causes resume to advance to retro rather than re-dispatching submit (see Resume Contract below).
Append to pipeline-log.md
Append one row to docs/harness/pipeline-log.md per the schema in docs/harness-protocol.md §6, using the transition event name metadata-done (matches protocol §7):
iOS submission over the API (reference — for resubmissions and automation)
POST /v1/appStoreVersionSubmissions is deprecated: 403 "Allowed operation is: DELETE". The current flow is three calls:
Resubmitting after a rejection. The rejected version is still attached to the submission that rejected it, so step 2 fails with STATE_ERROR.ITEM_PART_OF_ANOTHER_SUBMISSION. Detach first: for every submission in state UNRESOLVED_ISSUES, PATCH /v1/reviewSubmissionItems/<itemId> { removed: true }.
Do not create a new version for a rejection — REJECTED, DEVELOPER_REJECTED, METADATA_REJECTED, INVALID_BINARY are editable, so attach the new build to the same version. A duplicate version string fails with ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE.
Reviewer communication. Resolution Center has no API — you can read that a version is REJECTED but not why, and you cannot reply. appStoreReviewDetail.notes is readable, writable, and read by reviewers; it is the only channel available. When fixing a rejection, put the explanation in ios/fastlane/metadata/review_information/notes.txt and upload it before resubmitting. State facts about this build — "we will add it later" invites another rejection.
Metadata stays editable through `WAITING_FOR_REVIEW` and `IN_REVIEW` and locks at READY_FOR_SALE (PATCH → 409). A wrong release note is fixable until approval, then never.
Print exact manual steps
After writing state, print the following instructions verbatim for the developer:
PIPELINE PAUSED — MANUAL SUBMISSION REQUIRED
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/flame-harness-submit/SKILL.md
Popularity
- Repository stars
- 60
- Repository forks
- 5
- 7-day trend score
- 21
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/tjdrhs90/flutter-flame-harness/flame-harness-submit)