Skip to content
Development
Command

/app-ship

Ship a release — runs security review, then release-manager, gating on QA sign-off and clean bug board

From plugin
app-dev-team
427 skills30 agents27 commands2 hooks
Install
> /plugin marketplace add vmobifystudio/app-dev-team
> /plugin install app-dev-team@mobify-studio

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/app-ship

Context preview

What this command does when you run it.

Ship a release — runs security review, then release-manager, gating on QA sign-off and clean bug board

Command definition

app-ship.md
description: Ship a release — runs security review, then release-manager, gating on QA sign-off and clean bug board
argument-hint: [version override, e.g. 1.2.0, optional]
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, Agent

/app-ship — Cut a release

Version (optional, otherwise release-manager picks): $ARGUMENTS

Steps

1. **Bind the artifact, then run every mechanical precondition as ONE command.**

First bind what you are about to ship to the commit that passed. Everything else here reasons about a COMMIT; until the binary is bound to it, "readiness: PASS" is a true statement about source and silent about the file you hand to a store:

   node "${CLAUDE_PLUGIN_ROOT}/scripts/release-candidate.mjs" record \
     --artifact <path to the built .ipa/.aab> --platform ios|android --variant release \
     --by release-manager

This step stays explicit because only you know the artifact path — guessing it would be inventing the subject of the whole release. A build from a dirty tree records as such and reads STALE forever after; that is not a bug to work around.

Then:

   node "${CLAUDE_PLUGIN_ROOT}/scripts/orchestrator.mjs" ship

That runs the board doctor, the ONE readiness reducer (which now includes the artifact you just bound) and `ship-gate.sh`, in order, worst answer wins. They were three separate steps each ending in "read the exit code"; the discipline of running all three, every time, is exactly what a 328-line file erodes.

  • Exit `0` → continue to the runtime gate below.
  • Exit `1` → **do not release.** It names which precondition. **STALE is not a failure** —

nothing says the product is broken, it says nobody checked THIS candidate. Re-run the gates it names, or rebuild and re-record.

  • Exit `2` → **CANNOT EVALUATE, also not a pass.** A gate has never run, or no artifact was

recorded, so what would be uploaded is UNKNOWN.

Confirm the gate results still describe this candidate rather than last Tuesday's:

   node "${CLAUDE_PLUGIN_ROOT}/scripts/evidence-check.mjs" \
     --result docs/team/journey-result.json --head "$(git rev-parse HEAD)"

Every gate answers at a moment and then stops thinking. `journey-gate` says PASS, three commits land, and the PASS sits there green describing a candidate that no longer exists. Nobody lied; the verdict outlived its subject. Exit `1` → re-run the gate rather than reasoning about how little changed.

**`orchestrator ship` submits and publishes nothing** — that boundary is constitutional (I-12). It reports; you and the user decide.

1a. **If step 1 reported the ship gate as BLOCKED or CANNOT EVALUATE — resolve it here.**

Do **not** re-run `ship-gate.sh` by hand: `orchestrator ship` already ran it (with `--record`, so `docs/team/ship-gate-verdict.json` is written for the control room's Mission Control panel — dry run 5 found its narrower sweep could say `clear` while this gate had just returned BLOCKED). Running it twice invites acting on the older of two answers.

These preconditions are a script, not prose to improvise — improvising them produced three silent, confident failures in one session (a guard that could not fail, a field-index mistake, a regex that reported zero open S1/S2 bugs while two were open; see `defect-hunting`).

  • **BLOCKED** → a real condition: an open S1/S2, a ticket still in flight (including `blocked`),

a QA hold. Print its blockers verbatim and stop.

  • **CANNOT EVALUATE** → a required input is missing or unparseable. The gate prints one missing

input per line; **surface those lines verbatim** under `SHIP GATE: CANNOT EVALUATE` at the top of your output. This is a stop that names what is absent — never a pass, and never an unrecoverable error.

On CANNOT EVALUATE, offer the user exactly two ways forward per missing input, and take the one they choose (then re-run `orchestrator ship`, not the gate alone):

1. **Produce it.** Name the owning role and spawn it. A brownfield project reaching `/app-ship` without `docs/50-test-plan.md` has simply never run a QA wave — that file is only ever written inside a `/app-build` QA pass — so `qa-engineer` writes and executes one against the shipped surface. Then re-run the gate. 2. **Waive it, with the reason written down.** Append to `docs/60-releases.md` under this version — the release record `release-manager` already owns — a line reading `WAIVED: <artifact> — <who waived it> — <reason>`. Then **re-run `ship-gate.sh`** — it reads `docs/60-releases.md`, and it is the gate itself that decides the waiver applies, not you. A waiver it accepts comes back as `WAIVED: <artifact> by <who> — <reason>` in its output; repeat that line in the release summary and in step 4's confirmation question.

`<artifact>` must be the exact path the gate named (`docs/51-bugs.md`, `docs/50-test-plan.md`), and **all three fields must be present** — the gate rejects a waiver with no name or no reason and stays at `CANNOT EVALUATE`, saying the waiver was malformed. That is deliberate: an unsigned, unexplained waiver is a skipped gate wearing a decision's clothes. If the gate still says `CANNOT EVALUATE` after you wrote a waiver, the waiver did not count — do not proceed on the strength of having written it.

**Once this project has a canonical version** (a `## vX.Y.Z` heading exists anywhere in `docs/60-releases.md`), a waiver must name it as a fourth field — `WAIVED: <artifact> — <who waived it> — <reason> — vX.Y.Z` — or it does not count. This closes the gap an external audit found and reproduced: an old waiver written for a prior version silently cleared a check for a new one. Write the field yourself; the gate will not infer it.

**A waiver is a founder decision, so it

Read more
Ships withapp-dev-team

Describe your app idea in one line. Get a shipped iOS & Android app. AI App Studio is a team of 30 AI specialists — a CEO, product manager, designers, iOS/Android engineers, a code reviewer, QA, and a release manager — that works like a real software studio.

Get the whole plugin

Other commands on app-dev-team.