Przeglądaj źródła

feat: 神的规范

HapeLee 2 miesięcy temu
rodzic
commit
a3a5058d67

+ 5 - 0
.agents/skills/legado-compose-migration/SKILL.md

@@ -24,6 +24,8 @@ Before editing, inspect the target View implementation if one exists, `MainActiv
    - For new screens, read `MainActivity`, nearby route screens, the relevant ViewModel/usecase/repository patterns, and shared UI components.
    - List UI state, user intents, one-shot effects, and external side effects.
    - Identify reusable Compose components under `ui/widget/components` before creating new components.
+   - Read `docs/dev/feature-first-structure.md`, identify the canonical Feature owner, and avoid
+     adding a second presentation package under legacy `ui/...`.
 
 3. Choose the minimal migration shape.
    - For new Compose-first screens, add a `MainActivity` navigation destination instead of creating a standalone Activity.
@@ -37,6 +39,9 @@ Before editing, inspect the target View implementation if one exists, `MainActiv
    - Use existing repositories/usecases when they already fit; introduce new domain/usecase classes when a new screen needs clean business boundaries or when a migration would otherwise duplicate or entangle business logic.
 
 4. Implement by layers.
+    - Place new Compose-first artifacts under `io.legado.app.feature.<name>` using the repository
+      feature-first convention. Keep an old `ui/...` Activity only as an explicit compatibility
+      bridge when required.
    - Create or update `FeatureContract.kt` first for `UiState`, `Intent`, `Effect`, dialog/sheet models, and menu action enums.
    - Update `FeatureViewModel.kt` to expose `uiState: StateFlow<UiState>` and `effects: SharedFlow<Effect>`, with a single `onIntent(...)` entry point unless the existing feature has a simpler established pattern.
    - Create `FeatureScreen.kt` as a stateless route-level composable: `state`, callbacks, and `onIntent`.

+ 16 - 4
.agents/skills/legado-compose-migration/references/project-patterns.md

@@ -4,7 +4,10 @@
 
 - App module: `app`.
 - Main source root: `app/src/main/java/io/legado/app`.
-- UI package: `ui/...`, usually grouped by feature.
+- Legacy UI package: `ui/...`; it remains a migration zone, not the destination for new
+  Compose-first Feature ownership.
+- Canonical new Feature package: `feature/<name>` as defined by
+  `docs/dev/feature-first-structure.md`.
 - Data package: `data/dao`, `data/entities`, `data/repository`.
 - Domain package: `domain/gateway`, `domain/model`, `domain/repository`, `domain/usecase`.
 - DI: `di/appModule.kt`, using Koin `singleOf`, `viewModelOf`, and parameterized `viewModel { ... }`.
@@ -36,17 +39,26 @@ Inspect these as local examples, not as APIs to copy blindly:
 For a migrated feature, prefer colocated files:
 
 ```text
-ui/<area>/<feature>/
+feature/<feature>/
   FeatureActivity.kt       # only for legacy Intent compatibility or full-screen migration host
   FeatureContract.kt
   FeatureViewModel.kt
+  FeatureRoute.kt          # only when route/host wiring is substantial
   FeatureScreen.kt
-  FeatureSheets.kt        # only if sheets are substantial
-  FeatureDialogs.kt       # only if dialogs are substantial
+  components/              # Feature-private UI only
+  sheet/                   # only if sheets are substantial
+  dialog/                  # only if dialogs are substantial
+  model/                   # presentation-only UI models
+  legacy/                  # temporary compatibility bridge with removal condition
 ```
 
 Use the smallest set of files. Do not split files just to match this shape.
 
+The directory is `feature/book-info`, the Gradle path is `:feature:book-info`, and the Kotlin
+package is `io.legado.app.feature.bookinfo`. Start with this shape inside `:app`; promote it to a
+Gradle module only after the boundary has real callers and verification. Do not create `api/impl` by
+default.
+
 ## MVI and UDF Rules
 
 Use this shape for behavior-heavy screens:

+ 4 - 0
.agents/skills/legado-compose-review/references/review-checklist.md

@@ -7,6 +7,7 @@ For a feature review, inspect the smallest complete slice:
 - `FeatureScreen.kt` and any `FeatureSheets.kt` / `FeatureDialogs.kt`.
 - `FeatureViewModel.kt`.
 - `FeatureContract.kt` if present.
+- Canonical owner under `feature/<name>` or the documented reason it remains in legacy `ui/...`.
 - Host route in `MainActivity.kt` or retained `FeatureActivity.kt`.
 - Koin registration in `di/appModule.kt`.
 - Repositories/usecases used by the feature.
@@ -32,6 +33,9 @@ Flag issues when:
 - MVI `FeatureIntent` user actions are confused with Android `Intent` launch/extras.
 - Domain/usecase boundaries are bypassed in new screens when a meaningful business action exists.
 - A migration introduces new domain abstractions for a single trivial UI action without reducing real complexity.
+- New Compose-first files extend a legacy `ui/...` package after the Feature has a canonical
+  `feature/<name>` owner, or the same Feature is split across both roots without an explicit
+  compatibility boundary.
 
 ## UDF and State Checks
 

+ 99 - 0
.agents/skills/legado-kmp-migration/SKILL.md

@@ -0,0 +1,99 @@
+---
+name: legado-kmp-migration
+description: Plan, implement, or review Legado Gradle modularization and Kotlin/Compose Multiplatform migration slices. Use for build-logic, module boundaries, commonMain extraction, expect/actual or platform interfaces, CMP feature sharing, KMP migration gates, capability matrices, and related scaffolding. Do not use for an ordinary Android-only Compose screen migration unless it also changes a multiplatform or Gradle boundary.
+---
+
+# Legado KMP/CMP Migration
+
+## Purpose
+
+Move one verified boundary toward KMP/CMP without weakening Android behavior. Treat
+platform-specific implementations as valid architecture, keep the Android app shippable after every
+slice, and use baselines as ratchets rather than waivers.
+
+Before acting, read repository `AGENTS.md` and `docs/dev/kmp-cmp-modernization.md`. For an
+implementation or review, also read [references/slice-checklist.md](references/slice-checklist.md).
+
+## Select the mode
+
+- **Plan:** produce a dependency inventory, target seam, capability impact, phases, gates and
+  rollback point. Do not create the full target module tree.
+- **Extract:** move a narrow set of models/rules/contracts behind adapters, migrate callers, run
+  both common and Android verification, then lower relevant baselines.
+- **Build logic:** add or change convention plugins and dependency rules using one representative
+  module before broad rollout.
+- **CMP feature:** share state/reducer/UI only where platform effects and navigation have explicit
+  host boundaries.
+- **Scaffold:** generate only conventions proven by at least two accepted manual examples; require
+  dry-run and no-overwrite behavior.
+- **Review:** report behavior and boundary findings before recommending changes. Do not edit unless
+  fixes were requested.
+
+## Required workflow
+
+1. Define the slice and evidence.
+    - Name exact source files/packages, current callers, intended target module/source set, and
+      behavior that must not change.
+    - Separate current facts from planned modules and task names.
+    - Inspect Gradle files, version catalog, relevant tests, and imports rather than assuming an API
+      is multiplatform.
+    - For Feature work, read `docs/dev/feature-first-structure.md` and distinguish package
+      colocation, Android module extraction, and KMP conversion as separate stages.
+
+2. Classify every dependency.
+    - `common-ready`: Kotlin/common library API with a non-Android compile target.
+    - `contract-needed`: behavior can be represented by a narrow interface and platform
+      implementation.
+    - `platform-island`: lifecycle, service, reader rendering, Rhino/JVM or another capability that
+      should remain platform-specific.
+    - `unknown`: verify against primary documentation or a compile PoC before designing around it.
+
+3. Choose the smallest seam.
+    - Prefer stable values, pure rules, gateways and use cases before storage, engines and UI.
+    - Prefer ordinary interfaces plus DI over `expect/actual`; reserve `expect/actual` for platform
+      primitives that every target must provide statically.
+    - Keep platform assembly in the app/Koin composition root.
+    - Do not change storage, network, navigation, DI and UI technology in the same slice.
+    - Create Feature `api/impl` only when a real Gradle boundary and caller require it.
+
+4. Establish gates before moving code.
+    - Add characterization/contract tests at the old boundary.
+    - Use report → freeze baseline → blocking for a new rule.
+    - Never raise a platform-import or dependency baseline to make a migration pass.
+
+5. Implement additively.
+    - Introduce contract/module and Android adapter first.
+    - Migrate a bounded caller set.
+    - Delete the old entry only after no callers remain; otherwise document its removal condition.
+    - Keep app-host composition, navigation runtime and platform effects outside shared UI.
+    - Preserve the canonical `io.legado.app.feature.<name>` package when promoting `:app` code to
+      `:feature:<name>` so module extraction does not require another conceptual reorganization.
+
+6. Verify and report.
+    - Always retain the repository G0 Android gates.
+    - For common code, run the actual `commonTest`, metadata, and selected non-Android target
+      compile tasks that exist in the changed project.
+    - For adapters, run contract and Android behavior tests. For reader/rule/service changes,
+      include parity or performance evidence proportional to risk.
+    - Report exact commands, dependency/baseline deltas, capability changes, rollback path, and
+      unverified targets.
+
+## Non-negotiable boundaries
+
+- No Android, JDK-only, Room DAO/entity, resource ID, `File`, URI, service, Activity or View type in
+  shared public contracts.
+- No core → feature, Feature `api` → Feature, or Feature `impl` → another Feature `impl` dependency.
+- No silent no-op implementation for an unsupported target; model the capability explicitly.
+- Report target support by evidence level: compile, contract-test, smoke, package, and
+  release-ready. Do not collapse them into one supported/unsupported flag.
+- No generic dumping-ground module and no empty architecture-shaped modules.
+- Do not make Compose reader replacement a KMP prerequisite; share render models while allowing the
+  Android renderer to remain specialized.
+- Preserve current rule-script, import/export, database, settings and reader semantics until
+  dedicated tests authorize a behavior change.
+
+## Relationship to Android Compose work
+
+Use `legado-compose-migration` for Android-only View→Compose work and `legado-compose-review` for
+Android Compose review. Combine them with this skill only when the same bounded change crosses a
+KMP/CMP or Gradle-module boundary.

+ 4 - 0
.agents/skills/legado-kmp-migration/agents/openai.yaml

@@ -0,0 +1,4 @@
+interface:
+  display_name: "Legado KMP Migration"
+  short_description: "Plan and review Legado KMP/CMP migration slices"
+  default_prompt: "Use $legado-kmp-migration to plan a reviewable KMP/CMP migration slice with explicit gates."

+ 78 - 0
.agents/skills/legado-kmp-migration/references/slice-checklist.md

@@ -0,0 +1,78 @@
+# KMP/CMP Slice Checklist
+
+Read this reference for implementation plans, extraction work, scaffolding, or reviews.
+
+## Before change
+
+- Scope names exact packages/files and one target boundary.
+- Existing behavior is captured by tests or a written manual parity list.
+- Current callers and module edges are known.
+- Imports/dependencies are classified as common-ready, contract-needed, platform-island, or unknown.
+- The chosen non-Android target and actual Gradle verification tasks are known.
+- A rollback point exists and does not require reverting unrelated work.
+
+## Contract quality
+
+- Public types express domain values, not Android/JVM/storage implementation details.
+- Error, cancellation, threading, transaction, ordering and serialization semantics are explicit.
+- Capability absence is representable and visible to callers.
+- An ordinary interface is used unless `expect/actual` provides a concrete static benefit.
+- New abstractions have a real caller and reduce a measurable dependency.
+
+## Module graph
+
+- App host owns implementation aggregation, DI and navigation graph aggregation.
+- Core does not import Feature.
+- Feature API does not import another Feature.
+- Feature implementation uses other Feature APIs only.
+- Shared modules do not depend on platform implementations.
+- Platform implementations are injected from Koin/app composition roots.
+- Gradle `api` exposure is intentional; other dependencies use `implementation`.
+- No module is created only to match the target diagram.
+- Package colocation, Android Gradle module extraction, and KMP/CMP conversion are separate
+  reviewable stages; the slice does not combine all three by default.
+
+## Source sets and platform code
+
+- `commonMain` is compiled by at least one non-Android target.
+- JVM+Android-only sharing has an explicit source-set/module owner and verified IDE/consumer
+  behavior; it is not mislabeled as common.
+- Platform files are in the narrowest relevant source set.
+- Android services, Room/Context/URI/resources, notifications and renderer code stay Android-side.
+- Rhino/JS behavior stays behind a capability boundary until another target has a compatible
+  implementation.
+- Shared Compose code emits callbacks/effects; host navigation and platform launchers stay outside.
+
+## Gates
+
+- G0 Android test/lint/architecture/debug gates pass.
+- Common tests and actual metadata/target compile tasks pass.
+- Capability status distinguishes compile, contract-test, smoke, package, and release-ready
+  evidence.
+- Adapter contract tests cover success, failure and cancellation where relevant.
+- Serialization/database changes have forward/backward or migration evidence.
+- Reader/rule/service changes have parity and, when relevant, real-device performance evidence.
+- Reduced historical violations lower their baseline in the same change.
+- `git diff --check` passes.
+
+## Scaffolding
+
+- At least two accepted manual examples prove the convention.
+- Dry-run is available and default execution refuses overwrites.
+- Existing graphs/DI are not replaced wholesale.
+- Only necessary files are generated; no empty layers.
+- Template fixtures or compile tests cover generator changes.
+- Generated committed source is reviewed like handwritten code.
+
+## Review output
+
+List findings by impact:
+
+- P0/P1: behavior/data loss, incompatible rule or storage semantics, broken actual,
+  lifecycle/thread/cancellation defects, reader regression.
+- P2: illegal dependency, platform leakage, false capability, state duplication, baseline
+  relaxation, or unnecessary cross-platform abstraction.
+- P3: convention, naming, documentation, graph or template drift.
+
+For each finding, give a tight file/line reference, concrete impact, and smallest credible fix. If
+no finding exists, state remaining unverified platform, device or performance risks.

+ 16 - 42
.github/copilot-instructions.md

@@ -1,47 +1,21 @@
-# Copilot Instructions for AI Coding Agents
+# Copilot repository instructions
 
-## 项目架构概览
-- 本项目为 Android 阅读器(Material Design 3 风格重构版),包含主 app(Kotlin/Java)、配套 web 端(Vue3)、多模块扩展。
-- 主要目录:
-  - `app/`:Android 主程序,核心逻辑、服务、UI、数据层。
-  - `modules/web/`:Web 端书架与源编辑,需与 app 后端联动。
-  - `modules/book/`、`modules/rhino/`:功能扩展模块。
+Read and follow `/AGENTS.md` before changing this repository. It is the source of truth for
+project-wide constraints, source priority, validation, current Android boundaries, and the KMP/CMP
+direction.
 
-## 关键组件与数据流
-- 书籍、书源、订阅源等核心数据结构定义于 `app/src/main/java/io/legato/kazusa/data/`。
-- 解析规则、导入/导出逻辑见 `model/`、`model/localBook/`。
-- 服务(如音频播放、下载、朗读、Web 服务)在 `service/`,通过广播或接口与 UI、数据层交互。
-- UI 组件分布于 `ui/`,按功能细分(如书架、阅读、搜索、订阅等)。
-- Web 端通过 REST API 与 app 通信,需配置 `.env.development` 的 `VITE_API` 指向 app 的 web 服务 IP。
+For specialized work, also read the matching repository skill:
 
-## 构建与开发流程
-- **Android 构建**:
-  - 使用 Gradle,入口为 `build.gradle`、`app/build.gradle`。
-  - 常用命令:`./gradlew assembleRelease`、`./gradlew test`。
-  - ProGuard 混淆规则见 `proguard-rules.pro`、`cronet-proguard-rules.pro`。
-- **Web 端开发**:
-  - 进入 `modules/web/`,使用 `pnpm dev` 启动开发,`pnpm build` 打包。
-  - 调试需保证手机与电脑同网段,手机端开启 web 服务。
+- `/.agents/skills/legado-compose-migration/SKILL.md` for View/XML to Compose or new Android Compose
+  screens.
+- `/.agents/skills/legado-compose-review/SKILL.md` for Compose architecture and behavior review.
+- `/.agents/skills/legado-kmp-migration/SKILL.md` for modularization, `commonMain`, CMP, platform
+  adapters, migration gates, and scaffolding.
 
-## 项目约定与特殊模式
-- 书源、订阅源规则高度自定义,相关解析逻辑集中于 `model/analyzeRule`、`model/rss`。
-- 加密/解密相关辅助函数见 `help/crypto/`,RhinoJs 调用 Java 方法有特殊重载(见 README)。
-- 主题、权限、网络存储等通用功能在 `lib/` 下有独立实现。
-- 书籍文件支持 TXT、EPUB、PDF、UMD,解析入口为 `model/localBook/LocalBook.kt`。
+Do not infer current package names, Gradle tasks, dependencies, navigation APIs, or platform support
+from examples. Confirm them in source, tests, `settings.gradle`, Gradle build files, and
+`gradle/libs.versions.toml`. The KMP target structure in `docs/dev/kmp-cmp-modernization.md` is a
+staged proposal, not a description of modules that already exist.
 
-## 外部依赖与集成
-- 主要依赖:JsoupXpath、json-path、rhino-android、okhttp、glide、ktor、bga-qrcode-zxing、colorpicker、commons-text、markwon、hanlp、epublib-core 等。
-- 依赖声明见 `build.gradle`、`app/build.gradle`,部分第三方库源码在 `lib/`。
-
-## 典型开发场景示例
-- 新增书源解析:扩展 `model/analyzeRule`,同步更新 UI 相关界面。
-- 增加服务:在 `service/` 新建服务类,注册广播或接口,UI 层调用。
-- Web 端联动:确保 API 路由与 app web 服务一致,调试时修改 `.env.development`。
-
-## 参考文档与社区
-- 官方帮助文档:https://www.yuque.com/legado/wiki
-- 书源规则教程:https://mgz0227.github.io/The-tutorial-of-Legado/
-- 社区交流:Telegram、Discord、语雀社区
-
----
-如需补充或有疑问,请反馈具体场景或模块。
+Keep changes surgical, preserve existing workspace edits, run risk-matched verification, and report
+exact commands plus unverified behavior.

+ 170 - 382
AGENTS.md

@@ -1,397 +1,185 @@
 # AGENTS.md
 
-This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
-
-## Coding Guidelines
-
-**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
-
-### Think Before Coding
-
-- State assumptions explicitly. If uncertain, ask.
-- If multiple interpretations exist, present them — don't pick silently.
-- If a simpler approach exists, say so. Push back when warranted.
-- If something is unclear, stop. Name what's confusing. Ask.
-
-### Simplicity First
-
-- No features beyond what was asked.
-- No abstractions for single-use code.
-- No "flexibility" or "configurability" that wasn't requested.
-- No error handling for impossible scenarios.
-- If you write 200 lines and it could be 50, rewrite it.
-
-### Surgical Changes
-
-- Don't "improve" adjacent code, comments, or formatting.
-- Don't refactor things that aren't broken.
-- Match existing style, even if you'd do it differently.
-- If you notice unrelated dead code, mention it — don't delete it.
-- Remove imports/variables/functions that YOUR changes made unused.
-- Don't remove pre-existing dead code unless asked.
-
-### Settings Gateway Conventions
-
-- Ordinary settings gateways mutate state through `update { current -> current.copy(...) }`.
-- Do not introduce `*SettingsUpdate` dispatch types or `updateAll` on settings gateways.
-- Submit related multi-field changes in one `copy(...)` transform so the SSOT can apply them atomically.
-- Keep specialized APIs such as `ReadStyleMutation`, `ThemePackageSettingsGateway.applyAndAwait`,
-  `ThemeStateTransaction`, and `AppUiConfigurationGateway` in their dedicated shapes.
-
-### Goal-Driven Execution
-
-Transform tasks into verifiable goals:
-- "Add validation" → "Write tests for invalid inputs, then make them pass"
-- "Fix the bug" → "Write a test that reproduces it, then make it pass"
-- "Refactor X" → "Ensure tests pass before and after"
-
-## Build / Test / Run
-
-```bash
-# Quick compile check (Kotlin only, no dex/package — fastest for verifying code compiles)
-.\gradlew.bat :app:compileAppDebugKotlin
-
-# Assemble all variants
-./gradlew assembleAppRelease
-
-# Assemble without R8 (for crash debugging — no minification/shrinking)
-./gradlew assembleAppNoR8
-
-# Debug build
-./gradlew assembleAppDebug
-
-# Run unit tests (JVM, local)
-./gradlew test
-
-# Run a single test class
-./gradlew test --tests "io.legado.app.model.cache.CacheDownloadQueueTest"
-
-# Run connected Android tests
-./gradlew connectedAndroidTest
-
-# Lint
-./gradlew lint
-
-# Update Cronet (after changing CronetVersion in gradle.properties)
-./gradlew app:downloadCronet
-```
-
-The project uses JDK 21 for development (set in `build.gradle.kts` via `jvmToolchain`). CI uses JDK 17 for building.
-
-Gradle properties: 8 GB heap, configuration cache disabled (`gradle.properties:31`), non-transitive R classes, precise resource shrinking enabled.
-
-## Architecture
-
-This is a Material Design 3 fork of [Legado](https://github.com/gedoor/legado). `app/src/main/java/io/legado/app/` uses **Clean Architecture** with three layers:
-
-| Layer | Package | Role |
-|---|---|---|
-| Data | `data/` | Room DB (`AppDatabase`, version 85, ~22 DAOs, ~25 entities), repository implementations |
-| Domain | `domain/` | Gateway interfaces, use cases (14), domain models — no framework dependencies |
-| UI | `ui/` | Jetpack Compose screens, Navigation 3 routes, ViewModels |
-
-Additional top-level packages:
-- **`help/`** — Infrastructure "glue": HTTP (OkHttp + Cronet), book content processing, backup/WebDAV, JS engine, config
-- **`model/`** — Runtime state coordinators (not entities): `ReadBook`, `AudioPlay`, `CacheBook`, `BookCover`, etc.
-- **`service/`** — Android foreground/background services (audio playback, TTS, download, web server)
-- **`web/`** — Embedded HTTP server (Ktor) for remote bookshelf/source editing
-- **`lib/`** — Third-party library wrappers (MOBI parser, WebDAV client, legacy View theme system, cronet)
-- **`base/`** — Abstract Activity/Fragment/ViewModel base classes
-- **`utils/`** — Extension functions and utility classes (~70 files)
-
-Modules: `:app`, `:modules:book` (epub/TXT parsing, namespace `me.ag2s`), `:modules:rhino` (Rhino JS wrapper, namespace `com.script`). There is also a Vue 3 web frontend in `modules/web/` (pnpm, separate from the Android build).
-
-## Dependency Injection (Koin)
-
-Two modules loaded in `App.onCreate()`:
-
-```kotlin
-startKoin {
-    modules(appDatabaseModule, appModule)
-}
-```
-
-- **`di/appDatabaseModule.kt`** — Singleton `AppDatabase` + factory bindings for all 22 DAOs
-- **`di/appModule.kt`** — Singletons (repositories, use cases, gateways, Coil `ImageLoader`), `viewModelOf` / `viewModel { }` for all ViewModels, some parameterized definitions
-
-Gateways are bound to their repository implementations explicitly (e.g., `single<LocalBookGateway> { LocalBookRepository(get()) }`), not through `singleOf`.
-
-## Navigation
-
-Uses **Jetpack Navigation 3** (`androidx.navigation3`) with type-safe `@Serializable` sealed interfaces for route keys:
-
-```kotlin
-@Serializable
-private sealed interface MainRoute : NavKey
-@Serializable
-private data object MainRouteHome : MainRoute
-@Serializable
-private data class MainRouteCache(val groupId: Long) : MainRoute
-```
-
-`MainActivity` holds a single `NavDisplay` with `entryProvider { ... }` defining all composable entries. `Launcher0` through `LauncherW` extend `MainActivity` to provide multiple launcher icon alias entries. Separate activities handle the reader (`ReadBookActivity` — still View-based), book info, source management, replace rules, file manager, QR scanner, etc.
-
-## Theme System
-
-A multi-engine theming system in `ui/theme/`:
-
-1. **Material 3 Expressive** (default): Uses `MaterialExpressiveTheme` with `MotionScheme.expressive()`
-2. **Miuix** (alternative): Uses `top.yukonga.miuix.kmp` theming engine
-
-14 theme modes (`AppThemeMode` enum) — Dynamic (Monet), 12 named presets, Custom (MaterialKolor seed-color generation), Transparent. `CustomColorScheme` wraps `com.materialkolor` with configurable `PaletteStyle` (TonalSpot, Neutral, Vibrant, Expressive, Rainbow, etc.) and `ColorSpec` (2021 vs 2025).
-
-Legacy View-based theme still exists in `lib/theme/` (used by non-migrated screens like `ReadBookActivity`).
-
-## Hybrid Compose + View
-
-The app is mid-migration from Views to Compose. View-based screens (reader, book info, source management) coexist with Compose screens (main tabs, settings, search, RSS, cache management). XML layouts, `viewBinding`, and traditional Activities are still heavily used. The `viewBinding` build feature is enabled but Compose screens are the target.
-
-## Jetpack Compose Requirements (new screens MUST follow)
-
-All **new** UI screens must be implemented in Jetpack Compose following the patterns below. Do **not
-** create new View-based Activities/Fragments/XML layouts. Existing View-based screens can remain
-until migrated.
-
-### MVI/UDF Architecture
-
-Every Compose screen follows a strict **Model-View-Intent** pattern with three artifacts defined in
-a `*Contract.kt` file:
-
-```
-ui/{feature}/
-├── XxxContract.kt      // UiState, Intent, Effect (and optionally Sheet/Dialog)
-├── XxxViewModel.kt     // ViewModel
-├── XxxScreen.kt        // Screen composable
-└── XxxRouteScreen.kt   // (optional) outer wrapper for activity results / lifecycle
-```
-
-**Contract definitions:**
-
-```kotlin
-// @Stable data class — all screen state in one place
-@Stable
-data class XxxUiState(
-    val loading: Boolean = false,
-    val items: ImmutableList<ItemUi> = persistentListOf(),
-    val activeSheet: XxxSheet? = null,
-    val activeDialog: XxxDialog? = null,
-)
-
-// sealed interface — every user action is an Intent
-sealed interface XxxIntent {
-    data class LoadData(val id: Long) : XxxIntent
-    data object Refresh : XxxIntent
-}
-
-// sealed interface — one-shot side effects (navigation, toast, etc.)
-sealed interface XxxEffect {
-    data class ShowToast(val message: String) : XxxEffect
-    data class NavigateTo(val route: MainRoute) : XxxEffect
-}
-
-// (optional) sealed interface for multi-sheet/dialog scenarios
-sealed interface XxxSheet { data object Filter : XxxSheet }
-sealed interface XxxDialog { data class Confirm(val msg: String) : XxxDialog }
-```
-
-**Naming rules:**
-
-- State: `{Feature}UiState` — `@Stable data class`
-- Intent: `{Feature}Intent` — `sealed interface` with `data class` / `data object` members
-- Effect: `{Feature}Effect` — `sealed interface`
-- Sheet/Dialog: `{Feature}Sheet`, `{Feature}Dialog` — `sealed interfaces` stored in UiState
-
-### ViewModel
-
-```kotlin
-class XxxViewModel(/* injected dependencies */) : ViewModel() {
-
-    private val _uiState = MutableStateFlow(XxxUiState())
-    val uiState = _uiState.asStateFlow()
-
-    private val _effects = MutableSharedFlow<XxxEffect>(extraBufferCapacity = 16)
-    val effects = _effects.asSharedFlow()
-
-    fun onIntent(intent: XxxIntent) {
-        when (intent) {
-            is XxxIntent.LoadData -> loadData(intent.id)
-            is XxxIntent.Refresh -> refresh()
-        }
-    }
-
-    private fun loadData(id: Long) {
-        // Use viewModelScope, update _uiState via update { it.copy(...) }
-    }
-}
-```
-
-Key rules:
-
-- Extend `ViewModel()` directly (not `BaseViewModel`).
-- `_uiState` is `MutableStateFlow`, exposed as `StateFlow` via `.asStateFlow()`.
-- `_effects` is `MutableSharedFlow(extraBufferCapacity = 16)`, exposed via `.asSharedFlow()`.
-- Emit effects via `_effects.tryEmit(...)`.
-- Single `onIntent()` entry point, dispatched via `when`.
-
-### Screen Composable
-
-```kotlin
-// Stateless screen — ViewModel wired in entry provider or RouteScreen
-@Composable
-fun XxxScreen(
-    state: XxxUiState,
-    onIntent: (XxxIntent) -> Unit,
-    effects: Flow<XxxEffect>,                   // one-shot effects from ViewModel
-    onBack: () -> Unit,
-    onNavigateToYyy: (YyyRoute) -> Unit,
-) {
-    // Collect effects
-    LaunchedEffect(Unit) {
-        effects.collectLatest { effect ->
-            when (effect) {
-                is XxxEffect.ShowToast -> { /* ... */ }
-                is XxxEffect.NavigateTo -> onNavigateToYyy(effect.route)
-            }
-        }
-    }
-
-    AppScaffold(
-        topBar = {
-            GlassMediumFlexibleTopAppBar(
-                title = { Text("Title") },
-                scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior(),
-                navigationButton = { TopBarNavigationButton(onBack) },
-            )
-        },
-    ) { contentPadding ->
-        // UI content, no business logic here
-    }
-}
+本文件定义仓库级不变量。专项流程放在 `.agents/skills/`,长期方案放在 `docs/dev/`
+;不要把临时任务、讨论过程或一次性结论继续堆进本文件。
+
+## 资料优先级
+
+执行任务前按以下顺序建立上下文:
+
+1. 用户本次目标、范围与验收条件。
+2. 本文件的仓库级约束。
+3. 与任务匹配的 `.agents/skills/*/SKILL.md` 及其明确要求读取的 reference。
+4. `docs/dev/` 中的专项设计或迁移计划。
+5. 当前源码、测试、Gradle 配置和相邻实现;它们用于确认当前版本的真实 API 与行为。
+6. 外部资料只作为设计依据;涉及会变化的 Android、Kotlin、Compose 或库 API 时,以官方当前文档和本仓库版本为准。
+
+文档与源码不一致时,先指出差异。不要为了让实现符合过期文档而静默改代码。
+
+## Skill 路由
+
+- XML/View/Activity/Fragment/RecyclerView 到 Compose 的页面迁移、新 Compose 页面:使用
+  `legado-compose-migration`。
+- Compose Screen、Contract、ViewModel、导航、状态与兼容边界审查:使用 `legado-compose-review`。
+- Gradle 模块拆分、`commonMain` 抽取、KMP/CMP、`expect/actual`、平台能力适配、迁移门禁或相关脚手架:使用
+  `legado-kmp-migration`。
+- 单个任务可组合 skill,但只读取与当前工作切片有关的 reference。
+
+## 工作方式
+
+- 开始前说明假设、成功条件和不在范围内的内容。存在多种会显著改变结果的解释时,不静默选择。
+- 优先做最小、可回滚、可独立验证的垂直切片;不得借任务重构无关代码。
+- 修 bug 时先建立可复现测试或其他可观察证据;重构前后保持同一行为验证通过。
+- 保留工作区中用户已有修改。只移除本次改动产生的无用代码和资源。
+- 不为了“架构完整”创建空模块、单方法包装、无调用方抽象或通用 `Utils/Common` 容器。
+
+## 当前工程事实与目标方向
+
+当前工程是 Android-first 的渐进迁移仓库,不是已经完成的 KMP 工程:
+
+- Gradle 模块:`:app`、`:modules:book`、`:modules:rhino`、`:baselineprofile`;`modules/web` 是独立 Vue 3
+  工程。
+- `:app` 同时包含 Android UI、数据、领域、服务和大量遗留 View 代码;包目录只表达逻辑边界,尚无 Gradle
+  编译隔离。
+- 新 UI 以 Jetpack Compose、Material 3、Navigation 3、Koin、StateFlow/SharedFlow 为默认;阅读器渲染核心等成熟
+  View 代码可作为 Android 专业渲染岛保留。
+- `domain/` 是迁移目标边界,但当前仍存在 Android/JVM 类型渗透;不要把“目标纯净度”描述成已完成事实。
+- 长期目标是渐进演进为 KMP/CMP。详细目标结构、阶段和门禁见 `docs/dev/kmp-cmp-modernization.md`。
+
+### Feature-first 过渡结构
+
+- 新 Compose-first Feature 和完成整页迁移的代码使用 `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。
+- Feature 内共置 Contract、ViewModel、Route、Screen 和私有 components/dialog/sheet/model;文件的详细归属与迁移步骤见
+  `docs/dev/feature-first-structure.md`,现有业务 owner 见 `docs/dev/feature-catalog.md`。
+- App host 聚合导航和 DI;Feature 不直接依赖其他 Feature 实现。领域、数据、平台能力不能为了目录整齐塞入
+  Feature UI 包。
+- 旧 `ui/...` 是渐进迁移区,不做全量机械搬家;一个 Feature 的新 owner 建立后,禁止继续向旧包新增同职责文件。
+
+### 目标依赖方向
+
+```text
+app host / composition root
+        ↓
+feature implementation → feature API
+        ↓
+domain / model / platform contracts
+        ↓
+data abstractions
+        ↓
+platform implementations (Android/JVM/iOS/...)
 ```
 
-Key rules:
-
-- Screen is **stateless** — receives `state`, `onIntent`, `effects`, never accesses ViewModel
-  directly.
-- Effects collected in `LaunchedEffect(Unit) { ... }` using `collectLatest`.
-- Alternatively, effects can be collected in the outer `RouteScreen` or entry provider if the screen
-  doesn't need them directly.
-- Use project custom widgets: `AppScaffold`, `AppText`, `AppIcon`, `AppIcons`, `AppAlertDialog`,
-  `AppModalBottomSheet`, `NormalCard`, `GlassMediumFlexibleTopAppBar`, `TopBarNavigationButton`,
-  `TopBarActionButton`, etc.
-- No business logic, no direct DB/network calls in composables.
-
-Two input patterns are acceptable:
-
-- **Stateless (preferred for new screens):** `state: XxxUiState` + `onIntent: (XxxIntent) -> Unit` —
-  ViewModel wired in entry provider or RouteScreen.
-- **ViewModel as default param:** `viewModel: XxxViewModel = koinViewModel()` — simpler for
-  standalone screens.
-
-### Stability
-
-- All `UiState` and UI item data classes **must** be annotated with `@Stable`.
-- Use `ImmutableList` (from `kotlinx.collections.immutable`) for list properties in state classes,
-  not `List` or `MutableList`.
-- Prefer `persistentListOf()` / `toImmutableList()` for default values.
-
-### Navigation
-
-Uses **Navigation 3** (`androidx.navigation3`). Routes are `@Serializable` sealed interfaces:
-
-```kotlin
-// In MainNavKey.kt
-@Serializable
-data class MainRouteXxx(val id: Long) : MainRoute
-```
-
-Entry registered in `MainNavGraph.kt`:
-
-```kotlin
-entry<MainRouteXxx> { route ->
-    val viewModel = koinViewModel<XxxViewModel>()
-    XxxScreen(
-        state = viewModel.uiState.collectAsStateWithLifecycle().value,
-        onIntent = viewModel::onIntent,
-        onBack = { onNavigateBack() },
-        onNavigateToYyy = { onNavigateToRoute(it) },
-    )
-}
-```
-
-Key rules:
-
-- Screens **never** reference the navigator directly — receive `onBack`, `onNavigateToXxx` lambdas.
-- Navigation is callback-based, wired by the entry provider.
-- New routes added to the `MainRoute` sealed interface in `MainNavKey.kt`.
-
-### Koin DI
-
-- Register ViewModels in `di/appModule.kt` with `viewModelOf(::XxxViewModel)`.
-- Inject in Compose via `koinViewModel()` (default param or explicit in entry provider).
-- For keyed ViewModels (e.g. per-book): `koinViewModel<XxxViewModel>(key = route.bookUrl)`.
-- Repositories/gateways/use cases registered as `singleOf(::...)`.
-
-### Activity Base Class
-
-New standalone Compose activities extend `BaseComposeActivity`:
-
-```kotlin
-class XxxActivity : BaseComposeActivity() {
-    @Composable
-    override fun Content() {
-        // Screen content — AppTheme is already applied by the base class
-    }
-}
-```
-
-### RouteScreen Wrapper
-
-For screens needing activity result handling, lifecycle observation, or permission requests, use a
-two-layer pattern:
+- 应用宿主负责组装 Feature 实现、DI、平台生命周期和导航运行时。
+- Feature 之间通过稳定契约或导航 API 协作,不直接依赖其他 Feature 的实现。
+- `commonMain` 只容纳经过依赖审计、能被至少一个非 Android 目标编译验证的代码;移动目录不等于完成跨平台迁移。
+- Android `Context`、Room 实体/DAO、Service、Broadcast、Activity Result、Cronet、文件
+  URI、通知、媒体会话、成熟阅读器渲染和现有 Rhino 集成默认留在平台侧,通过窄接口进入共享层。
+- 平台能力不可用时必须显式建模为 capability/unsupported,不得用静默空实现伪造跨平台支持。
+
+## Compose 屏幕约束
+
+- 新屏幕使用 Compose;不要新增 XML/View Activity/Fragment。
+- Android Compose 新屏幕采用 UDF/MVI:`@Stable XxxUiState`、`XxxIntent`、`XxxEffect`;ViewModel 直接继承
+  `ViewModel`,私有 `MutableStateFlow` / `MutableSharedFlow(extraBufferCapacity = 16)`,对外只暴露只读
+  Flow,并由单一 `onIntent` 分发用户动作。共享 Feature 的状态宿主按 KMP skill 单独决策。
+- Screen/Content 保持无业务逻辑、无 DAO/网络/存储直连;ViewModel 通过 Gateway/Repository/UseCase
+  访问业务能力。
+- 所有 UiState 与 UI item 数据类标注 `@Stable`。Compose 渲染边界中的集合使用
+  `kotlinx.collections.immutable`;内部计算和数据层不机械替换集合类型。
+- 导航、权限、文件选择和其他宿主动作通过回调或 Effect 处理。新目的地优先由 `MainActivity` 的 Navigation
+  3 图组装。
+- 使用项目的 `AppScaffold`、主题、top bar、dialog/sheet、列表和设置组件。正确处理 edge-to-edge 与
+  predictive back。
+- Activity 仅在遗留 `Intent`/result 兼容确有需要时作为薄宿主保留。
+
+具体文件形态和审查清单由 Compose migration/review skill 维护,本文件不复制完整模板。
+
+## 数据与设置边界
+
+- UI(包括 ViewModel)不得新增 DAO、`appDb.*Dao`、网络客户端或旧偏好 API 直连;历史债务由
+  `verifyConfigArchitecture` 的基线棘轮冻结并逐步下调。
+- 普通设置 Gateway 通过 `update { current -> current.copy(...) }` 修改状态;关联字段在一次
+  `copy(...)` 中原子提交。
+- 不引入 `*SettingsUpdate` 分发类型或 `updateAll`。
+- `ReadStyleMutation`、`ThemePackageSettingsGateway.applyAndAwait`、`ThemeStateTransaction`、
+  `AppUiConfigurationGateway` 等专用 API 保持现有形态。
+- Koin 中 Gateway 接口到 Repository 实现保持显式绑定;不要用构造函数绑定掩盖接口归属。普通
+  Repository/UseCase/ViewModel 可继续使用项目既有 `singleOf` / `viewModelOf` 约定。
+- 新的共享领域契约不得暴露 `Context`、`File`、`Uri`、Room 类型、Android resource id 或 JVM-only
+  stream;用领域值、`ByteArray`/抽象 source-sink、序列化模型或平台接口表达。
+
+## KMP/CMP 迁移纪律
+
+- 先写行为/契约测试和依赖清单,再移动代码;一个 PR 只完成一个可说明的边界变化。
+- 优先抽取稳定模型、纯函数、规则解析契约和 UseCase;网络、数据库、JS 引擎、服务、阅读器渲染最后通过平台适配器处理。
+- `expect/actual` 只用于真正的平台原语。可用普通接口 + DI 表达的能力,不使用 `expect/actual`。
+- 平台实现由 Koin 和应用 composition root 组装。
+- 不因 KMP 目标同时替换数据库、网络、DI、导航和 UI 框架。每次只改变一个主要风险维度。
+- Feature `api/impl` 只有形成真实 Gradle 边界并存在跨 Feature/宿主调用时才创建,不能只建同名文件夹。
+- 首个非 Android 目标是架构证明,不承诺功能齐平;目标顺序由 capability matrix 和产品需求决定。
+
+## 代码生成
+
+- 生成器用于编码已稳定的约定,不用于发明架构。至少有两个手工迁移且通过审查的同类样本后,才能固化模板。
+- 脚手架必须可预览或 dry-run、拒绝覆盖已有文件、输出最小文件集,并在已有 Feature 上采用追加/AST
+  感知修改,禁止覆盖 route graph 或 DI 文件。
+- 生成的可提交源码仍接受普通代码审查;真正的编译期生成物放入 `build/generated`,不得手工编辑或提交。
+- 模板和生成器变更必须有 fixture/snapshot 或编译验证,并与 convention plugin 的模块类型保持一致。
+
+## 重构审查门禁
+
+每个迁移 PR 至少回答:
+
+- 行为基线是什么,哪些测试或手工路径证明没有回退?
+- 依赖边界净变化是什么,是否减少了 Android/JVM 泄漏或历史基线?
+- 新模块/接口是否有真实调用方,能否用更小改动完成?
+- Android 实现与共享契约的错误、线程、取消、序列化语义是否一致?
+- 做了哪些验证,哪些真机、性能、外部服务或其他平台行为尚未验证?
+- CI 是否执行了受影响的共享测试、目标编译或 adapter contract test?
+
+减少历史违规时必须同步下调对应 baseline;门禁不接受“先放宽基线再迁移”。详细分级见 KMP/CMP 现代化计划。
+
+## 构建与验证
+
+开发与 CI 使用 JDK 21。按风险选择最小充分验证:
+
+```powershell
+# Kotlin/Compose 快速编译
+.\gradlew.bat :app:compileAppDebugKotlin
 
-- Outer `XxxRouteScreen`: handles `ActivityResultLauncher`, lifecycle callbacks, file pickers,
-  permission requests. Wires ViewModel.
-- Inner `XxxScreen`: pure UI, stateless with `state` + `onIntent`.
+# 当前主验证集
+.\gradlew.bat testAppDebugUnitTest lintAppDebug verifyConfigArchitecture assembleAppDebug --continue --no-configuration-cache
 
-### Material 3 vs Miuix
+# 资源、Manifest、生成绑定或打包变化
+.\gradlew.bat :app:assembleAppDebug
 
-The project supports two Compose theme engines. If a screen needs engine-specific UI, branch on:
+# Release/R8 相关
+.\gradlew.bat assembleAppRelease
+.\gradlew.bat assembleAppNoR8
 
-```kotlin
-if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
-    // Miuix implementation
-} else {
-    // Material 3 implementation
-}
+# Web 前端
+pnpm --dir modules/web build
 ```
 
-For detailed Compose review conventions and migration patterns, see
-`.Codex/skills/legado-compose-review/`.
-
-## Rhino JavaScript Engine
-
-Book sources, RSS sources, and HTTP TTS use JavaScript rules. `initRhino()` in `App.kt` registers `NativeBaseSource` wrappers for `BookSource`, `RssSource`, `HttpTTS` (writable JS objects) and `ReadOnlyJavaObject` wrappers for rule entities. Rule parsing logic lives in `help/source/` and `model/analyzeRule/`.
+KMP 任务名在模块实际创建后才存在;不要假装运行尚未定义的 `commonTest`、目标编译或 API
+检查任务。新增模块时把其真实任务加入 CI,并在交付中列出准确命令。
 
-## Important Constraints
+所有文本改动至少运行 `git diff --check`。构建通过不替代架构边界、行为和真机性能复核。
 
-- **Do not update jsoup** beyond 1.16.2 — a breaking change in newer versions (see [jsoup#2017](https://github.com/jhy/jsoup/pull/2017)) affects `AnalyzeByJSoup.kt` and the JsoupXpath library
-- Hutool dependency removed — crypto/编码/日期工具已替换为 JCA (`javax.crypto`/`java.security`) 与 `java.time`,新增内部工具在 `help/crypto/CryptoUtils.kt`
-- Package name discrepancy: code namespace is `io.legado.app` but `applicationId` is `io.legato.kazusa`
-- Min SDK 26, target SDK 37, compile SDK 37
-- Release builds enable R8 minification + resource shrinking; `noR8` variant disables both for crash debugging
-- APK is split by ABI (`armeabi-v7a`, `arm64-v8a`, plus universal)
-- Firebase Analytics and Performance are included; `google-services` plugin applied
+## 重要项目约束
 
-## Web Frontend
+- 不将 jsoup 升级到 1.16.2 以上;新版行为会影响 `AnalyzeByJSoup.kt` 与 JsoupXpath。
+- 不重新引入 Hutool;加密、编码与日期使用现有 JCA、`java.time` 和 `help/crypto/CryptoUtils.kt` 路径,KMP
+  抽取时再通过能力契约替换 JVM API。
+- 代码 namespace 为 `io.legado.app`,Android `applicationId` 为 `io.legato.kazusa`,不要混用。
+- 当前 minSdk 26、target/compile SDK 37;Release 启用 R8 与资源压缩,`noR8` 变体用于排障。
+- Rhino 书源/RSS/TTS 规则、Android 服务、Web 服务和阅读器渲染属于高行为风险平台能力,迁移前必须建立兼容测试或
+  capability 边界。
+- `modules/web` 必须连接应用内 Ktor WebService;开发环境通过 `VITE_API` 指向设备服务地址。
 
-Located in `modules/web/` — a Vue 3 + TypeScript + Vite project for remote bookshelf and source editing. Must connect to the app's built-in HTTP server (started via `WebService` in the main activity settings). Commands:
-
-```bash
-cd modules/web
-pnpm install
-pnpm dev       # dev server
-pnpm build     # production build
-```
+## 交付说明
 
-Set `VITE_API` in `.env.development` to the app's web service IP.
+最终说明应包含:修改的职责范围、关键设计取舍、实际运行的验证与结果、未验证风险。不要把“已生成代码”或“已移动文件”当作完成标准。

+ 48 - 0
docs/dev/feature-catalog.md

@@ -0,0 +1,48 @@
+# Feature catalog(初始提案)
+
+> 本表用于统一 owner 与 canonical name,不表示模块已经存在。每个条目在首次迁移前仍需做调用方和依赖审计。
+
+| 当前主要位置                                   | Canonical owner             | 目标类型                          | 备注                                                                           |
+|------------------------------------------|-----------------------------|-------------------------------|------------------------------------------------------------------------------|
+| `ui/main/MainActivity*`、全局 route graph   | `app/android`               | app host                      | 保留导航、DI、launcher 与全局 shell 聚合,不建 `feature-main`                              |
+| `ui/main/bookshelf`                      | `feature/bookshelf`         | Feature                       | 书架列表、分组入口和选择状态;书籍数据仍经 core data/domain                                       |
+| `ui/main/home`、`ui/main/homepage`        | `feature/home`              | Feature                       | 先确认两者职责后合并 canonical name,禁止继续新增第三套首页包                                       |
+| `ui/main/explore`、`ui/book/explore`      | `feature/explore`           | Feature                       | 区分入口容器与探索结果子流程,避免两个独立 impl 互相依赖                                              |
+| `ui/book/info`                           | `feature/book-info`         | Feature                       | 适合作为中型样板候选;兼容 Activity 可暂留 legacy bridge                                     |
+| `ui/book/search`、`ui/book/searchContent` | `feature/book-search`       | Feature                       | 全局搜书与正文搜索可先作为同域子流程,稳定后再评估拆分                                                  |
+| `ui/book/toc`                            | `feature/table-of-contents` | Feature                       | TXT 目录规则作为子流程,不直接下沉为公共 widget                                                |
+| `ui/book/bookmark`                       | `feature/bookmarks`         | Feature                       | 与阅读入口通过 route/result contract 协作                                             |
+| `ui/book/group`、`ui/book/manage`         | `feature/book-management`   | Feature                       | 先收拢管理动作,再决定书籍分组是否形成独立契约                                                      |
+| `ui/book/import`、`ui/file` 中导入流程         | `feature/book-import`       | Feature + platform adapter    | 文件选择/URI 留 Android adapter,导入状态与规则归 Feature/domain                           |
+| `ui/book/cache`                          | `feature/book-cache`        | Feature                       | 缓存任务执行由 domain/service 能力提供                                                  |
+| `ui/book/changecover`                    | `feature/change-cover`      | Feature                       | 图片选择与裁剪由 host/platform effect 处理                                             |
+| `ui/book/changesource`、`ui/book/source`  | `feature/book-source`       | Feature                       | source edit/manage/debug 可作为同域子包,避免交叉 impl 依赖                                |
+| `ui/book/readRecord`                     | `feature/reading-history`   | Feature                       | 新包统一全小写;不要延续 camelCase package                                               |
+| `ui/book/readaloud`                      | `feature/read-aloud`        | Feature + platform adapter    | 控制 UI 可 Feature 化,TTS/service/media 生命周期留 platform                           |
+| `ui/book/audio`                          | `feature/audio-playback`    | Feature + platform adapter    | 播放服务、媒体会话和通知留 Android platform                                               |
+| `ui/book/read`                           | `platform/android/reader`   | platform island               | 当前不作为普通 Feature 搬迁;共享状态/模型另行立项                                               |
+| `ui/book/manga`                          | `feature/manga`             | Feature + renderer capability | 图片渲染、缓存和手势需专项边界审计                                                            |
+| `ui/config/*`                            | `feature/settings`          | Feature domain                | 先保留 settings 下的 `appearance`、`reading`、`backup`、`advanced` 等 section,不为每页建模块 |
+| `ui/theme`                               | `core/designsystem`         | core candidate                | theme engine、token 与基础主题;Feature 专属样式不进入 core                                |
+| `ui/widget/components`                   | owner audit                 | core/ui candidate             | 有至少两个 Feature 调用且职责稳定的组件才进入 `core/ui` 或 `core/designsystem`                  |
+| `ui/widget` 其余 View-era 控件               | legacy/platform owner       | migration zone                | Recycler/View/Dialog 控件按实际调用方迁走,禁止整目录改名为 core                                |
+| `ui/rss/*`、`ui/main/rss`                 | `feature/rss`               | Feature domain                | 初期一个业务域,内部 `feed`、`article`、`source`、`subscription` 子包                       |
+| `ui/replace`                             | `feature/replace-rules`     | Feature                       | `edit` 是同 Feature 子流程                                                        |
+| `ui/dict`                                | `feature/dictionary`        | Feature                       | rule editor 留同域,执行引擎经 domain/platform contract                               |
+| `ui/highlightTagRule`、`ui/tagGroupRule`  | `feature/tag-rules`         | Feature domain                | 可作为小型迁移样板候选,先核对实际共享模型                                                        |
+| `ui/ai/chat`                             | `feature/ai-chat`           | Feature                       | AI provider/config 归 settings 或 data/platform,不放 Screen                      |
+| `ui/login`                               | `feature/source-login`      | Feature + host effect         | WebView/cookie/platform auth 通过 adapter                                      |
+| `ui/about`                               | `feature/about`             | Feature                       | 边界小,适合首个 package relocation 样板候选                                             |
+| `ui/welcome`                             | `feature/onboarding`        | Feature                       | 首次启动状态经 settings/domain contract                                             |
+| `ui/qrcode`                              | `feature/qr-scan`           | Feature + Android capability  | Camera/permission/result 留 host/platform                                     |
+| `ui/association`                         | `feature/file-association`  | Feature + Android capability  | 外部 Intent/URI 是兼容入口,不进入共享 contract                                           |
+| `ui/browser`                             | `platform/android/browser`  | platform capability           | 若出现独立用户流程再由 Feature 包装,不把 WebView 放 core UI                                  |
+
+## Catalog 使用规则
+
+- 一项只能有一个 canonical owner;子流程通过子包表达,不复制完整 Feature 栈。
+- 发现当前包横跨多个 owner 时,先画依赖和入口,不直接移动。
+- owner 变更必须同步本表、route/DI 聚合位置和迁移 PR 的回滚说明。
+- 新 Feature 名先登记再落文件,避免同义目录继续增长。
+- Feature 提升为 Gradle module 或 KMP module 后,在备注中记录真实 module path、source sets 与验证
+  task。

+ 175 - 0
docs/dev/feature-first-structure.md

@@ -0,0 +1,175 @@
+# Feature-first 工程结构与文件归属规范
+
+> 状态:Android 单体整理与 KMP/CMP 模块化之间的过渡规范。本文定义新文件放置位置和旧目录迁移方式,不授权一次性移动现有源码。
+
+## 1. 为什么需要过渡层
+
+当前 `app/src/main/java/io/legado/app/ui` 约有 803 个 Kotlin 文件,其中 `book`、`widget`、`config`
+体量很大;目录同时按页面、业务域、控件类型和历史技术分组。Compose Contract/Screen/ViewModel
+已有基本形态,但没有统一的 Feature 所有权,因此仅靠 Compose 编码规范不能解决:
+
+- 同一 Feature 的 route、状态、UI、dialog、兼容 Activity 分散;
+- `widget`、`utils`、`help` 容易继续成为共享倾倒区;
+- 当前包结构和未来 `:feature:<name>` 模块结构不同,模块化时还要再次大搬迁;
+- 文件移动、业务重构和 KMP 抽取容易被混入同一个 PR。
+
+采用三级演进,不直接创建几十个空 Gradle 模块:
+
+```text
+Stage A  :app 内 feature-first 包
+Stage B  独立 Android :feature:<name> 模块
+Stage C  有共享价值时再把该模块改为 KMP/CMP source sets
+```
+
+## 2. 统一命名
+
+以“书籍详情”为例:
+
+| 类型                  | 形式                               |
+|---------------------|----------------------------------|
+| 物理目录                | `feature/book-info/`             |
+| Gradle project path | `:feature:book-info`             |
+| Kotlin 包            | `io.legado.app.feature.bookinfo` |
+| 类型前缀                | `BookInfo`                       |
+| Android resource 前缀 | `feature_book_info_`             |
+
+- 目录和 Gradle path 使用 kebab-case;Kotlin 包使用全小写且不含连字符。
+- 不使用含义重叠的 `home` / `homepage`、`manage` / `management` 新命名;新增前先查 Feature catalog。
+- Feature 名表达用户能力,不用 `screen`、`compose`、`new`、`v2` 或技术实现命名。
+
+## 3. Stage A:`:app` 内的标准 Feature 包
+
+所有新 Compose-first Feature,以及正在完整迁移且已有清晰 owner 的页面,目标位置为:
+
+```text
+app/src/main/java/io/legado/app/feature/<feature>/
+├── FeatureContract.kt        # UiState、Intent、Effect、Dialog/Sheet 状态
+├── FeatureViewModel.kt       # 状态所有者与 intent 分发
+├── FeatureRoute.kt           # Navigation/host 接线;需要时才创建
+├── FeatureScreen.kt          # 无状态业务 Screen
+├── components/               # 只被该 Feature 使用的组件
+├── dialog/                   # 复杂且独立的 Feature dialog
+├── sheet/                    # 复杂且独立的 Feature sheet
+├── model/                    # 仅 presentation 使用的稳定 UI model
+└── legacy/                   # 临时兼容桥;必须记录删除条件
+```
+
+目录按需创建。四个核心文件也不是强制空模板:简单 Feature 可以合并小型 Contract 或组件,但不能把业务状态塞回
+Composable。
+
+### 文件归属
+
+| 文件/职责                                     | 应放位置                                                   | 不应放位置                 |
+|-------------------------------------------|--------------------------------------------------------|-----------------------|
+| Screen、Contract、ViewModel、Feature 私有组件    | `feature/<name>`                                       | `ui/widget`、`utils`   |
+| Navigation key/参数的稳定公开契约                  | Feature 根或未来 `api`                                     | 公共 widget、data        |
+| 全局 nav graph 聚合、Koin 聚合                   | app host                                               | Feature Screen        |
+| 两个以上 Feature 使用的纯 UI 组件                   | `core/ui` 或 `core/designsystem` 候选                     | 为“以后可能复用”提前移动         |
+| 领域模型、Gateway、UseCase                      | 当前 `domain/<business>`,未来 `core:model` / `core:domain` | Compose package       |
+| Repository 实现、DAO mapping                 | 当前 `data/<business>`,未来 `core:data` / platform data    | ViewModel、Composable  |
+| Activity Result、Context、Service、通知、文件 URI | Android host/platform adapter                          | `commonMain`、纯 Screen |
+| 阅读渲染、Rhino、Android service                | platform island                                        | 普通共享 Feature          |
+
+### Route 与 Screen 的边界
+
+- `FeatureRoute` 可取得 ViewModel、生命周期状态、导航回调和 Android launcher。
+- `FeatureScreen` 只接收 `state`、`onIntent` 和必要的 UI 回调。
+- Feature 不直接修改全局 back stack;公开“去哪”和“返回什么”,由 app host 接线。
+- 兼容 Activity 只解析旧 Intent/extras/result,并转交 Feature;不成为第二状态所有者。
+
+## 4. Stage B:提升为真实 Gradle Feature 模块
+
+只有满足以下条件才从 `:app` 提升:
+
+- Feature owner 和用户入口清晰;
+- 已至少完成一个可运行的 Screen/流程,而不是空架构;
+- 对 `app`、其他 Feature、data/platform 的依赖已经列清;
+- 迁移后能删除或下调至少一个依赖/架构基线;
+- 有独立编译或测试任务,并存在可回滚点。
+
+目标形态:
+
+```text
+feature/<feature>/
+├── build.gradle.kts
+└── src/main/kotlin/io/legado/app/feature/<package>/...
+```
+
+Gradle module 默认使用 `:feature:<name>`。Feature 之间不依赖 `impl`;跨 Feature 跳转由
+route/navigation contract 协作。只有契约确实被多个模块或宿主消费时才拆:
+
+```text
+:feature:<name>:api
+:feature:<name>:impl
+```
+
+不要给每个页面默认生成 `api/impl`。
+
+## 5. Stage C:按价值升级为 KMP/CMP
+
+不是所有 Android Feature 都需要共享。升级后目录形态为:
+
+```text
+feature/<feature>/src/
+├── commonMain/kotlin/...     # Contract、纯 reducer/presenter、可共享 Screen
+├── commonTest/kotlin/...     # 状态归约和业务契约测试
+├── androidMain/kotlin/...    # Android ViewModel/route/effect adapter
+├── desktopMain/kotlin/...    # 目标确实需要时创建
+└── iosMain/kotlin/...        # 目标确实需要时创建
+```
+
+- `Contract` 和纯状态归约通常最先成为 common 候选。
+- `Screen` 只有不依赖 Android resource/API、navigation runtime 和平台 launcher 时才进入 `commonMain`。
+- ViewModel/状态宿主是否共享必须通过实际依赖与 lifecycle API 验证,不因文件名机械移动。
+- source set 不为未来假设预建;新增 target 同时新增真实 compile/test gate。
+
+## 6. Core、platform 与 app host
+
+长期物理结构统一为:
+
+```text
+app/android/                    # Android Application、导航图、Koin 聚合、兼容入口
+app/desktop/                    # 需要时创建
+core/model/                     # 领域值与序列化模型
+core/domain/                    # Gateway、UseCase、纯业务规则
+core/data/                      # Repository 契约与可共享组合
+core/designsystem/              # token、theme、基础视觉组件
+core/ui/                        # 跨 Feature 组合组件
+feature/<name>/                 # 用户能力
+platform/android/<capability>/  # DB、网络、服务、reader、rule engine 等实现
+platform/jvm/<capability>/
+```
+
+`app` 只负责组装,不承载可复用业务实现。`core` 不依赖 Feature;Feature 不依赖其他 Feature 的实现;platform
+实现依赖 core contract,反向依赖禁止。
+
+## 7. 资源和测试
+
+- Stage A 资源仍在 `app/src/main/res`,新资源统一使用 `feature_<feature>_` 前缀,便于模块提升时精确移动。
+- Feature 私有 drawable/font/raw 记录 owner;没有第二调用方不移入公共资源。
+- Stage B 后资源随 Feature module 存放;公共主题/token 才进入 designsystem。
+- 测试路径镜像生产包:`app/src/test/.../feature/<name>` 或模块的 `src/commonTest`、
+  `src/androidUnitTest`。
+- Preview fixture、fake 和 test data 放在 Feature 的 preview/test source set;不放入生产 `utils`。
+
+## 8. 旧结构迁移规则
+
+1. 维护 `docs/dev/feature-catalog.md`,记录 canonical name、现包、入口、owner、状态和目标模块;先解决命名,不先移动文件。
+2. 选择一个中小型、已 Compose 化的 Feature 做样板;不要先移动 `book/read`、`config` 或整个 `widget`。
+3. 第一批 PR 只做 package/file relocation 与 import 修复,行为保持不变。
+4. 第二批再收拢 Contract/Screen/ViewModel 和直接 DAO/host 依赖。
+5. 边界稳定后才提升 Gradle module;KMP 化是之后独立切片。
+6. 旧 Activity/Fragment/XML 未迁移时可以留在 `ui/...`;通过明确 route/adapter 调用新
+   Feature,不为目录整齐强搬遗留实现。
+7. Feature 完成迁移后,禁止继续向其旧 `ui/...` 包新增文件,并删除空目录和无调用方兼容桥。
+
+每次迁移必须报告:移动文件、包名/API 变化、依赖边变化、旧路径剩余调用方、资源 owner、测试和回滚方式。
+
+## 9. 首批建议
+
+- 先建立 catalog 和静态检查的 report 模式。
+- 从 `about`、`highlightTagRule`、`replace/edit` 等边界较小且已有 Compose Contract/Screen/ViewModel
+  的候选中选一个样板,实际选择前做依赖审计。
+- `book/read` 保持平台岛;`widget/components` 先做 owner/调用方盘点,不整体改名为 core。
+- `config` 先作为一个 Settings 业务域,内部按 section 分包;不要立即为每个设置页创建独立 Gradle
+  module。

+ 256 - 0
docs/dev/kmp-cmp-modernization.md

@@ -0,0 +1,256 @@
+# Legado KMP/CMP 工程现代化路线图
+
+> 状态:提案 / 渐进执行基线(2026-08-20)。本文区分“当前事实”“目标结构”和“计划门禁”;未创建的模块与
+> Gradle task 都不是现有 API。
+
+## 1. 目标与非目标
+
+目标是在不牺牲 Android 行为、阅读性能和规则兼容性的前提下,把仓库演进为可验证的 Kotlin Multiplatform /
+Compose Multiplatform 工程:
+
+- 共享稳定的模型、领域规则、UseCase、Repository 组合逻辑和适合共享的 Compose UI。
+- Android、Desktop、iOS 等宿主只组装平台实现与能力差异。
+- 用 Gradle 依赖边界和 CI 门禁替代仅靠包名与评审维持的约定。
+- 让 AI 与人工开发使用同一套 AGENTS、专项 Skill、架构文档、源码与测试证据。
+
+非目标:不一次性重写 `:app`;不承诺首个非 Android 目标功能齐平;不把所有代码移入 `commonMain`;不以
+Compose 替换成熟阅读器渲染为前置;不在同一批同时替换数据库、网络、导航、DI、UI 和模块系统;不照搬 Now in
+Android 的模块数量。
+
+## 2. 依据与当前基线
+
+设计参考:
+
+- [Now in Android](https://github.com/android/nowinandroid) 的应用组装、Feature `api/impl`、core 模块和
+  convention plugin
+  思路,以及其 [架构说明](https://github.com/android/nowinandroid/blob/main/docs/ArchitectureLearningJourney.md)
+  与 [模块化说明](https://github.com/android/nowinandroid/blob/main/docs/ModularizationLearningJourney.md)。
+- [AndroidProject-Compose 工程边界](https://compose.dusksnow.top/help/intro/modularization.html)、[架构职责](https://compose.dusksnow.top/help/intro/architecture.html)
+  和 [AI 辅助开发](https://compose.dusksnow.top/help/intro/ai-coding.html) 的“规则 / Skill / 文档 /
+  源码 / 测试”分层。
+- KMP/CMP 的具体 API
+  与支持矩阵必须在每个实施切片中重新核对 [Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html)、[Compose Multiplatform](https://www.jetbrains.com/compose-multiplatform/)
+  和 Android 官方文档,本文不冻结易过期的版本结论。
+
+2026-08-20 盘点快照:
+
+- 现有 Gradle 模块为 `:app`、`:modules:book`、`:modules:rhino`、`:baselineprofile`;`modules/web` 独立构建。
+- `app/src/main/java/io/legado/app` 约有 803 个 UI Kotlin 文件,UI、领域、数据、平台服务仍集中在 `:app`。
+- `domain/` 中仍存在 Android import、`File`、`InputStream`、`java.time`、JCA 等 JVM 类型,不满足直接搬入
+  `commonMain` 的条件。
+- 已有 147 个本地测试文件,以及 `verifyConfigArchitecture`、lint、unit test、debug assemble 的 CI 门禁。
+- `:modules:book` 的解析职责相对独立,但源码以 Java/JDK API 为主且含少量 Android
+  API,适合作为早期依赖审计与边界试点,不能直接视为 `commonMain`;`:modules:rhino` 依赖 Rhino、OkHttp 和
+  JVM 生态,应先作为平台实现保留。
+- `docs/dev/mad-modernization-plan.md` 已确立“行为优先、基线棘轮、阅读器渲染岛”的纪律,本路线不能推翻它。
+- 当前单体内部的 Feature-first 目录、文件归属和模块晋级门槛见 `docs/dev/feature-first-structure.md`
+  ,现有目录到 canonical owner 的初始映射见 `docs/dev/feature-catalog.md`;它们是本路线进入真实 Gradle
+  模块前的过渡规范。
+
+盘点数字会变化。实施前重新运行 inventory,不把数字写进自动门禁。
+
+## 3. 目标工程结构
+
+目标结构是方向图,不要求一次创建全部模块:
+
+```text
+build-logic/convention/             # 模块类型、编译、Compose、测试、lint 约定
+app/android/                        # Android Application、DI、服务与兼容入口
+app/desktop/                        # 首个低成本非 Android 证明宿主
+app/ios/                            # capability 与产品范围成熟后再创建
+core/model/                         # 可序列化领域值
+core/domain/                        # Gateway、UseCase、纯规则
+core/platform/                      # Clock、FileAccess、RuleEngine 等窄契约
+core/data/                          # Repository 接口与可共享组合逻辑
+core/designsystem/                  # CMP tokens、theme、稳定基础组件
+core/ui/                            # 跨 Feature UI
+feature/<domain>/                   # presentation、Feature UI、内部 DI
+feature/<domain>/api/               # 仅在真实跨模块契约出现后拆分
+feature/<domain>/impl/              # 与 api 成对且有真实消费者时存在
+platform/android/{database,network,ruleengine,services,reader}/
+platform/jvm/ruleengine/            # Rhino 可复用时的 JVM 实现
+modules/{book,rhino,web}/            # 现有能力渐进演进
+```
+
+### 模块依赖不变量
+
+1. App host 是 composition root,可依赖所有需要交付的 Feature `impl` 和平台实现。
+2. Feature `api` 不依赖其他 Feature;Feature `impl` 只能依赖其他 Feature 的 `api`,不能依赖其 `impl`。
+3. Core 不反向依赖 Feature。Feature graph 聚合放在 app host,不放入可复用 navigation/core 模块。
+4. `commonMain` 不依赖 Android、JDK、Room DAO、文件 URI、资源 ID 或平台服务类型。
+5. 平台实现依赖共享契约,公共契约不依赖具体平台实现。
+6. `api` 只暴露真实稳定契约;默认使用 `implementation`,需要下游编译可见时才使用 Gradle `api`。
+7. 不设无限增长的 `core:common` 或 `utils`。跨 Feature 复用必须有稳定职责名称。
+
+### Source set 策略
+
+- `commonMain`:平台无关实现;至少由一个非 Android target 编译,才算完成共享。
+- `commonTest`:领域不变量、序列化、Repository 组合、状态归约和规则契约测试。
+- `androidMain`:Context、Room/SQLite 驱动、服务、通知、URI、Cronet/OkHttp、Activity 兼容等。
+- `desktopMain` / `iosMain`:只实现 capability matrix 承诺的能力;不支持项显式返回不可用状态。
+- 优先普通 interface + DI;只有语言/系统原语且各 target 必须静态提供时才采用 `expect/actual`。
+- Android 与 Desktop/JVM 共用但不能进入 common 的代码,先放有明确 owner 的 JVM library/source
+  set;自定义中间 source set 必须同时验证 Gradle、IDE 分析和消费方 JVM target,不能用同一源码根多挂
+  target 掩盖边界。
+- 禁止用 `expect class URL/File/InputStream`、OkHttp API 镜像或 `Any`
+  类型擦除维持旧公开签名;先改成领域值、source/sink、request/response 或 capability 契约。
+
+## 4. 平台能力矩阵
+
+每新增 target,先在 PR 中更新矩阵;不允许用空实现把红格伪装成绿格。
+
+| 能力                       | Android     | Desktop/JVM 试点 | iOS 候选   | 初始策略                                 |
+|--------------------------|-------------|----------------|----------|--------------------------------------|
+| 领域模型 / 纯规则 / UDF reducer | 支持          | 支持             | 支持       | 第一批 commonMain                       |
+| TXT/EPUB 解析              | 支持          | 待验证            | 待验证      | 先审计 `modules:book` 的 Android/JVM API |
+| Room 数据                  | 支持          | 待 PoC          | 待 PoC    | 先抽 Gateway,保留 Android actual         |
+| HTTP / Cronet            | 支持          | 部分             | 部分       | 共享请求语义,客户端留平台侧,之后再评估 Ktor            |
+| Rhino JS 规则              | 支持          | JVM 可评估        | 不支持/替代引擎 | `RuleEngine` capability + 兼容测试       |
+| 前台服务 / 通知 / 媒体会话         | 支持          | 不适用            | 平台实现     | 永不进入 commonMain                      |
+| 阅读器渲染 / 手势 / 翻页动画        | Android 专业岛 | 不承诺            | 不承诺      | 共享 render model,不强制共享 renderer       |
+| Compose 设计系统 / 低风险页面     | 支持          | 试点             | 后续       | 先 tokens 和叶子组件,再 Feature             |
+| WebService / 远程书架        | 支持          | 可选             | 可选       | 协议模型可共享,server lifecycle 平台化         |
+
+## 5. 分阶段迁移
+
+### Phase 0:冻结基线与建立地图
+
+- 记录模块图、包依赖、Android/JVM import、测试和关键 Android 行为路径。
+- 新规则采用 report → freeze baseline → blocking,历史值只允许下降。
+- 为书源/规则、导入导出、阅读记录、设置和阅读器关键路径补契约/行为基线。
+- 明确首个非 Android 目标和首个共享 Feature;建议先用 Desktop 做证明,产品目标另行决策。
+
+退出条件:现有 CI 全绿;每个候选切片有 owner、输入输出、平台依赖表和回滚点。
+
+### Phase 1:构建逻辑与边界门禁
+
+- 引入 `build-logic` convention plugins,先覆盖新模块,不一次改写 `:app` 全部构建脚本。
+- 按 `feature-first-structure.md` 建立 Feature catalog 与 `:app` 内 canonical package;新代码先停止扩大旧
+  `ui/...` 混杂结构。
+- 只定义需要的模块类型:KMP/CMP library、Android app/library、JVM library、Feature API/impl。
+- 在 smoke module 中评估当前 AGP/Kotlin 组合下的 KMP Android library plugin,通过后再固化到
+  convention plugin。
+- 生成/校验模块依赖图;禁止 core→feature、feature impl→feature impl 等逆向依赖。
+- 保留 `verifyConfigArchitecture` 作为 Android 遗留棘轮;新增规则逐步拆成职责明确的 task 或静态分析。
+- convention plugin 只放跨模块稳定默认值;native 打包、版本改写和单平台发布流程拆到独立插件或脚本。
+
+退出条件:代表性模块可用 convention plugin 构建;依赖违规有可读错误;Android 产物不变。
+
+### Phase 2:纯 Kotlin/KMP 核心
+
+按风险从低到高抽取:值对象与序列化模型 → 纯函数/解析规则 → Gateway → UseCase → Repository 组合。
+
+- 先消除公共 API 中的 `Context`、`File`、`InputStream`、Room entity、资源 ID 和 JVM-only 时间/加密类型。
+- 每移动一组类型,先在原位置建立适配层,迁移调用方后再删除旧入口。
+- `modules:book` 先做 dependency audit;能纯化的源码进入 common,平台文件访问留 adapter。
+- 每个 `expect/actual` 声明在审查中写明为何普通接口 + DI 不足,并由门禁统计新增量。
+
+退出条件:`commonTest` 通过,至少一个非 Android target 编译,同一 Android 行为测试仍通过,历史泄漏基线下降。
+
+### Phase 3:平台契约与数据层
+
+- 为 Clock、Dispatcher、FileAccess、Archive、Crypto、RuleEngine、NetworkEngine、Database 等定义窄能力契约。
+- Android 实现先包住现有 Room、OkHttp/Cronet、Rhino、文件与系统服务,不同时更换底层库。
+- 平台实现由 Koin/app composition root 显式注入。
+- Repository 的同步、缓存选择、错误语义和取消语义可共享后再移到 common。
+- 数据库跨平台方案用 PoC 和迁移/导入导出测试决策,不能仅凭“支持 KMP”替换生产数据库。
+
+退出条件:Android adapter contract tests 通过;共享层没有平台类型;故障、取消、事务与序列化语义有测试。
+
+### Phase 4:CMP 设计系统与首个 Feature
+
+- 先共享颜色/尺寸/排版 token、无平台依赖的叶子组件和 Preview fixtures。
+- 选择低风险、少系统依赖、非阅读主链的 Feature 做首个端到端切片。
+- Route/导航 runtime 留在 host;共享 UiState、Intent/reducer、Screen/Content,在平台层桥接 Effect。
+- 不在首个 Feature 同时替换 Navigation 3。先共享 route 语义/参数,至少两个宿主出现相同栈需求并有返回结果测试后,再决定是否共享导航
+  runtime。
+- Material 3 与 Miuix 双引擎只有在目标平台均有真实实现时才进入公共组件,否则以平台主题 adapter 隔离。
+
+退出条件:Android 行为与视觉基线通过;选定 target 能编译并完成主路径;无平台调用泄入 shared UI。
+
+### Phase 5:按业务域扩展
+
+- 以垂直 Feature 为单位迁移,不按技术层进行长时间“大搬家”。
+- 只有跨 Feature 契约稳定时拆 `api/impl`;应用宿主聚合所有 impl 和导航 graph。
+- 每完成一批,删除已无调用方的兼容入口并下调 baseline。
+
+### Phase 6:高风险平台岛决策
+
+- Rhino:以现有脚本兼容测试决定 JVM 复用、替代引擎或非 JVM target 不支持。
+- 阅读器:共享书籍/章节/render model 与业务状态;Android `ReadView` 可长期保留。新 renderer 必须先过真机性能与交互
+  parity。
+- Android 服务、媒体、通知、WebService:共享协议和领域状态,生命周期实现保持平台专属。
+
+### Phase 7:新增正式 target
+
+只有 capability matrix、产品范围、发布链、崩溃/性能观察和数据兼容方案明确后,才把 Desktop/iOS
+从架构证明提升为正式产品 target。
+
+## 6. 门禁分级
+
+| 级别             | 适用范围                      | 必须通过                                                                        | 状态           |
+|----------------|---------------------------|-----------------------------------------------------------------------------|--------------|
+| G0 Android 基线  | 所有 PR                     | unit test、lint、`verifyConfigArchitecture`、debug assemble、`git diff --check` | 已存在          |
+| G1 模块边界        | 新/改 Gradle 模块             | convention plugin、依赖图规则、无循环、无禁止方向                                           | 计划           |
+| G2 Common 纯度   | `commonMain` 变化           | 禁止平台 API、`commonTest`、metadata + 非 Android target compile、公共 API 检查         | 随首个 KMP 模块启用 |
+| G3 数据/平台适配     | Gateway/Repository/actual | contract tests、取消/线程/错误/事务语义、序列化和迁移兼容                                       | 按切片启用        |
+| G4 CMP Feature | shared UI                 | Android UI/行为验证、目标平台 smoke、导航/effect parity、accessibility/insets            | 按 Feature 启用 |
+| G5 高风险能力       | reader/rules/services     | 真机 parity、性能基线、脚本兼容、release/noR8 或专项验证                                      | 强制人工审批       |
+| G6 发布          | 正式 target                 | target 测试/打包/签名、数据升级回退、观测与 capability 文档                                    | 产品化后启用       |
+
+门禁规则:新门禁先 report 再冻结 baseline 最后 blocking;baseline 减少时必须下调,新增违规不得抬高;未创建的
+task 只能写成计划名称;主分支 CI 跑对应级别完整矩阵;平台状态分别记录
+compile、contract-test、smoke、package、release-ready,不能用一个“支持”覆盖所有等级;编译成功不替代行为、视觉、数据兼容和性能验证。
+
+## 7. 代码生成与 convention plugins
+
+优先顺序:
+
+1. convention plugin 统一 target、compiler、Compose、lint、测试、显式 API 与 source-set 默认值。
+2. 约定稳定后,Feature scaffold 生成最小 Contract、ViewModel/presenter、Screen/Content、测试与 build 文件。
+3. 模块图从 Gradle 模型生成,不手工维护易漂移的图。
+4. 库自身的编译期生成物留在 `build/generated`,不包装成第二套自研框架。
+
+生成器门禁:至少两个手工样本已证明结构稳定;支持 `--dry-run` 且拒绝覆盖;已有 graph/DI
+只结构化追加或输出人工补丁;不生成空层;有 fixture 和编译验证;生成结果继续接受普通审查。
+
+建议 Phase 1/2 稳定后再实现 `tools/new-kmp-feature`,本文不提前固定其 CLI/API。
+
+## 8. 重构审查协议
+
+推荐顺序:characterization test → contract/adaptor → 调用方迁移 → 删除旧入口 → baseline 下调。
+
+- 一个 PR 只改变一个主要维度;“移动模块 + 换库 + 改 UI + 改业务行为”必须拆分。
+- 纯移动与逻辑修改尽量分开;每个新抽象必须有真实调用方。
+- P0/P1:数据损坏、规则不兼容、阅读/服务回归、错误 actual、取消/线程/事务破坏。
+- P2:依赖倒置、平台类型泄漏、Feature impl 耦合、状态双重所有权、baseline 放宽。
+- P3:命名、模板、文档或模块图漂移。
+
+PR 必备证据:前后依赖变化、行为不变量与测试、`expect/actual` 数量净变化、精确验证命令、capability matrix
+的 compile/test/smoke/package 状态变化、未验证项、兼容入口的移除条件与回滚路径。
+
+## 9. AI 上下文与工作流
+
+```text
+AGENTS.md                 全局不变量、资料优先级、最低验证
+.agents/skills/           专项执行与审查流程
+docs/dev/                 架构理由、路线图、能力矩阵
+source + tests + Gradle   当前真实 API、行为和可执行证据
+```
+
+推荐流程:明确目标/验收 → AGENTS → 专项 Skill → 设计文档与相邻实现 → 依赖/行为基线 → 单一切片 →
+风险匹配门禁 → diff 与未验证项报告。
+
+AI 不得根据目标结构虚构当前模块、task 或 API;文档建议 KMP 不等于获得大规模移动或删除权限。
+
+## 10. 首批建议 Backlog
+
+1. 生成当前包/模块依赖和平台 import 报告,先作为 CI artifact。
+2. 建 `build-logic`,仅给一个 KMP smoke module 使用,验证 convention plugin。
+3. 从 `domain/model` 选择 3–5 个纯值对象建立 `shared:core:model` PoC 和 `commonTest`。
+4. 为 `File`/`InputStream` 暴露点设计 source/sink capability,不改底层实现。
+5. 审计 `modules:book` 的 Java/JDK/Android 依赖,输出 common-ready、需改写/adapter、必须 JVM 三类清单。
+6. 选择低风险设置/信息 Feature 做 CMP 垂直切片,保留 Android 主导航和 DI。
+7. 两个成功 Feature 后再决定是否实现脚手架。
+
+完成以上项目后,再基于构建时间、模块图和真实跨平台收益决定下一批,不预先创建整棵目标目录树。