Files
PlantAI-rag-new/docs/team/report_dev3.md
jerryandClaude Fable 5 8467dec3a8 fix(bff): 断流崩溃根治——SSE 取消链模块明文化 + 团队文档归档
客户端中途断开流时 Cython 协程 __Pyx_Coroutine_SendEx 断言 abort 整个 BFF。
证据链:3.2.8 崩 db.c / 3.3.0 仍崩(升级无效) / db 明文后迁移 sse_relay.c,
证明取消链上任意 Cython 协程均可触发。最终把 db.py/qa.py/sse_relay.py
加入编译白名单(明文),隔离实例断流探针 10/10 存活,生产同构建实测通过。
代价:3 模块失去编译硬化(无凭据内容),编译模块 30->27。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 21:13:56 +08:00

88 lines
12 KiB
Markdown
Raw Permalink 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.
# DEV3 P3 自测报告 —— 前端 UI(自然触发 + 文档 TAB + 能力徽标 + 右栏可拖宽)
- 日期:2026-09-03
- 归属:开发 agent-3(P3 前端 UI)
- 事实源:docs/team/brief_dev3_p3.md、docs/team/cases_intent.md、docs/docgen_team_context.md
## 一、完成模块清单(按任务书 A–L)
| 编号 | 文件 | 状态 | 说明 |
|---|---|---|---|
| A | ui/src/api/docgen.js | 新增 | uploadLibrary / generate / getArtifactContent / saveArtifactContent / editArtifact / exportArtifact / listLibrary / downloadUrl,全部经 http.js 封装(默认 600s 读超时兜底);端点契约按 P2 任务书 docgen.py 约定(dev2 开发中,联调后生效) |
| B | ui/src/i18n/docgen.js | 新增 | DOCGEN_STEPS 五步(解析资料/检索模版/规划大纲/逐节生成/导出文档,相位复用 PHASE_COLOR_VAR 键 planning/retrieval/evidence/generation/done)、CAP_BADGES 能力徽标文案、DG 全量新文案 |
| C | ui/src/intent.js | 新增 | 纯函数无 Vue 依赖,与后端 intent.py 同词典同规则(EXPORT_WORD/FORMAT_WORD/EDIT_WORD/WRITE_VERB/DOC_TYPE/DOC_TYPE_EXCLUDE 与 cases_intent.md 逐词一致);漏判安全(不命中一律 qa) |
| D | ui/src/stores/docgen.js | 新增 | 文档 TAB 状态:activeArtifact/md/editing/exporting/templateCandidates;不 import bench.js 状态,只调 bench.setActiveTab(动态 import 防循环依赖) |
| E | ui/src/components/chat/Composer.vue | 修改 | 拖拽上传(dragover 高亮遮罩 → drop 预检 20MB×5 → uploadLibrary 入库 → chips 可移除/失败可重试);发送按 intent.js 分流:docgen → chat.sendDocgen、edit → docgen store 预填指令+切 TAB、export → 导出请求、qa → 原 emit('send') 路径(附件注记进消息文本)。send() 原逻辑零改动 |
| F | ui/src/stores/chat.js | 修改 | 新增 newDocgenSkeletonMessage(kind:docgen,pipeline 块 capabilities:[{key:doc_writer,label:文档生成}],steps 五步)、collectQaCaps/injectQaCaps(QA 路径注入本次启用能力 chips:联网搜索/答案润色/Superpowers,label 取 settings store manifest)、sendDocgen(description, libraryIds)(用户消息落库→骨架→generate 600s→成功 pipeline done + artifact 块 + 持久化 + 自动切 bench.setActiveTab('doc'),失败 _errorMarker 标注可重试)、retry 对 docgen 消息同位置替换走文档生成路径。send/_runStream 零改动(仅骨架落库后一行 injectQaCaps) |
| G | ui/src/components/chat/PipelineCard.vue | 修改 | pc-head 加 capabilities 徽标区(v-for chip,复用 badge 视觉,渐变描边);block.capabilities 缺省时零渲染;标题支持 block.title 覆盖(docgen 显示「文档生成流水线」) |
| H | ui/src/components/chat/DocCard.vue | 新增 | 消息流内文档卡片:标题/模版名/「打开预览」(自足闭环:docgen store 加载 + bench 切文档 TAB,不向上游抛事件) |
| I | ui/src/components/bench/DocPane.vue | 新增 | 第 5 TAB 主体:markdown-it 渲染预览(复用 markdown.js 消毒);块级就地编辑(标题/段落/列表/表格行 contenteditable,blur 序列化回 md + debounce 800ms PATCH);「用 prompt 修改」输入行(edit 端点);导出按钮组(PDF/DOCX → download_url 下载,手动兜底) |
| J | ui/src/components/bench/BenchPanel.vue | 修改 | tabs 数组 = labels.BENCH_TABS + DOCGEN_TAB{key:doc,label:文档}(不碰 labels.js 红线);pane 链 v-else-if 挂 DocPane(两处模板改动,无逻辑改动) |
| K | ui/src/components/chat/AssistantMessage.vue | 修改 | 块渲染加 artifact 分支(DocCard) |
| L | ui/src/components/layout/AppShell.vue + ui/src/styles/base.css | 修改 | ① .bench-resizer 左边缘 8px col-resize 拖拽实时改宽(min 300px / max 60vw,mouseup 持久化 localStorage pa-bench-width,初始 380px,折叠态隐藏不拖拽,拖拽中禁用过渡跟手);② 展开/收起按钮改左栏 .side-handle-btn 同款(26×26 圆角、hairline 描边、surface-1 底、跨右栏左边缘半嵌 left:0+translate(-50%,-50%)、垂直居中、图标 13px);③ 箭头镜像左栏:折叠态朝左(指向内容侧)、展开态朝右;④ 折叠态 28px 竖条保留为点击区(bench-wrap 条身点击展开,按钮嵌其上同款式);⑤ 宽度变化与手柄位置联动(transition 0.3s 同左栏,.bench-resizing 时禁用过渡) |
## 二、自测结果
1. **intent.js 30 条用例(docs/team/cases_intent.md 表)**:30 / 30 通过。
测试方式:node --input-type=module 直接 import ui/src/intent.js 跑断言脚本(intent.js 保持纯函数无 Vue 依赖,脚本存于 /tmp/test_intent_dev3.mjs,未落仓库)。英文格式词已按事实源要求做大小写不敏感判定(PDF→pdf)。
2. **新 JS 文件语法**:node --check 全部通过(intent.js / api/docgen.js / i18n/docgen.js / stores/docgen.js / stores/chat.js)。
3. **.vue 单文件组件编译**:@vue/compiler-sfc 逐文件编译(script setup + template)7 个改动/新增 .vue 全部通过(Composer / DocCard / DocPane / PipelineCard / AssistantMessage / BenchPanel / AppShell)。检查脚本在 /tmp/vue_sfc_check.mjs,未落仓库。
4. **既有前端单测回归**:`npx vitest run` 全量 172 条 —— 171 通过,1 失败:
- 失败项:tests/frontend/unit/b3/benchpanel.test.js「四 tab 来自 labels.BENCH_TABS」期望 4 个 tab,实际 5 个(P3 按任务书 J 新增「文档」TAB 的必然结果,属任务书要求的行为变化)。
- b1 appshell.test.js 7/7 通过(旧 .bench-collapse-btn 保留在 DOM 仅 CSS 退役,折叠断言未破坏);
- b2 chat.store.test.js 19/19、b2 components.test.js 20/20 通过(send/_runStream 零改动 + 徽标区缺省零渲染已回归验证)。
## 三、已知限制
1. **后端联调未做**:docgen 端点契约按 P2 任务书编写(dev2 开发中)。api/docgen.js 的路径/字段(items[].library_id、artifact.artifact_id、download_url 等)待 BFF 联调后可能微调。
2. **前端到后端的 artifact 字段别名**:generate 响应可能返回 artifact_id 或 id,api 层已容错。
3. **pipeline 中间状态是简化模拟**:sendDocgen 等待 600s 同步 generate 期间,仅把 planning 步置 running(无法获得后端实时阶段);生成成功/失败后整体置 done/error。若后端后续支持进度回调,可在此扩展。
4. **附件 chips 的引用持久性**:用户消息落库仅 text(现有 messages 接口只有 text 字段),library_ids 放在前端消息 meta 上,会话刷新后不保留。重试时退化为无附件生成(不阻塞)。
5. **DocPane 就地编辑不支持全部 markdown**:序列化覆盖标题/段落/列表/表格/引用/代码块,其它行内格式(加粗/斜体/链接)在编辑序列化后会丢失(预览完整保留)。
6. **intent.js 的 exclude 过滤逻辑**:按事实源表的 30 条用例全部通过,但事实源未指定「exclude 词命中时若同时有 doc_type 应如何处理」的精确算法(现为:排除词优先否决,即便同时含文档类型词)。后端 dev1 实现若与我对齐无碍;若有差异需按用例表裁决。
7. **session_caps 未加载时能力徽标不显示**:QA 能力徽标依赖 settings store 已加载 manifest(现状是只有打开设置面板才 load)。若希望问答时徽标始终准确,需在会话创建/发送时触发 settings.load——涉及 settings store 调用时机,交 TL 裁决。
## 四、需要 TL 裁决/联动的问题
1. **b3 benchpanel.test.js 需更新**(测试 agent 归属):第 5 个「文档」TAB 是任务书 J 要求的行为变化,该断言「tabs 长度为 4」需改为 5 并追加文档 TAB 用例。
2. **B1 的 .bench-collapse-btn 保留方案确认**:旧折叠按钮保留 DOM 但 CSS display:none(为了不破坏 b1 appshell.test.js 的折叠断言)。这是「视觉退役但保留 DOM」的折中,若 TL 希望彻底移除并同步更新 b1 测试,请告知。
3. **右栏拖宽覆盖 laptop 档(1024–1279px)固定 340px 列宽**:P3 用 .app-shell 内联 grid-template-columns 覆盖了 responsive.css 的 laptop 档固定列宽(用户拖宽优先于档位默认收窄);B1 原「laptop 自动收窄 340px」的行为被替换为「保持 380px/用户宽度」。如需保留档位收窄行为,需 B1 联动确认。
4. **能力徽标显示时机**(见限制 7):settings store 的 load 时机是否要与会话创建/发送联动。
5. **artifact 块落库的持久化结构**:前端落库 artifact 块用 {type:'artifact', artifact_id, title, template_name, status};P2 的 schemas.py 定义了 ArtifactBlock 字段(artifact_id/title/template_name/status),前端已按此对齐,请 dev2 联调时确认无出入。
6. **api/docgen.js 端点契约确认**:uploadLibrary 返回 items[].library_id(P2 契约);若 dev2 实际实现返回结构不同,需按实际调整 docgen.js 与 Composer 的 chip 解析(集中在 api 层与 Composer.uploadOne 两处)。
## 五、TL 裁决跟进(2026-09-03 微补丁)
- 裁决 1/2/3/5/6:无需改动(测试 agent 更新 b3 测试、保留 DOM 折中接受、拖宽优先于档位收窄列入 R1 已知行为变化、契约已核实对齐)。
- 裁决 4 已实施:Composer.vue onMounted 增加「manifest 为空时静默 load 一次」(manifest 为空时 settings.load(sessionId),sessionId 取自 chat store,失败静默;不碰 send/_runStream 内部)。自测:@vue/compiler-sfc 编译 7/7 通过;b2 components.test.js + b1 appshell.test.js 27/27 通过。
## 六、R1 修复记录(2026-09-03)
TL 审阅 R1 结果定性 2 个 bug,已修复并自测:
1. **BUG-2(FAIL,已修)**:DocPane 编辑模式 contenteditable 未接线——原实现误以为 CSS 能加 HTML 属性(注释「由 :deep 样式承担」不成立),编辑模式实际不可编辑。修复:DocPane.vue 新增 `wireContenteditable()`,`enterEdit` 与 `onEditBlur` 重建后在 nextTick 遍历 editRoot.children 程序化设置 contenteditable="true"(h1-h6/p/pre/blockquote 给元素本身,ul/ol 给 li,table 给 tr),不改 renderMarkdown 全局。附带:onEditBlur 加 30ms 防抖,避免 blur→重建→focus 迁移的二次 blur 重复序列化。
2. **BUG-3(契约缝隙,已修)**:后端 ArtifactOut 无 markdown 字段,生成成功切 TAB 显示「内容不可用」。修复:docgen store 的 adoptArtifact 改为 `adoptArtifact(artifact, {initialMd})`——无本地 md 时自动调 getArtifactContent 拉取后再渲染;chat.js sendDocgen 成功路径把 payload.markdown(若有)作为 initialMd 传入。
3. **BUG-4(顺带修)**:open 同 id 短路不拉取。修复:同 id 且有本地内容且非 force 才复用;切换不同文档或 force 时清旧 md 再拉取(避免旧内容残留与误判)。删除废弃的 setContent(由 initialMd 取代)。
自测结果:
- node --check:chat.js / docgen.js / intent.js / api 层全部通过
- @vue/compiler-sfc 编译检查:7 个 .vue 全部通过
- vitest 全量:**173 / 173 通过**(测试 agent 更新后的 b3 用例含文档 TAB)
- R1 临时 mount 级自测(不落仓库,跑完即删):DocPane 编辑接线(h1/p/li contenteditable=true)、blur 序列化重建后仍接线、adoptArtifact 自动拉取、open 短路语义 4 条全部通过
## 七、R2 修复记录(2026-09-03)
TL 审阅 R2 结果定性 DC-UI-08 为修复不完整(真实 bug):根 div 挂 @blur 收不到事件——**blur 不冒泡**,子 contenteditable 块失焦时 onEditBlur 永远不触发(真实用户流程 PATCH 次数=0)。已修复并自测:
1. **事件监听**:`@blur` → `@focusout`(focusout 冒泡,根 div 可收到所有子块失焦)。onEditBlur 增加 relatedTarget 判断:`editRoot.contains(e.relatedTarget)` 时 = 块间焦点迁移,不序列化(防重建打断输入)。
2. **焦点恢复**:失焦块在顶层 children 的索引被记录,序列化重建后 nextTick 恢复焦点到同索引块(体验:焦点不丢失)。
3. **退出编辑 flush**:点「预览」退出时 focusout 定时器会因 editing=false 被跳过——exitEdit 现在同步序列化 flush 最后一次未保存编辑(不 flush 会丢字)。
自测结果:
- @vue/compiler-sfc 编译检查:7 个 .vue 全部通过
- vitest 全量:**173 / 173 通过**
- R2 临时行为测试(mount 级,不落仓库,跑完即删)3/3:
- 块间焦点切换(relatedTarget 在编辑区内)不触发序列化保存
- 失焦到编辑区外触发一次序列化 + PATCH(debounce 合并),重建后仍接线
- 退出编辑 flush 最后一次未保存编辑