Merge origin/v3 into codex/feat/plugin-data-query-sdk-v3

This commit is contained in:
jxxghp
2026-08-28 06:57:20 +08:00
45 changed files with 1258 additions and 494 deletions
+28 -13
View File
@@ -69,7 +69,7 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
| 指标 | 当前值 | 解释 |
|---|---:|---|
| 宿主 Python 模块 / 内部依赖边 | 849 / 6,940 | `dependency-baseline.json` 当前快照 |
| 宿主 Python 模块 / 内部依赖边 | 850 / 6,944 | `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,820 / 596 文件 | strict frontier 当前覆盖 41 个文件,本批迁移路径的类型债务已清零 |
| Ruff 历史诊断 | 885 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率低水位 | Application 78.81%Domain 79.29% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |
| 全量 mypy 历史债务 | 11,809 / 596 文件 | strict frontier 当前覆盖 41 个文件,本批迁移路径的类型债务已清零 |
| Ruff 历史诊断 | 875 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率低水位 | Application 78.89%Domain 79.29% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |
### 3.3 热点文件
@@ -107,8 +107,8 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
|---|---|---|---|---|
| ARCH-001 | P0 | 已交付 | 恢复 mypy ratchet | `5df388719` 已推送,主线既有 CI gate 通过 |
| ARCH-101 | P1 | 已交付 | 统一规则、总览、基线和语义门禁 | `113355784` 已推送,Unit Tests `33031697902`、Pylint `33031697785` 全绿,远端 `0/0` |
| ARCH-102 | P1 | 执行中 | 将 Transfer pending 升级为真实 E3 状态机 | `S1-L1.1``S1-L1.5` 全部交付后,崩溃窗口可判定恢复,结果未知时进入人工确认 |
| ARCH-103 | P1 | 执行 | 类型化 Chain/Agent 数据 Port 与 DTO | 宿主主路径不再注入无 Session Oper,不向入口泄漏 ORM |
| ARCH-102 | P1 | 已交付 | 将 Transfer pending 升级为真实 E3 状态机 | `e9de149db``a2e249f20` 已推送;Unit Tests `33092427327`、Pylint `33092427348` 全绿,崩溃结果未知时进入人工确认 |
| ARCH-103 | P1 | 执行 | 类型化 Chain/Agent 数据 Port 与 DTO | 宿主主路径不再注入无 Session Oper,不向入口泄漏 ORM |
| ARCH-104 | P1 | 待执行 | 收口跨多次写入的业务事务 | 站点/规则引用清理可整体回滚或幂等恢复 |
| ARCH-105 | P1 | 待执行 | 明确 post-commit 与 Outbox 完成语义 | “业务已提交、后置效果 pending”可被调用方正确识别 |
| ARCH-106 | P1 | 待执行 | 让线程/队列/日志 writer 由 bootstrap/lifecycle 显式构造 | 导入或普通 Chain 构造不再启动进程资源 |
@@ -196,8 +196,8 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
claim/lease/heartbeat/attempt、过期接管、固定退避的唯一恢复入口和有界关闭 owner。
- `S1-L1.4 幂等执行与终态结算``VERIFIED`。已交付稳定 operation ledger、严格结果探测、
唯一 retry owner、`manual_review` 人工判定和 history/pending/outbox 同 UoW 终态结算。
- `S1-L1.5 E3 全链收口``PLANNED`完成崩溃矩阵、兼容验收与旧路径删除。此叶交付前,
ARCH-102 父项保持“执行中”,不得以局部绿色宣称 E3 完成
- `S1-L1.5 E3 全链收口``VERIFIED`。崩溃矩阵、3.0.17 升降级、重复回放、稳定计划身份、
outcome/settlement 一致性和插件 ABI 已完成验收;旧 fail-open、重复状态与兼容层外旧入口已删除
**问题与证据**
@@ -227,7 +227,13 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
- 管理员人工判定 API 只公开 `not_applied` 与带结果证据的 `applied`,并持久记录操作者、理由、结论
和 revision;无租约人工路径不能直接伪造失败终态。
- 以上实现已满足 `docs/adr/0007-background-action-reliability.md:123-139` 对 E3 稳定身份、步骤状态、
lease/heartbeat 和人工恢复的阶段性要求;完整崩溃矩阵与兼容收口仍由 `S1-L1.5` 验收。
lease/heartbeat 和人工恢复的要求。`RETRY_WAIT`、重放、双重失败、人工放弃和结算崩溃窗口均有
故障注入覆盖;计划指纹、步骤成员关系和所有状态写入使用精确 CAS,异常不再降级到旧执行路径。
- canonical 模块已按职责聚合为 `app/application/chain/events.py``app/application/transfer/execution.py`
`app/runtime/resources.py``durable_events.py``transfer_execution.py``managed_resources.py`
等旧物理模块已退役,仅允许精确 Compat manifest 和兼容测试引用旧导入名,宿主不保留重复导出。
- 交付提交为 `e9de149db``a2e249f20`;精确 head SHA 的 Unit Tests `33092427327` 与 Pylint
`33092427348` 全绿,覆盖率低水位同步提升至 Application `78.71%`
**目标与步骤**
@@ -266,16 +272,25 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
`app/application/agentdata.py:91-120` 还通过 `__dict__.update()` 动态组装端口。
- `app/startup/initializers/modules.py:848-863,896-910` 仍向生产 Chain/Agent 注入多个无 Session Oper。
- 无 Session Oper 会为单次调用独立创建事务;一个业务操作的“查询后更新”可能被拆成多个事务。
- Workflow query 和 Chain/Agent raw data port 仍返回 `Any`/ORMSubscription mutation 内部也消费 ORM
因而存在 Session 生命周期外 detached/lazy-load 的潜在风险。公开 Subscription、Site、History
QueryService 已经投影 DTO属于完成项,不应重做。
- Workflow query 已在 S1-L2 迁入冻结 DTO 和 adapter-owned Session 投影;其余 Chain/Agent raw data port
仍返回 `Any`/ORMSubscription mutation 内部也消费 ORM,因而仍存在 Session 生命周期外
detached/lazy-load 的潜在风险。公开 Subscription、Site、History QueryService 已经投影 DTO
属于完成项,不应重做。
- S1-L2 由 `b4f873654``a01a35bcb` 交付;精确 head SHA 的 Unit Tests `33098869736`
Pylint `33098869837` 全绿,Application 覆盖率低水位提升并固化至 `78.78%`。该证据只完成
Workflow query 纵切面,不能替代 S1-L3 对其余 Chain/Agent raw data port 的清零。
**目标与步骤**
- [ ] 按领域定义 Query/Command Protocol,不再使用通用 `OperFactory = Callable[[], Any]`
- [ ] 写 Port 由 `db/adapters` 创建单操作 Session/UoWOper 的 canonical 写方法只 stage/flush
读取方法仍可在调用方 Session 中查询。
- [ ] 查询 Port 在 adapter Session 内映射为冻结 DTO/ProjectionApplication 和 API 不接收 ORM。
- [x] Workflow 查询 Port 在 adapter Session 内映射为冻结 DTO/ProjectionAPI、Agent、Chain、Scheduler、
Workflow runtime 和中心服务分享均不接收 ORM。
- [x] Workflow 执行写端由 Chain 直连 `WorkflowExecutionPort` 和短 Session/UoW 事务服务;canonical
`WorkflowOper` 只保留显式 Session query/stage,旧无 Session 五方法只存在于 SDK Legacy/Compat。
- [x] 删除 Chain registry 中零消费者 `*PortProxy`/动态转发和 `ChainRuntimeContext.data_ports`
伪注入;Workflow 执行服务只在 Application owner 配置一次,不再重复注册到 `ChainDataPorts`
- [ ] `ChainDataPorts`/`AgentDataPorts` 可暂时保留为兼容聚合器,但字段必须显式、可类型检查。
- [ ] 以一个业务纵切面迁移并验证后,再迁移下一组,禁止一次替换所有 Oper。
- [ ] 增加 AST 门禁,禁止向 `ChainDataPorts``AgentDataPorts` 和新的 canonical use-case service
+2 -2
View File
@@ -704,8 +704,8 @@ flowchart LR
| 指标 | 当前值 |
|---|---:|
| Python 模块 | 849 |
| 内部导入边 | 6,940 |
| Python 模块 | 850 |
| 内部导入边 | 6,944 |
| 非平凡 SCC | 2`ARCH-107` 临时 Chain 包根环;精确 containment 的 TMDB 移植包环) |
| Direct egress | 6612 条待迁移债务,54 条精确 containment |
| Module Contract V2 spec | 217(其中 215 个进入 `run_module` 观察面) |
+18 -8
View File
@@ -86,9 +86,9 @@ G-ARCH 只有在以下条件全部满足后才可完成:
退出条件:Transfer 达到 E3;正式数据 Port 类型化;跨表业务操作有单一 UoWpost-commit/Outbox
竞争、失败呈现、at-least-once 和幂等语义全部闭环。
`S1-L1` 是 ARCH-102 的 Transfer E3 父项,当前状态为**执行中**。只有 `S1-L1.1`
`S1-L1.5` 全部 `DELIVERED`,真实调用链完成迁移旧 fail-open 路径退出 canonical 主程序后,
父项和 ARCH-102 才能标记已交付
`S1-L1` 是 ARCH-102 的 Transfer E3 父项,当前状态为 **DELIVERED**`S1-L1.1`
`S1-L1.5` 全部交付,真实调用链完成迁移旧 fail-open、重复状态和兼容层外旧入口已退出
canonical 主程序;兼容只经统一 Compat/SDK 门面提供
| Leaf | 状态 | 依赖 | 完成定义 |
|---|---|---|---|
@@ -96,9 +96,18 @@ G-ARCH 只有在以下条件全部满足后才可完成:
| S1-L1.2 Planning checkpoint | `VERIFIED` | S1-L1.1 | 版本化输入与指纹先持久化;无 legacy provider 时以 `accepted -> planned` CAS 提交完整计划,有 provider 时先提交 `provider_pending`,全部返回空后再以第二次 CAS 提交 `planned`;重放只执行冻结目标,所有文件副作用晚于对应 checkpoint commit |
| S1-L1.3 Lease 与恢复调度 | `VERIFIED` | S1-L1.2 | claim/lease/heartbeat/attempt 与过期接管规则落地;启动回放和同进程恢复共用唯一调度入口,同一任务同时只有一个 worker owner |
| S1-L1.4 幂等执行与终态结算 | `VERIFIED` | S1-L1.3 | 文件操作、历史提交和 checkpoint 可重放;唯一 retry owner 生效,未知外部结果进入 `manual_review`,仅完整终态删除 pending |
| S1-L1.5 E3 全链收口 | `PLANNED` | S1-L1.4 | 崩溃矩阵、升级/降级、重复回放和插件 ABI 验收完整;旧 fail-open、重复状态与兼容层外旧入口删除,ARCH-102 债务归零 |
| S1-L2 Workflow typed query | `PLANNED` | S0 | Workflow Application Port 不返回 `Any`/ORMSession 内投影 DTO,正式调用方全部切换 |
| S1-L3 Chain/Agent typed data ports | `PLANNED` | S1-L2 | `ChainDataPorts`/`AgentDataPorts` 的 raw Oper/`Any` factory 全部清零,兼容调用进入 Legacy 层 |
| S1-L1.5 E3 全链收口 | `DELIVERED` | S1-L1.4 | `e9de149db``a2e249f20`:崩溃矩阵、3.0.17 升降级、重复回放和插件 ABI 验收完整;旧 fail-open、重复状态与兼容层外旧入口删除Unit Tests `33092427327`、Pylint `33092427348` 全绿ARCH-102 债务归零 |
| S1-L2 Workflow typed query | `DELIVERED` | S0 | `b4f873654``a01a35bcb`Workflow Application Port 不返回 `Any`/ORMSession 内投影冻结 DTO,正式调用方全部切换Unit Tests `33098869736`、Pylint `33098869837` 全绿,覆盖率低水位提升至 Application `78.78%` |
| S1-L3 Chain/Agent typed data ports | `ACTIVE` | S1-L2 | `ChainDataPorts`/`AgentDataPorts` 的 raw Oper/`Any` factory 全部清零,兼容调用进入 Legacy 层 |
| S1-L3.1 Workflow typed execution | `DELIVERED` | S1-L2 | `17d8be2af``b33b29876`:Chain 直连类型化事务服务且单次执行只取一个 port;canonical Oper 删除旧 writer/无 Session 写方法,旧 ABI 只在 `_legacy/workflow.py` 与 Compat overlayUnit Tests `33103913838`、Pylint `33103913935` 全绿,Application 覆盖率低水位提升至 `78.79%` |
| S1-L3.2 Chain registry/DI | `ACTIVE` | S1-L3.1 | 显式类型化 factory,删除 PortProxy 与失效的双重注入,构造器注入真实控制调用 |
| S1-L3.2.1 Registry hygiene | `DELIVERED` | S1-L3.1 | `ac7a20132`:删除零消费者 PortProxy/动态转发和 `ChainRuntimeContext.data_ports` 伪注入;Workflow 退出 Chain registry,只保留 Application owner 单一配置入口;Unit Tests `33120205586`、Pylint `33120205581` 全绿 |
| S1-L3.3 DownloadFailure/MediaServer | `PLANNED` | S1-L3.2 | 两组窄 DTO/Port/adapter 清零 raw Oper,不跨远端 I/O 持有 Session |
| S1-L3.4 User | `PLANNED` | S1-L3.3 | 认证、偏好与渠道绑定投影冻结快照,User Chain/Agent 不接收 ORM |
| S1-L3.5 History | `PLANNED` | S1-L3.4 | Download/Transfer history 统一 typed query/mutation,删除下载历史双事务 fail-open |
| S1-L3.6 Site | `PLANNED` | S1-L3.5 | 复用 Site query/health,补齐同步 typed commandSession 内完成 DTO 投影 |
| S1-L3.7 Subscription | `PLANNED` | S1-L3.6 | Chain/Workflow/interaction 全部消费 typed query/command;完成后进入 S1-L4 原子事务收口 |
| S1-L3.8 Agent/Transfer locator gate | `PLANNED` | S1-L3.7 | 删除 AgentDataPorts 与 Chain locator 跨层泄漏,AST 门禁确认 canonical 无 raw getter/Oper/Any |
| S1-L4 Subscription mutation UoW | `PLANNED` | S1-L3 | Subscription mutation 不跨 Session 传 ORM,正式写路径一个 UoW,旧自动事务入口退出 canonical 路径 |
| S1-L5 站点/规则引用原子清理 | `PLANNED` | S1-L4 | SystemConfig+Subscribe 同事务更新,commit 后快照原子发布,并发/故障注入无部分状态 |
| S1-L6 Outbox 完成语义 | `PLANNED` | S0 | claim 竞争双发清零;业务提交与 effect pending 可区分;stager/store 分离;handler 幂等与崩溃测试完整 |
@@ -144,7 +153,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 | 当前受控 885 条诊断归零,规则集扩展经过独立审查且新增诊断为零 |
| S4-L5 Ruff 治理债务清零 | `PLANNED` | S3 | 当前受控 875 条诊断归零,规则集扩展经过独立审查且新增诊断为零 |
| S4-L6 Coverage/并发/质量证据 | `PLANNED` | S3,S4-L1,S4-L2 | 高风险包纳入 coverageraw concurrency 分类清零;Module Quality 有真实 evidence test |
### S5Plugin、Agent、Domain、Startup 与最终收口
@@ -263,7 +272,8 @@ git diff --check
- 本叶不引入 claim、lease、heartbeat、attempt、执行步骤幂等或 `manual_review`;这些由
`S1-L1.3``S1-L1.4` 交付。
- 文件操作成功后到历史结算前的未知结果仍未达到 E3,ARCH-102 父项继续保持执行中。
- 本叶当时不单独承诺文件操作成功后到历史结算前的未知结果;该能力现已由 `S1-L1.4`
`S1-L1.5` 的持久步骤账本、严格探测、`manual_review` 与 task-aware settlement 完整交付。
**Local verification (2026-08-27)**
+19 -7
View File
@@ -137,11 +137,12 @@ Session. `app/db/adapters/` is the concrete persistence-adapter layer: it may
depend on Application-owned Protocols, UoW/Session and Oper implementations.
This deliberate dependency inversion is the only `DB implementation ->
Application contract` direction; Application must remain free of DB imports.
Migrated workflow, user, interaction, messaging, music, site, media-server, download, subscribe and transfer
Chain consumers use the named `get_chain_*_port()` functions from
`app/application/chain/data.py`; they must not alias migration-time `*PortProxy`
classes back to database Oper names. Those proxy classes remain compatibility
boundaries while the other established Chain domains migrate independently.
Migrated user, interaction, messaging, music, site, media-server, download, subscribe and transfer
Chain consumers temporarily use the named `get_chain_*_port()` functions from
`app/application/chain/data.py` while each owner establishes typed DTO/Port contracts.
The retired migration-time `*PortProxy` classes and dynamic `__getattr__` forwarding must not
be recreated; they had no host, SDK or plugin consumers. Workflow execution uses its owning
`app.application.workflow` service directly and must not be registered again in `ChainDataPorts`.
Agent orchestration, memory and tool implementations follow the same rule via
the named `get_agent_*_port()` functions from `app/application/agentdata.py`.
The legacy Agent `*Port` proxy classes remain import-compatible boundaries and
@@ -225,9 +226,19 @@ ModuleManager 与 startup 组合根继续关闭其余资源但必须向上返回
`app.runtime.execution.OwnedThreadPoolExecutor` 是进程级同步执行器有界收敛的唯一事实源;新的专用
线程池不得复制 Future 追踪、worker join 或重试关闭实现。DoH 查询线程池也必须复用该 owner:恢复系统
DNS 后有限等待,超时保留原 executor 并向 startup 返回 `False`,真实收敛前不得创建替代线程池或回填缓存。
工作流节点线程池同样复用该 executor;所有 `WorkflowExecutor` 必须在 concrete `WorkFlowManager` 登记,
工作流节点线程池同样复用该 executor;所有 `WorkflowExecutor` 必须在 concrete `WorkflowManager` 登记,
manager 停机先封口新执行并向活动 owner 发送本地取消,再有限等待执行线程和节点 worker。未收敛时必须
保留动作注册表和执行 owner,并让工作流生命周期 fail-fast,禁止继续释放仍被动作使用的插件或模块依赖。
工作流读取统一使用 `app.application.workflow.WorkflowQueryService` 和冻结的 `WorkflowSnapshot`
`app.db.adapters.workflow.TransactionalWorkflowQueryRepository` 必须在自有短 Session 内完成 ORM 投影与
嵌套 JSON 深拷贝。API、Agent、Chain、Scheduler、`WorkflowManager` 和中心服务分享不得读取 raw
`WorkflowOper` 或把 ORM 带出 Session。旧 `WorkFlowManager` 拼写只由 Compat 符号覆盖承接,不进入
canonical 模块定义或 `__all__`
工作流执行状态写入统一依赖 `app.application.workflow.WorkflowExecutionPort`Chain 在一次执行中只获取
一个事务端口,并由 `TransactionalWorkflowExecutionService` 为每次状态写入持有短 Session/UoW。
canonical `app.db.oper.workflow.WorkflowOper` 只提供显式 Session 的 query/stage 方法;旧无 Session
`start/success/fail/step/reset` 仅由 `app.sdk._legacy.workflow` 和精确 Compat 映射承接,且不进入
`app.db.oper.__all__`
协程环境文件日志属于有界 E1 观测能力,只允许单一队列 writer;队列满时不得再以无界 executor
形成第二条异步写入路径。日志关闭必须有限等待 writer 与文件处理器,未收敛时 `LoggerManager`
保留原 owner 并让 lifespan 以关闭失败结束,不得先清空引用或用无界 `join()` 掩盖失败。
@@ -667,7 +678,8 @@ driven workflow registration.
| `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` |
| `app/application/workflow.py` | Workflow use cases, frozen query snapshot and typed runtime ports consumed by API, Agent, Chain and Scheduler; `WorkflowManager` is registered by `app/startup/initializers/workflow.py` |
| `app/db/adapters/workflow.py` | Short-session Workflow query projection and execution-state transaction adapters |
| `app/db/adapters/` | SQLAlchemy repository/UoW implementations for Application-owned persistence Protocols |
| `app/startup/composition/` | HostRuntime, configuration snapshots and cross-layer adapter wiring |
| `app/startup/initializers/` | Domain-scoped initialization and shutdown hooks |