@code-migration/wow-migrator 0.2.1 → 0.2.4
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/package.json +1 -1
- package/skills/android-to-kmp-migrator/SKILL.md +32 -6
- package/skills/android-to-kmp-migrator/bind.md +15 -0
- package/skills/android-to-kmp-migrator/output-contract.md +61 -12
- package/skills/android-to-kmp-migrator/references/kmp-expert.md +260 -0
- package/skills/android-to-kmp-migrator/references/kmp-mvi-flowredux.md +344 -0
- package/skills/android-to-kmp-migrator/references/kmp-mvvm.md +328 -0
- package/skills/android-to-kmp-migrator/roles/completion-report.md +2 -1
- package/skills/android-to-kmp-migrator/roles/global-migration-phase.md +7 -2
- package/skills/android-to-kmp-migrator/roles/migration-planning-gate.md +7 -2
- package/skills/android-to-kmp-migrator/roles/migration-prep.md +12 -2
- package/skills/android-to-kmp-migrator/roles/migration-verification.md +5 -3
- package/skills/android-to-kmp-migrator/roles/module-implementation.md +20 -2
- package/skills/android-to-kmp-migrator/roles/module-node-review-fix.md +19 -3
- package/skills/android-to-kmp-migrator/roles/target-project-assistant.md +10 -1
- package/skills/android-to-kmp-migrator/workflow.md +23 -2
- package/skills/kmp-test-validator/SKILL.md +3 -3
- package/skills/kmp-test-validator/bind.md +3 -2
- package/skills/kmp-test-validator/dependencies.yaml +15 -2
- package/skills/kmp-test-validator/output-contract.md +92 -8
- package/skills/kmp-test-validator/roles/validation-code-gate.md +53 -7
- package/skills/kmp-test-validator/roles/validation-workspace-state.md +7 -2
- package/skills/kmp-test-validator/workflow.md +3 -1
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: android-to-kmp-migrator
|
|
3
3
|
description: |
|
|
4
|
-
Module-first Swarm Skill that migrates Legacy Android into an existing KMP target
|
|
5
|
-
Use only after android-project-analyst has finished and package P6 is ready, when the user wants module-first porting then whole-system assembly followed by validation.
|
|
6
|
-
Do NOT invoke before android-project-analyst completes P6. Do NOT treat migrator completion as final without invoking kmp-test-validator at V0. Do NOT use for Legacy Android analysis only, KMP-only feature work, or non-migration refactors.
|
|
7
|
-
version: "0.
|
|
4
|
+
Module-first Swarm Skill that migrates Legacy Android into an existing KMP target by editing target KMP source after analyst P6 understanding — planning/prep/implementation roles create and update files under kmp_target_project_path, global integrate wires cross-module glue, then mandatory kmp-test-validator handoff — without full-project build during migration.
|
|
5
|
+
Use only after android-project-analyst has finished and package P6 is ready, when the user wants module-first porting with real target code changes then whole-system assembly followed by validation.
|
|
6
|
+
Do NOT invoke before android-project-analyst completes P6. Do NOT treat planning or analysis artifacts alone as migration success — target KMP files MUST be edited. Do NOT treat migrator completion as final without invoking kmp-test-validator at V0. Do NOT use for Legacy Android analysis only, KMP-only feature work, or non-migration refactors.
|
|
7
|
+
version: "0.9"
|
|
8
8
|
kind: swarm-skill
|
|
9
9
|
disable-model-invocation: false
|
|
10
10
|
roles:
|
|
@@ -57,13 +57,13 @@ roles:
|
|
|
57
57
|
|
|
58
58
|
# Android To KMP Migrator Swarm Skill
|
|
59
59
|
|
|
60
|
-
Module-first migrator for Legacy Android → KMP target assembly.
|
|
60
|
+
Module-first migrator for Legacy Android → KMP target assembly. **Upstream analyst P6 is read-only input; this skill's job is to edit the target KMP project** under `kmp_target_project_path`.
|
|
61
61
|
|
|
62
62
|
**Canonical contract**: [output-contract.md](output-contract.md)
|
|
63
63
|
|
|
64
64
|
## Protocol Summary
|
|
65
65
|
|
|
66
|
-
0. Pre-flight — [dependencies.yaml](dependencies.yaml): `rg` / `git` / `curl`, optional `jetbrains` MCP (`optional_mcp`), upstream analyst **P6** (`upstream_inputs`); record `dependency_preflight` in `run_manifest.json`.
|
|
66
|
+
0. Pre-flight — [dependencies.yaml](dependencies.yaml): `rg` / `git` / `curl`, optional `jetbrains` MCP (`optional_mcp`), upstream analyst **P6** (`upstream_inputs`); **identify `design_mode` from user input (default `mvi`)**; record `dependency_preflight` and `design_mode` in `run_manifest.json`.
|
|
67
67
|
1. Verify analyst **P6**; `run_manifest.json`, `upstream_analyst_index.json`.
|
|
68
68
|
2. Migration inventory + `modules_migration_index.json`.
|
|
69
69
|
3. Workspace state init.
|
|
@@ -73,6 +73,23 @@ Module-first migrator for Legacy Android → KMP target assembly.
|
|
|
73
73
|
7. Global representation + completion-report `report` mode.
|
|
74
74
|
8. **kmp-test-validator** — **mandatory** when **V0** ready (MG17).
|
|
75
75
|
|
|
76
|
+
## Design Mode (architecture pattern)
|
|
77
|
+
|
|
78
|
+
The migrator targets one presentation architecture per run, selected at **pre-flight (Step 0)** by the Leader from the **user input**, then frozen for the whole run and recorded in `run_manifest.json` → `design_mode`.
|
|
79
|
+
|
|
80
|
+
| `design_mode` | Architecture reference | When chosen |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `mvi` **(default)** | [references/kmp-mvi-flowredux.md](references/kmp-mvi-flowredux.md) | Default when user input gives no clear signal; or user mentions MVI, FlowRedux, state machine, reducer, intent, unidirectional, sealed `State`/`Action`, `dispatch`, `inState`, `onEnter` |
|
|
83
|
+
| `mvvm` | [references/kmp-mvvm.md](references/kmp-mvvm.md) | User mentions MVVM, shared `ViewModel`, `StateFlow`/`uiState`, `viewModelScope`, `collectAsStateWithLifecycle`, KMP-ObservableViewModel, SKIE |
|
|
84
|
+
|
|
85
|
+
Both modes also follow [references/kmp-expert.md](references/kmp-expert.md) for base KMP/CMP conventions.
|
|
86
|
+
|
|
87
|
+
**Rules**:
|
|
88
|
+
- **Default is `mvi`** — when the user input contains no explicit or implied architecture signal, the Leader MUST select `mvi`.
|
|
89
|
+
- Record the decision as `design_mode: { value, source: "user_input | default", signals: [] }` in `run_manifest.json` at **MG0**.
|
|
90
|
+
- The Leader passes `design_mode` and the resolved `architecture_reference_path` to every architecture-producing role (planning-gate, prep, module-implementation, module-node-review-fix, global-migration-phase) and to TPA for target-pattern detection.
|
|
91
|
+
- `design_mode` is **fixed for the run**; a mid-run change requires a fresh run, not in-place mutation.
|
|
92
|
+
|
|
76
93
|
## Skill Chain (mandatory)
|
|
77
94
|
|
|
78
95
|
```text
|
|
@@ -108,6 +125,7 @@ android-project-analyst (P6) → android-to-kmp-migrator (M0–V0) → kmp-test-
|
|
|
108
125
|
| [bind.md](bind.md) | Limits, constraints, failures |
|
|
109
126
|
| [dependencies.yaml](dependencies.yaml) | CLI + optional MCP per role |
|
|
110
127
|
| [roles/](roles/) | Role specs |
|
|
128
|
+
| [references/](references/) | Architecture references: `kmp-mvi-flowredux.md` (MVI, default), `kmp-mvvm.md` (MVVM), `kmp-expert.md` (base KMP/CMP) |
|
|
111
129
|
|
|
112
130
|
## Handoff Gates
|
|
113
131
|
|
|
@@ -123,6 +141,14 @@ android-project-analyst (P6) → android-to-kmp-migrator (M0–V0) → kmp-test-
|
|
|
123
141
|
## Shared Rules
|
|
124
142
|
|
|
125
143
|
- **Skill chain**: `android-project-analyst` **P6** before migrator; `kmp-test-validator` **after** migrator **V0** — both mandatory.
|
|
144
|
+
- **Design mode**: identified from user input at pre-flight, **default `mvi`**; frozen for the run; architecture-producing roles MUST follow the resolved `architecture_reference_path` (`kmp-mvi-flowredux.md` for `mvi`, `kmp-mvvm.md` for `mvvm`).
|
|
145
|
+
- **Target KMP edit mandate**: after analyst P6 understanding, migrator roles MUST create or update production files under `kmp_target_project_path`. Planning-only or artifact-only completion is invalid.
|
|
146
|
+
- **Roles that edit target** (record `changed_files[]` or `integration_changed_files[]`):
|
|
147
|
+
- `migration-prep` — optional scaffold edits (theme, resources, routes, models) when planning allows
|
|
148
|
+
- `module-implementation` `ui` / `logic` — required per-module UI and logic port
|
|
149
|
+
- `module-node-review-fix` `fix` — scoped remediation in `allowed_files`
|
|
150
|
+
- `global-migration-phase` `integrate` — cross-module glue and entry-point wiring
|
|
151
|
+
- **Read-only on target**: `target-project-assistant`, `migration-planning-gate`, `migration-verification`, `global-migration-phase` `align`, `completion-report`.
|
|
126
152
|
- Analyst **P6** required; TPA owns all target Q&A.
|
|
127
153
|
- Mode boundaries non-negotiable: `ui`/`logic`, `integrate`/`align`, `review`/`fix`.
|
|
128
154
|
- No full project build in migrator.
|
|
@@ -16,11 +16,20 @@
|
|
|
16
16
|
- **Forbidden**: `incremental_build` in migrator.
|
|
17
17
|
- **kmp-test-validator**: full compile/build/test at package **V0**.
|
|
18
18
|
|
|
19
|
+
## Target KMP Edit Mandate
|
|
20
|
+
|
|
21
|
+
- Analyst **P6** is read-only input. Migrator success requires **editing the target KMP project** at `kmp_target_project_path`.
|
|
22
|
+
- **Edit-owning roles**: `migration-prep` (optional scaffold), `module-implementation` `ui`/`logic` (required), `module-node-review-fix` `fix`, `global-migration-phase` `integrate`.
|
|
23
|
+
- **Read-only on target**: TPA, `migration-planning-gate`, `migration-verification`, `global-migration-phase` `align`, `completion-report`.
|
|
24
|
+
- When planning tasks require file changes, `changed_files[]` MUST be non-empty and paths MUST resolve under `kmp_target_project_path`.
|
|
25
|
+
- `migration_report.json` MUST aggregate `target_changed_files[]` before **V0**.
|
|
26
|
+
|
|
19
27
|
## Behavioral Constraints
|
|
20
28
|
|
|
21
29
|
- **Skill chain (mandatory)**:
|
|
22
30
|
- **Before migrator**: `android-project-analyst` MUST finish and produce package **P6** (`handoff_gates.P6.ready = true`). If P6 is missing or stale, return `blocked` and dispatch analyst — do not start migrator nodes.
|
|
23
31
|
- **After migrator**: when package **V0** is ready, Leader MUST invoke `kmp-test-validator` (MG17). Migrator completion without validator dispatch is invalid.
|
|
32
|
+
- **Design mode**: at pre-flight the Leader identifies `design_mode` from user input — **default `mvi`** when no architecture signal is present. It is recorded in `run_manifest.json`, **frozen for the run**, and every architecture-producing role MUST follow the resolved `architecture_reference_path` (`references/kmp-mvi-flowredux.md` for `mvi`, `references/kmp-mvvm.md` for `mvvm`). Both modes also follow `references/kmp-expert.md`.
|
|
24
33
|
- **Role schedule**: dispatch only role IDs listed in [SKILL.md](SKILL.md).
|
|
25
34
|
- **Mode discipline**:
|
|
26
35
|
- `module-implementation`: `ui` then `logic` — separate invocations
|
|
@@ -36,6 +45,9 @@
|
|
|
36
45
|
| Failure | Response |
|
|
37
46
|
|---|---|
|
|
38
47
|
| Unknown or invalid role ID | Reject; use role from `SKILL.md` registry |
|
|
48
|
+
| `design_mode` not identified at pre-flight | Default to `mvi`; record `source: default` in `run_manifest.json` |
|
|
49
|
+
| Architecture-producing dispatch missing `design_mode` | Reject; re-dispatch with `design_mode` + `architecture_reference_path` |
|
|
50
|
+
| Code produced against wrong architecture vs `design_mode` | `needs_rerun` owning role with correct reference |
|
|
39
51
|
| `ui` and `logic` combined | Reject invocation |
|
|
40
52
|
| `integrate` and `align` combined | Reject invocation |
|
|
41
53
|
| Verification restoration failed | Rerun `module-implementation` or `migration-prep`; no completion record |
|
|
@@ -44,6 +56,9 @@
|
|
|
44
56
|
| Build requested in migrator | Block; route to kmp-test-validator |
|
|
45
57
|
| Migrator invoked before analyst P6 | Block; dispatch `android-project-analyst` first |
|
|
46
58
|
| V0 ready but validator not invoked | Block; dispatch `kmp-test-validator` at MG17 |
|
|
59
|
+
| Analysis/planning only — no target edits when required | Block package M3; rerun `module-implementation` or `migration-prep` |
|
|
60
|
+
| `changed_files` outside `kmp_target_project_path` | Block; rerun owning edit role |
|
|
61
|
+
| Empty `target_changed_files` in `migration_report.json` when scope required edits | Block package V0 |
|
|
47
62
|
|
|
48
63
|
## Dependencies
|
|
49
64
|
|
|
@@ -128,13 +128,26 @@ output_root = <output_dir or ~/.a2c_agents/migration>/android-to-kmp-migrator
|
|
|
128
128
|
- **`incremental_build` is forbidden** in `migration-verification` during migrator runs.
|
|
129
129
|
- **Full compile/build/preview/behavioral tests** are delegated to **`kmp-test-validator`** after `migration_report.*` handoff package **`V0`** is ready.
|
|
130
130
|
|
|
131
|
+
### Target KMP Edit Mandate (mandatory)
|
|
132
|
+
|
|
133
|
+
- **Purpose**: `android-project-analyst` **P6** supplies Legacy Android understanding only. The migrator MUST translate that understanding into **concrete edits** in the existing KMP target at `kmp_target_project_path`.
|
|
134
|
+
- **Analysis alone is not migration**: planning gates, prep handoffs, TPA alignment, and representations do **not** satisfy migration without target file changes where tasks require implementation.
|
|
135
|
+
- **Edit-owning roles** (each MUST record paths under `kmp_target_project_path`):
|
|
136
|
+
- `migration-prep` → `migration_prep.json` → `changed_files[]` (optional scaffold when planning allows)
|
|
137
|
+
- `module-implementation` `ui` / `logic` → `module_implementation_ui.json` / `module_implementation_logic.json` → `changed_files[]`, `target_edit_summary`
|
|
138
|
+
- `module-node-review-fix` `fix` → `module_node_fix.json` → `changed_files[]`
|
|
139
|
+
- `global-migration-phase` `integrate` → `global_system_integration.json` → `integration_changed_files[]`, `entry_point_wiring[]`
|
|
140
|
+
- **Read-only on target**: `target-project-assistant`, `migration-planning-gate`, `migration-verification`, `global-migration-phase` `align`, `completion-report`.
|
|
141
|
+
- **Fail closed**: when `migration_planning_gate.json` → `planning.tasks[]` includes file-changing work for a module, package **M3** is false if both `module_implementation_ui.json` and `module_implementation_logic.json` have empty `changed_files[]` or paths resolve outside `kmp_target_project_path`.
|
|
142
|
+
- **Aggregation**: `migration_report.json` MUST include `target_changed_files[]` — deduplicated union of all module and global integrate target paths with `owning_role` and `migration_module_id` (or `global` for integrate).
|
|
143
|
+
|
|
131
144
|
---
|
|
132
145
|
|
|
133
146
|
## Write Order (Leader Schedule)
|
|
134
147
|
|
|
135
148
|
| Step | Gate | Required artifacts before next step |
|
|
136
149
|
|---|---|---|
|
|
137
|
-
| `MG0` | Run lock | `run_manifest.json
|
|
150
|
+
| `MG0` | Run lock | `run_manifest.json` (incl. `design_mode`), `upstream-index/upstream_analyst_index.json` |
|
|
138
151
|
| `MG1` | Workspace init | global `migration_workspace_state.*` |
|
|
139
152
|
| `MG2` | Migration index | `migration_module_inventory.*`, `modules_migration_index.json`, per-module `module_brief.json` |
|
|
140
153
|
| `MG3` | Target baseline | global `target-project-assistant/*` (`mode: global_baseline`) + `target_alignment_revision.*` |
|
|
@@ -184,8 +197,9 @@ output_root = <output_dir or ~/.a2c_agents/migration>/android-to-kmp-migrator
|
|
|
184
197
|
| `migration-planning-gate/migration_planning_gate.json` |
|
|
185
198
|
| `migration-prep/migration_prep.json` |
|
|
186
199
|
| `module-implementation/ui/module_implementation_ui.json`, `module-implementation/logic/module_implementation_logic.json` + approved reviews |
|
|
187
|
-
|
|
|
188
|
-
| `
|
|
200
|
+
| **Target edits**: when planning tasks require file changes, both UI and logic implementation artifacts MUST have non-empty `changed_files[]` under `kmp_target_project_path` |
|
|
201
|
+
| `migration_verification.json` with all required `check_ids` passed (including `target_files_exist` when `changed_files` non-empty) |
|
|
202
|
+
| `module_completion_record.json` with `ui_restoration` and `logic_restoration` passed and `target_changed_files[]` listing module target paths |
|
|
189
203
|
|
|
190
204
|
### Package `M4` — All modules migrated
|
|
191
205
|
|
|
@@ -199,7 +213,7 @@ output_root = <output_dir or ~/.a2c_agents/migration>/android-to-kmp-migrator
|
|
|
199
213
|
| Required paths |
|
|
200
214
|
|---|
|
|
201
215
|
| Package `M4` |
|
|
202
|
-
| `global/node-results/global-migration-phase/integrate/global_system_integration.json` |
|
|
216
|
+
| `global/node-results/global-migration-phase/integrate/global_system_integration.json` with non-empty `integration_changed_files[]` when cross-module glue or entry-point wiring is required |
|
|
203
217
|
| `global_migration_representation.json` |
|
|
204
218
|
|
|
205
219
|
### Package `M6` — Post-integration alignment passed
|
|
@@ -215,7 +229,7 @@ output_root = <output_dir or ~/.a2c_agents/migration>/android-to-kmp-migrator
|
|
|
215
229
|
| Required paths |
|
|
216
230
|
|---|
|
|
217
231
|
| Package `M6` |
|
|
218
|
-
| `report/migration_report.json` |
|
|
232
|
+
| `report/migration_report.json` with non-empty `target_changed_files[]` when any scheduled module required implementation |
|
|
219
233
|
| `global_migration_representation.json` |
|
|
220
234
|
| analyst `SPEC/*` paths recorded in `run_manifest.json` |
|
|
221
235
|
|
|
@@ -227,6 +241,30 @@ output_root = <output_dir or ~/.a2c_agents/migration>/android-to-kmp-migrator
|
|
|
227
241
|
|
|
228
242
|
## Key Artifact Content Requirements
|
|
229
243
|
|
|
244
|
+
### `run_manifest.json` → `design_mode`
|
|
245
|
+
|
|
246
|
+
Records the presentation architecture pattern, identified from **user input** at pre-flight (Step 0a) and **frozen for the run**. Default is `mvi` when user input gives no clear signal.
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"design_mode": {
|
|
251
|
+
"value": "mvi",
|
|
252
|
+
"source": "default",
|
|
253
|
+
"signals": [],
|
|
254
|
+
"architecture_reference_path": "references/kmp-mvi-flowredux.md"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
| Field | Meaning |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `value` | `mvi` (default) \| `mvvm` |
|
|
262
|
+
| `source` | `user_input` when a signal was matched; `default` when none |
|
|
263
|
+
| `signals` | matched keywords/phrases from user input (empty when defaulted) |
|
|
264
|
+
| `architecture_reference_path` | `references/kmp-mvi-flowredux.md` for `mvi`, `references/kmp-mvvm.md` for `mvvm` |
|
|
265
|
+
|
|
266
|
+
The Leader MUST pass `design_mode.value` and `design_mode.architecture_reference_path` into every architecture-producing dispatch (`migration-planning-gate`, `migration-prep`, `module-implementation`, `module-node-review-fix`, `global-migration-phase`) and to `target-project-assistant` for target-pattern detection.
|
|
267
|
+
|
|
230
268
|
### `upstream_analyst_index.json`
|
|
231
269
|
|
|
232
270
|
```json
|
|
@@ -262,8 +300,10 @@ Machine lookup: `migration_module_id` → `legacy_module_id`, `module_output_roo
|
|
|
262
300
|
{
|
|
263
301
|
"migration_module_id": "",
|
|
264
302
|
"legacy_module_id": "",
|
|
303
|
+
"kmp_target_project_path": "",
|
|
265
304
|
"completion_status": "completed | needs_rerun | blocked",
|
|
266
305
|
"verification_ref": "",
|
|
306
|
+
"target_changed_files": [{ "path": "", "owning_role": "migration-prep | module-implementation | module-node-review-fix", "mode": "ui | logic | fix | null" }],
|
|
267
307
|
"ui_restoration": { "status": "passed | failed", "gaps": [] },
|
|
268
308
|
"logic_restoration": { "status": "passed | failed", "gaps": [] },
|
|
269
309
|
"upstream_match": { "module_representation_path": "", "matched_claims": [], "missing_claims": [] },
|
|
@@ -274,6 +314,7 @@ Machine lookup: `migration_module_id` → `legacy_module_id`, `module_output_roo
|
|
|
274
314
|
|
|
275
315
|
### `migration_verification.json` — required `check_ids` (migrator only)
|
|
276
316
|
|
|
317
|
+
- `target_files_exist` — every path in aggregated module `changed_files[]` exists on disk under `kmp_target_project_path`
|
|
277
318
|
- `source_set` — files in allowed source sets
|
|
278
319
|
- `syntax_check` — Kotlin/syntax validity on changed files (static; no full project compile)
|
|
279
320
|
- `api_contract` — API/model shape vs planning + analyst data contracts
|
|
@@ -311,14 +352,16 @@ Human/agent-readable synthesis of align mode; includes `entry_point_alignment_re
|
|
|
311
352
|
|
|
312
353
|
## Leader Obligations
|
|
313
354
|
|
|
314
|
-
1. Verify analyst package `P6` before `MG0` completes.
|
|
355
|
+
1. Verify analyst package `P6` before `MG0` completes; identify `design_mode` from user input (default `mvi`) and write it to `run_manifest.json` at `MG0`, then pass `design_mode` + `architecture_reference_path` into every architecture-producing dispatch.
|
|
315
356
|
2. Dispatch `target-project-assistant` for all target-project questions; other roles MUST reference TPA artifacts instead of re-analyzing target ad hoc.
|
|
316
|
-
3.
|
|
317
|
-
4.
|
|
318
|
-
5. Run `global-migration-phase` `
|
|
319
|
-
6.
|
|
320
|
-
7.
|
|
321
|
-
8.
|
|
357
|
+
3. Ensure each module produces **target KMP edits** via `module-implementation` (and optional `migration-prep` / `module-node-review-fix` `fix`) before writing `module_completion_record.json`.
|
|
358
|
+
4. Write `module_completion_record.json` after each module passes `migration-verification`; include aggregated `target_changed_files[]` for the module.
|
|
359
|
+
5. Run `global-migration-phase` `integrate` only after package `M4`; integrate MUST edit target glue when assembly requires it.
|
|
360
|
+
6. Run `global-migration-phase` `align` only after integrate; **no code changes** in align mode.
|
|
361
|
+
7. Dispatch only role IDs listed in [SKILL.md](SKILL.md).
|
|
362
|
+
8. Set `handoff_gates` (`M0`–`M6`, `V0`) in workspace ledger and `migration_report.json`.
|
|
363
|
+
9. Aggregate all module and global integrate target paths into `migration_report.json` → `target_changed_files[]`.
|
|
364
|
+
10. **MUST** invoke `kmp-test-validator` when `V0` is true (MG17). Do not end the migration workflow without validator dispatch or explicit validator blockers in `migration_report.json`.
|
|
322
365
|
|
|
323
366
|
## Invalid Artifact Handling
|
|
324
367
|
|
|
@@ -331,3 +374,9 @@ Human/agent-readable synthesis of align mode; includes `entry_point_alignment_re
|
|
|
331
374
|
| `module_completion_record` failed | Re-enter module loop from routed node |
|
|
332
375
|
| `post_integration_alignment` omissions | Rerun listed modules or `global-migration-phase integrate` |
|
|
333
376
|
| Full build requested during migrator | Reject — route to `kmp-test-validator` |
|
|
377
|
+
| Planning complete but `changed_files[]` empty when tasks require edits | `needs_rerun` → `module-implementation` or `migration-prep` |
|
|
378
|
+
| `changed_files` paths outside `kmp_target_project_path` | `blocked` — reject artifact; rerun owning role |
|
|
379
|
+
| `target_files_exist` failed | `needs_rerun` → owning edit role |
|
|
380
|
+
| `design_mode` missing from `run_manifest.json` at MG0 | `blocked`, `reason: missing` — Leader must identify (default `mvi`) before module dispatch |
|
|
381
|
+
| Architecture-producing dispatch missing `design_mode` / `architecture_reference_path` | Reject dispatch; re-dispatch with `design_mode` injected |
|
|
382
|
+
| Implementation/review uses the wrong architecture vs `design_mode` | `needs_rerun` → owning role with correct `architecture_reference_path` |
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kmp-expert
|
|
3
|
+
description: >
|
|
4
|
+
Foundational Kotlin Multiplatform / Compose Multiplatform knowledge for KMP/CMP
|
|
5
|
+
development. Use when working on any KMP/CMP task: project setup, source set
|
|
6
|
+
hierarchy, expect/actual, Gradle/version-catalog config, choosing the library
|
|
7
|
+
stack (Ktor, SQLDelight, Room, Koin, serialization), iOS interop (SKIE,
|
|
8
|
+
KMP-NativeCoroutines), Wasm/web targets, or debugging build/source-set issues.
|
|
9
|
+
Triggers on Kotlin Multiplatform, KMP, CMP, Compose Multiplatform, commonMain,
|
|
10
|
+
expect/actual, shared module, iosMain, klib, Kotlin/Native.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Kotlin Multiplatform / Compose Multiplatform — expert skill
|
|
14
|
+
|
|
15
|
+
This is the foundational knowledge layer for KMP/CMP work. For presentation
|
|
16
|
+
architecture, see the companion `kmp-mvvm` and `kmp-mvi-flowredux` skills — this
|
|
17
|
+
skill covers everything beneath them: structure, source sets, interop, and the stack.
|
|
18
|
+
|
|
19
|
+
References:
|
|
20
|
+
- https://kotlinlang.org/docs/multiplatform/multiplatform-discover-project.html
|
|
21
|
+
- https://kotlinlang.org/docs/multiplatform/multiplatform-hierarchy.html
|
|
22
|
+
- https://blog.jetbrains.com/kotlin/2026/05/new-kmp-default-structure/
|
|
23
|
+
|
|
24
|
+
## KMP vs CMP — know which one you mean
|
|
25
|
+
|
|
26
|
+
- **KMP (Kotlin Multiplatform)** — shares non-UI logic (networking, persistence,
|
|
27
|
+
business rules). You build the UI natively per platform (Compose on Android,
|
|
28
|
+
SwiftUI on iOS). Officially supported by Google for Android/iOS logic sharing.
|
|
29
|
+
- **CMP (Compose Multiplatform)** — a declarative UI framework layered on top of KMP
|
|
30
|
+
that lets you share the UI too. On iOS it renders via Skia (Skiko), not native widgets.
|
|
31
|
+
|
|
32
|
+
Decision rule: push as much as possible into `commonMain`. Use KMP-only when you need
|
|
33
|
+
the deepest native iOS fidelity; use CMP when one UI codebase outweighs that.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Compilation targets
|
|
38
|
+
|
|
39
|
+
| Target | Compiler | Output |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Android / JVM backend | Kotlin/JVM | JVM bytecode |
|
|
42
|
+
| iOS / macOS / watchOS / tvOS / Linux / Windows | Kotlin/Native (LLVM) | Native binary / `.framework` |
|
|
43
|
+
| Web | Kotlin/Wasm (or legacy Kotlin/JS) | WebAssembly / JS |
|
|
44
|
+
|
|
45
|
+
Kotlin/Native produces standalone binaries with no VM, which is how KMP gets native
|
|
46
|
+
performance on iOS.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Project structure (JetBrains 2026 default)
|
|
51
|
+
|
|
52
|
+
The default structure changed in 2026 to give each module a single responsibility,
|
|
53
|
+
aligning with Android Gradle Plugin 9.0.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
project-root/
|
|
57
|
+
├── shared/ ← KMP library: ALL shared code (the only multiplatform module)
|
|
58
|
+
│ └── src/
|
|
59
|
+
│ ├── commonMain/ ← 80–90% of code lives here
|
|
60
|
+
│ ├── androidMain/ ← Android actuals + JVM-only deps
|
|
61
|
+
│ ├── iosMain/ ← iOS actuals (intermediate; see hierarchy below)
|
|
62
|
+
│ ├── desktopMain/ ← JVM desktop actuals
|
|
63
|
+
│ ├── wasmJsMain/ ← web actuals
|
|
64
|
+
│ └── commonTest/
|
|
65
|
+
├── androidApp/ ← thin Android entry point, depends on :shared
|
|
66
|
+
├── iosApp/ ← Xcode project consuming the shared framework
|
|
67
|
+
├── desktopApp/ ← JVM desktop entry point
|
|
68
|
+
└── webApp/ ← Wasm/JS entry point
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The old single `composeApp` module that mixed library + app concerns is replaced by a
|
|
72
|
+
clean `shared` library plus separate `*App` modules. Generate new projects at
|
|
73
|
+
`kmp.jetbrains.com`. Reference samples: `KMP-App-Template`, `kotlinconf-app`, RSS Reader.
|
|
74
|
+
|
|
75
|
+
For large apps, modularize further: feature modules (`:feature-x`) each split into
|
|
76
|
+
`domain`/`data`/`presentation`, plus shared `:core-network`, `:core-db`, `:core-ui`.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Source set hierarchy — the mental model
|
|
81
|
+
|
|
82
|
+
Source sets form a tree. During compilation for a target, Kotlin combines **all** source
|
|
83
|
+
sets on the path from `commonMain` down to the platform leaf.
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
commonMain
|
|
87
|
+
/ \
|
|
88
|
+
appleMain jvmAndroidShared (you create if needed)
|
|
89
|
+
/ \ / \
|
|
90
|
+
iosMain macosMain androidMain desktopMain
|
|
91
|
+
/ \
|
|
92
|
+
iosArm64 iosSimulatorArm64
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Key facts:
|
|
96
|
+
- `commonMain` compiles to every target; code here may use only multiplatform APIs.
|
|
97
|
+
- **Intermediate source sets** (`iosMain`, `appleMain`, `nativeMain`) share code among a
|
|
98
|
+
subset of targets. Put `actual` declarations in the intermediate set (e.g. `iosMain`),
|
|
99
|
+
not in each leaf (`iosArm64Main`, `iosSimulatorArm64Main`).
|
|
100
|
+
- The **default hierarchy template** (modern Kotlin) auto-wires `nativeMain`, `appleMain`,
|
|
101
|
+
`iosMain`, etc. You rarely need manual `dependsOn` anymore.
|
|
102
|
+
- Kotlin does **not** auto-share a JVM+Android source set. If their deps overlap, create a
|
|
103
|
+
custom intermediate set and wire it with `dependsOn` (IDE intellisense may complain but
|
|
104
|
+
it compiles).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## expect / actual — the platform contract
|
|
109
|
+
|
|
110
|
+
The cornerstone mechanism. Declare an `expect` in `commonMain`; provide an `actual` per
|
|
111
|
+
target (or per intermediate source set).
|
|
112
|
+
|
|
113
|
+
```kotlin
|
|
114
|
+
// commonMain
|
|
115
|
+
expect fun getPlatform(): Platform
|
|
116
|
+
interface Platform { val name: String }
|
|
117
|
+
|
|
118
|
+
// androidMain
|
|
119
|
+
actual fun getPlatform(): Platform = object : Platform {
|
|
120
|
+
override val name = "Android ${Build.VERSION.SDK_INT}"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// iosMain
|
|
124
|
+
actual fun getPlatform(): Platform = object : Platform {
|
|
125
|
+
override val name = UIDevice.currentDevice.systemName()
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Three forms:
|
|
130
|
+
1. `expect fun` / `actual fun` — functions (most common)
|
|
131
|
+
2. `expect class` / `actual class` — full classes
|
|
132
|
+
3. **Interface + expect factory** (recommended) — declare an `interface` in common, an
|
|
133
|
+
`expect fun buildX(): Interface` factory, and platform `actual` factories returning
|
|
134
|
+
platform impls. This keeps common code free of platform types and is easier to test.
|
|
135
|
+
|
|
136
|
+
Prefer the interface+factory form over `expect class`; it avoids the strictness of
|
|
137
|
+
matching every member signature across platforms.
|
|
138
|
+
|
|
139
|
+
When NOT to use expect/actual: if a library already provides a multiplatform API (Ktor,
|
|
140
|
+
SQLDelight, kotlinx-datetime), use it directly in `commonMain` — no expect/actual needed.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## The standard 2026 library stack
|
|
145
|
+
|
|
146
|
+
Verify multiplatform support at `klibs.io` before adding any dependency.
|
|
147
|
+
|
|
148
|
+
| Concern | Library | Notes |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| Networking | **Ktor client** | `commonMain` core + per-platform engine (OkHttp/Android, Darwin/iOS, CIO/desktop) |
|
|
151
|
+
| Serialization | **kotlinx.serialization** | apply the plugin to the module that defines `@Serializable` types |
|
|
152
|
+
| Persistence | **SQLDelight** (typed SQL) or **Room KMP** (Google, DAO-style) | SQLDelight for control; Room for Android-team familiarity |
|
|
153
|
+
| Key-value | **multiplatform-settings** or **DataStore** | small prefs |
|
|
154
|
+
| DI | **Koin** | no codegen → fast multiplatform compile; `koin-compose-viewmodel` for CMP |
|
|
155
|
+
| Dates/time | **kotlinx-datetime** | multiplatform `Instant`, `LocalDate`, time zones |
|
|
156
|
+
| Coroutines | **kotlinx-coroutines-core** | the concurrency backbone |
|
|
157
|
+
| Image loading | **Coil 3** | multiplatform |
|
|
158
|
+
| Navigation | Navigation-Compose (KMP), **Voyager**, or **Decompose** | Decompose pairs well with KMP lifecycle |
|
|
159
|
+
| Testing | **kotlinx-coroutines-test** + **Turbine** | flow testing |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Gradle configuration (modern, terse)
|
|
164
|
+
|
|
165
|
+
```kotlin
|
|
166
|
+
// shared/build.gradle.kts
|
|
167
|
+
plugins {
|
|
168
|
+
kotlin("multiplatform")
|
|
169
|
+
kotlin("plugin.serialization")
|
|
170
|
+
id("com.android.library")
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
kotlin {
|
|
174
|
+
androidTarget()
|
|
175
|
+
iosX64(); iosArm64(); iosSimulatorArm64()
|
|
176
|
+
jvm("desktop")
|
|
177
|
+
wasmJs { browser() }
|
|
178
|
+
|
|
179
|
+
// Default hierarchy template auto-creates iosMain/appleMain/nativeMain.
|
|
180
|
+
sourceSets {
|
|
181
|
+
commonMain.dependencies {
|
|
182
|
+
implementation(libs.ktor.client.core)
|
|
183
|
+
implementation(libs.kotlinx.serialization.json)
|
|
184
|
+
implementation(libs.koin.core)
|
|
185
|
+
implementation(libs.kotlinx.coroutines.core)
|
|
186
|
+
}
|
|
187
|
+
androidMain.dependencies { implementation(libs.ktor.client.okhttp) }
|
|
188
|
+
iosMain.dependencies { implementation(libs.ktor.client.darwin) }
|
|
189
|
+
getByName("desktopMain").dependencies { implementation(libs.ktor.client.cio) }
|
|
190
|
+
commonTest.dependencies {
|
|
191
|
+
implementation(libs.turbine)
|
|
192
|
+
implementation(libs.kotlinx.coroutines.test)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Use a `gradle/libs.versions.toml` version catalog for all dependency coordinates —
|
|
199
|
+
it is the standard for keeping versions consistent across modules.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## iOS interop — the hard part
|
|
204
|
+
|
|
205
|
+
Kotlin/Native exports through an Objective-C bridge, which is lossy: sealed classes lose
|
|
206
|
+
exhaustiveness, `Int?` becomes a boxed `KotlinInt`, and `suspend` functions become
|
|
207
|
+
completion-handler callbacks. Mitigations:
|
|
208
|
+
|
|
209
|
+
| Tool | What it fixes |
|
|
210
|
+
|---|---|
|
|
211
|
+
| **SKIE** | Maps Kotlin `Flow` → Swift `AsyncSequence`, sealed classes → Swift enums with associated values, default args. Easy setup, less verbose. |
|
|
212
|
+
| **KMP-NativeCoroutines** | Maps `suspend`/`Flow` to Swift `async/await`, Combine, or RxSwift with proper cancellation. The more battle-tested option. |
|
|
213
|
+
| **KMP-ObservableViewModel** | Lets SwiftUI observe Kotlin ViewModels and handles the iOS lifecycle/store-owner boilerplate. |
|
|
214
|
+
| **Swift Export (emerging)** | Direct Kotlin→Swift modules (suspend→async/await, sealed→enums) without the ObjC layer. Still experimental — don't build production architecture on it yet. |
|
|
215
|
+
|
|
216
|
+
Practical rules:
|
|
217
|
+
- Reduce the exported surface: mark internal code `internal`/`private` and enable
|
|
218
|
+
`explicitApi()` so you don't export everything by default.
|
|
219
|
+
- Annotate the flows/suspend functions you expose with the chosen tool's annotation
|
|
220
|
+
(`@NativeCoroutines`, `@NativeCoroutinesState`).
|
|
221
|
+
- iOS engineers should never see `KotlinInt` or completion handlers — that's a sign your
|
|
222
|
+
interop layer is missing.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Common gotchas
|
|
227
|
+
|
|
228
|
+
| Symptom | Cause | Fix |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| `@Serializable` compiles but crashes at runtime | serialization plugin not on the module defining the type | Apply `kotlin("plugin.serialization")` to that module |
|
|
231
|
+
| IDE shows red but it compiles | IntelliSense lagging on intermediate source sets | Sync Gradle; the build is the source of truth |
|
|
232
|
+
| Can't share code across JVM + Android | Kotlin doesn't auto-create that intermediate set | Create a custom source set with `dependsOn` |
|
|
233
|
+
| iOS sees opaque classes, no exhaustive switch | Raw ObjC bridge without SKIE | Add SKIE or KMP-NativeCoroutines |
|
|
234
|
+
| Slow Kotlin/Native builds | Native compilation is inherently slower than JVM | Use `embedAndSign`/`SKIE` caching; iterate on Android/desktop, verify on iOS less often |
|
|
235
|
+
| `expect class` won't compile — member mismatch | strict signature matching across platforms | Switch to interface + `expect` factory function |
|
|
236
|
+
| Coroutine on iOS never cancels | exposed raw `suspend` without interop annotation | Annotate with `@NativeCoroutines` for proper cancellation |
|
|
237
|
+
| Android-only API leaked into commonMain | wrote `android.*` import in common code | Move it behind expect/actual or an interface |
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Build/run quick reference
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
./gradlew :shared:build # compile the shared library, all targets
|
|
245
|
+
./gradlew :androidApp:installDebug # build + install Android app
|
|
246
|
+
./gradlew :desktopApp:run # run desktop (JVM) app
|
|
247
|
+
./gradlew :shared:iosSimulatorArm64Test # run iOS tests on simulator
|
|
248
|
+
# iOS app itself is built/run from Xcode (iosApp project) consuming the framework
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## When advising on KMP/CMP work
|
|
254
|
+
|
|
255
|
+
1. Default to putting code in `commonMain`; only drop to platform source sets when an API
|
|
256
|
+
genuinely differs. Reach for expect/actual last, libraries first.
|
|
257
|
+
2. Check `klibs.io` for multiplatform support before suggesting any dependency.
|
|
258
|
+
3. For UI: ask whether they want shared UI (CMP) or native UI (KMP-only) before scaffolding.
|
|
259
|
+
4. For presentation logic, defer to the `kmp-mvvm` or `kmp-mvi-flowredux` skill.
|
|
260
|
+
5. Always consider the iOS interop cost of anything exposed across the Swift boundary.
|