reis-mobile 0.7.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "AI agents for mobile engineering",
8
- "version": "0.7.2"
8
+ "version": "0.7.3"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "reis-mobile",
13
13
  "source": "./",
14
14
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development.",
15
- "version": "0.7.2",
15
+ "version": "0.7.3",
16
16
  "author": {
17
17
  "name": "Warleson Reis"
18
18
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reis-mobile",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development. Use /reis-mobile:doctor, /reis-mobile:review and /reis-mobile:test.",
5
5
  "author": {
6
6
  "name": "Warleson Reis"
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The pr
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.7.3] - 2026-09-21
8
+
9
+ ### Added
10
+
11
+ - `/reis-mobile:release [request]` and entry-command forwarding audit mobile store readiness with per-target identity, pass/fail/unknown evidence gates and GO/GO WITH RISKS/NO-GO verdicts. A packaged helper rejects incomplete evidence; audits remain read-only by default.
12
+ - `reis-mobile release` prepares routing, redacted git context, diagnostics and release references for all five stacks, including both native artifacts in cross-platform apps. A public Flutter sample audit demonstrates the report and external verification limits.
13
+
7
14
  ## [0.7.2] - 2026-09-18
8
15
 
9
16
  ### Added
package/README.md CHANGED
@@ -44,20 +44,12 @@ A mobile app is not a generic project. A code review that does not know about `B
44
44
  ## What's new
45
45
 
46
46
  <!-- reis-mobile:latest-release:start -->
47
- ### v0.7.2
47
+ ### v0.7.3
48
48
 
49
49
  #### Added
50
50
 
51
- - Kotlin Multiplatform architect, test and performance specialists, a `mobile-kmp` platform skill and focused KMP references in the eight cross-stack topic skills. Routing and coverage tests guard the new paths.
52
- - `/reis-mobile:project [feature]` creates or refreshes a verified app object map in `.reis-mobile/project.md` while preserving team-authored product context.
53
-
54
- #### Changed
55
-
56
- - Specialist guidance distinguishes configured shared source sets, Android/iOS host behavior and target-specific validation; the shared brief verifies profile paths and separates team knowledge from code evidence.
57
-
58
- #### Fixed
59
-
60
- - Release automation opens a pull request for synchronized files instead of pushing directly to a protected `main` branch, and waits for those changes to merge before tagging and publishing.
51
+ - `/reis-mobile:release [request]` and entry-command forwarding audit mobile store readiness with per-target identity, pass/fail/unknown evidence gates and GO/GO WITH RISKS/NO-GO verdicts. A packaged helper rejects incomplete evidence; audits remain read-only by default.
52
+ - `reis-mobile release` prepares routing, redacted git context, diagnostics and release references for all five stacks, including both native artifacts in cross-platform apps. A public Flutter sample audit demonstrates the report and external verification limits.
61
53
 
62
54
  <!-- reis-mobile:latest-release:end -->
63
55
 
@@ -230,7 +222,7 @@ After editing agents, skills or commands, restart Claude Code or Codex.
230
222
  ## Commands
231
223
 
232
224
  ```bash
233
- /reis-mobile # Lists the commands (also accepts doctor, project, review, debug, test, debate or a free-form request)
225
+ /reis-mobile # Lists the commands (also accepts doctor, project, review, debug, test, release, debate or a free-form request)
234
226
  /reis-mobile my android build fails on gradle # Free-form request: detects the stack and applies the right agent and skills
235
227
  /reis-mobile:doctor # Environment and project: SDKs, Xcode, CocoaPods, Gradle wrapper, lock files
236
228
  /reis-mobile:doctor --all # Checks every tool, not only those for the detected stack
@@ -242,6 +234,7 @@ After editing agents, skills or commands, restart Claude Code or Codex.
242
234
  /reis-mobile:test run the checkout unit tests # Uses the existing runner, module or scheme
243
235
  /reis-mobile:test add XCTest coverage for cancellation # Tests the actual Swift type and its dependency seams
244
236
  /reis-mobile:test audit Espresso checkout tests # Reviews assertions and synchronization without editing
237
+ /reis-mobile:release audit both stores for the production variant # Evidence-backed readiness, read-only
245
238
  /reis-mobile:debate Riverpod or BLoC in this app? # Debate between the specialists, with a decision by lead-mobile
246
239
  /reis-mobile:debate --rounds 3 --external ... # More rounds; --external adds Codex and Gemini, if installed
247
240
  ```
@@ -267,6 +260,22 @@ reis-mobile test --base main -- "write regression tests for these changes"
267
260
 
268
261
  Its output includes the resolved project directory, explicit `test` intent, selected specialist and skills, redacted change context, toolchain diagnostics and answer language. `/reis-mobile test ...` forwards to the same slash-command workflow.
269
262
 
263
+ ### Release readiness
264
+
265
+ `/reis-mobile:release [request]` audits the actual app artifacts for Flutter, native Android/iOS, React Native/Expo and Kotlin Multiplatform. `/reis-mobile release [request]` forwards to the same workflow. It resolves the app ID, flavor/scheme, environment, version/build, source revision, store/track and binary before evaluating readiness. Apps targeting both stores receive separate Android and iOS checks.
266
+
267
+ The report lists each required gate as **pass**, **fail** or **unknown**, with evidence and next actions: identity, build, tests, analysis, version, signing, symbols, current store requirements, privacy, listing/localization and rollout/recovery. Any failed or unknown required gate means **NO-GO**. All gates passing means **GO**, or **GO WITH RISKS** when documented non-blocking risks remain. A packaged verdict helper enforces this decision rule; the specialist must still verify the evidence itself.
268
+
269
+ The audit inspects existing files, artifacts and accessible CI/store records. It does not build, run tests, bump versions, tag, upload or change store settings by default. Missing CI results, upload history or portal confirmations remain unknown; local configuration is not proof that a check passed. Explicit execution requests retain their requested scope and prior authorization.
270
+
271
+ ```bash
272
+ reis-mobile release --dir apps/mobile --json -- "audit Android production and iOS App Store"
273
+ ```
274
+
275
+ The CLI only prepares routing, redacted git context, diagnostics and release reference paths. Its JSON includes `release.platforms` (candidate targets), `release.references`, `release.evidenceStatus: "unverified"` and `release.verdict: null`. Exit 0 does not approve a release. In Codex, ask it to run the CLI, read the returned instructions and follow `mobile-release`'s readiness contract; slash-command parity is not assumed.
276
+
277
+ See the [readiness contract](skills/mobile-release/references/readiness.md) and [public Flutter sample audit](docs/examples/release-readiness.md) for the report format and evidence limits.
278
+
270
279
  ### Project context
271
280
 
272
281
  `/reis-mobile:project [feature]` resolves the mobile app, traces real entry points, state owners, data or native boundaries and nearby tests, then creates or refreshes `.reis-mobile/project.md`. In a monorepo it writes beside `.reis-mobile/config.yaml`; otherwise it writes in the app root. The generated section contains only facts tied to code or configuration and identifies unknown product rules. Team-authored sections remain intact on refresh. Agents check its paths and commands against the current checkout before using them.
@@ -320,7 +329,7 @@ The router never invents a specialist: if no agent serves the intent, it warns y
320
329
  | Full audit with several specialists | agent `lead-mobile` | ✅ |
321
330
  | Decide between two architectures with a real trade-off | `/reis-mobile:debate` | ✅ |
322
331
  | Dedicated test command (including XCTest and Espresso) | `/reis-mobile:test` | ✅ |
323
- | Check whether the app is ready for the store | `/reis-mobile:release` | 🔜 v0.8 |
332
+ | Check whether the app is ready for the store with evidence per target | `/reis-mobile:release` | ✅ |
324
333
  | Consolidated decision by consensus between agents | `/reis-mobile:council` | 🔜 v0.10 |
325
334
 
326
335
  <details>
@@ -428,7 +437,7 @@ About 85 guides come from [flutter/skills](https://github.com/flutter/skills), [
428
437
 
429
438
  ## Trust, security and limits
430
439
 
431
- **Read-only CLI context.** `doctor`, `detect`, `route`, `review` and `test` do not modify your project. The `/reis-mobile:test` slash command can write or fix tests and execute runners when requested. `/reis-mobile:project` writes only the optional project profile. The `/reis-mobile:review` and `/reis-mobile:debate` commands instruct the model not to edit app code; the debate writes its rounds to `.reis-mobile/debates/`.
440
+ **Read-only CLI context.** `doctor`, `detect`, `route`, `review`, `test` and `release` do not modify your project. `/reis-mobile:release` inspects existing release evidence and remains read-only unless execution is explicitly requested. The `/reis-mobile:test` slash command can write or fix tests and execute runners when requested. `/reis-mobile:project` writes only the optional project profile. The `/reis-mobile:review` and `/reis-mobile:debate` commands instruct the model not to edit app code; the debate writes its rounds to `.reis-mobile/debates/`.
432
441
 
433
442
  **Secrets masked.** The diff goes through [`core/security/redact.mjs`](core/security/redact.mjs) before reaching the model. Lock files and generated code (`*.g.dart`, `*.freezed.dart`, `*.pbxproj`) stay out of the diff. Redaction is a protection layer, not a guarantee. See [SECURITY.md](SECURITY.md).
434
443
 
@@ -463,7 +472,7 @@ About 85 guides come from [flutter/skills](https://github.com/flutter/skills), [
463
472
  | v0.6.0 | Accessibility auditor and release engineer agents, `mobile-accessibility` and `mobile-release` skills, ANR, memory leak and deep link debugging guides | ✅ |
464
473
  | v0.7.0 | `/reis-mobile:test`, native tests (XCTest, Espresso) and project-specific specialist contracts | ✅ |
465
474
  | v0.7.1 | Update notices, single-source version and release automation | ✅ |
466
- | v0.8.0 | `/reis-mobile:release` with quality gates | ⏳ |
475
+ | Unreleased | `/reis-mobile:release` with per-target evidence, required gates and deterministic readiness verdicts (previously planned for v0.8.0) | ✅ |
467
476
  | v0.9.0 | MCP server | ⏳ |
468
477
  | v0.10.0 | Multi-provider (OpenAI, Gemini, OpenRouter, Ollama) beyond the debate's `--external` | ⏳ |
469
478
  | v1.0.0 | First stable version: Flutter, Android, iOS and React Native | ⏳ |
@@ -21,7 +21,7 @@ Use the [project brief](../docs/agent-context.md) once or reuse the supplied bri
21
21
 
22
22
  ## Produce the requested artifact
23
23
 
24
- - **Readiness:** apply the skill checklist to this release identity. Each gate is pass/fail/unknown with evidence and the responsible owner or next check. Verify current store requirements from official sources; local code cannot prove console approval or highest uploaded build number.
24
+ - **Readiness:** apply the [readiness contract](../skills/mobile-release/references/readiness.md) and its verdict helper to this release identity. Unknown or failed required gates block readiness; evaluate each intended native artifact separately. Each gate is pass/fail/unknown with evidence and the responsible owner or next check. Verify current store requirements from official sources; local code cannot prove console approval or highest uploaded build number.
25
25
  - **Release notes:** establish the actual prior/current commit range. Map each user-facing change to commits/PRs, group by the app's feature names, preserve listing locales and check store limits from the reference. Unsupported claims stay out of the notes.
26
26
  - **Pipeline:** trace the existing jobs and artifact flow before proposing changes. Specify inputs, output artifact paths, secret names, build matrix and the dependency that prevents upload after failed checks. Keep build, signing and publishing steps independently identifiable.
27
27
  - **Rollout incident:** tie crash/ANR or adoption evidence to the affected build/cohort. Name the available halt, server flag or higher-build hotfix path from this app's deployment model, and state who remains exposed after a halt.
@@ -11,6 +11,7 @@ import { runDoctor } from '../core/diagnostics/doctor.mjs';
11
11
  import { runInit } from '../core/install/init.mjs';
12
12
  import { PLUGIN_ROOT } from '../core/paths.mjs';
13
13
  import { loadRegistry, validateRegistry } from '../core/registry/registry.mjs';
14
+ import { releaseContext } from '../core/release/context.mjs';
14
15
  import { INTENT_IDS } from '../core/router/intents.mjs';
15
16
  import { route } from '../core/router/router.mjs';
16
17
  import { checkForUpdate, clearUpdateCache } from '../core/update/check.mjs';
@@ -28,6 +29,7 @@ Commands:
28
29
  review [prompt...] Route a code review and collect the git context for it
29
30
  debug [problem...] Route a build or runtime failure and report the toolchain it runs on
30
31
  test [request...] Prepare test routing, git context and toolchain checks (does not run tests)
32
+ release [request...] Prepare release evidence context (does not build or publish)
31
33
  agents | skills | stacks List registered components
32
34
  validate Validate the plugin's agents, skills and stacks
33
35
  update-check Check npm for a newer reis-mobile release
@@ -72,6 +74,7 @@ const COMMANDS = {
72
74
  review: commandReview,
73
75
  debug: commandDebug,
74
76
  test: commandTest,
77
+ release: commandRelease,
75
78
  agents: (options) => commandList('agents', options),
76
79
  skills: (options) => commandList('skills', options),
77
80
  stacks: (options) => commandList('stacks', options),
@@ -106,7 +109,7 @@ async function main(argv) {
106
109
  return (await command({ ...options, dir: project.projectDir, project, prompt: rest.join(' ') })) ?? 0;
107
110
  }
108
111
 
109
- const PROJECT_COMMANDS = new Set(['detect', 'doctor', 'route', 'review', 'debug', 'test']);
112
+ const PROJECT_COMMANDS = new Set(['detect', 'doctor', 'route', 'review', 'debug', 'test', 'release']);
110
113
 
111
114
  async function commandUpdateCheck({ force, json }) {
112
115
  const result = await checkForUpdate({ force });
@@ -277,6 +280,26 @@ async function commandTest({ dir, project, base, prompt, json, lang }) {
277
280
  return 0;
278
281
  }
279
282
 
283
+ /** Prepare release evidence; the specialist audits it before assigning a verdict. */
284
+ async function commandRelease({ dir, project, prompt, json, lang }) {
285
+ const result = await route({ prompt, intent: 'release', projectDir: dir });
286
+ const context = collectReviewContext({ projectDir: dir });
287
+ const doctor = await runDoctor({ projectDir: dir });
288
+ const language = await resolveLanguage({ flag: lang });
289
+ const release = releaseContext(result.detection);
290
+ if (json) return print({ ...summarizeRoute(result), language, projectConfig: project.config?.path ?? null, context, doctor, release });
291
+
292
+ printProject(project, true);
293
+ printRoute(result);
294
+ printLanguage(language);
295
+ printContext(context);
296
+ console.log(`\nRelease platforms (verify) ${release.platforms.join(', ') || 'unknown'}`);
297
+ for (const reference of release.references) console.log(` Reference ${reference}`);
298
+ console.log('\nRelease context prepared; readiness has not been evaluated. No builds, tests or uploads were executed.\n');
299
+ printDoctorChecks(doctor);
300
+ return 0;
301
+ }
302
+
280
303
  async function commandList(kind, { json }) {
281
304
  const registry = await loadRegistry();
282
305
  const items = registry[kind];
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: reis-mobile entry point — lists the commands or forwards to doctor, project, review, debug, test, debate or the right agent from a free-form request
3
- argument-hint: "[doctor | project | review | debug | test | debate | free-form request]"
2
+ description: reis-mobile entry point — lists the commands or forwards to doctor, project, review, debug, test, release, debate or the right agent from a free-form request
3
+ argument-hint: "[doctor | project | review | debug | test | release | debate | free-form request]"
4
4
  allowed-tools: ["Bash(node:*)", "Bash(git:*)", "Read", "Grep", "Glob", "Edit", "Write"]
5
5
  ---
6
6
 
@@ -24,6 +24,7 @@ Reply only with this list. The only thing to run is `node "${CLAUDE_PLUGIN_ROOT}
24
24
  /reis-mobile:review [--base <ref>] Code review of the changes (or the whole project)
25
25
  /reis-mobile:debug <problem> Finds the cause of a build or runtime failure and proposes the fix
26
26
  /reis-mobile:test [request] Runs, writes, audits or fixes tests using the project's test setup
27
+ /reis-mobile:release [request] Audits store readiness with evidence and a go/no-go verdict
27
28
  /reis-mobile:debate <question> Debate between the specialists, with a decision by lead-mobile
28
29
  /reis-mobile <free-form request> Forwards the request to the agent and skills for the detected stack
29
30
  ```
@@ -55,6 +56,10 @@ Read `${CLAUDE_PLUGIN_ROOT}/commands/debate.md` and follow all of it, treating t
55
56
 
56
57
  Read `${CLAUDE_PLUGIN_ROOT}/commands/test.md` and follow all of it, treating the rest of the arguments (without the word `test`) as `$ARGUMENTS`. This entry point has the same behavior as `/reis-mobile:test`.
57
58
 
59
+ ## `release [request]`
60
+
61
+ Read `${CLAUDE_PLUGIN_ROOT}/commands/release.md` and follow all of it, treating the rest of the arguments (without the word `release`) as `$ARGUMENTS`. This entry point has the same behavior as `/reis-mobile:release`.
62
+
58
63
  ## Any other text
59
64
 
60
65
  It is a free-form request, in English or Portuguese.
@@ -0,0 +1,46 @@
1
+ ---
2
+ description: Audit mobile release readiness with per-target evidence, store quality gates and a GO/GO WITH RISKS/NO-GO verdict
3
+ argument-hint: "[request, version, variant or target store/track]"
4
+ allowed-tools: ["Bash(node:*)", "Bash(git:*)", "Read", "Grep", "Glob", "WebFetch", "WebSearch"]
5
+ ---
6
+
7
+ # reis-mobile release
8
+
9
+ Arguments received: `$ARGUMENTS`
10
+
11
+ ## 1. Prepare context
12
+
13
+ Run:
14
+
15
+ ```bash
16
+ node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" release --dir "$PWD" -- "<request>"
17
+ ```
18
+
19
+ Pass the request as a single literal argument after `--`, or omit it when empty. Use proper shell escaping: double quotes alone do not protect `$()`, backticks or embedded quotes. Never evaluate the request as shell code. The CLI forces `release` intent even if the request mentions a build failure. Stop and show the error if context preparation fails. Exit 0 means context preparation succeeded, not that a release is ready.
20
+
21
+ Use `Project` (`detection.projectDir` in JSON) as the app root and `context.repoRoot` for git-relative paths. The CLI lists candidate release platforms and reference paths (`release.platforms` and `release.references`); they are unverified inventory, not a verdict. Inspect untracked files and required files omitted by diff truncation. The git diff alone is not release evidence.
22
+
23
+ If the resolved directory does not exist or `detection.stack` is `unknown`, report NO-GO with unresolved identity/targets and the exact app path needed. Do not use a stack inferred from the prompt as evidence of an app on disk. Feed an empty `targets` array to the verdict helper rather than inventing artifacts.
24
+
25
+ ## 2. Bind the release to the app
26
+
27
+ Read in order:
28
+
29
+ 1. `${CLAUDE_PLUGIN_ROOT}/docs/agent-context.md`; build or reuse the Project brief from the resolved app root.
30
+ 2. `${CLAUDE_PLUGIN_ROOT}/agents/<Agent>.md`, normally `mobile-release-engineer`.
31
+ 3. `${CLAUDE_PLUGIN_ROOT}/skills/<skill>/SKILL.md` for each routed skill, including `mobile-release`.
32
+ 4. `${CLAUDE_PLUGIN_ROOT}/skills/mobile-release/references/readiness.md` and each relevant release reference from the CLI output.
33
+
34
+ Apply the selected role directly, or pass the existing brief to that specialist when delegation is available and appropriate. Do not repeat project discovery. Resolve the actual app ID, flavor/scheme/environment, version/build, source revision, store/track and built artifact for each target. For every field, record its source and distinguish configured values from values verified in the built artifact. Without an explicit request, inventory configured mobile targets and existing artifacts; leave ambiguous destinations and missing artifacts unknown.
35
+
36
+ Flutter, React Native/Expo and Kotlin Multiplatform shipping to Android and iOS require both native release references and separate artifact checks. Confirm hosts/targets in the build configuration; a KMP framework build is not an iOS archive. Honor an explicitly limited store scope and name exclusions. Native folders generated by Expo need configuration inspection, not prebuild during the audit.
37
+
38
+ ## 3. Audit, evaluate and report
39
+
40
+ Follow the required gates, evidence rules, verdict helper and report format in `readiness.md`. Reconcile local facts with existing artifact metadata, actual CI results and current official store requirements. Do not claim a store or CI check passed from local configuration alone. If external information is unavailable, identify the exact confirmation needed.
41
+
42
+ Run `${CLAUDE_PLUGIN_ROOT}/skills/mobile-release/scripts/verdict.mjs` with the actual per-target gates on stdin as described in the reference. Missing evidence must remain unknown. Report its GO, GO WITH RISKS or NO-GO verdict with release identity, gate evidence, blockers, risks, unknowns, next actions and rollout/rollback. Its exit status is not a readiness verdict.
43
+
44
+ The audit is read-only by default: no builds/tests, version bump, tag, upload, signing change or store-setting change. An explicit release execution request follows the selected agent's scope rules and prior authorization; never execute a publishing lane just because it exists. Distinguish commands inspected/prepared from those actually executed.
45
+
46
+ Write user-facing prose in the CLI's `Language`: `en` is English, `pt` is Brazilian Portuguese; with `-` or `null`, follow the user's language. Keep paths, gate IDs, verdicts and quoted output unchanged.
@@ -0,0 +1,11 @@
1
+ import { join } from 'node:path';
2
+
3
+ import { PLUGIN_ROOT } from '../paths.mjs';
4
+
5
+ /** Detection suggests references, never proves store targets or release readiness. */
6
+ export function releaseContext(detection) {
7
+ const platforms = detection.platforms.filter((platform) => ['android', 'ios'].includes(platform));
8
+ const references = detection.stack === 'unknown' ? [] : [...new Set([detection.stack, ...platforms])]
9
+ .map((stack) => join(PLUGIN_ROOT, 'skills/mobile-release/references', `${stack}.md`));
10
+ return { platforms, references, evidenceStatus: 'unverified', verdict: null };
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reis-mobile",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "AI agents and skills for Flutter, Android, iOS, React Native and Kotlin Multiplatform — a Claude Code and Codex plugin for reviews, debugging, tests, architecture, security and Firebase.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -13,6 +13,7 @@ One release process for every mobile stack. This file holds the process and what
13
13
 
14
14
  | Need | Read |
15
15
  |---|---|
16
+ | Evidence-backed readiness audit, verdict and report | [references/readiness.md](references/readiness.md) |
16
17
  | Flutter app | [references/flutter.md](references/flutter.md) |
17
18
  | Native Android, or the Android build of any app | [references/android.md](references/android.md) |
18
19
  | Native iOS, or the iOS build of any app | [references/ios.md](references/ios.md) |
@@ -25,11 +26,13 @@ Flutter and React Native apps also need the Android and iOS references for signi
25
26
 
26
27
  ## 2. Readiness checklist
27
28
 
28
- Check each item against the project, not against memory. Mark each one **pass**, **fail** (with `file:line` or the missing artifact) or **unknown** (needs information only the team has, such as the store console state).
29
+ For readiness audits, first follow [the readiness contract](references/readiness.md): resolve each artifact identity, collect per-target evidence and apply its required gates and deterministic verdict helper.
30
+
31
+ Check each item against the project, not against memory. For the readiness verdict, a missing artifact or unavailable check is **unknown**; use **fail** only for an observed failure with evidence. Record a source reference and next action for every gate.
29
32
 
30
33
  **Version**
31
- - [ ] The version name follows the project's scheme and changed since the last release tag (`git describe --tags --abbrev=0`).
32
- - [ ] The build number is higher than any build already uploaded. Both stores reject a build number they have seen.
34
+ - [ ] The version name follows the app's actual versioning scheme and is verified in the artifact and applicable CI overrides.
35
+ - [ ] The build number satisfies the destination store’s numbering scope, verified against upload history: Android versionCode increases across tracks; iOS build numbers must be unused within the applicable version train. See the native references.
33
36
  - [ ] Android, iOS and any cross-platform manifest (`pubspec.yaml`, `app.json`) agree on the version.
34
37
 
35
38
  **Build quality**
@@ -47,25 +50,10 @@ Check each item against the project, not against memory. Mark each one **pass**,
47
50
  **Rollout**
48
51
  - [ ] Staged rollout (Play) or phased release (App Store) planned for updates, with the crash-free and ANR metrics to watch and the threshold that halts it.
49
52
  - [ ] Rollback plan: stores do not roll back a binary. Halting a rollout stops new installs; users who updated need a new build with a higher build number, a server-side kill switch or a remote config flag. Know which one applies before shipping.
50
- - [ ] The release commit is tagged and the tag matches the version.
53
+ - [ ] The release commit is tagged and the tag matches the version, when this is the project's release convention.
51
54
 
52
55
  ## 3. Report
53
56
 
54
- ```markdown
55
- ## Release readiness — <app> <version> (<build>)
56
- Verdict: GO | GO WITH RISKS | NO-GO
57
-
58
- ### Blockers
59
- - item — evidence (file:line or missing artifact) — fix
60
-
61
- ### Risks
62
- - item — why it matters — mitigation
63
-
64
- ### Unknown
65
- - item — who or what can confirm it
66
-
67
- ### Rollout plan
68
- Stages, metrics to watch, halt threshold, rollback path.
69
- ```
57
+ Use the [readiness report and decision rules](references/readiness.md#report). Any failed or unknown required gate means **NO-GO**. **GO WITH RISKS** requires all gates to pass with explicitly documented non-blocking risks; **GO** requires no outstanding risks.
70
58
 
71
- A single blocker makes the verdict NO-GO. Do not bump versions, create tags, upload builds or change store settings unless the user asks; propose the exact commands instead.
59
+ Readiness audits inspect existing evidence without running builds/tests or modifying the app. Version bumps, tags, uploads and store changes follow the user's explicit execution scope and existing authorization.
@@ -0,0 +1,88 @@
1
+ # Evidence-backed readiness audit
2
+
3
+ Use this contract for `/reis-mobile:release` and any readiness request. Other release tasks (store copy, pipeline implementation, deployment) retain their requested scope.
4
+
5
+ ## Release identity and target coverage
6
+
7
+ For every intended artifact, record **app/package or bundle ID → flavor/scheme/environment → version/build → source revision → store/track → built artifact** with evidence. Inspect build overrides, CI parameters and artifact metadata; distinguish configured values from values verified in the binary. A directory or configured KMP target does not prove a shippable host exists. A shared framework is not an iOS app archive.
8
+
9
+ Use detection as an inventory, then confirm actual Android/iOS hosts and requested destinations. Flutter, React Native/Expo and KMP shipping to both stores need separate gate rows for both artifacts. A prompt focusing on Android does not silently approve the iOS artifact. If the user explicitly limits the audit to one store, state the exclusion and do not issue a combined verdict. Expo can generate native folders: inspect app configuration and EAS profiles without running prebuild. Unknown targets or missing binaries stay unknown.
10
+
11
+ Read the stack reference plus [android.md](android.md) and/or [ios.md](ios.md) for the actual destinations. KMP also needs [kotlin-multiplatform.md](kotlin-multiplatform.md). Do not substitute a shared-code test for a native host test.
12
+
13
+ ## Required gates
14
+
15
+ Each target has these gate IDs. Every gate is **pass**, **fail**, or **unknown**, with a source reference and an exact next action/owner when unresolved. A failed check needs observed failure evidence; unavailable evidence is unknown. A local config file proves configuration only, not an executed check, successful upload, current console state or store approval.
16
+
17
+ | ID | Evidence required |
18
+ |---|---|
19
+ | `identity` | All identity fields above, including artifact provenance and agreement with the intended source revision. Record dirty/untracked source and unknown CI overrides. |
20
+ | `build` | Successful release build log/CI job for this revision and artifact, with the actual variant and production environment. A workflow YAML or debug build is insufficient. |
21
+ | `tests` | Actual results for applicable unit/integration/device checks on this revision, with counts and skipped/failed checks. Resolve required suites from project policy and the changed behavior. |
22
+ | `analysis` | Actual lint/static-analysis results for the release revision and applicable targets. Missing tools or logs are unknown, not pass. |
23
+ | `version` | Artifact version/build matched to source/CI inputs and store upload history, applying the platform's numbering scope. Verify the previous release using the project's convention, not merely the closest tag. See [Android versioning](https://developer.android.com/studio/publish/versioning) and [Apple's build identification](https://developer.apple.com/help/app-store-connect/reference/app-information/app-information). |
24
+ | `signing` | Signed artifact verification, intended distribution identity, and applicable entitlements/profile or upload-key setup. Never read or print private keys, passwords or tokens. |
25
+ | `symbols` | Matching R8/native symbols, dSYMs, Dart debug info and/or JS source maps, with archive/upload evidence as required by the app. Verify build IDs/UUIDs or provenance. |
26
+ | `store` | Current official requirement URL and access date, target SDK/toolchain evidence, and any account/track eligibility or review state needed for the intended action. Unavailable portal confirmation stays unknown. |
27
+ | `privacy` | Permissions/data collection and third-party SDK inventory reconciled with manifests, usage descriptions, privacy policy and store disclosures. Local declarations alone do not prove console forms agree. |
28
+ | `listing` | Required locales, release notes/screenshots/metadata, and reviewer access where applicable. Account secrets never enter the report. |
29
+ | `rollout` | Owner, stages, monitoring signals and halt thresholds, plus a verified halt/kill-switch/higher-build hotfix path. Mark proposed thresholds as proposals until accepted. A halt does not revert installed binaries. See [Play staged rollouts](https://support.google.com/googleplay/android-developer/answer/6346149) and [Apple phased releases](https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases); confirm their current behavior before planning recovery. |
30
+
31
+ If a requirement within a gate does not apply (for example no R8 mapping when R8 is disabled), cite evidence and the reason; do not omit the gate. A gate can pass with a justified non-applicability statement only after verifying all of its applicable requirements. No gate is waived simply to obtain GO.
32
+
33
+ Current store rules must be checked against official sources, such as [Google Play target API requirements](https://support.google.com/googleplay/android-developer/answer/11926878) and [Apple's upcoming requirements](https://developer.apple.com/news/upcoming-requirements/). State the exact unresolved confirmation if browsing or store access is unavailable. Do not reuse a date or minimum SDK from memory.
34
+
35
+ ## Decision rule
36
+
37
+ - **NO-GO:** any required gate fails, is unknown, is missing, or has no supporting evidence. Missing targets also block. Distinguish observed failures from missing verification.
38
+ - **GO WITH RISKS:** every required gate passes and explicitly documented non-blocking risks remain. A risk cannot waive an unresolved required gate.
39
+ - **GO:** every required gate passes and there are no outstanding risks.
40
+
41
+ The combined verdict covers every included target; one platform passing cannot approve another.
42
+
43
+ Use [the verdict helper](../scripts/verdict.mjs) to check the report's decision. It validates completeness and consistency, **not the truth of the evidence or whether a risk is truly non-blocking**. Supply JSON on stdin using a quoted heredoc delimiter (no shell expansion):
44
+
45
+ ```bash
46
+ node "${CLAUDE_PLUGIN_ROOT}/skills/mobile-release/scripts/verdict.mjs" <<'REIS_RELEASE_EVIDENCE'
47
+ {"targets":[{"target":"android:production","gates":[{"id":"build","status":"unknown","evidence":[],"nextAction":"Release owner: provide the release CI job and artifact for the requested revision."}]}],"risks":[]}
48
+ REIS_RELEASE_EVIDENCE
49
+ ```
50
+
51
+ This intentionally incomplete example returns NO-GO. Replace it with the actual targets and all 11 gates; use one target name per artifact/store/track combination. `evidence` is an array of non-empty references (`file:line`, command plus result/log, artifact metadata, CI URL or dated portal confirmation). `nextAction` explains what resolves each fail/unknown. `risks` is an optional array of objects with `id`, `description`, evidence references, `mitigation` and `owner`; include only evidenced issues whose impact truly does not block the release. The auditor is responsible for that classification. Do not place secrets in JSON. Choose a heredoc delimiter absent from the payload. The helper writes only stdout; exit 0 means evaluation succeeded, **not** GO. Malformed input exits 1 and must be corrected before reporting a verdict. Preserve returned unknowns and blockers; do not manually upgrade the result.
52
+
53
+ ## Report
54
+
55
+ Write in the requested language; keep gate IDs and verdict identifiers stable.
56
+
57
+ ```markdown
58
+ ## Release readiness — <app> <version/build>
59
+ Verdict: GO | GO WITH RISKS | NO-GO
60
+ Scope: <included artifacts/stores/tracks and explicit exclusions>
61
+
62
+ ### Release identity
63
+ | Target | App ID | Variant/environment | Version/build | Revision | Store/track | Artifact | Evidence |
64
+ |---|---|---|---|---|---|---|---|
65
+
66
+ ### Gates
67
+ | Target | Gate | Status | Evidence / missing confirmation | Next action / owner |
68
+ |---|---|---|---|---|
69
+
70
+ ### Blockers
71
+ Observed failures and unverified required gates, separately identified.
72
+
73
+ ### Risks
74
+ Non-blocking risks, mitigation and owner.
75
+
76
+ ### Unknowns
77
+ Missing evidence and the exact external confirmation needed.
78
+
79
+ ### Next actions
80
+ Ordered resolution steps; commands prepared versus commands actually executed.
81
+
82
+ ### Rollout and rollback
83
+ Stages, signals, thresholds, owner and recovery for users already updated.
84
+ ```
85
+
86
+ ## Read-only boundary
87
+
88
+ A readiness audit inspects files, existing artifacts/logs and accessible CI/store records. Do not build, run tests, install dependencies, execute Fastlane lanes, prebuild, bump versions, tag, upload, change signing or store settings by default. Inspect scripts before considering execution. Prepare exact commands for missing checks and label them **not executed**. An explicit request to execute a release authorizes only that requested scope; reuse existing authorization, and complete reviewable preparation before any additional approval that is actually needed. Never change an app merely to make the audit green.
@@ -0,0 +1,59 @@
1
+ import { realpathSync } from 'node:fs';
2
+ import { pathToFileURL } from 'node:url';
3
+
4
+ const REQUIRED_GATES = ['identity', 'build', 'tests', 'analysis', 'version', 'signing', 'symbols', 'store', 'privacy', 'listing', 'rollout'];
5
+ const text = (value) => typeof value === 'string' && value.trim().length > 0;
6
+
7
+ /** Validate decision consistency. Evidence truth and applicability remain the auditor's job. */
8
+ export function evaluateReadiness(report) {
9
+ if (!report || !Array.isArray(report.targets)) throw new Error('targets must be an array');
10
+ const risks = report.risks ?? [];
11
+ if (!Array.isArray(risks) || !risks.every((risk) => risk && text(risk.id) && text(risk.description)
12
+ && Array.isArray(risk.evidence) && risk.evidence.length > 0 && risk.evidence.every(text)
13
+ && text(risk.mitigation) && text(risk.owner))) {
14
+ throw new Error('risks must include id, description, evidence, mitigation and owner');
15
+ }
16
+ const targets = new Set();
17
+ const gates = [];
18
+ for (const item of report.targets) {
19
+ if (!item || !text(item.target) || !Array.isArray(item.gates)) throw new Error('each target needs a name and gates array');
20
+ if (targets.has(item.target)) throw new Error(`duplicate target: ${item.target}`);
21
+ targets.add(item.target);
22
+ const byId = new Map();
23
+ for (const gate of item.gates) {
24
+ if (!gate || !REQUIRED_GATES.includes(gate.id)) throw new Error('unknown gate id');
25
+ if (byId.has(gate.id)) throw new Error(`duplicate gate: ${gate.id}`);
26
+ if (!['pass', 'fail', 'unknown'].includes(gate.status)) throw new Error(`invalid status for ${gate.id}`);
27
+ if (gate.evidence !== undefined && (!Array.isArray(gate.evidence) || !gate.evidence.every((value) => typeof value === 'string'))) {
28
+ throw new Error(`evidence for ${gate.id} must be a string array`);
29
+ }
30
+ byId.set(gate.id, gate);
31
+ }
32
+ for (const id of REQUIRED_GATES) {
33
+ const supplied = byId.get(id);
34
+ const evidence = (supplied?.evidence ?? []).filter(text);
35
+ const status = !supplied || (supplied.status !== 'unknown' && !evidence.length) ? 'unknown' : supplied.status;
36
+ gates.push({
37
+ target: item.target, id, status, evidence,
38
+ nextAction: status === 'pass' ? 'None.' : (text(supplied?.nextAction) && supplied.nextAction !== 'None.'
39
+ ? supplied.nextAction : `Provide and verify ${id} evidence for ${item.target}.`),
40
+ });
41
+ }
42
+ }
43
+ if (!targets.size) gates.push({ target: 'unresolved', id: 'targets', status: 'unknown', evidence: [], nextAction: 'Identify the app artifacts and intended store targets.' });
44
+ const blockers = gates.filter((gate) => gate.status !== 'pass');
45
+ const unknowns = gates.filter((gate) => gate.status === 'unknown');
46
+ return { verdict: blockers.length ? 'NO-GO' : risks.length ? 'GO WITH RISKS' : 'GO', gates, blockers, risks, unknowns };
47
+ }
48
+
49
+ // stdin avoids creating an audit file in the inspected project. Exit zero means evaluated, not GO.
50
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
51
+ try {
52
+ let input = '';
53
+ for await (const chunk of process.stdin) input += chunk;
54
+ console.log(JSON.stringify(evaluateReadiness(JSON.parse(input)), null, 2));
55
+ } catch (error) {
56
+ console.error(`readiness: ${error.message}`);
57
+ process.exitCode = 1;
58
+ }
59
+ }