Slash commands — `/plan` `/sync-api` `/review` `/ship`
The workspace root's .claude/commands/ holds four slash commands. This note covers the operational flow of why each one was created and when it gets used.
.claude/commands/
├── plan.md # cross-directory implementation plan
├── sync-api.md # BFF ↔ mobile shared API contract consistency check
├── review.md # change review (per-directory dispatch)
└── ship.md # release gate (full build/test + consistency)
The flow sits naturally on a single line:
design → implement → contract check → review → ship
/plan (bff-dev/kmp-dev) /sync-api /review /ship
/plan <feature> — design from the BFF first
.claude/commands/plan.md
When cross-directory work comes in — e.g. "reflect BGM context in automatic subtitle generation" — designing BFF, shared, and cmp simultaneously in a single agent's head always leaves one side underbaked. So the command forces six explicit phases:
- Impact scope (BFF route? shared DTO? cmp UI? iOS adapter?)
- Design from the BFF first — the BFF is the contract's source of truth
- Map mobile shared changes (
commonMainfirst → expect/actual only where required) - Map mobile cmp UI changes (domain calls go only through
:shared) - Verification gates (per-directory build/test +
/sync-api) - Risk check (BFF compatibility, Windows paths, signed URLs, BFF_BASE_URL env)
The command's value: details like BFF route signatures / @SerialName policy / mobile BuildConfig env vars get raised in the same slot, in the same phrasing, across every plan. The cost of asking the same item differently each time disappears.
/sync-api — BFF ↔ mobile contract consistency
.claude/commands/sync-api.md
The BFF's routes/DTOs and mobile shared's BffApi/DTOs aren't linked at compile time — the two directories have separate gradle builds, so when a BFF DTO changes, mobile only finds out at runtime.
/sync-api is the verification command that bridges that gap. It cross-checks five items:
- Endpoint coverage — BFF's
/api/v2/*lines up 1:1 with mobileBffApimethods - DTO field consistency —
@SerialName, types, nullability - Multipart part names — keys like
video_0/audio_0/image_0/configmatch character for character - HTTP status codes — BFF
ErrorHandlingmapping ↔ mobileexpectSuccess = trueResponseExceptionhandling - Domain interface consistency —
AudioSeparationRepositoryand friends correspond logically to BFF endpoints
When to run it:
- Before merging a BFF PR (the enforcement point for the "BFF is source of truth" policy)
- Verification gate 5 in
/plan - Item 4 in
/ship
Why this is needed: after a case where mobile build passed + BFF build passed → real-device integration surfaced a runtime error like Field 'foo_id' is required, but it was missing. Gaps not caught at build time have to be hoisted into an explicit verification step.
/review — per-directory dispatch
.claude/commands/review.md
Review itself has different angles per directory — BFF prioritizes security/efficiency/error mapping, KMP/CMP prioritizes commonMain pollution, expect/actual consistency, and JVM-only library leakage.
So /review doesn't inspect directly — it looks at the change scope and dispatches to the per-directory review skill:
vibi-bffchanges:vibi-bff/.claude/skills/review.md(4 angles + severity labels)vibi-mobilechanges: BFF checklist with KMP angles added- Changes in both: the above two + cross-API contract check (including a
/sync-apicall)
/review's value: it auto-invokes the right checklist based on directory location — a human doesn't have to judge "this PR is BFF-only, so focus on security" / "this one touches mobile too, so also expect/actual" every time.
/ship — release gate
.claude/commands/ship.md
Bundles the build command + tests + /sync-api consistency into one pass. Five phases:
vibi-bff—./gradlew test+compileKotlin+ env verificationvibi-mobile (:shared)—:shared:testDebugUnitTest+:shared:build+ iOS framework linkvibi-mobile (:cmp)—:cmp:assembleDebug+ iOS simulator build- Cross API contract —
/sync-apimismatches at 0 - Risk check —
BFF_BASE_URLproduction address, iOSInfo.plistpermissions, Android permissions, etc.
/ship itself doesn't run the builds — per the in-memory policy (operating-rules.md), gradle/xcodebuild/simctl auto-execution is disallowed. The user takes the command, a human triggers it, and Claude grades the output against the checklist.
What the commands have in common
- They all end with a checklist + output format. If the output is free-form, every run looks different and can't be compared.
- One command calls another —
/shipcalls/sync-api, and/reviewcalls/sync-apiwhen needed. The commands are small building blocks. - The "BFF is source of truth" policy is the baseline running through all of them.