关于 jetpack-compose-audit
用于审查 Android Jetpack Compose 仓库的证据型审计 Skill,面向评估 Compose 性能、状态管理、副作用和 Composable API 质量的开发者。它要求以代码证据和官方文档支持结论,并生成带评分和修复优先级的报告。
主要功能
- 01审查性能、状态、副作用和 API
- 02结合代码证据与官方文档
- 03生成分类评分和优先修复项
- 04尝试生成 Compose 编译器报告
- 05检查 Android 启动画面资源
适用场景
- 01评估 Compose 仓库质量
- 02定位重组或状态管理问题
- 03审查副作用与 Composable API
- 04检查启动画面图标模糊风险
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ hamen/ compose_skill --skill jetpack-compose-audit安装后在 Claude Code 中输入/jetpack-compose-audit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.claude-plugin/plugin.json.cursor-plugin/plugin.jsonevals/evals.jsonreferences/canonical-sources.mddiagnostics.mdreport-template.mdscoring.mdsearch-playbook.mdscripts/compose-reports.init.gradle详细介绍
Jetpack Compose 审计
此技能用于审计 Android Jetpack Compose 仓库,并生成严格、以证据为依据的报告。
技能版本: 4.3.2 — 发布于 2026-07-07。Compose 版本线: Kotlin 2.0.20+ / Compose Compiler 1.5.4+(默认启用 Strong Skipping Mode)。如需了解变更内容,请参阅 README 更新日志。
此技能有意聚焦于以下四个类别:
- 性能
- 状态管理
- 副作用
- Composable API 质量
v1 不会对设计或 Material 3 合规性评分。如果审计发现可能存在设计系统问题,请建议使用 material-3 技能进行后续审计(参考实现:https://github.com/hamen/material-3-skill)。
测试、焦点/键盘导航和 Compose Multiplatform 属于覆盖范围说明,而非评分类别。若存在这些方面,请梳理相应情况、指出明显风险,并建议后续参考 compose-agent(testing、focus、kmp)。除非同一根因明显影响四个评分类别之一,否则不要将这些方面计入 0-100 分。
Android 启动体验资源也属于相关覆盖范围,而非评分类别。始终检查启动屏幕主题资源;如果 Android 12+ 可能渲染出模糊的静态启动图标,请将其列为 Android Launch UX 问题,并在证据明确时纳入 Critical Findings / Prioritized Fixes。
v1 中不涵盖的范围
这些是有意设定的范围边界;请在报告中指出限制,不要默默地给出覆盖不足的审查结果:
- Material 3 合规性、主题、颜色/字体排版令牌——请交由
material-3技能处理。 - 无障碍评分(
semantics、内容描述、触控目标尺寸)——将明显问题作为说明指出,不评分。 - UI 测试覆盖率和 Compose 测试规则模式——说明是否存在,不评分;建议使用
compose-agent focus on testing深入审查。 - Compose Multiplatform 特有规则(
expect/actual、特定目标代码路径)——说明涉及范围和明显的平台边界风险;建议使用compose-agent focus on kmp深入审查。 - Wear OS / TV / Auto / Glance 界面——说明明显的焦点/键盘/D-pad 风险;建议使用
compose-agent focus on focus深入审查。 - 构建性能(增量编译、KSP/KAPT 选择)。
如果用户明确要求审查上述任一方面,请缩小审查范围并在报告中说明。
适用场景
当用户要求以下事项时,使用此技能:
- 审计 Jetpack Compose 仓库
- 审查 Compose 架构或质量
- 对代码库打分
- 检查重组、状态或副作用问题
- 找出现有仓库中违反 Compose 最佳实践的情况
常见触发语句:
- “审计这个 Compose 仓库”
- “给这个 Jetpack Compose 代码库打分”
- “审查状态提升和副作用”
- “检查 Compose 性能”
- “给这个仓库评分”
预期输出
同时生成:
- 名为
COMPOSE-AUDIT-REPORT.md的仓库报告文件 - 一段简短的聊天摘要,包含总分、各类别分数、最严重的问题和首要修复项
审计原则
- 严格审查,但以证据为依据。
- 不要只根据搜索结果判断类别。先阅读有代表性的文件,再做评估。
- 对报告中的每项重要扣分,都要引用具体文件路径。
- 每项扣分都要引用官方文档 URL。 不要给出“相信我”的结论——评分标准中的每条规则都映射到
references/canonical-sources.md中的规范来源。报告模板要求每项发现都包含一行References:。 - 优先采用 Android 官方指导,而不是未经证实的经验之谈。
- 重视性能,但不要将其视为唯一评估角度。
- 不要因应用代码未满足公共库的纯粹性要求而扣分。API 质量检查主要针对可复用的内部组件、设计系统组件和共享 UI 构建块。
- 只有存在反复出现或系统性问题时,才给出
0-3分;孤立错误不足以此评分。 - 除非仓库在该类别中整体表现始终出色,否则不要给出
9-10分。
流程
1. 确认范围
确定目标路径:
- 如果用户传入了明确路径(
[repo path or module path]),则使用该路径。 - 如果未传入路径,则默认使用当前工作目录。
- 如果路径不存在,请让用户说明清楚。
映射模块之前,先确认项目中确实使用了 Compose(快速失败):
- 在任何
build.gradle*或libs.versions.toml中搜索androidx.compose - 在
src/下搜索setContent {或@Composable
如果两项都没有结果,请停止并报告目标不在范围内。不要先执行完整的模块映射。
如果 Compose 只出现在 samples/、demos/ 或测试源代码中(生产代码中没有使用),则将范围缩小到这些目录,将置信度设为 Low,并在报告中说明审计针对的是示例代码,而非生产路径。不要用演示代码评估生产质量类别。
2. 映射代码库
评分前,确定以下内容:
- Gradle 模块
- Android 应用模块和功能模块
- 可能的 Compose 源代码根目录
- 共享 UI/组件包
- 主题或设计系统包
- 状态持有者或 ViewModel 所在区域
- 测试和预览位置
- UI 测试、截图测试、焦点/键盘测试和语义测试的位置
- 是否存在与基线配置文件相关的模块或配置
- 是否存在 KMP/Compose Multiplatform 源集(
commonMain、androidMain、iosMain、desktopMain、wasmJsMain) - 是否存在 Android 启动屏幕主题和可绘制资源(
res/values*/themes.xml、styles.xml、drawable-v31)
3. 构建 Compose 界面映射
查找:
@Composable函数- 可复用 UI 组件
- 屏幕和路由
ViewModel的使用情况remember、rememberSaveable、mutableStateOfcollectAsStateWithLifecycle、collectAsStateLaunchedEffect、DisposableEffect、SideEffect、rememberUpdatedState、produceStateLazyColumn、LazyRow、items、itemsIndexed- 动画 API:
animate*AsState、Animatable、updateTransition、rememberInfiniteTransition、AnimatedVisibility、AnimatedContent、Crossfade - 焦点 API:
FocusRequester、focusRequester、focusProperties、focusable、onFocusChanged、onPreviewKeyEvent、onKeyEvent - UI 测试 API:
createComposeRule、createAndroidComposeRule、onNodeWithText、assertIsDisplayed、assertIsFocused、截图测试规则 - KMP/CMP 指示项:
commonMain、expect、actual、AndroidView、UIKitView、传入通用 UI 的平台服务 - Android 启动资源:
windowSplashScreenAnimatedIcon、android:windowSplashScreenAnimatedIcon、drawable-v31、animated-vector
如果代码库较大,请按类别或模块进行审计。如果可以使用子 Agent,请通过启动 Explore 类型的子 Agent 并行扫描各类别(不使用写入工具),然后汇总结果。
3a. 审查 Android 启动体验资源
这项检查属于非评分的相关覆盖项,但每次常规审查都应执行,因为该问题对用户可见,而且仅扫描 Kotlin 代码时很容易漏掉。
请查找:
res/values*/themes.xml和res/values*/styles.xml中的windowSplashScreenAnimatedIcon和android:windowSplashScreenAnimatedIcon- 引用的可绘制资源,例如
@drawable/ic_splash res/drawable-v31/中按 API 版本限定的覆盖项- 可绘制资源 XML 根元素:
<animated-vector>、<vector>、<adaptive-icon>、<bitmap>、<layer-list>
AnimatedVectorDrawable 实现了 Animatable,因此平台会在 SurfaceView 上以原始尺寸绘制它;静态图标则会走内部的 ImmobileIconDrawable 路径(先以 108 dp 预渲染,再放大到 160/192 dp 图标的尺寸),这正是 XHDPI+ 设备上图标显得模糊的原因。满足以下所有条件时,请报告一项发现:
- 某个 Android 应用的主题设置了
windowSplashScreenAnimatedIcon。 - API 31+ 的资源解析指向静态图标(
<vector>、<adaptive-icon>、位图或包含这些资源的图层列表),或者引用的启动图标没有drawable-v31覆盖项。 - API 31+ 上的资源不是
<animated-vector>。
请将其报告为 Android Launch UX:Android 12+ 静态启动图标可能会渲染模糊,而不是作为 Compose 评分扣分项。证据应包括主题项、解析到的可绘制资源文件,以及是否缺少 res/drawable-v31 中的 animated-vector 覆盖项。修复方向是保持主题中的 @drawable/<name> 不变,并让它在 API 31+ 上解析到一个封装实际 vector 的 <animated-vector>——不需要动画器;空的 <animated-vector> 就足够了,因为决定渲染路径的是它属于 AVD。报告中还应提及一个重要的正确性注意事项:封装器必须引用一个名称不同的 vector(引用自身主题名称的封装器会在 API 31+ 上重新解析到自身,从而陷入循环)。请引用 Android 启动画面文档、AndroidX SplashScreen 参考文档,以及平台问题 https://issuetracker.google.com/issues/520672537(其中引用了 AOSP 源码)。该问题始于 Android 12(API 31)上的 XHDPI+ 设备,并且在此技能发布时仍处于开放状态;请根据问题页面确认当前状态,而不要直接断言它仍未解决。
4. 生成 Compose 编译器报告(自动)
不要要求用户编辑 build.gradle 或自行运行命令。此技能会使用随附的 Gradle 初始化脚本运行构建,为每个 Compose 模块注入 reportsDestination / metricsDestination,且不会修改目标仓库中的任何文件。评分前,请尝试以下步骤:
-
定位技能附带的初始化脚本:
scripts/compose-reports.init.gradle。其绝对路径就是技能的安装位置——大多数安装位于~/.claude/skills/jetpack-compose-audit/scripts/compose-reports.init.gradle。如果无法解析该路径,则回退到将脚本写入<target>/.compose-audit-reports.init.gradle,并在运行后将其删除。 -
检查目标项目中是否有 Gradle 包装器:
test -x <target>/gradlew。如果没有,则跳至第 6 步的回退方案。 -
选择编译目标。 优先选择仍会触发 Compose 模块 Kotlin 编译且成本最低的任务:
- 使用
rg -l 'com\.android\.application' -g '*.gradle*'查找第一个应用模块 - 按以下顺序尝试:
:<app-module>:compileReleaseKotlinAndroid、:<app-module>:compileReleaseKotlin、assembleRelease、assembleDebug - 如果项目只有 Compose 库(
com.android.library),则改用该模块。
- 使用
-
运行构建。 告知用户构建即将开始(可能需要几分钟)。
cd <target> && ./gradlew <task> \ --init-script <path-to>/compose-reports.init.gradle \ --no-daemon --quiet使用 600 秒超时。如果任务失败,则按第 3 步中的顺序再尝试下一个回退任务一次。不要无限循环。
-
收集报告。
find <target> -path '*/build/compose_audit/*' \ \( -name '*-classes.txt' -o -name '*-composables.txt' -o -name '*-composables.csv' -o -name '*-module.json' \)从这些文件中提取:
- 不稳定类(
*-classes.txt中以unstable class开头的行),且这些类被用作可组合项参数 - 不可跳过但可重启的命名可组合项(忽略零参数 lambda;聚焦于
*-composables.txt或*-composables.csv中isLambda == "0"的实际命名函数) - 模块级可跳过性计数(来自
*-module.json),并根据*-composables.csv计算仅命名项的可跳过性(过滤掉isLambda == "1"的行,并计算sum(skippable) / sum(restartable))。在 Performance 部分引用这两项,并指出零参数 lambda 可能会人为拉高模块级指标。
- 不稳定类(
-
构建失败或 Gradle 不可用时的回退方案。 根据源码推断稳定性问题,但须:
- 在报告的 Notes And Limits 中设置
Compiler diagnostics used: no,并简要说明失败原因(缺少包装器、编译错误、超时) - 将总体置信度降低一级
- 将每项与稳定性相关的推断注明为“根据源码推断——未经编译器报告验证”
- 在报告的 Notes And Limits 中设置
第 5 步得出的稳定性结论属于实测证据,应按通常权重评估。第 6 步的回退结论属于推断,必须在报告中明确标注。
5. 审查四个类别
使用 references/scoring.md 中的评分标准和 references/search-playbook.md 中的启发方法。
**Navigation 3:**如果仓库使用
rememberNavBackStack、NavDisplay或NavKey,请在评分前运行 playbook 第 2b 节(Nav3 检测与审查)。Nav3 的发现对应 State Management 和 Side Effects——请参阅第 2b 节中的评分说明。
Performance
重点关注:
- 组合期间的高开销工作
- 可避免的重组
- 懒加载列表的键
- 存在
collectAsLazyPagingItems时,分页列表的键以及对LazyPagingItems的误用 - 状态读取时机不当
- 不稳定或范围过宽的读取
- 反向写入,包括跨阶段反向写入(布局回调写入在组合期间读取的状态)以及可组合项主体中的快照集合自失效
- 动画阶段使用是否正确(通过 lambda 修饰符将逐帧变化的值延后到布局/绘制阶段、在
remember中持有Animatable、合理限定rememberInfiniteTransition的作用域,使其离屏时停止) - 明显的发布版性能优化实践
状态管理
重点关注:
- 状态提升是否正确
- 单一事实来源
- 可复用的无状态边界
- 是否正确使用
remember与rememberSaveable - 是否使用具备生命周期感知能力的可观察集合
- 存在
LazyPagingItems时对分页LoadState的处理(加载 / 空状态 / 错误 / 追加) - 可观察与不可观察的可变状态
副作用
重点关注:
- 是否在组合过程中错误地执行副作用
- 是否正确选择副作用 API
- 副作用键
- 是否捕获过期的 lambda
- 清理是否正确
- 副作用行为是否具备生命周期感知能力
- 对于由目标驱动的状态变化,是否使用
LaunchedEffect(target)驱动动画(而不是在组合主体中驱动;rememberCoroutineScope().launch { animateTo(...) }通常用于由事件或手势驱动的动画) - 是否在组合过程中或
LaunchedEffect(Unit)中调用分页LazyPagingItems.refresh()/retry()来“进入时加载”——初始加载应由PagingData负责;这些操作仅应由用户发起
Composable API 质量
重点关注可复用的内部组件,而不是每个叶子屏幕。
检查:
- 是否存在
modifier,以及其位置是否正确 - 参数顺序
- 显式配置优于隐式配置
- 默认值是否有意义
- 在可复用 API 中,是否避免使用
MutableState<T>或State<T>参数(若存在更合适的 API 形式) - 对于调用方可能合理地需要控制时长的可复用动画组件,是否公开
animationSpec: AnimationSpec<T>;对于共享或工具可见的动画,是否使用有意义的label值 - 组件的用途与分层
6. 验证发现
扣分之前:
- 阅读异味出现的文件
- 确认该模式确实存在,而非误报
- 检查代码库其他位置是否已有弥补该问题的做法
- 区分个别错误与系统性模式
- 对于稳定性发现(可跳过 / 可重启 / 不稳定参数),使用第 4 步生成的编译器报告。引用具体的报告行(例如
app/build/compose_audit/app_release-classes.txt:42)作为证据。只有在第 4 步失败时,才可退而使用基于源码推断的稳定性结论,并将其标注为推断。
7. 评分
为每个类别赋予 0-10 分,并标注状态:
0-3:不合格4-6:需要改进7-8:良好9-10:优秀
使用 references/scoring.md 中的权重计算总分。
必须遵守实测上限,不可将其视为建议。 第 4 步生成编译器报告时,references/scoring.md 中的性能评分标准会规定一个上限。首先判断 Strong Skipping 是否已启用(Kotlin 2.0.20+ / Compose Compiler 1.5.4+,或显式启用相关标志),并选择匹配的表格——SSM 关闭时的表格依据 skippable% 和不稳定类数量;SSM 开启时的表格依据 skippable%、实例重建 churn,以及不稳定参数的 equals() 质量。在得出定性性能评分后,必须应用上限;如果评分超过上限,就要降低评分。在报告中展示计算过程,并注明使用了哪张表:
Performance ceiling check:
Strong Skipping: OFF (Kotlin 1.9.x, no opt-in) → applying SSM-off table
skippable% = 186/273 = 68.1% → falls in 50-70% band → cap at 4
qualitative score: 7
applied score: 4 (ceiling lowered from 7)
在 Strong Skipping 下,skippable% 通常会接近 100%,不再是决定上限的主要因素;上限通常取决于观察到的 churn(传入 composable 的 listOf(...) / mapOf(...) / 新建字面量),或不稳定参数上代价高昂 / 有缺陷的 equals()。请在上限核对部分明确列出这些发现,以便读者审核评分依据。
不要将 skippable% 向上取整到更高的区间。68.1% 不等于 ≥70%。如果定性评分不高于上限,则无需调整,但报告中仍必须包含核对过程,以便读者审核。
如果某个类别确实缺少足够的可审计内容,请标记为 N/A,说明原因,并对其余权重进行归一化。
8. 编写报告
使用 references/report-template.md。
报告必须包含:
- 总体评分
- 分类评分表
- 最重要的关键发现
- 逐类别分析理由
- 证据文件路径
- 按优先级排序的修复清单
- 如果怀疑存在设计问题,可选择注明后续运行
material-3 - 可选择注明测试、焦点/键盘以及 KMP/CMP 界面的覆盖情况;除非这些说明与评分类别重叠,否则不影响评分
将报告写入:
- 被审计目标(用户传入的路径)内的
COMPOSE-AUDIT-REPORT.md,而非当前工作目录。
如果该路径下已存在 COMPOSE-AUDIT-REPORT.md,请勿直接覆盖。应先征得用户同意,或在同一目录下写入 COMPOSE-AUDIT-REPORT-<YYYY-MM-DD>.md。
9. 返回简短摘要
在聊天中,提供一份与报告中「优先修复项」部分一致的摘要,而非泛泛的回顾。开发者无需打开报告文件,也应能仅凭这份摘要采取行动。
请包含:
- 总体评分(如果同一路径下存在先前的
COMPOSE-AUDIT-REPORT*.md,还需注明与先前评分的差异) - 每个类别的一句话评判,并注明适用的上限(如:「性能 8/10 — 受 SSM-on 表限制:重新创建了
FeedItemUiModel参数」) - 如果动画缺陷影响了性能,请在「性能」一项中明确指出(如:「性能 6/10 — 动画
offset读取导致DrawerContent每帧重新组合」) - 如果动画缺陷影响了副作用,请在「副作用」一项中明确指出(如:「副作用 5/10 — 在
SettingsScreen组合过程中调用了scope.launch { animateTo(...) }」) - 如果分页缺陷影响了副作用,请在「副作用」一项中明确指出(如:「副作用 6/10 —
FeedScreen上的LaunchedEffect(Unit) { feed.refresh() }在组合过程中加载数据」) - 当步骤 4 成功时,提供编译器报告要点:Strong Skipping 开启/关闭(或各模块设置不一)、应用了哪个上限表、模块整体的
skippable%、仅命名项的skippable%、实际决定上限的指标、不稳定共享类型的数量,以及任何构建失败的模块 - 最重要的三项可操作修复,每项包含:
- 具体改动(「在
feed/FeedList.kt:42的items(...)中添加key = { it.id }」) - 文件路径和行号——与报告「优先修复项」中列出的一致
- 一个来自
references/canonical-sources.md的官方文档 URL - 与当前适用的上限表相符的预期影响:SSM 关闭时,应围绕仅命名项的
skippable%/ 不稳定参数减少来表述;SSM 开启时,应围绕消除实例重新创建开销、修复昂贵或错误的equals(),或解除决定上限的限制来表述
- 具体改动(「在
- 是否值得接下来运行
material-3审计 - 是否值得接下来运行专项检查:
compose-agent focus on testing、compose-agent focus on focus、compose-agent focus on kmp、compose-agent focus on animation或compose-agent focus on paging
聊天摘要中的前三项修复必须与报告「优先修复项」中的内容一致(文件路径和文档链接也必须相同)。不要在聊天中添加报告未包含的泛泛建议。
证据规则
- 尽量提供多个示例,而非只举一个醒目的例子。
- 不仅要指出问题,也要提供正面证据。
- 不要推断无法从代码中证实的运行时问题。
- 如果某条规则依据官方指南,但应用层面的权衡可能足以解释偏离,请将其说明为一种权衡,而不要假定偏离总是错误的。
大型仓库策略
对于中大型仓库:
- 先梳理模块。
- 为每个模块挑选有代表性的文件。
- 尽可能并行扫描各类别。
- 将重复发现归并为系统性问题,而不是把同一问题列出二十遍。
应避免的事项
- 不要在没有仓库证据的情况下给出通用检查清单。
- 如果仓库是应用,不要把报告写成公共库 API 讲解。
- 不要仅仅因为应用使用了 Compose 就抬高性能评分。
- 不要对孤立的实验或示例文件过度扣分,除非它们属于生产路径。
- v1 中不要评估设计。
- 不要单独标记
LaunchedEffect(Unit)或LaunchedEffect(true)——“仅运行一次”是惯用模式。只有当其代码块捕获的值可能发生变化,且未使用rememberUpdatedState时才标记。Paging 例外:LaunchedEffect(Unit) { lazyPagingItems.refresh() }(或retry())属于副作用缺陷——初始分页加载是PagingData的职责,而refresh()/retry()应由用户发起。请标记。 - 不要因 Compose Multiplatform 代码路径使用 Android 专属 API(
collectAsStateWithLifecycle、lifecycle-runtime-compose)而扣分。应将平台限制作为权衡因素说明。 - 不要在不同类别中对同一根本原因重复计分。稳定性问题通常会同时出现在“性能”和“状态”类别中——选择主要类别,并交叉引用。
参考资料
references/scoring.md— 含行内引用的逐条规则评分标准references/search-playbook.md— 搜索模式和风险信号启发式规则references/report-template.md—COMPOSE-AUDIT-REPORT.md的必需结构references/canonical-sources.md— 每项扣分都必须引用的官方 URLreferences/diagnostics.md— 可直接复制粘贴的 Gradle/代码片段,用于 Compose Compiler 报告、稳定性配置、基线配置文件和 R8 检查scripts/compose-reports.init.gradle— 技能在第 4 步中通过--init-script注入的 Gradle 初始化脚本,用于自动生成编译器报告
验收评估
evals/evals.json 保存了验收用例,用于评估对现有代码的审计——每个 {prompt, expected_output, expectations} 都是一段代码片段,以及审计必须得出的发现。用相反方向的 compose-agent 写入模式评估用例,镜像覆盖了第 12–14 个用例(跨阶段回写、Strong Skipping 错误线索、快照自失效),因此评分路径与创作路径保持一致。针对每个提示词运行一个模型,并检查每项预期结果。