feat: 功能清单进智能体提示,补字幕按功能模块约定改为样板

- 外围功能模块(补字幕、原站视图、桌面模式、网页搜索、MCP、定时任务)声明 manifest,与 FEATURES 一一对应;
  系统提示「能力地图」末尾列出外围功能(没有开关,能力只列本次可用的)
- 补字幕:订阅 organization.finished(认出值得补字幕的整理,自动补字幕还没做,不建任务),
  写入字幕后在同一事务发 subtitle.written;原有行为不变

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-06 19:27:14 +08:00
1 parent 1db689d9a7
commit 7aa5182f3d
14 files changed
+237 -17

No files matched your search

@@ -1,6 +1,7 @@
import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { parseEventPayload } from "@umt/contracts";
import { zipSync } from "fflate";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { createSubtitlesModule } from "../../src/modules/subtitles/index.ts";
@@ -159,6 +160,18 @@ describe.skipIf(!hasPg)("补字幕:上传、写入、撤销(真实 PG + 临时
const hist = await api("GET", `/subtitles/writes?entityId=${entityId}`);
expect(hist.body.data.items).toHaveLength(2);
// 生命周期广播(ADR-006):每个写入一条 subtitle.written,payload 过 contracts schema
const events = await h.db
.selectFrom("outbox_event")
.select(["payload", "idempotency_key"])
.where("type", "=", "subtitle.written")
.orderBy("at")
.execute();
expect(events.map((e) => parseEventPayload("subtitle.written", e.payload))).toEqual([
{ writeId: w1.id, mediaEntityId: entityId, videoLocationId: loc.e1, language: "zh-Hant", source: "upload" },
{ writeId: w2.id, mediaEntityId: entityId, videoLocationId: loc.e2, language: "zh-Hans&en", source: "upload" },
]);
expect(events.map((e) => e.idempotency_key)).toEqual([`subtitle.written/${w1.id}`, `subtitle.written/${w2.id}`]);
expect((await api("POST", `/subtitles/writes/${w2.id}/undo`)).body.data.undoneAt).toBeTruthy();
expect(await readFile(join(dir, "示例剧 - S01E02.chi.zh-cn.srt"), "utf8")).toBe("旧字幕");