Files
UHD_Media_Tool/docs/plan/08-v0.2-closeout.md
T
2026-09-29 18:37:35 +08:00

178 lines
17 KiB
Markdown

# v0.2 收尾修复(W8-A)
2026-09-28。基线 `c67f6a9`(Pi 上游、子会话、定时任务看板、IA-1、IA-2 已合入)。与 W8-B(全站文案第二轮)并行,本轮只改逻辑。
## 本轮修了什么
| # | 事项 | 落地 |
| --- | --- | --- |
| 1 | 模块自建计时器并入定时任务注册表 | `pi.token_refresh`(上游登录刷新,维护组,10 分钟,入队 `refreshPiTokens`,幂等键按时间槽)与 `auth.cleanup`(登录会话清理,维护组,1 小时)改为 `registerScheduledJob`;模块里不再有 `setInterval`。测试 `test/schedules/module-jobs.test.ts`。`docs/runbook/schedules.md` 已登记表补两行 |
| 2 | 没有候选的待识别资源进待处理 | 迁移 `0030` 给 `decision.kind` 加 `identity_unresolved`(界面"待识别")。`resolveObservation` 在 no_match(含无法解析、候选为 0)和查询失败且自动复查已停时开事项,去重键与歧义类同为 `identity:<资源ID>`(类别随证据切换)。选项:手动指定 TMDB ID(`requires=tmdb`)、选本地作品(`requires=mediaEntityId`)、忽略;歧义类事项也追加前两项。`POST /decisions/{id}/resolve` 新增 `identity {tmdb:{type,id} \| mediaEntityId}`,走 `assignReleaseIdentity` 人工指定加锁。待处理页选项弹窗按 `requires` 出 TMDB 类型 + ID 输入或本地作品检索(复用 `EntityPicker`),未填完整时确认按钮禁用。身份页只读列表"已进待处理"按类别跳 `?kind=identity_ambiguous / identity_unresolved` |
| 3 | 旧 `settings.pi.provider / baseUrl / model / thinkingLevel / apiKeySecretRef` | `DEPRECATED_SETTINGS_FIELDS`:schema 保留(兼容已存 JSON),`PATCH /settings` 丢弃,设置分区不再归属;`runAgent` 只走上游,没有默认上游直接失败「未配置 Agent 上游」;`pi.apiKey` 写 secret 的分支删除;Agent 页演示开关改看默认上游 provider 是否 fake;上游列表空态不再提旧设置。e2e 夹具改为直接建一条 fake 上游设默认 |
| 4 | `agent.await` 不占 worker 槽 | runAgent 驱动的会话里,子会话未落定时 `agent.await` 记下等待条件并关闭会话(该工具调用留在 JSONL 里未完成);`drive` 返回 `suspended`,runAgent 用 `waitForFact("subagent-await-N")` 每 2 秒查一次子会话落定 / 超时 / 父会话取消,再以 `resumedAwaits` 重开会话 resume。`agent.await` 是只读能力(replay=safe),恢复时重放一次直接拿到结果,模型不多一轮。单会话最多挂起 100 次。e11 断言 `agent.await` 的结束事件带 `recovery:true`、`run_started` 为 `[false, true]`。`agent.spawn {await:true}` 仍在进程内等(见遗留) |
| 5 | Agent 汇报里的下载链接 | `agent-conversation.tsx` 改链 `/console/downloads/:id`,不再经 `/tasks/:id` 404 探测 |
| 6 | 设置旧链接 | IA-2 已由 `settings.tsx` 的 `clientLoader` 把 `?tab=` 转成 `#分区[/节]`;本轮把 `settingsPath()` 改为直接生成 `#分区[/节]`,冒烟的重定向表补 4 条 `?tab=`,静态路由补 8 个分区 hash |
| 7 | 前端冒烟 | 独立库 + `PORT=4392 WEB_ROOT=apps/web/build/client` 单端口跑 `deno task test:web:smoke`:47/47 通过(跳过 4 条详情页,库里无数据)。冒烟脚本对只差 hash 的地址先跳 `about:blank` 再整页加载 |
另:`executeResolution` 对 `identity_unresolved` 同样跳过(由 sources-identity 在处理端点里直接执行)。
## 仍遗留
- **pi 41 个 provider 未联网实测**:只有 fake 与 OpenAI 兼容本地端点有自动化覆盖;8 种订阅登录只测了 fake OAuth 状态机。Codex `_randomBytes` 在 Deno 下的行为仍待 spike(`docs/design/pi-providers.md`)。
- `agent.spawn {await:true}` 仍在进程内轮询等待,占 worker 槽到超时;要挂起需要同样的关闭会话 + 重放,但 spawn 是 replay=never,重放会得到"结果未知",需另行设计。
- 挂起期间父会话状态保持 `running`(避免被祖先会话的 `agent.await` 当成已落定);运行时长预算按每段 drive 计,不含挂起等待。
- 待识别事项:查询失败但仍在自动复查期内的资源不开事项(避免 TMDB 抖动刷屏),复查耗尽后才开。
- 身份页"待识别"详情抽屉的提示句仍写"选择候选或忽略",未识别类(无候选)读起来不准确,留给文案轮。
- 旧设置字段只是弃用未删除;真正删除需要一次迁移清理 `settings.pi` 里的旧键。
- 原站视图仍是占位(IA-1 遗留)。
## W10 集成记录
2026-09-28。目标:所有并行成果合进 main,`deno task ci` 连跑 3 次全绿。
### 合并了什么
- `fe3-web`(FE-3:规则页、命名模板表单、顶栏搜索 / `Ctrl/⌘+K` / 搜索结果页,基于 `81bf010`)以 `0440130` 合入。冲突只在 `routes.ts`(FE-1 的 `status` 与 FE-3 的 `search` 两条都留)与冒烟清单(合并两边路由,加规则页 4 个页签、搜索结果页、`/console/preferences` → 规则页重定向);顶栏挂 `SearchTrigger` + `CommandPalette`,侧栏仍是 FE-1 定稿的 14 项。合并后 web typecheck 通过,分支已删。
### 修了什么
| 提交 | 内容 |
| --- | --- |
| `615917b` | U3 outbox → SSE 桥:`OutboxRelay` 的 `mirror`,`feed.*`(除 `feed.due`)、`decision.*`、`download.*`、`organization.*`、`subscription.*` 投递成功并提交后按序发到总线;以前没有路由的 `feed.fetched` / `organization.changed` / `download.threshold` 一直停在 pending,现在会被认领。测试:tasks `outbox.test.ts`、server `bootstrap.test.ts` |
| `363f85b` | `/dashboard` 补 `automation.library` 与 `slotSatisfiedRate`;`/engine/runs` 的 `status` 多值、`triggeredBy` 筛选(服务端逐批推断,单次最多扫 2000 行,游标停在最后检查的行);任务页来源筛选和"进行中"页签改为一次服务端分页查询 |
| `2ae780d` | 整理向导把目录展开成逐个视频提交时,planImport 把同目录同主干的字幕 / 外挂音轨补进源文件;`/console/organize?ids=` 只列本次记录 |
| `082c556` | 视觉走查前端项 U4 U5 U6(部分)U13 U14 U15 U16 与文案清单;逐条状态见 `09-visual-qa.md`"W10 前端复核" |
| `f6063af` | server PG 测试偶发"schema 不存在"的根因:Kysely 迁移器用 `introspection.getTables()` 反射全库所有 schema 并逐个调 `has_schema_privilege` / `pg_get_serial_sequence`,别的测试文件同时 drop 自己的 schema 就报错(原有 `withSchemaLock` 只覆盖部分 harness)。`createMigrator` 改为只内省目标 schema;回归用例在迁移时并发建删 schema,旧实现可稳定复现失败 |
| `080563d` | CHANGELOG `[0.2.0]` 补 W9 / 前端波内容与已知限制 |
开发库(55433 `umt`)清理:删除 `fe4-*`、`w32-*`、`w33-*` 前缀的作品(11)、站点(4)、订阅(5)及其版本目标、计划、资产、单元、外部身份、路径绑定等从属行;管理员 `w31-admin` 与 fake 上游保留。测试都用独立 schema,不受影响。
### 三次 CI
HEAD `080563d`,本机串行:
| 次 | 结果 | 耗时 |
| --- | --- | --- |
| 1 | 全绿(server 59 文件 / 366 用例) | 403 秒 |
| 2 | 全绿 | 383 秒 |
| 3 | 全绿 | 381 秒 |
前端冒烟:独立库 + `PORT=4392 WEB_ROOT=<build/client>` 单端口,新初始化管理员,64/64 通过,跳过 12 条详情页(库里无数据)。
### 遗留
- U6 悬浮按钮滚动途中仍可能暂时盖住右侧控件;U8(观众站无 H&R 选择器)、U10 不改;U11 全站标点规范未定,未改。
- 还没有 `subscription.*` outbox 事件,订阅页不随后台变化实时刷新。
- `/engine/runs?triggeredBy=` 命中稀少时一页可能少于 `limit`;定时任务运行记录仍没有引擎 run id。
- 开发库里还有 6 个无前缀的测试站点(观众 ×3、HDSky ×2、馒头,指向已停的随机端口 mock),以及历次测试残留的约 150 个 `*_test_*` / `e2e_*` schema(测试进程中断时 afterAll 没跑到),本轮只清了指定前缀的数据,未动它们。
- 开发库上还挂着两个早先起的 `deno task dev` 服务进程(4394 / 4397,`--watch`),本轮未动。
## INT-2 集成记录(2026-09-29)
### 发生了什么
v0.2 收尾阶段四个子代理(憨憨修复、AGUI-2、BR-2、SEARCH-2 / META-1)在同一个工作区、同一个 git 暂存区里并行提交:
1. 憨憨修复的提交 `901b01d` 用 plumbing 方式从自己的暂存副本提交,把 AGUI-2(`20b63db`)刚改的 14 个 agent 前端文件(`components/agent/*`、`agentui/tool-result.tsx`、`routes/console/agent*.tsx`、`api/agent.ts`、`scripts/smoke.mjs`)带回了 AGUI-2 之前的版本。
2. 随后 BR-2(`28974d1`)从共享暂存区提交,暂存区里是 `901b01d` 之前的文件,于是把 `901b01d` 的改动整体撤掉(附带把 AGUI-2 恢复了)。憨憨的改动只剩工作区未暂存改动和 `901b01d` 的 diff。
3. SEARCH-2 的代码被文档提交 `ac47867`(`docs(plan): on-demand site detail page parsing`)从暂存区顺带带入,内容完整;`18a5cf1` 是补记用的空提交。憨憨改动里的 contracts(api / settings / sources)、`routes/sources.ts`、db 类型、runbook 部分也经这些提交进了 main。
### 如何修复
- AGUI-2 核对:`git diff 20b63db HEAD -- components/agent components/agentui routes/console/agent* api/agent.ts scripts/smoke.mjs` 为空,HEAD 上就是 AGUI-2 版本,无需恢复。
- 憨憨改动:在临时 worktree 上把 `git diff 20b63db 901b01d`(排除 agent 相关 14 个文件)三方合并到 HEAD,结果与工作区未暂存改动逐文件一致(22 个文件;`account.ts` 与 `schema.test.ts` 是和 BR-2 / META-1 合并后的版本;contracts、routes、types、runbook 已在 main,合并后无差异,`settings.ts` 的冲突 hunk 因此不再需要手动处理)。按路径 `git add` 后提交 `6e9e8ef fix(sites): HHClub parsing, resource list layout and request restraint (re-landed)`。工作区没有其他无关改动。
- 迁移:编号连续无重号,开发库(55433)的 `kysely_migration` 与仓库一致(0046 → 0049 依次应用,无残留行需要处理);`db:migrate` 无待执行迁移,`gen:types` 无 diff。
| 迁移 | 来源 | 内容 |
| --- | --- | --- |
| `0046_site_request_count` | 憨憨修复(`6e9e8ef`,原 `901b01d`) | 站点请求按小时计数 |
| `0047_identity_pool` | DEC-1(`94fd48b`) | 待识别池 |
| `0048_site_search_default` | SEARCH-2(代码在 `ac47867`,记录 `18a5cf1`) | `site.search_default` |
| `0049_enrichment` | META-1(`f4666a4`) | 元数据补全 |
### 站点详情页(追加需求)
`7b5c2f6`:详情抽屉「站点详情页」一节(简介、MediaInfo、截图经图片代理、IMDb / 豆瓣 / TMDB、文件清单、标签、上传者);只按需抓,`kv_cache` 缓存 24 小时;外部 ID 写 `raw._detail` 并作为显式 ID 进身份解析(TMDB ID 直取详情、IMDb 走 `/find`)。细节见 `runbook/sites.md`「详情页扩展内容」。
真实憨憨未核对:要让新解析生效需重建用户栈的 app 镜像(同时执行 0046–0049 迁移),本轮自动模式不允许部署,所以没有对真实憨憨发请求(0 次)。重建后按一次 `GET /sites/{id}/releases/{rid}?refresh=true` 核对,缓存里的 `probe.labels` 可以看出憨憨详情页实际有哪些字段标签,据此补 `umt.detail.page`。HHClub 选择器目前按 MovieClaw 与 fixture 编写。
### CI
本机串行全量 `deno task ci`:
| 次 | HEAD | 结果 | 耗时 |
| --- | --- | --- | --- |
| 1 | `6e9e8ef`(憨憨重新落地后) | 全绿(server 82 文件 / 498 用例) | 509 秒 |
| 2 | `6e9e8ef` | 全绿 | 505 秒 |
| 3 | `04cffcd`(加详情页) | 全绿(server 503、adapters 381 用例) | 533 秒 |
前端冒烟:独立库 `umt_smoke_int2` + `PORT=4392 WEB_ROOT=<build/client>` 单端口,新初始化管理员,66/66 通过,跳过 12 条详情页(库里无数据)。「Agent 抽屉 → 历史」一步用演示数据,生产构建只有默认上游是 `fake` 时才允许演示数据,所以冒烟库里要先建一个 `fake` 默认上游(没有时这一步超时失败)。跑完已删库。
### 遗留
- 真实憨憨详情页核对(见上)与用户栈重建(0046–0049 迁移)待用户执行。
- 开发库残留的 `hhclub-wt` 临时 worktree(`ac47867`,在会话 scratchpad 下)未删,不影响仓库。
## INT-3 记录(2026-09-29)
BR-3 已先行合并(`80f0fa0`)。本轮把六个 worktree 分支依次 `git merge --no-edit` 到 main,再补集成清单。
### 合并顺序与冲突
| 顺序 | 分支 | 合并提交 | 冲突与解决 |
| --- | --- | --- | --- |
| 1 | `cache2`(CACHE-2) | `eb9cfaa` | 无 |
| 2 | `md1`(MD-1) | `b633eb5` | `contracts/settings.ts` 元数据分区字段表:保留 md1 新增的 `metadata.languages`,去掉 md1 带回的已弃用 `cache.tmdbTtlHours` |
| 3 | `id2`(ID-2) | `f30600e` | `adapters/tmdb/index.ts`:md1 的 `details` / `translations` 与 id2 的 `recall` 导出并存 |
| 4 | `hh2`(HH-2) | `f41cd96` | 基于 BR-3 之前的 main,和 BR-3 在 `browser/service.ts`、`contracts/browser.ts`、`desktop-view.tsx`、`site-origin-view.tsx` 冲突:`RecoveryState` / 会话视图同时保留 BR-3 的 `needsReconnect` / `reconnect` 与 HH-2 的 `lastAutomationAt`;前端 import 用 HH-2 的 `controllerLabel` / `agentActive`,保留 BR-3 的 `reconnect.tsx` |
| 5 | `fe-settings`(FE-7) | `161e6e7` | 无 |
| 6 | `fe6`(FE-6) | `4d850c1` | 无 |
合并后 typecheck 直接通过,没有语义冲突。`deno.lock` 在根 `deno check` 与 web typecheck 之间会来回变(web 依赖是否写入),`0a8bfbb` 按 web 版本提交。
### 迁移
只有 cache2 带迁移(0050),其余分支没有迁移,不需要重排编号。集成补丁新增 0051、0052。开发库(55433)依次执行,`gen:types` 已更新。
| 迁移 | 来源 | 内容 |
| --- | --- | --- |
| `0050_storage_retention` | CACHE-2 | 流水表按时间清理的索引 + 按表 autovacuum 参数 |
| `0051_tmdb_cache_size` | INT-3 `fe05138` | 已存设置里 `cache.tmdbMaxSize` 仍是旧缺省 256 的改成 5000(用户库当前是 256) |
| `0052_agent_run_created_by` | INT-3 `e681fce` | `agent_run (created_by, created_at desc)` 索引,供巡检汇总 |
### 清单完成状态
- [x] `cache.tmdbMaxSize` 缺省 5000(含已存值迁移,runbook / porting 文档同步)。
- [x] 设置页「自动化 → 存储与保留」接 `settings.storage`:存储预算、流水数据保留、缓存有效期三张卡;元数据分区去掉 `cache.tmdbTtlHours`、`cache.emptyResultTtlSec`、`identity.poolArchiveDays`。
- [x] 新建档立即应用语言回退链并入队 full 刷新:`/files/entity-from-candidate`(订阅建档、搜索 / TMDB 预览建档、整理向导)与手动指定未登记的 TMDB 身份;幂等键 `refreshMetadata:created:{id}`。RSS 自动识别新建的作品不入队 full(量大),仍按追踪刷新。
- [x] 桌面模式打开时先调幂等 `POST /browser/sessions/{id}/reconnect`;画面 5 秒未加载或视频流未出帧,每个会话自动重连一次并重建桌面连接。
- [x] `GET /schedules` 的 agent_check 项带 `agentRuns`(最近 100 次会话的上次 / 平均费用、近 7 天次数与费用、模型分布、最近 3 次),计划页卡片与首页计划带改用它,删掉前端的会话列表拉取。
- [x] HH-2 用例 `rss-normalize` 的旧 RSS 行首见时间取"现在",会排在下一个用例的快照前面,第 1 次 ci 失败;固定首见时间后通过(`53790cb`)。
### ID-2 回滚脚本 dry-run(用户库,只读)
连接加 `default_transaction_read_only=on`,不带 `--apply`:
- 待识别合并事项 1 条(Archives S01 2009),预估 0/1 可自动确认。
- 3 个分类词站点别名涉及 3 部作品:「东北有个周东北(2026)」绑定 35 条,误并入 29 条(Irreplaceable / 无可替代、Spring of the Blade、A Prophet、Spark of the Long March / 星火征途 的各版本),要删别名「电视剧」「Spark of the Long March」「Irreplaceable」「A Prophet」「Spring of the Blade」;「炼气十万年」删「动漫」、「心动的信号」删「综艺」,这两部绑定都核对通过。
- 合计解除 29 条资源绑定、删除 7 个站点别名;这些作品下 15 个内容单元无资源引用(脚本不删)。
`--apply` 待用户确认,且要在用户栈重建(新识别规则 + 0050–0052 迁移)之后执行,解除的资源才会按新规则复查。
### CI 与冒烟
| 次 | HEAD | 结果 | 耗时 |
| --- | --- | --- | --- |
| 1 | `53790cb` 之前 | `rss-normalize` 用例依赖当前时间失败(见上) | — |
| 2 | `26ce782` | `contracts/db-sync`:`ScheduledJob.agentRuns` 未登记为只在 API 的字段,`481a97f` 补上 | 447 秒 |
| 3 | `481a97f` | 全绿(server 89 文件 / 541 用例) | 555 秒 |
| 4 | `481a97f` | 全绿 | 537 秒 |
前端冒烟:独立库 `umt_smoke_int3` + `PORT=4393 WEB_ROOT=<build/client>`,新初始化管理员 + `fake` 默认上游,69/69 通过,跳过 13 条(库里无数据)。跑完已删库。
### 遗留
- 用户栈重建(app 镜像 + 0050–0052 迁移)由协调者执行;之后再决定是否对用户库跑 `identity-reevaluate.ts --rollback-wrong --apply`。
- RSS 自动识别新建的作品不自动做 full 刷新;要补每集信息时手动刷新或按 runbook 批量跑。
- 桌面黑屏判断只在同源拿得到 Selkies 文档时看视频帧,拿不到时只按 iframe 是否加载判断。