Skip to content
Development
Command

/app-build

Run the sprint — launch developer agents in parallel, review each PR, gate to QA, surface bugs back into the loop

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-build

Context preview

What this command does when you run it.

Run the sprint — launch developer agents in parallel, review each PR, gate to QA, surface bugs back into the loop

Command definition

app-build.md
description: Run the sprint — launch developer agents in parallel, review each PR, gate to QA, surface bugs back into the loop
argument-hint: [ticket IDs, optional — defaults to all ready tickets]
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, Agent

/app-build — Execute the sprint

Tickets (optional, default = all ready): $ARGUMENTS

Before spawning any ticket owner, run the unified dispatch gate:

node "${CLAUDE_PLUGIN_ROOT}/scripts/dispatch-preflight.mjs" \
  --root <project> --ticket <ID> --context "$CONTEXT_MANIFEST" --schedule "$SCHEDULE_PLAN" \
  --capability "$CAPABILITY_MANIFEST" --risk "$RISK_POLICY" \
  --role <role> --operation write --path <changed-path> --file <changed-file> --change <summary>

This composes context freshness, scheduler admission, capability allowlisting, and blast-radius routing. `--ticket` is checked against the scheduler's own `ready` set — a ticket the scheduler has not marked ready fails preflight even if every other check would have passed, so a caller cannot launch on its own belief that a ticket is ready. A failed or unavailable check stops the spawn and is reported to the manager.

How this loop writes the board

Every status change below is an append to `docs/31-board-events.jsonl` through one command:

node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" move <ID> <event> --by <role> [--detail "..."]

`docs/31-board.md` is regenerated from that log on every append. **Never edit a cell** — the next render erases it and no rule in this plugin ever saw it. The loop's steps map to events exactly:

| Step | Situation | Event | |---|---|---| | 2 | a developer picks the ticket up | `claimed` | | 3 | developer returns `DONE: APP-NNN` | `done_reported` | | 3 | `verify-done.sh` VERIFIED / REJECTED | `verified` / `rejected` | | 3 | routed to review | `review_requested` (`--detail "-> code-reviewer"`) | | 3 | reviewer begins | `started` | | 4 | `APPROVED` / `REQUEST CHANGES` | `approved` / `changes` | | 4 | merge gate clears | `merged` | | 5 | QA verdict | `qa_passed` / `qa_failed`, then `closed` | | any | stopped, for any reason | `blocked`, later `unblocked` |

**Exit `1` is a refusal, and a refusal is a finding.** The CLI rejects the transition and prints why and what is legal from here. Do not retry it, do not work around it, and do not touch the Markdown: surface the refusal verbatim in the standup and fix the thing it names. Each refusal corresponds to a state this board could previously be written into and the doctor could only report afterwards — a review requested on an unverified DONE, an owner approving their own work, a merge with no non-owner approval, a claim on a dependency that never merged. Exit `2` means the log is missing or unreadable: **that is CANNOT EVALUATE, not an empty board.** Stop the round.

Steps

0. **Run every mechanical precondition as ONE command, before spawning anything.**

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

This replaces four separate steps that used to be four separate paragraphs: the **board doctor** gate, the **budget** ceiling, the **toolchain** check, and printing **what is legal now**. Every one of them was already a script with its own exit code. What was prose was the discipline of running all four, in order, every round — and that is exactly the kind of discipline this codebase has repeatedly proven does not survive contact with a long file. Dry run 3's skipped transition was not ignorance; it was load.

It is READ-ONLY. It claims nothing, advances nothing, appends nothing.

  • Exit `0` → every precondition CLEAR, and it prints the legal transitions per ticket. Continue.
  • Exit `1` → **BLOCKED. Spawn nobody this round.** It names which precondition and why: an

incoherent board (repair it with `tech-manager`, then re-run), a reached ceiling or the emergency stop (raising a ceiling is the user's decision, never a workaround you apply to keep going), or a declared tool at the wrong version.

  • Exit `2` → **CANNOT EVALUATE. Also not a pass.** Usually no board yet (`/app-plan` first), or

no `docs/team/project-profile.json` so the toolchain is UNPROVEN. Supply what it names.

**If a project has no event log yet but does have a hand-written `docs/31-board.md`**, migrate once before this step, then continue as normal:

   node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" migrate docs/31-board.md --out docs/31-board-events.jsonl \
     && node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" render

Announce how much it reconstructed as `inferred` (`ts: null`) — those are events the hand-written board never recorded, so `/app-status` metrics will be thin for tickets that predate the log. If migrate exits non-zero the board is too old to reconstruct: say `LEGACY BOARD: no event log`, use `board-doctor` as the authority for the rest of the run, and do not strand the project over it.

**The `GATE` lines in its output are the ones with a closed role list.** Those are the steps that must not be routed around, and `orchestrator explain <TICKET>` says why any given transition is refused — including the ones you expected to be legal, which is the more useful half.

1. **Read state.**

  • `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" show --json` — the derived state of every

ticket, from the log. Find tickets where `status` is `todo` and every `dependsOn` ID is **merged** — that is, `qa` **or** `done`. (On the legacy path, read `docs/31-board.md` directly; the readiness rule is identical.)

You do not have to get this exactly right, and that is the point: `claimed` is **refused** on a ticket whose dependency has no `merged` event, so a mis-read of readiness costs a refusal rather than a developer working on sand.

A dependency is satisfied when its code is on the integration branch, not when QA has finished with 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.