本文件定义仓库级不变量。专项流程放在 .agents/skills/,长期方案放在 docs/dev/
;不要把临时任务、讨论过程或一次性结论继续堆进本文件。
执行任务前按以下顺序建立上下文:
.agents/skills/*/SKILL.md 及其明确要求读取的 reference。docs/dev/ 中的专项设计或迁移计划。文档与源码不一致时,先指出差异。不要为了让实现符合过期文档而静默改代码。
legado-compose-migration。legado-compose-review。commonMain 抽取、KMP/CMP、expect/actual、平台能力适配、迁移门禁或相关脚手架:使用
legado-kmp-migration。Utils/Common 容器。当前工程是 Android-first 的渐进迁移仓库,不是已经完成的 KMP 工程:
:app、:modules:book、:modules:rhino、:baselineprofile;modules/web 是独立 Vue 3
工程。:app 同时包含 Android UI、数据、领域、服务和大量遗留 View 代码;包目录只表达逻辑边界,尚无 Gradle
编译隔离。domain/ 是迁移目标边界,但当前仍存在 Android/JVM 类型渗透;不要把“目标纯净度”描述成已完成事实。docs/dev/kmp-cmp-modernization.md。io.legado.app.feature.<name>;目录、Gradle
path、Kotlin package 分别采用 feature/book-info、:feature:book-info、feature.bookinfo。:app 内先形成稳定 Feature 包,再提升为真实 :feature:<name> 模块;只有确有多宿主/多模块消费者时才拆
api/impl,只有确有跨平台价值时才改为 KMP/CMP source sets。docs/dev/feature-first-structure.md,现有业务 owner 见 docs/dev/feature-catalog.md。ui/... 是渐进迁移区,不做全量机械搬家;一个 Feature 的新 owner 建立后,禁止继续向旧包新增同职责文件。app host / composition root
↓
feature implementation → feature API
↓
domain / model / platform contracts
↓
data abstractions
↓
platform implementations (Android/JVM/iOS/...)
commonMain 只容纳经过依赖审计、能被至少一个非 Android 目标编译验证的代码;移动目录不等于完成跨平台迁移。Context、Room 实体/DAO、Service、Broadcast、Activity Result、Cronet、文件
URI、通知、媒体会话、成熟阅读器渲染和现有 Rhino 集成默认留在平台侧,通过窄接口进入共享层。@Stable XxxUiState、XxxIntent、XxxEffect;ViewModel 直接继承
ViewModel,私有 MutableStateFlow / MutableSharedFlow(extraBufferCapacity = 16),对外只暴露只读
Flow,并由单一 onIntent 分发用户动作。共享 Feature 的状态宿主按 KMP skill 单独决策。@Stable。Compose 渲染边界中的集合使用
kotlinx.collections.immutable;内部计算和数据层不机械替换集合类型。MainActivity 的 Navigation
3 图组装。AppScaffold、主题、top bar、dialog/sheet、列表和设置组件。正确处理 edge-to-edge 与
predictive back。Intent/result 兼容确有需要时作为薄宿主保留。具体文件形态和审查清单由 Compose migration/review skill 维护,本文件不复制完整模板。
appDb.*Dao、网络客户端或旧偏好 API 直连;历史债务由
verifyConfigArchitecture 的基线棘轮冻结并逐步下调。update { current -> current.copy(...) } 修改状态;关联字段在一次
copy(...) 中原子提交。*SettingsUpdate 分发类型或 updateAll。ReadStyleMutation、ThemePackageSettingsGateway.applyAndAwait、ThemeStateTransaction、
AppUiConfigurationGateway 等专用 API 保持现有形态。singleOf / viewModelOf 约定。Context、File、Uri、Room 类型、Android resource id 或 JVM-only
stream;用领域值、ByteArray/抽象 source-sink、序列化模型或平台接口表达。expect/actual 只用于真正的平台原语。可用普通接口 + DI 表达的能力,不使用 expect/actual。api/impl 只有形成真实 Gradle 边界并存在跨 Feature/宿主调用时才创建,不能只建同名文件夹。build/generated,不得手工编辑或提交。每个迁移 PR 至少回答:
减少历史违规时必须同步下调对应 baseline;门禁不接受“先放宽基线再迁移”。详细分级见 KMP/CMP 现代化计划。
开发与 CI 使用 JDK 21。按风险选择最小充分验证:
# Kotlin/Compose 快速编译
.\gradlew.bat :app:compileAppDebugKotlin
# 当前主验证集
.\gradlew.bat testAppDebugUnitTest lintAppDebug verifyConfigArchitecture assembleAppDebug --continue --no-configuration-cache
# 资源、Manifest、生成绑定或打包变化
.\gradlew.bat :app:assembleAppDebug
# Release/R8 相关
.\gradlew.bat assembleAppRelease
.\gradlew.bat assembleAppNoR8
# Web 前端
pnpm --dir modules/web build
KMP 任务名在模块实际创建后才存在;不要假装运行尚未定义的 commonTest、目标编译或 API
检查任务。新增模块时把其真实任务加入 CI,并在交付中列出准确命令。
所有文本改动至少运行 git diff --check。构建通过不替代架构边界、行为和真机性能复核。
AnalyzeByJSoup.kt 与 JsoupXpath。Packages.cn.hutool.*);②help/crypto/CryptoUtils.kt 的 base64 解码统一走
cn.hutool.core.codec.Base64.decode,以兼容缺 = 填充等宽松输入(Kotlin
kotlin.io.encoding.Base64 严格填充会导致部分书源取章名/正文回归)。版本固定 5.8.22 勿升级。
应用内部加密主体仍用现有 JCA(javax.crypto/java.security)与 help/crypto 路径,KMP 抽取时
再通过能力契约替换 JVM API。io.legado.app,Android applicationId 为 io.legato.kazusa,不要混用。noR8 变体用于排障。modules/web 必须连接应用内 Ktor WebService;开发环境通过 VITE_API 指向设备服务地址。最终说明应包含:修改的职责范围、关键设计取舍、实际运行的验证与结果、未验证风险。不要把“已生成代码”或“已移动文件”当作完成标准。