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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +24 -15
- package/agents/mobile-release-engineer.md +1 -1
- package/bin/reis-mobile.mjs +24 -1
- package/commands/reis-mobile.md +7 -2
- package/commands/release.md +46 -0
- package/core/release/context.mjs +11 -0
- package/package.json +1 -1
- package/skills/mobile-release/SKILL.md +9 -21
- package/skills/mobile-release/references/readiness.md +88 -0
- package/skills/mobile-release/scripts/verdict.mjs +59 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "AI agents for mobile engineering",
|
|
8
|
-
"version": "0.7.
|
|
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.
|
|
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.
|
|
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.
|
|
47
|
+
### v0.7.3
|
|
48
48
|
|
|
49
49
|
#### Added
|
|
50
50
|
|
|
51
|
-
-
|
|
52
|
-
-
|
|
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` |
|
|
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 `
|
|
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
|
-
|
|
|
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
|
|
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.
|
package/bin/reis-mobile.mjs
CHANGED
|
@@ -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];
|
package/commands/reis-mobile.md
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
32
|
-
- [ ] The build number
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|