forked from admin/PlantAI-rag-new
客户端中途断开流时 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>
12 KiB
12 KiB
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 时禁用过渡) |
二、自测结果
- 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)。
- 新 JS 文件语法:node --check 全部通过(intent.js / api/docgen.js / i18n/docgen.js / stores/docgen.js / stores/chat.js)。
- .vue 单文件组件编译:@vue/compiler-sfc 逐文件编译(script setup + template)7 个改动/新增 .vue 全部通过(Composer / DocCard / DocPane / PipelineCard / AssistantMessage / BenchPanel / AppShell)。检查脚本在 /tmp/vue_sfc_check.mjs,未落仓库。
- 既有前端单测回归:
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 零改动 + 徽标区缺省零渲染已回归验证)。
三、已知限制
- 后端联调未做:docgen 端点契约按 P2 任务书编写(dev2 开发中)。api/docgen.js 的路径/字段(items[].library_id、artifact.artifact_id、download_url 等)待 BFF 联调后可能微调。
- 前端到后端的 artifact 字段别名:generate 响应可能返回 artifact_id 或 id,api 层已容错。
- pipeline 中间状态是简化模拟:sendDocgen 等待 600s 同步 generate 期间,仅把 planning 步置 running(无法获得后端实时阶段);生成成功/失败后整体置 done/error。若后端后续支持进度回调,可在此扩展。
- 附件 chips 的引用持久性:用户消息落库仅 text(现有 messages 接口只有 text 字段),library_ids 放在前端消息 meta 上,会话刷新后不保留。重试时退化为无附件生成(不阻塞)。
- DocPane 就地编辑不支持全部 markdown:序列化覆盖标题/段落/列表/表格/引用/代码块,其它行内格式(加粗/斜体/链接)在编辑序列化后会丢失(预览完整保留)。
- intent.js 的 exclude 过滤逻辑:按事实源表的 30 条用例全部通过,但事实源未指定「exclude 词命中时若同时有 doc_type 应如何处理」的精确算法(现为:排除词优先否决,即便同时含文档类型词)。后端 dev1 实现若与我对齐无碍;若有差异需按用例表裁决。
- session_caps 未加载时能力徽标不显示:QA 能力徽标依赖 settings store 已加载 manifest(现状是只有打开设置面板才 load)。若希望问答时徽标始终准确,需在会话创建/发送时触发 settings.load——涉及 settings store 调用时机,交 TL 裁决。
四、需要 TL 裁决/联动的问题
- b3 benchpanel.test.js 需更新(测试 agent 归属):第 5 个「文档」TAB 是任务书 J 要求的行为变化,该断言「tabs 长度为 4」需改为 5 并追加文档 TAB 用例。
- B1 的 .bench-collapse-btn 保留方案确认:旧折叠按钮保留 DOM 但 CSS display:none(为了不破坏 b1 appshell.test.js 的折叠断言)。这是「视觉退役但保留 DOM」的折中,若 TL 希望彻底移除并同步更新 b1 测试,请告知。
- 右栏拖宽覆盖 laptop 档(1024–1279px)固定 340px 列宽:P3 用 .app-shell 内联 grid-template-columns 覆盖了 responsive.css 的 laptop 档固定列宽(用户拖宽优先于档位默认收窄);B1 原「laptop 自动收窄 340px」的行为被替换为「保持 380px/用户宽度」。如需保留档位收窄行为,需 B1 联动确认。
- 能力徽标显示时机(见限制 7):settings store 的 load 时机是否要与会话创建/发送联动。
- artifact 块落库的持久化结构:前端落库 artifact 块用 {type:'artifact', artifact_id, title, template_name, status};P2 的 schemas.py 定义了 ArtifactBlock 字段(artifact_id/title/template_name/status),前端已按此对齐,请 dev2 联调时确认无出入。
- 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,已修复并自测:
- 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 重复序列化。 - BUG-3(契约缝隙,已修):后端 ArtifactOut 无 markdown 字段,生成成功切 TAB 显示「内容不可用」。修复:docgen store 的 adoptArtifact 改为
adoptArtifact(artifact, {initialMd})——无本地 md 时自动调 getArtifactContent 拉取后再渲染;chat.js sendDocgen 成功路径把 payload.markdown(若有)作为 initialMd 传入。 - 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)。已修复并自测:
- 事件监听:
@blur→@focusout(focusout 冒泡,根 div 可收到所有子块失焦)。onEditBlur 增加 relatedTarget 判断:editRoot.contains(e.relatedTarget)时 = 块间焦点迁移,不序列化(防重建打断输入)。 - 焦点恢复:失焦块在顶层 children 的索引被记录,序列化重建后 nextTick 恢复焦点到同索引块(体验:焦点不丢失)。
- 退出编辑 flush:点「预览」退出时 focusout 定时器会因 editing=false 被跳过——exitEdit 现在同步序列化 flush 最后一次未保存编辑(不 flush 会丢字)。
自测结果:
- @vue/compiler-sfc 编译检查:7 个 .vue 全部通过
- vitest 全量:173 / 173 通过
- R2 临时行为测试(mount 级,不落仓库,跑完即删)3/3:
- 块间焦点切换(relatedTarget 在编辑区内)不触发序列化保存
- 失焦到编辑区外触发一次序列化 + PATCH(debounce 合并),重建后仍接线
- 退出编辑 flush 最后一次未保存编辑