The release model
Every Ship skill assumes one shared mental model. This page is that model — no
commands, just the ideas the commands implement. (In the plugin it ships as the
release-model skill, the required reading the other skills load first.)
Two layers, one human on top
A release is a deterministic pipeline plus judgment:
- The deterministic layer — fastlane lanes wired to
fastlane-plugin-flutter_deploy. Mechanical, versioned, identical every run. If a step’s correct action is the same every run, it belongs in the gem — never re-implemented in a skill or a script. - The judgment layer — the skills: the flow, the gates, and turning a raw failure into an exact next action. Anything that requires reading something ambiguous — an error string, a diff, CI state — lives here.
- You — above both. Every irreversible or outward-facing act sits behind your explicit word. See Release Interlocks.
The candidate is frozen source
Staged apps ship through two stages, in order: staging first, then
production. These are separate store listings, with different bundle ids and
different keystores, so a staging binary can never be uploaded to the
production listing.
That is why the release candidate is the source at the RC tag, not a binary. Each flavor’s binary is a rendering of that one frozen source:
Build number carries the train: staging build N reaches production as build N. A different number would mean a different build shipped than was tested.
- Staging is prepared: bump, cut
rc/X.Y.Z, build. - Production is promoted, never re-prepared: check out the same tag, rebuild the production flavor, reuse the version and build number.
The binary your testers signed off on is the one that ships.
Copy is approved once, then immutable
Store copy follows the same frozen logic as source. Play notes are drafted and approved once, at upload time. From there they flow verbatim to:
- TestFlight copy.
- Promoted-stage note files (same copy, same approval, no new gate).
- The public site record.
- The App Store Connect paste-ins.
Downstream steps derive; they do not improve. Consistency beats polish. In practice, every downstream “improvement” to approved copy ends up reverted back to the approved text. Divergence is proposed with a reason, or not at all.
The gates
lane / skill work human gate — never automated across the one destructive step
Two facts about the gates that are easy to miss:
- “Verified, promote” is both signal and consent. After you test the staging build on a device, your word carries the verification and the go-ahead in one sentence — and it is always asked for, never inferred.
- The console acts split in two. Public promotion in the store consoles begins before finalize; the App Store Connect paste-in and review submission is the last act of the whole train, after the public record exists.
Why finalize is the dangerous step
Finalize does two things that cannot be walked back:
- It deletes the RC branch with
-D, bypassing git’s unmerged-work protection by design. - It tags the release with a bare
git tag, which lands on whatevermainpoints at when it runs.
main ──●──────●━━━━━━━━━━━━━━━━━━━◎──────●── │ bump X.Y.Z+N FROZEN finalize tags HERE │ └─ the bare tag X.Y.Z must land on this commit │ rc/X.Y.Z └──○──────○ build base — COMMIT NOTHING HERE deleted by finalize with -D: git's unmerged-work protection is bypassed BY DESIGN
Two rules make it safe, and one skill enforces them
(verifying-candidate-integrity):
- Commit nothing to an RC branch, ever. A fix discovered mid-release is
either release content (goes to
main, then re-cut as a new RC round) or tooling (goes tomain, unrelated to this train). There is no third category. mainis frozen from bump to finalize. Before finalize goes live,main..rc/X.Y.Zmust be empty and the head ofmainmust still be the bump commit. Any failure is a hard stop, not a warning.
Who leads what
| You lead | The skills lead |
|---|---|
| Invocations | Mechanics |
| On-device verification | Gate sequencing |
| Copy taste | Failure interpretation |
| Console actions | Drafting |
| Merge and publish decisions | Cross-repo bookkeeping |
A release is never started, advanced past a gate, or finalized on a skill’s initiative.