@mmerterden/multi-agent-pipeline 20.1.0 → 20.2.1

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/docs/facts.json +6 -6
  3. package/manifest.json +55 -34
  4. package/package.json +1 -1
  5. package/pipeline/multi-agent-refs/features/usage-reporting.md +7 -3
  6. package/pipeline/schemas/prefs.schema.json +4 -0
  7. package/pipeline/scripts/usage-register.mjs +2 -0
  8. package/pipeline/skills/.skill-manifest.json +36 -20
  9. package/pipeline/skills/.skills-index.json +75 -9
  10. package/pipeline/skills/shared/README.md +13 -7
  11. package/pipeline/skills/shared/external/android-architecture/SKILL.md +71 -0
  12. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +142 -0
  13. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +314 -0
  14. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +432 -0
  15. package/pipeline/skills/shared/external/android-datastore/SKILL.md +236 -0
  16. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +297 -0
  17. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +249 -0
  18. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +270 -0
  19. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +62 -0
  20. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +255 -0
  21. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +257 -0
  22. package/pipeline/skills/shared/external/android-performance/SKILL.md +86 -602
  23. package/pipeline/skills/shared/external/android-performance/references/patterns.md +659 -0
  24. package/pipeline/skills/shared/external/android-security/SKILL.md +117 -430
  25. package/pipeline/skills/shared/external/android-security/references/patterns.md +690 -0
  26. package/pipeline/skills/shared/external/{android_ui_verification → android-ui-verification}/SKILL.md +1 -1
  27. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +35 -733
  28. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +299 -0
  29. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +255 -0
  30. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +167 -0
  31. package/pipeline/skills/shared/external/app-intents/SKILL.md +39 -174
  32. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +178 -0
  33. package/pipeline/skills/shared/external/compose-components/SKILL.md +48 -0
  34. package/pipeline/skills/shared/external/compose-components/references/patterns.md +200 -0
  35. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +66 -3
  36. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +191 -0
  37. package/pipeline/skills/shared/external/compose-testing/SKILL.md +107 -397
  38. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +631 -0
  39. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +121 -449
  40. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +715 -0
  41. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +143 -0
  42. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +27 -102
  43. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +42 -0
  44. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +94 -383
  45. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +640 -0
  46. package/pipeline/skills/shared/external/room-database/SKILL.md +101 -440
  47. package/pipeline/skills/shared/external/room-database/references/patterns.md +614 -0
  48. package/pipeline/skills/shared/external/storekit/SKILL.md +69 -343
  49. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +371 -0
  50. package/pipeline/skills/shared/external/widgetkit/SKILL.md +25 -101
  51. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +107 -0
  52. package/pipeline/skills/skills-index.md +8 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "skillCount": 213,
3
+ "skillCount": 219,
4
4
  "entries": [
5
5
  {
6
6
  "name": "accessibility-compliance-accessibility-audit",
@@ -47,26 +47,48 @@
47
47
  "relativePath": "shared/external/alarmkit/SKILL.md"
48
48
  },
49
49
  {
50
- "name": "android_ui_verification",
51
- "description": "Automated end-to-end UI testing and verification on an Android Emulator using ADB. Use when an Android build needs driving on an emulator to verify its UI end to end.",
50
+ "name": "android-architecture",
51
+ "description": "Design and implement Clean Architecture with multi-module structure on Android. Covers domain/data/presentation layers, Hilt dependency injection with @Module/@Provides/@Binds, UseCase pattern with operator invoke, Repository pattern with Kotlin Flow, and mapper pattern between layers. Use when stru",
52
52
  "platform": null,
53
53
  "group": "external",
54
54
  "plugin": "ai-android-toolkit",
55
- "invokeAs": "ai-android-toolkit:android_ui_verification",
55
+ "invokeAs": "ai-android-toolkit:android-architecture",
56
56
  "triggerKeywords": [],
57
57
  "triggerPaths": [],
58
- "relativePath": "shared/external/android_ui_verification/SKILL.md"
58
+ "relativePath": "shared/external/android-architecture/SKILL.md"
59
59
  },
60
60
  {
61
- "name": "android-architecture",
62
- "description": "Design and implement Clean Architecture with multi-module structure on Android. Covers domain/data/presentation layers, Hilt dependency injection with @Module/@Provides/@Binds, UseCase pattern with operator invoke, Repository pattern with Kotlin Flow, and mapper pattern between layers. Use when stru",
61
+ "name": "android-build-quality-gates",
62
+ "description": "Encode Android module and quality rules as build-failing Gradle tasks: layer-dependency checks, coverage floors, a Compose-stability gate, lint-on-diff and screenshot validation. Use when a multi-module app needs enforceable boundaries and quality gates in the build, not in review.",
63
63
  "platform": null,
64
64
  "group": "external",
65
65
  "plugin": "ai-android-toolkit",
66
- "invokeAs": "ai-android-toolkit:android-architecture",
66
+ "invokeAs": "ai-android-toolkit:android-build-quality-gates",
67
67
  "triggerKeywords": [],
68
68
  "triggerPaths": [],
69
- "relativePath": "shared/external/android-architecture/SKILL.md"
69
+ "relativePath": "shared/external/android-build-quality-gates/SKILL.md"
70
+ },
71
+ {
72
+ "name": "android-datastore",
73
+ "description": "Typed Jetpack DataStore as the modern SharedPreferences replacement: Preferences and typed stores, serializer-layer encryption, and migration. Use when persisting settings, session or tokens, encrypting key-value state, or migrating off EncryptedSharedPreferences.",
74
+ "platform": null,
75
+ "group": "external",
76
+ "plugin": "ai-android-toolkit",
77
+ "invokeAs": "ai-android-toolkit:android-datastore",
78
+ "triggerKeywords": [],
79
+ "triggerPaths": [],
80
+ "relativePath": "shared/external/android-datastore/SKILL.md"
81
+ },
82
+ {
83
+ "name": "android-design-tokens-codegen",
84
+ "description": "A single-source JSON to typed-Kotlin codegen pipeline for design tokens, UI-test ids, analytics events and deeplink manifests, run as Gradle tasks with a --check CI freshness gate. Use when a design system or cross-platform app needs one source of truth that generates typed code.",
85
+ "platform": null,
86
+ "group": "external",
87
+ "plugin": "ai-android-toolkit",
88
+ "invokeAs": "ai-android-toolkit:android-design-tokens-codegen",
89
+ "triggerKeywords": [],
90
+ "triggerPaths": [],
91
+ "relativePath": "shared/external/android-design-tokens-codegen/SKILL.md"
70
92
  },
71
93
  {
72
94
  "name": "android-jetpack-compose-expert",
@@ -79,6 +101,17 @@
79
101
  "triggerPaths": [],
80
102
  "relativePath": "shared/external/android-jetpack-compose-expert/SKILL.md"
81
103
  },
104
+ {
105
+ "name": "android-mvi-viewmodel",
106
+ "description": "A reusable generic BaseViewModel<State, Intent, Event> for MVI Compose apps: StateFlow state, one-shot events over a rendezvous Channel, a single intent dispatch, and a use-case executor with shared loading and error routing. Use when building or reviewing an MVI ViewModel base, intent-to-state redu",
107
+ "platform": null,
108
+ "group": "external",
109
+ "plugin": "ai-android-toolkit",
110
+ "invokeAs": "ai-android-toolkit:android-mvi-viewmodel",
111
+ "triggerKeywords": [],
112
+ "triggerPaths": [],
113
+ "relativePath": "shared/external/android-mvi-viewmodel/SKILL.md"
114
+ },
82
115
  {
83
116
  "name": "android-performance",
84
117
  "description": "Optimize Android app performance with Baseline Profiles for startup, Compose stability annotations (@Stable, @Immutable, strong skipping), recomposition debugging (Layout Inspector, recomposition counts), R8 optimization, LeakCanary memory leak detection, Macrobenchmark startup tracing, Coil image l",
@@ -101,6 +134,17 @@
101
134
  "triggerPaths": [],
102
135
  "relativePath": "shared/external/android-security/SKILL.md"
103
136
  },
137
+ {
138
+ "name": "android-ui-verification",
139
+ "description": "Automated end-to-end UI testing and verification on an Android Emulator using ADB. Use when an Android build needs driving on an emulator to verify its UI end to end.",
140
+ "platform": null,
141
+ "group": "external",
142
+ "plugin": "ai-android-toolkit",
143
+ "invokeAs": "ai-android-toolkit:android-ui-verification",
144
+ "triggerKeywords": [],
145
+ "triggerPaths": [],
146
+ "relativePath": "shared/external/android-ui-verification/SKILL.md"
147
+ },
104
148
  {
105
149
  "name": "api-patterns",
106
150
  "description": "API design principles and decision-making. REST vs GraphQL vs tRPC selection, response formats, versioning, pagination. Use when choosing between REST, GraphQL and tRPC, or shaping response formats, versioning and pagination.",
@@ -1421,6 +1465,17 @@
1421
1465
  "triggerPaths": [],
1422
1466
  "relativePath": "shared/core/multi-agent-search/SKILL.md"
1423
1467
  },
1468
+ {
1469
+ "name": "multi-agent-security-review",
1470
+ "description": "Run a standalone defensive, static security review of a diff, branch or repo: run-scoped threat model, reviewer-shaped findings joined to OWASP + CWE with CVSS scoring, evidence and before/after fixes, plus an offline dependency inventory. No live target, no payloads. Use when reviewing code for sec",
1471
+ "platform": null,
1472
+ "group": "core",
1473
+ "plugin": null,
1474
+ "invokeAs": "multi-agent-security-review",
1475
+ "triggerKeywords": [],
1476
+ "triggerPaths": [],
1477
+ "relativePath": "shared/core/multi-agent-security-review/SKILL.md"
1478
+ },
1424
1479
  {
1425
1480
  "name": "multi-agent-setup",
1426
1481
  "description": "First-run setup wizard: keychain token discovery, Git Identity onboarding, and pipeline preparation. Use when the pipeline is being set up for the first time, or tokens and git identity need onboarding.",
@@ -1784,6 +1839,17 @@
1784
1839
  "triggerPaths": [],
1785
1840
  "relativePath": "shared/external/search-first/SKILL.md"
1786
1841
  },
1842
+ {
1843
+ "name": "security-review",
1844
+ "description": "Run a defensive, static security review of a diff or a repo: build a run-scoped threat model, find vulnerabilities joined to OWASP + CWE with CVSS scoring and evidence, and emit reviewer-shaped findings with before/after fixes. Use when reviewing code for security, auditing a dependency set, prepari",
1845
+ "platform": null,
1846
+ "group": "external",
1847
+ "plugin": "ai-common-toolkit",
1848
+ "invokeAs": "ai-common-toolkit:security-review",
1849
+ "triggerKeywords": [],
1850
+ "triggerPaths": [],
1851
+ "relativePath": "shared/external/security-review/SKILL.md"
1852
+ },
1787
1853
  {
1788
1854
  "name": "shareplay-activities",
1789
1855
  "description": "Build shared real-time experiences using GroupActivities and SharePlay. Use when implementing shared media playback, collaborative app features, synchronized game state, or any FaceTime, Messages, AirDrop, or nearby visionOS group activity on iOS, macOS, tvOS, or visionOS.",
@@ -2,24 +2,24 @@
2
2
 
3
3
  Single source of truth for skills delivered to both Claude Code (`~/.claude/skills/`) and Copilot CLI (`~/.copilot/skills/`) by the installer.
4
4
 
5
- **Total:** 213 skills (60 core + 153 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
5
+ **Total:** 219 skills (61 core + 158 external). Auto-generated by `scripts/gen-skills-index.mjs` - do not edit by hand.
6
6
 
7
7
  ## Directory layout
8
8
 
9
- - **`core/`** - 60 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
10
- - **`external/`** - 153 iOS / Android / generic skills imported from the upstream skill library. Mirrors of third-party guidance.
9
+ - **`core/`** - 61 `multi-agent*` orchestration skills that are pipeline-critical. Edits here are core-code changes.
10
+ - **`external/`** - 158 iOS / Android / generic skills imported from the upstream skill library. Mirrors of third-party guidance.
11
11
  - Install destinations (ADR-0009): Claude Code gets NO local copy of `external/` - it loads those skills from the `multi-agent-plugins` marketplace, namespaced (`ai-<stack>-toolkit:<name>`); only the two compliance catalogs from `core/` land in `~/.claude/skills/`. Copilot CLI and Codex CLI receive a flat copy filtered to the enabled stacks. `external/` remains the single authoring source that `build-stack-plugins.mjs` publishes from.
12
12
 
13
13
  Source layout is logical grouping only - skill discovery at runtime is unchanged.
14
14
 
15
15
  ## Categories
16
16
 
17
- - [Pipeline Orchestration](#pipeline-orchestration) - 60
17
+ - [Pipeline Orchestration](#pipeline-orchestration) - 61
18
18
  - [iOS / Apple Ecosystem](#ios-apple-ecosystem) - 90
19
- - [Android / Kotlin](#android-kotlin) - 13
19
+ - [Android / Kotlin](#android-kotlin) - 17
20
20
  - [Web](#web) - 10
21
21
  - [Backend / API](#backend-api) - 11
22
- - [Cross-cutting](#cross-cutting) - 29
22
+ - [Cross-cutting](#cross-cutting) - 30
23
23
 
24
24
  ## Pipeline Orchestration
25
25
 
@@ -71,6 +71,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
71
71
  | [`multi-agent-save`](./core/multi-agent-save/) | `core` | Save a recurring job as a reusable /multi-agent:<name> command. Reviews the conversation + your CLAUDE.md for candidate routines, you pick o |
72
72
  | [`multi-agent-scan`](./core/multi-agent-scan/) | `core` | Skill security scan: walks local skill directories against a tiered pattern catalog. Use when local skill directories need checking for unsa |
73
73
  | [`multi-agent-search`](./core/multi-agent-search/) | `core` | Log search across every agent-log.md with smart ranking and filters. Optional --semantic flag queries the per-repo triage corpus. Use when s |
74
+ | [`multi-agent-security-review`](./core/multi-agent-security-review/) | `core` | Run a standalone defensive, static security review of a diff, branch or repo: run-scoped threat model, reviewer-shaped findings joined to OW |
74
75
  | [`multi-agent-setup`](./core/multi-agent-setup/) | `core` | First-run setup wizard: keychain token discovery, Git Identity onboarding, and pipeline preparation. Use when the pipeline is being set up f |
75
76
  | [`multi-agent-stack`](./core/multi-agent-stack/) | `core` | Select the active stack(s) for this repo by enabling the matching marketplace plugin(s) in .claude/settings.json. Multi-select: pass several |
76
77
  | [`multi-agent-status`](./core/multi-agent-status/) | `core` | Show every multi-agent task's ID, phase, branch, and status. Use when asked what is running, or for an overview of every task. |
@@ -185,11 +186,15 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
185
186
 
186
187
  | Skill | Group | Description |
187
188
  |-------|-------|-------------|
188
- | [`android_ui_verification`](./external/android_ui_verification/) | `external` | Automated end-to-end UI testing and verification on an Android Emulator using ADB. Use when an Android build needs driving on an emulator to |
189
189
  | [`android-architecture`](./external/android-architecture/) | `external` | Design and implement Clean Architecture with multi-module structure on Android. Covers domain/data/presentation layers, Hilt dependency inje |
190
+ | [`android-build-quality-gates`](./external/android-build-quality-gates/) | `external` | Encode Android module and quality rules as build-failing Gradle tasks: layer-dependency checks, coverage floors, a Compose-stability gate, l |
191
+ | [`android-datastore`](./external/android-datastore/) | `external` | Typed Jetpack DataStore as the modern SharedPreferences replacement: Preferences and typed stores, serializer-layer encryption, and migratio |
192
+ | [`android-design-tokens-codegen`](./external/android-design-tokens-codegen/) | `external` | A single-source JSON to typed-Kotlin codegen pipeline for design tokens, UI-test ids, analytics events and deeplink manifests, run as Gradle |
190
193
  | [`android-jetpack-compose-expert`](./external/android-jetpack-compose-expert/) | `external` | Expert guidance for building modern Android UIs with Jetpack Compose, covering state management, navigation, performance, and Material Desig |
194
+ | [`android-mvi-viewmodel`](./external/android-mvi-viewmodel/) | `external` | A reusable generic BaseViewModel<State, Intent, Event> for MVI Compose apps: StateFlow state, one-shot events over a rendezvous Channel, a s |
191
195
  | [`android-performance`](./external/android-performance/) | `external` | Optimize Android app performance with Baseline Profiles for startup, Compose stability annotations (@Stable, @Immutable, strong skipping), r |
192
196
  | [`android-security`](./external/android-security/) | `external` | Secure Android apps with ProGuard/R8 obfuscation, OkHttp certificate pinning, BiometricPrompt authentication, EncryptedSharedPreferences, Pl |
197
+ | [`android-ui-verification`](./external/android-ui-verification/) | `external` | Automated end-to-end UI testing and verification on an Android Emulator using ADB. Use when an Android build needs driving on an emulator to |
193
198
  | [`compose-components`](./external/compose-components/) | `external` | Build Material 3 UI components and custom composables with Jetpack Compose. Covers TopAppBar, Scaffold, BottomSheet, SnackbarHost, slot-base |
194
199
  | [`compose-navigation`](./external/compose-navigation/) | `external` | Implement type-safe navigation in Jetpack Compose using Navigation 2.8+ with @Serializable routes, NavHost, composable<Route>, nested naviga |
195
200
  | [`compose-testing`](./external/compose-testing/) | `external` | Test Jetpack Compose UI with createComposeRule, semantic matchers (onNodeWithText, onNodeWithTag, onNodeWithContentDescription), actions (pe |
@@ -255,6 +260,7 @@ Source layout is logical grouping only - skill discovery at runtime is unchang
255
260
  | [`localization-reuse-map`](./external/localization-reuse-map/) | `external` | >- |
256
261
  | [`pdfkit`](./external/pdfkit/) | `external` | Display and manipulate PDF documents using PDFKit. Use when embedding PDFView to show PDF files, creating or modifying PDFDocument instances |
257
262
  | [`search-first`](./external/search-first/) | `external` | \| |
263
+ | [`security-review`](./external/security-review/) | `external` | Run a defensive, static security review of a diff or a repo: build a run-scoped threat model, find vulnerabilities joined to OWASP + CWE wit |
258
264
  | [`signal-community`](./external/signal-community/) | `external` | Check whether other people hit the same problem, via Stack Overflow and Hacker News (keyless) plus opt-in Reddit and X. Use when a bug repor |
259
265
  | [`skill-creator`](./external/skill-creator/) | `external` | Author and refine skills for a plugin/toolkit efficiently. Use when creating a new skill, trimming or splitting an existing one, writing a s |
260
266
  | [`spm-build-analysis`](./external/spm-build-analysis/) | `external` | Analyze Swift Package Manager dependencies, package plugins, module variants, and CI-oriented build overhead that slow Xcode builds. Use whe |
@@ -12,10 +12,12 @@ and Jetpack libraries from 2024-2025.
12
12
  ## Contents
13
13
 
14
14
  - [Multi-Module Structure](#multi-module-structure)
15
+ - [Feature api/impl Split](#feature-apiimpl-split)
15
16
  - [Domain Layer](#domain-layer)
16
17
  - [Data Layer](#data-layer)
17
18
  - [Presentation Layer](#presentation-layer)
18
19
  - [Hilt Dependency Injection](#hilt-dependency-injection)
20
+ - [Cross-Module Navigation Graph Aggregation](#cross-module-navigation-graph-aggregation)
19
21
  - [Mapper Pattern](#mapper-pattern)
20
22
  - [Do's and Don'ts](#dos-and-donts)
21
23
  - [Troubleshooting](#troubleshooting)
@@ -77,6 +79,33 @@ include(":feature:search")
77
79
  include(":feature:detail")
78
80
  ```
79
81
 
82
+ ## Feature api/impl Split
83
+
84
+ For features that navigate to each other, split each one into two modules:
85
+
86
+ - `:feature:x:api` -- only the cross-feature contract: `@Serializable` route
87
+ definitions and the entry-param models a caller needs to launch the feature.
88
+ - `:feature:x:impl` -- screens, ViewModels, UI state, and DI wiring.
89
+
90
+ A feature depends only on the `:api` of the features it navigates to, never
91
+ their `:impl`:
92
+
93
+ ```kotlin
94
+ // feature:home:impl/build.gradle.kts
95
+ dependencies {
96
+ implementation(project(":feature:home:api"))
97
+ implementation(project(":feature:detail:api")) // route + entry params only
98
+ // NEVER depend on :feature:detail:impl
99
+ }
100
+ ```
101
+
102
+ Because no `impl` module can see another `impl`, they stay mutually invisible,
103
+ compile in parallel, and cannot form dependency cycles. The `:app` module wires
104
+ them together (see
105
+ [Cross-Module Navigation Graph Aggregation](#cross-module-navigation-graph-aggregation)).
106
+ Full module layout and route contract:
107
+ [references/patterns.md#feature-apiimpl-split](references/patterns.md#feature-apiimpl-split).
108
+
80
109
  ## Domain Layer
81
110
 
82
111
  The domain layer contains business logic with zero Android dependencies. It
@@ -231,6 +260,11 @@ sealed interface HomeUiState {
231
260
  }
232
261
  ```
233
262
 
263
+ For the reusable base ViewModel seam these screen ViewModels extend -- immutable
264
+ state as `StateFlow`, one-shot events, a single intent entry point, and
265
+ centralized use-case execution -- see the `android-mvi-viewmodel` skill. Do not
266
+ reimplement that base class here.
267
+
234
268
  ## Hilt Dependency Injection
235
269
 
236
270
  ### Application Setup
@@ -324,6 +358,36 @@ abstract class DispatcherModule {
324
358
  }
325
359
  ```
326
360
 
361
+ ## Cross-Module Navigation Graph Aggregation
362
+
363
+ Each `:impl` module contributes its destinations to a shared `Set<NavGraph>` via
364
+ Hilt multibinding; the host registers the whole set into one `NavHost`. The
365
+ `:app` module never depends on any `:impl`.
366
+
367
+ ```kotlin
368
+ fun interface NavGraph {
369
+ fun NavGraphBuilder.build(navController: NavHostController)
370
+ }
371
+
372
+ // In each :impl module
373
+ @Module
374
+ @InstallIn(SingletonComponent::class)
375
+ abstract class HomeNavModule {
376
+ @Binds
377
+ @IntoSet
378
+ abstract fun bindHomeNavGraph(impl: HomeNavGraph): NavGraph
379
+ }
380
+
381
+ // In :app -- register every contributed graph, naming no feature
382
+ NavHost(navController, startDestination = HomeRoute) {
383
+ navGraphs.forEach { graph -> with(graph) { build(navController) } }
384
+ }
385
+ ```
386
+
387
+ This wires many feature modules into a single `NavHost` without the app module
388
+ depending on every `impl`. Full host injection and per-feature contribution:
389
+ [references/patterns.md#cross-module-navigation-graph-aggregation](references/patterns.md#cross-module-navigation-graph-aggregation).
390
+
327
391
  ## Mapper Pattern
328
392
 
329
393
  Mappers isolate layer boundaries. Each layer has its own model type.
@@ -372,9 +436,13 @@ class FlightMapper @Inject constructor() {
372
436
  - Define a `DispatcherProvider` interface for testable coroutine dispatchers
373
437
  - One use case per business operation
374
438
  - Use `sealed interface` for UI state to guarantee exhaustive `when` handling
439
+ - Split each feature into `:api` (routes + entry params) and `:impl` (screens, ViewModels, DI)
440
+ - Aggregate feature nav graphs into the host with Hilt `@IntoSet` multibinding
375
441
 
376
442
  ### Don'ts
377
443
  - Do not let feature modules depend on each other directly
444
+ - Do not depend on another feature's `:impl` module -- depend on its `:api` only
445
+ - Do not make the `:app` module reference feature `impl` modules in code
378
446
  - Do not put Android framework classes in the domain layer
379
447
  - Do not expose `MutableStateFlow` from ViewModels (expose read-only `StateFlow`)
380
448
  - Do not inject `Activity` or `Fragment` into ViewModels
@@ -402,6 +470,9 @@ class FlightMapper @Inject constructor() {
402
470
  - [ ] UI state is a `sealed interface` with Loading/Success/Error
403
471
  - [ ] Hilt modules use `@Binds` for interfaces, `@Provides` for instances
404
472
  - [ ] Feature modules do not depend on each other
473
+ - [ ] Each feature is split into `:api` (routes/entry params) and `:impl` (screens/ViewModels/DI)
474
+ - [ ] Feature `impl` modules depend only on other features' `api`, never their `impl`
475
+ - [ ] Feature nav graphs aggregate into the host via Hilt `@IntoSet` multibinding
405
476
  - [ ] Mappers separate DTO/Entity/Domain models
406
477
  - [ ] Coroutine dispatchers are injectable via `DispatcherProvider`
407
478
  - [ ] No `GlobalScope` usage; all coroutines use structured concurrency
@@ -0,0 +1,142 @@
1
+ # Android Clean Architecture -- Reference Patterns
2
+
3
+ Full code for the patterns summarized in `SKILL.md`. Load the section you need
4
+ when implementing that specific structure.
5
+
6
+ ## Contents
7
+
8
+ - [Feature api/impl Split](#feature-apiimpl-split)
9
+ - [Cross-Module Navigation Graph Aggregation](#cross-module-navigation-graph-aggregation)
10
+
11
+ ## Feature api/impl Split
12
+
13
+ Split every feature into an `:api` module (the cross-feature contract) and an
14
+ `:impl` module (everything private to the feature).
15
+
16
+ ```
17
+ :feature:home:api # Serializable routes + entry-param models only
18
+ :feature:home:impl # Screens, ViewModels, UI state, DI
19
+ :feature:search:api
20
+ :feature:search:impl
21
+ :feature:detail:api
22
+ :feature:detail:impl
23
+ ```
24
+
25
+ ### The api module holds only the contract
26
+
27
+ ```kotlin
28
+ // :feature:detail:api
29
+ @Serializable
30
+ data class DetailRoute(val id: String)
31
+
32
+ @Serializable
33
+ data class DetailEntryParams(val id: String, val source: String)
34
+ ```
35
+
36
+ No screens, no ViewModels, no Compose dependency -- just the route and the
37
+ models a caller needs to launch the feature.
38
+
39
+ ### The impl module holds everything private
40
+
41
+ ```kotlin
42
+ // :feature:detail:impl -- not visible to any other feature
43
+ @HiltViewModel
44
+ class DetailViewModel @Inject constructor(
45
+ savedStateHandle: SavedStateHandle,
46
+ private val getDetailUseCase: GetDetailUseCase,
47
+ ) : ViewModel() {
48
+ private val route = savedStateHandle.toRoute<DetailRoute>()
49
+ // ... state, intents, events
50
+ }
51
+
52
+ @Composable
53
+ internal fun DetailScreen(viewModel: DetailViewModel = hiltViewModel()) { /* ... */ }
54
+ ```
55
+
56
+ ### Dependency direction
57
+
58
+ ```kotlin
59
+ // feature:home:impl/build.gradle.kts
60
+ dependencies {
61
+ implementation(project(":feature:home:api"))
62
+ implementation(project(":feature:detail:api")) // route + entry params only
63
+ implementation(project(":core:model"))
64
+ implementation(project(":core:ui"))
65
+ // NEVER implementation(project(":feature:detail:impl"))
66
+ }
67
+ ```
68
+
69
+ Because no `impl` module can see another `impl`, the graph of feature modules is
70
+ acyclic by construction, the modules compile in parallel, and a caller can only
71
+ couple to a feature through its published route and entry params.
72
+
73
+ ## Cross-Module Navigation Graph Aggregation
74
+
75
+ Each `:impl` module contributes its destinations to a shared `Set<NavGraph>` via
76
+ Hilt multibinding. The host module (`:app`) injects the full set and registers
77
+ every contribution into one `NavHost`, without depending on any `:impl`.
78
+
79
+ ### The contribution contract
80
+
81
+ ```kotlin
82
+ // :core:ui (or a :core:navigation module)
83
+ fun interface NavGraph {
84
+ fun NavGraphBuilder.build(navController: NavHostController)
85
+ }
86
+ ```
87
+
88
+ ### Each feature contributes its graph
89
+
90
+ ```kotlin
91
+ // :feature:home:impl
92
+ class HomeNavGraph @Inject constructor() : NavGraph {
93
+ override fun NavGraphBuilder.build(navController: NavHostController) {
94
+ composable<HomeRoute> {
95
+ HomeScreen()
96
+ }
97
+ }
98
+ }
99
+
100
+ @Module
101
+ @InstallIn(SingletonComponent::class)
102
+ abstract class HomeNavModule {
103
+ @Binds
104
+ @IntoSet
105
+ abstract fun bindHomeNavGraph(impl: HomeNavGraph): NavGraph
106
+ }
107
+ ```
108
+
109
+ ### The host registers the whole set
110
+
111
+ ```kotlin
112
+ // :app
113
+ @Composable
114
+ fun AppNavHost(
115
+ navGraphs: Set<@JvmSuppressWildcards NavGraph>,
116
+ navController: NavHostController = rememberNavController(),
117
+ startDestination: Any = HomeRoute,
118
+ ) {
119
+ NavHost(navController = navController, startDestination = startDestination) {
120
+ navGraphs.forEach { graph -> with(graph) { build(navController) } }
121
+ }
122
+ }
123
+ ```
124
+
125
+ Injecting the set into the composable is done with a Hilt entry point or by
126
+ passing it from an `@AndroidEntryPoint` activity:
127
+
128
+ ```kotlin
129
+ @AndroidEntryPoint
130
+ class MainActivity : ComponentActivity() {
131
+ @Inject lateinit var navGraphs: Set<@JvmSuppressWildcards NavGraph>
132
+
133
+ override fun onCreate(savedInstanceState: Bundle?) {
134
+ super.onCreate(savedInstanceState)
135
+ setContent { AppNavHost(navGraphs = navGraphs) }
136
+ }
137
+ }
138
+ ```
139
+
140
+ Adding a feature to the app is now: add its `:impl` to the `:app` runtime
141
+ classpath and its `@IntoSet` binding registers automatically. The `:app` module
142
+ never names the feature in code.