reis-mobile 0.7.1 → 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 +22 -0
- package/README.md +54 -30
- package/agents/kmp-architect.md +26 -0
- package/agents/kmp-performance-engineer.md +24 -0
- package/agents/kmp-test-engineer.md +26 -0
- package/agents/lead-mobile.md +3 -0
- package/agents/mobile-release-engineer.md +1 -1
- package/bin/reis-mobile.mjs +24 -1
- package/commands/debate.md +4 -1
- package/commands/project.md +48 -0
- package/commands/reis-mobile.md +13 -3
- package/commands/release.md +46 -0
- package/core/registry/registry.mjs +1 -0
- package/core/release/context.mjs +11 -0
- package/docs/agent-context.md +2 -2
- package/package.json +3 -2
- package/skills/mobile-accessibility/SKILL.md +2 -1
- package/skills/mobile-accessibility/references/flutter.md +1 -1
- package/skills/mobile-accessibility/references/kotlin-multiplatform.md +9 -0
- package/skills/mobile-architecture/SKILL.md +2 -1
- package/skills/mobile-architecture/references/kotlin-multiplatform.md +11 -0
- package/skills/mobile-code-review/SKILL.md +2 -1
- package/skills/mobile-code-review/references/flutter/widget-review.md +1 -1
- package/skills/mobile-code-review/references/flutter.md +1 -1
- package/skills/mobile-code-review/references/kotlin-multiplatform.md +11 -0
- package/skills/mobile-debug/SKILL.md +2 -1
- package/skills/mobile-debug/references/kotlin-multiplatform.md +10 -0
- package/skills/mobile-firebase/SKILL.md +2 -1
- package/skills/mobile-firebase/references/kotlin-multiplatform.md +10 -0
- package/skills/mobile-flutter/SKILL.md +1 -1
- package/skills/mobile-kmp/SKILL.md +26 -0
- package/skills/mobile-kmp/references/apple-interop.md +9 -0
- package/skills/mobile-kmp/references/build-performance.md +9 -0
- package/skills/mobile-kmp/references/compose-ui.md +9 -0
- package/skills/mobile-kmp/references/source-sets.md +9 -0
- package/skills/mobile-release/SKILL.md +11 -22
- package/skills/mobile-release/references/kotlin-multiplatform.md +12 -0
- package/skills/mobile-release/references/readiness.md +88 -0
- package/skills/mobile-release/scripts/verdict.mjs +59 -0
- package/skills/mobile-security/SKILL.md +2 -1
- package/skills/mobile-security/references/kotlin-multiplatform.md +10 -0
- package/skills/mobile-test/SKILL.md +2 -1
- package/skills/mobile-test/references/kotlin-multiplatform.md +12 -0
|
@@ -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/docs/agent-context.md
CHANGED
|
@@ -6,7 +6,7 @@ Read this contract once per task. If the caller supplied a brief, reuse it and v
|
|
|
6
6
|
|
|
7
7
|
Use the CLI's resolved `Project` directory and stack/platform focus. Read repository instructions that apply to the files in scope, then relevant architecture docs, build manifests, lock files, nearby implementation and tests. In a monorepo, keep repository-wide constraints and app-specific conventions distinct. Skip generated files and unrelated features.
|
|
8
8
|
|
|
9
|
-
If the repository contains `.reis-mobile/project.md`, read it for team knowledge that code cannot explain: product vocabulary, critical journeys, intentional boundaries and supported environments. This is optional Markdown read by the model, not a new CLI configuration key. Verify its file paths, symbols and commands against the current checkout; mark stale statements. Do not require a setup interview or create the file unless requested.
|
|
9
|
+
If the repository contains `.reis-mobile/project.md`, read it for team knowledge that code cannot explain: product vocabulary, critical journeys, intentional boundaries and supported environments. In a monorepo with `.reis-mobile/config.yaml`, look beside that config; otherwise look under the resolved app root. This is optional Markdown read by the model, not a new CLI configuration key. Verify its file paths, symbols and commands against the current checkout; mark stale statements and distinguish team-authored business rules from facts inferred from code. Do not require a setup interview or create the file unless requested.
|
|
10
10
|
|
|
11
11
|
## Build a compact brief
|
|
12
12
|
|
|
@@ -33,7 +33,7 @@ Trace at least one concrete path through the relevant objects: an action to a st
|
|
|
33
33
|
|
|
34
34
|
## Optional team context
|
|
35
35
|
|
|
36
|
-
A team can
|
|
36
|
+
A team can run `/reis-mobile:project [feature]` to create or refresh a verified object map in `.reis-mobile/project.md`, then add product knowledge that cannot be inferred from code. The command preserves team-authored text on refresh. The optional profile can live beside the existing `.reis-mobile/config.yaml` (which continues to support only `app`). Useful content is short and specific:
|
|
37
37
|
|
|
38
38
|
```markdown
|
|
39
39
|
# Mobile project context
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reis-mobile",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "AI agents and skills for
|
|
3
|
+
"version": "0.7.3",
|
|
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",
|
|
7
7
|
"author": "Warleson Reis",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"swift",
|
|
44
44
|
"swiftui",
|
|
45
45
|
"kotlin",
|
|
46
|
+
"kotlin-multiplatform",
|
|
46
47
|
"jetpack-compose",
|
|
47
48
|
"dart",
|
|
48
49
|
"firebase",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-accessibility
|
|
3
|
-
description: Audits and fixes
|
|
3
|
+
description: Audits and fixes mobile accessibility across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform, including shared Compose UI and native hosts. Covers TalkBack/VoiceOver labels, roles, state, reading order, touch targets, text scaling, contrast and focus. Use for a11y audits, screen-reader bugs or accessible UI implementation.
|
|
4
4
|
intents: [accessibility]
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -17,6 +17,7 @@ One accessibility process for every mobile stack. This file holds what is true e
|
|
|
17
17
|
| Native Android: Jetpack Compose or Views | [references/android.md](references/android.md) |
|
|
18
18
|
| Native iOS: SwiftUI or UIKit | [references/ios.md](references/ios.md) |
|
|
19
19
|
| React Native or Expo | [references/react-native.md](references/react-native.md) |
|
|
20
|
+
| Kotlin Multiplatform with shared Compose UI or native host UI | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
20
21
|
|
|
21
22
|
In Flutter and React Native apps, the native reference applies only to screens or components written natively (platform views, native modules with UI).
|
|
22
23
|
|
|
@@ -27,7 +27,7 @@ Pitfalls:
|
|
|
27
27
|
## Announcements and focus
|
|
28
28
|
|
|
29
29
|
- Mark text that changes on its own (a counter, a status) with `Semantics(liveRegion: true, ...)`.
|
|
30
|
-
- For one-off messages,
|
|
30
|
+
- For one-off messages, inspect the installed Flutter SDK's `SemanticsService` API and the project's SDK pin before recommending an announcement call; `pubspec.lock` does not identify the exact Flutter SDK version.
|
|
31
31
|
- `SnackBar`s shown through `ScaffoldMessenger` are announced by the framework.
|
|
32
32
|
- Dialogs opened with `showDialog` and routes block the semantics of what is behind them; custom overlays built with `Stack` and `Positioned` do not — use `BlockSemantics` or a route.
|
|
33
33
|
- Control reading order with `Semantics(sortKey: OrdinalSortKey(n))` only when the visual order and the widget order genuinely differ.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Kotlin Multiplatform accessibility
|
|
2
|
+
|
|
3
|
+
Determine whether the changed screen is shared Compose Multiplatform UI, native Android UI, native SwiftUI/UIKit, or a mix. KMP shared domain code has no accessibility tree by itself.
|
|
4
|
+
|
|
5
|
+
- For shared Compose UI, inspect semantics and state on the actual component, then verify the Android and iOS host experience with TalkBack and VoiceOver. Check the installed Compose version's iOS accessibility behavior against [JetBrains' iOS accessibility guide](https://kotlinlang.org/docs/multiplatform/compose-ios-accessibility.html).
|
|
6
|
+
- For native host UI, use the [Android](android.md) or [iOS](ios.md) reference and inspect the adapter that turns a shared state/error into labels, announcements and focus transitions.
|
|
7
|
+
- Test the same critical journey on both targets at large text sizes. A shared composable test does not prove native screen-reader behavior on either device.
|
|
8
|
+
|
|
9
|
+
Give **screen path:symbol → rendered target UI → missing name/role/state/order or focus behavior → target-specific fix → verification**. If a target UI is absent, report that fact instead of claiming parity.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-architecture
|
|
3
|
-
description: Designs and reviews
|
|
3
|
+
description: Designs and reviews mobile architecture across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform. Covers state ownership, data flow, modules, dependency boundaries, navigation, offline data and KMP shared source sets versus host apps. Use when structuring a feature, choosing state management, refactoring layers or deciding where a real class or screen belongs.
|
|
4
4
|
intents: [architecture]
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -17,6 +17,7 @@ What makes a mobile codebase keep up with its team, and how each stack expresses
|
|
|
17
17
|
| Native Android (Kotlin), or the Android side of Kotlin Multiplatform | [references/android.md](references/android.md) |
|
|
18
18
|
| Native iOS (Swift) | [references/ios.md](references/ios.md) |
|
|
19
19
|
| React Native or Expo | [references/react-native.md](references/react-native.md) |
|
|
20
|
+
| Kotlin Multiplatform shared code and target boundaries | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
20
21
|
|
|
21
22
|
## 2. Map before judging
|
|
22
23
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Kotlin Multiplatform architecture
|
|
2
|
+
|
|
3
|
+
Read the [KMP source-set guide](../../mobile-kmp/references/source-sets.md) before assigning a responsibility to shared or target code.
|
|
4
|
+
|
|
5
|
+
1. Draw the actual module and source-set graph from Gradle configuration. Mark `commonMain`, intermediate sets, target sets and the Android/iOS host entry points that consume the module. Do not infer a shared UI from the KMP plugin.
|
|
6
|
+
2. Trace one user action through each host into the same shared domain object. Record the state owner, repository or use-case interface, platform adapter, persistence model and error conversion. Include the Swift-facing API if iOS calls shared Kotlin.
|
|
7
|
+
3. Put platform-neutral rules and data contracts in shared code only when both callers need the same behavior. Keep lifecycle, permissions, UI navigation and platform SDK setup with their actual owners. For small platform operations, compare an `expect`/`actual` seam with an injected interface; follow the project's existing pattern.
|
|
8
|
+
4. Specify object mappings at API and persistence boundaries, especially nullability and error types exported to Swift. Name who writes each object and which cache is authoritative.
|
|
9
|
+
5. Return a table of **object/symbol → source set → owner → Android caller → iOS caller → test seam**, plus a minimal migration sequence. Mark proposed paths as proposed.
|
|
10
|
+
|
|
11
|
+
Check [Kotlin's source-set hierarchy](https://kotlinlang.org/docs/multiplatform/multiplatform-dsl-reference.html) and [platform API connection guidance](https://kotlinlang.org/docs/multiplatform/multiplatform-connect-to-apis.html) against the installed Kotlin version.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-code-review
|
|
3
|
-
description: Reviews mobile code for
|
|
3
|
+
description: Reviews mobile code for production bugs across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform, including KMP source-set and host API contracts. Covers lifecycle, concurrency, UI state and native boundaries with file:line evidence. Use for a code review, PR, project audit, quality check or /reis-mobile:review.
|
|
4
4
|
intents: [review, performance]
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -19,6 +19,7 @@ Detect the stack first, then apply the platform checklist to the files in scope:
|
|
|
19
19
|
| Native Android (Kotlin/Java), or the Android side of Kotlin Multiplatform | [references/android.md](references/android.md) |
|
|
20
20
|
| Native iOS (Swift/Objective-C) | [references/ios.md](references/ios.md) |
|
|
21
21
|
| React Native or Expo | [references/react-native.md](references/react-native.md) |
|
|
22
|
+
| Kotlin Multiplatform shared and target changes | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
22
23
|
|
|
23
24
|
In a cross-platform app, native code in `android/`, `ios/` or native modules gets the native checklist too. Security findings follow `mobile-security` when it is loaded.
|
|
24
25
|
|
|
@@ -23,7 +23,7 @@ Apply to Dart files with widgets (`StatelessWidget`, `StatefulWidget`, `Consumer
|
|
|
23
23
|
- [ ] Long lists with `ListView(children: [...])` / `Column` inside `SingleChildScrollView`: use `ListView.builder` or `SliverList`.
|
|
24
24
|
- [ ] `shrinkWrap: true` on a large list inside another scroll view: forces layout of every item.
|
|
25
25
|
- [ ] Rebuild scope too large: `setState`, `BlocBuilder` or `Consumer` at the top of the screen when only one part changes. Use `buildWhen`, `select` or extract the widget.
|
|
26
|
-
- [ ] `MediaQuery.of(context)` used only for the size: `MediaQuery.sizeOf(context)` rebuilds less. Confirm the Flutter
|
|
26
|
+
- [ ] `MediaQuery.of(context)` used only for the size: `MediaQuery.sizeOf(context)` rebuilds less. Confirm the project's Flutter SDK pin or installed toolchain before suggesting it.
|
|
27
27
|
- [ ] Network images without `cacheWidth`/`cacheHeight` or without caching, decoded at full resolution to display thumbnails.
|
|
28
28
|
- [ ] Animated `Opacity` or `ClipRRect`/`BackdropFilter` in list items: prefer `FadeTransition`/`AnimatedOpacity` and avoid unnecessary clipping.
|
|
29
29
|
- [ ] Heavy work (parsing large JSON, cryptography, image processing) on the UI thread: use `compute`/`Isolate.run`.
|
|
@@ -13,6 +13,6 @@ Apply the checklists in this order; stop early for a small diff that only touche
|
|
|
13
13
|
|
|
14
14
|
- Dart style and idioms: the Effective Dart and Dart 3 guides in the `mobile-flutter` skill (`references/dart.md`).
|
|
15
15
|
- Follow the project's state management (BLoC/Cubit, Riverpod, Provider) instead of suggesting another one.
|
|
16
|
-
- Check the Flutter
|
|
16
|
+
- Check the Flutter SDK pin or installed toolchain before recommending newer APIs (`MediaQuery.sizeOf`, Dart 3 patterns); `pubspec.lock` records dependency resolution, not the exact Flutter SDK version.
|
|
17
17
|
- Generated files (`*.g.dart`, `*.freezed.dart`, `*.mocks.dart`) are reviewed through their source annotations, not line by line.
|
|
18
18
|
- Native code under `android/` and `ios/` follows [android.md](android.md) and [ios.md](ios.md).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Kotlin Multiplatform review
|
|
2
|
+
|
|
3
|
+
Read the changed module's source-set graph and all configured target implementations. Review the public contract as it is consumed by Android and the Swift/iOS host.
|
|
4
|
+
|
|
5
|
+
- **Shared logic:** check that DTO/domain conversions, error mapping, coroutine ownership and repository state remain target-neutral. Cite the exact shared symbol and affected caller.
|
|
6
|
+
- **Target completeness:** compare every changed `expect` declaration and actual implementation or injected adapter across configured targets. Check target-only dependencies are declared in the correct source set.
|
|
7
|
+
- **iOS boundary:** inspect framework export, Swift nullability/error handling and callback or coroutine lifetime when a shared API changes. A shared Kotlin compile alone does not validate the Swift caller.
|
|
8
|
+
- **UI boundary:** first determine whether the project shares Compose UI. Review the Android/iOS host lifecycle and accessibility in the correct native or Compose reference.
|
|
9
|
+
- **Tests:** look for common tests for the rule and target/host tests where behavior depends on platform services. Verify that CI actually runs the relevant target tasks.
|
|
10
|
+
|
|
11
|
+
Return only reproducible findings at `path:line`, with the affected target and smallest fix. Use the [source-set guide](../../mobile-kmp/references/source-sets.md) for the dependency graph.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-debug
|
|
3
|
-
description:
|
|
3
|
+
description: Diagnoses build and runtime failures across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform. Covers pub/Dart, Gradle/AGP, Xcode/CocoaPods, Metro, KMP source sets and iOS frameworks, crashes and deep links. Use for failing builds, app launches, stack traces or /reis-mobile:debug.
|
|
4
4
|
intents: [debug, build, dependency, migration]
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -19,6 +19,7 @@ Choose by the stack **and** by the layer the error comes from — a Flutter or R
|
|
|
19
19
|
| Gradle, the Android build, logcat crashes or ANRs (native Android, or the `android/` folder of any app) | [references/android.md](references/android.md) |
|
|
20
20
|
| Xcode, signing, CocoaPods, Swift packages or iOS crash logs (native iOS, or the `ios/` folder of any app) | [references/ios.md](references/ios.md) |
|
|
21
21
|
| A React Native or Expo app: Metro, native modules, JavaScript errors | [references/react-native.md](references/react-native.md) |
|
|
22
|
+
| Kotlin Multiplatform compilation, source-set or framework integration | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
22
23
|
| A deep link, App Link or Universal Link that opens the browser, the wrong screen or crashes (any stack) | [references/deep-links.md](references/deep-links.md) |
|
|
23
24
|
|
|
24
25
|
When `reis-mobile debug` (or `/reis-mobile:debug`) ran, its `area` line points at the layer: `gradle`, `manifest`, `proguard` → Android; `xcode`, `signing`, `cocoapods`, `spm` → iOS; `pub` → Flutter; `metro` → React Native.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Kotlin Multiplatform debugging
|
|
2
|
+
|
|
3
|
+
Find the first failing module, Gradle task, compilation and target in the log. Read `settings.gradle.kts`, the affected `build.gradle.kts`, version catalog and source-set directories before suggesting a fix.
|
|
4
|
+
|
|
5
|
+
1. For an unresolved symbol, check whether its dependency and declaration belong to `commonMain`, an intermediate source set or a target source set. Confirm that the failing target actually depends on that set.
|
|
6
|
+
2. For `expect`/`actual` errors, compare declarations and implementations for every configured target; follow the installed Kotlin compiler's message rather than assuming a missing iOS file.
|
|
7
|
+
3. For an iOS framework or Xcode failure, separate Kotlin framework compilation/linking from the Swift host build, then inspect the project's integration method and selected architecture. Use the [Apple integration guide](../../mobile-kmp/references/apple-interop.md).
|
|
8
|
+
4. For an Android failure, apply the [Android debugging reference](android.md) to the host/module. For a native iOS failure, apply the [iOS reference](ios.md) to the host. A shared-source error remains a KMP issue.
|
|
9
|
+
|
|
10
|
+
Report the first error line, affected source set/target, configuration evidence and the exact project task that reproduces it. Discover task names from the wrapper instead of guessing them. Kotlin's [source-set reference](https://kotlinlang.org/docs/multiplatform/multiplatform-dsl-reference.html) defines the hierarchy.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-firebase
|
|
3
|
-
description: Integrates
|
|
3
|
+
description: Integrates and reviews Firebase across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform. Covers Auth, databases, Storage, Functions, FCM, Analytics, Crashlytics, Remote Config, App Check and KMP shared-to-platform SDK boundaries. Use for any Firebase setup, bug, security rule, emulator, configuration file or product integration.
|
|
4
4
|
routing: manual
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -19,6 +19,7 @@ Detect the stack first, then read the platform guide (shared setup and the produ
|
|
|
19
19
|
| Native Android (Kotlin), or the Android side of Kotlin Multiplatform | [references/android.md](references/android.md) |
|
|
20
20
|
| Native iOS (Swift) | [references/ios.md](references/ios.md) |
|
|
21
21
|
| React Native or Expo | [references/react-native.md](references/react-native.md) |
|
|
22
|
+
| Kotlin Multiplatform shared API and Android/iOS Firebase boundaries | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
22
23
|
|
|
23
24
|
In a Flutter or React Native app, native code in `android/` and `ios/` (a notification service, an app extension) follows the native guides.
|
|
24
25
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Firebase in Kotlin Multiplatform
|
|
2
|
+
|
|
3
|
+
Start with the installed Gradle dependencies and source sets. Do not assume a Firebase SDK is available to `commonMain`, or that Android and iOS use the same API surface. Identify the actual Firebase wrapper and platform initialization in this repository.
|
|
4
|
+
|
|
5
|
+
1. Map each requested product operation to a shared interface or target implementation, the Android Firebase setup and the iOS Firebase setup. Record who owns listeners, token refresh and error conversion.
|
|
6
|
+
2. Verify target dependency availability against the installed library and version. If a third-party KMP wrapper is used, inspect its target support and native integration before moving a call into shared code.
|
|
7
|
+
3. Keep each host's configuration, push notification entitlement/permission and lifecycle handling in that host. Read the [Android](android.md) or [iOS](ios.md) product reference only for the affected target.
|
|
8
|
+
4. Test shared mapping against fakes, then test each real target's configuration or emulator setup separately. Do not treat a `commonTest` pass as proof of FCM delivery or native SDK setup.
|
|
9
|
+
|
|
10
|
+
Report **product → shared symbol → Android implementation/config → iOS implementation/config → verification**, marking unsupported or unverified targets explicitly. Check [Kotlin's platform API guidance](https://kotlinlang.org/docs/multiplatform/multiplatform-connect-to-apis.html) for the boundary options.
|
|
@@ -26,5 +26,5 @@ Each topic file lists its guides; open only the ones the task needs.
|
|
|
26
26
|
## Before writing code
|
|
27
27
|
|
|
28
28
|
- Follow the project: its state management, folder structure, lints and packages already in `pubspec.yaml`.
|
|
29
|
-
- Check the Flutter and
|
|
29
|
+
- Check the Flutter SDK pin (`.fvmrc`, toolchain config or CI) and the installed `flutter --version` before using newer APIs; `pubspec.lock` records resolved packages, not the exact Flutter SDK version.
|
|
30
30
|
- Prefer packages the project already depends on over adding new ones; when adding one, check that it supports every platform the app targets.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobile-kmp
|
|
3
|
+
description: Works on Kotlin Multiplatform mobile projects with shared Kotlin source sets and Android/iOS targets. Use for architecture, tests, debugging, reviews, performance, security, accessibility, dependencies and releases when `org.jetbrains.kotlin.multiplatform` is applied; inspect actual targets, `commonMain`/`commonTest`, target source sets, Compose Multiplatform and the iOS framework/host app before giving platform advice.
|
|
4
|
+
intents: [architecture, test, performance, review, debug, security, release, accessibility, migration, dependency, build, deployment]
|
|
5
|
+
stacks: [kotlin-multiplatform]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Kotlin Multiplatform
|
|
9
|
+
|
|
10
|
+
KMP is a sharing mechanism, not a promise that both apps use the same UI or SDK. Resolve the module and target graph before choosing Android, iOS or shared-code guidance.
|
|
11
|
+
|
|
12
|
+
| Question | Read |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Which code and dependencies can be shared? | [references/source-sets.md](references/source-sets.md) |
|
|
15
|
+
| How does shared Kotlin reach the iOS app? | [references/apple-interop.md](references/apple-interop.md) |
|
|
16
|
+
| Is UI shared with Compose Multiplatform or native on each target? | [references/compose-ui.md](references/compose-ui.md) |
|
|
17
|
+
| Which build, measurement or release task applies? | [references/build-performance.md](references/build-performance.md) |
|
|
18
|
+
|
|
19
|
+
## Ground the answer
|
|
20
|
+
|
|
21
|
+
1. Read `settings.gradle.kts`, the affected module's `build.gradle.kts`, version catalog and source-set directories. Record the configured targets; `androidMain` and `iosMain` are not proof that a target is built in CI.
|
|
22
|
+
2. Trace a real symbol from `commonMain` through an interface or `expect` declaration to each configured implementation and its Android/iOS caller. Name the module, source set and owner at every boundary. For shared Compose UI, trace its host entry too.
|
|
23
|
+
3. Match validation to the changed boundary: shared Kotlin tests on configured targets, target tests for `actual` implementations, and host UI/device tests for behavior that crosses into Android or iOS. A JVM test is not iOS coverage.
|
|
24
|
+
4. Use the topic skill for the requested work. Use `mobile-android` or `mobile-ios` only for an affected host or target-specific implementation; do not apply native-app instructions blindly to `commonMain`.
|
|
25
|
+
|
|
26
|
+
The [Kotlin Multiplatform source-set reference](https://kotlinlang.org/docs/multiplatform/multiplatform-dsl-reference.html) defines `commonMain`, `commonTest` and target compilations. Confirm DSL and task names in the project's installed Kotlin and Gradle versions.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# iOS host and framework integration
|
|
2
|
+
|
|
3
|
+
Locate the iOS app target, scheme and the shared framework declaration in the KMP build. Determine whether the app integrates it through an Xcode build phase, a local/remote Swift package, CocoaPods or another checked-in path. Trace the same Kotlin API to its Swift call and back through errors, callbacks or state observation.
|
|
4
|
+
|
|
5
|
+
For an iOS build failure, separate Kotlin/Native compilation, framework export/linking, Xcode signing and Swift source errors. Inspect the actual build phase and artifact path before changing Gradle or Pods. Match architecture, configuration and framework version to the failing simulator or device. For a release, verify the framework embedded in the signed app matches the shared module revision and preserve the app's existing symbol upload path.
|
|
6
|
+
|
|
7
|
+
An iOS simulator test of shared Kotlin does not exercise the Swift app's navigation, permissions or VoiceOver. Use the iOS topic reference when the changed behavior lives in the host.
|
|
8
|
+
|
|
9
|
+
Reference: [official iOS integration methods](https://kotlinlang.org/docs/multiplatform-ios-integration-overview.html).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Build, profiling and delivery
|
|
2
|
+
|
|
3
|
+
List Gradle tasks for the affected module and configured target before naming a command. Use the wrapper, version catalog and CI matrix as the source of the Kotlin, Gradle, Compose and Android plugin versions. A compilation failure in `commonMain` affects all targets; an `ios*` link failure or Android variant failure has a narrower owner.
|
|
4
|
+
|
|
5
|
+
For performance, identify whether the cost is shared algorithm work, Android UI, iOS host/Compose UI or framework crossing. Measure the same journey and input on each affected target with that target's profiler and release-like configuration; do not infer iOS frame time from JVM or Android traces. Attribute retained state to the shared object, native host or bridge that owns it.
|
|
6
|
+
|
|
7
|
+
For delivery, trace Android artifact and iOS archive separately to their app IDs, signing, symbols and store workflow. The shared library is an input to both; publishing one platform does not publish the other. Preserve the host app's rollout and rollback process.
|
|
8
|
+
|
|
9
|
+
References: [KMP test tasks and reports](https://kotlinlang.org/docs/multiplatform-run-tests.html), [iOS framework integration](https://kotlinlang.org/docs/multiplatform-ios-integration-overview.html).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Shared UI versus native UI
|
|
2
|
+
|
|
3
|
+
First find where the screen is implemented. A KMP app may share only logic and render with Jetpack Compose on Android and SwiftUI/UIKit on iOS, or share UI with Compose Multiplatform. Do not apply Compose tests or semantics to a native Swift screen.
|
|
4
|
+
|
|
5
|
+
For shared Compose UI, trace the screen's state owner, Composable entry, navigation owner and the Android/iOS host that installs it. Keep platform services behind the project's existing boundary. Inspect resource generation and the source set holding each resource; Android `R` resources and Compose Multiplatform `Res` accessors are different APIs.
|
|
6
|
+
|
|
7
|
+
For accessibility, inspect Compose semantics and verify them on both targets with TalkBack/VoiceOver. For target-specific dialogs, text input, insets or navigation, inspect the native host as well. Verify API availability against the installed Compose Multiplatform version.
|
|
8
|
+
|
|
9
|
+
References: [Compose resources](https://kotlinlang.org/docs/multiplatform/compose-multiplatform-resources.html), [iOS accessibility](https://kotlinlang.org/docs/multiplatform/compose-ios-accessibility.html).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Shared-code boundaries
|
|
2
|
+
|
|
3
|
+
Read the actual `kotlin {}` targets and `sourceSets {}` dependency graph. Identify `commonMain`, intermediate source sets (such as `iosMain`) and target sources. A file's folder does not establish that every configured binary includes it; check `dependsOn` and the target hierarchy.
|
|
4
|
+
|
|
5
|
+
For a requested feature, record the shared public type, its caller on each target, input/output values and error/cancellation contract. Keep pure business rules and serialization in common code when all targets use the same contract. Put platform SDK calls behind a small common interface or `expect`/`actual` declaration; confirm every configured target has the implementation. Prefer the project's existing injection pattern rather than adding a second one.
|
|
6
|
+
|
|
7
|
+
When a shared type is exported to Swift, check the actual framework API and conversion at the call site. Kotlin nullability, exceptions, collections and asynchronous work can have a different Swift-facing shape from the Kotlin call. Do not infer that a Kotlin unit test covers the Swift consumer.
|
|
8
|
+
|
|
9
|
+
References: [source-set DSL](https://kotlinlang.org/docs/multiplatform/multiplatform-dsl-reference.html), [platform APIs and interfaces](https://kotlinlang.org/docs/multiplatform/multiplatform-connect-to-apis.html).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-release
|
|
3
|
-
description: Prepares
|
|
3
|
+
description: Prepares mobile releases across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform, including separate Android/iOS artifacts from a shared KMP revision. Covers versions, signing, symbols, store readiness, rollout, release notes and CI/CD. Use for release audits, store submission, build pipelines or rollout planning.
|
|
4
4
|
intents: [release, deployment]
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -13,10 +13,12 @@ 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) |
|
|
19
20
|
| React Native or Expo app | [references/react-native.md](references/react-native.md) |
|
|
21
|
+
| Kotlin Multiplatform shared module and both host apps | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
20
22
|
| Release notes and store listing text | [references/store-copy.md](references/store-copy.md) |
|
|
21
23
|
| A CI/CD pipeline that builds, signs and uploads | [references/ci-cd.md](references/ci-cd.md) |
|
|
22
24
|
|
|
@@ -24,11 +26,13 @@ Flutter and React Native apps also need the Android and iOS references for signi
|
|
|
24
26
|
|
|
25
27
|
## 2. Readiness checklist
|
|
26
28
|
|
|
27
|
-
|
|
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.
|
|
28
32
|
|
|
29
33
|
**Version**
|
|
30
|
-
- [ ] The version name follows the
|
|
31
|
-
- [ ] 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.
|
|
32
36
|
- [ ] Android, iOS and any cross-platform manifest (`pubspec.yaml`, `app.json`) agree on the version.
|
|
33
37
|
|
|
34
38
|
**Build quality**
|
|
@@ -46,25 +50,10 @@ Check each item against the project, not against memory. Mark each one **pass**,
|
|
|
46
50
|
**Rollout**
|
|
47
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.
|
|
48
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.
|
|
49
|
-
- [ ] 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.
|
|
50
54
|
|
|
51
55
|
## 3. Report
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
## Release readiness — <app> <version> (<build>)
|
|
55
|
-
Verdict: GO | GO WITH RISKS | NO-GO
|
|
56
|
-
|
|
57
|
-
### Blockers
|
|
58
|
-
- item — evidence (file:line or missing artifact) — fix
|
|
59
|
-
|
|
60
|
-
### Risks
|
|
61
|
-
- item — why it matters — mitigation
|
|
62
|
-
|
|
63
|
-
### Unknown
|
|
64
|
-
- item — who or what can confirm it
|
|
65
|
-
|
|
66
|
-
### Rollout plan
|
|
67
|
-
Stages, metrics to watch, halt threshold, rollback path.
|
|
68
|
-
```
|
|
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.
|
|
69
58
|
|
|
70
|
-
|
|
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,12 @@
|
|
|
1
|
+
# Kotlin Multiplatform release
|
|
2
|
+
|
|
3
|
+
A shared module revision can affect two separate app artifacts. Inventory the Android application, iOS host project and framework integration that this repository actually ships.
|
|
4
|
+
|
|
5
|
+
| Boundary | Verify |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Shared module | Exact commit/version consumed by both hosts, configured target builds and common/target tests. |
|
|
8
|
+
| Android app | Version/build number, signing, release variant, R8 mapping and Play rollout using the [Android guide](android.md). |
|
|
9
|
+
| iOS app | Host version/build number, framework architecture, archive/signing, dSYM and App Store rollout using the [iOS guide](ios.md). |
|
|
10
|
+
| CI | Both artifacts built from the intended shared revision; host tests and upload jobs are distinguished from framework compilation. |
|
|
11
|
+
|
|
12
|
+
Record **artifact → shared revision → build/test evidence → signing/symbol evidence → rollout owner**. One target passing does not make the other ready. Use the [Apple integration guide](../../mobile-kmp/references/apple-interop.md) to identify the framework path, and confirm current store requirements in each store's official portal.
|
|
@@ -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
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mobile-security
|
|
3
|
-
description:
|
|
3
|
+
description: Reviews mobile security across Flutter, native Android, native iOS, React Native and Kotlin Multiplatform using OWASP MASVS. Covers credential storage, secrets, transport, deep links, WebViews, logging and platform implementations of shared KMP contracts. Use for security audits, auth/token handling, pentest findings or release hardening.
|
|
4
4
|
intents: [review, security, release]
|
|
5
5
|
stacks: ["*"]
|
|
6
6
|
---
|
|
@@ -17,6 +17,7 @@ Organized by the OWASP MASVS categories. Apply the general checks below, then th
|
|
|
17
17
|
| Native Android (Kotlin/Java), or `android/` in any app | [references/android.md](references/android.md) |
|
|
18
18
|
| Native iOS (Swift/Objective-C), or `ios/` in any app | [references/ios.md](references/ios.md) |
|
|
19
19
|
| React Native or Expo | [references/react-native.md](references/react-native.md) |
|
|
20
|
+
| Kotlin Multiplatform shared code and platform implementations | [references/kotlin-multiplatform.md](references/kotlin-multiplatform.md) |
|
|
20
21
|
|
|
21
22
|
Firebase-specific protection (security rules, App Check) is covered by `mobile-firebase`.
|
|
22
23
|
|