refactor: normalize capability module paths

This commit is contained in:
jxxghp
2026-08-27 22:20:38 +08:00
parent ea58c245dd
commit f134e068e7
85 changed files with 749 additions and 691 deletions
+4 -4
View File
@@ -69,7 +69,7 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
| 指标 | 当前值 | 解释 |
|---|---:|---|
| 宿主 Python 模块 / 内部依赖边 | 842 / 6,882 | `dependency-baseline.json` 当前快照 |
| 宿主 Python 模块 / 内部依赖边 | 844 / 6,898 | `dependency-baseline.json` 当前快照 |
| 非平凡 SCC | 2 | 新增 Chain 包根环;另一个是隔离的 29 模块 TMDB 移植包环 |
| 跨层 DB 边界债务 | 0 | Application、Chain、API、Agent、Runtime、Workflow 到 DB 的受控债务均为零 |
| Model/Oper 事务债务 | 0 | 自建 Session、自动事务装饰器、直接 commit/rollback 等基线均为零 |
@@ -77,9 +77,9 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
| Event Contract | 53 | 均已有 payload model,但当前全部是 diagnostic enforcement |
| Python 源码量 | 约 271,400 行 | 60 个文件超过 1,000 行,14 个超过 2,000 行 |
| 长方法 | 281 个超过 80 行 | 67 个超过 150 行,23 个超过 250 行;大量是私有方法 |
| 全量 mypy 历史债务 | 11,983 / 601 文件 | strict frontier 当前覆盖 41 个文件,且 ratchet 已新增 2 个错误 |
| Ruff 历史诊断 | 929 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率低水位 | Application 78.24%Domain 79.29% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |
| 全量 mypy 历史债务 | 11,827 / 596 文件 | strict frontier 当前覆盖 41 个文件,本批迁移路径的类型债务已清零 |
| Ruff 历史诊断 | 889 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率低水位 | Application 78.63%Domain 79.29% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |
### 3.3 热点文件
+5 -5
View File
@@ -200,7 +200,7 @@ Chain/Agent 的 `Any` factory 和裸 Oper 仍需迁移为类型化 Port/DTO
| `app/adapters/web/` | Web 技术适配:动态插件路由注册、认证依赖和 OpenAPI 重建;不承载插件路由用例 | `plugin/routes.py` |
| `app/adapters/observability/` | 可选观测技术适配;核心层只依赖 `runtime/observability` 定义的窄端口 | `otel.py` |
| `app/application/` | 读取配置/持久化状态的聚焦应用服务:识别、过滤、通知、RSS、站点、下载器、媒体服务器、存储、整理规则、可靠副作用等;同一主题拆成子包 | `recognition.py``rules.py``rss.py``outbox.py``site/``subscription/``plugin/` |
| `app/application/chain/` | Chain 运行时上下文、跨领域数据端口和 durable event 命令;将组合根注入的能力以命名 getter 暴露给 Chain | `context.py``data.py``durable_events.py` |
| `app/application/chain/` | Chain 运行时上下文、跨领域数据端口和 durable event 命令;将组合根注入的能力以命名 getter 暴露给 Chain | `context.py``data.py``events.py` |
| `app/application/subscription/` | 订阅新增、查询、变更、删除、媒体身份与搜索契约 | `write.py``contract.py``mutation.py``delete.py``identity.py``search.py` |
| `app/application/plugin/` | 插件市场、安装、运行时端口、文件夹操作和动态路由用例;具体 FastAPI 路由适配器在 adapters 层 | `catalog.py``install.py``runtime.py``folders.py``routes.py` |
| `app/application/messaging/` | 渠道回环入口、消息渲染/路由、命令交互会话、插件按钮回调、Agent 消息桥接 | `ingress.py``message.py``router.py``agent.py` |
@@ -458,7 +458,7 @@ flowchart LR
|---|---|
| **Config Reload** | 继承 `ConfigReloadMixin` 并声明 `CONFIG_WATCH`,配置变更时自动重建长生命周期对象(如下载器客户端重连) |
| **Singleton** | `EventManager``ModuleManager``PluginManager` 等全局共享管理器继承 `foundation/singleton.py``Singleton` |
| **Managed Resource** | 可选进程级技术资源(浏览器、虚拟显示等)以 data-only `capability.toml` 声明,`runtime/extensions` 解释生命周期,`startup` 构建 Runtime,消费者经 `runtime/managed_resources.py` 显式获取;插件使用浏览器走 `app.sdk.browser` |
| **Managed Resource** | 可选进程级技术资源(浏览器、虚拟显示等)以 data-only `capability.toml` 声明,`runtime/extensions` 解释生命周期,`startup` 构建 Runtime,消费者经 `runtime/resources.py` 显式获取;插件使用浏览器走 `app.sdk.browser` |
| **Observability** | `runtime/observability` 定义低基数指标和默认 no-op 端口,Startup 可选装配 OTelHTTP、DB、Event、Module、Scheduler、插件生命周期和 Agent 只提交白名单标签 |
---
@@ -631,7 +631,7 @@ flowchart LR
RE["events.py<br/>事件总线"]
RL["log.py<br/>日志运行时(依赖叶子)"]
RCA["cache.py<br/>缓存协议 / 内存后端 / 装饰器"]
MR["managed_resources.py<br/>托管资源门面"]
MR["resources.py<br/>托管资源门面"]
end
subgraph adapters["app/adapters(具体 I/O"]
@@ -704,8 +704,8 @@ flowchart LR
| 指标 | 当前值 |
|---|---:|
| Python 模块 | 842 |
| 内部导入边 | 6,882 |
| Python 模块 | 844 |
| 内部导入边 | 6,898 |
| 非平凡 SCC | 2`ARCH-107` 临时 Chain 包根环;精确 containment 的 TMDB 移植包环) |
| Direct egress | 6612 条待迁移债务,54 条精确 containment |
| Module Contract V2 spec | 217(其中 215 个进入 `run_module` 观察面) |
+4 -4
View File
@@ -144,7 +144,7 @@ G-ARCH 只有在以下条件全部满足后才可完成:
| S4-L2 Event strict contract | `PLANNED` | S0-L2.6,S1-L6 | 宿主事件输入/输出按风险 strict,诊断例外只属于第三方插件兼容 |
| S4-L3 Complexity v2 | `PLANNED` | S3 | 私有方法、class/file、圈复杂度进入门禁;所有超限通过职责拆分归零 |
| S4-L4 全量 mypy 清零 | `PLANNED` | S3,S4-L1,S4-L2 | `mypy-baseline.json` 归零并删除债务接受路径,全宿主 strict 类型通过 |
| S4-L5 Ruff 治理债务清零 | `PLANNED` | S3 | 当前受控 929 条诊断归零,规则集扩展经过独立审查且新增诊断为零 |
| S4-L5 Ruff 治理债务清零 | `PLANNED` | S3 | 当前受控 889 条诊断归零,规则集扩展经过独立审查且新增诊断为零 |
| S4-L6 Coverage/并发/质量证据 | `PLANNED` | S3,S4-L1,S4-L2 | 高风险包纳入 coverageraw concurrency 分类清零;Module Quality 有真实 evidence test |
### S5Plugin、Agent、Domain、Startup 与最终收口
@@ -175,7 +175,7 @@ planning、lease、幂等执行或终态恢复已经完成。
**Ownership**
- `app/application/transfer.py` 拥有 admission DTO、Protocol、结果语义与 persist-before-enqueue 编排。
- `app/application/transfer/workflow.py` 拥有 admission DTO、Protocol、结果语义与 persist-before-enqueue 编排。
- `app/db/adapters/` 提供短 Session/UoW 的 Transfer pending 持久化实现;`app/db/oper/` 只接收
adapter 拥有的 Session 并 stage/flush。
- `app/startup/` 负责构造并注入 adapter,宿主 Chain 不再取得 raw/`Any` `TransferPendingOper`
@@ -242,11 +242,11 @@ git diff --check
**Ownership and compatibility**
- `app/application/transfer.py` 拥有 planning input、plan item、checkpoint 和状态错误合同;JSON 版本、
- `app/application/transfer/workflow.py` 拥有 planning input、plan item、checkpoint 和状态错误合同;JSON 版本、
指纹及 resolved 上下文均可跨进程 round-trip。
- `app/modules/filemanager/transhandler.py` 是唯一目标规划与文件执行实现;`FileManagerModule.transfer`
`TransHandler.transfer_media` 已删除,不保留第二套重命名、覆盖或目录递归逻辑。
- `app/db/adapters/transfer.py` 通过短 Session/UoW 提交 checkpointOper 只负责带状态和指纹条件的
- `app/db/adapters/transfer/admission.py` 通过短 Session/UoW 提交 checkpointOper 只负责带状态和指纹条件的
stage3.0.14 migration 可升级、降级并在中断后重跑。
- cleanup intent 随准入输入冻结。宿主路径由 FileManager 在 `TransferIntercept` 放行后、任何文件写入前
执行;legacy provider 路径为保持旧 ABI 顺序,在全部冻结引用解析成功后、调用 provider 前执行。
+14 -13
View File
@@ -65,10 +65,10 @@ to make the directory tree look symmetrical.
| `app/application/search/` | Search state and later search-plan use cases |
| `app/application/download/` | Download task querying/control and later submission use cases |
| `app/application/music/` | Multi-source music catalog orchestration |
| `app/application/chain/` | Injectable Chain runtime context and compatibility provider |
| `app/application/chain/` | Injectable Chain runtime capabilities: `context.py` owns the runtime dependency aggregate, `data.py` owns named persistence ports, and `events.py` owns durable event write contracts plus replayable payload conversion |
| `app/application/agentdata.py` | Named Agent data ports; canonical Agent consumers use `get_agent_*_port()` and do not alias legacy proxies to Oper classes |
| `app/application/outbox.py` | Durable intent and Outbox repository/dispatcher contracts for post-commit side effects |
| `app/application/transfer_execution.py` | Durable transfer execution contracts: stable operation identity, step/checkpoint state, retry/manual-review commands and terminal-settlement DTOs; contains no SQLAlchemy or external I/O |
| `app/application/transfer/` | Durable transfer use cases: `workflow.py` owns admission/planning/queue behavior; `execution.py` owns stable operation identity, step/checkpoint state, retry/manual-review commands and terminal-settlement DTOs |
| `app/application/plugin/` | Plugin market catalog, installation command, installed-plugin identity contract and startup migration, runtime port, folder operations and dynamic-route use cases; filenames remain single words (`catalog.py`, `identity.py`, `migration.py`, `install.py`, `runtime.py`, `folders.py`, `routes.py`) |
| `app/application/server/` | MoviePilot Server reporting and sharing use cases; local data readers and transport callbacks are injected by startup |
| `app/application/site/` | Configured site catalog, authentication level and index-resource capability; the generated extension and its data bundle stay together here |
@@ -94,7 +94,7 @@ directory categories.
| `app/runtime/observability/` | Low-cardinality metric contracts and no-op-capable observation facade |
| `app/runtime/log.py` | Complete console/plugin/file logging runtime and shutdown |
| `app/runtime/cache.py` | Cache protocols, memory implementations, decorators and proxies |
| `app/runtime/managed_resources.py` | Provider-neutral acquisition, observation and shutdown facade for process-owned optional resources |
| `app/runtime/resources.py` | Provider-neutral acquisition, observation and shutdown facade for process-owned optional resources |
| `app/runtime/tasks.py` | Lifespan-scoped ownership, cancellation and bounded shutdown waiting for in-process background tasks |
| `app/runtime/execution.py` | Shared sync/async execution and cross-thread submission boundary with correlation propagation |
| `app/runtime/correlation.py` | Request/cross-thread correlation context and safe propagation into logs and child work |
@@ -152,7 +152,7 @@ retained only for compatibility and is not a canonical Oper substitute.
Durable transfer execution follows one explicit boundary. The Chain freezes each
external file operation into the Application-owned contract in
`app/application/transfer_execution.py`; `app/db/adapters/transfer_execution.py`
`app/application/transfer/execution.py`; `app/db/adapters/transfer/execution.py`
uses short transactions to persist the task ledger and fences every state change
with the current lease and attempt token. `app/db/oper/transferexecutionstep.py`
remains table-oriented and never owns retry or recovery policy. External file I/O
@@ -206,7 +206,7 @@ mechanism remains in `app/adapters/system/resource.py`.
可选的进程级技术资源使用 Managed Resource 合同:实现及其 data-only
`capability.toml` 与适配器同目录,`runtime/extensions` 只解释通用的同步/异步
`start``stop` 生命周期,`startup` 负责构建 Capability Runtime。声明必须使用
`on_first_use`,普通启动只发现声明;消费者通过 `app/runtime/managed_resources.py`
`on_first_use`,普通启动只发现声明;消费者通过 `app/runtime/resources.py`
显式获取资源。关闭路径先释放消费者,再关闭已初始化 Runtime,未使用的资源不得因关闭而物化。
应用级启动顺序使用 `app/startup/lifecycle/components.py` 的组件描述声明依赖、
normal/safe-mode 范围、start/stop 顺序、超时预算和失败策略。新增进程级资源不得只在
@@ -484,9 +484,9 @@ Durable post-commit side effects have a separate boundary:
must not replace an Outbox or persistent task table.
Transfer durable admission follows the same ownership direction without using
the Outbox as an execution queue: `app/application/transfer.py` owns the typed
the Outbox as an execution queue: `app/application/transfer/workflow.py` owns the typed
admission and versioned planning-checkpoint contracts, while
`app/db/adapters/transfer.py` commits admission and the
`app/db/adapters/transfer/admission.py` commits admission and the
`accepted -> provider_pending -> planned` compare-and-set transitions in short
Session/UoW scopes. `app/modules/filemanager/` owns the
single pure-plan and checkpoint-execution implementation: all file writes occur
@@ -504,8 +504,8 @@ command; `FileManagerModule.transfer` and `TransHandler.transfer_media` must not
be recreated.
Transfer execution ownership is orthogonal to those planning phases.
`app/application/transfer.py` defines the claim, heartbeat, release and fenced
mutation Port; `app/db/adapters/transfer.py` implements each operation in a
`app/application/transfer/workflow.py` defines the claim, heartbeat, release and fenced
mutation Port; `app/db/adapters/transfer/admission.py` implements each operation in a
short UoW with a unique lease token. Any active lease rejects another claim,
including one from the same process owner. Expired leases may be taken over with
a new token and incremented attempt count, while the stale token cannot renew,
@@ -659,8 +659,9 @@ driven workflow registration.
| `app/application/subscription/write.py` | Subscription media translation and sync/async write-port orchestration |
| `app/application/outbox.py` | Durable intent, topic handler and Outbox repository contracts |
| `app/db/adapters/outbox.py` | SQLAlchemy Outbox persistence, claim/lease and retry state adapter |
| `app/application/transfer.py` | Transfer task, durable admission, versioned planning input/checkpoint contracts and queue use case |
| `app/db/adapters/transfer.py` | SQLAlchemy admission/checkpoint persistence, CAS state transition and detached snapshot adapter |
| `app/application/chain/events.py` | Chain durable-event write port, settlement projection and replayable payload conversion |
| `app/application/transfer/workflow.py` | Transfer task, durable admission, versioned planning input/checkpoint contracts and queue use case |
| `app/db/adapters/transfer/admission.py` | SQLAlchemy admission/checkpoint persistence, CAS state transition and detached snapshot adapter |
| `app/application/scheduling.py` | Runtime scheduler facade for Agent tools and endpoints; `Scheduler` class registered by `app/startup/initializers/scheduler.py` |
| `app/application/commands.py` | Command registry facade for Agent tools and endpoints; `Command` class registered by `app/startup/initializers/command.py` |
| `app/application/workflow.py` | Workflow use cases plus the runtime port consumed by API and Chain; `WorkFlowManager` is registered by `app/startup/initializers/workflow.py` |
@@ -700,8 +701,8 @@ driven workflow registration.
| `app/adapters/external/plugin/client.py` | Plugin-market read adapter and cache-refresh boundary |
| `app/adapters/system/plugin/package.py` | Plugin package installation adapter |
| `app/adapters/system/plugin/dependency.py` | Plugin dependency inspection and installation adapter |
| `app/runtime/extensions/managed_resource_adapter.py` | Data-only managed-resource registry and sync/async lifecycle adapters |
| `app/runtime/managed_resources.py` | Lightweight acquisition, state observation and shutdown facade |
| `app/runtime/extensions/resource.py` | Data-only managed-resource registry and sync/async lifecycle adapters |
| `app/runtime/resources.py` | Lightweight acquisition, state observation and shutdown facade |
| `app/foundation/reflection.py` | Generic reflection and Python module discovery |
| `app/adapters/network/http.py` | Shared synchronous and asynchronous HTTP clients |
| `app/adapters/network/browser.py` | Browser launch facade and browser session implementation |
+5 -2
View File
@@ -9,8 +9,9 @@ All new code must follow these conventions. Consistent naming is how the codebas
| Context | Convention | Examples |
|---|---|---|
| Python source files | `snake_case.py` | `download.py`, `qbittorrent.py`, `package.py` |
| New files in canonical capability packages | Focused `snake_case.py`; prefer a package-owned noun and an existing owned domain file before adding one | `torrent.py`, `plugin_manager.py`, `package.py` |
| Module package directories | `snake_case/` | `qbittorrent/`, `synologychat/` |
| New files in canonical capability packages | Prefer one lowercase responsibility noun; extend an existing owner before adding a sibling file | `torrent.py`, `package.py`, `resources.py` |
| Multi-file capabilities | Create a same-named package and use focused single-word child files; do not flatten related `<capability>_<role>.py` siblings | `transfer/workflow.py`, `transfer/execution.py` |
| Module package directories | `snake_case/`; package roots do not duplicate-export host implementations | `qbittorrent/`, `synologychat/`, `transfer/` |
| Test files | `test_<domain>.py` | `test_download_chain.py`, `test_subscribe_endpoint.py` |
| Alembic migrations | Auto-generated by Alembic; do not rename | `20240101_add_column.py` |
| Skill directories | `<kebab-case>/` | `transfer-failed-retry/`, `moviepilot-cli/` |
@@ -118,6 +119,8 @@ All new code must follow these conventions. Consistent naming is how the codebas
| `def handleConfigChanged():` | `def on_config_changed():` or `def handle_config_changed():` |
| `configuration.get("RssUrls")` | `configuration.get(SystemConfigKey.RssUrls)` |
| `class subscribe_oper:` | `class SubscribeOper:` |
| `transfer.py` + `transfer_execution.py` | `transfer/workflow.py` + `transfer/execution.py` |
| Package-root host re-exports for an old path | Exact SDK/Compat mapping; host code imports the owning child module |
| `MessageChannel.Telegram`(新代码) | `NotificationChannel.Telegram` |
| `Notification(title=...)`(新代码) | `Message(title=...)` |