| name | mobile-release |
| description | Use when preparing a mobile release from the dev branch and deciding whether changes should ship through the app stores or through the OTA pipeline before creating the release PR to mobile-main. |
| disable-model-invocation | true |
| allowed-tools | Bash, Read, Write, Edit, Glob, Grep |
Mobile Release
Perform a mobile release from dev, with an explicit release-mode decision before the bump.
The skill must recommend one of these modes, explain why, ask the user to confirm, and then write apps/mobile/release-plan.json before running the bump:
store: normal App Store / Google Play release
ota: OTA-only publish, no store builds
Do not recommend or write any other mode. The current implementation only supports store and ota.
The CI release flow is file-driven:
apps/mobile/release-plan.json is the editable plan on dev
pnpm bump runs apps/mobile/scripts/apply-release-config.ts
- that script writes
apps/mobile/release.json for the new version and resets the plan back to a safe default
- GitHub Actions reads
apps/mobile/release.json after merge to decide which pipelines to trigger
Pre-flight checks
- Confirm the current branch is
dev. If not, abort with a warning.
- Run
git pull --rebase in the repo root.
- Read:
apps/mobile/package.json
apps/mobile/release-plan.json
.github/workflows/tag.yml
.github/workflows/publish-ota.yml
Step 1: Gather changes since last release
- Find the last mobile tag:
git tag --sort=-creatordate | grep -E '^mobile[@/]' | head -1
- If no tag exists, fall back to the last release commit subject:
git log --format="%H %s" | grep -Ei "^[a-f0-9]* release\\(mobile\\): release v" | head -1 | awk '{print $1}'
- Collect commits since that point:
git log <last-tag-or-commit>..HEAD --oneline --no-merges
- Collect changed files:
git diff --name-only <last-tag-or-commit>..HEAD
- Categorize commits into:
Shiny new things
Improvements
No longer broken
Thanks
Step 2: Recommend a release mode
Recommend store when:
- any native or binary-affecting paths changed, for example:
apps/mobile/ios/**
apps/mobile/android/**
apps/mobile/native/**
apps/mobile/package.json
apps/mobile/app.config.ts
apps/mobile/app.config.base.ts
apps/mobile/eas.json
apps/mobile/ios/Folo/Info.plist
.github/workflows/build-ios.yml
.github/workflows/build-android.yml
- Expo / React Native / native dependency changes require a new binary
- permissions, entitlements, icons, splash, Firebase config, or build configuration changed
Recommend ota when:
- changes are limited to JS/TS/assets that the current binary can already run
- no native or runtime-affecting paths changed
- the goal is to ship without store review
Determine the target runtime
If recommending ota, derive the target runtime from the store binaries that users currently have installed, not from the new release version, latest mobile tag, or latest OTA release.
- Check the public store versions first:
curl --fail --silent --show-error https://ota.folo.is/versions | jq '.store.mobile'
- Cross-check the store runtime model in
apps/mobile/app.config.base.ts. Today the mobile runtime defaults to the binary package version unless OTA_RUNTIME_VERSION is explicitly set during an OTA export.
- Use the current App Store / Google Play binary version as the OTA
runtimeVersion. Example: if the stores still show 0.5.0, an OTA release for 0.5.4 must use "runtimeVersion": "0.5.0" so existing store users can receive it.
- If iOS and Android store versions differ, or if the target installed runtime is not clear, stop and ask the user. The release plan supports only one OTA
runtimeVersion; do not guess or silently pick the newest version.
Never choose the previous OTA release version just because it is the latest working manifest. A runtime mismatch publishes valid assets that only newer binaries can see, leaving current store users stuck on the older OTA.
If you cannot determine the runtime confidently, stop and ask the user to confirm it.
Confirmation gate
Present:
- the recommended mode
- rationale based on changed files and commits
- for
ota, the proposed runtimeVersion
- for
ota, the proposed channel
Wait for explicit user confirmation before continuing.
Step 3: Write the release plan file
Update apps/mobile/release-plan.json to match the confirmed mode.
Examples:
Store release
{
"mode": "store",
"runtimeVersion": null,
"channel": null
}
OTA release
{
"mode": "ota",
"runtimeVersion": "0.4.1",
"channel": "production"
}
Step 4: Update changelog
- Read
apps/mobile/changelog/next.md.
- Present the categorized changes and draft changelog content.
- Wait for user confirmation or edits before writing.
- Write the final content to
apps/mobile/changelog/next.md.
Step 5: Commit pre-bump files before bump
nbump requires a clean working tree.
- Stage:
git add apps/mobile/changelog/next.md apps/mobile/release-plan.json
- Commit:
git commit -m "docs(mobile): prepare release metadata"
- If nothing changed, continue.
Step 6: Execute bump
- Verify the working tree is clean:
git status --short
- Run:
cd apps/mobile && pnpm bump
pnpm bump will:
- apply changelog
- bump
package.json
- update
ios/Folo/Info.plist
- run
tsx scripts/apply-release-config.ts ${NEW_VERSION}
- write
apps/mobile/release.json
- reset
apps/mobile/release-plan.json back to the safe default store
- create the release branch
- push the branch
- open a PR to
mobile-main
Step 7: Verify and report
- Confirm the PR was created successfully.
- Read
apps/mobile/release.json on the release branch and report:
- new version
- final release mode
- runtimeVersion and channel if present
- PR URL
- Summarize expected post-merge automation:
mode=store
- create mobile tag
- trigger preview Android build
- trigger production Android build
- trigger production iOS build
- no OTA publish
mode=ota
- create mobile tag
- trigger OTA publish only
- no store builds
Do not require live OTA manifest verification during release PR preparation. The user manually merges the PR later, so the OTA publish happens after this workflow finishes and there may be a time gap before the Worker syncs. If the user later asks to check the rollout, verify the workflow run, GitHub Release assets, and /manifest at that time.
References
- Bump config:
apps/mobile/bump.config.ts
- Release plan:
apps/mobile/release-plan.json
- Release config:
apps/mobile/release.json
- Apply release config script:
apps/mobile/scripts/apply-release-config.ts
- Changelog dir:
apps/mobile/changelog/
- Apply changelog script:
apps/mobile/scripts/apply-changelog.ts
- Release config resolver:
.github/scripts/resolve-mobile-release-config.mjs
- Tag orchestration:
.github/workflows/tag.yml
- OTA publish workflow:
.github/workflows/publish-ota.yml
- App config:
apps/mobile/app.config.ts
- iOS Info.plist:
apps/mobile/ios/Folo/Info.plist