svharness 0.8.0

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 (134) hide show
  1. package/README.md +531 -0
  2. package/bin/cli.js +3 -0
  3. package/dist/adapters/_frontmatter.js +24 -0
  4. package/dist/adapters/claude-code.js +12 -0
  5. package/dist/adapters/codechat.js +12 -0
  6. package/dist/adapters/cursor.js +19 -0
  7. package/dist/adapters/generic.js +19 -0
  8. package/dist/adapters/index.js +26 -0
  9. package/dist/adapters/qoder.js +12 -0
  10. package/dist/commands/apply.js +272 -0
  11. package/dist/commands/init.js +420 -0
  12. package/dist/core/agent-injector.js +192 -0
  13. package/dist/core/next-steps.js +91 -0
  14. package/dist/core/render-meta.js +81 -0
  15. package/dist/core/repomix-pack.js +54 -0
  16. package/dist/core/scaffold.js +93 -0
  17. package/dist/core/state.js +80 -0
  18. package/dist/index.js +239 -0
  19. package/dist/types.js +5 -0
  20. package/dist/utils/baseline-copy.js +591 -0
  21. package/dist/utils/baseline-defaults.js +106 -0
  22. package/dist/utils/logger.js +56 -0
  23. package/dist/utils/validate-args.js +132 -0
  24. package/dist/utils/version.js +23 -0
  25. package/dist/wiki/abort.js +30 -0
  26. package/dist/wiki/config.js +79 -0
  27. package/dist/wiki/defaults.js +16 -0
  28. package/dist/wiki/envLoader.js +78 -0
  29. package/dist/wiki/index.js +29 -0
  30. package/dist/wiki/openaiCompat.js +219 -0
  31. package/dist/wiki/repowikiCanonicalSections.js +67 -0
  32. package/dist/wiki/repowikiCheckpoint.js +106 -0
  33. package/dist/wiki/repowikiConfig.js +9 -0
  34. package/dist/wiki/repowikiGit.js +73 -0
  35. package/dist/wiki/repowikiIndexer.js +824 -0
  36. package/dist/wiki/repowikiMarkdownPost.js +123 -0
  37. package/dist/wiki/repowikiMetadataContent.js +64 -0
  38. package/dist/wiki/repowikiMetadataJson.js +15 -0
  39. package/dist/wiki/repowikiScanner.js +156 -0
  40. package/dist/wiki/repowikiStructureNav.js +286 -0
  41. package/dist/wiki/repowikiStructureNormalize.js +218 -0
  42. package/dist/wiki/wikiStructureXml.js +316 -0
  43. package/dist/wiki/wikiTasksWriter.js +127 -0
  44. package/package.json +57 -0
  45. package/templates/_shared/apply-skills/harness-apply-skills-main.md +91 -0
  46. package/templates/_shared/build-rules/harness-build-rule-agent-agnostic.md +35 -0
  47. package/templates/_shared/build-rules/harness-build-rule-chinese-only.md +49 -0
  48. package/templates/_shared/build-rules/harness-build-rule-memory-write.md +31 -0
  49. package/templates/_shared/build-rules/harness-build-rule-orchestrator-flow.md +25 -0
  50. package/templates/_shared/build-rules/harness-build-rule-skills-tasks-output.md +35 -0
  51. package/templates/_shared/build-rules/harness-build-rule-specs-schema.md +32 -0
  52. package/templates/_shared/build-rules/harness-build-rule-user-interaction.md +63 -0
  53. package/templates/_shared/build-skills/harness-build-skill-knowledge-builder.md +120 -0
  54. package/templates/_shared/build-skills/harness-build-skill-orchestrator.md +87 -0
  55. package/templates/_shared/build-skills/harness-build-skill-spec-builder.md +85 -0
  56. package/templates/_shared/build-skills/harness-build-skill-wiki-writer.md +77 -0
  57. package/templates/_shared/meta/AGENTS.md.ejs +53 -0
  58. package/templates/_shared/meta/CHANGELOG.md.ejs +15 -0
  59. package/templates/_shared/meta/README.md.ejs +51 -0
  60. package/templates/_shared/meta/VERSION.ejs +1 -0
  61. package/templates/_shared/meta/harness.yaml.ejs +52 -0
  62. package/templates/_shared/skeleton/agent-env/memory/categories/.gitkeep +1 -0
  63. package/templates/_shared/skeleton/agent-env/memory/inbox/.gitkeep +1 -0
  64. package/templates/_shared/skeleton/agent-env/skills/.gitkeep +1 -0
  65. package/templates/_shared/skeleton/agent-env/tools/.gitkeep +1 -0
  66. package/templates/_shared/skeleton/assets/baseline/code/.gitkeep +1 -0
  67. package/templates/_shared/skeleton/assets/baseline/repomix/.gitkeep +1 -0
  68. package/templates/_shared/skeleton/assets/baseline/wiki/.gitkeep +1 -0
  69. package/templates/_shared/skeleton/assets/raw/.gitkeep +1 -0
  70. package/templates/_shared/skeleton/assets/requirements/.gitkeep +1 -0
  71. package/templates/_shared/skeleton/commands/install/.gitkeep +1 -0
  72. package/templates/_shared/skeleton/commands/update/.gitkeep +1 -0
  73. package/templates/_shared/skeleton/specs/behavior/schema.json +39 -0
  74. package/templates/_shared/skeleton/specs/interfaces/schema.json +38 -0
  75. package/templates/_shared/skeleton/specs/signals/schema.json +37 -0
  76. package/templates/_shared/skeleton/specs/ui/schema.json +44 -0
  77. package/templates/_shared/skeleton/tasks/templates/.gitkeep +0 -0
  78. package/templates/android-compose/skeleton/agent-env/rules/harness-compose-mandatory.mdc +49 -0
  79. package/templates/android-compose/skeleton/agent-env/rules/harness-coroutines-scope.mdc +52 -0
  80. package/templates/android-compose/skeleton/agent-env/rules/harness-hilt-injection.mdc +47 -0
  81. package/templates/android-compose/skeleton/agent-env/rules/harness-mvi-layering.mdc +58 -0
  82. package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/SKILL.md +260 -0
  83. package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/gradle-module-patterns.md +66 -0
  84. package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/implementation-checklist.md +45 -0
  85. package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/udf-data-flow.md +80 -0
  86. package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/SKILL.md +79 -0
  87. package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/references/interact.md +83 -0
  88. package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/references/journeys.md +97 -0
  89. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/SKILL.md +162 -0
  90. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/canonical-sources.md +116 -0
  91. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/diagnostics.md +182 -0
  92. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/report-template.md +135 -0
  93. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/scoring.md +277 -0
  94. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/search-playbook.md +303 -0
  95. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/scripts/compose-reports.init.gradle +58 -0
  96. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-state/SKILL.md +196 -0
  97. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/SKILL.md +192 -0
  98. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/composable-api-guide.md +123 -0
  99. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/performance-recipes.md +97 -0
  100. package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/state-patterns.md +93 -0
  101. package/templates/android-compose/skeleton/agent-env/skills/harness-kotlin-coroutines/SKILL.md +167 -0
  102. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/SKILL.md +45 -0
  103. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/CONFIGURATION.md +44 -0
  104. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/KEEP-RULES-IMPACT-HIERARCHY.md +83 -0
  105. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/REDUNDANT-RULES.md +222 -0
  106. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/REFLECTION-GUIDE.md +139 -0
  107. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/android/topic/performance/app-optimization/enable-app-optimization.md +176 -0
  108. package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/android/training/testing/other-components/ui-automator.md +312 -0
  109. package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/SKILL.md +87 -0
  110. package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/analysis-of-the-project-and-layout.md +42 -0
  111. package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/android/develop/ui/compose/designsystems/migrate-xml-theme-to-compose.md +168 -0
  112. package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/android/develop/ui/compose/setup-compose-dependencies-and-compiler.md +183 -0
  113. package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/identify-optimal-xml-candidate.md +31 -0
  114. package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/xml-layout-migration.md +86 -0
  115. package/templates/android-xml/skeleton/agent-env/rules/seed-aidl-thread.md +29 -0
  116. package/templates/android-xml/skeleton/agent-env/rules/seed-lifecycle-awareness.md +32 -0
  117. package/templates/android-xml/skeleton/agent-env/rules/seed-mvc-layering.md +32 -0
  118. package/templates/android-xml/skeleton/agent-env/rules/seed-view-binding.md +33 -0
  119. package/templates/android-xml/skeleton/agent-env/rules/seed-xml-styling.md +27 -0
  120. package/templates/cpp/skeleton/agent-env/rules/seed-cmake-explicit-sources.md +31 -0
  121. package/templates/cpp/skeleton/agent-env/rules/seed-header-guards.md +34 -0
  122. package/templates/cpp/skeleton/agent-env/rules/seed-include-layering.md +39 -0
  123. package/templates/cpp/skeleton/agent-env/rules/seed-no-cyclic-deps.md +29 -0
  124. package/templates/cpp/skeleton/agent-env/rules/seed-raii.md +30 -0
  125. package/templates/python/skeleton/agent-env/rules/seed-context-managers.md +60 -0
  126. package/templates/python/skeleton/agent-env/rules/seed-docstrings.md +48 -0
  127. package/templates/python/skeleton/agent-env/rules/seed-import-order.md +49 -0
  128. package/templates/python/skeleton/agent-env/rules/seed-pep8-naming.md +45 -0
  129. package/templates/python/skeleton/agent-env/rules/seed-type-annotations.md +43 -0
  130. package/templates/web-react/skeleton/agent-env/rules/seed-controlled-component.md +43 -0
  131. package/templates/web-react/skeleton/agent-env/rules/seed-effect-cleanup.md +43 -0
  132. package/templates/web-react/skeleton/agent-env/rules/seed-hook-rules.md +42 -0
  133. package/templates/web-react/skeleton/agent-env/rules/seed-key-stability.md +39 -0
  134. package/templates/web-react/skeleton/agent-env/rules/seed-no-props-drilling.md +43 -0
@@ -0,0 +1,135 @@
1
+ # Report Template
2
+
3
+ Write the audit report to `COMPOSE-AUDIT-REPORT.md` using this structure.
4
+
5
+ **Citation rule:** every finding (Critical Findings *and* per-category Evidence bullets) must include a `References:` line with at least one URL pointing to the official documentation rule the code violates. Use the URLs in `references/canonical-sources.md` and `references/scoring.md`. A finding without a citation should not appear in the report — that's the credibility lever this audit relies on.
6
+
7
+ ```markdown
8
+ # Jetpack Compose Audit Report
9
+
10
+ Target: [repo path or module path]
11
+ Date: [YYYY-MM-DD]
12
+ Scope: [modules or directories audited]
13
+ Excluded from scoring: [paths or globs treated as samples / tests / previews]
14
+ Confidence: [High | Medium | Low]
15
+ Overall Score: [X/100]
16
+
17
+ ## Scorecard
18
+
19
+ | Category | Score | Weight | Status | Notes |
20
+ |----------|-------|--------|--------|-------|
21
+ | Performance | X/10 | 35% | [fail / needs work / solid / excellent] | [short note] |
22
+ | State management | X/10 | 25% | [fail / needs work / solid / excellent] | [short note] |
23
+ | Side effects | X/10 | 20% | [fail / needs work / solid / excellent] | [short note] |
24
+ | Composable API quality | X/10 | 20% | [fail / needs work / solid / excellent] | [short note] |
25
+
26
+ ## Critical Findings
27
+
28
+ List the most important findings first. Each finding should include:
29
+
30
+ - severity
31
+ - why it matters
32
+ - 2-4 concrete file examples
33
+ - the likely fix direction
34
+
35
+ Example format:
36
+
37
+ 1. **Performance: repeated expensive work happens inside composition**
38
+ - Why it matters: [brief reason]
39
+ - Evidence: `path/a.kt:42`, `path/b.kt:117`
40
+ - Fix direction: [brief recommendation]
41
+ - References: <https://developer.android.com/develop/ui/compose/performance/bestpractices>
42
+
43
+ ## Category Details
44
+
45
+ ### Performance — [X/10]
46
+
47
+ **What is working**
48
+
49
+ - [positive evidence]
50
+
51
+ **What is hurting the score**
52
+
53
+ - [problem 1]
54
+ - [problem 2]
55
+
56
+ **Evidence**
57
+
58
+ - `path/to/file1.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
59
+ - `path/to/file2.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
60
+
61
+ ### State Management — [X/10]
62
+
63
+ **What is working**
64
+
65
+ - [positive evidence]
66
+
67
+ **What is hurting the score**
68
+
69
+ - [problem 1]
70
+ - [problem 2]
71
+
72
+ **Evidence**
73
+
74
+ - `path/to/file1.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
75
+ - `path/to/file2.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
76
+
77
+ ### Side Effects — [X/10]
78
+
79
+ **What is working**
80
+
81
+ - [positive evidence]
82
+
83
+ **What is hurting the score**
84
+
85
+ - [problem 1]
86
+ - [problem 2]
87
+
88
+ **Evidence**
89
+
90
+ - `path/to/file1.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
91
+ - `path/to/file2.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
92
+
93
+ ### Composable API Quality — [X/10]
94
+
95
+ **What is working**
96
+
97
+ - [positive evidence]
98
+
99
+ **What is hurting the score**
100
+
101
+ - [problem 1]
102
+ - [problem 2]
103
+
104
+ **Evidence**
105
+
106
+ - `path/to/file1.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
107
+ - `path/to/file2.kt:LL` — [brief reason] · References: <https://developer.android.com/...>
108
+
109
+ ## Prioritized Fixes
110
+
111
+ 1. [Highest leverage fix]
112
+ 2. [Second fix]
113
+ 3. [Third fix]
114
+ 4. [Optional follow-up]
115
+
116
+ ## Notes And Limits
117
+
118
+ - [state if only part of the repo was audited]
119
+ - [state if confidence is medium/low]
120
+ - [state if some categories had limited surface area]
121
+ - Weight choice: [default 35/25/20/20, or note any deviation and why]
122
+ - Renormalization: [list any N/A categories and the renormalized weights]
123
+ - Compiler diagnostics used: [yes / no — link to the Compose Compiler reports if generated; "no" means stability claims are inferred from source, not measured]
124
+
125
+ ## Suggested Follow-Up
126
+
127
+ - Run `material-3` audit if the repo also shows likely design-system or Material 3 problems.
128
+ ```
129
+
130
+ ## Tone
131
+
132
+ - Keep the tone strict and direct.
133
+ - Avoid filler praise.
134
+ - Give credit only where the codebase actually demonstrates good patterns.
135
+ - Prefer a few strong findings over dozens of weak bullets.
@@ -0,0 +1,277 @@
1
+ # Scoring
2
+
3
+ ## Category Weights
4
+
5
+ Use these default weights for Android Jetpack Compose app repositories:
6
+
7
+ | Category | Weight |
8
+ |----------|--------|
9
+ | Performance | 35% |
10
+ | State management | 25% |
11
+ | Side effects | 20% |
12
+ | Composable API quality | 20% |
13
+
14
+ Performance carries the heaviest weight in v1 because Compose performance issues are the most common reason teams audit a codebase, and the smells are the most measurable from source alone. For state-heavy apps with little perf-sensitive UI (forms, dashboards, settings), a 30/30/20/20 split is reasonable — apply judgment and document the choice in the report's "Notes And Limits" section.
15
+
16
+ ### N/A vs Low Confidence
17
+
18
+ Mark a category `N/A` only when its surface area is structurally absent — for example, scoring API quality on a repo with zero shared/reusable components. If the surface area is merely thin, score it with `Low` confidence instead of dropping it. `N/A` should be rare.
19
+
20
+ ### Renormalization
21
+
22
+ If a category is `N/A`, renormalize the remaining weights so they still sum to 1.0:
23
+
24
+ `weight_i_new = weight_i / sum(remaining_weights)`
25
+
26
+ Worked example — Composable API quality is `N/A`, so the remaining weights are Performance (35%), State (25%), Side effects (20%), summing to 80%:
27
+
28
+ - Performance: `0.35 / 0.80 = 0.4375` → 44%
29
+ - State: `0.25 / 0.80 = 0.3125` → 31%
30
+ - Side effects: `0.20 / 0.80 = 0.2500` → 25%
31
+
32
+ State the renormalization in the report.
33
+
34
+ ## Score Bands
35
+
36
+ | Score | Status | Meaning |
37
+ |-------|--------|---------|
38
+ | 0-3 | fail | Systemic issues, repeated misuse, or architecture-level risk |
39
+ | 4-6 | needs work | Mixed quality, recurring smells, meaningful refactor value |
40
+ | 7-8 | solid | Mostly healthy with some targeted fixes needed |
41
+ | 9-10 | excellent | Consistently strong patterns, only minor issues |
42
+
43
+ ## Overall Score
44
+
45
+ Report both:
46
+
47
+ - per-category scores on a `0-10` scale
48
+ - an overall score on a `0-100` scale
49
+
50
+ Compute:
51
+
52
+ `overall = weighted_average(category_scores) * 10`
53
+
54
+ Round to the nearest whole number.
55
+
56
+ ## Confidence
57
+
58
+ Add a confidence note in the report:
59
+
60
+ - `High`: enough Compose surface area, multiple representative modules/files read
61
+ - `Medium`: some categories based on limited sample size
62
+ - `Low`: small repo, partial module access, or sparse Compose surface
63
+
64
+ Low confidence does not block scoring, but it must be stated clearly.
65
+
66
+ ## Category Rubric
67
+
68
+ Each rule below carries an inline citation. **Every deduction in the report must reference the same citation** so readers can verify against the official source.
69
+
70
+ ### Performance
71
+
72
+ Reward:
73
+
74
+ - expensive calculations cached with `remember(keys)` or moved out of composition → [docs](https://developer.android.com/develop/ui/compose/performance/bestpractices)
75
+ - stable `key =` in lazy layouts where list identity matters → [docs](https://developer.android.com/develop/ui/compose/lists)
76
+ - `contentType` on heterogeneous lazy lists, so Compose can reuse compositions only between items of the same type → [docs](https://developer.android.com/develop/ui/compose/lists)
77
+ - `derivedStateOf` used for state that changes faster than its observable output (e.g. scroll position → "show button" boolean) → [docs](https://developer.android.com/develop/ui/compose/side-effects)
78
+ - deferred reads via lambda modifiers (`Modifier.offset { … }`, `Modifier.graphicsLayer { … }`, `Modifier.drawBehind { … }`) → [docs](https://developer.android.com/develop/ui/compose/performance/bestpractices), [phases](https://developer.android.com/develop/ui/compose/performance/phases)
79
+ - absence of backwards writes (writing to state that has already been read in the same composition) → [docs](https://developer.android.com/develop/ui/compose/performance/bestpractices)
80
+ - stability hygiene: `@Stable` / `@Immutable` on data classes used as composable params → [docs](https://developer.android.com/develop/ui/compose/performance/stability)
81
+ - `kotlinx.collections.immutable` (`ImmutableList`, `PersistentList`) for collection params → [stability](https://developer.android.com/develop/ui/compose/performance/stability), [fix](https://developer.android.com/develop/ui/compose/performance/stability/fix)
82
+ - `compose_compiler_config.conf` used to mark third-party types stable → [fix](https://developer.android.com/develop/ui/compose/performance/stability/fix)
83
+ - typed state factories (`mutableIntStateOf`, `mutableLongStateOf`, `mutableFloatStateOf`, `mutableDoubleStateOf`) for primitives instead of boxed `mutableStateOf<Int>` → [state](https://developer.android.com/develop/ui/compose/state)
84
+ - `@ReadOnlyComposable` / `@NonRestartableComposable` used deliberately on hot-path helpers → [strong skipping](https://developer.android.com/develop/ui/compose/performance/stability/strongskipping)
85
+ - evidence of Strong Skipping Mode awareness (Kotlin 2.0.20+ / Compose Compiler 1.5.4+); opt-outs (`@NonSkippableComposable`, `@DontMemoize`) used only with justification → [strong skipping](https://developer.android.com/develop/ui/compose/performance/stability/strongskipping)
86
+ - evidence of performance-aware configuration such as baseline profiles, R8 / minify enabled in release, or `ProfileInstaller` setup → [baseline profiles](https://developer.android.com/develop/ui/compose/performance/baseline-profiles)
87
+ - `ReportDrawnWhen { ... }` used to signal first-meaningful-content for accurate TTID/TTFD metrics → [tooling](https://developer.android.com/develop/ui/compose/performance/tooling)
88
+ - edge-to-edge opt-in via `enableEdgeToEdge()` (first-party) rather than `accompanist-systemuicontroller` on projects that support it → [lists](https://developer.android.com/develop/ui/compose/lists)
89
+ - evidence the team uses Compose Compiler reports / metrics to verify skippability → [tooling](https://developer.android.com/develop/ui/compose/performance/tooling), [diagnose](https://developer.android.com/develop/ui/compose/performance/stability/diagnose)
90
+
91
+ Deduct for:
92
+
93
+ - collection transforms or expensive computation inside composable bodies without caching → [docs](https://developer.android.com/develop/ui/compose/performance/bestpractices)
94
+ - lazy list items without stable keys when identity/moves matter → [lists](https://developer.android.com/develop/ui/compose/lists)
95
+ - heterogeneous lazy lists missing `contentType` → [lists](https://developer.android.com/develop/ui/compose/lists)
96
+ - reading rapidly changing state too high in the tree → [phases](https://developer.android.com/develop/ui/compose/performance/phases), [bestpractices](https://developer.android.com/develop/ui/compose/performance/bestpractices)
97
+ - frequent-state values passed to non-lambda modifiers when a layout/draw-phase alternative exists → [bestpractices](https://developer.android.com/develop/ui/compose/performance/bestpractices)
98
+ - backwards writes — writing to state already read in the same composition body → [bestpractices](https://developer.android.com/develop/ui/compose/performance/bestpractices)
99
+ - repeated broad recomposition smells across screens/components → [stability](https://developer.android.com/develop/ui/compose/performance/stability)
100
+ - raw `List`/`Map`/`Set` parameters on widely reused composables when the rest of the codebase has the immutable-collections dependency available → [stability](https://developer.android.com/develop/ui/compose/performance/stability)
101
+ - `mutableStateOf<Int|Long|Float|Double>` where the typed factory exists (autoboxing) → [state](https://developer.android.com/develop/ui/compose/state)
102
+ - `derivedStateOf { ... }` whose block does not actually read any `State` object (meaning it will never invalidate, and the overhead of `derivedStateOf` is wasted) → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
103
+ - `@NonSkippableComposable` / `@DontMemoize` opt-outs without a justifying comment → [strong skipping](https://developer.android.com/develop/ui/compose/performance/stability/strongskipping)
104
+ - if the project is on a Compose Compiler track older than 1.5.4 / Kotlin 2.0.20, stability matters more than the rules above assume — note this in the report and weight unstable-param findings more heavily → [strong skipping](https://developer.android.com/develop/ui/compose/performance/stability/strongskipping)
105
+ - `remember { … }` whose body reads `LocalConfiguration`, `LocalDensity`, or `LocalLayoutDirection` without declaring those values as keys — the cached value silently goes stale on rotation / foldable posture / font-scale changes → [state](https://developer.android.com/develop/ui/compose/state)
106
+ - `indexOf()` / `lastIndexOf()` / `indexOfFirst { }` called inside a `LazyListScope` item factory — O(n²) scroll cost and crash risk when identity moves; use `itemsIndexed` instead → [lists](https://developer.android.com/develop/ui/compose/lists)
107
+ - `animateItemPlacement()` on Compose 1.7+ — replaced by `Modifier.animateItem()` → [lists](https://developer.android.com/develop/ui/compose/lists)
108
+ - Accompanist libraries where first-party replacements exist: `accompanist-pager` (→ `HorizontalPager`), `accompanist-swiperefresh` (→ `PullToRefreshBox`), `accompanist-flowlayout` (→ `FlowRow` / `FlowColumn`), `accompanist-systemuicontroller` (→ `enableEdgeToEdge()`) — deduct only when the replacement is available on the project's Compose version → [lists](https://developer.android.com/develop/ui/compose/lists)
109
+ - `Canvas` / `Spacer` with only `Modifier.fillMaxSize()` and no explicit height or aspect ratio — may enter draw with `Size.Zero`, producing `NaN` math and Skia-pipeline crashes. Require `Modifier.size(...)`, `Modifier.height(...)`, or `Modifier.aspectRatio(...)` on drawing surfaces → [bestpractices](https://developer.android.com/develop/ui/compose/performance/bestpractices)
110
+ - lazy-list `key = { ... }` computed from a source that cannot guarantee uniqueness (merged flows, paginated streams, `hashCode()` on non-`data class`) — `IllegalArgumentException: Key ... was already used` crashes production. Verify with the duplicate-lazy-key heuristic in `search-playbook.md` → [lists](https://developer.android.com/develop/ui/compose/lists)
111
+
112
+ Suggested interpretation:
113
+
114
+ - `9-10`: clean patterns are common and performance smells are rare
115
+ - `7-8`: minor or localized issues
116
+ - `4-6`: repeated recomposition or lazy-list issues
117
+ - `0-3`: serious, widespread performance problems or unsafe state-write patterns
118
+
119
+ #### Measured Ceilings (apply when compiler reports are available)
120
+
121
+ Compiler reports generated in Step 4 give hard numbers. When present, apply these ceilings *after* qualitative scoring — a category cannot exceed the cap even if qualitative evidence is strong. Report the applied ceiling in the Performance section so the score is auditable.
122
+
123
+ Let `skippable%` = `skippableComposables / restartableComposables` from `*-module.json`.
124
+ However, because zero-argument lambdas structurally cannot skip and artificially anchor this overall metric, you MUST also compute the **named-only `skippable%`** from `*-composables.csv` (by filtering out rows where `isLambda == "1"`). Use this **named-only percentage** for the ceiling conditions below, and state the distinction clearly in the report.
125
+
126
+ | Condition | Ceiling |
127
+ |-----------|---------|
128
+ | `skippable%` ≥ 95% and zero unstable classes used as shared/reusable composable params | no cap (9-10 possible) |
129
+ | `skippable%` ≥ 85% and ≤3 unstable classes used as shared/reusable composable params | cap at 8 |
130
+ | `skippable%` 70-85% or 4-7 unstable classes used as params | cap at 6 |
131
+ | `skippable%` 50-70% or ≥8 unstable classes used as params | cap at 4 |
132
+ | `skippable%` < 50% | cap at 3 |
133
+ | Strong Skipping disabled on a Kotlin 2.0.20+ project without written justification | cap at 4 |
134
+ | `@NonSkippableComposable` / `@DontMemoize` used on hot-path composables without justification | cap at 5 |
135
+
136
+ When compiler reports are **not** available (Step 4 failed, `Compiler diagnostics used: no`), ceilings do not apply — rely on source-inferred judgment, but cap any Performance score at 7 to reflect reduced confidence.
137
+
138
+ If a non-trivial subset of modules failed to build (partial reports), state which modules contributed and treat `skippable%` as a floor estimate rather than a ground truth.
139
+
140
+ ### State Management
141
+
142
+ Reward:
143
+
144
+ - clear single source of truth → [architecture](https://developer.android.com/develop/ui/compose/architecture)
145
+ - correct hoisting to the lowest common reader / highest writer → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
146
+ - related state hoisted together when driven by the same events → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
147
+ - stateless reusable composables with stateful wrappers where useful → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
148
+ - `rememberSaveable` for UI state that should survive recreation → [state](https://developer.android.com/develop/ui/compose/state)
149
+ - custom `Saver` / `mapSaver` / `listSaver` / `@Parcelize` for non-bundleable types in `rememberSaveable` → [state](https://developer.android.com/develop/ui/compose/state)
150
+ - `collectAsStateWithLifecycle()` in Android UI code → [state](https://developer.android.com/develop/ui/compose/state)
151
+ - observable immutable state instead of mutable non-observable containers → [state](https://developer.android.com/develop/ui/compose/state)
152
+ - correct observable collections via `mutableStateListOf` / `mutableStateMapOf` instead of wrapping `mutableListOf`/`mutableMapOf` in a `MutableState` → [state](https://developer.android.com/develop/ui/compose/state)
153
+ - typed state factories (`mutableIntStateOf` and friends) — cross-listed with Performance because the failure mode is autoboxing → [state](https://developer.android.com/develop/ui/compose/state)
154
+ - plain state-holder classes when screen logic grows; idiomatic shape is `@Stable class FooState(...)` paired with a `@Composable fun rememberFooState(...): FooState` factory → [architecture](https://developer.android.com/develop/ui/compose/architecture)
155
+ - ViewModel used as the source of truth for screen-level state, scoped at the screen level (not deep in the tree, not inside reusable components) → [architecture](https://developer.android.com/develop/ui/compose/architecture), [state](https://developer.android.com/develop/ui/compose/state)
156
+ - ViewModel-exposed flows converted with `.stateIn(scope, SharingStarted.WhileSubscribed(5_000), initial)` — survives configuration changes without restarting upstream work → [architecture](https://developer.android.com/develop/ui/compose/architecture)
157
+ - `remember(key)` invalidation when cached values depend on inputs → [state](https://developer.android.com/develop/ui/compose/state)
158
+
159
+ Deduct for:
160
+
161
+ - duplicated or split ownership of state → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
162
+ - under-hoisted shared state → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
163
+ - reusable components with unnecessary internal state → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
164
+ - misuse of `remember` where `rememberSaveable` is more appropriate → [state](https://developer.android.com/develop/ui/compose/state)
165
+ - non-observable mutable collections or mutable data holders used as state (`mutableListOf` held in a `var`, `ArrayList` mutated in place) → [state](https://developer.android.com/develop/ui/compose/state)
166
+ - `mutableListOf` / `mutableMapOf` wrapped in a `MutableState` where `mutableStateListOf` / `mutableStateMapOf` would correctly observe element changes → [state](https://developer.android.com/develop/ui/compose/state)
167
+ - Android flows collected in UI without lifecycle awareness when the code is Android-specific (skip on Compose Multiplatform code paths) → [state](https://developer.android.com/develop/ui/compose/state)
168
+ - state scattered across multiple sibling composables without a clear owner → [state hoisting](https://developer.android.com/develop/ui/compose/state-hoisting)
169
+ - `remember { computeFromInput(x) }` with no `key` — stale cached value when `x` changes → [state](https://developer.android.com/develop/ui/compose/state)
170
+ - `viewModel()` invoked deep in a composable tree (rather than at the screen entry point) or ViewModels passed via `CompositionLocal` → [architecture](https://developer.android.com/develop/ui/compose/architecture), [compositionlocal](https://developer.android.com/develop/ui/compose/compositionlocal)
171
+ - `rememberSaveable { mutableStateOf(SomeNonBundleable(...)) }` without a `Saver` — restoration silently fails after process death → [state](https://developer.android.com/develop/ui/compose/state)
172
+ - string-based navigation routes (`composable("home")`, `navigate("profile/$id")`) on Navigation Compose 2.8+ where type-safe `@Serializable` routes are available — loses compile-time checking and encourages argument-encoding bugs → [navigation](https://developer.android.com/develop/ui/compose/navigation)
173
+ - `mutableStateOf` held in a `ViewModel` instead of `StateFlow` / `MutableStateFlow` — couples the ViewModel to the Compose runtime and hurts testability. App-level teams may accept this tradeoff deliberately; note the tradeoff rather than deducting heavily unless it is widespread → [architecture](https://developer.android.com/develop/ui/compose/architecture)
174
+ - `Channel<UiEvent>` exposed as the one-shot event stream from a ViewModel without a buffered `SharedFlow` alternative — events silently drop when there is no active collector (configuration change, lifecycle transition). Prefer `MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST)` → [architecture](https://developer.android.com/develop/ui/compose/architecture)
175
+ - `rememberSaveable` used inside a `LazyListScope` item factory (per-item expansion state, per-item form fields) — each entry is serialized into the saved-state `Bundle` which is capped at ~1 MB; large lists trigger `TransactionTooLargeException` → [state](https://developer.android.com/develop/ui/compose/state)
176
+
177
+ Suggested interpretation:
178
+
179
+ - `9-10`: strong UDF, clear ownership, minimal ambiguity
180
+ - `7-8`: mostly healthy with some hoisting or saveability gaps
181
+ - `4-6`: repeated ownership confusion or weak state boundaries
182
+ - `0-3`: systemic duplication, stale data risks, or non-observable state misuse
183
+
184
+ ### Side Effects
185
+
186
+ All citations in this category point to the canonical [Side-effects in Compose](https://developer.android.com/develop/ui/compose/side-effects) page unless noted. The official docs put `derivedStateOf` and `snapshotFlow` under side-effects. v1 keeps `derivedStateOf` weighted under Performance because that's where its value most often lands in audits, but the side-effects category also looks at it for *misuse*. Pick the dominant category and cross-reference rather than double-counting.
187
+
188
+ Reward:
189
+
190
+ - side-effect-free composition → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
191
+ - correct use of `LaunchedEffect`, `DisposableEffect`, `SideEffect`, `rememberUpdatedState`, and `produceState` → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
192
+ - effects keyed to the right lifecycle inputs → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
193
+ - cleanup for listeners, observers, and subscriptions → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
194
+ - stale callback capture avoided with `rememberUpdatedState` when needed → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
195
+ - `snapshotFlow { … }` collected from inside a `LaunchedEffect` for Compose-state → Flow conversions → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
196
+ - `rememberCoroutineScope()` used only for event-driven work (button taps, gesture handlers); long-lived/keyed work lives in `LaunchedEffect` → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
197
+ - navigation, snackbar, analytics, and repository calls live in event handlers or `LaunchedEffect`, never in the composition body → [side-effects](https://developer.android.com/develop/ui/compose/side-effects), [navigation](https://developer.android.com/develop/ui/compose/navigation)
198
+
199
+ Deduct for:
200
+
201
+ - launching threads, coroutines, navigation, or external work directly in composition → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
202
+ - wrong effect API choice → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
203
+ - incorrect or missing effect keys → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
204
+ - stale captures in long-lived effects → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
205
+ - empty or suspicious `onDispose` → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
206
+ - listeners or observers registered without cleanup → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
207
+ - `snapshotFlow { … }` invoked outside an effect, or used to compute values that `derivedStateOf` would handle more cheaply → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
208
+ - `rememberCoroutineScope()` used to launch work that should live in a keyed `LaunchedEffect` (manual cancellation, lifecycle handling reinvented) → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
209
+ - `derivedStateOf { a + b }`-style misuse where inputs change as often as outputs — pure overhead per the official guidance → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
210
+ - `LaunchedEffect(Unit)` / `LaunchedEffect(true)` flagged only when the body captures parameter or state values that may change without being keyed or wrapped in `rememberUpdatedState`. The "run once on enter" pattern itself is idiomatic; do not deduct for it → [side-effects](https://developer.android.com/develop/ui/compose/side-effects)
211
+ - `navController.navigate(...)` invoked from the composition body instead of an event handler or effect → [navigation](https://developer.android.com/develop/ui/compose/navigation)
212
+
213
+ Suggested interpretation:
214
+
215
+ - `9-10`: deliberate, lifecycle-aware effect usage
216
+ - `7-8`: mostly correct with small effect-key or cleanup issues
217
+ - `4-6`: recurring misuse of effects or composition-time work
218
+ - `0-3`: side effects commonly happen in composition or cleanup is broadly unsafe
219
+
220
+ ### Composable API Quality
221
+
222
+ This category is lighter-touch for app repositories. Focus on shared internal components, UI kits, and reusable building blocks. The two authoritative sources are the [Compose API guidelines](https://developer.android.com/develop/ui/compose/api-guidelines) and the deeper [Component API guidelines](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md).
223
+
224
+ Reward:
225
+
226
+ - reusable components expose `modifier: Modifier = Modifier` → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
227
+ - `modifier` is the first optional parameter and applied once as the first link in the chain on the root-most UI node → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
228
+ - parameter order is sensible: required, `modifier`, optional, trailing content lambda → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
229
+ - defaults are meaningful and not hidden behind nullable sentinel behavior; defaults exposed through a `ComponentDefaults` object → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
230
+ - explicit parameters preferred over component-specific `CompositionLocal` indirection. `CompositionLocal` is appropriate for *tree-scoped* data with sensible defaults (theme tokens like `LocalContentColor`, `LocalTextStyle`); not appropriate for component-specific configuration → [compositionlocal](https://developer.android.com/develop/ui/compose/compositionlocal), [api-guidelines](https://developer.android.com/develop/ui/compose/api-guidelines)
231
+ - components are focused and layered instead of multipurpose grab-bags → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
232
+ - slot APIs (`content: @Composable RowScope.() -> Unit`) used for flexible composition; receiver scopes (`RowScope`, `ColumnScope`, `BoxScope`) applied where they guide layout → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
233
+ - `Basic*` naming for unstyled / minimal variants alongside the opinionated public version (e.g. `BasicTextField` ↔ `TextField`) → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
234
+ - distinct components for visual variants (`ContainedButton`, `OutlinedButton`, `TextButton`) instead of a single component with a `style` enum → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
235
+ - `@Composable` functions are PascalCase and Unit-returning where they emit UI → [api-guidelines](https://developer.android.com/develop/ui/compose/api-guidelines)
236
+ - custom modifiers built with `Modifier.Node` rather than the discouraged `composed { }` factory → [custom modifiers](https://developer.android.com/develop/ui/compose/custom-modifiers)
237
+ - `movableContentOf` / `movableContentWithReceiverOf` used to preserve slot-content lifecycle when content moves between containers → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
238
+ - reusable APIs prefer `value: T` (immediate read) or `value: () -> T` (deferred read) plus `onValueChange: (T) -> Unit` over `MutableState<T>` parameters → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
239
+ - isolated components define `@Preview` configurations to prove they render stateless → [tooling](https://developer.android.com/develop/ui/compose/tooling/previews)
240
+
241
+ Deduct for:
242
+
243
+ - shared components missing `modifier` → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
244
+ - multiple modifier params or modifier applied to the wrong child (anywhere other than the root-most emitted layout) → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
245
+ - hardcoded strings in UI elements (e.g. `Text("Share with friends")` instead of `stringResource(id = R.string...)`) which break i18n support and create brittle tests → [resources](https://developer.android.com/develop/ui/compose/resources)
246
+ - hardcoded magic numbers like `.padding(12.dp)` or `.size(24.sp)` or explicit `Color(0xFF...)` instead of routing through `MaterialTheme` tokens or `dimensionResource`. A clean theme is vital for dark mode and accessibility → [theming](https://developer.android.com/develop/ui/compose/designsystems/material3)
247
+ - no `@Preview` coverage for extracted UI chunks. If a component is reusable, it should have a preview proving it has no hidden ambient dependencies → [tooling](https://developer.android.com/develop/ui/compose/tooling/previews)
248
+ - `modifier` with a non-no-op default like `Modifier.padding(8.dp)` (caller's modifier silently loses the padding) → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
249
+ - parameter ordering that makes APIs awkward or misleading → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
250
+ - nullable params used only as "use internal default" signals — expose the default explicitly via a `ComponentDefaults` object instead → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
251
+ - passing raw network models, database entities, or complex domain objects directly into UI components instead of mapping them to stable, UI-specific presentation models (UiState). This leaks backend structure into the presentation tier, encourages giant data models, and often forces the Compose compiler to treat the arguments as unstable. → [architecture](https://developer.android.com/develop/ui/compose/architecture)
252
+ - `MutableState<T>` or `State<T>` params in reusable APIs when avoidable; the official replacement is `value: T` or `value: () -> T` plus `onValueChange: (T) -> Unit` → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
253
+ - giant multipurpose components that should be split or wrapped → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
254
+ - behavior added as parameters that should be modifiers (`onClick`, `clipToCircle`) → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
255
+ - style/configuration objects passed to a single component instead of distinct components per variant → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
256
+ - non-Compose lifecycles attached to composables — for example, an `onClick` callback on a layout component when `Modifier.clickable` would do → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
257
+ - modifier ordering that quietly changes semantics in shared components (e.g. `Modifier.padding(...).clickable {}` extends the click region into the padding; `Modifier.clickable {}.padding(...)` does not). Flag when the choice looks accidental → [custom modifiers](https://developer.android.com/develop/ui/compose/custom-modifiers)
258
+ - hardcoded `dp`, `sp`, or explicit `Color` constructs in reusable components instead of using `MaterialTheme.colorScheme`, `MaterialTheme.typography`, or dimension resources; this destroys dark mode compliance and accessible font scaling → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
259
+ - `CompositionLocal` used for component-specific configuration (vs. truly tree-scoped data); ViewModels stored in `CompositionLocal`; locals with no sensible default → [compositionlocal](https://developer.android.com/develop/ui/compose/compositionlocal)
260
+ - custom modifiers built with `Modifier.composed { }` when `Modifier.Node` would do — `composed { }` is officially discouraged for performance → [custom modifiers](https://developer.android.com/develop/ui/compose/custom-modifiers)
261
+ - `Scaffold { innerPadding -> ... }` content that does not apply `innerPadding` to its root child (or consume it via `consumeWindowInsets`) — content draws behind the `TopAppBar` / `BottomAppBar` / system bars → [component API](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-component-api-guidelines.md)
262
+
263
+ Suggested interpretation:
264
+
265
+ - `9-10`: shared UI APIs are clean, predictable, and reusable
266
+ - `7-8`: solid conventions with a few inconsistencies
267
+ - `4-6`: repeated API smell in shared components
268
+ - `0-3`: shared component APIs are confusing, rigid, or actively error-prone
269
+
270
+ ## Scoring Rules
271
+
272
+ - One isolated issue should not tank a whole category.
273
+ - Repeated issues across several modules should count more than a single bad file.
274
+ - Production code matters more than samples, previews, or scratch files.
275
+ - Positive patterns should raise confidence and may offset minor issues.
276
+ - App teams may intentionally deviate from framework/library guidance. Note the tradeoff before deducting heavily.
277
+ - **Every deduction in the report must include a `References:` line citing one or more URLs from the rubric above (or from `references/canonical-sources.md`).** A deduction without a citation should not appear in the report.