Files
llm-wiki/docs/graph/2026-06-16-interaction-risk-audit.md
T
2026-07-12 21:26:08 +08:00

89 lines
12 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.
# Graph Interaction Risk Audit
日期:2026-06-16
适用计划:`docs/plans/2026-06-16-graph-six-layer-architecture-phased-plan.md`
## 审计目标
这份审计把本轮图谱重构要覆盖的交互风险集中记录下来。它不是普通 bug list,而是后续每个阶段的验收清单:每个风险都要明确期望行为、owner layer、验证方法和目标阶段。
核心原则:
- 图谱拥有自己的交互空间。节点、边、社区色块、空白画布上的滚轮、拖拽、hover、点击、键盘取消都先进入图谱交互链路。
- 浏览器默认行为只在明确允许的文本、输入、阅读区域发生。
- 命中、坐标、状态、渲染不能各算各的。
- 工作台和离线 HTML 是同等目标,不能只修其中一个。
## Owner Layer 约定
| Owner layer | 负责范围 |
|---|---|
| GraphGestures | 原始 pointer、wheel、keyboard、touch 事件;点击/拖拽/缩放意图;阻止浏览器默认行为 |
| SpatialIndex / GraphLayout | 节点、边、社区色块、空白图谱区域命中;布局边界;社区色块软边界 |
| GraphViewport | 世界坐标、屏幕坐标、小地图坐标转换;相机缩放、平移、fit、resize anchor |
| GraphState | hover、selection、active drag、pins、positions、viewport 等图谱运行状态 |
| GraphRenderer | 节点、边、社区色块、小地图、hover 卡片、工具栏、离线 reader 的绘制 |
| GraphFacade | workbench/offline 公开 API、宿主回调、持久化 pin、抽屉/选择语义协调 |
## 风险矩阵
| 风险 | 期望行为 | Owner layer | 验证方法 | 目标阶段 |
|---|---|---|---|---|
| Trackpad wheel 在图谱内被页面滚动或页面缩放抢走 | 鼠标或触控板停在节点、边、社区色块、空白画布上时,wheel 都只改变图谱相机;页面尺寸、页面滚动位置和浏览器缩放不变 | GraphGestures + GraphViewport | Node test 覆盖 wheel target policyworkbench/offline browser 脚本记录 transform 变化、root scroll、页面 viewport 指标 | Phase 2 |
| `ctrl/meta + wheel` 触发浏览器页面缩放 | 图谱拥有的 surface 内,pinch-like wheel 被图谱接管;输入框、阅读正文等 blocker 内不接管 | GraphGestures | Browser 脚本 dispatch cancelable wheel,记录 `visualViewport.scale``devicePixelRatio`、clientWidth 和 graph transform | Phase 2 |
| 鼠标在节点上滚轮不能缩放 | 节点命中优先,但节点上的 wheel 仍走 graph zoom,不因为节点可点击/可拖拽而阻断缩放 | GraphGestures + SpatialIndex | `gestures.test.ts` 覆盖 node target wheelworkbench/offline 真实节点上滚轮验证 transform 改变 | Phase 2 |
| 鼠标在社区色块上滚轮不能缩放 | 社区色块可点击选择整个社区,但 wheel 仍走 graph zoom;点击和滚轮互不抢语义 | GraphGestures + SpatialIndex | `gestures.test.ts` 覆盖 community target wheelcommunity wash browser regression | Phase 2 |
| 空白拖动画布时选中工具栏或页面文字 | active pan/drag 期间禁止 native selection;结束或取消后清理锁定状态 | GraphGestures + GraphRenderer | Browser 脚本拖动画布后断言 `window.getSelection().toString()` 为空、toolbar/search 文本未被选中、root dataset 无 stuck active | Phase 2 |
| 原生 drag selection 或页面 scroll 泄漏进图谱 | 图谱 owned surface 在 active gesture 期间阻止默认拖选、滚动和 scroll chainingroot scroll 被复位 | GraphGestures | Browser 脚本拖拽空白、拖节点、拖社区色块,检查 root scroll、document selection、body scroll | Phase 2 |
| 快速松开节点后节点回到原位 | drag end intent 必须带最终 pointer 坐标;即使最后一帧 move 没来得及提交,release 坐标也会提交成 pin | GraphGestures + GraphViewport + GraphState + GraphFacade | State machine test 覆盖 final screen pointbrowser 脚本快速 mouse down/move/up,检查节点位置和 pinned 状态 | Phase 2 |
| 节点拖拽不跟手或跳到别处 | 拖拽以 grabbed offset 为基准,屏幕点经 GraphViewport 反投影;节点中心不突然吸到鼠标中心,也不使用旧坐标公式 | GraphViewport + GraphState + GraphFacade | `simulation-bridge.test.ts` 覆盖 offsetbrowser 脚本记录 pointer 与节点中心距离上限 | Phase 3 |
| 节点被社区色块或固定世界尺寸锁住 | 社区色块是软视觉区域;节点可拖出色块;世界 bounds 由布局和 pin/outlier 推导,不用 `1000x680` 当硬笼子 | GraphLayout + GraphViewport | render model/community wash testscommunity wash browser regression 拖出初始 wash 并检查 pin | Phase 3 / Phase 6 |
| 社区色块无限追随远处离群节点 | 色块可受拖出节点影响,但有上限;不会变成覆盖全屏的巨大色块;社区成员关系不因拖拽改变 | GraphLayout + GraphRenderer | `community-wash.test.ts` 覆盖 outlier capbrowser 脚本检查 wash 大小上限和成员稳定 | Phase 6 |
| 点击社区和拖动社区阈值冲突 | 小位移点击选中社区并打开选区语义;超过阈值不触发社区点击,也不误启动节点拖拽 | GraphGestures + SpatialIndex | `gestures.test.ts` 覆盖 community move cancelbrowser 脚本 move past threshold 后 visible nodes 不变 | Phase 2 / Phase 6 |
| DOM stacking order 改变后命中不同 | 节点命中优先于边和社区;社区优先于空白;命中来源是 SpatialIndex,不是 `elementFromPoint` 或 DOM 层级 | SpatialIndex / GraphLayout + GraphGestures | 新增 `spatial-index.test.ts`,构造重叠节点/边/社区并打乱 DOM 顺序;renderer boundary test 禁止旧 DOM hit owner | Phase 1 / Phase 7 |
| Hover 简介位置漂移 | hover anchor 来自节点/边的世界坐标,经 GraphViewport 投影;zoom、pan、drag、resize、drawer open 后重新计算 | GraphViewport + GraphState + GraphRenderer | `overlays.test.ts` 扩展投影;workbench/offline browser 测 preview 和 anchor gap、viewport 内不溢出 | Phase 3 |
| Hover 卡片与节点不同步 | hover 状态存于 GraphStateRenderer 只绘制;节点位置更新后卡片 anchor 跟随同一状态快照 | GraphState + GraphRenderer | Browser 脚本在拖拽后 hover,记录卡片相对节点距离;state test 覆盖 hover snapshot | Phase 3 |
| Search 输入框边界误接管 | search 内 wheel、pointer、keyboard 不触发图谱 zoom/pan/shortcut;离开 search 后恢复图谱控制 | GraphGestures + GraphFacade | `gestures.test.ts` blocker 覆盖 searchbrowser 脚本打开 search 后滚轮不改变图谱 transform | Phase 2 |
| Toolbar/legend 边界误接管 | 工具栏和图例作为图谱控件:点击按控件语义走;wheel/pointer 不误触发画布 pan;空白点击可关闭 popover | GraphGestures + GraphRenderer | toolbar state testsbrowser 脚本覆盖 toolbar panel、legend row、blank close | Phase 2 |
| Minimap 边界误接管 | minimap 有自己的点击/导航语义;不被 blank pan 或 graph wheel 错认;minimap 投影只走 GraphViewport | GraphGestures + GraphViewport + GraphRenderer | viewport minimap testsbrowser 脚本 wheel over minimap 不 zoom graphminimap click/viewport rect 稳定 | Phase 3 |
| Drawer/reader 边界误接管 | workbench drawer 和 offline reader 内允许文本阅读/滚动/按钮;不触发 graph drag/zoom;图谱 anchor 不因 drawer resize 漂移 | GraphGestures + GraphFacade + GraphViewport | Browser 脚本 drawer wheel 不 zoom graphdrawer resize 后选中节点保持可见且 hover 不溢出 | Phase 2 / Phase 3 |
| 数据刷新时正在拖拽 | active drag 期间 diff queue 暂存或合并;释放后以最终 pin/position 重建 layout 和 SpatialIndex,不用旧 snapshot 覆盖用户拖拽 | GraphState + GraphFacade + GraphLayout | GraphDiffQueue tests 已有基础,扩展 active drag release 后 replaybrowser 可用 mock refresh 验证无回弹 | Phase 3 |
| Pointer cancel / lost pointer capture 后状态卡住 | 取消事件只清理 active gesture,不误提交 click/pinroot dataset、cursor、selection lock 都恢复 | GraphGestures + GraphState | `gestures.test.ts` 已覆盖基础,扩展 DOM cleanupbrowser 脚本 dispatch pointercancel/lostpointercapture 后继续可操作 | Phase 2 |
| Escape 行为混乱 | active drag/pan 优先取消当前手势;无 active gesture 时清理 hover/selection/drawer/search,具体语义由 GraphFacade 统一 | GraphGestures + GraphFacade + GraphState | State machine testworkbench/offline browser 脚本覆盖 drawer、selection、search、active drag | Phase 2 / Phase 4 |
| 键盘快捷键抢输入框 | search、drawer、text-control 内的 Tab/Enter/Space/Arrow/plus/minus/zero 不触发图谱快捷键;图谱 focus 内才响应 | GraphGestures + GraphFacade | gestures blocker testsbrowser 脚本聚焦 search/drawer 按键验证不改变 graph transform/selection | Phase 2 |
| Graph focus 不明确 | 点击/Tab 进入图谱后键盘归图谱;离开图谱或进入文本控件后键盘归宿主/浏览器 | GraphGestures + GraphFacade | Browser 脚本检查 focus ring、keyboard commands only under graph focus | Phase 4 |
| Touch 一指拖动画布和拖节点不稳定 | 支持 pointer events 的触摸:一指空白 pan、一指节点 dragpointercancel 清理;没有复杂多指编辑时提供按钮 fallback | GraphGestures + GraphViewport | Browser 脚本模拟 pointerType touchNode state machine test 覆盖 touch-like pointer sequence | Phase 2 |
| 双击或连续点击语义散落 | 双击 fit/reset 或其他图谱命令必须由 GraphGestures 发 intentRenderer 不直接绑定 root dblclick | GraphGestures + GraphFacade | Final cleanup `rg` 检查 static-renderer 不再绑定 root dblclickbrowser 脚本覆盖 reset/fit | Phase 5 / Phase 7 |
| 旧 renderer 继续偷偷拥有交互 | `static-renderer.ts` 终态只能是 composition/compat shell;不能保留 root wheel/pointer/dblclick/keydown/hit-test/coordinate owner | GraphFacade + GraphRenderer | Final cleanup `rg` 检查;renderer-boundary test 用 runtime contract 防止换文件名复发 | Phase 5 / Phase 7 |
| Workbench 与离线 HTML 行为分叉 | graph-engine 内的行为同源;workbench 只负责宿主抽屉和能力,offline 只负责离线 reader;核心交互测试两端都跑 | GraphFacade | `tests/graph-workbench-interactions.regression-1.sh``tests/graph-offline-phase-6.regression-1.sh` 同时通过 | Phase 7 |
## 当前覆盖与缺口
已有覆盖:
- `packages/graph-engine/test/gestures.test.ts` 已覆盖基础 target 分类、节点拖拽、社区点击取消、空白 pan、pointer cancel、Escape。
- `packages/graph-engine/test/viewport.test.ts` 已覆盖 zoom/pan/fit/minimap/resize anchor。
- `packages/graph-engine/test/simulation-bridge.test.ts` 已覆盖 dragged point under pointer 和 off-world drag 不被投影层 clamp。
- `packages/graph-engine/test/community-wash.test.ts``render-model.test.ts` 已覆盖社区色块 outlier cap、拖出后成员稳定。
- `tests/browser/graph-workbench-interactions.mjs``graph-offline-phase-6.mjs``graph-community-wash-interactions.mjs` 已有真实浏览器检查基础。
必须补齐:
- SpatialIndex 作为真实命中来源,而不是 DOM target 的接口包装。
- 图谱内 `ctrl/meta + wheel` 和 trackpad pinch-like wheel 不改变浏览器页面 zoom 的浏览器证据。
- 空白 pan、节点 drag 后无 native text selection 的浏览器证据。
- 快速 release 用最终 pointer 坐标提交 pin 的浏览器证据。
- hover preview 在 drag、zoom、pan、drawer resize 后仍锚定节点/边的浏览器证据。
- renderer boundary 检查,防止旧 `static-renderer.ts` 或新 renderer 文件重新接管交互。
## 阶段入口条件
Phase 1 之前必须满足:
- 本审计文档已提交。
- 进度账本记录 Phase 0 task 0.2 的验证和提交号。
- 不开始行为改动,直到 task 0.3 把现有测试与缺口映射到目标阶段。
后续每阶段完成时,progress 文件必须把本审计中的相关风险标成已有验证证据,不能只写“实现完成”。