docs(architecture): remove closure ledger

This commit is contained in:
jxxghp
2026-08-26 22:58:41 +08:00
parent ec72336f9b
commit 9e1f241ef3
2 changed files with 0 additions and 299 deletions
-4
View File
@@ -704,9 +704,6 @@ flowchart LR
物理文件仍是稳定入口的,不应为了目录规整新增“自己映射自己”的别名,也不应在 canonical 包中
保留多余导出。
当前架构现状差距、优化点与建议实施顺序见
[`docs/refactor/backend-architecture-review.md`](refactor/backend-architecture-review.md)。
---
## 附录:相关文档索引
@@ -722,5 +719,4 @@ flowchart LR
| [`docs/subscribe-lifecycle.md`](subscribe-lifecycle.md) | 订阅生命周期详解 |
| [`docs/mcp-api.md`](mcp-api.md) | MCP 工具端点说明 |
| [`docs/v3t-runtime-governance.md`](v3t-runtime-governance.md) | V3/V3t 运行依赖、故障恢复、GIL 可观测性与兼容退场门禁 |
| [`docs/refactor/backend-architecture-review.md`](refactor/backend-architecture-review.md) | 2026-08 架构评审:现状差距、优化点与实施顺序 |
| [`docs/adr/0007-background-action-reliability.md`](adr/0007-background-action-reliability.md) | 后台动作 E0–E3 可靠性分级与完成语义决策 |
@@ -1,295 +0,0 @@
# MoviePilot V3 后端架构收敛台账
> 更新时间:2026-08-26
>
> 本文是本轮工作的实时计划、进度和证据事实源。历史评审文档及本文在旧提交中的内容
> 只用于追溯,不再作为待办清单。架构规范仍以 `docs/rules/05-architecture.md` 为准,
> 后台动作可靠性分级以 `docs/adr/0007-background-action-reliability.md` 为准。
## 一、范围和完成定义
本轮只修改 `MoviePilot` 主仓。`MoviePilot-Frontend``MoviePilot-Plugins`
`MoviePilot-Rust`、资源、构建、服务、OCR、Wiki 和官网仓仅用于核对外部契约,
不在本轮产生代码、提交或推送。
本轮不是新一轮开放式架构改造。一个事项只有同时满足以下条件才可准入:
1. 本轮开始前已有代码、提交、任务记录或“已实施但未验收/未交付”的明确证据;
2. 当前 `v3` 仍能复现未闭环状态;
3. 可以在两天内完成实现、回归、独立提交和推送;
4. 不破坏插件 ABI、跨仓契约、显式 Session/UoW 和现有并发工作;
5. 有确定的验收命令和停止条件。
仅有历史建议、尚未启动的愿望项、需要产品决策或无法在两天内完整交付的主题,
一律记录为“排除”,不创建执行目标。
每个批次保持单一主题。批次通过验证后使用显式路径暂存,提交并推送到 `v3`
随后在本文记录提交 SHA、远端 SHA、ahead/behind 和验证结果。共享工作区中,
审计可以并行,但同时只允许一个叶子目标编辑主仓。
## 二、当前架构事实
### 2.1 宿主边界
* FastAPI 入口和启动生命周期位于 `app/startup/`,组合根负责装配运行时服务;
API、Command 和 Application 层负责用例边界。
* Domain 保持业务规则,Application 负责用例编排,Adapter/Module 负责外部系统,
Chain 是保留兼容面的宿主编排层。当前依赖门禁持续检查这些方向。
* 宿主 Model/Base/Oper 不隐式创建 SessionApplication Command/UoW 拥有
commit/rollback 和提交后副作用。transaction debt 基线当前为零。
* 宿主配置读取已经迁入类型化读取端口、配置服务或快照;旧 Settings 门面只为插件
兼容保留。配置债务门禁当前通过。
### 2.2 运行时所有权
* 进程级资源由生命周期组件和显式 owner 负责启动、停止与超时收敛;
TaskRegistry、工作流执行 owner、线程池 owner 和持久事件 Outbox 已有独立门禁。
* E0-E3 后台动作按 ADR 分级。业务事务提交成功后,非关键通知失败不能反向回滚业务;
需要可靠交付的意图进入持久队列并由 dispatcher 重试。
* 全局兼容对象仍可能存在,但 canonical 宿主调用必须经过组合根、运行时端口或 getter,
不得恢复 service locator 和隐式事务所有权。
### 2.3 插件和外部契约
本轮只读核对确认以下契约必须保持:
* `_PluginBase` 生命周期、`close -> stop_service` 收敛语义和 `get_*` 投影 hook
* 旧导入路径及对象 identity、插件无 Session 的 Oper 调用兼容;
* `media_source + media_id` 成对身份、虚拟实例的 source plugin 身份;
* 动态插件 API 原始响应、插件远程组件和市场/安装边界;
* Rust 导入名、V3 资源落点、Server/OCR HTTP 边界。
官方插件基线和 483 个插件侧测试已经只读通过;其他仓没有产生修改。
## 三、历史任务对账
| 既有任务 | 当前证据 | 结论 |
|---|---|---|
| `core/helper/utils` 迁移与精确兼容导入 | 物理旧根已清零;Compat/SDK、官方插件基线和兼容测试存在 | 关闭:后续提交已完成 |
| 显式 Session/UoW 与宿主事务所有权 | transaction baseline 中隐式事务和宿主自建 Session 均为零 | 关闭:已完成 |
| 长期整改阶段 0-67 | 阶段 67 提交 `ffe6da213` 是当前 `v3` 祖先 | 阶段已交付,只复核明确余项 |
| 宿主 Settings/全局配置迁移 | `9dbe424c3` 清除 canonical 宿主兼容代理使用,门禁通过 | 关闭:已完成 |
| durable 事件可靠性第一批 | `362f60675``bd0795082` 已交付事件保留、清理和门禁 | 事件切片关闭;后台 owner 只审计原阶段余项 |
| 跨线程任务终态和模块关闭所有权 | `6c334f7b9``be7dfd77a` 已在当前分支 | 关闭:已完成 |
| Ruff/Mypy/Coverage 增量门禁第一批 | 脚本和 CI 已接入,但低水位没有真实固化 | 准入:见批次 1、2 |
| 大型 Chain 拆分、全量 sync/async 合并、扩大类型范围 | 历史内容只有建议,没有未交付实现 | 排除:未启动,不新开大任务 |
| 资源原子更新、Server helper 再拆分、Wiki/Web 陈旧内容 | 本轮只读审计新发现或属于其他仓 | 排除:超出既有主仓任务范围 |
审计开始时存在来自并行 Issue #6468 的 6 个未提交文件,该任务已独立提交推送为
`88703d645`。随后 `0a5b6a637`Issue #6464)和 `d24c52ea9`Issue #6472
由其他任务合入 `origin/v3`;本轮只快进同步,没有把这些改动纳入自己的提交。
## 四、批次计划和实时进度
| 批次 | 叶子目标 | 状态 | 当前证据/停止条件 |
|---|---|---|---|
| 0 | 历史任务清账、现行架构图、外部契约核对和宿主基线对齐 | 已推送 | `d234c7132`;远端同 SHAahead/behind `0/0`;架构契约 `71 passed` |
| 1 | Mypy fail-closed,并把 Ruff/Mypy 已下降债务固化为真实低水位 | 已推送 | `6062b0661`;远端同 SHAahead/behind `0/0`Mypy 11994、Ruff 976 |
| 2 | 用全量串行测试初始化非零 Coverage 低水位,并补齐 CI/文档防回退契约 | 已推送 | `265d3c6d1`;远端同 SHAahead/behind `0/0`Application 77.76%Domain 79.24% |
| 3 | 收口阶段 62 遗留的 QQ Gateway heartbeat Timer 所有权 | 已推送 | `50b85235d`;远端同 SHAahead/behind `0/0`;全量 `6391 passed, 6 skipped` |
| 4 | 收口并行 Rousi 提交暴露的质量门禁回退 | 已推送 | `9e81421eb`;远端同 SHAahead/behind `0/0`;全量 `6404 passed, 6 skipped` |
| Final | 全仓回归、插件兼容复核、台账定稿和远端一致性验证 | 已完成 | 所有准入项已推送;全量与适用门禁通过;代码交付 SHA 远端 `0/0` |
### 批次 0:审计与基线对齐
已完成:
* 核对主仓、历史治理 worktree、阶段 67 祖先关系和后续治理提交;
* 并行核对插件/前端、Rust/资源/外围服务契约,所有参考仓保持只读;
* 运行 transaction、configuration、complexity、async blocking、task ownership、
service locator 和 startup performance 快速门禁,已检查项通过;
* 在同步最新远端后定位 dependency baseline 的唯一漂移:
`app.api.endpoints.plugin` 新增到 `app.application.plugin.identity`
`app.application.plugin.inventory` 的两条依赖。这是 API 调用 Application 的合法方向,
来自已合入的 #6472,已机械更新基线;
* #6472 新增公开枚举 `PluginSourceBindingStatus` 后没有刷新 Schema 惰性导出清单,
已用生成脚本补入并恢复确定性排序;
* `baseline.py --check-host``scripts/schema/exports.py --check` 和相关架构契约测试通过,
测试结果为 `71 passed`
交付证据:提交 `d234c7132` 已推送到 `origin/v3``git ls-remote` 返回同一 SHA
`HEAD...origin/v3``0/0`,且本地 HEAD 是远端祖先。
### 批次 1Ruff/Mypy 低水位闭环
已确认的既有缺口:
* `mypy_ratchet.py` 只解析 stdout,忽略 return code 和 stderr;当前 Mypy 内部错误退出 2
仍被报告为“通过”,现有 Mypy fixture 因扫描中断而无效;
* 当前 Ruff 为 978 项,fixture 仍保留 1623 项,允许已消除的 645 项重新引入;
* CI 已调用两条棘轮,但缺少工具异常 fail-closed 和低水位新鲜度契约。
停止条件:等价改写触发 Mypy 崩溃的表达式;工具退出 2 或输出不可解析时门禁失败;
完整扫描正常退出并生成真实 Mypy fixtureRuff/Mypy fixture 与当前结果完全相等;
针对性测试、两条门禁和适用静态检查通过;批次独立提交推送。
本地验收结果:
* 消息 executor 提交改为零参数 `partial`,保持空底层上下文提交和 worker 请求快照,
Mypy 1.18.2 不再 INTERNAL ERROR;同步 fake loop 已验证提交阶段无关联 ID、渠道回调可见 ID;
* Mypy runner 固定使用 CI 的 Linux/Python 3.14 分析目标,只接受退出码 0/1,拒绝 stderr、
缺失/重复摘要和摘要计数不一致;完整扫描覆盖 601 个文件、11994 项。旧 fixture 的
342 个文件、6209 项已确认是截断快照;macOS 与 Linux 目标生成结果逐字一致;
* Ruff/Mypy 将增长与低水位滞后分开;已有基线存在增长时 `--write` 被拒绝,
只有纯下降或缺失的新基线可以写入;
* Ruff fixture 从 1623 收紧到 976Mypy 和 Ruff 默认路径复跑均通过;
* 质量/CI/上下文/严格类型专项 `33 passed`,架构契约 `71 passed`,改动文件 Pylint `10.00/10`
交付证据:提交 `6062b0661` 已推送到 `origin/v3``git ls-remote` 返回同一 SHA
`HEAD...origin/v3``0/0`。提交严格包含上述 10 个批次路径;并行任务在提交前推进的
`1f7fac2b2` 是其父提交,不在本批次 diff 中。
### 批次 2Coverage 低水位闭环
已确认的既有缺口:Coverage 脚本和 CI 已落地,但 fixture 的 Application/Domain 都是
`0 statements / 0 covered / 0.00%`,所以门禁没有保护作用;开发文档仍误称该任务只手工运行。
停止条件:在批次 1 的最终代码快照上运行串行全量 Coverage;fixture 为真实非零结果;
零 statements、工具失败和低水位未固化均会失败;CI 命令存在性有契约测试;文档与 CI 一致;
批次独立提交推送。
本地验收结果:
* 脚本拒绝 malformed/零语句报告、非法或不完整基线、布尔/负数/越界计数和计数不一致的
百分比;唯一旧全零 fixture 只允许初始化一次,后续不能再借 `--write` 绕过回退;
* 回退使用整数交叉相乘比较真实比例,避免四舍五入隐藏下降;任一包下降会整体拒绝写入且
基线原始字节不变,等比例计数变化和覆盖提升都必须显式固化完整快照;
* GitHub Actions run `32977180133``15ddbbbaf` 上完成 Ubuntu/Python 3.14、locked
依赖和串行全量 Coverage,日志精确聚合为 Application `9292/11949`77.76%)、Domain
`3390/4278`(79.24%);本机报告得到相同计数后,旧全零 fixture 已初始化并复验通过;
* CI 在同一 job 内按 erase、serial run、report、JSON、XML 顺序生成报告,先上传 canonical
工件再执行只读 ratchetPR、push、手工触发、Python 版本、锁依赖和禁止绕过均有契约测试;
* 当前应用代码快照的本地串行全量结果为 `6379 passed, 6 skipped`;随后新增的边界测试由
最新专项覆盖,质量/CI 专项 `26 passed`Ruff 通过,Pylint `10.00/10`,宿主、Schema、
Coverage 和锁文件门禁均通过。
交付证据:提交 `265d3c6d1` 已推送到 `origin/v3``git ls-remote` 返回同一 SHA
`HEAD...origin/v3``0/0`,提交严格包含批次 2 的 8 个主仓路径。
### 批次 3QQ Gateway heartbeat owner
阶段 62 的既有合同要求消息渠道只有在线程真实终止后才能返回成功,超时时 owner 和句柄
继续保留供重试。当前 QQ Gateway 的 Hello 回调会递归创建 `threading.Timer` 心跳;
WebSocket 和 Gateway 退出路径只调用 `cancel()`,而 `QQBot.stop()` 只等待 Gateway 主线程。
当 heartbeat callback 已进入 `send()` 时,`cancel()` 不能终止它,外层因此可能误报收敛。
停止条件:Gateway 最终退出路径等待当前 heartbeat owner 真正终止,不为 heartbeat 叠加
第二份调用方等待预算;heartbeat 未终止时 Gateway 线程保持存活,`QQBot` 在现有 20 秒
Gateway join 预算后保留 owner 并允许再次停止;故障注入证明首次
停止返回 `False`、释放 callback 后第二次返回 `True`;生命周期专项和 task ownership 门禁通过;
批次独立提交推送。
本地实现与专项验收:
* 递归 Timer 已替换为一个由 Gateway 聚合、按 connection generation 捕获精确 WebSocket 的
常驻心跳线程;新 Hello 必须先失效并等待旧 generation,旧连接 close 不能终止新连接心跳;
* 状态锁只保护 epoch、owner 和 sequence 快照,任何 `join()`、网络发送及 close 回调都不持锁;
Gateway 的每次连接和最终退出都等待子 owner 终止,所以未收敛时外层 Gateway 线程保持存活;
* 回调中的 Identify、Invalid Session 均使用回调携带的精确 WebSocket,避免 `ws_holder`
被并发 close 清空或替换后误发到别的连接;公开 `QQBot.stop()` 合同和等待预算未改变;
* 三条阻塞发送、重复 Hello 和双连接重连故障注入测试并行重复 4 轮均通过;完整生命周期专项
`96 passed`Ruff 通过,Pylint `10.00/10`task ownership、复杂度、async blocking、
宿主基线及 Ruff/Mypy ratchet 均通过;Mypy 低水位从 11994 净减至 11993。
* 双连接用例直接证明重连前必须等待旧心跳终止,且迟到的旧连接 close 不会终止新 generation
独立代码审查未发现生产线程生命周期、锁序、generation 或 reconnect 合同缺陷;
* 4 分片全量回归 `6391 passed, 6 skipped`;锁文件、Schema、Coverage、service locator 和
官方插件 ABI 语义门禁均通过,架构/质量专项 `50 passed`。插件参考仓只有不参与语义判定的
schema/provenance 漂移,且本地分支落后其远端 2 个提交,本轮保持参考仓只读且不刷新指纹。
交付证据:提交在并行 Rousi 和 Ruff baseline 提交之后重放为 `50b85235d`,已推送到
`origin/v3``git ls-remote` 返回同一 SHA`HEAD...origin/v3``0/0`
### 批次 4:并行提交质量门禁集成
批次 3 最终回归期间,远端合入 `5484223d1`Rousi PeerGo 个人 API Key)和
`b1c415089`(对应 Spider Ruff 低水位)。前者行为专项通过,但在 fail-closed 门禁下暴露两条
新增 Mypy 错误;Parser 触达文件和基类还各有一条已消除但未固化的 Ruff 导入债务。
准入边界:只修复当前主线提交造成的类型回退及同文件低水位,不扩张站点解析器重构。基类
`err_msg` 显式标注为真实的 `Optional[str]`Rousi 在调用时间解析器前验证 `registered_at`
为字符串;由此净消除 10 条既有 assignment 债务。触达文件导入排序完成后,Mypy 低水位
从 11993 收紧为 11983Ruff 从远端固化后的 975 继续收紧为 973。
停止条件:Rousi 行为、解析器相关回归、Mypy/Ruff 只读 ratchet、适用架构门禁和最终全量
测试通过;批次独立提交推送;台账定稿后远端保持 `0/0`
本地验收结果:
* Rousi `14 passed`;与 SunnyPT、TorrentLeech 和 Yema 合计 `33 passed`,触达文件 Ruff 通过,
Pylint `10.00/10`
* Mypy `11983`、Ruff `973` 的只读 ratchet 通过,Coverage 仍为 Application 77.76%、
Domain 79.24%;锁文件、Schema、宿主架构、复杂度、async blocking、service locator、
task ownership 和官方插件 ABI 语义门禁均通过;架构/质量专项 `50 passed`
* 在不含共享工作树并行改动的干净 detached worktree 中,包含并行 Rousi 提交和本批源码/
测试的 4 分片全量回归为 `6402 passed, 6 skipped`;随后远端 `ee53d6df0` 交付 #6449
durable event 修复,其 blob 与共享工作树副本逐字一致。本批重放到该提交后,durable 专项
`5 passed`,最终合并 SHA 的 4 分片全量为 `6404 passed, 6 skipped`
交付证据:提交 `9e81421eb` 已推送到 `origin/v3``git ls-remote` 返回同一 SHA
本地提交是远端祖先,`HEAD...origin/v3``0/0`,提交严格包含本批 6 个主仓路径。
## 五、验证矩阵
每个批次按改动范围选择下列命令,Final 全部执行:
```bash
uv lock --check
uv run --locked --no-sync python scripts/architecture/baseline.py --check-host
uv run --locked --no-sync python scripts/architecture/ruff_ratchet.py
uv run --locked --no-sync python scripts/architecture/mypy_ratchet.py
uv run --locked --no-sync pytest -q tests/test_architecture_contract_baseline.py
uv run --locked --no-sync pytest -q tests/test_quality_ratchets.py tests/test_mypy_gate.py
uv run --locked --no-sync python scripts/architecture/baseline.py \
--check-plugins --plugin-repo ../MoviePilot-Plugins
uv run --locked --no-sync python -m coverage run tests/run.py --serial
```
提交推送后还必须验证:
```bash
git ls-remote origin refs/heads/v3
git merge-base --is-ancestor HEAD origin/v3
git rev-list --left-right --count HEAD...origin/v3
```
## 六、进度日志
| 时间 | 事件 | 结果 |
|---|---|---|
| 2026-08-26 | 建立父目标和并行只读审计 | 只准入既有、可复现、两天内可交付事项 |
| 2026-08-26 | 对账阶段 0-67 和后续远端提交 | 多数旧台账余项已由后续提交关闭 |
| 2026-08-26 | 插件/外部仓契约核对 | 参考仓只读;插件基线和 483 个测试通过 |
| 2026-08-26 | 质量门禁核验 | 确认 Mypy fail-open、Ruff 低水位滞后和 Coverage 零基线 |
| 2026-08-26 | 同步并行 Issue 提交 | 主仓快进到 `d24c52ea9`,本轮文档改动完整保留 |
| 2026-08-26 | 复验宿主基线 | 只剩 #6472 引入的两条合法依赖尚未写入 fixture |
| 2026-08-26 | 收口 #6472 生成契约 | dependency fixture 与 Schema 导出清单已更新;架构契约 `71 passed` |
| 2026-08-26 | 后台 owner 历史余项审计 | 仅 QQ heartbeat Timer 满足阶段 62 既有合同和两天准入条件,其余候选关闭或排除 |
| 2026-08-26 | 批次 0 交付 | `d234c7132` 已推送;远端同 SHAahead/behind `0/0` |
| 2026-08-26 | 批次 1 本地验收 | Mypy 11994、Ruff 976;专项 `33 passed`;架构契约 `71 passed`Pylint `10.00/10` |
| 2026-08-26 | 批次 1 交付 | `6062b0661` 已推送;远端同 SHAahead/behind `0/0`;显式变更 10 个路径 |
| 2026-08-26 | 批次 2 canonical Coverage | run `32977180133` 与本机计数一致;Application 77.76%Domain 79.24%;零 fixture 已初始化 |
| 2026-08-26 | 批次 2 本地验收 | 串行全量 `6379 passed, 6 skipped`;最新专项 `26 passed`Coverage/宿主/Schema/锁文件门禁通过 |
| 2026-08-26 | 批次 2 交付 | `265d3c6d1` 已推送;远端同 SHAahead/behind `0/0`;显式变更 8 个路径 |
| 2026-08-26 | 批次 3 启动 | 仅收口阶段 62 已记录的 QQ heartbeat owner,不扩张公开 `QQBot.stop()` 合同 |
| 2026-08-26 | 批次 3 专项验收 | 三条故障注入并行重复 4 轮稳定;生命周期 `96 passed`;静态、所有权和架构门禁通过 |
| 2026-08-26 | 批次 3 全量验收 | 最终快照 4 分片 `6391 passed, 6 skipped`;全部适用主仓门禁通过;插件 ABI 语义无变化 |
| 2026-08-26 | 批次 3 交付 | `50b85235d` 已推送;远端同 SHAahead/behind `0/0` |
| 2026-08-26 | 批次 4 启动 | 只收口并行 Rousi 提交的类型回退和触达文件低水位;不扩大解析器重构 |
| 2026-08-26 | 批次 4 本地验收 | 解析器 `33 passed`;干净 worktree 全量 `6402 passed, 6 skipped`;全部适用门禁通过 |
| 2026-08-26 | 同步并行 #6449 | `ee53d6df0` 已在远端;共享工作树 blob 完全相同,rebase 后无重复 diff |
| 2026-08-26 | 最终合并验收 | durable `5 passed`;最终 SHA 全量 `6404 passed, 6 skipped`;质量门禁通过 |
| 2026-08-26 | 批次 4 交付 | `9e81421eb` 已推送;远端同 SHAahead/behind `0/0`;显式变更 6 个路径 |
| 2026-08-26 | Final 收口 | 全部准入项已交付;排除项均有证据;其他仓只读;代码工作区干净 |
## 七、本轮停止条件
本轮在以下条件全部满足后结束:
* 历史审计确认的所有准入项均已实现、验证、独立提交并推送;
* 其余旧事项都有“已由后续提交完成”“从未开工”“外部决策”或“非主仓”的证据;
* 宿主架构、质量、全量测试和官方插件兼容门禁通过;
* 本地提交是远端 `v3` 的祖先,ahead/behind 为 `0/0`
* 工作区不存在本轮遗留,其他任务和其他仓的改动未被暂存、改写或提交。
截至本台账定稿,以上条件全部满足;未把从未启动的大型改造、外部仓变化或 provenance 指纹
漂移误报为本轮未完成任务。