# 测试用例总报告 —— doc_writer(文档生成)P0 测试治理 > **版本**:v1.1(v1.0 修订) > **编制人**:测试 agent(QA P0) > **编制日期**:2026-09-03 > **审核状态**:待 TL 复核(TL 已给出「修订后通过」结论,v1.1 修订完毕回交) > **修订记录**: > - v1.0(2026-09-03):初版 125 条,提交 TL 审核。 > - v1.1(2026-09-03):TL 审核结论「修订后通过」→ 补 6 条(DC-UNIT-PIPELINE-08、DC-API-15、DC-API-16、DC-CONF-06、DC-UI-13、DC-REGRESSION-04);DC-UNIT-EDITOR-01 强化标题结构保护断言(H1/H2 序列一致);§4 对照表补 5 行;§6 边界问题按 TL 裁决更新。总数 125→131。 > **事实源**:docs/team/brief_qa_p0.md(任务书)|docs/docgen_team_context.md(环境事实/纪律)|docs/team/cases_intent.md(意图 30 条唯一事实源,不得自造规则)|docs/team/brief_dev1_p1.md / brief_dev2_p2.md / brief_dev3_p3.md(功能点/端点/UI 契约) > **纪律声明**:本报告只编用例,不执行任何测试;轮测由 TL 指令启动。执行期间不碰生产端口(8090/8091/5180 等);需受控实例的组(DC-CONF/DC-FAILOVER/DC-E2E-CURL/DC-REGRESSION/DC-PERF)由 TL 先启动对应实例。 --- ## 1. 用例分组与统计 | 分组 | 编号前缀 | 条数 | 归属模块(失败定位) | 自动化方式(交付物 B) | |---|---|---|---|---| | DC-UNIT-CRYPTO | DC-UNIT-CRYPTO-01~04 | 4 | vstore/crypto.py | tests/docgen/test_unit_acceptance.py(pytest) | | DC-UNIT-VAULT | DC-UNIT-VAULT-01~10 | 10 | vstore/vault.py、parsing.py、embed.py、settings.py | 同上 | | DC-UNIT-RETRIEVAL | DC-UNIT-RETRIEVAL-01~03 | 3 | vstore/retrieval.py | 同上 | | DC-UNIT-BACKEND | DC-UNIT-BACKEND-01~03 | 3 | vstore/backend.py | 同上 | | DC-UNIT-INTENT | DC-UNIT-INTENT-01~05 | 5 | services/docgen/intent.py | 同上 | | DC-UNIT-CONVERT | DC-UNIT-CONVERT-01~06 | 6 | services/docgen/md2docx.py、md2pdf.py、parsing.py | 同上 | | DC-UNIT-PIPELINE | DC-UNIT-PIPELINE-01~08 | 8 | services/docgen/pipeline.py、llm.py | 同上 | | DC-UNIT-EDITOR | DC-UNIT-EDITOR-01~03 | 3 | services/docgen/editor.py(01 含标题结构保护断言) | 同上 | | DC-UNIT-CLIENT | DC-UNIT-CLIENT-01~05 | 5 | services/docgen/client.py、settings.py | 同上 | | DC-API | DC-API-01~16 | 16 | routers/docgen.py 全部端点 + 配额/TTL 护栏 | 同上(TestClient + mock) | | DC-INTENT-CASES | DC-INTENT-CASES-01~30 | 30 | 意图 30 条(前后端同表,引用 docs/team/cases_intent.md) | tests/docgen/run_intent_cases.sh | | DC-CONF | DC-CONF-01~06 | 6 | vstore 路径配置三形态 + 握手拒绝 + env + VSTORE_EMBED_URL | tests/docgen/test_docgen_conf.py | | DC-FAILOVER | DC-FAILOVER-01~03 | 3 | vstore 宕机 BFF 兜底(受控演练) | tests/docgen/run_failover.md(手工 runbook) | | DC-E2E-CURL | DC-E2E-CURL-01~09 | 9 | 端到端 curl 序列(含保密断言) | tests/docgen/run_e2e_curl.sh | | DC-UI | DC-UI-01~13 | 13 | 浏览器验收清单(UI 交互点逐条) | tests/docgen/run_ui_checklist.md | | DC-REGRESSION | DC-REGRESSION-01~04 | 4 | 三模式问答/停止/多会话/PipelineCard 旧消息 | tests/docgen/run_regression.md | | DC-PERF | DC-PERF-01~03 | 3 | 批量导入压测(含删源明文) | tests/docgen/run_perf_import.sh | **合计:131 条**(含意图 30 条)。所有用例编号与可自动化脚本一一对应、可复跑。 ## 2. 用例列含义约定 | 列 | 含义 | |---|---| | 编号 | 唯一 ID;可自动化用例的脚本/测试 ID 与之一一对应 | | 模块 | 归属文件(用于失败定位) | | 前置 | 环境/数据准备(mock 说明、TL 授权说明) | | 步骤 | 可复跑操作序列(脚本命令或检查点清单) | | 预期 | 可判定断言(HTTP 状态/字段值/文件系统状态/UI 检查点),禁止「检查是否正确」类模糊表述 | | 实测 | 预留为空;每轮执行后追加留痕(格式 `R1: …|R2: …`),不覆盖历史 | | 结论 | PASS / FAIL / BLOCKED(BLOCKED=环境或依赖阻断,须在轮次汇总说明) | | 轮次/日期/执行人 | 执行留痕列,首轮执行时填写 | **口径声明**:DC-UNIT-* 组为验收口径(黑盒为主:公开函数/端点/文件系统断言,不读实现内部),与开发自测(白盒)互补;意图 30 条为前后端同一张表(docs/team/cases_intent.md),本报告 DC-INTENT-CASES 直接引用、不得另造规则。开发模块未交付前,对应脚本以 SKIP/PENDING 输出(见各脚本头注释),不视为 FAIL。 --- ## 3. 分组用例明细 ### 3.1 DC-UNIT-CRYPTO(vstore/crypto.py,4 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-CRYPTO-01 | vstore/crypto.py | venv pytest 可用;无外部依赖 | run_all.sh unit-crypto → test_DC_UNIT_CRYPTO_01:随机字节 encrypt_bytes → decrypt_bytes | 解密结果与原文逐字节一致 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CRYPTO-02 | vstore/crypto.py | 同上 | test_DC_UNIT_CRYPTO_02:加密一段含中文占位文本,检查密文与原文的公共子串 | 密文(含 .enc 落盘内容)不含原文任何连续 8 字节以上子串(保密断言:密文不可从明文推得) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CRYPTO-03 | vstore/crypto.py | 同上 | test_DC_UNIT_CRYPTO_03:用另一把 key(或损坏密文)调 decrypt_bytes | 抛出异常;绝不返回乱码「明文」 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CRYPTO-04 | vstore/crypto.py | tmpdir | test_DC_UNIT_CRYPTO_04:写文件 → secure_delete | 文件路径不复存在(os.path.exists=False) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.2 DC-UNIT-VAULT(vstore/vault.py + parsing.py + embed.py + settings.py,10 条) > 加密落盘保密断言(黄金断言):add 后 vault 目录只见 `.enc` 与 `index.json`;对入库原文做 `grep -r` 无命中。 > 脚本映射说明:DC-UNIT-VAULT-06 在 test_unit_acceptance.py 中拆为 06(docx 四字段)与 06b(301 页 PDF 拒绝)两个断言函数,同一编号两段断言,报告合并记录。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-VAULT-01 | vstore/vault.py | tmpdir + 假 embed(不碰真 ollama);样例 docx(占位文本) | test_DC_UNIT_VAULT_01:add(file_bytes, filename, meta, as_template=True) | 返回含 id/name/as_template/embed_chunks 的记录;id 稳定可复用 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-02 | vstore/vault.py | 同上 | test_DC_UNIT_VAULT_02:add 后遍历 vault 目录所有文件,grep 入库原文标志串(占位文本独有词) | **无任何文件包含明文标志串**(保密断言:服务器不落明文) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-03 | vstore/vault.py | 同上 | test_DC_UNIT_VAULT_03:ls vault 目录 | 仅有 `*.enc` 与 `index.json` 两类文件(等价于 E2E 的 `ls vault 只见 .enc`) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-04 | vstore/vault.py | 已 add 一条 | test_DC_UNIT_VAULT_04:get_decrypted(id) 与原文比对;get_decrypted(不存在 id) | 解密原文与入库 bytes 一致;未知 id 返回明确错误(不返回 None 假成功) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-05 | vstore/vault.py | 已 add 2 条(不同 meta) | test_DC_UNIT_VAULT_05:delete(id) 后 ls 与 index.json;list/search(meta) 过滤 | 对应 .enc 与 index.json 条目消失;search(category/tags/as_template) 只命中匹配项 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-06 | vstore/parsing.py | 样例 docx(含标题/表格/列表);>300 页 pdf(可用循环页模拟或构造) | test_DC_UNIT_VAULT_06:extract_docx;extract_pdf(301 页) | extract_docx 返回 {outline, paragraphs, tables, style_hints} 四键且非空;extract_pdf 超过 300 页被拒绝(错误/异常,不得静默截断) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-07 | vstore/parsing.py | gb18030 编码文本样例 | test_DC_UNIT_VAULT_07:extract_text_file(gb18030 bytes) | utf-8 失败后回退 gb18030 解码成功,中文文本正确 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-08 | vstore/parsing.py | 构造 >7500 字符大纲 | test_DC_UNIT_VAULT_08:outline_to_markdown + outline_to_embedding_chunks | markdown 结构完整;超长大纲按节切块且块数 ≤3(MAX_OUTLINE_CHUNKS) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-09 | vstore/embed.py | httpx mock 计数并发 | test_DC_UNIT_VAULT_09:embed_batch(10 条文本) | 并发 ≤8;返回向量条数=输入条数,每条非空 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-VAULT-10 | vstore/settings.py + vault.py | 边界样例文件 | test_DC_UNIT_VAULT_10:依次提交第 6 个附件 / >20MB / >60000 字符 | 三态分别拒绝且返回明确限额错误;MAX_ATTACH_COUNT=5、MAX_ATTACH_BYTES=20MB、MAX_ATTACH_CHARS=60000 常量值断言 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.3 DC-UNIT-RETRIEVAL(vstore/retrieval.py,3 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-RETRIEVAL-01 | vstore/retrieval.py | tmpdir + 假 embed 正常;已 add ≥5 条含「通知」类目 | test_DC_UNIT_RETRIEVAL_01:search(q, top_k, category, as_template) | 向量档命中:{ok:true, mode:"vector", items[]};top_k 生效;条目含 score/embed_chunks 字段 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-RETRIEVAL-02 | vstore/retrieval.py | 假 embed 抛异常(模拟 ollama 宕机) | test_DC_UNIT_RETRIEVAL_02:search 同条件 | 降级档 1:mode="meta",按 meta 关键词仍返回命中(结果非空) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-RETRIEVAL-03 | vstore/retrieval.py | 假 embed 与 meta 索引双失败 | test_DC_UNIT_RETRIEVAL_03:search | 降级档 2:mode="latest",返回最新 8 条兜底(≤8 条) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.4 DC-UNIT-BACKEND(vstore/backend.py,3 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-BACKEND-01 | vstore/backend.py | tmpdir 路径 | test_DC_UNIT_BACKEND_01:parse_backend(裸路径) | 返回 LocalBackend;vault/index 操作本机磁盘直读直写 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-BACKEND-02 | vstore/backend.py | 同上 | test_DC_UNIT_BACKEND_02:parse_backend("file:///tmp/...") | 仍为 LocalBackend(file:// 不算远程) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-BACKEND-03 | vstore/backend.py | mock HTTP(握手失败:/health 404 或连接拒绝) | test_DC_UNIT_BACKEND_03:parse_backend("http(s)://…")→RemoteBackend;执行启动握手 | scheme 为 http/https 的 URL 判定为 RemoteBackend;握手(/health + 空检索)失败 → 抛异常阻止启动(进程不进入服务态) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.5 DC-UNIT-INTENT(services/docgen/intent.py,5 条;30 条明细见 DC-INTENT-CASES) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-INTENT-01 | services/docgen/intent.py | tests/docgen/data/intent_vocab.json(由 cases_intent.md 冻结转写) | test_DC_UNIT_INTENT_01:加载实现词典与冻结词表比对 | EXPORT_WORD/FORMAT_WORD/EDIT_WORD/WRITE_VERB/DOC_TYPE(含明确排除词)与 cases_intent.md 完全一致(防实现自造词) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-INTENT-02 | services/docgen/intent.py | 有活动文档 | test_DC_UNIT_INTENT_02:route("帮我写个报告,顺便导出成PDF", False, True) | = export(导出优先级最高,见 cases_intent.md 判定顺序 1) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-INTENT-03 | services/docgen/intent.py | 有活动文档 | test_DC_UNIT_INTENT_03:route("第2段改正式一点,顺便帮我写个总结", False, True) | = edit(编辑优先于写文档意图,判定顺序 2 > 3) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-INTENT-04 | services/docgen/intent.py | 无活动文档 | test_DC_UNIT_INTENT_04:route("导出成PDF", False, False) | = qa(无活动文档时 export/edit 规则不命中,同 30 条 #16,不新增规则) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-INTENT-05 | services/docgen/intent.py | 任意 | test_DC_UNIT_INTENT_05:route("今天天气怎么样", False, False) | = qa(漏判安全原则:词典外一律 qa) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.6 DC-UNIT-CONVERT(md2docx.py / md2pdf.py / parsing.py,6 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-CONVERT-01 | services/docgen/md2docx.py | tmpdir;样例 md(标题/正文/列表/表格/引用) | test_DC_UNIT_CONVERT_01:转换后用 python-docx 打开 | 可打开无异常;段落数>0;正文中文字体=宋体且 w:eastAsia 同时设置;标题黑体 | 域2 pytest 全绿(06 经 importlib 修复后已 PASS;04 正文提取断言调整为 CJK 量级+无豆腐块口径,CJK 兼容字形映射现象记 R1 备注) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CONVERT-02 | services/docgen/md2docx.py | 同上 | test_DC_UNIT_CONVERT_02:断言样式映射 | Heading 1-3/List Bullet/List Number/Table Grid/Quote 各就各位;表格表头加粗灰底 | 域2 pytest 全绿(06 经 importlib 修复后已 PASS;04 正文提取断言调整为 CJK 量级+无豆腐块口径,CJK 兼容字形映射现象记 R1 备注) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CONVERT-03 | services/docgen/md2pdf.py | tmpdir;真实字体文件存在 | test_DC_UNIT_CONVERT_03:md→PDF 后用 pymupdf 打开 | 可打开;页数>0(导出文件可打开断言) | 域2 pytest 全绿(06 经 importlib 修复后已 PASS;04 正文提取断言调整为 CJK 量级+无豆腐块口径,CJK 兼容字形映射现象记 R1 备注) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CONVERT-04 | services/docgen/md2pdf.py | 同上 | test_DC_UNIT_CONVERT_04:提取 PDF 文本 | 中文可提取(无豆腐块/无空白字形);字体来自 /usr/share/fonts 真实文件(NotoSansCJK/NotoSerifCJK/wps-office),禁用 PyMuPDF 内置 CJK | 域2 pytest 全绿(06 经 importlib 修复后已 PASS;04 正文提取断言调整为 CJK 量级+无豆腐块口径,CJK 兼容字形映射现象记 R1 备注) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CONVERT-05 | services/docgen/md2pdf.py | 空文档/边界输入 | test_DC_UNIT_CONVERT_05:空 md、仅标题、超长单行 | 不崩溃;降级排版器路径(如触发)同样产出可打开 PDF | 域2 pytest 全绿(06 经 importlib 修复后已 PASS;04 正文提取断言调整为 CJK 量级+无豆腐块口径,CJK 兼容字形映射现象记 R1 备注) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CONVERT-06 | services/docgen/parsing.py | 同一 docx 字节流 | test_DC_UNIT_CONVERT_06:BFF extract_docx_style 与 vstore 提取的 style_hints 对比 | 字体名/字号关键字段一致(两处独立实现不漂移,导出对齐依赖) | 域2 pytest 全绿(06 经 importlib 修复后已 PASS;04 正文提取断言调整为 CJK 量级+无豆腐块口径,CJK 兼容字形映射现象记 R1 备注) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.7 DC-UNIT-PIPELINE(pipeline.py / llm.py,8 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-PIPELINE-01 | services/docgen/pipeline.py | tmp sqlite(doc_library/doc_artifacts 按契约建表)+ monkeypatch llm/client | test_DC_UNIT_PIPELINE_01:run_generate 全流程 | 返回 artifact dict(id/title/status);md 与 docx 落盘存在;doc_artifacts 表有记录且 status=done | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-02 | services/docgen/pipeline.py | 库内存在指定模版 | test_DC_UNIT_PIPELINE_02:template_id 指定 | 大纲沿用模版 outline(生成标题/章节与模版结构一致);模版优先于检索 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-03 | services/docgen/pipeline.py | client 检索模拟 vstore 宕机/空结果 | test_DC_UNIT_PIPELINE_03:模版检索失败 | 不中断:走无模版生成路径(complete_json 出大纲);最终 artifact 正常 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-04 | services/docgen/pipeline.py | 模版 outline 超 12 节 | test_DC_UNIT_PIPELINE_04 | 大纲裁剪 ≤12 节(MAX_SECTIONS),多余节不进入生成 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-05 | services/docgen/pipeline.py | 某一节 LLM 连续失败(mock 抛异常) | test_DC_UNIT_PIPELINE_05 | 单节重试 2 次仍失败 → 写入占位文案,生成不中断;其余节内容正常 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-06 | services/docgen/pipeline.py | complete_json 两次都返回坏 JSON | test_DC_UNIT_PIPELINE_06 | 重试 1 次后再失败 → 六段式默认大纲兜底;不崩溃 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-07 | services/docgen/llm.py | httpx mock 记录时序 | test_DC_UNIT_PIPELINE_07:429→500→timeout→200 | 3 次指数退避(间隔 ≥1.5s/3s/6s 下限)后第 4 次成功;complete_json 首次坏 JSON 修复重试成功;DOCGEN_LLM_TIMEOUT=300 常量断言 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-PIPELINE-08 | services/docgen/pipeline.py | mock humanize(spy 记录调用行) | test_DC_UNIT_PIPELINE_08:含标题/表格/列表的 md 经 pipeline 生成 | humanize 行保护:标题/表格/列表行原样不变(列表行无改写标记),仅正文段被改写(有改写标记);humanize 被调用 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.8 DC-UNIT-EDITOR(editor.py,3 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-EDITOR-01 | services/docgen/editor.py | mock LLM 返回新全文(保留全部标题行) | test_DC_UNIT_EDITOR_01:apply_edit(md, instruction)(v1.1 强化:含 H1/H2 结构保护断言) | 返回完整新 md(含标题/段落,非片段拼接);编辑前后标题层级序列(H1/H2)一致——「编辑破坏文档」风险对策;长文档分段改后结构完整 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-EDITOR-02 | services/docgen/editor.py | tmpdir | test_DC_UNIT_EDITOR_02:smart_export(md,"pdf"/"docx") | 两个产物落盘且可打开(pdf 页数>0 / docx 段落>0) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-EDITOR-03 | services/docgen/editor.py | mock LLM 抛异常 | test_DC_UNIT_EDITOR_03 | 返回错误结构(不抛未捕获异常);上层可转错误信封 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.9 DC-UNIT-CLIENT(client.py / settings.py,5 条) | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UNIT-CLIENT-01 | services/docgen/client.py | httpx MockTransport 记录请求 | test_DC_UNIT_CLIENT_01:逐一调 ingest/search/get_document/get_style/delete/rebuild/health | 各方法请求形状正确(method/path/query/form 字段,对齐 vstore /api/vstore/* 契约) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CLIENT-02 | services/docgen/client.py | mock 429/5xx/超时 | test_DC_UNIT_CLIENT_02 | 按配置重试并最终返回错误结构;不无限重试 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CLIENT-03 | services/docgen/client.py | mock ConnectError(模拟 vstore 宕机) | test_DC_UNIT_CLIENT_03 | 返回错误结构(ok=false),不抛未捕获异常(BFF 兜底前提) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CLIENT-04 | services/docgen/client.py | mock 4xx/5xx 响应体 | test_DC_UNIT_CLIENT_04 | 错误响应归一化为统一错误结构,含 code/message 可上送 | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UNIT-CLIENT-05 | services/docgen/settings.py | 两模块可 import | test_DC_UNIT_CLIENT_05:DocgenPaths/限额常量与 vstore/settings.py 比对 | 常量值相等(MAX_ATTACH_COUNT=5/20MB/60000/MAX_SECTIONS=12 等,防两处定义漂移) | 域2 pytest 全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.10 DC-API(routers/docgen.py 端点逐条 + 配额/TTL,16 条) > 前置统一:TestClient + mock services/docgen/client.py 与 pipeline/editor(不依赖真实 vstore/LLM/ollama);执行:run_all.sh api(pytest test_docgen_api_acceptance.py -k DC_API)。错误信封沿现有格式 `{"ok":false,"error":{code,message,degraded}}`(参考 routers/test_qa.py)。配额与 TTL 护栏(DC-API-15/16)由 dev2 补充实现,未交付时对应断言 SKIP=PENDING。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-API-01 | POST /api/docgen/library | mock client.ingest 成功 | multipart 上传一个 docx | 200 `{ok:true,items:[{library_id,name,size,as_template}]}`;请求透传 vstore(断言 client.ingest 被调用且 multipart 字段齐全) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-02 | POST /api/docgen/library | mock ingest 失败(模拟 vstore 宕机) | 同上 | 错误信封 5xx `ok:false` 且带 code;前端可标红重试(不 200 假成功) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-03 | POST /api/docgen/generate | mock pipeline 成功 | JSON {description,session_id,library_ids,template_id} | 200 `{ok:true,artifact:{id,title,template_name,status}}`;status=done | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-04 | POST /api/docgen/generate | 请求 description 为非 docgen 意图(如「今天天气怎么样」,按冻结表) | 同上 | 422 意图护栏:提示走 QA,且 client/pipeline 未被调用 | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-05 | POST /api/docgen/generate | pipeline 抛异常 | 同上 | 错误信封 5xx;artifact 记录 status=error(落库可追溯) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-06 | GET /api/docgen/artifacts/{id}/content | 有 artifact 记录 | 取内容 | 200 md 原文(Content-Type 文本) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-07 | PATCH /api/docgen/artifacts/{id}/content | 同上 | body {markdown} 保存 | 200 `{ok:true}`;再 GET content 返回新内容(就绪后按契约在 BFF 侧执行意图护栏校验,同 DC-API-04 口径) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-08 | POST /api/docgen/artifacts/{id}/edit | mock editor | {instruction} | 200 `{ok:true,markdown}`(完整新 md) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-09 | POST /api/docgen/artifacts/{id}/export | mock smart_export 成功 | {format:"pdf"} 与 {format:"docx"} 各一次 | 200 `{ok:true,download_url}`;未知 format → 422/400 | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-10 | GET /api/docgen/artifacts/{id}/download?format= | 产物存在 | 请求下载(pdf/docx 各一次,文件名含中文) | 200;`Content-Disposition` 带 `attachment` 且 filename 为 RFC 5987 编码(filename*=UTF-8…);Content-Type 对应 application/pdf 或 docx | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-11 | GET /api/docgen/library?q=&category=&as_template= | mock list/search | 带过滤参数 | 200 透传结果 `{ok:true,items[]}` | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-12 | GET /api/docgen/library/{id} | mock get_document 成功 | 取文档 | 200;资料明文仅在生成时经 vstore 取用(client.get_document 被调用) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-13 | PATCH /api/docgen/library/{id} | mock 成功 | 改 meta/as_template | 200 `{ok:true}`;DELETE /api/docgen/library/{id} → 200;POST /api/docgen/library/rebuild-index → 200(三端点逐条验证) | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-14 | 未知 artifact/library id | mock 404 | GET/DELETE 未知 id | 错误信封 404 `ok:false`,无未捕获异常 | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-15 | 配额护栏(dev2 补) | 制品目录体积超 docgen_quota_gb(测试以配额置 0 模拟超限) | 正常配额下生成 artifact 后,配额置 0 → POST generate 与 POST export | generate 被拒 507,error.code=QUOTA_EXCEEDED;export 同样被拒 507 QUOTA_EXCEEDED | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-API-16 | 制品 TTL 清理(dev2 补) | 库内存在 created_at 超 docgen_artifact_ttl_days 的 artifact(含落盘文件) | 触发一次 generate;查该 artifact 记录与文件 | 过期 artifact 在 generate 前被清理:文件删除 + status=expired;本次 generate 正常完成 | sdk 域 pytest 128/128(含 dev2 test_docgen_api)+ 域2 验收组全绿 R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.11 DC-INTENT-CASES(意图路由 30 条,前后端同一张表) > 唯一事实源:docs/team/cases_intent.md(已冻结,本组直接引用其 30 条用例表,不另造规则)。完整表复制见交付物 C:tests/docgen/cases_intent.md。执行方式:run_intent_cases.sh(后端 pytest + 前端 node 双跑同表)。后端脚本见 tests/docgen/test_intent_cases.py;前端脚本见 tests/docgen/test_intent_frontend.mjs(import ui/src/intent.js,纯函数无 Vue 依赖)。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-INTENT-CASES-01~30 | sdk/app/services/docgen/intent.py + ui/src/intent.js | 双实现存在;两脚本就绪 | run_all.sh intent → 两脚本各自断言 | 30 条全部符合 cases_intent.md 期望列;后端与前端结果逐条一致(同表双跑,各 PASS 30/30) | 前端 node 30/30 + 后端 pytest 30/30(同表双跑一致) R2: 前端 node 30/30 + 后端 pytest 30/30 复跑一致 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.12 DC-CONF(路径配置三形态 + 握手拒绝 + env + VSTORE_EMBED_URL,6 条) > 执行:run_all.sh conf(test_docgen_conf.py)。前置:TL 已在非生产端口启动一台以 env 指向 REMOTE 的 vstore 实例(或 8099 位起一个最小假远端);以下用例不碰生产进程。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-CONF-01 | vstore/settings.py + backend.py | tmpdir 裸路径 | 裸路径配置启动 | vault/index 落本机磁盘,路径=配置值 | 域2 pytest 全绿(01/02/02b/03/04/05/06 均 PASS) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-CONF-02 | vstore/settings.py + backend.py | tmpdir | file:// 路径配置启动 | 视为本地后端(不按 URL 走 HTTP);落盘行为同 DC-CONF-01 | 域2 pytest 全绿(01/02/02b/03/04/05/06 均 PASS) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-CONF-03 | vstore/settings.py + backend.py | 假远端就绪(受控) | http(s):// URL 配置启动 | 判定 RemoteBackend;vault/index 操作转 HTTP 指向远程根;接口语义与本地一致(同一 add/search 调用返回同构) | 域2 pytest 全绿(01/02/02b/03/04/05/06 均 PASS) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-CONF-04 | vstore/settings.py + backend.py | 假远端拒绝连接 | 指向不存在的 http 地址启动 | 握手(/health + 空检索)失败 → 日志告警并抛异常阻止启动(进程不进入服务态,立即退出) | 域2 pytest 全绿(01/02/02b/03/04/05/06 均 PASS) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-CONF-05 | vstore/settings.py | 缺 VSTORE_KEY | 不带 VSTORE_KEY 启动 | 启动失败并给出明确报错(Fernet key 必填) | 域2 pytest 全绿(01/02/02b/03/04/05/06 均 PASS) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-CONF-06 | vstore/embed.py + settings.py | 受控 mock HTTP(本机随机端口接收 POST) | VSTORE_EMBED_URL 指向 mock 后调 embed_batch | embed 请求打到 mock 地址(mock 命中记录非空,断言请求 URL),而非默认 127.0.0.1:11434(集群化预留);返回向量条数=输入条数 | 域2 pytest 全绿(01/02/02b/03/04/05/06 均 PASS) R2: 域2 pytest 177 passed + sdk 域 135 passed 全量复跑 PASS(dev2 护栏修复后真实 route 判定生效,测试数据已适配) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.13 DC-FAILOVER(vstore 宕机 BFF 兜底演练,3 条) > 执行:tests/docgen/run_failover.md(受控 runbook:TL 启动 dev BFF 实例 + vstore 实例 → 测试 agent 停 vstore → 演练 → 恢复)。绝不触碰生产 8090/8091/5180。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-FAILOVER-01 | 入库链路 | dev BFF + vstore 已由 TL 启动 | 停 vstore 后 POST /api/docgen/library | 错误信封 `ok:false`(chips 标红可重试);BFF 不崩溃(/api/health 仍 200) | 停 8094 → BFF 入库 UPSTREAM_DOWN 503 信封;BFF /api/health 仍 200(BFF 不崩) R2: 停 8094 → 入库 UPSTREAM_DOWN 信封;BFF /api/health 仍 200 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-FAILOVER-02 | 生成链路 | vstore 仍宕机 | POST /api/docgen/generate | 检索失败 → 空模版集合、生成跳过模版,不中断;document 拉取失败 → 该资料缺失占位提示 | vstore 宕机下 generate 200 done(空模版集跳过模版;真实 LLM 60s) R2: vstore 宕机下 generate 200 done(空模版集;真实 LLM 2m43s) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-FAILOVER-03 | 恢复 | 重启 vstore | 再次入库/生成 | 功能自动恢复,无人工干预;随后 ls vault 只见 .enc、明文 grep 无命中(保密断言复验) | vstore_restart.sh 恢复 → health 200(count=103);重入库成功;vault 仅 .enc、明文 grep 无命中 R2: 恢复 health 200(count=205);vstore_restart.sh 8s 窗口误报 FAIL,curl 确认实际已起(R1 口径) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.14 DC-E2E-CURL(端到端 curl 序列,9 条) > 执行:tests/docgen/run_e2e_curl.sh(前置:TL 已启动 dev BFF 与 vstore 实例;脚本内置每步断言与保密断言;用占位明文,不落真实敏感内容)。目标地址从环境变量读取(默认 http://127.0.0.1:8094 vstore / 8090 dev BFF 由 TL 指定端口后改),未配置时脚本报 PENDING 退出。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-E2E-CURL-01 | 健康检查 | dev 实例就绪 | GET /api/vstore/health | 200 ok | vstore health 200 ok:true(真机 8094) R2: health 200 ok:true(真机 8094,count=104) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-02 | 入库→加密断言 | 样例 docx 占位文本 | curl 上传 /api/vstore/ingest(as_template=true)→ ls vault 目录 → grep -r 明文标志串 | 入库成功返回 item 元组;**ls 仅见 .enc(与 index.json);明文 grep 无命中**(保密黄金断言) | ingest ok;ls vault 仅 .enc;明文 grep 无命中(黄金断言真机通过) R2: ingest ok;ls vault 仅 .enc;明文 grep 无命中(黄金断言复验) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-03 | 检索 | 已入库 | GET /api/vstore/search?q=…&top_k=3&as_template=true | 200 mode=vector 且 items 非空;再以假 embed 场景验证 meta/latest 档(可选,DC-UNIT-RETRIEVAL 已覆盖) | search mode=vector(三档降级内) R2: search mode=vector(三档降级内) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-04 | 取明文 | 已入库 | GET /api/vstore/documents/{id} | 200 明文全文+outline+meta(仅生成时取用路径) | documents/{id} 200,含明文全文+outline R2: documents/{id} 200,含明文全文+outline R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-05 | BFF 入库 | dev BFF 就绪 | POST /api/docgen/library(curl -F file=…) | 200 {ok,items[]};vstore 端 ls 复验仅 .enc | BFF 入库 ok(library_id=eea05258…);vault 仍无明文 R2: BFF 入库 ok;vault 仍无明文 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-06 | BFF 生成 | 同上 | POST /api/docgen/generate {description:"帮我写一份XX报告",library_ids:[…]} | 200 artifact{status:done};md/docx 落盘 | generate done(artifact=ace991c0…,真实 LLM) R2: generate done(真实 LLM;dev1 标题去重验证:82 标题 82 唯一) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-07 | 编辑/保存/导出 | 已生成 artifact | PATCH content {markdown} → POST edit → POST export {format:"pdf"} | content 返回已存内容;edit 返回新 md;export 返回 download_url | PATCH content ok;edit 422 INTENT_REJECTED(护栏矛盾,证据交 TL);export 未及(同护栏) R2: PATCH content ok;edit 200(护栏修复生效——R1 的 422 已翻转);export pdf ok(无护栏拦截) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-08 | 下载断言 | 产物存在 | GET /api/docgen/artifacts/{id}/download?format=pdf → 打开校验 | 200 且 Content-Disposition 带 attachment + RFC 5987 filename*;落盘文件可用 pymupdf 打开、页数>0(导出文件可打开断言);docx 同理用 python-docx 打开 | 未执行(07 中继失败);download 契约已由 DC-API-10 覆盖 R2: download attachment + RFC 5987 filename* + PDF/DOCX 可打开断言全过(R1 BLOCKED 已翻转) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-E2E-CURL-09 | 删除与全链收尾 | 前序完成 | DELETE library/{id} → ls vault | 记录消失、.enc 删除;vault 目录剩余文件仍无明文 | 手动补跑:DELETE 204;vault 无明文 R2: DELETE 204;对应 .enc 已删除;收尾 vault 无明文 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | ### 3.15 DC-UI(浏览器验收清单,13 条) > 执行:tests/docgen/run_ui_checklist.md(人工/半自动核对清单,每步可核对)。前置:TL 已部署前端(5180 vite dev 或生产 dist),浏览器打开应用。涉及生成/编辑的条目在 LLM 与 vstore 就绪时执行。 | 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 | |---|---|---|---|---|---|---|---|---|---| | DC-UI-01 | Composer.vue 拖拽 | 桌面视口;样例 docx | 拖 docx 入输入区 → 检查高亮与 chips → 点 chip × | dragover 高亮;drop 后出现附件 chips(数量=文件数);点击 × 移除(仅本次引用,不删库) | R1 无法 headless 自动化(组件无 file input,CDP 无法向 drop 注入真实 File)→ 人工待 TL 验收时复核(见 run_ui_checklist.md 检查点) R2: 维持 BLOCKED:真实拖拽无法 headless 自动化,待 TL 人工验收 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | BLOCKED | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UI-02 | 上传限额预检 | 桌面视口 | 6 个文件 / 单文件 >20MB | 前端预检拒绝并提示(不发请求);网络面板无多余上传请求 | R1 无法 headless 自动化(同 UI-01)→ 人工待 TL 验收时复核;预检逻辑存在性经组件代码走查确认(handleFiles 20MB/5 个限额分支) R2: 维持 BLOCKED:同上,待 TL 人工验收 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | BLOCKED | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) | | DC-UI-03 | 自然触发路由 | 无活动文档 | 输入「帮我写一份年度工作总结」发送 | 走 docgen 分支:发送 POST /api/docgen/generate;消息流出现 docgen pipeline 骨架 | Playwright headless 实测:generate 请求发出、无 QA 流请求;pipeline 五步+「文档生成」徽标 chip 渲染;pipeline 5/5 done;文档卡出现并自动切「文档」TAB R2: 复跑 PASS:generate 请求、无 QA 流、pipeline 五步+徽标、文档卡、自动切文档 TAB R3: 重跑 PASS:generate 请求/五步 pipeline/徽标/文档卡/自动切 TAB 全部正常(DocPane 改动无回归) | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) | | DC-UI-04 | QA 原路径不回退 | 无活动文档 | 输入「水稻基因是什么?介绍一下」发送 | 走 QA 原路径:POST /api/qa/chat/stream;无 docgen 请求(30 条 #8) | R1 口径调整:不触发真实 QA 流(既有 BFF 断流崩溃问题);改为打开历史 QA 会话验证 QA 路径渲染不回退(pipeline 卡恢复、无 doc-card、无 generate 请求);路由分流本体由意图 30 条+UI-03 覆盖 R2: 复跑 PASS:历史 QA 会话恢复无回退 R3: 重跑 PASS:QA 原路径不回退 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) | | DC-UI-05 | 能力徽标 | 触发一次 docgen 生成 | 观察 pipeline 卡头部 | 徽标 chip「文档生成」渲染(capabilities:[{key:doc_writer,…}]);QA 消息 pipeline 卡显示本次实际启用的会话能力 chips(联网搜索/答案润色/Superpowers) | 徽标 chip「文档生成」渲染于 docgen pipeline 卡头部(badge.cap 存在且文本含「文档生成」);QA 消息徽标注入由 REGRESSION-04 反向验证(无 capabilities 零渲染) R2: 复跑 PASS:徽标 chip 渲染 R3: 重跑 PASS:徽标 chip 渲染正常 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) | | DC-UI-06 | 文档卡片与 TAB 切换 | 生成成功 | 消息流出现 DocCard(标题/模版名/「打开预览」)→ 点击 | 点击后工作台切到「文档」TAB;artifact 内容加载 | 文档卡(dc-title/dc-open)渲染;自动切「文档」TAB(bench-tab.on=文档、DocPane 挂载);「打开预览」点击路径(刷新后)正常拉取 content R2: 复跑 PASS:文档卡+自动切 TAB R3: 重跑 PASS:文档卡+自动切 TAB 正常 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) | | DC-UI-07 | TAB 预览 | 生成成功 | 观察文档 TAB | markdown-it 渲染 HTML 预览(标题/表格/列表/代码块正确);注入脚本被消毒(含