Files
Agent-System/.doc/audit/2026-09-13-document-revision-editor-hydration-race.md
WeiCHEN 4baa1220ee docs(ops): close docrev UI audits after main+post-restart PASS
Record real Playwright ledgers, mark 20 audits and 9 remediations done, and leave management-implementation-plan as plan-only.
2026-09-23 04:00:49 +08:00

4.4 KiB
Raw Permalink Blame History

title, status, created, updated, severity
title status created updated severity
UI-06 编辑弹窗打开期间 hydration 换块 ID 导致保存静默丢稿 done 2026-09-13 2026-09-22 high

UI-06 编辑弹窗打开期间 hydration 换块 ID 导致保存静默丢稿

W3 UI-rerun 关闭(2026-09-22):status=done。真实 Playwright 账本 PASS:ui-acceptance-results.json(main UI-00..16)+ ui-acceptance-results-post-restart.json(UI-15)。任务 tip 已合入本地 dev;详见 short-term 与 测试报告。

现象

第十二轮真实 UI(schema 30,dev@4c465d2 基线):

  • 双击正文块、fill 标记、点击「保存修改」动作均成功,编辑器随后关闭
  • 30 秒内修订仍为 r0
  • 网络 trace 中 PATCH /api/v1/documents/.../content 数量为 0
  • 动作 monotonic:dblclick 611755–611807;fill 611860–611910;save 611917–611979
  • 恰在 fill 开始时,后台 GET /api/v1/docgov/documents/doc_proj6140_intake/content 于 611862.379 发起并完成 hydration

本轮不是选择器点错按钮(第七轮 save-selector),而是保存路径在客户端被静默短路。

根因分析

层 问题 后果
AST 身份 markdownToBlockAST / generateId() 明确 Math.random(),每次 parse 新建 blk_* 弹窗持有打开时的块 ID,当前 docAST 已是新 ID
hydrate 丢账本 fetchRevisionState 依赖被窄化为 Promise<{etag:string}>;buildControlledDocumentAST 之后没有 applyHeadBlockMap(rev.st.head_block_map) 服务端已有稳定块身份,前端每次 hydration 仍随机换 ID
保存 handleSaveBlockEdit 按 updated.id 在 docAST.rootBlock.children 查找;idx === -1 时 setEditingBlock(null) 后 return 关窗、不发 PATCH、无提示
hydration 触发 useEffect([session, sessionValidated, activeDocId, deliverables]) 在 lease/工作流同步改 deliverables 时重跑 fill 窗口打出新 GET
迟到响应 hydrate .then 无 generation / 编辑门闩 弹窗打开后或 PATCH 成功后仍可覆盖编辑基线(即使 ID 已对齐,也会回滚新正文)
会话绑定 只按块 ID 保存;activeDocId 可被 canonical/session/任务流改写,不经 handleSelectDoc 可能对另一份文档发 PATCH,或块 ID 碰巧对上
关窗续跑 编辑中拒绝/跳过的首次 hydrate,effect 依赖不再变化 关窗后可能永久停在 loading skeleton
切文档 handleSelectDoc 在编辑中静默 closeBlockEditor 草稿被丢;modal 虽 fixed inset:0 zIndex 100 挡住 SlimTabBar,但 chat/drawer 等仍会调 handleSelectDoc

deliverables 被 5s lease 同步、任务完成后 syncControlledDocsAfterTask、工作流列表合并(清 cache)以及 governance 事件改写。任一变化都会在编辑期间再拉正文并换块身份。

严重度

high

用户可见「已保存」(弹窗关闭),账本仍 r0。属于静默丢稿,不是提示后的失败。

关联整改

证据

  • 失败证据目录(根代理保留、本轮不得改/移/删/提交):.doc/test-reports/assets/2026-09-13-document-revision-runtime-ui/schema30-ui06-r-bump-noop-failure/
  • Playwright 断言:[data-testid="doc-rev-revision"] 30s 内仍为 r0

开放问题

  • 本轮只做前端门闩与单测/构建验证;未跑真实 UI-06,不得标 E2E 通过或任务 done
  • 关窗后的下一次 deliverables 驱动 hydrate 仍会重建块 ID(编辑会话已结束;缺 map 时仍随机,门闩已消费)
  • 首次 hydrate 在 editor_open 被拒后会卡 skeleton:可达(骨架块可双击,或清 cache 后编辑中跳过 GET)。关窗时 takeHydrationResume() 递增 hydrateResumeKey 重跑 effect。
  • modal 不能硬保证挡死所有 handleSelectDoc;编辑中切文档 fail-closed,不静默关窗。