mcpField
J
jetpack-compose-audit

jetpack-compose-audit

github.com/hamen测试与质量375210 次安装15 周前更新
代码仓库

关于 jetpack-compose-audit

用于审查 Android Jetpack Compose 仓库的证据型审计 Skill,面向评估 Compose 性能、状态管理、副作用和 Composable API 质量的开发者。它要求以代码证据和官方文档支持结论,并生成带评分和修复优先级的报告。

主要功能

  1. 01审查性能、状态、副作用和 API
  2. 02结合代码证据与官方文档
  3. 03生成分类评分和优先修复项
  4. 04尝试生成 Compose 编译器报告
  5. 05检查 Android 启动画面资源

适用场景

  1. 01评估 Compose 仓库质量
  2. 02定位重组或状态管理问题
  3. 03审查副作用与 Composable API
  4. 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.json
evals/
evals.json
references/
canonical-sources.md
diagnostics.md
report-template.md
scoring.md
search-playbook.md
scripts/
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、mutableStateOf
  • collectAsStateWithLifecycle、collectAsState
  • LaunchedEffect、DisposableEffect、SideEffect、rememberUpdatedState、produceState
  • LazyColumn、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+ 设备上图标显得模糊的原因。满足以下所有条件时,请报告一项发现:

  1. 某个 Android 应用的主题设置了 windowSplashScreenAnimatedIcon。
  2. API 31+ 的资源解析指向静态图标(<vector>、<adaptive-icon>、位图或包含这些资源的图层列表),或者引用的启动图标没有 drawable-v31 覆盖项。
  3. 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,且不会修改目标仓库中的任何文件。评分前,请尝试以下步骤:

  1. 定位技能附带的初始化脚本:scripts/compose-reports.init.gradle。其绝对路径就是技能的安装位置——大多数安装位于 ~/.claude/skills/jetpack-compose-audit/scripts/compose-reports.init.gradle。如果无法解析该路径,则回退到将脚本写入 <target>/.compose-audit-reports.init.gradle,并在运行后将其删除。

  2. 检查目标项目中是否有 Gradle 包装器:test -x <target>/gradlew。如果没有,则跳至第 6 步的回退方案。

  3. 选择编译目标。 优先选择仍会触发 Compose 模块 Kotlin 编译且成本最低的任务:

    • 使用 rg -l 'com\.android\.application' -g '*.gradle*' 查找第一个应用模块
    • 按以下顺序尝试::<app-module>:compileReleaseKotlinAndroid、:<app-module>:compileReleaseKotlin、assembleRelease、assembleDebug
    • 如果项目只有 Compose 库(com.android.library),则改用该模块。
  4. 运行构建。 告知用户构建即将开始(可能需要几分钟)。

    cd <target> && ./gradlew <task> \
        --init-script <path-to>/compose-reports.init.gradle \
        --no-daemon --quiet
    

    使用 600 秒超时。如果任务失败,则按第 3 步中的顺序再尝试下一个回退任务一次。不要无限循环。

  5. 收集报告。

    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 可能会人为拉高模块级指标。
  6. 构建失败或 Gradle 不可用时的回退方案。 根据源码推断稳定性问题,但须:

    • 在报告的 Notes And Limits 中设置 Compiler diagnostics used: no,并简要说明失败原因(缺少包装器、编译错误、超时)
    • 将总体置信度降低一级
    • 将每项与稳定性相关的推断注明为“根据源码推断——未经编译器报告验证”

第 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

聊天摘要中的前三项修复必须与报告「优先修复项」中的内容一致(文件路径和文档链接也必须相同)。不要在聊天中添加报告未包含的泛泛建议。

证据规则

  • 尽量提供多个示例,而非只举一个醒目的例子。
  • 不仅要指出问题,也要提供正面证据。
  • 不要推断无法从代码中证实的运行时问题。
  • 如果某条规则依据官方指南,但应用层面的权衡可能足以解释偏离,请将其说明为一种权衡,而不要假定偏离总是错误的。

大型仓库策略

对于中大型仓库:

  1. 先梳理模块。
  2. 为每个模块挑选有代表性的文件。
  3. 尽可能并行扫描各类别。
  4. 将重复发现归并为系统性问题,而不是把同一问题列出二十遍。

应避免的事项

  • 不要在没有仓库证据的情况下给出通用检查清单。
  • 如果仓库是应用,不要把报告写成公共库 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 — 每项扣分都必须引用的官方 URL
  • references/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 错误线索、快照自失效),因此评分路径与创作路径保持一致。针对每个提示词运行一个模型,并检查每项预期结果。

评论

支持 Markdown