Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 26 additions & 62 deletions .codemap/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -631,86 +631,51 @@
],
"loc": 2409,
"contentHash": "fb31a5d9516731fe561cbfdeaae24f92b052ca23194294b4972664716f93d32e",
"score": 70,
"score": 69,
"grade": "C",
"tags": [
"fallback",
"silent-except",
"legacy",
"duplication",
"bloat",
"god-component",
"monkeypatch"
"god-component"
],
"findings": [
{
"sev": "MED",
"loc": "app/services/subtitle_data_service.py:352",
"text": "手工 revision 的 active/base 检查在事务外,并发编辑可覆盖 active 选择。"
"loc": "app/services/subtitle_auto_workflow_service.py:42",
"text": "prepare_task_subtitle_review 先提交 ensure_source_track/ensure_clip_track 的数据库副作用,之后才在 BEGIN IMMEDIATE 中检查活动 Job、lease 和 Task 状态。并发状态变化或冲突 Job 会使入口失败,但已生成的字幕 revision/track 变更无法回滚。"
},
{
"sev": "MED",
"loc": "app/services/subtitle_data_service.py:489",
"text": "批准 revision 在事务外校验,并发或重放旧请求可回退 active 版本。"
"loc": "app/services/subtitle_data_service.py:1222",
"text": "_sync_dependent_clip_tracks 逐条调用 sync_clip_track、逐条提交。源轨 revision 已提交后,任一切片同步失败会留下部分 up_to_date、部分 pending_sync 的状态。"
},
{
"sev": "MED",
"loc": "app/services/subtitle_data_service.py:135",
"text": "source/clip track 先查后插,NULL output_clip_id 约束不足,并发可能重复源轨。"
"loc": "app/services/subtitle_data_service.py:1017",
"text": "_load_source_cues 对 checksum 不匹配、JSON 损坏或非法 segment 直接跳过;只要仍有有效 cue 就返回部分 ASR 字幕,可能生成静默缺句的 revision。"
},
{
"sev": "MED",
"loc": "app/services/subtitle_auto_workflow_service.py:87",
"text": "批量批准逐 clip 独立提交,Job 后创建;中途失败会留下部分批准。"
},
{
"sev": "MED",
"loc": "app/services/subtitle_data_service.py:168",
"text": "source revision 提交后逐 clip 独立同步,异常会形成混合版本。"
},
{
"sev": "MED",
"loc": "app/services/subtitle_workflow_service.py:475",
"text": "字幕 job 完成与激活分两次写入,迟到 worker 仍可能激活旧成片。"
},
{
"sev": "LOW",
"loc": "app/services/subtitle_auto_workflow_service.py:273",
"text": "auto_config_json 损坏时静默降为空并写回,可能丢弃其他配置。"
},
{
"sev": "MED",
"loc": "app/services/subtitle_data_service.py:1045",
"text": "字幕数据服务聚合 track、revision、cue、导入导出、波形和渲染辅助,回归半径大。"
},
{
"sev": "LOW",
"loc": "app/services/subtitle_workflow_service.py:312",
"text": "统一 revision 外仍保留旧调用方适配,存在历史口径漂移成本。"
"loc": "app/services/subtitle_data_service.py:1341",
"text": "_probe_media_dimensions 在 ffprobe 不存在、超时、输出损坏或尺寸缺失时静默回退 1080x1920,真实媒体故障不可见。"
},
{
"sev": "MED",
"loc": "tests/test_subtitle_editor.py:189",
"text": "缺并发 save/approve/ensure track 与批处理中途失败测试。"
},
{
"sev": "LOW",
"loc": "tests/test_subtitle_auto_workflow.py:172",
"text": "核心真实渲染在 FFmpeg/FFprobe 缺失时会跳过,异常 Provider 证据不足。"
"loc": "app/services/subtitle_data_service.py:1322",
"text": "字幕数据层仍集中承担 revision/cue 持久化、导入导出、ASS、波形与 ffprobe,约 1434 行;默认样式加载存在重复实现,修改影响面偏大。"
},
{
"sev": "LOW",
"loc": "app/services/subtitle_data_service.py:761",
"text": "波形处理把完整 PCM 捕获到内存,超长媒体存在时长线性内存峰值。"
},
{
"sev": "LOW",
"loc": "app/services/subtitle_data_service.py:1024",
"text": "单条字幕文本可接近文件上限,放大渲染、导出和 Prompt 资源消耗。"
"loc": "app/services/subtitle_workflow_service.py:237",
"text": "旧版 _activate_subtitle_job 仍可无 lease/revision/status 校验激活记录;正常生产渲染路径已不再调用,但遗留兼容入口仍有风险。"
}
],
"auditedHash": "fb31a5d9516731fe561cbfdeaae24f92b052ca23194294b4972664716f93d32e",
"auditedAt": "2026-08-24",
"auditedRev": "9ae8440d80d0527c7dd296fe1331b63aac2818ea"
"auditedAt": "2026-08-25",
"auditedRev": "2a2774dd248cc80377dd7edf8e1f98e8ec2f4a20"
},
{
"id": "pipeline_queue",
Expand Down Expand Up @@ -742,30 +707,29 @@
],
"loc": 3350,
"contentHash": "79bcc0b7de14b28318a7578e71d5281909254180ff351895b72fb10cefb87c68",
"score": 88,
"score": 87,
"grade": "B",
"tags": [
"god-component",
"bloat",
"legacy",
"glue",
"over-fit"
"legacy"
],
"findings": [
{
"sev": "LOW",
"loc": "app/services/pipeline_engine.py:977-1073",
"text": "PUBLISH_JOB_CREATING 尚无专用 reconcile 分支,恢复主要依赖重复创建的幂等去重。"
"loc": "app/services/pipeline_engine.py:1135",
"text": "PUBLISH_JOB_CREATING 重启恢复已具备 source、workflow_job_id、active output 和文件证据校验,但 provisional 仍把全部 recovered_ids 归入 created,skipped 恒为空;混合批次恢复后的计数和取消语义可能与原批次不同。"
},
{
"sev": "LOW",
"loc": "tests/test_pipeline_checkpoint.py:358-625; tests/test_pipeline_state_stability.py:275-313",
"text": "仍缺真实进程重启、lease 过期边界及发布内容证据变更的进程级回归测试。"
"sev": "MED",
"loc": "app/services/pipeline_engine.py:87",
"text": "PipelineEngine 约1810行、46个方法,继续集中9步执行、checkpoint/reconcile、发布证据、取消清理和失败收口;lease、旧checkpoint、切片与发布协议高度耦合,维护及回归面仍偏大。"
}
],
"auditedHash": "79bcc0b7de14b28318a7578e71d5281909254180ff351895b72fb10cefb87c68",
"auditedAt": "2026-08-24",
"auditedRev": ""
"auditedAt": "2026-08-25",
"auditedRev": "2a2774dd248cc80377dd7edf8e1f98e8ec2f4a20"
},
{
"id": "publish_center",
Expand Down
12 changes: 12 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1225,3 +1225,15 @@
- Windows 子进程树终止现在校验 `taskkill` 退出码并等待进程退出;超时、启动失败、非零退出和无法确认退出都会显式报错,不再让上层误以为已经安全停止。
- 移除路径测试中的全局模块 reload 泄漏,并让边界测试始终引用当前运行时 `storage_service`,消除全量测试顺序依赖。
- 独立验收:边界专项 `52 passed`,全量 `533 passed`;Ruff、Python Compileall 和 `git diff --check` 全部通过。未访问真实 NAS 内容,未触发 AI 或真实投稿。

## 2026-08-25 稳定 V1 P1.3d 字幕批次原子性与恢复

- 字幕“审核并批量烧录”改为一个 `BEGIN IMMEDIATE` 事务:所有当前 active revision 在事务内重新校验,批准、创建/复用 Workflow Job 和 `subtitle_delivery_mode` 同时提交;任一 revision、配置或 Job 插入失败时整批回滚。
- 单条批准也在写锁内重新核对 active revision,过期页面请求不能把字幕轨回退到旧版本;损坏的 `auto_config_json` 改为明确失败,不再静默覆盖为 `{}`。
- 字幕成片的临时文件切换、completed/verified 写回和 active 版本切换合并为一次短事务,并绑定当前 Workflow Job 的 owner、lease token、未过期时间和取消状态;旧 Worker 或旧 revision 不能激活迟到结果。
- 新 Worker 接管时会收口同一 Workflow Job 遗留的 processing 子任务,只清理本执行标记的 `.part.mp4` 和没有数据库引用的中断最终文件;其他 Job、已验证 active 文件、外部文件和历史 revision 均保留。
- 若进程在“字幕 DB 已提交、checkpoint 尚未写入”的窄窗口退出,重启会从同一 Workflow Job 的 active + verified 结果恢复 checkpoint,不重复运行 FFmpeg。
- 字幕完成与后续自动流水线 Job 改为同事务提交;取消、过期 lease 或不兼容的续跑 payload 会整笔回滚。发布草稿批次在提交前再次核验 lease,并可从同一 Workflow Job 的持久化草稿证据恢复 checkpoint。
- 源轨生成、切片轨同步、手工保存和字幕导入均在写锁内重读 active revision 并使用条件更新;迟到的旧读取不能覆盖较新的人工版本。“跳过字幕”也会拒绝已经排队的后续自动流水线。
- Worker 子进程启动失败会明确写入 failed 而不是让队列线程退出;重启接管同时收口 processing/queued 字幕子记录,按 revision 复用的 ASS 缓存不会被误删。
- 最终独立验收 Ruff、Compileall 通过,字幕/Job fencing/队列/自动流水线/checkpoint/状态机/发布关联/版本回滚共 `167 passed`。Pytest 明确使用临时 `test_workflow.sqlite3`;活动库前后 size 与 SHA256 完全一致。未调用 AI、Chrome 或真实平台投稿。
4 changes: 3 additions & 1 deletion NEXT_STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
- [x] P1.3b:自动流水线已增加版本化步骤 checkpoint、输入/产物证据、旧 Job 原位重试和重领恢复;已覆盖 AI、转写、切片、文案、排期与发布草稿的失效边界。
- [x] P1.3c:已增加 `schema_migrations` 版本/checksum 账本、迁移前 Online Backup 和 fail-closed v2 活动发布唯一索引;重复数据、索引漂移或账本异常都会拒绝启动。本轮未直接迁移活动库。
- [x] 任务 20 专项:完成状态恢复、重复 AI 409 保护、上传单入口、最近 5 个任务名、长直播参数显隐和“跳过字幕后进入片段审核”闭环。
- [ ] P1.3d:处理字幕批准批次原子性、字幕/切片中断目录清理和剩余跨进程恢复边界
- [x] P1.3d:字幕批准、Job 创建和交付模式已原子提交;成片激活绑定 lease/revision;接管会清理本执行残留并恢复“DB 已提交但 checkpoint 未写”的结果
- [ ] P1.4:统一第三方 AI/FFmpeg 超时、错误 JSON、429/5xx 与重试幂等边界,并避免重复计费。
- [ ] P1.5:在不扩大个人本地项目范围的前提下处理密钥日志、输入校验和本地管理员接口门禁。
- [ ] P2:拆分 God Service、去除查询重复、补核心集成/故障测试和可观测性;不做全面重构。
Expand All @@ -34,6 +34,8 @@
5. P1B.1/P1B.2、P1.3a 与 P1.3b 代码及隔离测试均已完成;活动库尚未增加 `workflow_jobs.lease_token`,本轮也未重启正式服务。下一独立轮先确认没有活动 Workflow Job,再做在线备份、幂等迁移和正式重启烟测。
6. P1.3b 不增加数据库列。合并并完成正式迁移/重启后,可用一条低风险测试任务在 AI 或切片完成后重启 Worker,确认同一 Job 从 checkpoint 续跑;不要点击真实发布,也不要用重要待发布素材做故障演练。
7. P1.3c 对正式库只做了只读预检:当前无活动发布重复组,旧唯一索引仍在,尚未写入 `schema_migrations`。合并后的首次正式重启会先创建迁移备份再切换 v2 索引;应与尚未部署的 Workflow fencing Schema 一起安排受控迁移和重启验收,不要在服务运行中手工改索引。
8. P1.3d 不增加数据库列,也不需要手工清理 `06_subtitled`。合并并正式重启后,可用一条低风险测试任务执行“审核并批量烧录”;不要通过强杀正式服务制造故障,跨进程接管已由隔离 lease 回归验证。
9. 下一轮进入 P1.4,优先统一 AI/FFmpeg/FFprobe 的超时、429/5xx、坏 JSON 和持久化调用幂等;在该轮完成前,不要把 Provider 自动重试次数调高。

## 2026-08-24 工程体检确认的原始整改顺序

Expand Down
17 changes: 17 additions & 0 deletions PROJECT_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ Codemap 独立复评结果:`Publish Scheduler 62→71(C)`、`Publishers &

Codemap 独立复评将 `SQLite Persistence` 从 **64/C 提升到 73/C**,原 HIGH“先删索引、重建失败静默吞掉”和 MED“没有迁移账本”均已移除。模块仍为 C:历史列探测和多处 `executescript` 继续属于 pre-ledger compatibility,不能保证整个旧库升级原子;备份恢复工具还缺 FK/ledger/关键索引验证与运行中服务闸门。P1.3c 已完成有限范围目标,但项目整体仍维持 **可用 V1**,下一轮按路线处理 P1.3d,而不是借机全面重写数据库层。

## 0.7 P1.3d 字幕批次与跨进程恢复状态(2026-08-25)

| P1.3d 项目 | 结果 | 验证证据 |
| --- | --- | --- |
| 批量批准原子性 | 已封口 | active revision、cue/质量、批准、Workflow Job 创建/复用和字幕交付模式在同一个 `BEGIN IMMEDIATE` 中提交;任一条失败整批回滚。 |
| Revision 并发覆盖 | 已封口 | 手工保存、单条批准、原片轨生成、切片轨同步和字幕导入均在写锁内重读 current active revision 并使用条件更新;旧页面或迟到执行不能把 active 指针回退。 |
| 成片激活与旧 Worker | 已封口 | 临时文件切换、completed/verified 和 active 版本切换绑定当前 workflow owner/token/expiry/cancel 与当前已批准 revision;迟到结果不能覆盖新版本。 |
| 中断目录与子记录 | 已封口 | 接管会收口同一 Workflow Job 的 `processing/queued` 子记录,只清理本次标记的 `.part.mp4` 和无数据库引用的孤儿最终视频;其他 Job、active 文件、历史结果和可复用 ASS 不删除。 |
| 跨进程 checkpoint | 已封口 | “字幕 DB 已提交但 checkpoint 未写”可恢复;字幕完成与后续 auto-pipeline Job 同事务提交;发布草稿已提交但 checkpoint 未写时按 schedule、任务目录和 workflow 证据恢复。 |
| Lease 与进程启动 | 已封口 | 所有通用 Job 写回拒绝过期 lease;发布批次提交前二次验 lease;子进程启动失败明确标记 failed,不再让 Worker 线程无声退出。 |
| 路径和证据 | 已封口(自动流水线) | Workflow 创建发布草稿前,视频/封面必须位于当前任务受控目录、扩展名合法且非空,并持久化 size/fingerprint,坏草稿不会先提交再被去重掩盖。 |
| 隔离回归 | 通过 | Ruff、Compileall 通过;10 组字幕/Job/自动流水线/checkpoint/状态机/发布关联/版本回滚测试 `167 passed`。活动库前后 size 与 SHA256 一致。 |

Codemap 最终独立复评未再发现 P1.3d 新的 HIGH。`Subtitle` 为 **69/C**:本轮的批准、激活、接管和 revision 覆盖风险已封口,剩余中风险主要是源轨向多个切片逐条同步的部分完成、损坏 ASR chunk 静默跳过、FFprobe 画布 fallback 以及 1400 行 God Service;前两项中的输入/探测故障归入 P1.4,结构拆分归入 P2。`Pipeline & Job Queue` 为 **87/B**,核心 lease/follow-up/发布恢复与创建时媒体/文案证据边界已经收口;剩余问题主要是恢复计数语义和 God Component 拆分,均不值得在本轮扩大重构。

本轮没有增加数据库列、没有迁移活动库、没有调用 AI Provider、Chrome 或真实投稿。项目成熟度继续维持 **可用 V1**:P1.3d 已解决“字幕正常时能跑、进程切换时可能重复或覆盖”的问题,但 P1.4 的第三方超时/错误响应/计费幂等和 P1.5 的密钥/输入/XSS/管理员门禁仍是改判“稳定 V1”的必要条件。

## 1. Executive Summary

### 结论
Expand Down
Loading