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
2 changes: 1 addition & 1 deletion .codemap/codemap.html

Large diffs are not rendered by default.

18 changes: 7 additions & 11 deletions .codemap/codemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

> **Interactive view:** [`.codemap/codemap.html`](codemap.html) — per-module scores, findings, LoC, and the dependency graph. This file is the written report.

**Generated:** 2026-08-24 · **Modules:** 13 · **Size:** 50778 tracked LoC across 132 files
**Generated:** 2026-08-24 · **Modules:** 13 · **Size:** 51786 tracked LoC across 132 files

## Health by layer

| Layer | Modules | Avg score |
|---|--:|--:|
| 界面 · API | 2 | 56 |
| 业务编排 | 3 | 67 |
| 业务编排 | 3 | 73 |
| 媒体与 AI 处理 | 4 | 69 |
| 外部执行边界 | 2 | 74 |
| 持久化与运维 | 2 | 66 |
Expand All @@ -35,8 +35,8 @@ _LoC is the representative file/folder per module; folder-level modules overlap
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| Publish Center | 5,593 | 52 D | god-component, bloat, legacy, dual-format, fallback, silent-except, placeholder, duplication |
| Pipeline & Job Queue | 3,350 | 88 B | god-component, bloat, legacy, glue, over-fit |
| Task Review & Cut | 3,036 | 78 B | god-component, glue, fallback, dual-format, legacy, over-fit |
| Pipeline & Job Queue | 2,342 | 72 C | fallback, silent-except, legacy, stub, god-component, glue, over-fit |

### 媒体与 AI 处理

Expand Down Expand Up @@ -72,7 +72,7 @@ _LoC is the representative file/folder per module; folder-level modules overlap
- **Ops & Delivery (68/C)** — scripts/migrate_task_dirs_to_project_names.py:211: 任务目录迁移用非 WAL-aware 主库 copy,先移动目录再统一更新提交,无文件补偿,异常会让路径/DB 不一致。
- **Subtitle (70/C)** — app/services/subtitle_data_service.py:352: 手工 revision 的 active/base 检查在事务外,并发编辑可覆盖 active 选择。
- **Publish Scheduler (71/C)** — app/services/publish_scheduler.py:756-785: recover_interrupted_jobs 每轮加载全部 PUBLISHING 任务,并对每个任务串行查询 Worker,没有批量上限、并发控制或退避;Worker 不可用或卡住任务较多时,单轮耗时按任务数乘以网络超时增长,会延迟后续排期处理。
- **Pipeline & Job Queue (72/C)** — app/services/pipeline_engine.py:108,311-314; app/services/job_service.py:488-504: 自动流水线仅在内存 context 中保存步骤结果,虽有 update_job_checkpoint API,但 PipelineEngine 未持久化调用;重启后可能从 PREPARING_SOURCE 重新执行并重复 AI、封面和排期副作用
- **Publishers & Worker (76/B)** — scripts/publish_host_worker.py:321: _prior_job_execution_requires_review 扫描旧 execution journal 时,若 journal 缺少 identity 或已损坏,直接 continue;跨 execution 无法确认其 job_id 时不会阻断新 execution。旧 Worker 在上传后崩溃并留下无身份/损坏日志时,仍存在重复投稿边界

## All findings

Expand All @@ -93,7 +93,7 @@ _LoC is the representative file/folder per module; folder-level modules overlap
- **SQLite Persistence** · `app/db/database.py:628-671` — _create_indexes 先删除 uq_publish_jobs_active_clip_platform_mode,随后对所有索引创建统一捕获 sqlite3.Error 并静默忽略;唯一部分索引创建失败时,旧索引已删除且无告警,活动发布任务可能失去唯一约束并产生重复记录。
- **Ops & Delivery** · `scripts/migrate_task_dirs_to_project_names.py:211` — 任务目录迁移用非 WAL-aware 主库 copy,先移动目录再统一更新提交,无文件补偿,异常会让路径/DB 不一致。

### MED (63)
### MED (59)

- **Frontend UI** · `app/templates/system_status.html:98` — 配置/API Key 字段进入 DOM,base.html:11 还承载本地管理 Token;需确认全链路始终掩码。
- **Frontend UI** · `app/static/css/styles.css:870` — 使用多个未在 :root 定义的 CSS 自定义属性,相关声明可能失效。
Expand Down Expand Up @@ -133,10 +133,6 @@ _LoC is the representative file/folder per module; folder-level modules overlap
- **Subtitle** · `app/services/subtitle_workflow_service.py:475` — 字幕 job 完成与激活分两次写入,迟到 worker 仍可能激活旧成片。
- **Subtitle** · `app/services/subtitle_data_service.py:1045` — 字幕数据服务聚合 track、revision、cue、导入导出、波形和渲染辅助,回归半径大。
- **Subtitle** · `tests/test_subtitle_editor.py:189` — 缺并发 save/approve/ensure track 与批处理中途失败测试。
- **Pipeline & Job Queue** · `app/services/pipeline_engine.py:108,311-314; app/services/job_service.py:488-504` — 自动流水线仅在内存 context 中保存步骤结果,虽有 update_job_checkpoint API,但 PipelineEngine 未持久化调用;重启后可能从 PREPARING_SOURCE 重新执行并重复 AI、封面和排期副作用。
- **Pipeline & Job Queue** · `app/services/job_worker.py:198-202` — WorkflowJobRunner 将子进程 stdout/stderr 重定向到 DEVNULL,父进程通常只能记录退出码,重启恢复和人工诊断证据不足。
- **Pipeline & Job Queue** · `app/services/job_service.py:127-139` — payload/result/checkpoint JSON 损坏时静默保留原字符串,执行器后续可能产生不明确的数据类型错误。
- **Pipeline & Job Queue** · `app/services/job_service.py:19-23; app/services/job_worker.py:60-69` — ai_analysis 与 publish job 类型仍可创建,但 Worker 没有对应执行分支,最终统一失败,属于可创建但不可执行的 legacy/stub 入口。
- **Publish Center** · `app/services/publish_service.py:266` — publish_service.py 当前约4750行,混合配置/账号/OAuth、文案与封面、队列同步、历史、旧 OpenCLI 脚本、API Provider 和页面上下文,形成高耦合 God Component,任一发布流程改动的 blast radius 很大。
- **Publish Center** · `app/services/publish_service.py:2941` — 历史查询 SQL 只按 PUBLISH_HISTORY_STATUSES 的大写值过滤,之后才调用 _normalize_publish_status;LEGACY_STATUS_MAP 支持旧小写状态但旧记录会在 SQL 层被提前排除,历史页可能漏数据。
- **Publish Center** · `app/services/publish_service.py:2144` — sync_task_publish_jobs 封面生成失败时只把 {'cover_error': ...} 放入 item_covers,仍继续插入 WAITING 任务且不计入 errors;同步可能返回 ok,但生成的任务实际无法通过 local_browser readiness。
Expand Down Expand Up @@ -175,8 +171,8 @@ _LoC is the representative file/folder per module; folder-level modules overlap
- **Subtitle** · `tests/test_subtitle_auto_workflow.py:172` — 核心真实渲染在 FFmpeg/FFprobe 缺失时会跳过,异常 Provider 证据不足。
- **Subtitle** · `app/services/subtitle_data_service.py:761` — 波形处理把完整 PCM 捕获到内存,超长媒体存在时长线性内存峰值。
- **Subtitle** · `app/services/subtitle_data_service.py:1024` — 单条字幕文本可接近文件上限,放大渲染、导出和 Prompt 资源消耗。
- **Pipeline & Job Queue** · `app/services/pipeline_engine.py:693-720` — 产物 JSON 或排期时间配置损坏时静默回退默认值,故障可见性不足
- **Pipeline & Job Queue** · `app/services/job_worker.py:310-353` — 父 Worker 在 lease 已过期但尚未被其他执行接管时仍可能写入 Job 终态;token 接管后会被拒绝,当前属于较低概率恢复边界
- **Pipeline & Job Queue** · `app/services/pipeline_engine.py:977-1073` — PUBLISH_JOB_CREATING 尚无专用 reconcile 分支,恢复主要依赖重复创建的幂等去重
- **Pipeline & Job Queue** · `tests/test_pipeline_checkpoint.py:358-625; tests/test_pipeline_state_stability.py:275-313` — 仍缺真实进程重启、lease 过期边界及发布内容证据变更的进程级回归测试
- **Publish Center** · `app/services/publish_providers.py:204` — _post_multipart 先 file_path.read_bytes() 再 b''.join(chunks),完整视频和完整 multipart body 同时驻留内存,大文件发布存在约双倍峰值。
- **Publish Center** · `app/services/publish_service.py:1569` — _batch_find_publish_jobs 对每条记录直接调用 _normalize_job(row),未传入预取 accounts;_normalize_job 会逐条触发 readiness/account 查询,发布中心批量切片时形成 N+1 查询。
- **Publish Center** · `tests/test_publish_history.py:111` — 现有定向测试未覆盖原始 Secret/Token 响应、旧小写历史状态、同步封面 partial、批量无效 ID,以及 API 超时后已接收的重复投稿边界。
Expand Down
48 changes: 13 additions & 35 deletions .codemap/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"htmlPath": ".codemap/codemap.html",
"mdPath": ".codemap/codemap.md",
"spineDesc": "用户通过页面/API 创建素材任务,SQLite Job 驱动转写、AI 选片、审核切片和字幕,再由发送中心排期并交给 Windows Chrome Worker,所有状态与审计证据回写 SQLite。",
"tracked_loc": 50778,
"tracked_loc": 51786,
"tracked_files": 132,
"rev": "a01f103c43916d25310254f6f8389e9fc9e52521"
},
Expand Down Expand Up @@ -740,52 +740,30 @@
"tests/test_job_queue.py",
"tests/test_subtitle_auto_workflow.py"
],
"loc": 2342,
"contentHash": "6c81ef8f8e0fb45e635df5c6facba8ed4366581b7aca28e6c1ab9fd1b070a8dc",
"score": 72,
"grade": "C",
"loc": 3350,
"contentHash": "79bcc0b7de14b28318a7578e71d5281909254180ff351895b72fb10cefb87c68",
"score": 88,
"grade": "B",
"tags": [
"fallback",
"silent-except",
"legacy",
"stub",
"god-component",
"bloat",
"legacy",
"glue",
"over-fit"
],
"findings": [
{
"sev": "MED",
"loc": "app/services/pipeline_engine.py:108,311-314; app/services/job_service.py:488-504",
"text": "自动流水线仅在内存 context 中保存步骤结果,虽有 update_job_checkpoint API,但 PipelineEngine 未持久化调用;重启后可能从 PREPARING_SOURCE 重新执行并重复 AI、封面和排期副作用。"
},
{
"sev": "MED",
"loc": "app/services/job_worker.py:198-202",
"text": "WorkflowJobRunner 将子进程 stdout/stderr 重定向到 DEVNULL,父进程通常只能记录退出码,重启恢复和人工诊断证据不足。"
},
{
"sev": "MED",
"loc": "app/services/job_service.py:127-139",
"text": "payload/result/checkpoint JSON 损坏时静默保留原字符串,执行器后续可能产生不明确的数据类型错误。"
},
{
"sev": "MED",
"loc": "app/services/job_service.py:19-23; app/services/job_worker.py:60-69",
"text": "ai_analysis 与 publish job 类型仍可创建,但 Worker 没有对应执行分支,最终统一失败,属于可创建但不可执行的 legacy/stub 入口。"
},
{
"sev": "LOW",
"loc": "app/services/pipeline_engine.py:693-720",
"text": "产物 JSON 或排期时间配置损坏时静默回退默认值,故障可见性不足。"
"loc": "app/services/pipeline_engine.py:977-1073",
"text": "PUBLISH_JOB_CREATING 尚无专用 reconcile 分支,恢复主要依赖重复创建的幂等去重。"
},
{
"sev": "LOW",
"loc": "app/services/job_worker.py:310-353",
"text": "父 Worker 在 lease 已过期但尚未被其他执行接管时仍可能写入 Job 终态;token 接管后会被拒绝,当前属于较低概率恢复边界。"
"loc": "tests/test_pipeline_checkpoint.py:358-625; tests/test_pipeline_state_stability.py:275-313",
"text": "仍缺真实进程重启、lease 过期边界及发布内容证据变更的进程级回归测试。"
}
],
"auditedHash": "6c81ef8f8e0fb45e635df5c6facba8ed4366581b7aca28e6c1ab9fd1b070a8dc",
"auditedHash": "79bcc0b7de14b28318a7578e71d5281909254180ff351895b72fb10cefb87c68",
"auditedAt": "2026-08-24",
"auditedRev": ""
},
Expand Down Expand Up @@ -1189,4 +1167,4 @@
"auditedRev": "38fd60386a609b6ea85a3eb511b83cb296f4d2a1"
}
]
}
}
14 changes: 14 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Development Log

## 2026-08-24 稳定 V1 P1.3b 自动流水线断点恢复

- 新增 `auto_pipeline_step_v1` 版本化 checkpoint;每一步记录 `running/succeeded/failed`、连续完成前缀、输入基线和紧凑产物证据,不保存 Secret、Prompt、完整 AI payload 或大对象。
- checkpoint 写入继续使用 Workflow Job owner + lease token fencing;传入 `job_id` 却没有当前有效 lease 时,在状态更新、handler 和文件副作用之前直接拒绝执行。
- Worker 重启/租约接管后会验证转写 Markdown、AI active run 与候选集合、选片 JSON、active cut run、字幕审核状态、文案/排期 JSON 和发布草稿,再从第一个未确认步骤继续。
- AI、转写、切片、准备素材、字幕、文案和排期的 running 步骤支持产物 reconciliation;证据完整时补记成功,输入变化或证据不足时重做,不把旧产物当作新结果。
- 切片恢复绑定 `cut_run_id`、启用候选、当前任务受控目录、非空文件、size 与首尾 fingerprint;空文件、错 run、路径越界或内容变化均 fail closed。
- AI 恢复同时核对分析文件、数据库 `analysis_payload_json`、active run 和候选 `clip_key`;转写或候选输入变化时拒绝复用,避免恢复过期结果。
- 发布草稿恢复绑定 schedule 的切片/平台、草稿字段、允许状态、视频路径/指纹和封面 hash;旧 Worker 取消本轮草稿时还必须满足当前 Job owner/token 的同事务 `EXISTS` 条件。
- `/auto-retry` 现在在 `BEGIN IMMEDIATE` 中原位重排最新失败/取消 Job,保留其 checkpoint;不会另建空 Job 后重复调用 AI、FFmpeg 或发布草稿。直接 Job retry 仍保持同一语义。
- 自动流水线 checkpoint 与字幕渲染 Job 的旧 `{completed: ...}` checkpoint 按 Job 类型隔离;没有修改数据库 Schema、活动库、AI/FFmpeg 参数或真实发布逻辑。
- 定向回归覆盖崩溃后重领、失败 Job 原位重试、旧 token、无 lease、输入变化、损坏/未知 checkpoint、空切片/错 run、发布草稿篡改、取消竞态和测试清理隔离;最终完整测试 `651 passed`,Ruff 与 Python Compileall 全部通过。Codemap 独立复评将 `Pipeline & Job Queue` 从 `72/C` 提升到 `88/B`,剩余低风险项是发布创建步骤依赖幂等去重而非专用 reconcile,以及尚无真实进程重启级测试。
- 诚实边界:Provider 返回前进程崩溃且没有落下可验证 run/文件时仍可能再次计费;SQLite 与外部 AI/FFmpeg 无法构成严格 exactly-once,后续 P1.4 继续处理超时与调用幂等。

## 2026-08-24 稳定 V1 P1.3a 任务状态与切片原子性

- `PATCH /api/tasks/{task_id}/status` 改为显式允许表 + `BEGIN IMMEDIATE` 条件更新;空任务不能直接标记完成,并发状态变化不会被旧请求覆盖。所有内部状态写入同时拒绝已永久删除任务。
Expand Down
7 changes: 4 additions & 3 deletions NEXT_STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
- [x] P1.2a:Workflow Job 增加每次 claim 唯一的 `lease_token`,旧 Worker 的心跳、进度、checkpoint、终态和 release 均不能覆盖新执行。
- [x] P1.2b:Publish Job 使用现有 `execution_id` 完成所有写回 fencing,并让 Windows Publish Worker 对重复 execution 幂等;跨进程锁、恢复 fail-closed 和 Scheduler 优雅停机已覆盖。
- [x] P1.3a:任务状态接口受控转换;Task/Job lease 代际一致;自动流水线取消恢复、READY 终态和关联发布清理明确;切片批次编号、输出目录、Task 终态和数据库激活原子化。
- [ ] P1.3b:为自动流水线增加按步骤持久化 checkpoint 和副作用复用,补齐进程重启后的精确续跑;再处理字幕批准批次原子性与中断目录清理
- [x] P1.3b:自动流水线已增加版本化步骤 checkpoint、输入/产物证据、旧 Job 原位重试和重领恢复;已覆盖 AI、转写、切片、文案、排期与发布草稿的失效边界
- [ ] P1.3c:修复数据库唯一索引重建失败被静默吞掉的问题,并为幂等迁移增加可追踪版本与失败证据;不在本轮直接迁移活动库。
- [ ] P1.3d:处理字幕批准批次原子性、字幕/切片中断目录清理和剩余跨进程恢复边界。
- [ ] P1.4:统一第三方 AI/FFmpeg 超时、错误 JSON、429/5xx 与重试幂等边界,并避免重复计费。
- [ ] P1.5:在不扩大个人本地项目范围的前提下处理密钥日志、输入校验和本地管理员接口门禁。
- [ ] P2:拆分 God Service、去除查询重复、补核心集成/故障测试和可观测性;不做全面重构。
Expand All @@ -22,8 +23,8 @@
2. 不需要点击“立即发送”;本轮没有执行真实投稿,也没有改变 28 条 `NEED_REVIEW` 的人工确认边界。
3. 若未来永久删除返回 `cleanup_pending`,不要手工移动隔离目录;保留返回信息和 manifest,使用后续安全清理入口重试。
4. 修复前数据库备份位于 `data/backups/workflow-before-foreign-key-repair-20260824-130321-535723-35a0f972.sqlite3`,只有活动库无法通过完整性检查时才考虑恢复,不要直接覆盖当前数据库。
5. P1B.1/P1B.2 与 P1.3a 代码、隔离测试均已完成;活动库尚未增加 `workflow_jobs.lease_token`,本轮也未重启正式服务。下一独立轮先确认没有活动 Workflow Job,再做在线备份、幂等迁移和正式重启烟测。
6. P1.3a 不需要手工修改数据库。合并后可用一条低风险测试任务验证:取消后显示“已取消”;正常完成后停在“待人工确认发布”;不要点击真实发布。
5. P1B.1/P1B.2、P1.3a 与 P1.3b 代码及隔离测试均已完成;活动库尚未增加 `workflow_jobs.lease_token`,本轮也未重启正式服务。下一独立轮先确认没有活动 Workflow Job,再做在线备份、幂等迁移和正式重启烟测。
6. P1.3b 不增加数据库列。合并并完成正式迁移/重启后,可用一条低风险测试任务在 AI 或切片完成后重启 Worker,确认同一 Job 从 checkpoint 续跑;不要点击真实发布,也不要用重要待发布素材做故障演练

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

Expand Down
Loading