reflutter
GitHub

The skills

The plugin ships twelve skills:

  • Nine are the train — the runbook and the checks that answer a question you were going to ask anyway.
  • Two ride alongside it — store copy and Android signing.
  • One is standalone — a widget performance audit.

The naming grammar behind all of them is Skills are questions. Every skill’s deep reference is its own SKILL.md in the plugin; this page tells you which one to reach for, and when.

Required reading — release-model

The doctrine, as a loadable skill: frozen candidates, carried build numbers, gates, the frozen window. Other skills load it first; you should read its page once before your first train.

The driver — ship

The runbook itself: /reflutter:ship. Takes the app from a ready main to verified artifacts in internal testing, one stage at a time, dry-run first, stopping at every gate. User-invoked only — nothing starts a release for you.

Use case: you have a fix merged on main and want it in testers’ hands. You type /reflutter:ship, confirm the dry-run summary, and say “run live.” Everything from analysis to a freshness-verified .aab + .ipa happens in one lane; the next thing the runbook asks of you is store copy.

“Where were we?” — checking-train-status

Reconstructs the train’s position from evidence: the release receipt (.release-state, written by the lanes on live milestones) when it exists, git and filesystem archaeology otherwise. Read-only; ends with the single next step.

Use case: you come back Monday, the terminal is closed, and you can’t remember whether Friday’s release uploaded. Ask “where were we with the release?” — you get the station, the evidence, any warnings, and the exact next command. Human-only facts (did you verify on device?) are asked, never assumed.

“Is this safe?” — verifying-candidate-integrity

The pre-finalize gate as a named step. Proves nothing is stranded on the RC branch, the frozen window held, every promoted stage consumed the RC tag, and Podfile.lock drift is classified. PASS/FAIL with evidence; any FAIL is a hard stop with the exact remediation.

Use case: production is verified and you are about to finalize — the one destructive step. Run the gate first: it is the difference between “finalize deleted the scaffolding” and “finalize silently destroyed a commit.” It also audits finished releases: point it at an old version and it checks the tag landed on the bump commit.

Something failed — diagnosing-lane-failures

The judgment layer for a red lane. Maps the failure to its real cause — env files, signing, Apple agreements, stale-binary false alarms, Podfile drift — and hands back one next action. Never advances the release; never retries a step that burned a build number.

Use case: the iOS build dies with “CocoaPods is installed but broken.” That message is almost always a lie. The skill knows the two real causes (cocoapods missing from the bundle; a non-UTF-8 locale), tells you which one you have, and what to run.

Fleet sweep — auditing-train-hygiene

One read-only pass over every app repo: stranded RC commits, diverged mains, tags off the bump commit, husk branches, stale receipts, release debt. One report, grouped by severity then app, every finding with evidence and a named remediation.

Use case: a quarterly “what did past trains leave behind?” pass across all your apps — or the calibration run before you schedule the scouts.

Standing watchers — scout-stranded-work · scout-train-staleness

Scheduled versions of the checks you would forget to run. scout-stranded-work (daily) watches the two silent-data-loss conditions; scout-train-staleness (weekly) watches for abandoned trains and quietly accumulating release debt. Their thresholds are plain-English sentences inside each SKILL.md — edit them there, no code involved. Scouts report and never act; a clean fleet is reported explicitly.

Use case: you never think about them — until one reports “rc/2.22.0 has a commit that is not on main” the morning after a rushed session, before finalize would have destroyed it.

Onboarding an app — instrumenting-release-lanes

Puts a manual app on the train: writes the reflutter.json release block (facts read from the build config, never guessed), scaffolds the one-line Fastfile / tag-pinned Pluginfile / Gemfile, verifies signing, then proves the wiring with a dry run of every derived lane. Never runs anything live.

Use case: a new app has been shipping via hand-run flutter build commands. One invocation later it has the same lanes, guards, and receipt as every other app — and its first real train is a deliberate /reflutter:ship.

Store copy — drafting-store-notes

The store-listing step of the train. Drafts Google Play notes (en-US + es-419, in Play’s template, within the character limit) and the TestFlight “What to Test” file from the CHANGELOG.md entry, gets the copy approved in conversation, then writes both files at the exact paths the prepare lane printed. Invoked by ship after a successful prepare; the approved copy is then immutable downstream.

Use case: the runbook has built verified artifacts and stops for store copy. You don’t hand-write it into two consoles — the skill drafts both locales and the What-to-Test file from the changelog, you approve, and it lands them where the upload step expects.

The GitHub release write-up and the public site record are separate authoring moments that also travel with a release; the runbook stops and hands off rather than drafting that copy itself.

Build readiness — release-signing

Train-adjacent, and a dependency of ship: per-flavor Android signing configs (env-var-first, keystore/*.properties fallback), keytool keystore generation per environment, a gitignored keystore/ folder, and the runtime permissions the release build needs in the main manifest.

Use case: the first release build dies with a FinalizeBundleTask NullPointerException, or the app runs in debug but throws errno 7 host-lookup only in the release flavor. This skill is where both are fixed — and where a new app’s signing is set up before its first train.

Standalone — auditing-widget-performance

Not release machinery: an enforcing skill that audits widgets for Equatable chain completeness, context.select scoping, RepaintBoundary placement, and one-widget-per-file. It ships in the plugin with its own eval fixtures.

Use case: a list scrolls poorly or a BlocBuilder rebuilds too often. Point this skill at the widget and it names the missing Equatable field, the over-broad context.watch, or the absent RepaintBoundary.