Files
PlantAI-rag-new/tests/docgen/test_case_report.md
T
jerryandClaude Fable 5 4749f77544 fix(docgen): 两批手工测试修复——上传/预览/检索 + 护栏/导出/命名/编辑还原/二次预览
第一批:上传去限制+进度展示、模板HTML预览+按需导出、检索TAB实时更新
第二批:edit护栏移除、导出按钮纯化+file_name落库、编辑失焦还原、
       二次预览白底根治(防竞态+渲染兜底)
模板体系:7套公文模板(templates/)+templating渲染器、websearch联网

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 15:49:54 +08:00

569 lines
95 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 测试用例总报告 —— 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 预览(标题/表格/列表/代码块正确);注入脚本被消毒(含 <script> 的 md 不执行) | 预览 markdown 渲染 HTML 通过(h1/h2 标签存在);消毒沿用 markdown.js renderMarkdown(LLM 输出先消毒) R2: 复跑 PASS:dev3 BUG-3 修复生效——自动切 TAB 后预览直接可用(无需刷新绕行) R3: 重跑 PASS:自动切 TAB 后预览直接可用(BUG-3 修复保持) | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-UI-08 | 就地编辑保存 | 预览可见 | 编辑标题/正文/列表/表格行 → blur | 内容即时更新;blur 后 debounce 触发 PATCH content 保存;刷新页面/重进 TAB 内容不丢 | FAIL:就地编辑 contenteditable 未接线(源码注释宣称实现,编辑模式 DOM 无任何 contenteditable 块;serializeMd 代码存在但不可达)——dev3 实现缺陷,交 TL R2: R2 新 FAIL:contenteditable 接线已修复(28 块可编辑,R1 缺陷翻转),但保存仍不可达——@blur 挂 editRoot 根 div(blur 不冒泡),真实块失焦不触发;证据:realBlockBlurPATCH=0 rootBlurDispatchPATCH=1(链路本身完好)。交 TL R3: 重跑 PASS(R3 定向核心):dev3 focusout 修复生效——真实失焦到编辑区外必现 PATCH(R2 的 realBlockBlurPATCH=0 已翻转为 ≥1);块间焦点切换与编辑区内迁移零误触发;刷新/重进 TAB 两处编辑内容均持久(三阶段全过) | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-UI-09 | prompt 修改 | 预览可见 | 「用 prompt 修改」输入「把标题改成黑体」提交 | 调用 edit 端点;TAB 内容替换为新 md;预览更新 | 「用 prompt 修改」输入行 → edit 端点实测:请求发出且响应可见(R1 实测 edit 端点被 BFF 护栏 422 拦截,前端 editError 错误态可见——失败可见原则成立);端点本体由 DC-API-08(mock 放行)测得 OK R2: 复跑 PASS:prompt 修改 → edit 端点 200(护栏修复后真实 LLM 编辑可用) R3: 重跑 PASS:prompt 修改 → edit 端点 200 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-UI-10 | 导出与下载 | 生成成功 | 依次点「导出 PDF」「导出 DOCX」;检查下载 | 两文件均下载;浏览器下载列表可见;下载后本地可打开(导出文件可打开断言) | 导出 PDF 按钮 → export 端点实测:请求发出且响应可见(R1 实测 export 端点同样被护栏 422——跨模块契约矛盾,证据交 TL);端点本体由 DC-API-09 覆盖 R2: 复跑 PASS:导出 PDF → export 端点 200(无护栏拦截) R3: 重跑 PASS:导出 PDF → export 端点 200 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-UI-11 | 右栏拖宽 | 桌面视口 | 按住 .bench-resizer 左缘拖动 → 松开 → 刷新 | 宽度实时跟随;clamp 于 300px~60vw;松开后 localStorage[pa-bench-width] 写入;刷新后宽度保持 | 拖宽实测:向左拖 120px 后宽 > 初始宽且 ≤60vw;localStorage pa-bench-width 写入;刷新后宽度保持(±0 舍入) R2: 复跑 PASS:拖宽+持久化 R3: 重跑 PASS:拖宽+持久化无回归 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-UI-12 | 折叠手柄仿左栏 | 桌面视口 | 折叠右栏 → 检查手柄 → 展开 | 折叠态保留 28px 竖条点击区,按钮 26×26 圆角/hairline 描边/半嵌左缘,图标 13px;折叠朝左展开朝右(镜像左栏);宽度变化与手柄位置 0.3s 过渡;状态入 localStorage[pa-bench-collapsed] | 折叠实测:P3 AppShell 手柄(.bench-wrap > .bench-handle-btn)可见;折叠后 app-shell 带 bench-collapsed 类、resizer 隐藏、localStorage 写入;再点展开恢复 R2: 复跑 PASS:折叠手柄 R3: 重跑 PASS:折叠手柄无回归 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-UI-13 | 聊天输入触发导出 | 存在活动文档 | 聊天框输入「导出成PDF」发送;再试「转成PDF」;无活动文档时输入「导出成PDF」 | 有活动文档:不出现 generate 请求,走 export 意图分支并触发导出下载(30 条 #14/#27 的 UI 表现);无活动文档:走 QA 原路径不触发导出(#16) | 有活动文档时聊天框「导出成PDF」→ export 分支实测:出现 export 请求且无 generate 请求(分流正确,30 条 #14 的 UI 表现);无活动文档路径由意图 30 条 #16 覆盖 R2: 复跑 PASS:导出分支无 generate R3: 重跑 PASS:导出分支分流无回归 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
### 3.16 DC-REGRESSION(回归清单,4 条)
> 执行:tests/docgen/run_regression.md(Playwright 既有套件 + 人工核对,不依赖 docgen 新功能也可执行)。前置:TL 部署前端与 dev BFF;mock 场景可参考 tests/e2e/mock-scenarios.spec.js。
| 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 |
|---|---|---|---|---|---|---|---|---|---|
| DC-REGRESSION-01 | 三模式问答 | dev 前端 | 问答/方案/检索三模式各发一问 | 问答与方案模式出答案块;检索模式只出来源不出答案(FR-CHAT-006 无回退);请求体字段正确 | 三模式实测:检索模式发 /api/qa/search 非流式请求(PASS);方案模式流式请求体 native_chain_enabled=true/max_hops=3 且 pipeline 终态 done(真实上游 ~90s)(PASS);问答模式正常流由历史会话恢复验证(UI-04 同路径),完整问答流(不断流)TL 冒烟已证实无损 R2: 复跑 PASS:检索模式 /api/qa/search 非流式;方案模式 native_chain 参数生效+终态 done R3: 重跑 PASS:检索模式非流式;方案模式参数+终态 done | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-REGRESSION-02 | 停止 | 流式进行中 | 点击「停止」 | 流关闭;当前消息置 error 标注「已停止」;已收到的块保留不丢;BFF 无悬挂请求 | 停止实测:流中点停止 → err-note「已停止」标注出现 + pipeline 块保留不丢(UI 行为 PASS);停止后发送按钮未恢复——停止触发了 BFF 崩溃(__Pyx_Coroutine_SendEx 断言,TL 已单独记录的既有问题,非本次引入),按 TL 口径不判 FAIL R2: 复跑 PASS(按 TL 口径):停止 → 已停止标注+块保留;按钮未恢复+BFF 崩溃=既有断流问题(证据 btnEnabled=false bffHealth=false,r1fix 构建同崩),每次已重启恢复 R3: 重跑 PASS(按 TL 口径):停止 → 已停止标注+块保留;按钮未恢复+BFF 崩溃=既有断流问题(证据 btnEnabled=false bffHealth=false),已重启恢复 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-REGRESSION-03 | 多会话 | ≥2 会话 | 切换会话/刷新 | 历史消息正确恢复(含 docgen artifact 块可重渲染);切换不串数据;无未捕获异常 | 多会话实测:历史会话(QA + docgen 两种)切换/刷新后消息正确恢复(REGRESSION-04 与 UI-04 均已验证历史恢复路径);docgen artifact 块重渲染并「打开预览」可用 R2: 复跑 PASS:多会话恢复 R3: 重跑 PASS:多会话恢复(UI-04/REGRESSION-04 同路径复验) | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
| DC-REGRESSION-04 | PipelineCard 旧消息回归 | 存在历史 QA 消息(无 capabilities 字段) | 打开该会话观察 pipeline 卡头部;与 docgen 消息对比 | 历史消息零渲染徽标区、无 JS 报错;仅 docgen 消息显示「文档生成」徽标 | 历史 QA 消息(无 capabilities 字段)pipeline 卡头部零渲染徽标(badge.cap 计数=0)、无 JS 报错;与 docgen 消息徽标(UI-05)形成对照 R2: 复跑 PASS:历史 QA 消息无徽标零渲染 R3: 重跑 PASS:历史 QA 消息无徽标零渲染 | PASS | R3 | 2026-09-03 | 测试 agent(QA P0) |
### 3.17 DC-PERF(批量导入压测,3 条)
> 执行:tests/docgen/run_perf_import.sh(前置:TL 已启动受控 vstore 实例)。数据源:tooling/scripts/docgen_import.py(dev2 交付)。样例文件放 tests/docgen/data/perf/(生成脚本 run_perf_import.sh --gen 自动生成占位 docx,明文只落 data/ 目录,vault 不落明文)。
| 编号 | 模块 | 前置 | 步骤 | 预期 | 实测 | 结论 | 轮次 | 日期 | 执行人 |
|---|---|---|---|---|---|---|---|---|---|
| DC-PERF-01 | 百级导入 | 受控 vstore;占位样例 100 个 | 批量 ingest 100 个(并发 8) | 全部成功(100/100),无 5xx;时耗记录于实测列 | 100/100 成功,耗时 91s(并发 8;样例 docx 占位) R2: 100/100 成功,耗时 131s(并发 8) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) |
| DC-PERF-02 | 原子索引切换 | 导入完成 | rebuild-index → 观察 index.json 替换过程 | 经临时索引 os.replace 原子切换,无半截索引窗口;索引行号==json 下标 | rebuild ok;index.json items=103 结构合法(原子切换) R2: rebuild ok;index.json items=205 结构合法 R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) |
| DC-PERF-03 | 导入后删源明文 | 导入完成 | 检查源目录 | 源明文目录已删除(--keep-source 未指定时);vault 仅 .enc + index.json(保密断言);--keep-source 指定时目录保留 | 导入工具自动删除源明文目录;vault 无明文(grep 无命中) R2: 源明文目录自动删除;vault 无明文(grep 无命中) R3: 未重跑组:后端零改动,沿用 R2 结果(TL R3 口径) | PASS | R2→R3沿用 | 2026-09-03 | 测试 agent(QA P0) |
---
## 4. 功能点覆盖声明(TL 核算依据)
| # | 计划功能点 | 对应用例 |
|---|---|---|
| 1 | 自然触发意图路由(前后端同词典同优先级) | DC-UNIT-INTENT-01~05、DC-INTENT-CASES-01~30、DC-UI-03/04 |
| 2 | 加密入库(服务器不落明文) | DC-UNIT-CRYPTO-01~04、DC-UNIT-VAULT-01~05、DC-E2E-CURL-02/09、DC-PERF-03 |
| 3 | vstore 独立服务 8094(全端点) | DC-API(库管理四端点)、DC-UNIT-CLIENT-01、DC-E2E-CURL-01/03/04 |
| 4 | 路径配置三形态 + 远程握手拒绝 | DC-CONF-01~05、DC-UNIT-BACKEND-01~03 |
| 5 | 能力徽标 | DC-UI-05 |
| 6 | 文档 TAB:预览/就地编辑/prompt 修改(含编辑结构保护) | DC-UI-06/07/08/09、DC-API-06/07/08、DC-UNIT-EDITOR-01(结构保护强化)/02/03 |
| 7 | 导出 PDF+DOCX+浏览器下载(attachment) | DC-UNIT-CONVERT-01~06、DC-API-09/10、DC-UI-10、DC-E2E-CURL-07/08 |
| 8 | 右栏拖拽改宽 + 手柄仿左栏 + 宽度持久化 | DC-UI-11/12 |
| 9 | 模版检索三档降级(向量→meta→最新8条) | DC-UNIT-RETRIEVAL-01~03 |
| 10 | vstore 宕机 BFF 兜底 | DC-FAILOVER-01~03、DC-UNIT-CLIENT-03、DC-UNIT-PIPELINE-03 |
| 11 | 批量导入(含导入后删源明文) | DC-PERF-01~03 |
| 12 | 回归(三模式问答/停止/多会话) | DC-REGRESSION-01~03 |
| 13 | vstore 各模块(crypto/parsing/embed/vault/retrieval/backend/api) | DC-UNIT-CRYPTO/VAULT/RETRIEVAL/BACKEND + DC-API-01~03 |
| 14 | docgen 服务层各模块(settings/client/parsing/llm/intent/md2docx/md2pdf/pipeline/editor) | DC-UNIT-CLIENT/INTENT/CONVERT/PIPELINE/EDITOR |
| 15 | 意图 30 条(前后端同一张表) | DC-INTENT-CASES-01~30 |
| 16 | 导出文件可打开断言 | DC-UNIT-CONVERT-01/03、DC-E2E-CURL-08、DC-UI-10 |
| 17 | 下载头 attachment + RFC 5987 | DC-API-10、DC-E2E-CURL-08 |
| 18 | 右栏宽度持久化 localStorage | DC-UI-11 |
| 19 | 保密断言:ls 只见 .enc + 明文 grep 无命中 | DC-UNIT-VAULT-02/03、DC-E2E-CURL-02、DC-FAILOVER-03、DC-PERF-03 |
| 20 | humanize 行保护(标题/表格/列表行原样、正文改写) | DC-UNIT-PIPELINE-08 |
| 21 | 制品配额与 TTL(507 QUOTA_EXCEEDED / 过期清理) | DC-API-15/16 |
| 22 | VSTORE_EMBED_URL 可配置(集群化预留) | DC-CONF-06 |
| 23 | prompt 触发导出(UI 表现,30 条 #14/#27) | DC-UI-13 |
| 24 | PipelineCard 旧消息无徽标回归 | DC-REGRESSION-04 |
**覆盖结论**:计划功能点清单 100% 覆盖(上表每行 ≥1 条用例,全部 131 条均落于上表,无孤儿用例)。计划「验证」章节若另含未列入上表的要求,由 TL 指定后补条。
---
## 5. 轮次记录(框架;每轮开始/结束汇总留痕,不覆盖历史)
### TL 端口授权与执行口径(2026-09-03 登记,R1 起生效)
- **R1 授权端口**:8090(部署后的 BFF,含 docgen)与 8094(部署后的 vstore)——部署完成后这两个端口即本轮被测版本,脚本目标指向它们(run_e2e_curl.sh 以 DOCGEN_E2E_BFF/DOCGEN_E2E_VSTORE 环境变量指向)。
- **永不触碰**:8091(QA 金标树)、8099(mock)、5180(vite dev,不动其进程;UI 组用 Playwright headless 自起服务或按既有 tests/e2e 配置执行)。
- **DC-FAILOVER 演练**:允许停 8094 的 vstore 进程;演练结束必须确认 vstore 已恢复(/api/vstore/health 200)才记录 PASS。
- **DC-UI/DC-REGRESSION 组**:优先用 Playwright headless 自动化(服务器 ui/ 有 playwright 与 tests/e2e 先例);纯视觉项截图存档,无法自动化的标记人工待 TL 验收时复核。
### R1
- 开始:轮次 R1 | 日期:2026-09-03 | 执行人:测试 agent(QA P0)| 环境:BFF 8090(本次构建,Cython 硬化,docgen 8 端点已挂载);vstore 8094(local 后端,VSTORE_ROOT=/home/xzq/5090/teamWorkspace/zh/plantai-rag-new-deploy-unified/docgen,vault/index/artifacts 三目录);vite 5180(ui 最新前端 v5.4.11);QA 金标 8091 / mock 8099 未动;已预置 2 条占位样例(da1c8edf… as_template=true 直接 ingest;134d331c… as_template=false 经 BFF 上传)
- 执行范围:域 2 全量(vstore/tests + tooling/scripts/test_docgen_import.py + tests/docgen/)+ sdk 域(TL 口径)+ run_all.sh 分组逐组(unit/api/intent/conf/e2e/perf/failover/ui/regression)
- 结束汇总(R1 完成,2026-09-03):
- 总数 131 | PASS 126 | FAIL 2 | BLOCKED 3
- 分组:域 2 pytest 177 passed;sdk 域 pytest 128 passed;意图 30 条(前后端同表双跑 60 断言全过);
DC-UNIT 47 PASS;DC-API 16 PASS;DC-CONF 6 PASS;DC-E2E-CURL 7 PASS/1 FAIL/1 BLOCKED;
DC-FAILOVER 3 PASS;DC-PERF 3 PASS;DC-UI 10 PASS/1 FAIL/2 BLOCKED(人工);DC-REGRESSION 4 PASS
- FAIL 明细:
1. DC-E2E-CURL-07(edit 步骤 422 INTENT_REJECTED):BFF 路由护栏矛盾——routers/docgen.py
_intent_guard 调 is_docgen_intent(text)(intent.py 内部 route(text, False, False)),
edit/export 类指令(「润色一下第一段」「把标题改成黑体」「导出文档 xx 为 pdf」)在
has_active_artifact 被硬编码 False 下永远 != "docgen" → 结构性 422;而写类+文档类型词
指令(「帮我改写这份总结」)→ 200。实测证据已收集(4 组指令对比 + export 端点)。
对应用例 DC-API-08/09 用 mock 护栏放行测得端点本体 OK;DC-UI-09/10 真机验证了
「失败可见」前端行为。根因在 dev2 护栏与 cases_intent.md 判定顺序不一致
(应传入 has_active_artifact=True,或对 edit/export 端点按 edit/export 语义豁免)。
交 TL 裁决修复;本 agent 不改预期。
2. DC-UI-08(就地编辑 contenteditable 未接线):dev3 DocPane 源码注释宣称块级
contenteditable(blur 序列化 serializeMd 代码已写),但编辑模式 DOM 实测无任何
contenteditable 属性/样式,serializeMd 不可达——就地编辑功能未实现。交 TL。
- BLOCKED 明细:
1. DC-E2E-CURL-08:依赖 07 的 export(被护栏 422)→ 未执行;download 契约已由
DC-API-10(mock 全量断言 attachment+RFC 5987)覆盖。
2. DC-UI-01/02:真实文件拖拽无法 headless 自动化(组件无 file input,CDP 无法向 drop
注入真实 File)→ 按 R1 口径标人工,待 TL 验收时按 run_ui_checklist.md 复核。
- R1 备注(实现缝隙/环境事实,不判 FAIL,交 TL):
a. BFF generate 响应契约不含 markdown 字段 → chat.sendDocgen 的 setContent 条件
(payload.markdown != null)不成立,生成后自动切「文档」TAB 显示「内容不可用」;
刷新后经「打开预览」按钮(docgen.open 拉取 content)正常。
b. 生成成功后立即点「打开预览」:docgen.open() 因 activeArtifact 同 id 短路 return,
不拉取 content(与 a 叠加)。
c. BFF 断流崩溃(__Pyx_Coroutine_SendEx 断言):R1 期间被 QA 流停止/中途关页触发 3 次,
均为 TL 已单独记录的既有问题(9月2日旧构建同崩溃);每次已用 bff_restart.sh 恢复
并 curl health 确认(R1 结束后 8090/8094 均健康)。
d. PDF 正文提取存在 CJK 兼容字形映射(如「落」→U+F8E4 区字形),非豆腐块;CONVERT-04
断言按「CJK 量级 + 无 U+FFFD」口径。
e. 导入工具 docgen_import.py 的 --vstore 需含 /api/vstore 前缀(工具按 {vstore}/ingest
拼接);DC-PERF 脚本已按此调整并全过。
f. FAILOVER-02 生成产物首节标题重复(「## 工作总体回顾」出现两次)——LLM 输出质量瑕疵,
非结构错误。
R1 结束时服务状态:BFF 8090 health ok;vstore 8094 health ok(count=104);未触碰
8091/8099/5180 进程。
- 域 2 pytest:177 passed(vstore/tests + tooling/scripts/test_docgen_import.py + tests/docgen/)
- sdk 域 pytest:128 passed(routers/test_docgen_api.py + services/test_docgen_*.py + test_db.py + test_sessions_api.py)
- 意图 30 条:后端 30/30 + 前端 node 30/30(同表双跑一致)
- DC-E2E-CURL:6 PASS / 1 FAIL(07 edit 护栏 422)/ 1 BLOCKED(08 依赖 07)/ 1 PASS(09 手动补跑)
- DC-PERF:3/3 PASS(100 导入 91s;index 103 条;源明文自动删除;vault 无明文)
- DC-FAILOVER:3/3 PASS(vstore 宕 BFF 兜底 + 恢复 health 200 + 保密断言复验)
- FAIL 明细:
1. DC-E2E-CURL-07(edit 步骤 422 INTENT_REJECTED)——BFF 路由护栏矛盾:routers/docgen.py 的
_intent_guard 调 is_docgen_intent(text)(services/docgen/intent.py 内部
route(text, False, False)),而 edit/export 类指令(如「润色一下第一段」)在无活动文档
参数下永远 != "docgen" → 结构性 422。实测证据:edit 指令「把标题改成黑体/润色一下第一段」
→ 422;「帮我改写这份总结/重新生成这份报告」(写类动词+文档类型词)→ 200;export 端点
「导出文档 xx 为 pdf」→ 422。对应用例 DC-API-08/09 用 mock 护栏放行测得端点本体 OK,
DC-E2E-CURL-07/08 与 DC-UI-09/10 在真机被护栏拦截。此为实现层契约矛盾(dev2 护栏
与 cases_intent.md 判定顺序不一致——护栏应传入 has_active_artifact=True 再判定,或对
edit/export 端点豁免),已把证据列在报告 R1 记录,交 TL 裁决;本 agent 不改预期。
2. 生成质量观察(不判 FAIL,记备注):FAILOVER-02 生成产物首节标题「## 工作总体回顾」
重复出现两次(非结构错误,属 LLM 输出质量瑕疵)。
- R1 备注(环境/口径):
a. PDF 正文提取存在 CJK 兼容字形映射(如「落」提取为 U+F8E4 区兼容字形),非豆腐块;
DC-UNIT-CONVERT-04 断言按「CJK 量级 + 无 U+FFFD」口径(R1 调整记录在用例实测列)。
b. 浏览器组 UI-01/02(真实文件拖拽)无法 headless 自动化(组件无 file input,CDP 无法
向 drop 注入真实 File)→ 标人工待 TL 验收时复核。
c. 导入工具 docgen_import.py 的 --vstore 需含 /api/vstore 前缀(工具按 {vstore}/ingest 拼接);
DC-PERF 脚本已按此调整。
### R1 环境事实登记(TL 通知,非 bug)
1. 「客户端中途断流 → BFF 崩溃(__Pyx_Coroutine_SendEx 断言)」是既有问题(TL A/B 证实:9月2日旧构建同崩溃,非本次引入)。R1 回归组避开「故意断流」场景,正常流/停止按钮按既有行为测;该问题 TL 已单独记录,不纳入本轮闭环。
2. bff_restart.sh / vite_restart.sh / vstore_restart.sh 的 12s/8s 等待窗口在本机可能误报 FAIL(实际服务已起)——R1 若遇脚本报 FAIL 先 curl health 确认再记。
3. 导入工具 docgen_import.py 的 rebuild-index 与主库已连通,DC-PERF 可正常测。
4. TL 冒烟已证实:vstore health 200;ingest 成功且 vault 只见 .enc、明文 grep 无命中(保密断言真机通过);BFF→vstore 透传入库成功;完整 QA 流走到底(生产问答主链路无损)。
### R2
- 开始:轮次 R2 | 日期:2026-09-03 | 执行人:测试 agent(QA P0)| 环境:BFF 8090=R1 修复版已重编译部署(dev2 护栏 + dev1 标题去重;TL 真机验证:写文档意图 200 自动命中模版 sample_059、非 docgen 422、export 无护栏、非法 format VALIDATION);前端 5180=vite dev 已热更新含 dev3 修复(contenteditable 接线 + 内容拉取 + 短路修复),UI 执行前刷新页面;vstore 8094 未变(count=104);备份 /home/xzq/backups/docgen-r1fix-20260903-1706
- 执行范围:全量复测 131 条(R1 三个修复触碰 generate/edit/export/pipeline/DocPane 共享路径);R1 FAIL/BLOCKED 预期翻转:E2E-CURL-07/08 → PASS、UI-08 → PASS;UI-01/02 维持 BLOCKED 待 TL 人工验收;断流崩溃口径不变(既有问题,触发即记录不判 FAIL)
- 结束汇总(R2 完成,2026-09-03):
- 总数 131 | PASS 128 | FAIL 1 | BLOCKED 2
- 分组:域 2 pytest 177 passed;sdk 域 pytest 135 passed;意图 30 条(前后端同表双跑一致);
DC-UNIT 47 PASS;DC-API 16 PASS;DC-CONF 6 PASS;DC-E2E-CURL 9 PASS(R1 的 07/08 FAIL/BLOCKED 已翻转);
DC-FAILOVER 3 PASS;DC-PERF 3 PASS;DC-UI 10 PASS/1 FAIL/2 BLOCKED;DC-REGRESSION 4 PASS
- FAIL 明细(1 条):
DC-UI-08(就地编辑保存不可达):R1 的 contenteditable 未接线缺陷已由 dev3 修复(实测 28 块可编辑),
但保存触发仍不可达——DocPane 模板把 @blur="onEditBlur" 挂在 editRoot 根 div,blur 事件不冒泡,
子块失焦永远触发不到监听器(根 div 自身从不获得焦点)。气密诊断证据:
realBlockBlurPATCH=0(真实用户流程无 PATCH),rootBlurDispatchPATCH=1(对根 div 直接派发 blur 时
serializeMd→scheduleSave→PATCH 链路完好)。修复建议:改 @focusout(冒泡)或对每个子块挂 blur。
交 TL。测试证据:tests/e2e/docgen-ui.spec.js DC-UI-08 用例 + console 证据行。
- BLOCKED 明细(2 条):DC-UI-01/02(真实文件拖拽无法 headless 自动化,维持待 TL 人工验收)。
- R2 备注:
a. R1 三个修复验证:护栏(E2E-07/08、UI-09/10 真机 200);标题去重(E2E-06 产物 82 标题 82 唯一,
R1 的 FAILOVER-02 重复标题现象未再出现);contenteditable 接线(UI-08 实测 28 块)。
b. dev3 BUG-3/4 修复验证:自动切 TAB 后预览直接可用(UI-07 无需刷新绕行)。
c. 断流崩溃既有问题:R2 期间触发 1 次(REGRESSION-02 停止),r1fix 构建同崩,按 TL 口径记录不判 FAIL;
每次已用 bff_restart.sh 恢复(脚本 8s 窗口误报 FAIL 时以 curl health 为准)。
d. R2 结束服务状态:BFF 8090 health ok;vstore 8094 health ok(count=205);未触碰 8091/8099/5180。
### R2 准备记录(2026-09-03,TL 审阅 R1 修复后的域 2 复核)
1. dev2 已修复意图护栏:_intent_guard 改为直接调 dev1 的 route 三参判定
(generate: route(desc,False,False)=="docgen";edit: route(inst,False,True) in ("docgen","edit");
export 无护栏,format 由 pydantic Literal 白名单校验)。
2. 我的验收脚本适配(只改测试数据,预期断言不变):
- _make_artifact 描述 "一份测试文档" → "写一份测试报告"(写类动词+文档类型词,符合冻结词典);
- DC-API-16 TTL 触发生成描述 "触发清理" → "帮我写一份年度工作总结";
- DC-API-06 的 markdown.startswith 断言同步为 "# 写一份测试报告";
- DC-API-04 的护栏拒绝用例维持 "今天天气怎么样"(按冻结表,未动)。
3. 域 2 复核结果:177 passed(vstore/tests + tooling/scripts/test_docgen_import.py + tests/docgen/)。
4. 本 agent 不 commit/push,等待 TL 部署完成后发 R2 正式启动令。
(后续轮次追加,模板同上。)
---
## 6. 待 TL 裁决的边界问题(v1.1:TL 已裁决,见各条「裁决」行)
1. **下载/导出端点的意图护栏口径**:任务书 P2 只列 generate/edit/export 带意图护栏,未提及 content 保存(PATCH)/download。建议:PATCH content 与 download 不做意图校验(内容保存与下载不应被护栏拒绝),但 export 的 format 参数必须白名单校验。若 TL 希望护栏全端点覆盖,请回写口径,我在本报告与 API 用例中同步修改。
**裁决**:同意本建议——PATCH content 与 download 不做意图校验;export 的 format 白名单校验维持(pydantic Literal 已实现)。报告无需改动。
2. **下载端点是否要求「产物已导出」**:DC-API-10 假定 download 不依赖前置 export(artifact 落盘即可下载)。若契约是「必须先 export 生成 download_url 才能 download」,请 TL 指定,我改前置与步骤。
**裁决**:正式契约为「download 直接服务磁盘上该格式文件;md 在 generate 后即存在,pdf/docx 在首次 export 前不存在 → 404(带明确 message)」。DC-API-10 前置「产物存在」与契约一致,无需改动。
3. **受控实例端口**:DC-CONF-03/04(Remote 握手)与 DC-FAILOVER、DC-E2E-CURL、DC-PERF 需要「非生产端口的 dev BFF/vstore 实例」,由 TL 启动并告知端口(脚本读环境变量,默认占位 8094/8090 需 TL 确认后执行)。执行前请在轮次记录 R1 环境栏写明端口。
**裁决**:P4 部署时 TL 启动 dev 实例并写入 R1 环境栏(端口届时告知);脚本环境变量读取机制正确。
4. **30 条意图用例的自动脚本归属**:后端 pytest(test_intent_cases.py)与前端 node 脚本(test_intent_frontend.mjs)均落在 tests/docgen/ 下;dev1/dev3 自测应使用他们自己的脚本(cases_intent.md 同一张表)。本组脚本只作验收复核,不替代开发自测。
**裁决**:同意,意图双跑脚本只作验收复核。
5. **DC-PERF 导入工具依赖**:tooling/scripts/docgen_import.py 由 dev2 交付;若交付晚于首轮轮测,DC-PERF 标记 BLOCKED(原因=工具未交付),不计 FAIL。
**裁决**:导入工具已交付(dev2 已含 docgen_import.py),DC-PERF 正常测,无 BLOCKED 口径。
### R3
- 开始:轮次 R3 | 日期:2026-09-03 | 执行人:测试 agent(QA P0)| 环境:前端 5180 vite 热更新已含 dev3 R2 修复(focusout 冒泡监听 + relatedTarget 防块间误触发 + 焦点恢复 + 退出 flush);BFF/vstore 未变(后端零改动)
- 执行范围:定向复测——DC-UI 可自动化 10 条 + DC-REGRESSION 4 条全量重跑;其余组未重跑(沿用 R2)
- 结束汇总(R3 完成,2026-09-03):
- R3 重跑项:DC-UI 10/10 PASS(含 DC-UI-08 三阶段:真实失焦 PATCH 翻转、块间零误触发、刷新持久);DC-REGRESSION 4/4 PASS(02 按既有断流口径)
- 全量折算(R3 重跑 14 条 + R2 沿用 117 条 = 131 条口径):**PASS 129 | FAIL 0 | BLOCKED 2**
- BLOCKED 明细(2 条,维持):DC-UI-01/02(真实文件拖拽无法 headless 自动化,转 TL P5 人工验收)
- R3 备注:
a. DC-UI-08 R2 FAIL 已由 dev3 focusout 修复翻转(气密证据:R2 realBlockBlurPATCH=0 → R3 真实失焦 PATCH ≥1);
块间焦点切换(relatedTarget 在编辑区内)零误触发、刷新持久均实测通过。
b. DocPane 改动对 UI-06/07/09/10 无回归(R3 全组重跑通过)。
c. 断流崩溃既有问题 R3 触发 1 次(REGRESSION-02 停止),按口径记录不判 FAIL,已重启恢复;
R3 结束 BFF 8090 health ok、vstore 8094 health ok(count=205)。
- 开始:轮次 R3 | 日期:____ | 执行人:____ | 环境:____
- 执行范围:____
- 结束汇总:____
---
## 最终验收(Team Leader,2026-09-03)
**验收人**:Team Leader(本会话主 agent)
**验收日期**:2026-09-03
**验收结论**:**通过**(131 条终态:PASS 129 + 2 BLOCKED 由 TL 翻转 = 131/131)
### TL 亲自执行的验收项(逐条)
| 项 | 执行方式 | 结果 |
|---|---|---|
| curl 端到端全链 | 生产 8090 真实调用:content 读取 → edit(真实 LLM,标题改「水稻育种进展年度报告」生效)→ export docx/pdf(download_url 正常)→ download(attachment + RFC 5987 filename*)→ python-docx 打开(16 段、标题正确)+ pymupdf 打开(1 页、中文可提取) | PASS |
| 意图 30 条双跑 | 前端 node 30/30 PASS;后端 pytest 30/30 PASS(执行口径:PYTHONPATH=sdk + -c tests/docgen/pytest.ini) | PASS |
| 路径配置三形态 + 握手拒绝 | tests/docgen/test_docgen_conf.py 7 passed(含受控假远端) | PASS |
| vstore 宕机兜底演练 | TL 亲自停 8094 → BFF 上传返回 UPSTREAM_DOWN(degraded=true,chips 可标红重试)→ 生成无模版仍正常完成(template_name 空)→ BFF 健康不受影响 → 重启 vstore 后上传复验通过(count=206) | PASS |
| DC-UI-01 拖拽上传(原 BLOCKED) | TL 用 Playwright 官方 DataTransfer 模式(真实文件内容)验收:dragover 高亮 drop-active → drop 真实上传 201 → chip 出现 → 点移除消失 | **PASS(翻转)** |
| DC-UI-02 超额预检(原 BLOCKED) | 21MB 文件 drop → 上传请求数 = 0(前端预检拦截,不发请求) | **PASS(翻转)** |
| 浏览器其余 UI 项(03~13) | QA R3 Playwright 全组重跑 10/10 + REGRESSION 4/4 | PASS(沿用 R3) |
| 批量导入压测 | QA DC-PERF 100 条导入成功、删源明文、vault 无明文 | PASS(沿用 R2) |
### 验收期间环境事实(不判 FAIL,留档)
1. 断流崩溃(Cython 3.2.8 既有问题):验收期间未由 TL 操作触发;QA 三轮触发均按口径记录。**根治已立项**(任务 #15,用户指令:doc_writer 完成后立即开始)。
2. bff_restart.sh / vite_restart.sh / vstore_restart.sh 等待窗口在本机偶发误报 FAIL(实际服务正常),不影响结论。
3. 后端意图 30 条测试执行需 PYTHONPATH=sdk(QA 执行口径正确,已在 R 轮次记录)。
### 交付物确认
- 功能:自然触发文档生成、加密入库(vault 仅 .enc、明文 grep 无命中)、vstore 独立服务 8094(路径可配置 file/URL)、能力徽标、右侧文档 TAB(预览/就地编辑/prompt 修改/导出 PDF+DOCX 浏览器下载)、右栏拖宽+手柄仿左栏、模版检索三档降级、宕机兜底、批量导入(并发 8 + 导入后删源明文)
- 测试资产:tests/docgen/ 131 条用例 + 可复跑脚本 + 三轮留痕 + TL 验收记录
- 纪律:QA 树零改动、红线文件零改动、未 push git、备份完整(docgen-20260903-1425 / docgen-r1fix-20260903-1706)
---
# 手工测试第一批修复(2026-09-04,用户手工测试反馈)
**修复项**:问题1 上传去限制+进度展示;问题2 模板 HTML 所见即所得预览+按需导出(PDF 优先);问题3 检索 TAB 对话中实时更新。
## 服务端测试(本轮新增/变更,2026-09-04)
| 项 | 执行方式 | 结果 |
|---|---|---|
| 新增 DC-UNIT-TPL-01~08(内置模板注册表/分类/render_html/style_hints 覆盖/异常安全/css Story 安全门禁/wrapper 注入点/表格类) | pytest tests/docgen/test_templates.py | PASS 8/8 |
| 新增 DC-UNIT-WEB-01~06(bing 解析/网络失败/超大页/标题不足/gb18030/环境闸) | pytest tests/docgen/test_websearch.py(MockTransport 离线) | PASS 6/6 |
| 新增 DC-UNIT-PIPELINE-09~11(html/css/template_key/outline_source 产出;web 大纲种子;template_style css 覆盖) | pytest tests/docgen/test_unit_acceptance.py | PASS 3/3 |
| 新增 DC-UNIT-CONVERT-07(模板 HTML→PDF 中文可提取 + 红头;lang 字体陷阱回归门禁) | pytest | PASS |
| DC-UNIT-VAULT-10 翻转(21MB 入库成功 + EMBED_BODY_CAP 断言) | pytest | PASS(行为变更) |
| DC-UNIT-VAULT-06b 翻转(301 页 PDF 可解析) | pytest | PASS(行为变更) |
| DC-UNIT-CLIENT-05 翻转(上传限制常量已移除断言) | pytest | PASS(行为变更) |
| 后端全量回归 | vstore/tests + test_unit_acceptance + test_intent_cases + test_templates + test_websearch | **176/176 PASS** |
| 前端全量回归 | ui npm test(vitest 18 文件) | **173/173 PASS** |
## TL 亲自验收(2026-09-04)
| 项 | 执行方式 | 结果 |
|---|---|---|
| 大文件上传无限制 | 25MB txt 经生产 8090 上传 → 201、size 正确回填(vstore 20MB/BFF 100MB 门槛均已移除);8 文件并发上传 | PASS |
| 真实 LLM 生成(模板) | 「写一份实验室安全管理的通知」→ template_key=official、has_html=true、outline_source=library | PASS |
| GET /html | 红头/doc-title/css 齐全、内容正确 | PASS |
| PDF 导出(预览同源) | export pdf → %PDF 魔数 → pymupdf 提取:红头/正文/表格全部可读(lang 字体陷阱已修) | PASS |
| 浏览器验收脚本 | Playwright 真实浏览器(服务器 chromium-1148):模板渲染/编辑锁定/就地编辑保存回显/PDF 按钮优先/导出完成(R1 首跑 13/15,红头锁定与脚本 bug 修复后 R2 复跑) | 见下轮 |
| 检索 TAB 实时更新 | QA 流式期间采样卡片计数递增(修复:chat.js:560 raw ctx.events.push → 响应式代理推入) | 见下轮 |
## 环境事实(2026-09-04 新增,留档)
1. **markdown-it-py commonmark 预设不启表格**:`.enable("table")` 前 | 表格在 HTML 渲染/PDF 里是纯文本段落——本次一并修复(模板渲染与 PDF 导出的表格依赖它)。
2. **MuPDF Story 的 `<html lang="zh-CN">` 字体陷阱**:lang 属性触发按语言选字体,中文提取乱码(浏览器预览正常所以此前无感)——PDF 路径剥离 lang(DC-UNIT-CONVERT-07 回归门禁)。
3. **MuPDF CSS 注释解析**:含 CJK 全角标点的注释会报语法错误——PDF 路径适配时剥注释。
4. **PDF 体积遗留项**:Noto TTC 全量嵌入导致 PDF ~40-58MB(老路径同样存在),需 fontTools+pycffsubr 子集化(venv 未装)——列 V1.5。
5. 库内检索命中优先级:as_template 压测样例会优先命中为模版(设计行为:库内 > 联网 > 自设计);联网路径由 DC-UNIT-WEB 离线 mock + 服务器真实 bing 可达性实测覆盖。
6. 本会话 in-app Browser 不可用(无 browser 面板),浏览器验收用服务器 Playwright 替代。
### 浏览器验收 R2(2026-09-04 下午,复跑+独立验证)
| 项 | 结果 |
|---|---|
| 问题2 全链(15 项:模板渲染/红头/css 注入/编辑锁定/就地编辑保存回显/PDF 优先按钮/PDF 导出完成) | **15/15 PASS**(R1 的「红头区锁定」FAIL 已修:bodyFragment 只取 body 内层 HTML、shadow 内无 <body> 元素导致锁定逻辑未执行 → 改以 shadow 顶层 children 为锁定基准) |
| 问题3 检索 TAB 实时更新(独立脚本,真实 QA 流) | **PASS**:采样序列 3s:1 → 18s:3 → 24s:6 → 30s:7,对话进行中卡片实时递增(修复:chat.js raw ctx.events.push → 经 streamCtx 响应式代理推入)。R1 报告的两个 FAIL 为验收脚本选择器错误(.stage-card 不存在,真实 DOM 为 .tool-card),非产品缺陷。 |
| 无控制台错误 | PASS |
## 手工测试第二批修复(2026-09-04 晚)
### 用户报的问题(5 个)
1. 预览区输入修改 prompt「底部的公司名称和日期居右」点修改报「修改失败」
2. 导出按钮点击后变成「下载」按钮,未保持导出动作
3. 下载文件名用整段 prompt 命名,要求核心意思命名(如「十一放假通知」)
4. 编辑模式手动调整文字后失焦/切回预览,内容被还原
5. 已开一个预览时再点另一文档「打开预览」,面板白底无内容
### 根因与修复
| 问题 | 根因 | 修复 |
|---|---|---|
| 1 | 意图护栏 edit 路径逐词判路由,「…居右」不含编辑词典词被判 qa → 422 INTENT_REJECTED | edit 端点护栏移除(被调用本身即活动文档上下文);generate 护栏不变 |
| 2 | runExport 成功后写 exportUrls → 按钮变「下载」再点击只 window.open 旧文件 | 删除 exportUrls 形态;按钮恒为「导出 PDF/DOCX」;每次 POST export 重新生成;导出前 flushSave 保最新内容 |
| 3 | download 端点用 title(=prompt 截断 60 字)命名 | db 增 file_name 列;pipeline 增 name_for_prompt 规则提炼(礼貌词/动词/量词剥离 + DOC_TYPE 词典匹配 + 主题修饰词吞并);generate 落库;download 用 file_name 兜底 title |
| 4 | 失焦序列化后立即用旧 html 重建 shadow 抹掉编辑;切预览不等保存(800ms 防抖) | 有变化时不重建 shadow(保存后 fetchHtml 刷新);exitEdit flushSave 立即 PATCH;防抖 800→350ms |
| 5 | open(B) 时旧 html 残留 + loading 把宿主 v-if 卸载重建 + watch 守卫 !docgen.html 跳过 + fetch 无序号竞态 | open 清 html/css/letterhead/exportError;fetchContent/fetchHtml 归属序号防竞态;renderNonce 兜底 html 拉取失败;watch(md) 无条件重渲染 |
### 验证
- 后端 pytest:53/53 全绿(新增 PIPELINE-09 file_name 断言 + 09b 规则提炼)
- 前端 vitest:173/173 全绿
- curl 端到端(外网 8.163.92.8 → 回环):
- generate(用户原 prompt)→ file_name「十一放假通知」、letterhead「三亚海古纪生物科技有限公司」
- export pdf/docx → 200;download Content-Disposition = 「十一放假通知.pdf/.docx」
- edit「把底部的公司名称和日期居右」→ ok:True,md 尾部含右对齐落款
- 浏览器实测(隧道 15180→服务器):问题 1 修改按钮无错误且落款右对齐;问题 2 导出中「导出中…」→ 回到「导出 PDF」无「下载」形态;问题 4 编辑文字后切预览内容保留;问题 5 连开两个预览均有内容无白底
- 遗留:PDF 40-58MB 字体子集化(V1.5,需 fontTools);散落文件 sdk/app/{routers-docgen.py,templating.py} 已清理(误 scp 产物)
### 部署
备份 docgen-fix2-20260904-2114;编译 29 模块;BFF 重启(0.0.0.0);UI dist index-C_6hvku5.js