Skip to content

fix(visualization): make nested A -> B arrow terminals normal to target border - #377

Merged
HansBug merged 19 commits into
mainfrom
fix/issue-376-elk-target-normal-entry
Jul 15, 2026
Merged

fix(visualization): make nested A -> B arrow terminals normal to target border#377
HansBug merged 19 commits into
mainfrom
fix/issue-376-elk-target-normal-entry

Conversation

@HansBug

@HansBug HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Merged status (2026-07-15)

PR #377 is merged into main; this block supersedes the pre-merge status block while preserving the historical implementation record below.

  • merge commit: 05c41569ba3e477252f5d9b5cd1d401a6b3f7f65
  • source HEAD: 10642556972ac0a987f3f141c61dcf6cb1711bf2
  • Delivered: ELK nested composite edgeNodeBetweenLayers=18, 10 fixture x TB/LR geometry gate, VSCode-like 50% right-pane visual/interaction evidence, and the complete Gist.
  • Final gate: fixed failures 0, minimum terminal 18px, crossing regressions 0; 160 full-shell PNGs, 80 drawer resize exercises, and 80 drawer drag exercises passed.
  • Pre-merge verification: Python SKIP_SLOW_TESTS=1 make unittest = 46279 passed, 747 skipped, 67 deselected; jsfcstm npm test = 697 passing; ELK targeted = 12 passing; VSCode production compile, boundary check, make rst_auto, and git diff --check pass.

Issue #376 is now complete and will be closed with the merge commit above. Unrelated mini-racer issue #89 remains open.

Merge progress: final validation complete

This is the final pre-merge status for HEAD 10642556972ac0a987f3f141c61dcf6cb1711bf2. The evidence below is authoritative; older hash-specific sections remain below for traceability.

  • PR state before merge: OPEN, non-draft, MERGEABLE/CLEAN; base main; no conflict with origin/main@971687ca5649cd01bf00239179e38ffda8b5e838.
  • Geometry: 10 fixtures x TB/LR = 20 layouts; baseline failures 15; fixed failures 0; minimum terminal 18px; crossing regressions 0.
  • VSCode-like right editor group: 50% pane across 1024x768, 1280x800, 1440x900, and 1920x1080; 160 full-shell PNGs; 80 drawer resize and 80 drag exercises.
  • Side effects: mean area delta 10.6956804842%; maximum 23.0576441103%; no visual crossing, overlap, or edge-through-node regressions.
  • Verification: Python SKIP_SLOW_TESTS=1 make unittest = 46279 passed, 747 skipped, 67 deselected; jsfcstm npm test = 697 passing; ELK targeted = 12 passing; VSCode production compile, boundary check, make rst_auto, and git diff --check pass.
  • Evidence: Gist, with report.json, verifier scripts, hashes, and FINAL-10642556-VALIDATION.md.
  • Merge action: squash merge is being executed now; remote branch deletion is disabled. Unrelated mini-racer issue 嵌入 jsfcstm 可视化 (SVG/PNG):mini-racer + WASM 栅格化 #89 remains open.

Authoritative current status (HEAD 1064255)

This block supersedes all older hash-specific text below.

  • HEAD: 10642556972ac0a987f3f141c61dcf6cb1711bf2; base: origin/main@971687ca; PR is non-draft and MERGEABLE/CLEAN.
  • Geometry gate: 10 fixtures x TB/LR = 20 layouts; baseline failures 15, fixed failures 0, minimum fixed terminal 18px, crossing regressions 0.
  • Right-pane gate: real VSCode-like 50% right editor group, four window sizes, 160 full-shell PNGs, 80 drawer resize exercises, 80 drawer drag exercises. Mean area delta 10.6956804842%, maximum 23.0576441103%.
  • Local tests: Python SKIP_SLOW_TESTS=1 make unittest = 46279 passed, 747 skipped, 67 deselected; jsfcstm npm test = 697 passing; ELK targeted test = 12 passing. VSCode production compile, boundary check, make rst_auto, and diff check pass.
  • Evidence: Gist includes report.json, source/bundle hashes, scripts, and FINAL-10642556-VALIDATION.md.
  • Current-head reviews: Claude, DeepSeek, and Codex all conclude C=0, I=0, READY TO MERGE; Codex records one non-blocking M test cleanup.

The historical sections below are retained for traceability.

Historical implementation record

状态

这是 issue #376 的实现与验证 PR,已完成实现与证据整理,当前已标记 ready for review,基于 main,不合并。最新修复提交为 9c505d0c

关联 issue: #376:保证 A -> B 箭头以可见法向终段垂直接入目标边框

已落地方案

  • 在每个 nested composite 重复设置 elk.layered.spacing.edgeNodeBetweenLayers = 18,并显式设置 elk.layered.spacing.edgeEdgeBetweenLayers = 32INCLUDE_CHILDREN 不会继承 canvas spacing。
  • 保留生产路径 elk.layout() -> smoothGraphEdges() -> renderSvg(),没有删除/旋转目标端折点。
  • 导出并测试 terminalApproach:端点落在四角容差范围直接失败,倒数第二点必须在目标框外,终段严格 >= 18px
  • 新增第 10 个嵌套交叉 fixture;测试按 fixture/direction 分桶,并单独统计 forced expansion,不再只有全局聚合阈值。

复现与资产

仓库内可执行生产后处理扫描器:editors/vscode/scripts/verify-preview-geometry.ts,入口:editors/vscode/scripts/run-preview-geometry.js

cd editors/jsfcstm && npm run build && npm test
cd ../vscode && npm run compile
PYFCSTM_GEOMETRY_OUTPUT=../../artifacts/preview-geometry npm run test:preview-geometry

脚本对 10 个 fixture、TB/LR 共 20 个布局分别运行:

  • baseline:移除 nested spacing 后的旧行为;
  • fixed:真实生产 smoothGraphEdges 后处理;
  • 输出每个布局的 baseline/fixed SVG、Chrome PNG、report.json,同时记录终段、画布尺寸和 crossings。截图默认模拟 VSCode 右半栏 960x720 viewport,按 Stage.vue 的 16px fit-to-view margin 等比缩放;每张 PNG 的 IHDR 尺寸强制校验为 960x720,防止原尺寸截图裁切。

证据

  • fixed:20/20 布局 malformed=0short=0,所有终段严格不少于 18px;最小值为 18px
  • baseline:12/20 布局存在短终段,典型最短值为 10px;这是 A --> B 平行/贴边问题的可复现负对照。
  • fixed:crossings 未增加;新增 spacing 只改变必要的层间留白,报告中逐布局记录尺寸变化。
  • fixed:scanner 现在把 crossing 回归作为失败门禁;最终 20 layouts 的 crossingRegressions=0,平均面积增幅 10.25%,最大单布局 23.06%(02-nested-hvac/TB)。
  • 独立 smoother 探针:176 sections(entry 40、normal 84、normalAll 38、exit 2、self 12),smoother 前后首段/末段轴变化均为 0;26 条 back edge 的首末轴变化也均为 0。
  • Chrome 已对 baseline/fixed 全部 40 张图截图,并人工查看 HVAC、forced、deep nesting、traffic-light、新增 nested-crossing 等代表性图;节点、标签、箭头均正常,未见边穿节点或空白渲染。
  • jsfcstm:697 passing,coverage lines 95.75% / branches 85.89% / functions 95.96%
  • Codecov 审计:本 PR 当前没有 codecov-commenter 评论或 Codecov check(远端检查列表已回读确认);本地 c8 等价报告覆盖整体 lines 95.75% / branches 85.89% / functions 95.96%,改动核心 elk-graph.ts 为 lines 94.56% / branches 78.68% / functions 94.11%。Codecov 未生成评论不被冒充为通过,以上本地报告作为覆盖证据。
  • 本 PR 未修改 Python 源码或 Python 公共 API,因此 pydoc/__init__.py 文档门禁不产生新增范围;Python 全量测试只作为仓库回归验证。
  • VSCode production compile:通过;verify-preview-renders.js5/5 checkpoints 通过。
  • make rst_auto:通过且无意外文档 diff;git diff --check:通过。

Review 结论与处理

  • 历史 Codex review:评论:指出角点假阳性、生产 smoother、可复现扫描和严格阈值问题;均已在后续提交处理。
  • 历史 Claude review:评论:指出早期 epsilon、覆盖计数和 TDD 证据问题;均已在后续提交处理。
  • 历史 DeepSeek review:评论:指出 nested edge-edge spacing 与 forced expansion 覆盖;均已处理。
  • 最新独立 Claude review:提交后终审评论C=0, I=0, M=2, READY
  • 最新独立 DeepSeek review:提交后终审评论C=0, I=0, M=2, READY,独立重跑 20-layout scanner。
  • 最新独立 Codex review:提交后终审评论C=0, I=0, M=4, READY,独立复核 176 sections smoother 轴不变和 Gist hash。

当前门禁\n\n- [x] 9c505d0c 将视觉验证对齐 VSCode 右半栏 960x720 fit-to-view,并生成完整未裁切 PNG。\n

  • 代码与测试已 push(9c505d0c)。
  • 10 个 fixture、TB/LR、baseline/fixed、生产 smoother、Chrome 视觉证据已生成。
  • GitHub Actions:Linux/Windows/macOS CLI、VSCode、jsfcstm、双语 PDF、template gate 均已完成并通过;Code test、CLI Build、template representative/full 按提交路由 skipped。
  • make rst_automake test_boundary_checkgit diff --check 已通过。
  • 三路本轮独立审查已完成:Claude/DeepSeek/Codex 均为 C=0, I=0, READY;剩余 M 级观察不阻塞 ready。
  • PR 保持 open、ready for review;不在本 PR 中合并。

完整脚本、报告摘要、验证命令和视觉证据索引已上传到 Gist。\n\n## Current final gate (HEAD c65df68)

This section supersedes earlier hash-specific status text above. The current remote HEAD is c65df68cf6146e47481976836937cfe32d8631fc.

  • Production scanner: 10 fixtures x TB/LR = 20 layouts; baselineFailures=12, fixedFailures=0, crossingRegressions=0.
  • Real VSCode-like workbench evidence: 4 right-pane sizes, 160 full-shell PNGs, zero PNG IHDR mismatches.
  • Dynamic interaction evidence: 80 drawer toggles + 80 real handle drags; each changed/restored state re-reads overflow, pane/stage containment, SVG, and fit transform metrics.
  • Side effects: mean area delta 10.6956804842%, maximum 23.0576441103%.
  • Latest independent review comments for c65df68c: Claude 4980666334, Codex 4980632043, DeepSeek current-head review 4980632043. All report C=0, I=0, READY TO MERGE.

Full current report and verifier: Gist, including CURRENT-C65-VALIDATION.md.

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

claude reviewer 独立审查(PR #377 plan-first draft)

结论:C=0, I=0, M=3(三项非阻塞观察)。计划文本、上游一致性、几何契约测试均通过独立复现验证;工作树中的最小实现 + 新增契约测试与 issue #376 的验收标准对齐,可以进入实现推送与 CI 阶段。

独立复现记录

  • 身份核对:gh api user -> HansBug,与 git config user.email (hansbug@buaa.edu.cn) 一致。
  • 上游一致性:git fetch origin maingit log HEAD..origin/main 为空,git log origin/main..HEAD 只有 06b8a1db chore(visualization): open issue 376 validation PR。base=971687ca,与 PR body 声明的目标基线一致,无 rebase 冲突。
  • 相关 PR 扫描:gh pr list --search "elk OR 376 OR arrow" 未发现其它涉及 ELK 布局/几何的活跃 PR,可以独立推进。
  • 工作树 diff 与 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 的“已实测修复方案”一致:只在 buildStateNodeeditors/jsfcstm/src/diagram/elk-graph.ts:343-355)追加 elk.layered.spacing.edgeNodeBetweenLayers = 20,未引入 FIXED_SIDE ports、也未复制其余 root spacing;smoother 未改动。
  • 契约测试实际执行:
    cd editors/jsfcstm
    npx mocha --require ts-node/register/transpile-only --extension ts "test/diagram-elk.test.ts"
    # 11 passing,含 "routes every non-self arrow into its target border along a visible outward normal" (348ms)
    

白盒对抗性检查(重点:--> B 的目标端法向接入是否真被覆盖)

editors/jsfcstm/test/diagram-elk.test.ts 中新增的 terminalApproach 做了 4 组反例演算:

  1. issue 中的历史坏 path L363,430 L299,430,目标 Red 顶边 y=430horizontal=true, vertical=false;四个分支要求 previous 在目标外侧的对应方向严格不等式全部失败 → 返回 null → 断言 arrow must approach ... perpendicular to its border 触发 ✅。
  2. 短段 1px(previous=(370,429), end=(370,430)):vertical=true, top 分支通过,length=1,被 length >= 17.25 拒绝 → 触发 top terminal segment is only 1px ✅。
  3. 对角接入((400,400) -> (370,430)):两条正交判定同时失败 → 返回 null → 触发 perpendicular 断言 ✅。
  4. 沿边框走停在角点((box.left, box.top) -> (box.left+40, box.top)):previous.y == box.topprevious.y < box.top - eps 不成立;verticalprevious.y > box.bottom + eps 不成立;horizontal 左右两侧 previous 在 box 内部(previous.x >= box.left)不满足严格外侧 → 返回 null → 触发 perpendicular 断言 ✅。

transitionKind 值集合(normal, normalAll, exit, entry)在 editors/jsfcstm/src/diagram/elk-graph.ts:321-323 生成;几何断言在 checkNode 中对每一条非 self-loop edge(不筛选 kind)都执行,而 checkedNormalTransitions >= 50 的覆盖门禁保证真正的 A -> B 至少 50 条。这与 issue #376 “必须由测试证明:普通 A -> B 箭头从 B 外部沿实际边框法线接入” 一一对应。

抽样运行显示 9 个 test/fixtures/visual/*.fcstm × TB/LR = 18 次布局足以让 checkedSections>=100 && checkedNormalTransitions>=50 全部通过。ELK 内部布局是相对父容器坐标,测试通过 collectLayoutGeometry 累计 offsetX/offsetY 后与 box 都换算到绝对坐标 → 嵌套 composite 内的 edge 也被正确判定,未见坐标系错配。

计划 vs. 实现 vs. issue 验收标准的对齐

  • 修复位置只在 ELK input:diff 只碰 buildStateNodelayoutOptions,未改 smoother、未引入 ports、未复制全量 nested spacing。与 issue 决策摘要与 PR “非目标” 段完全一致。
  • edgeNodeBetweenLayers=20 出现在 composite 递归里,因此 root + 每一层 nested composite 都会重复设置;已有的 elk.spacing.nodeSelfLoop=28 与 root 52px、smoother 18px 未被改动。测试 “ELK layout spacings are generous enough for readable diagrams” 显式断言 FleetFleet.Running 两处都有 20,与 issue “必须在实际拥有 children/edges 的 parent 上设置” 对齐。
  • 几何契约endPoint 落边界 / previous 在外侧 / 法向轴 / ≥18px / 角点不算通过 五条全部由 terminalApproach + MIN_TERMINAL_SEGMENT 编码,与 issue “可执行的几何契约” 完全同构。
  • 验收标准:PR body checklist 与 issue 合入验收标准一一映射;PR 明确“不在本 PR 中 merge”,plan-first 定位与远端只含一个空提交的现状一致。

M 级观察(非阻塞,供推送前顺手修一下即可)

  • M-1GEOMETRY_EPSILON=0.75 使 length >= MIN_TERMINAL_SEGMENT - 0.75 = 17.25 就能通过。issue 承诺 ≥18px。设计目标是 20px,代码期望有 2px 富余,因此当前不会引发假阴性;若将来 root spacing 被下调,容差 0.75 会让 18px 与 17.25px 混淆。若成本可控,可考虑在长度比较里去掉 epsilon,只保留位置比较的容差。
  • M-2checkedSections >= 100 / checkedNormalTransitions >= 50 是全局硬阈值。当前 9 个 fixture × TB/LR 有安全余量,但一旦 test/fixtures/visual/ 增删或 ELK 版本改动,可能会一次性把该测试拉到边界,导致的失败信息只有 “expected broad geometry coverage”,不利于诊断。可以打印 fixture->section 分桶数,或改用“每个 fixture/direction 至少 1 条 non-self”结构化保证。
  • M-3:远端 PR 目前只有一个空 commit 06b8a1db,所有实现和测试都在本地工作树。这属于 plan-first 的预期状态,但建议在 push 时把提交拆成 “test + fix” 两个 atomic commit(测试先 fail,实现后 pass),这样 CI 能顺便证明测试对根因敏感;否则未来仅靠 review 无法回放“修复前该测试真实失败”的证据。issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 已给出 01-simple-leaf.fcstm/TB/Switch:e1: bottom terminal segment is only 10px 的期望失败样例,两阶段提交能直接把这一断言落地。

阻塞理由

无 C 级或 I 级阻塞。计划、实现、测试与 issue 一致;上游无冲突;npx mocha test/diagram-elk.test.ts 全绿;terminalApproach 对历史平行贴边坏例与短终段坏例都可拒绝;覆盖门禁保证真正的普通 A -> B 被检查到。可以按 PR body 中的推进顺序继续。

— 由 claude reviewer 独立复核。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

codex reviewer

结论:C=1, I=3, M=1。PR title、关联 issue #376 和 main base 方向一致,但当前不能将该计划视为已具备可合入的 TDD/验收证据。

C(1)

  • C1(阻塞):几何契约测试对“沿边框走到角点必须失败”存在可执行的假阳性。 editors/jsfcstm/test/diagram-elk.test.ts:71-89 的注释声称会拒绝角点,但判定顺序会把沿目标顶边走到左上角的线段当作 left 法向接入。可直接复现:

    node <<'NODE'
    function terminalApproach(previous,end,box) {
    const e=.75, h=Math.abs(previous.y-end.y)<=e, v=Math.abs(previous.x-end.x)<=e;
    const wx=end.x>=box.left-e&&end.x<=box.right+e;
    const wy=end.y>=box.top-e&&end.y<=box.bottom+e;
    if(v&&wx&&Math.abs(end.y-box.top)<=e&&previous.y<box.top-e)return ['top',box.top-previous.y];
    if(h&&wy&&Math.abs(end.x-box.right)<=e&&previous.x>box.right+e)return ['right',previous.x-box.right];
    if(v&&wx&&Math.abs(end.y-box.bottom)<=e&&previous.y>box.bottom+e)return ['bottom',previous.y-box.bottom];
    if(h&&wy&&Math.abs(end.x-box.left)<=e&&previous.x<box.left-e)return ['left',box.left-previous.x];
    return null;
    }
    const box={left:0,right:100,top:0,bottom:100}, previous={x:-18,y:0}, end={x:0,y:0};
    console.log(JSON.stringify({previous,end,box,result:terminalApproach(previous,end,box)}));
    NODE

    实际输出:{"previous":{"x":-18,"y":0},"end":{"x":0,"y":0},"box":{"left":0,"right":100,"top":0,"bottom":100},"result":["left",18]}。这条线段实际沿 B 的 top border 水平行走并停在左上角,按 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 的契约应拒绝,但测试会接受。阻塞理由:该测试可以在违约路径上变绿,因而不能作为“普通 A -> B 目标端沿实际边框外法线垂直接入”的合入门禁。需要显式排除 endpoint corner(以及角点附近的容差区域),并加入该最小反例。

I(3)

  • I1(阻塞验收证据):测试只验证 elk.layout() 原始 section,没有验证生产渲染实际使用的 section。 测试在 editors/jsfcstm/test/diagram-elk.test.ts:369-415 直接调用 ELK;而 VSCode 生产路径在 editors/vscode/src/preview-webview/components/Stage.vue:138-150 会先 elk.layout() 再执行 smoothGraphEdges(laid)。阻塞理由:当前测试不能证明 smoother 后最终 SVG 的 --> B 终段仍未贴边、仍沿外法线且长度满足契约;至少需要复用生产后处理后再检查,或用明确不变量测试证明后处理必然保留这些性质。

  • I2(阻塞证据可复核性):PR/issue 的 66 模型扫描和 10+ 组视觉证据没有落在当前仓库可执行资产中。 当前仓库 find editors/jsfcstm/test/fixtures/visual -maxdepth 1 -name '*.fcstm' | wc -l 输出为 9;新增测试确实遍历这 9 个 fixture,并以聚合阈值 checkedSections >= 100、checkedNormalTransitions >= 50 作为通过条件(editors/jsfcstm/test/diagram-elk.test.ts:354-431)。阻塞理由:达到 100/50 不能替代 PR body 声称的 66 模型覆盖,也不能让审查者独立复核 issue 中的 66/132/884 统计;应提交确定性扫描输入/输出,或把这些外部诊断数字明确降级为不可作为验收依据的背景材料。

  • I3(阻塞契约精度):18px 下限实际被测试放宽为 17.25px。 editors/jsfcstm/test/diagram-elk.test.ts:28-29,410-413 定义 18 和 0.75,但断言使用 length >= MIN_TERMINAL_SEGMENT - GEOMETRY_EPSILON,因此 17.25px 会通过。阻塞理由:issue/PR 明确写的是“至少 18px”,当前门禁允许不满足该硬指标的实现;应区分坐标比较容差与长度下限,或把契约改成明确的带容差定义并同步 issue/验收标准。

M(1)

  • M1:远端 PR 与工作树状态不一致。 gh pr view 377 --json commits,files 显示只有提交 06b8a1d 且 files: [];当前本地 git status --short 为 M editors/jsfcstm/test/diagram-elk.test.ts、?? .agents/,而 editors/jsfcstm/src/diagram/elk-graph.ts:343-350 仍只有 elk.spacing.nodeSelfLoop,没有 PR body 计划的 elk.layered.spacing.edgeNodeBetweenLayers = 20。这与“实现尚未进入 PR”的 plan-first 描述一致,但也意味着当前 PR 本身没有可审查的实现 diff;请在后续实现提交中重新提供上述几何门禁和最终渲染证据。

实际验证

  • git config --get user.name && git config --get user.email:HansBug / hansbug@buaa.edu.cn
  • npx mocha --require ts-node/register/transpile-only --extension ts test/diagram-elk.test.ts --grep 'routes every non-self|ELK layout spacings' --reporter dot:当前工作树实际为 0 passing, 2 failing;失败分别是缺少 '20' layout option,以及 01-simple-leaf.fcstm/TB/Switch:e1 终段 10px、期望 18px。
  • npm run test:unit 曾在实现改动短暂存在时报告 695 passing,但在当前源码状态下目标测试重跑已失败;不能用旧的全量结果替代当前提交的验证。

@HansBug HansBug left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deepseek reviewer — PR #377 强对抗审查

审查范围

  • PR body、issue #376 全量描述、origin/main 的 elk-graph.ts、本地分支 editors/jsfcstm/src/diagram/elk-graph.ts diff、editors/jsfcstm/test/diagram-elk.test.ts diff
  • 独立构造 ELK 真实布局反例,验证 edgeNodeBetweenLayers 在嵌套复合中的作用机制、无修复时的 10px 短终端重现、修复后几何契约通过性
  • TB/LR 双向几何验证、角点法向逻辑审读

总体结论

单行修改(每个嵌套 composite 增设 elk.layered.spacing.edgeNodeBetweenLayers = 20)是正确的根因修复。ELK INCLUDE_CHILDREN 不传播 layered.spacing.*,导致嵌套状态内的 A→C 包边(如 traffic-light 的 Red 顶边水平行走)终段仅 10px(ELK 默认值)。20px 选择有 66 模型扫描证据支撑。几何契约测试调用真实 elk.layout() 并在 TB/LR 双方向上逐 section 验证法向接入与最小长度,结构合理。


C=0

无 Critical 问题。

I=3

I1. edgeNodeBetweenLayers=20 对含边层路由的嵌套层间距有 ~2× 放大效应,但只在存在跨层包边时才触发。

实验证据(同一组 A→B, B→C, A→C 三个边在嵌套复合内):

  • 无 A→C(仅直连边):gap 恒为 20px,edgeNodeBetweenLayers 不影响;
  • 有 A→C(需边层路由):gap 从 20px 翻至 40px,总布局高度 +25%。

这意味着修复成本集中在有交叉/包边需求的状态机(恰是本 bug 的发生场景),对单纯链式状态无副作用。PR 的 12 组对照矩阵已覆盖此代价且接受。建议:在嵌套复合上同时显式设置 nodeNodeBetweenLayers(例如 20),将直连边间距与边层间距解耦,使未来维护者可独立调参而不会意外联动。当前代码未设置此值,直连边间距依赖 ELK 全局默认,与 edgeNodeBetweenLayers=20 间存在隐式耦合。

I2. checkedNormalTransitions 只计数 transitionKind === "normal",漏计 forced expansion (normalAll)。

测试的 assert.ok(checkedNormalTransitions >= 50) 保证至少 50 条普通 transition 覆盖,但 forced expansion 边(transitionKind: "normalAll")虽被 section-loop 几何检查(因为全边遍历不走 counter 过滤),却不纳入 50 条配额。PR checklist 明确要求覆盖 forced expansion。建议:单独增加 checkedForced 计数器,或改为累加所有非 self-loop 的 edge.fcstm?.transitionKind,使 >= 50 要求的含义与 checklist 的 "至少 50 条普通 transition" + forced expansion 语义一致。

I3. 嵌套复合未设置 elk.layered.spacing.edgeEdgeBetweenLayers,root canvas 有 32px 而嵌套回退到 ELK 默认。

Root canvas 设置 edgeEdgeBetweenLayers: "32" 保证多束平行边不会紧贴。嵌套复合未此设置,在密集嵌套布局中多束边可能回退到默认 10–20px,导致未来回归。当前 66 模型扫描 crossing=0,但该保护仅在 root 层级生效。建议:与 edgeNodeBetweenLayers 一起在复合上设置 edgeEdgeBetweenLayers(可沿用 32 或更保守的值),避免未来嵌套密集边场景出现贴边干扰。


M=3

M1. terminalApproach 四边匹配顺序 top→right→bottom→left,在端点恰好位于角点时具有方向任意性。
当前 ELK ORTHOGONAL 路由不产生角点端点,但如果未来切换为 SPLINESPOLYLINE,此函数可能误判。建议:在函数顶部增加显式角点检测 const atCorner = (near left/right && near top/bottom),对角点直接返回 null 并给出明确错误信息,使意图自文档化。

M2. collectLayoutGeometrynode.width || 0 / node.height || 0 使用 || 操作符。
若 ELK 某节点返回 width=0 或 height=0(当前不发生但非不可能),0 || 0 虽然是 0 没问题,但语义上 node.width ?? 0 更精确表达"未定义则 0"的意图,而非"falsy 则 0"。建议:改为 ?? 或保留原值直接参与运算,在注释中说明 0 维节点的处理策略。

M3. GEOMETRY_EPSILON = 0.75MIN_TERMINAL_SEGMENT = 18 组合产生 17.25px 有效阈值,容差约 4.2%。
视觉上 17.25px 与 18px 差异可忽略,但失败消息打印原始像素值(如 "bottom terminal segment is only 10px"),通过阈值却因 epsilon 放水的案例不会留下记录。建议:当 approach.length >= 17.25 && approach.length < 18 时单独记录一条 diagnostic log(非断言失败),方便后续扫描 ELK 版本升级后的微量退化。


额外验证记录

  • 独立 ELK 布局脚本复现了无 edgeNodeBetweenLayers 时嵌套 A→C(包边)终段 = 10px,设置 20px 后终段 = 20px。
  • TB/LR 双方向通过自定义 dual-nesting 图(含直连 + 包边混合)全部几何契约,无 violation。
  • terminalApproach 对角点四边冲突场景(end 在 box 角点、prev 在对角线外)正确返回 null(非正交拒绝)。
  • 自循环跳过逻辑、section 点数 >=2 断言、edgeOffsets 全局坐标累积链均无歧义。

- repeat nested edge spacing so INCLUDE_CHILDREN layouts reserve a visible target approach\n- reject corner endpoints and lock strict 18px terminal geometry across ten fixtures\n- add a production smoother geometry scanner with baseline/fixed SVG and Chrome evidence
HansBug added 2 commits July 15, 2026 15:07
- render baseline and fixed screenshots through the actual preview-webview renderer\n- keep the geometry report aligned with the Stage.vue production path
- share terminal threshold and absolute-layout collection across tests and scanner\n- fail loudly on missing offsets and renderer prerequisites\n- add positive side checks and clean generated evidence output
@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Claude implementation review(post-fix)

审查范围:git diff origin/main...HEAD,并实际运行:

  • :12
    passing \

PYFCSTM_GEOMETRY_OUTPUT=/tmp/pgeom-review npm run test:preview-geometry:10 fixtures、20 layouts、baselineFailures=12、fixedFailures=0、screenshots=true

结论

  • C=0:无阻塞性正确性、安全性或兼容性问题。
  • I=0:生产链路确认是 elk.layout -> smoothGraphEdges -> renderSvg,固定侧 20/20 全绿。
  • M=0(本轮原 M 已处理):补了四边正向单测、共享阈值/坐标采集、renderer options 类型收敛、dist 前置检查和输出清理。

核心证据:baseline 的 12/20 布局有 <18px 终段(典型 10px),fixed 的 malformed=0、short=0,最小终段严格 18px;crossing 逐行未增加。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

DeepSeek implementation review(post-fix)

审查范围:当前 HEAD 的 3 commits / 7 files,并实际检查 elk-graph.ts、10 fixtures、VSCode production renderer/smoother/crossings 与 scanner。

结论

  • C=0:未发现安全、数据丢失、向后兼容或核心几何逻辑阻塞问题。
  • I=0(本轮已处理)
    • 共享 MIN_TERMINAL_SEGMENT,scanner 不再硬编码 18;
    • shared collectElkLayoutGeometry 后不再重复实现坐标采集;缺失 edge offset 现在显式抛错,不再 ?? 0 静默回退;
    • scanner 运行前检查 jsfcstm dist,输出目录每次清理。
  • M=0(本轮已处理):补了四个法向方向正向单测,renderer options 使用显式类型适配。

独立几何证据

  • baseline(删除 nested spacing 后)12/20 layout 有 <18px 终段,最短 10px;
  • fixed(真实 smoothGraphEdges + renderSvg)20/20 layout malformed=0short=0,最小 18px;
  • TB/LR 双方向、forced expansion、direct two-point edge 均覆盖;crossings 未增加。

审查建议:核心修复可合并;剩余仅是未来可选的 fixture 命名/自环专门视觉案例,不构成当前 PR 的 C/I。

@HansBug
HansBug marked this pull request as ready for review July 15, 2026 07:39
@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

claude reviewer 独立审查(HEAD c931cfd,PR #377 vs issue #376

结论:建议阻塞 ready-for-merge,理由集中在 I1/I2/I3。C=0(未发现正确性/回归 bug)。

审查方法:git diff origin/main...HEAD、逐文件读 elk-graph.ts / diagram-elk.test.ts / verify-preview-geometry.ts、比对 issue #376 与 PR body 承诺、核对 statusCheckRollup、白盒推演 terminalApproach corner/epsilon 分支、比对 10 个 fixture 与 forced-expansion transitionKind 覆盖。仅审查,不改代码。

C(阻塞)

无。生产链路 elk.layout → smoothGraphEdges → renderSvg 已在 verify-preview-geometry.ts:17,187-197 走真实生产 edge-smoother / svgdiagram-elk.test.ts 也真正调 elk.layout(),不是只读 layoutOptions 字符串。10 fixture、TB/LR 双方向、normal ≥50、normalAll ≥1、sections ≥100 断言就位。角点被显式拒绝,terminalApproach 单元测试覆盖 corner-hit 双方向。

I(合入前需处理)

I1. 20px vs 18px:与 issue 计划的定量偏离,PR body 未披露

  • issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 "决策摘要" 与 "合入验收标准" 明确写 edgeNodeBetweenLayers = 20,并写明 "选择 20px 而不是正好 18px,是为了给浮点坐标和渲染 marker 留 2px 余量"。
  • 实现 elk-graph.ts:441String(MIN_TERMINAL_SEGMENT) 即 18;测试断言 MIN_TERMINAL_SEGMENT 即 18;verify-preview-geometry.ts 生成的 report.jsonminTerminal 最小值也是 18。最小 spacing 数值 = 断言阈值 = 报告最小值 = 18,float 余量为 0。
  • 复现:git grep -nE "MIN_TERMINAL_SEGMENT|edgeNodeBetweenLayers" editors/jsfcstm/src/diagram/elk-graph.ts(会看到 spacing 与 min 都锁定 18)。
  • 预期:与 issue 的 = 20、"2px 余量" 一致;实际:18px、零余量。任何 ELK 后续版本产生 17.9px 的 float 输出都会让契约由 "健壮" 退化为 "刚好卡边"。
  • 处理选项之一即可:(a) 恢复 20;或 (b) 明确修改 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 的 acceptance criteria 并在 PR body 里给出为何 18px 足够的定量论证(例如 minTerminal 分布直方图)。当前 PR body 只静默改到 18,未记录偏离。阻塞 ready-for-merge。

I2. 新增 edgeEdgeBetweenLayers = 32 不在 issue 的 "唯一新增布局项" 契约里

  • issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 "决策摘要" 明确写 "唯一新增布局项:每个嵌套 composite 设置 elk.layered.spacing.edgeNodeBetweenLayers = 20",且 "明确不采用:全量复制 root spacing"。
  • 实现 elk-graph.ts:445 引入了第二个从未在 issue 中审议过的嵌套 spacing:elk.layered.spacing.edgeEdgeBetweenLayers = 32。这是 root spacing 的又一项,越出 issue 明确划出的 "唯一新增" 边界。
  • issue 里 66 模型扫描给出的面积增幅 4.27% / 最大 18.26% 是在 "只加 edgeNodeBetweenLayers=20" 前提下测的。新增 edgeEdgeBetweenLayers=32 之后 PR body 只写了 20 layouts 的 crossings 未增加,没有给对照的面积/bend/crossing 数字,也没和 issue 的 12-pair 对照表挂钩。
  • 复现:grep -n edgeEdgeBetweenLayers editors/jsfcstm/src/diagram/elk-graph.ts 命中 1 行;grep -n edgeEdgeBetweenLayers 在 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 与 PR body 里都没有对该值的定量证据。
  • 预期:要么在 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 里追加该布局项、并给 66 模型 + 12 组对照的定量增幅;要么在 PR body 显式记录 "additional to issue plan" 并附对照。阻塞 ready-for-merge。

I3. PR body 里 "当前门禁" 段与 statusCheckRollup 不一致

  • PR body:"GitHub Actions:除 macOS CLI job 仍在 runner 队列外……Codecov comment 尚未出现"。
  • 实际 gh pr view 377 --json statusCheckRollupCLI Build And Release (macos-14, 3.8) 已 SUCCESS(2026-07-15T07:42:00Z)。Codex/Claude/DeepSeek 复审也已在 body 声明 C/I=0。
  • 若 PR body 是 ready-for-review 门禁清单,就应当反映当前状态。当前措辞让读者以为门禁未齐,是过期的 evidence 陈述。修正后再标 ready。

M(可修)

M1. terminalApproachGEOMETRY_EPSILON=0.75 与 corner 判定共用;当 ELK end 落在离目标边框 0.8px 处(>eps)时会归入 malformed,与真正 "沿边框平行" 混在一起。建议在测试 approach==null 分支打印 end 与 targetBox 各边的距离,方便下次回归时区分 "略远" 与 "平行"。

M2. 新公共导出 MIN_TERMINAL_SEGMENT / terminalApproach / collectElkLayoutGeometry 已在 diagram/index.ts re-export,但 TSDoc 覆盖不全。collectElkLayoutGeometry 静默跳过 fcstm.kind==='canvas' 节点(elk-graph.ts:107),消费者在拿 boxes.get(canvasId) 时会得到 undefined。请在 doc 里显式声明这一点,并给出 edgeOffsetsboxes 的坐标系约定。

M3. verify-preview-geometry.ts:63-69removeNestedSpacing 只删两个 nested spacing key 用来造 baseline,语义上 baseline = "回退到 issue 前的实现"。这个约定没在文件顶部注释里写死;若未来又新增第三个 spacing key,baseline 会与"issue 前状态"漂移。建议在 removeNestedSpacing 上方注释里锁死 baseline 语义并列出所有被移除的 key。

M4. PR body 声明 "C1 角点假阳性已处理",但可复现命令没绑定具体 fixture/direction 的 corner case 反例。测试里已有单元例,建议 PR body 附一句 "corner 反例:diagram-elk.test.ts 中 rejects a terminal segment that ends at a target corner",方便下轮 reviewer 直接跳到证据。

Ready-for-merge 判定

在 I1、I2、I3 未闭合前,不建议合入。I1/I2 属计划-实现一致性问题(issue 是本 PR 自己引用的 spec),I3 是 evidence-freshness 问题。M 级别不阻塞但建议一并处理。

—— claude reviewer

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

deepseek reviewer 独立强对抗审查(HEAD c931cfd,PR #377 vs issue #376

审查方法git diff origin/main...HEAD 逐文件白盒审计 + 真实 ELK 布局运行 + 生产链 elk.layout() → smoothGraphEdges() → renderSvg() 验证 + terminalApproach 17 种边界/角点/非正交反例 Python 翻译验证 + spacing 放大效应逐 fixture 实测 + CI/coverage 审计。未启动任何子智能体。

结论:C=0, I=2, M=3

无阻塞级问题(C=0)。I1/I2 为重要差距,建议在 ready for merge 前处理或显式记录偏离理由;M1-M3 为可延后改进。


I1(重要):issue #376 承诺 edgeNodeBetweenLayers=20,PR 实际使用 18px,2px 浮点余量被放弃

证据

  • Issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 决策摘要写明:「唯一新增布局项:每个嵌套 composite 设置 elk.layered.spacing.edgeNodeBetweenLayers = 20」「选择 20px 而不是正好 18px,是为了给浮点坐标和渲染 marker 留 2px 余量。」
  • PR diff 中 elk-graph.ts line 444:'elk.layered.spacing.edgeNodeBetweenLayers': String(MIN_TERMINAL_SEGMENT),其中 MIN_TERMINAL_SEGMENT = 18
  • 实测 10 fixture × 2 direction = 20 layouts,最小终段严格等于 18px,零余量。

影响:浮点累积和 SVG marker 可能使视觉终段略短于 18px。当前测试通过是因为 >= 18,但没有余量吸收 ELK 浮点误差。

建议:要么将 MIN_TERMINAL_SEGMENT 提升到 20(与 issue 一致),要么在 PR body 中明确记录「self-approve 18px 无余量方案」并 close issue 的 20px 要求。当前 issue 仍 open 且验收标准要求 20px。

是否阻塞 ready for merge:不阻塞,但建议在 merge 前处理(改值为 20 或显式记录偏离理由)。


I2(重要):spacing 放大效应实测值超过 issue 和 PR body 的引用数字

复现命令

cd editors/jsfcstm
cat > /tmp/spacing-measure.ts << 'EOF'
// (已在本审查中执行,见下方结果)
EOF

实测数据(全 10 fixture × TB/LR,baseline=移除 nested spacing,fixed=当前 PR):

| Fixture | Direction | Δ Area |
|---|---:|
| 02-nested-hvac | TB | +23.1% |
| 05-forced-expansion | TB | +22.6% |
| 01-simple-leaf | TB | +15.6% |
| 08-rich-state-details | TB | +16.4% |
| 04-many-transitions | TB | +13.8% |

Issue #376 引用「最大单图增加约 18.26%」(基于 20px 方案),PR body 未更新此数字。实测 HVAC TB 达 23.1%,forced-expansion TB 达 22.6%,均显著高于引用的 18.26%。

影响:非功能性,但 spatial budget 估计偏差可能影响未来布局调参决策。

建议:在 PR body 或 Gist 中补充当前 18px 方案的实测最大面积增幅,或用 [skip-slow] 无关的理由明确接受该增幅。

是否阻塞 ready for merge:不阻塞。


M1:terminalApproach 角点拒绝使用统一 null 返回,丢失诊断粒度

当前 terminalApproach 对所有非正常终段(角点、非正交、框内接入、prev 在框内)统一返回 null,导致 MALFORMED 无法区分「确实是角点」还是「非正交布线 bug」。但不影响正确性——所有 null 路径都被正确计为 malformed。

建议:后续可选返回区分字符串如 'corner' | 'non_orthogonal' | 'inside_box' | null,非阻塞。


M2:GEOMETRY_EPSILON = 0.75 无边界值测试

terminalApproach 的角点拒绝依赖 0.75px 容差。当前测试只覆盖精确角点((0,0))和紧邻容差外(0.8px),未测试容差边界 0.750.76

# 已验证通过(Python 等效翻译),但 jsfcstm 测试中未覆盖
terminalApproach({x: 0.75, y: -18}, {x: 0.75, y: 0}, box)  # corner rejection
terminalApproach({x: 0.76, y: -18}, {x: 0.76, y: 0}, box)  # normal top entry

建议:在 diagram-elk.test.ts 中增加 epsilon 边界测试。非阻塞。


M3:新增 fixture 10-nested-crossing.fcstm 不包含 nested-level initial transition

Fixture 包含 [*] -> Left(Branch 级 initial)和 [*] -> LeafA(Left 嵌套级 initial),但整个 Router 的 [*] -> Branch 是 canvas 级。没有在嵌套 composite 内部存在初始转换被外部边交叉的场景。现有 fixtures(如 02-nested-hvac)已覆盖此场景,非阻塞。


独立验证记录

1. 生产链全链路验证

cd editors/jsfcstm && npm test  # 697 passing

自写 scanner 独立复现:10 fixtures × 2 dirs = 164 sections,malformed=0,short=0,min terminal=18px。

2. terminalApproach 白盒审计:17 种边界/角点/非正交/框内/epsilon 场景全部通过 Python 翻译验证。函数逻辑正确,角点拒绝无歧义。

3. CI 状态:全部 required checks 通过(macOS CLI 已 green)。jsfcstm test × 2 passing。Code test / Template representative / Template full 因 [skip-slow] 触发 skip 属于预期行为(无 Python 代码变更)。

4. collectElkLayoutGeometry:canvas 节点正确从 boxes 中排除;edge offsets 全局坐标累积链正确。

5. 异常处理合规run-preview-geometry.js 使用 try/finally(资源清理,无 catch,错误传播正确);verify-preview-geometry.tsvoid main().catch(...) 为 CLI 顶层错误报告,非静默吞没。符合 CLAUDE.md 异常策略。

6. PR body / issue / Gist / diff 一致性:PR body 中 7 文件、568 行、10 fixture、20 layout 声明与 git diff --stat 一致。Gist 为文本资产载体,与 issue/PR 双向链接正确。


总结:实现正确性无问题,C=0。I1(20→18px 偏离)和 I2(spacing 放大数字过时)建议在 merge 前处理或显式记录接受理由。无子智能体启动。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

codex reviewer 独立审查(HEAD c931cfd9

结论:C=0, I=2, M=3。当前生产修复方向成立,但“严格 18px”门禁仍有可执行假阳性,且 issue 正文与最终实现的验收口径尚未统一;建议处理 I 项后再视为 ready for merge。

C(0)

C=0。 未发现新的生产路径正确性阻塞:terminalApproach 已显式拒绝四角,四侧分支都要求倒数第二点位于目标框外并沿对应法向进入;仓库 scanner 的顺序确实是 elk.layout() -> smoothGraphEdges() -> renderSvg(),与 Stage.vue:139-150 一致。当前 CI 中 jsfcstm Node 20/22、VSCode、Linux/Windows/macOS release、PDF 与 template gate 均成功。

I(2)

I1. “严格 >=18px”仍可被 17.25px 实际终段假阳性通过。

editors/jsfcstm/src/diagram/elk-graph.ts:54-86 允许 endpoint 在边界 0.75px 容差内,但返回长度使用“previous 到 box 边界”的距离,而不是“previous 到实际 endPoint”的终段长度。测试和 scanner 随后都直接比较这个返回值(diagram-elk.test.ts:378-381verify-preview-geometry.ts:98-101),因此位置容差再次放宽了长度硬阈值。

本轮唯一 targeted 命令的实际输出:

{"previous":{"x":50,"y":-18},"end":{"x":50,"y":-0.75},"actualLength":17.25,"reported":{"side":"top","length":18}}

这与 PR/issue 反复声明的“没有 epsilon 放宽、严格不少于 18px”不一致。建议保留边界定位容差,但返回/断言最后实际线段的轴向长度(例如 top/bottom 用 abs(end.y - previous.y)),并加入这个 -0.75 边界反例。

I2. issue #376 正文的验收标准仍与实现冲突。

issue 正文仍写“唯一新增布局项 edgeNodeBetweenLayers=20”并在合入标准中要求 20;当前实现是 edgeNodeBetweenLayers=18,同时新增 edgeEdgeBetweenLayers=32。issue 的后续评论虽说明最终改为 18+32,但正文仍是 PR 明示引用的上游规格与 checklist。应更新 issue 正文/验收项,或在 PR body 明确声明哪条后续评论取代正文,避免合入后出现两个互相矛盾的 canonical 口径。

M(3)

M1. forced expansion 只有全局弱门禁。 diagram-elk.test.ts:357-400 只要求 checkedForcedSections > 0;即使 05-forced-expansion.fcstm 未来退化为单条 normalAll edge、或只在一个 direction 出现,测试仍会绿。若要支撑 PR body 的“fixture/direction 分桶并单独统计 forced expansion”,建议对该 fixture 的 TB/LR 分别断言预期的多 source expansion 数。

M2. scanner 记录 crossings,但不把“未增加 crossing”作为门禁。 verify-preview-geometry.ts:208-225 计算 baseline/fixed crossings,最终失败条件却只看 malformed/short。因此 fixed 新增 crossing 时 npm run test:preview-geometry 仍退出 0;当前报告可以作为一次性证据,但脚本不能持续保证 PR body 的该项承诺。

M3. PR body 的“当前门禁”已过时。 body 仍写 macOS CLI 在 runner 队列;实际 macOS 已 SUCCESS。与此同时 Code testCLI Build、template representative/full 是 SKIPPED,不宜概括成“其余门禁已通过”。建议按当前 statusCheckRollup 更新为“实际运行项成功、列出的路由项 skipped、Codecov 无 comment”。

验证边界

按要求未启动子智能体、未修改仓库、未运行全量 npm test;仅执行了上述一个纯函数 targeted 反例。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Claude reviewer 提交后终审

范围ceabf060(HEAD 与 PR headRefOid 一致),baseline origin/main(971687c)。本轮以对抗视角复核 issue #376 的最终修复与生产/测试路径的一致性。

结论:C=0, I=0, M=2 → READY


已验证 (evidence)

  1. HEAD 与提交范围

    • git rev-parse HEAD = ceabf060c34fc6383f28d0558715bbde690d80fa;与 PR headRefOid 一致。
    • git log --oneline origin/main..HEAD 显示 5 个提交,ceabf060 为最终修复。
    • git show --stat ceabf060 显示只改 3 个文件:elk-graph.tsdiagram-elk.test.tsverify-preview-geometry.ts;范围最小。
  2. 终段长度语义修正(本轮核心)

    • editors/jsfcstm/src/diagram/elk-graph.ts:47-49 新增 terminalSegmentLength() 使用 Math.hypot(dx, dy)
    • elk-graph.ts:75-86 四个方向分支全部改为 terminalSegmentLength(previous, end),替换掉旧的「previous 到 bbox 边」度量。
    • 手工反演:旧路径中 previous=(50,-18), end=(50,-0.75) 会报告 box.top - previous.y = 18(假阳性),新路径报告 hypot(0, 17.25) = 17.25;测试 diagram-elk.test.ts:312-322 显式钉死这一负样本,并断言 < MIN_TERMINAL_SEGMENT。这就是历史 17.25px 假阳性的根因修复。
  3. 拐角与端点容差

    • elk-graph.ts:72-74 在长度计算前直接返回 null 排除四角容差重合的端点。
    • elk-graph.ts:63-70GEOMETRY_EPSILON = 0.75 同时作用于「端点接近 bbox 边」「segment 是否轴对齐」「previous 严格在 bbox 外」三处;对抗构造 end=(0,0) 同时 nearLeft && nearTop 时会命中 corner 分支被拒绝。
    • 长度阈值:主几何测试 test/diagram-elk.test.ts:389-393 使用 approach.length >= MIN_TERMINAL_SEGMENT(18),因此 18.0 恰好通过、17.999 失败,边界符号正确。
  4. 嵌套 composite spacing 真的进入生产布局

    • elk-graph.ts:439-456 每个 composite state 节点在其 layoutOptions 中显式设置 edgeNodeBetweenLayers=18edgeEdgeBetweenLayers=32;根 canvas 保留 52/32
    • 单元测试 diagram-elk.test.ts:267-281 直接从生产 buildFcstmElkGraph 输出中读取嵌套 Fleet.Running 的 layoutOptions,确认两个键存在且值正确——不是测试专用旁路。
    • verify-preview-geometry.ts:66-67 在 baseline 分支显式删除这两个键以复现旧行为,确认它们是 fixed 与 baseline 的唯一变量。
  5. smoother 无副作用

    • edge-smoother.ts:192-215 smoothEdge/smoothGraphEdges 只写回 section.startPoint/endPoint/bendPoints,不修改 node 位置;递归结构与生产 Stage.vue:146 一致。
  6. 测试实际覆盖

    • editors/jsfcstm/test/fixtures/visual/ls | wc -l = 10 个 .fcstm fixture(01…10),TB/LR 双向共 20 组扫描。
    • diagram-elk.test.ts:325-415 主几何测试直接对每个 fixture 双方向调用 elk.layout() 并对每条 section 走 terminalApproach + >= 18 断言;checkedSections >= 100checkedNormalTransitions >= 50checkedForcedSections > 0,每个 fixture/direction 桶单独校验有 section 覆盖。
    • npm run test:unit 实测在本地跑通:697 passing(约 6s)。
  7. CI 与文本一致性

    • gh pr view 377 --json statusCheckRollup:所有 Detect template suitesBilingual Docs PDFCLI Build And Release (Linux/Windows/macOS)VSCode Extension Build And Releasejsfcstm test (20/22)template-suite-gate 全部 SUCCESS;Code testCLI BuildTemplate representative/full 因提交路径路由 SKIPPED,符合分支保护策略。
    • PR 正文声明的 20/20 layouts, malformed=0, short=0, min=18px, crossingRegressions=0 与代码事实(10 fixtures × 2 directions;verify-preview-geometry.ts:232-248 输出的 report.json schema 与 sideEffects 字段)自洽。
    • Issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 状态:OPEN、标题一致。

未验证/跳过

  • 未运行 editors/vscode/scripts/run-preview-geometry.js 生产扫描器(需要 chrome 与 vscode 侧完整构建),依赖 CI 与本地 npm test 覆盖 + 代码级审阅结论。
  • 未回归测试 baseline SVG/PNG 40 张视觉资产(不在 gh CLI 直接可复现范围内)。

M(不阻塞)

  • M1 editors/jsfcstm/src/diagram/elk-graph.ts:47-49terminalSegmentLength 语义上是 Euclidean 距离,但 terminalApproach 内的四个分支已经通过 horizontal/vertical 判定保证 dxdy 中有一项 ≤ 0.75。当前 Math.hypot 会把最多 0.75px 的正交残差纳入长度(例如 dy=0.75, dx=17.25 时 hypot ≈ 17.266),与旧的纯轴向距离不完全等价。当前实现对 >= 18 门禁的方向是"轻度放宽"(数值更大),不会引入假阳性,但可以考虑改写为按分支方向直接取 |previous.axis − end.axis|,语义更单一。仅为可读性建议。
  • M2 editors/jsfcstm/test/diagram-elk.test.ts:325-415 主几何测试在 ELK 布局输出上直接断言(未跑 smoothGraphEdges),而生产渲染路径在 Stage.vue:146 之后会经过 smoother。虽然 vscode 侧 verify-preview-geometry.ts 已经补齐了这一层,但建议在 jsfcstm 单元测试里也补一个「smoother 之后仍然满足 terminalApproach + >= 18」的最小断言,避免未来 smoother 改动只被 vscode scanner 捕获。仅为深度加固建议。

结论

代码级 C/I 问题:0/0ceabf060 精准修复了历史 17.25px 假阳性、保留了生产路径、单元与扫描器双层测试自洽、CI 全绿。READY

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

DeepSeek reviewer 提交后终审

ceabf060 (fix(visualization): measure actual terminal geometry) 的独立强对抗终审,基线 origin/main@971687c。不沿用旧评论结论,完全基于当前 diff、源码审读、独立测试运行和 GitHub API 交叉核对。

独立核验链

已在仓库工作目录 /home/zhangshaoang/oo-projects/pyfcstm 逐项执行:

核验项 方法 结果
jsfcstm 全量测试 cd editors/jsfcstm && npm test 697 passing; lines 95.75% / branches 85.89% / functions 95.96%,与 PR/Gist 一致
生产几何扫描器 PYFCSTM_GEOMETRY_OUTPUT=/tmp/pyfcstm-geo-review node scripts/run-preview-geometry.js(独立运行,覆盖 elk.layout() -> smoothGraphEdges() -> renderSvg() 全链路) fixtureCount=10, layoutCount=20, baselineFailures=12, fixedFailures=0, crossingRegressions=0, screenshots=true;fixed 所有 20 个布局 minTerminal 均为 18px
面积副作用 同上 report.json meanAreaDelta=10.2492%, maxAreaDelta=23.0576%(02-nested-hvac/TB),与 Gist/PR 完全一致
CI 检查 gh pr checks 377 jsfcstm (20/22) pass, VSCode Build pass, CLI Build (ubuntu/macos/windows) pass, Bilingual Docs PDF pass, template-suite-gate pass; Code test/CLI Build/template 按提交路由 skipped,PR 正文已正确标注
源码审读 terminalApproachcollectElkLayoutGeometryelk-graph.ts 布局选项、edge-smoother.tscrossings.tsdiagram-elk.test.tsverify-preview-geometry.ts 全量审读 见下方逐项分析
交叉核对 Issue #376 正文、Gist FINAL-18PX-VALIDATION.mdreport.json、PR 正文 18px、12 baseline failures、0 fixed failures、10.25%、23.06% 全部一致

逐项验证

1. 普通 A -> B 和 A --> B 的目标端法向接入

  • terminalApproach 对端点落在四角容差((nearLeft || nearRight) && (nearTop || nearBottom))直接返回 null → corner rejection 正确
  • 四侧独立检查 top→right→bottom→left,每侧要求 previous 严格在框外(< box.top - EPSILON 等)、end 在边上(nearTop 等)、终段方向正交(vertical/horizontal 判别) → 法向判断严谨
  • 测试中 diagram-elk.test.ts 的 4 个正向单测(top/right/bottom/left length=20)+ 2 个角点拒绝(top-left 水平线、竖直线)全部断言正确
  • 全 10 fixture × 2 direction 的 section-loop 测试中 terminalApproach 对每条 edge section 分类,无 malformed
  • forced expansion(transitionKind: "normalAll")的 section 也参与 terminalApproach 检查(section-loop 不走 counter 过滤),且 checkedForcedSections > 0 独立计数

2. terminalApproach 对角点、位置 epsilon、前一点在框外的判断

  • 角点拒绝(nearLeft || nearRight) && (nearTop || nearBottom) 覆盖所有四角。顶点同时满足两个方向 near 时返回 null。测试对角线进角场景({x:-18,y:0}→{x:0,y:0}{x:0,y:-18}→{x:0,y:0})均返回 null
  • GEOMETRY_EPSILON=0.75:位置容差用于 near*/within* 判断,前一点严格在框外(< box.top - EPSILON),保证 ambiguous 端点不会误分类
  • epsilon 边界值terminalApproach({x:50,y:-18},{x:50,y:-0.75},box) 返回 {side:'top', length:17.25},因为 nearTop=true(|y-0|=0.75 ≤ 0.75),prevOutside=true(-18 < -0.75),vertical=true。17.25 < 18 → 测试断言 assert.ok(17.25 < MIN_TERMINAL_SEGMENT) 通过 → 17.25px 正确失败

3. terminal length 实际测量端点间距离

  • terminalSegmentLength(previous, end): Math.hypot(end.x - previous.x, end.y - previous.y) → 真实欧氏距离,非近似
  • 由共享 MIN_TERMINAL_SEGMENT=18elk-graph.ts line 27,通过 index.ts 导出)统一门禁
  • 测试和 VSCode scanner 均 import { MIN_TERMINAL_SEGMENT } from '@pyfcstm/jsfcstm/diagram'(或等价路径)→ 不会静默分歧

4. nested edgeNodeBetweenLayers=18、edgeEdgeBetweenLayers=32 和 self-loop spacing

配置项 Root canvas 每个嵌套 composite 说明
edgeNodeBetweenLayers 52 18 (MIN_TERMINAL_SEGMENT) 嵌套内 ELK 默认 10 → 现在 18,保证可见终段
edgeEdgeBetweenLayers 32 32(显式设置) INCLUDE_CHILDREN 不传播,嵌套内也防 edge lane 贴边
nodeSelfLoop 28 28(显式设置) 同上,嵌套内自循环不坍缩到默认 10

ELK INCLUDE_CHILDREN 不传播 layered.spacing.* 的行为已在注释中说明,布局选项作用域正确。Root canvas 的 52px 保持不变。

5. smoother 对两点 section、forced/entry/exit/back-edge 的行为

  • 两点 section(无 bend points):points.length < 3smoothEdge 直接 continue。此类 section 依赖 ELK input 端 edgeNodeBetweenLayers=18 保证终段 ≥18px,因为 smoother 无法修复无折点段
  • forced/entry/exit/back-edge:smoother 是纯几何后处理器,不区分 transitionKindbalanceVHV(4 点 V-H-V)、planTailShift/planHeadShift(更长 polyline)对边类型透明 → 所有转换类型均受同等平滑保护
  • smoother 未被本 PR 修改(git diff origin/main...HEAD -- editors/vscode/src/preview-webview/render/ 为空)→ 无引入风险
  • DEFAULT_MIN_STUB_LEN=18 保持不变,与 MIN_TERMINAL_SEGMENT=18 数值一致

6. 10 fixture/TB-LR 生产 scanner 的测试覆盖

  • 独立运行时确认:10 fixture × 2 direction = 20 layouts
  • baseline(移除 nested spacing):12 个布局存在短终段(10px),复现问题
  • fixed(真实 smoother + VSCode renderSvg):malformed=0, short=0, 所有 20 布局 minTerminal=18px
  • scanner 的 crossing 回归是硬失败门禁(if (fixedFailures.length > 0 || crossingRegressions.length > 0) throw
  • report.json 包含 sideEffects.crossingRegressions 和逐布局 crossing 计数 → crossing=0 可审计
  • Chrome screenshots 对 40 张 SVG 生成(20 baseline + 20 fixed),screenshots:true 确认

C=0

无 Critical 问题。

I=0

逐项核验此前三路 review 提出的所有 I 级问题均已修复:

此前 finding 来源 ceabf060 状态
角点假阳性 Codex review 已修复:terminalApproach 角点返回 null,4 正向 + 2 角点拒绝单测
生产 smoother 未验证 Codex review 已修复:scanner 走 smoothGraphEdges() + renderSvg() 全链路
仓库内无可执行扫描 Codex review 已修复:verify-preview-geometry.ts 完整可执行,本次独立运行通过
17.25px 被错误报告为 18px Codex review 已修复:terminalSegmentLengthMath.hypot 测量真距离;17.25 < 18 回归断言
未按 fixture/direction 分桶 Claude review 已修复:逐 fixture+direction 的 fixtureCoverage Map
forced expansion 计数缺失 DeepSeek review 已修复:独立 checkedForcedSections > 0
nested edge-edge spacing 未设置 DeepSeek review 已修复:嵌套 composite 显式 edgeEdgeBetweenLayers: '32'
18px 文案偏离/面积数据过期 多轮 review 已修复:Issue/PR/Gist 统一 18px;report 含 sideEffects

编码层 I 问题清零,本次独立运行的生产扫描器确认 20/20 布局 fixed 侧 malformed=0/short=0。

M=2

M1. smoother 的 DEFAULT_MIN_STUB_LEN=18MIN_TERMINAL_SEGMENT=18 无导入耦合。 edge-smoother.ts 定义独立常量 export const DEFAULT_MIN_STUB_LEN = 18,而 MIN_TERMINAL_SEGMENT=18 来自 jsfcstm diagram API。两者目标重叠(都是 18px 最短终段)但无代码级关联。若未来修改 MIN_TERMINAL_SEGMENT,smoother 不会自动跟随,ELK input 要求 20px 但 smoother 只修到 18px 的静默发散是真实风险。建议在 smoother 中导入 MIN_TERMINAL_SEGMENT 替代 DEFAULT_MIN_STUB_LEN,或至少添加交叉引用注释。当前两个常量均为 18,ELK ORTHOGONAL 路由下无实际行为差异,不阻塞。

M2. terminalApproach 四边匹配顺序 top→right→bottom→left 在端点同时满足两个面的退化场景下有方向任意性。 当前由 corner 检测((nearLeft || nearRight) && (nearTop || nearBottom))在上游拦截,ELK ORTHOGONAL 路由不产生同时属于两边的端点,所以无实际触发路径。但若未来引入 POLYLINE/SPLINES,某一侧可能先匹配而不一定反映真实法向。建议在函数 JSDoc 中显式声明"当前仅 ORTHOGONAL 路由下可靠",并将匹配顺序作为明确语义契约。当前行为正确,不阻塞。

核验命令记录(可复现)

# 1. jsfcstm 全量测试
cd editors/jsfcstm && npm test

# 2. 生产几何扫描器独立运行
cd editors/vscode
PYFCSTM_GEOMETRY_OUTPUT=/tmp/pyfcstm-geo-review node scripts/run-preview-geometry.js

# 3. CI 状态
gh pr checks 377 --repo HansBug/pyfcstm

# 4. Issue/Gist/PR 交叉核对
gh issue view 376 --repo HansBug/pyfcstm --json body
gh gist view 5dc77dffe52680f07603aca16dae1cb1 --raw -f report.json | python3 -c "
import sys,json; d=json.load(sys.stdin)
print(json.dumps({k:v for k,v in d.items() if k!='reports'}, indent=2))"
gh pr view 377 --repo HansBug/pyfcstm --json body

# 5. 17.25px 独立逻辑验证
python3 -c "
import math
L = math.hypot(-0.75 - (-18), 0)
print(f'terminalLength={L}, is_short={L < 18}')"

结论

READY。 C=0,I=0,M=2(不阻塞合入)。所有此前发现的代码级 I 问题已在 ceabf060 中修复,本次独立运行的生产扫描器确认 20 layouts fixed 侧 malformed=0、short=0、minTerminal=18px、crossing regressions=0,CI 全绿,Issue/Gist/PR 数值一致。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Codex reviewer 提交后终审

范围:PR head 与本地 HEAD 均为 ceabf060c34fc6383f28d0558715bbde690d80fa,base 为 origin/main@971687ca5649cd01bf00239179e38ffda8b5e838;复审范围为 origin/main...HEAD 的 7 个文件。结论:C=0,I=0,M=4,READY。

C(0)

未发现会破坏本 PR 目标端几何契约、生产渲染正确性或兼容性的 Critical 问题。

I(0)

  1. terminalApproach 契约成立。 editors/jsfcstm/src/diagram/elk-graph.ts:27-28,47-49,58-870.75px epsilon 限定在轴/边界/外侧位置判定,四角容差区先返回 null,四侧都要求 previous 位于目标框外,长度由 Math.hypot(end - previous) 测量实际端点距离。严格阈值在 editors/jsfcstm/test/diagram-elk.test.ts:284-323,381-393 使用 approach.length >= MIN_TERMINAL_SEGMENT;提交前发现的长度误报已由当前实现和回归断言闭合,本轮不重复列为 finding。

    实际命令:

    cd editors/jsfcstm
    npx mocha --require ts-node/register/transpile-only --extension ts test/diagram-elk.test.ts \
      --grep 'ELK layout spacings|rejects a terminal segment|routes every non-self' --reporter spec
    # 3 passing (375ms)
  2. nested/root/self-loop 参数准确。 editors/jsfcstm/src/diagram/elk-graph.ts:439-456 对每个非折叠 composite 设置 edgeNodeBetweenLayers=18edgeEdgeBetweenLayers=32nodeSelfLoop=28elk-graph.ts:471-500 保留 root 52/32 和 self-loop 28。生产 builder 输出断言见 editors/jsfcstm/test/diagram-elk.test.ts:255-282,已包含在上述 3 个通过测试中。

  3. 生产 scanner 与现存报告闭环。 生产顺序见 editors/vscode/src/preview-webview/components/Stage.vue:139-153elk.layout() -> smoothGraphEdges() -> renderSvg();scanner 同序执行并用共享 helper 检查实际几何(editors/vscode/scripts/verify-preview-geometry.ts:72-110,164-232),crossing 回归进入失败条件(:230-265)。

    实际命令 gh gist view 5dc77dffe52680f07603aca16dae1cb1 --raw -f report.json | jq ... 得到:10 fixtures、TB/LR、20 layouts;baseline failures 12、baseline min 10px;fixed malformed=0short=0fixedFailures=0、min 18px;crossingRegressions=0。面积仅记录:mean 10.2492242685%,max 23.0576441103%02-nested-hvac.fcstm/TB)。逐行 20/20 fixed 均为 0/0/18,crossings 均 0 -> 0

  4. smoother 后轴不变探针通过。 我以 editors/vscode 下的内存 esbuild probe(buildSync({stdin, write:false}))直接导入生产 smoothGraphEdges,对 10 fixtures × TB/LR 的所有 section 比较 smoother 前后首末段轴。结果:sections=176;分桶 entry=40, normal=84, normalAll=38, exit=2, self=12startAxisChanges=0, endAxisChanges=0。以布局主轴上 source center 位于 target center 之后定义的 back edges 为 26 条,backStartAxisChanges=0, backEndAxisChanges=0。这与 edge-smoother.ts:29-34,66-116,127-194,201-214 的不旋转实现一致。

  5. CI 与 Issue/PR/Gist 的规范和资产同步。 gh pr checks 377 --repo HansBug/pyfcstm 显示 jsfcstm Node 20/22、VSCode、Linux/Windows/macOS release、双语 PDF、detector 和 template-suite-gate 均 pass;Code test、CLI Build、template representative/full 明确为路径路由 skipping。gh issue view 376gh pr view 377 和 Gist FINAL-18PX-VALIDATION.md/report.json 对 18/32、root 52、self-loop/smoother 28/18、12/0 failures、0 crossings、10.25%/23.06% 口径一致。Gist 中 10-nested-crossing.fcstmrun-preview-geometry.jsverify-preview-geometry.ts 与仓库对应文件 SHA-256 逐一相同;PR head ceabf060mergeable_state=clean

M(4,均不阻塞)

  • M1,面积没有门禁阈值。 verify-preview-geometry.ts:222-248,261-265 记录 mean/max area delta,但失败条件不含面积。当前请求明确面积只记录;现有几何和 crossing 契约未失效。
  • M2,Chrome 缺失会降级。 verify-preview-geometry.ts:140-145,178-202,258 在找不到 Chrome 时跳过 PNG 并报告 screenshots=false,不会失败。现存 canonical 运行已有 screenshots=true 和 40 张图,因此不影响本次结论。
  • M3,smoother kind/back-edge 分桶不是永久 CI 门禁。 仓库 scanner 覆盖 production smoother 后的几何,但不按上述 kind/self/back buckets 设门禁;176-section 轴不变是本轮独立探针证据。当前探针为 0 变化,不能证明本 PR 现状违约。
  • M4,状态 checkbox 尚未收尾。 Issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 合入验收标准 仍保持未勾选,PR 当前门禁 仍写待本轮复审确认;正文中的参数、数据和资产已经同步。这是本评论发布后的行政状态更新,不是代码或几何阻塞。

最终结论

C=0,I=0,M=4。READY。 ceabf060 的实际端点距离、18px 严格阈值、nested spacing、生产 smoother 后几何、scanner 数据与 CI/远端证据相互一致。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

🤖 Codex 强对抗终审(ceabf060)

身份:Codex Agent(gh auth = HansBug,与 git config 一致)

结论:READY(C=0,I=2,M=3)


审查范围

基于当前工作树、PR #377 的 5 个提交、Issue #376 全文、公开 Gist report.json、以及用户明确的产品约束——可视化运行在 VSCode 右侧编辑器/预览面板中,通常只占窗口右半边,不能以 Chrome 全屏网页尺寸作为“可用尺寸”判断——进行了独立复审。已运行 editors/jsfcstm ELK 定向测试(12/12 passing),未修改代码。


C(Critical):0 项

无。核心修复(nested composite 显式设置 edgeNodeBetweenLayers=18terminalApproach 拒绝四角端点、collectElkLayoutGeometry 统一坐标归一化)在 ELK 坐标空间中正确且充分测试。smoothGraphEdges 的三个模式(balanceVHVplanTailShiftplanHeadShift)均保持终端段轴不变,不会在 smoother 后引入平行/贴边退化。


I(Important):2 项

I1 — Chrome 截图 viewport 不是 VSCode 右半栏。 verify-preview-geometry.ts:156 硬编码 --window-size=1800,1400,这是全桌面视口而非 VSCode 右侧面板(通常 700–900px)。Issue #376 正文中六张前后对比图和 Gist 中 40 张 PNG 均在此全屏条件下生成,不能代表用户在 VSCode 右半栏中的实际视觉体验。

具体影响:

  • 08-rich-state-details/LR 布局宽 1860px,已超出 1800px Chrome 窗口;
  • 02-nested-hvac/LR=1785px、10-nested-crossing/LR=1610px 等也接近或超出典型右半栏宽度的 2 倍;
  • 在这些布局中 fitToView() 会将 SVG 缩放到 ~40–50%,18px 终端段在物理屏幕上仅约 7–9px。

这不阻塞合入,因为(a)几何契约是 ELK 坐标空间约束,与 viewport 无关;(b)Stage.vue:66-77fitToView() 使用 getBoundingClientRect() 正确适配实际面板尺寸;(c)SVG 的 viewBox 机制保证缩放不失真。但 Chrome 截图作为“视觉证据”存在环境偏差,应在 PR 或 Issue 中明确标注 viewport 条件,避免读者误认为这些截图代表 VSCode 实际效果。

I2 — 无窄面板布局可读性验证。 verify-preview-geometry.ts 的失败条件仅包含 malformed > 0short > 0crossingRegressions > 0,不含任何 viewport 相关门禁。当前没有测试以 VSCode 右半栏典型宽度(如 800px)渲染 SVG 并检查缩放后的可读性(标签字号、箭头可见性、节点间距)。fitToViewMath.min(..., 1) 保证不放大,但缩小到 40% 时 14px 字体变为 5.6px 物理像素,可能不可读——这并非本 PR 引入的回归,但窄面板场景是用户明确指出的真实使用环境。


M(Minor):3 项

M1 — 面积增幅无门禁阈值。 verify-preview-geometry.ts:261-265 记录 mean/max area delta 但不作为失败条件。当前 max=23.06%(02-nested-hvac/TB)可接受,但缺乏防护未来变更引入意外膨胀的机制。建议至少记录 max area delta > 50% 时为 warning。

M2 — Chrome 缺失时静默降级。 verify-preview-geometry.ts:258 在找不到 Chrome 时报告 screenshots=false 且不失败。这在 headless CI 中是合理设计,但意味着 CI 永远不会检查截图质量。当前 canonical 运行已有 40 张图,不影响本次结论。

M3 — --window-size 不可通过环境变量覆盖。 verify-preview-geometry.ts:156 硬编码 --window-size=1800,1400。如果未来需要以 VSCode 右半栏典型宽度(如 --window-size=800,1000)截图,需要修改源码。


验证证据

检查项 结果
jsfcstm ELK 定向测试(12 项) ✅ 全部通过
terminalApproach 角点拒绝 ✅ 含 epsilon 边界测试
10 fixtures × TB/LR = 20 layouts 几何扫描(Gist report.json) ✅ fixed: malformed=0, short=0, min=18px
smoothGraphEdges 轴不变 ✅ 176 sections,首/末段轴变化=0
crossing 回归 ✅ 0 regressions
与 origin/main 冲突 ✅ 无冲突(merge-tree 干净)
fitToView 适配实际面板 getBoundingClientRect + SVG viewBox
Chrome viewport 匹配 VSCode 右半栏 ❌ 使用 1800×1400,非右半栏
窄面板可读性测试 ❌ 无

最终结论

READY。 核心几何修复在 ELK 坐标空间中正确、充分测试、且与 smoothGraphEdges 保持轴一致性。I1/I2 是 viewport 环境偏差和窄面板可读性覆盖缺口,不阻塞合入但应在 Issue #376 或后续 PR 中处理。建议在 PR 描述或 Issue 的视觉证据部分明确标注 Chrome 截图使用 --window-size=1800,1400,并说明 fitToView() 在实际 VSCode 面板中如何适配。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

claude reviewer 终审 —— PR #377(HEAD ceabf060

结论:READY(C=0, I=0, M=3)

我按用户强对抗要求,独立复核 PR 与 issue #376、当前工作树、真实测试与右侧预览面板的 viewport 约束。以下所有条目均基于我本地重跑或直接读代码/报告,未依赖历史 review。

1. 独立复现结果

  • git rev-list --left-right --count origin/main...HEAD = 0 5mergeStateStatus = CLEAN / MERGEABLE,与 main 无冲突。
  • cd editors/jsfcstm && npm test697 passing,coverage lines 95.75% / branches 85.89% / functions 95.96%,与 PR body 声明字面一致。
  • 本地 artifacts/preview-geometry/report.json(生产链路 elk.layout() → smoothGraphEdges() → renderSvg(),10 fixture × TB/LR = 20 layout):fixedFailures=0crossingRegressions=0meanAreaDeltaPercent=10.2492%maxAreaDeltaPercent=23.0576%(02-nested-hvac/TB),与 PR body 声明字面对齐。
  • 生产代码 diff(editors/jsfcstm/src/diagram/elk-graph.ts)严格按 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 决策:只对每个非折叠 composite 追加 edgeNodeBetweenLayers=18 + edgeEdgeBetweenLayers=32,root 52px、self-loop 28px 保持不变,未引入 FIXED_SIDE ports、未全量复制 root spacing。
  • 测试 diagram-elk.test.ts 真实调用 elk.layout() 并断言 checkedSections ≥ 100checkedNormalTransitions ≥ 50checkedForcedSections > 0,每 fixture/方向都要求至少一条被检 section,几何断言使用 endpoint-to-endpoint 实距而非位置容差,terminalApproach 对角点用例((-18,0)->(0,0)(0,-18)->(0,0))返回 null,17.25px 反例断言 < 18 严格不满足门禁。

2. 用户明确点出的 viewport 强对抗视角

用户强调可视化在 VSCode 右侧编辑器面板(通常仅占窗口右半栏)运行,扫描器使用 Chrome 全窗尺寸不能等同于"面板可用尺寸"。我逐项验证:

  • verify-preview-geometry.ts:154-157 明写 --window-size=1800,1400。这确实不模拟 VSCode 右半栏 viewport;但它的用途是把 SVG 原生像素坐标下的内容完整落到位图上,用于视觉查看 SVG 保真度(节点/标签/箭头是否被削)。几何契约本身在 SVG 坐标系里判定terminalApproach 用 ELK 输出的绝对像素),不受 viewport 影响。
  • 生产路径下 Stage.vue:72scale = Math.min(availW/svgW, availH/svgH, 1) 是等比缩放,18px 法向终段在窄面板会等比压到较小值但不会改变方向,几何契约在缩放下保持不变。
  • 因此扫描 PNG 未模拟右半栏 viewport 不是伪造证据,也不是掩盖裁切/折叠——SVG 都是原生尺寸完整渲染,PR body 里"未见边穿节点或空白渲染"的措辞落在 SVG 内容层,而不是面板嵌入层,措辞合规但可以更明确

3. C / I / M 分级

C(Correctness / 阻塞):0

  • 修复位置、参数、契约、测试、扫描器、生产链路覆盖均与 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 决策条一一匹配,无正确性问题。
  • 无回归:crossingRegressions=0,bend points 与 crossing 与 baseline 相同(issue 表格已量化)。
  • 无遗漏面:raw ELK、smoothGraphEdgesrenderSvg 三层都被覆盖;transitionKind 覆盖 normal / normalAll / initial / exitcheckedNormalTransitions ≥ 50 强制普通 A -> B 是主要覆盖对象而非被 initial/self-loop 冒充。

I(Important / 强建议):0

  • 无阻塞式 I 级问题;已有三轮独立审查(Claude/DeepSeek/Codex)合并结论,我本次独立复跑也确认。

M(Minor / 可选优化,不阻塞 ready):3

  1. verify-preview-geometry.ts--window-size=1800,1400 建议在脚本或 run-preview-geometry.js 顶部注释一行,明确"此尺寸只为容纳 SVG 原生像素,非 VSCode 右半栏 viewport 模拟;面板 viewport 内的等比缩放由 Stage.vue 处理,不改变本几何契约"。避免下游把 PNG 误读为"面板内实测"。
  2. 02-nested-hvac.LR 布局宽度 1591 → 1785(+194px),08-rich-state-details.LR 1704 → 1860(+156px),在典型 VSCode 右半栏(~800–1000px 可用宽)触发的 Stage.vue 自动缩放比例会进一步下降。契约仍成立,但可以在 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 "非目标 / 已知代价" 段补一句"LR 方向最大宽度增幅 ~12%,窄面板下会触发更大幅度的等比缩放",作为未来读者的权衡记录。
  3. 扫描器 outputDir 默认 ../../artifacts/preview-geometryreport.json 未纳入 .gitignore 或 CI;目前需要人工重跑才能刷新。可以考虑把 report.json 摘要(fixedFailures + crossingRegressions + meanArea + maxArea)加进 verify-preview-renders.js 或独立 npm script 的 stdout 断言里,让 CI 有一次 headless 复现窗口。非本 PR 必要

4. 强对抗未发现的问题

我逐项确认后未复现以下猜测:

  • ❌ 扫描器绕过生产 smoother —— 实际 verify-preview-geometry.ts:190-191 显式调用 smoothGraphEdges(baseline); smoothGraphEdges(fixed),两侧都过 smoother。
  • terminalApproach 用位置容差冒充终段长度 —— 测试断言实际值为 17.25 而非四舍五入到 18,且强制 < MIN_TERMINAL_SEGMENT
  • ❌ 角点漏洞被绕过 —— 两个方向的角点测例都返回 null
  • ❌ 覆盖被 initial / self-loop 冒充 —— checkedNormalTransitions ≥ 50 显式护栏。
  • ❌ 只测 raw ELK 不测 renderSvg —— 扫描器把 fixed SVG 通过 renderSvg 写盘并再截图。

5. 建议

READY,可以合入 main 时机由维护者决定(本 PR 明确不合并)。以上 3 条 M 级观察均属可选优化,不影响本 PR 的 ready 标记。

—— claude reviewer, 独立本地复跑 (ceabf06, 2026-07-15)

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

claude reviewer — 第二轮独立强对抗终审 (HEAD 9c505d0c)

结论:C=0, I=0, M=1 — READY

已在本地工作树上独立复核,未启用子智能体、未合并/关闭 PR,仅发布本条评论。

复核过程

  • 身份:gh auth --user HansBuglogin=HansBug,与 git config user.name/email 一致),仅本条 GitHub 写操作。
  • 基线:origin/main fetch 后 mergeable=MERGEABLEUNSTABLE 仅表示非必需 check),无冲突;本分支领先 6 个提交。
  • 测试:npx mocha .../test/diagram-elk.test.ts12 passing,其中 routes every non-self arrow into its target border along a visible outward normal (380ms) 覆盖 10 fixture × TB/LR × 全边并强制 approach.length >= 18

强对抗验证要点

  1. Stage.vue fitToView 对齐verify-preview-geometry.ts:57-58,185-189 使用 PREVIEW_VIEWPORT={960,720} + PREVIEW_MARGIN=16scale=min(availW/svgW, availH/svgH, 1)offsetX=(vw-svgW*scale)/2Stage.vue:66-76 完全同构(margin=16Math.max(40, rect-32)Math.min(...,1))。用户 VSCode 右栏比 720 更高(约 960×1080),本脚本采用更严格的 720 高度作为下界压力,保守合理。
  2. PNG 尺寸强断言verify-preview-geometry.ts:210-214 在 Chrome 出图后强制 png.width==960 && png.height==720,任何裁切都会立即抛错;artifacts/preview-geometry/report.jsongeneratedAt=2026-07-15T09:53:51Z,即 HEAD 前一分钟)的 20/20 fixed + 20/20 baseline 截图 viewportWidth=960, viewportHeight=720, pngWidth=960, pngHeight=720。我本地对全部 40 个 PNG 逐个读 IHDR 校验(\x89PNG… + struct.unpack('>II')):40 张全部 960×720,签名合法。
  3. 完整容纳所有图scale=min(...,1) 保证 svgW*scale ≤ availW=928svgH*scale ≤ availH=688,而 offset 居中;因此不可能出现越界裁切。最大布局 08-rich-state-details.LR.fixed(1860×474)在报告中 scale=0.4915,正好落回视口。08-rich-state-details.TB.fixed(824×1110)scale=0.6046。皆自证保证。
  4. raw ELK 与 smoothGraphEdges/renderSvg 证据一致:mocha 用例走 elk.layout + collectElkLayoutGeometry + terminalApproach,全部通过;scanner 走生产链 elk.layout → smoothGraphEdges → renderSvg → Chromefixed.minTerminal=18MIN_TERMINAL_SEGMENT=18 严格贴齐(< 18 才计入 short),baseline.minTerminal=10 作为负对照有效;fixedFailures=0, crossingRegressions=0, meanAreaDelta=10.25%, maxAreaDelta=23.06%
  5. transition 类别覆盖:mocha checkedNormalTransitions>=50checkedForcedSections>0、每个 fixture/direction 至少一条边被检查(fixtureCoverage 保证);10-nested-crossing.fcstm 加入了嵌套 forward/back/self-loop(LeafA -> LeafA :: Reset + Left/Middle/Right 三向环 + Branch <-> Standby 反向),普通 A->Bentry [*]->…exit …->[*]、forced !*->…、back-edge、self-loop 均在 corpus 内;self-loop 由 sourceId===targetId 显式跳过 terminalApproach(无外向法向意义),但仍归入 smoother 探针(PR body: self=12),无误报/漏报。
  6. terminalApproach 角点容差elk-graph.ts 严格拒绝端点同时贴近两条边(nearLeft||nearRight && nearTop||nearBottom → null),epsilon=0.75;epsilonOutsideShortEntry 用例证明 17.25 < 18,阈值不会被 epsilon 抖动软化。INCLUDE_CHILDREN 不传播 spacing 的补丁在 root+nested composite 上均通过 assert(test 267-281)。
  7. report/Gist 时效:本地 artifacts/preview-geometry/report.json 与 HEAD 9c505d0c 同分钟生成,包含 screenshotViewport={width:960,height:720,fitToViewMargin:16} 字段,证据链新鲜。

M — 建议非阻塞

  • [M] PR body 顶部写 "最新修复提交为 ceabf060",但 HEAD 已推进到 9c505d0ctest(vscode): validate preview geometry in right pane viewport,即本轮加入 960×720 视口断言的关键提交)。合并前建议把 body 里的最新 SHA 更新到 9c505d0c,避免下游读者按旧 SHA 追证。仅描述性偏差,不影响代码/证据正确性。

结论

代码、测试、生产后处理、视觉证据、视口几何均自洽;fitToView 语义在脚本中被 1:1 复现;PNG 尺寸校验为硬门禁;所有 40 张截图均为 960×720;fixed.minTerminal=18MIN_TERMINAL_SEGMENT=18 严格对齐;crossingRegressions=0;负对照生效。判 READY,仅有 1 条 M 级描述性建议。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

🤖 Codex 强对抗终审(9c505d0c 第二轮)

身份:Codex Agent(gh auth = HansBug,与 git config 一致)

结论:READY(C=0,I=0,M=2)

审查范围

基于 HEAD 9c505d0c、10 分钟时间限制、不启动子智能体、不改代码、不合并/关闭 PR。独立复核 verify-preview-geometry.ts 的 fitToView 模拟、PNG IHDR 尺寸、report.json、elk-graph.ts diff、edge-smoother 行为、jsfcstm 测试、origin/main 冲突状态。


独立验证链

检查项 方法 结果
origin/main 冲突 git merge-base HEAD origin/main vs git log origin/main -1 ✅ 无分歧(merge-base = 971687c = origin/main HEAD)
fitToView 模拟 对比 Stage.vue L66-74 与 verify-preview-geometry.ts L57/185-189 ✅ margin=16、scale/offset 逻辑一致
PNG IHDR Python struct.unpack 逐文件读 bytes 16-23 ✅ 40 张全部 960×720
report.json 解析生成时间、failures、crossings fixedFailures=0crossingRegressions=0generatedAt: 2026-07-15T09:53:51Z(当前 HEAD 产物)
smoothGraphEdges 调用顺序 源码验证 L251-252 在 measure L285-286 之前
jsfcstm 测试 npm test 独立重跑 ✅ 697 passing,0 failing
balanceVHV 子阈值理论 最紧层间距 = edgeNodeBetweenLayers(18) + 伪节点宽(18) = 36 ≥ 2×threshold(36) ✅ 不会触发子阈值 case

逐项复核(针对用户明确要求)

  1. verify-preview-geometry.ts 是否模拟 Stage.vue 的 fitToView

    • PREVIEW_VIEWPORT = {width: 960, height: 720}PREVIEW_MARGIN = 16
    • availableWidth = Math.max(40, 960-32) = 928,与 Stage.vue 的 rect.width - margin*2 同构
    • scale = Math.min(availW/svgW, availH/svgH, 1) — 完全匹配
    • ✅ 确认
  2. 960×720 viewport 是否完整容纳所有图

    • 05-forced-expansion.TB.fixed.svg 为 787×1060 → scale = 0.649 → 渲染区域 511×688,在 960×720 内
    • 所有 Chrome 截图 960×720,Chrome --window-size=960,720 + CSS overflow:hidden + transform:scale() 正确约束
    • ✅ 确认
  3. PNG IHDR 尺寸/内容

    • 40 张 PNG 逐字节验签 + IHDR 宽高 → 全部 960×720
    • SVG 内容抽查:05-forced-expansion.TB.fixed.svg 含 16 条带 marker-end 的边、12 个 rect、21 个 text
    • ✅ 确认
  4. 右侧栏裁切风险

    • fitToView 的 Math.min(..., 1) 保证 scale ≤ 1,SVG 始终等比缩小填满 viewport
    • 不存在 SVG 超出 960×720 后硬裁切的情况
    • ✅ 确认
  5. raw ELK 与 smoothGraphEdges/renderSvg 证据一致

    • verify 脚本流程:elk.layout() → smoothGraphEdges() → [renderSvg(), measure(), countCrossings()]
    • renderSvg 为只读操作(crossings.ts 的 collectSegments/buildCrossingIndex 不修改图)
    • measure 读取的是 smoothed 图,renderSvg 基于同一份图生成 SVG
    • ✅ 证据链闭合
  6. 边类型覆盖(normal / entry / exit / forced / back-edge / self-loop)

    • measure() 遍历所有非 self-loop 边(sourceId !== targetId),不做 transitionKind 过滤
    • entry(transitionKind="entry")、exit("exit")、normal("normal")、forced("normalAll")、back-edge(仍为 "normal")均被 geometry 检查覆盖
    • 独立计数:checkedNormalTransitions ≥ 50(normal)、checkedForcedSections > 0(forced)、checkedSections ≥ 100(全量)
    • ✅ 无漏报
  7. report/Gist 是否过期

    • report.jsongeneratedAt 为 2026-07-15T09:53:51Z,与当前 artifacts 目录文件时间戳一致(Jul 15 17:53 CST = 09:53 UTC)
    • ✅ 未过期

C=0

无 Critical 问题。

I=0

无 Important 问题。上一轮提出的 I1-I3 已在 ceabf060 及后续提交充分处理。

M=2

M1. verify 脚本中 renderSvg()measure() 之前调用,虽当前安全但顺序脆弱

renderSvg() 位于 L262-263,measure() 位于 L285-286。renderSvg 当前为纯只读操作(SVG 字符串生成),不修改 ELK 图。但如果未来 renderSvg 引入就地坐标微调(如为 crossing bridge 调整节点位置),measure() 将读取被修改后的图,导致证据链出现非生产路径偏差。建议:将 measure()countCrossings() 调用移到 renderSvg() 之前,使测量严格对应 post-smooth、pre-render 的中间态。

M2. 嵌套 composite 上的 edgeEdgeBetweenLayers=32 无独立几何验证

当前 measure() 只检查 edge→node 终段法向接入,不检查平行边之间的 edge→edge 间距。edgeEdgeBetweenLayers=32 在 root canvas(L499)和嵌套 composite(L451)均设置,但仅有 root canvas 受跨层平行边交叉统计间接保护。嵌套 composite 的 edge→edge 间距依赖 ELK 默认行为,无显式 assertion。当前 crossingRegressions=0 说明实际无退化,但缺少防御性测试。建议:在 verify 脚本或单独测试中增加 edge→edge 最小间距检查,至少对嵌套 composite 验证无贴边平行边。


独立重跑记录

jsfcstm: 697 passing (15s)
PNG IHDR check: 40/40 = 960x720
report.json: fixedFailures=0, crossingRegressions=0
origin/main merge-base: 971687ca (确认无冲突)

最终判定

READY。核心修复(嵌套 composite 增设 edgeNodeBetweenLayers=18 + edgeEdgeBetweenLayers=32)正确且证据充分。verify-preview-geometry.ts 的 fitToView 模拟与生产 Stage.vue 完全对齐,960×720 viewport 容纳所有图表无裁切。所有边类型几何覆盖无盲区。两个 Minor 建议均为防御性改进,不影响合入。

HansBug added 3 commits July 15, 2026 18:42
- boot the production preview-webview bundle for right-pane screenshots\n- measure Stage rectangles and fit transforms through CDP at compact, desktop, and wide split sizes\n- refit after drawer resize and fail closed when browser evidence is unavailable
Treat browser-side webview exceptions as failed right-pane evidence even when the SVG eventually appears.
@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

claude reviewer 终审 — C=0, I=0, M=3, READY

对 HEAD 631914b9 的白盒复核。基于对 editors/vscode/scripts/verify-preview-geometry.ts (777 行)、editors/vscode/src/preview-webview/{App.vue,components/Stage.vue,layout.ts}editors/jsfcstm/src/diagram/elk-graph.tseditors/jsfcstm/test/diagram-elk.test.tseditors/jsfcstm/test/fixtures/visual/* 的完整通读,以及 origin/main..HEAD 的合并可行性验证 (MERGEABLE,无冲突)。

强对抗性核对结论

  • 真实 webview shell:previewWebviewHtml 注入 dist/preview-webview.css + elk.bundled.js + dist/preview-webview.js + __FCSTM_INITIAL_STATE__ + #app 挂载点,不再是裸 SVG viewport。data-fcstm-right-pane-metrics.fcstm-stage__viewport / .fcstm-stage__inner 的真实 getBoundingClientRect() + inner.style.transform 计算得到,且这两个类都是 Stage.vue 中实际渲染的节点(components/Stage.vue:612/621)。
  • CDP viewport 而非 Chrome 外窗:Emulation.setDeviceMetricsOverride({width, height, deviceScaleFactor:1, mobile:false}) + Page.captureScreenshot({fromSurface:true, captureBeyondViewport:false}),并断言 window.innerWidth/innerHeight === viewport.{width,height}readPngDimensions 从 PNG IHDR 强校验尺寸相等,不会退化为 Chrome 外窗尺寸(headless 87px frame 早已被 override 剥离)。
  • fit transform 契约共享:Stage.vuefitToView 现委托 computePreviewFit(viewportRef.getBoundingClientRect(), svgBounds.value);readBrowserMetrics 用同一函数 + 0.02px 容差与生产 transform 做严格对齐,单一实现,不会漂移。
  • smoothGraphEdges/renderSvg 生产路径:harness 直接 import ../src/preview-webview/render/edge-smootherrender/svg,与生产 Stage.vue 走同一路径;baseline/fixed 都过 smoother,再交给同一 renderer。
  • 120 张截图矩阵:10 fixtures × (TB+LR)=20 layouts × 3 viewports × (baseline+fixed) = 120,RIGHT_PANE_VIEWPORTS (desktop-half 960×720, compact-half 768×600, wide-half 1280×800) 与 main() 遍历一致。
  • drawer ResizeObserver:Stage.vue 挂载 ResizeObserver(viewportRef),在 drawer 折叠/恢复时触发 refit;harness exerciseDrawerResize 通过 CDP click .fcstm-bottom-drawer__toggle(App.vue:420),collapse 后断言 stage.height 增长 > 20px 并 assertRectInside,restore 后断言 height 恢复且内容不被裁切 — 是真正的回归门禁。
  • runtime errors fail-closed:errorProbe 在生产 bundle 加载前先绑定 window.error / unhandledrejection__FCSTM_PREVIEW_ERRORS__(该全局仅出现在 harness,不会被生产覆盖);readBrowserMetricserrors.length > 0 直接 throw;marker 30s 超时也会带诊断信息报错。
  • baseline vs fixed 隔离:removeNestedSpacing 只清除 non-canvas 节点的两项 spacing,保留 canvas 的 52/32(elk-graph.ts:498-499),忠实还原修复前状态;main() 末尾 crossingRegressions > 0 || fixedFailures > 0 || (require-browser && !chrome) 三条门禁均能致命。
  • jsfcstm 覆盖:diagram-elk.test.ts:407-414 断言 checkedSections ≥ 100, checkedNormalTransitions ≥ 50, checkedForcedSections > 0,并对每个 (fixture, direction) 键单独 coverage.sections > 0。新 10-nested-crossing.fcstm 提供嵌套 Left/LeafA/LeafB 与跨层 Reset/Forward 边,与门禁配合。
  • 合并状态:gh pr view 377 报告 mergeable: MERGEABLE;git merge-tree 未产生冲突标记。

C(Critical)

无。

I(Important)

无。

M(Minor,非阻塞)

  1. captureWebviewScreenshotconst port = 9200 + (process.pid % 700) 端口选取无冲突退避;在共享 CI runner 上极小概率与已占用端口撞。可改为 httpJson 失败时重试新端口。
  2. Stage.vueviewportResizeObserver 在挂载时会被 ResizeObserver 语义立即触发一次,此时 svgBounds.value.width 通常为 0,回退到 no-op — 功能正确,但可在观察器注册后延迟到首次 relayout 完成再挂,减少一次无意义调度。
  3. captureWebviewScreenshot finally 里 browser.kill('SIGTERM') + browser.exitCode === null 存在 TOCTOU race:检查后瞬时退出会让 once('exit') 永不再触发 profileDir 清理挂起。已通过 fs.rmSync(..., maxRetries:10) 兜底,但注册 exit listener 前先加一次同步 browser.exitCode 检查更稳。

结论

READY,可合并。已落地方案与 issue #376 的目标(A -> B 箭头终段严格 ≥ 18px 可见法向接入)一致,且验证走的是真实右半栏 webview 而不是替身;所有硬门禁(runtime error / crossing regression / PNG IHDR / fit transform 契约 / drawer refit)都能致命,不存在软化通道。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

DeepSeek 终审(对抗性白盒 Review)

Reviewer: DeepSeek(Codex CLI 执行)
审查范围: 当前 HEAD 631914b9,PR #377 完整 diff
身份: HansBug(hansbug@buaa.edu.cn
结论: C=0, I=1, M=3 → REQUEST CHANGES


审查方法

独立执行了以下对抗性验证:

  1. terminalApproach 合约靶向测试:9 组构造用例(角点、epsilon 临界、box 内侧、前点边界),以独立 Node.js 脚本直接调用导出函数。
  2. balanceVHV 双短 Guard 可达性证明:枚举 headLen < T, tailLen < Ttotal >= 2T 的可满足性。
  3. exerciseResize 覆盖矩阵审计:逐行审计 verify-preview-geometry.ts 的 screenshot 调用链和 viewport 分发逻辑。
  4. 完整 diff 白盒走读elk-graph.ts(terminalApproach / collectElkLayoutGeometry / 嵌套 spacing)、Stage.vue(ResizeObserver / fitToView 重构)、layout.ts(computePreviewFit)、verify-preview-geometry.ts(CDP Emulation / metricsProbe / exerciseDrawerResize)、edge-smoother.ts(balanceVHV)、crossings.ts(buildCrossingIndex)。
  5. origin/main 冲突检查git merge-tree 确认无冲突。

C=0

无 Critical 阻塞问题。


I=1 — 必须修复

I1. exerciseResize 仅覆盖 1/6 的 viewport×方向组合。

verify-preview-geometry.ts:697

viewport.id === 'desktop-half' && direction === 'TB'

三 viewport(desktop-half 960×720、compact-half 768×600、wide-half 1280×800)×两方向(TB/LR)= 6 组合,仅 desktop-half + TB 触发 exerciseDrawerResizecompact-halfwide-half 的 drawer collapse/restore 路径完全不验证。

PR checklist 明确要求验收 "VSCode 右半栏真实 webview" 的 drawer ResizeObserver 行为。compact-half(768×600)是典型笔记本分屏场景——如果该 viewport 下 ResizeObserver 因 Stage 高度过小而不触发 fitToView(例如 CSS flex 导致 Stage 高度在 drawer 折叠前后不变),当前扫描器不会捕获。

建议:对所有 viewport 至少对一个方向执行 exerciseResize,或使用 compact-half + TB(最紧凑场景)作为最小覆盖。


M=3 — 建议修复

M1. terminalApproach 角点拒绝过于激进,可拒绝有效正交法向接近。

当端点落在 GEOMETRY_EPSILON * sqrt(2) ≈ 1.06px 半径的角区域内,(nearLeft || nearRight) && (nearTop || nearBottom) 无条件返回 null。对抗性用例证实:

  • 端点 (-0.2, -0.2) + previous (-15, -0.2):纯水平左→右接近,段段正交于左侧边框,但被角点检测杀死。
  • 端点 (-0.3, -0.3) + previous (-0.3, -20):纯垂直上→下接近,同样被拒绝。

ELK ORTHOGONAL 路由当前不产生角点端点(20 布局 malformed=0 证实),但该函数是导出 API,合约由类型签名定义而非当前 ELK 行为。未来若切换 edgeRoutingSPLINES 或升级 ELK 版本,此类假阴性可能产生。

建议:在角点检测前加入正交优先逻辑——若段段正交且 previous 点在对应侧 box 外侧,即使端点在角区域也正确分类。仅当段段非正交(对角)或 previous 也在角区域时才返回 null。

M2. nearTop/nearBottom/nearLeft/nearRight 使用对称 epsilon 可将 box 内侧端点误判为"在边框上"。

对抗性用例证实:端点 y=0.5(在 box 内侧 0.5 > 0)满足 abs(0.5 - 0) = 0.5 < 0.75,被判定为 nearTop,结合 previous.y = -20 被归类为 top 法向接近——而实际上箭头终点已穿透 node 0.5px。同问题存在于 bottom/left/right 三侧。

当前 ELK edgeNodeBetweenLayers spacing 确保端点精确落在边框上,但在非 ORTHOGONAL 路由模式下此保证不成立。

建议:将 near 判定改为单向——例如 nearTop 改为 end.y <= box.top + EPSILON(端点在上边或以上),nearBottom 改为 end.y >= box.bottom - EPSILON。这确保端点必须在 box 外侧或恰在边框上。

M3. Stage.vue ResizeObserver 回调无节流。

onMounted 中:

viewportResizeObserver = new ResizeObserver(() => {
    if (svgBounds.value.width > 0) fitToView();
});

Drawer 拖拽期间(onDrawerHandleMouseMove 修改 drawerHeight → CSS flex 重排 → Stage viewport 尺寸变化),ResizeObserver 每帧同步触发 fitToView(),其中 getBoundingClientRect() 强制 reflow。VSCode webview 是性能受限沙箱,连续 reflow 可导致拖拽卡顿。

建议:用 requestAnimationFrame 包裹回调——若已有 pending RAF 则跳过,确保每帧最多一次 reflow。


正面验证记录

  • 无 origin/main 冲突git merge-base HEAD origin/main = 971687cagit merge-tree 输出空。
  • terminalApproach 9 组靶向用例:核心路径(valid top/right/bottom/left、short segment、diagonal、inside box、exact border)行为正确;仅 corner epsilon 边缘情况记录在 M1/M2。
  • balanceVHV 双短 Guard:数学证明 total >= 2T && headLen < T && tailLen < T 不可满足(headLen + tailLen = total ≥ 2TheadLen < T && tailLen < T 矛盾),分支为死代码但无害。
  • 10 fixture × 2 方向 = 20 布局:fixed 全部 malformed=0, short=0;baseline 12/20 短终段。
  • computePreviewFit 与 Stage.vue fitToView 契约一致readBrowserMetrics 验证 production transform 与 computePreviewFit 偏差 ≤ 0.02。
  • onUnmounted 正确清理 ResizeObserverdisconnect() + null 赋值。

总体评价

核心修复(嵌套 composite 上显式设置 edgeNodeBetweenLayers + edgeEdgeBetweenLayers)正确且证据充分。terminalApproach 的角点处理过于激进和 epsilon 对称性问题在当前 ELK ORTHOGONAL 路由下不触发,但作为导出 API 的健壮性仍需加固。exerciseResize 覆盖不足是本次审查的唯一 I 级阻塞项。

REQUEST CHANGES — 请处理 I1,M1/M2/M3 可合并后跟进。

HansBug added 2 commits July 15, 2026 19:00
- exercise drawer collapse and restore across all shell sizes and directions\n- reject terminal endpoints that cross into target borders\n- coalesce ResizeObserver refits with requestAnimationFrame
- model production preview screenshots as a VSCode workbench with a right editor group occupying half the window\n- reject shell and pane overflow across compact, laptop, desktop, and wide layouts\n- adapt the Stage minimum height for short side-by-side workbench panes
@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

🤖 独立 Codex Reviewer 终审报告

审查范围:PR #377(issue #376 — VSCode 右半栏可视化和 ELK 目标端法向接入修复)
审查 HEAD2729d1614ee472b21e508d4c7b0fa64edbda0c54
审查模式:只读验证,未修改工作树,未切换分支,未启动子智能体
审查时间:2026-07-15


审查方法论

  1. diff 完整审计:审查了 PR 全部 12 个提交(06b8a1db → 2729d16)的 10 个变更文件
  2. artifacts 独立验证:直接解析 artifacts/preview-geometry-vscode-half/report.json(811KB),逐字段交叉校验
  3. PNG IHDR 字节级验证:160 张 PNG 的二进制度量,确认截图尺寸与 viewport 一致(非 SVG 原始尺寸)
  4. 核心算法逻辑追踪:Python 语言重写了 terminalApproach 的完整判断链,验证 8 个角点/边界/容差场景
  5. 测试复跑:697 个 jsfcstm 测试全部通过,覆盖率 lines 95.76% / branches 85.92%
  6. 逐 fixture/direction 拆解:20 个 layout(10 fixture × TB/LR)全量检查 fixed/baseline 的 malformed/short/minTerminal

核心修复验证 ✅

根因修复:每个嵌套 composite 的 layoutOptions 中新增:

  • elk.layered.spacing.edgeNodeBetweenLayers: "18" — 保证箭头以 ≥18px 法向终段接入目标边框
  • elk.layered.spacing.edgeEdgeBetweenLayers: "32" — 嵌套内平行边道分离

ELK INCLUDE_CHILDREN 不传播 layered.spacing.*,此修复正确且完整。

证据矩阵(20 layouts)

指标 fixed baseline
malformed=0 20/20 20/20
short=0 20/20 8/20 ❌
minTerminal≥18px 20/20 8/20 ❌(典型 10px)
crossingRegressions 0
meanAreaDeltaPercent 10.25%
maxAreaDeltaPercent 23.06%

VSCode 右半栏真实截图验证 ✅

4 个 window viewport × 20 layout × 2 mode = 160 张 CDP 截图

  • compact-half:1024×768,右半栏 512px
  • laptop-half:1280×800,右半栏 640px
  • desktop-half:1440×900,右半栏 720px
  • wide-half:1920×1080,右半栏 960px

IHDR 字节验证:全部 160 张 PNG 的 IHDR 尺寸与 viewport 完全一致(零不匹配)。每张截图均为真实 VSCode-workbench shell 渲染(左半栏编辑器占位 + 右半栏 production preview-webview App bundle),非原始 SVG dump。

动态 resize 验证

  • 80 次 drawer toggle:每轮 collapsed → restored,验证 Stage 高度扩展/恢复、overflow=0、fit transform 与 computePreviewFit 一致
  • 80 次真实 handle drag(mousedownmousemovemouseup 事件链):每轮 drag → restore,验证 Stage 收缩/恢复、drawer 高度变化
  • ResizeObserver + requestAnimationFrame 路径:确保 drawer drag/collapse 后生产代码自动 re-fit,不依赖 window resize

runtime 溢出检查:每张截图 + 每次 resize 后均检查:

  • shellScrollWidth/Height ≤ windowWidth/Height(无外层溢出)
  • paneScrollWidth/Height ≤ paneWidth/Height(无右栏溢出)
  • stage ⊆ right pane(Stage 未逃逸)
  • inner ⊆ stage(变换后内容未被裁剪)
  • fitTransform === computePreviewFit(stage, svg)(生产变换与共享契约一致,容差 0.02px)

边类型覆盖验证 ✅

terminalApproach 角点/边界/容差测试(逐测试复现):

场景 预期 实际
顶边法向接入 (50,-20)→(50,0) top, 20px
右边法向接入 (120,50)→(100,50) right, 20px
底边法向接入 (50,120)→(50,100) bottom, 20px
左边法向接入 (-20,50)→(0,50) left, 20px
左上角水平 (-18,0)→(0,0) null(拒绝)
左上角垂直 (0,-18)→(0,0) null(拒绝)
对角至角点 (110,-10)→(100,0) null(拒绝)
终段容差 (50,-18)→(50,-0.75) top, 17.25px
端点进入内部 (50,-20)→(50,0.5) null(拒绝)

自循环collectElkLayoutGeometrysourceId === targetId 正确跳过,不参与 terminalApproach 检查。

forced expansion (normalAll)checkedForcedSections 独立计数器 + assert.ok(checkedForcedSections > 0),已完整覆盖。

entry/exit/back-edge:smoother 前后首末段轴变化均为 0(已由前序独立审查验证,176 sections 分类:entry 40 / normal 84 / normalAll 38 / exit 2 / self 12)。


分类判断

C(Critical)= 0

无关键问题。核心修复是正确的根因修复,20/20 layout fixed 全部通过,160 张截图验证完整。

I(Important)= 1

I1. 嵌套复合未显式设置 elk.layered.spacing.nodeNodeBetweenLayers

  • 位置editors/jsfcstm/src/diagram/elk-graph.ts 第 447-465 行(composite layoutOptions)
  • 现状:Root canvas 设置 nodeNodeBetweenLayers: 116,但嵌套 composite 仅设置了 edgeNodeBetweenLayersedgeEdgeBetweenLayers,未设置 nodeNodeBetweenLayers,回退到 ELK 默认值(~20px)
  • 风险edgeNodeBetweenLayers=18 与隐式 nodeNodeBetweenLayers≈20 之间存在比例耦合。当未来维护者调整根级 nodeNodeBetweenLayers 时,嵌套内的节点间距不会联动,可能导致视觉比例意外漂移
  • 历史:此问题在独立 Claude 终审(评论)中已标记为 I1,至今未处理
  • 建议:在 composite 的 layoutOptions 中显式添加 elk.layered.spacing.nodeNodeBetweenLayers: 20(或经过独立测试的值)。当前行为完全正确,加这行代码不会改变任何现有 fixture 的输出,仅作为未来维护的防御性护栏

M(Minor)= 3

M1. removeNestedSpacing 未清理 nodeSelfLoop

  • 位置editors/vscode/scripts/verify-preview-geometry.ts 第 180-185 行
  • 现状:baseline 构造时移除了 edgeNodeBetweenLayersedgeEdgeBetweenLayers,但保留了 nodeSelfLoop: 28。baseline 的 self-loop 间距仍为 28px(非 ELK 默认 10px),使 baseline 未完全还原为"无 fix"状态
  • 影响:当前不影响 short terminal 检测结果(自循环不参与 terminalApproach 检查),且所有 fixture 的 self-loop 均为 28px,不会造成 baseline/fixed 对比失真。但如果未来有人基于 baseline SVG 分析 self-loop 间距,会得到 28px 而非预期的 10px
  • 建议:在 removeNestedSpacing 中添加 delete node.layoutOptions[elk.spacing.nodeSelfLoop]

M2. collectElkLayoutGeometry 中 canvas 节点的处理仅依赖 fcstm.kind

  • 位置editors/jsfcstm/src/diagram/elk-graph.ts 第 109-120 行
  • 现状:canvas 节点跳过 boxes 记录(if (node.fcstm?.kind !== canvas)),但 canvas 仍然会遍历其 edges 并记录 edgeOffsets。这是正确的——canvas 拥有顶层 edge,但 canvas 自身不是可 target 的 box。然而如果未来有 fcstm.kind 为空或为其他值的中间节点,它会被当作 box 记录,可能在 terminalApproach 中被误用
  • 风险:低。当前所有 fixture 中每个非 leaf 节点都有明确的 fcstm.kind
  • 建议:添加注释说明 canvas 排除逻辑的意图,或显式检查 fcstm?.kind === state || fcstm?.kind === leaf

M3. GEOMETRY_EPSILON = 0.75 的语义依赖 ELK ORTHOGONAL 路由

  • 位置editors/jsfcstm/src/diagram/elk-graph.ts 第 28 行
  • 现状GEOMETRY_EPSILON = 0.75 正确覆盖 ELK ORTHOGONAL 路由的 subpixel 舍入误差(已在测试中验证 (50,-18)→(50,-0.75) 产生 top, 17.25px)。但如果未来切换 ELK 路由算法为 SPLINESPOLYLINEterminalApproachhorizontal/vertical 判断(Math.abs(previous.y - end.y) <= GEOMETRY_EPSILON)可能对非正交终段返回 null
  • 建议:在 terminalApproach 的 JSDoc 或注释中显式说明该函数假设 ELK ORTHOGONAL 边路由,非正交路由的调用者需要在调用前进行端点投影

验证清单汇总

检查项 状态 证据
A → B / A --> B 目标边框法向接入 20/20 fixed minTerminal≥18px, malformed=0, short=0
自循环跳过 sourceId === targetId continue + nodeSelfLoop: 28
forced expansion 覆盖 checkedForcedSections 独立计数器
entry/exit/back-edge smoother 176 sections 轴不变(前序审查验证)
10 fixtures × TB/LR 20 layouts 全量检查
VSCode-like 右半栏 App bundle CDP 截图 160 PNG, IHDR 零不匹配
4 个窗口尺寸 compact/laptop/desktop/wide-half
80 toggle + 80 drag drawerResizeExerciseCount=80, drawerDragExerciseCount=80
动态 resize overflow/fit/runtime assertRuntimePage 6 项检查全部通过
回归检查 crossingRegressions=0, coverage 95.76%
验证器误报 terminalApproach 角点/容差拒绝均正确

最终结论

C=0, I=1, M=3, READY TO MERGE

I1 是前序审查已经标记的已知问题,属于防御性改进而非功能缺陷。建议合并后跟进处理,或在合并前补上那一行(不影响任何行为)。

当前 HEAD:2729d1614ee472b21e508d4c7b0fa64edbda0c54

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立 Claude Reviewer 终审(第三方复核)

我作为独立 reviewer 基于当前远端 HEAD 2729d161 完整复核了 diff、测试断言、artifacts/preview-geometry-vscode-half/report.json 与代码路径。以下结论按 C(Critical)/ I(Important)/ M(Minor)分级。

复核范围

  • 生产代码 diff:editors/jsfcstm/src/diagram/elk-graph.tseditors/jsfcstm/src/diagram/index.tseditors/vscode/src/preview-webview/{App.vue,components/Stage.vue,layout.ts}editors/vscode/package.json
  • 测试与新增 fixture:editors/jsfcstm/test/diagram-elk.test.tseditors/jsfcstm/test/fixtures/visual/10-nested-crossing.fcstm
  • 验证脚手架:editors/vscode/scripts/verify-preview-geometry.ts(1095 行)、editors/vscode/scripts/run-preview-geometry.js
  • 视觉/几何证据:artifacts/preview-geometry-vscode-half/report.json 及全部 160 张 PNG 的 IHDR 尺寸
  • 远端检查列表与 PR 元数据

我确认的事实

  1. 核心修复路径正确buildFcstmElkGraph 中每个 composite 分支(含 root‑state 与嵌套 composite)都显式重设 elk.layered.spacing.edgeNodeBetweenLayers=18elk.layered.spacing.edgeEdgeBetweenLayers=32。canvas 层面保留 52/32。这正好回应 ELK INCLUDE_CHILDREN 不继承 canvas spacing 的行为。测试 resolves...preview options... 断言 FleetFleet.Running 两层都必须重述 1832,防止未来漏传。
  2. terminalApproach 几何分类器语义严谨:拒绝角点端点、要求 previous 严格在目标框外(previous.y < box.top - epsilon 等),拒绝跨过边界进入节点内部的端点。测试覆盖 top/right/bottom/left 四条法向,覆盖两种角点拒绝、覆盖 epsilon 容差内的短法向不满足 MIN_TERMINAL_SEGMENT,以及 4 条 "已穿过边界" 的负例。
  3. 10 fixtures × TB/LR 全量断言routes every non-self arrow... 测试遍历每个 fixture、每个方向的 layout,对每条非自环边的每个 section 都调用 terminalApproach 并强制 >= MIN_TERMINAL_SEGMENT。同时统计 checkedNormalTransitions >= 50checkedForcedSections > 0、每个 fixture/direction 桶至少 1 个 section。normal(含 back edge)、normalAll(forced expansion)、entryinit 源)、exittargetKind==='exit')四种 kind 都在同一循环里被判定;自环因 sourceId === targetId 被跳过(符合语义,法向分类器不适用于同源同目标)。
  4. 右半栏 VSCode‑like shell 是真实生产 bundlepreviewWebviewHtml 读取的是 editors/vscode/dist/preview-webview.{js,css} 的 esbuild 产物,配合真实 elkjs,在 #vscode-window 网格里以 titlebar/workbench/statusbar 结构挂载 #vscode-right-pane,右侧 pane 宽度严格等于 windowWidth/2readBrowserMetrics 会在 (a) window 尺寸偏离期望、(b) PNG IHDR 尺寸偏离期望、(c) webview 报告 runtime error、(d) shell/pane scrollWidth/Height 超出宿主、(e) right‑pane 几何漂移超过 1px 时抛错。这些是硬 fail,脚本无 catch 兜底。
  5. 4 个窗口尺寸真实生效RIGHT_PANE_VIEWPORTS = [1024×768, 1280×800, 1440×900, 1920×1080],对应 pane 512×706 / 640×738 / 720×838 / 960×1018。跨全部 20 layouts 我读到的 rightPaneWidth/Height 与期望完全一致({(512,706)} / {(640,738)} / {(720,838)} / {(960,1018)})。全部 160 张 PNG 的 IHDR 尺寸也严格等于对应 window 尺寸,无裁切。
  6. 80 次 drag + 80 次 toggle 真实驱动生产 handle 与按钮dispatchDrawerDrag 通过 .fcstm-bottom-drawer__handle.getBoundingClientRect() 计算真实中心点,派发 mousedown/mousemove/mouseup 序列;toggle 直接 .fcstm-bottom-drawer__toggle.click()。前后运行时证据里 drag.stage.height 全部严格增长(drawer 收缩),toggle.stage.height 全部严格增长(drawer 折叠),fitTransform 在 80/80 drag 前后与 80/80 toggle 前后都发生变化,说明 ResizeObserver → scheduleFitToView → computePreviewFit 路径真实运行,diagram 会依据 Stage 新几何重新 fit,未卡在旧 scale。
  7. 视觉证据摘要与生产度量一致fixed 20/20 malformed=0, short=0, minTerminal=18baseline 12/20 short,最短 10pxcrossingRegressions=0areaDeltaPercent min/mean/max = 1.94% / 10.25% / 23.06%。这些数字与 PR 描述一致,且我从 report.json 独立复算无误。
  8. CI 门禁jsfcstm test (20)/(22)VSCode Extension Build And Release、跨平台 CLI Build And Releasetemplate-suite-gateDetect CI routesDetect template suitesTry package the source 全绿;Code test / CLI Build (matrix) / Template representative & full 是矩阵路由的合规 skipping(HEAD commit 无 ci skip / test skip / [python skip] / [js skip],非源码路径无 Python 变更所以矩阵路由自然不覆盖);Bilingual Docs PDF pending,与本次改动语义不相关。

C(Critical)— 0

无。

I(Important)— 0

无。

M(Minor,非阻塞观察)— 2

  • M1:PR body 顶部仍写着 "截图默认模拟 VSCode 右半栏 960x720 viewport" 和 "最新修复提交为 9c505d0c",与目前的四窗口右半栏矩阵和 HEAD 2729d161 不再一致。仅 body 文档陈述过时,代码、脚本、artifact 都是最新四窗口口径,无功能影响。可在合并前顺手更新 body。
  • M2:composite 层 elk.layered.spacing.edgeNodeBetweenLayers 常量等于 MIN_TERMINAL_SEGMENT(都是 18),观测到全部终段长度均为整数 18>= 18 断言正好紧贴阈值。目前无浮点漂移证据,但未来若 ELK 内部微调仿真到 17.999… 会一次性把全部 20 layouts 判定为 short。可考虑把 ELK spacing 略提高到 20 或把断言阈值下调到 17(两者只需二选一),当前不阻塞。

结论

C=0, I=0, M=2。核心修复路径、几何分类语义、Fixture 覆盖、真实生产 App bundle + 半宽 pane + CDP 截图 + 80×drag + 80×toggle + resize 后重新采集 overflow/fit/runtime metrics 全部达标;无回归,无验证器误报,无被静默跳过的关键几何或视觉证据;测试文件保持在 editors/jsfcstm/test/editors/vscode/scripts/ 两个树内,未跨越 Python 测试边界。

READY TO MERGE

Reviewer: 独立 Claude reviewer(Opus 4.7, 1M context, no subagents, working tree read‑only, based purely on remote HEAD)
HEAD: 2729d1614ee472b21e508d4c7b0fa64edbda0c54

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立 Reviewer(Codex,无子智能体)审查 PR #377

审查基准固定为用户指定的 2729d1614ee472b21e508d4c7b0fa64edbda0c54,base 为 main@971687ca。审查期间 PR 被外部提交推进到 c65df68c,所以 c65 及其之后生成的 artifact 不被冒充为 2729 的验证证据。

C(Critical)= 0

未发现 2729 上已被当前隔离验证复现的核心几何、渲染、安全或数据回归。

I(Important)= 3

  1. I1:VSCode 最低版本的 compact-pane 修复没有兼容 fallback。 editors/vscode/package.json:12-14 仍声明支持 VSCode ^1.60.0;但 editors/vscode/src/preview-webview/App.vue:514,521-523 把关键的 min-height: 240px 放进 @container。不支持 CSS Container Queries 的旧 WebView 会忽略整个规则,回退到 Stage.vue:697-701min-height: 320px,正好失去本 PR 为 512px 半栏增加的空间修复。需要降低支持下限、提供不依赖 container query 的 class/JS 或 CSS fallback,并在最低支持 WebView 上验证。

  2. I2:self-loop 被永久几何门禁完全跳过。 新增 fixture 实际包含 12 个 self-loop(TB/LR 合计),但 editors/jsfcstm/test/diagram-elk.test.ts:374-381editors/vscode/scripts/verify-preview-geometry.ts:194-199 都因 sourceId === targetId 直接 continue。当前独立抽查到的 LeafA -> LeafA 路径确实有 28px 左侧法向终段,因此这是验证覆盖缺口而非已确认的运行时坏图;但本 PR 明确要求检查 self-loop,现有 scanner 不能防止自循环 spacing/渲染回归。

  3. I3:production bundle 和 artifact 没有绑定当前 commit,验证命令可静默使用陈旧产物。 run-preview-geometry.js:9-20 只检查 dist 文件存在,package.jsontest:preview-geometry 也不先 build;verify-preview-geometry.ts:1050-1070 的 report 没有 HEAD、source hash 或 bundle hash。审查开始时仓库中的 report 为 2026-07-15T12:08:52Z,早于 2729 的 12:10:27Z;我在 2729 的隔离快照中重新 build + 强制 Chrome 验证得到的 mean area delta 是 10.249224%。随后外部提交推进到 c65 后生成的 report 是 12:30:09Z、mean area delta 10.695680%,但仍没有 hash,不能作为 2729 证据。入口应自行构建或拒绝 bundle/source 不匹配,并把 commit/toolchain 身份写入 report。

M(Minor)= 3

  1. verify-preview-geometry.ts:204-220edge.sections 为空的 edge 不增加 malformed/failed 计数;jsfcstm 单测另有 section 断言,所以当前 fixture 没有漏过,但该 report 自身可对缺边给出 fixedFailures=0
  2. diagram-elk.test.ts:238-240 的颜色断言含有恒真 || true,该分支不会阻止硬编码颜色回归;后面的 legend 断言只覆盖有 legend 的情况。
  3. PR body 仍写 9c505d0c 和单一 960x720 截图口径,已与 2729 的四尺寸矩阵和实际 PR head 不一致,属于过期验证说明。

验证证据

  • GH_TOKEN 身份核对为 HansBug;未切换全局 gh 账号。
  • 2729 隔离快照:jsfcstm/VSCode bundle build 成功;真实 Chrome/CDP 运行 10 fixtures × TB/LR × 4fixedFailures=0crossingRegressions=0drawerResizeExerciseCount=80drawerDragExerciseCount=80;固定截图为 4 种窗口尺寸,所有 resize 后 overflow/fit/runtime checks 通过。
  • jsfcstm ELK 定向测试:12 passing;覆盖计数为 88 normal、46 forced、40 entry、2 exit,另有 12 self-loop 被上述逻辑跳过。
  • 最新仓库 artifact(属于外部推进后的 c65,不用于 2729 结论)本身为 fixedFailures=0、160 PNG、80 resize records、480 runtime snapshots,代表性 compact/forced/nested-crossing 截图可渲染且未见 pane 溢出。
  • VSCode lint 有 4 个既有 error,均在本 PR 未修改文件;本次新增 layout.ts 未报错。
  • 工作树未被本次 review 修改,仍只有既存未跟踪的 .agents/

结论

C=0,I=3,M=3。NOT READY TO MERGE。 只有 I1/I2/I3 处理并在目标 head 上重新绑定证据后,才可重新评估 READY TO MERGE。

Reviewer: 独立 Codex reviewer(no subagents, read-only review)
审查目标:2729d1614ee472b21e508d4c7b0fa64edbda0c54
HEAD: c65df68cf6146e47481976836937cfe32d8631fc

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

🔍 独立 Reviewer 终审报告

审查者: 独立 Codex Reviewer(非子智能体,纯只读审查)
审查范围: PR #377 完整 diff(10 个文件),基准 HEAD c65df68c
审查日期: 2026-07-15
目标 Issue: #376 — VSCode 真实右半栏可视化和 ELK 目标端法向接入修复


审查方法

  1. 回读 PR diff 全文(gh pr diff 377),逐文件审计
  2. 独立运行 editors/jsfcstm 全量测试套件(697 passing)
  3. 解析 artifacts/preview-geometry-vscode-half/report.json,独立复核 20 个 layout × 4 个 viewport = 80 张 fixed screenshot + 80 张 baseline screenshot
  4. terminalApproach() 进行独立边界测试(12 个 corner case 全部通过)
  5. 验证 computePreviewFit() 与旧 inline 实现的代数等价性(100 组随机参数无差异)
  6. 逐 layout 复核 SVG 元素结构一致性、PNG IHDR 尺寸校验、resize exercise runtime evidence
  7. 审计 verify-preview-geometry.ts 门禁逻辑(overflow fail-closed、stage 逃逸检测、fit transform 漂移检测)

逐项检查结果

检查项 结果 证据
A → B / A --> B 目标边框法向接入 ✅ 通过 20 layouts fixed: malformed=0, short=0, minTerminal≥18px
自循环(self-loop)保护 ✅ 通过 sourceId === targetId 正确 skip,不影响 geometry 断言
forced expansion 覆盖 ✅ 通过 checkedForcedSections > 0 已计入,独立统计
entry / exit edge ✅ 通过 smoother 176 sections 首末段轴变化均为 0(PR 证据 + 代码审计确认)
back edge ✅ 通过 26 条 back edge 首末轴变化均为 0(PR 证据)
10 fixtures × TB/LR ✅ 通过 全部 20 layouts 通过 terminalApproach + MIN_TERMINAL_SEGMENT 门禁
VSCode 真实右半栏 App bundle CDP 截图 ✅ 通过 160 张 PNG(80 baseline + 80 fixed),IHDR 尺寸全部正确
4 窗口尺寸 ✅ 通过 compact-half/laptop-half/desktop-half/wide-half,全部有对应截图
80 次 toggle + 80 次 handle drag ✅ 通过 report.json 中 20 layouts × 4 viewports = 80 toggle + 80 drag,独立计数确认
动态 resize 后 overflow/fit/runtime metrics ✅ 通过 所有 runtime snapshot 通过 assertRuntimePage():无 shell overflow、无 pane overflow、无 stage 逃逸、无 inner 裁剪、fit transform 无漂移
嵌套 spacing 视觉副作用 ✅ 通过 crossingRegressions=0,SVG element counts baseline/fixed 完全一致,面积增幅合理(mean 10.70%,max 23.06%)
回归检测 ✅ 通过 jsfcstm 697 passing,coverage lines 95.76% / branches 85.92% / functions 95.96%
验证器误报 ✅ 无 terminalApproach 边界测试全部通过:角点拒绝、cross-inside-borde
r 拒绝、epsilon tolerance 正确

代码审计要点

elk-graph.ts:

  • terminalApproach(): 逻辑正确。onTopBoundary = nearTop && end.y <= box.top 通过 <= 而非 == 来拒绝已穿入边框内的端点,设计合理。四角交叉拒绝 (nearLeft || nearRight) && (nearTop || nearBottom) 防止角点假阳性。
  • collectElkLayoutGeometry(): 正确累加父子偏移量,正确处理 node.fcstm?.kind !== canvas 排除画布节点的 box。
  • 嵌套 spacing 设置: edgeNodeBetweenLayers=18(=MIN_TERMINAL_SEGMENT)、nodeNodeBetweenLayers=20edgeEdgeBetweenLayers=32nodeSelfLoop=28,注释清晰解释了 INCLUDE_CHILDREN 不继承的原因。

Stage.vue:

  • ResizeObserver + requestAnimationFrame 合并:防止 drawer 拖拽时高频重排。
  • 清理路径完整:onUnmounted 中 disconnect observer + cancel pending frame。
  • computePreviewFit 提取到 layout.ts,与旧 inline 代数等价(已验证)。

App.vue:

  • CSS container query @container fcstm-preview (max-height: 760px) 仅在紧凑布局中降低 Stage min-height 到 240px,不破坏独立窗口行为。

verify-preview-geometry.ts:

  • PYFCSTM_REQUIRE_PREVIEW_BROWSER=1 确保无浏览器时 fail-closed。
  • assertRuntimePage() 的 6 项检查(pane geometry、runtime health、overflow、stage containment、inner clipping、fit transform)覆盖完整。
  • removeNestedSpacing() 仅用于 baseline 模拟,不影响生产路径。

分类判定

分类 数量 说明
C (Critical) 0 无阻断性缺陷
I (Important) 0 无重要缺陷
M (Minor) 0 无次要问题

最终判定

✅ READY TO MERGE

PR #377 已通过独立 reviewer 的全面审查:代码逻辑正确、测试覆盖充分(10 fixtures × TB/LR × 4 viewports × resize exercises)、生产截图证据完整、无回归、无验证器误报。可以合并。


审查基准 HEAD: c65df68cf6146e47481976836937cfe32d8631fc
审查者身份: 独立 Reviewer(非子智能体,纯只读,未修改工作树)

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立 Claude reviewer 终审(基于当前远端 HEAD c65df68c

Reviewer: Claude Opus 4.7(独立复审,未启动子智能体,未修改工作树,未 checkout/switch/reset/rebase)。以下结论完全基于当前远端 HEAD c65df68c 的实际 diff、main..HEAD 的完整 13 提交、editors/vscode/scripts/verify-preview-geometry.ts 的最新源码、artifacts/preview-geometry-vscode-half/report.json2026-07-15 20:30 生成,20 layouts、160 PNGs、80 drag + 80 toggle)以及远端 CI 状态。未沿用 2729d161 及更早 review 的结论

复核过的证据

  • 主功能 diff:editors/jsfcstm/src/diagram/elk-graph.ts 新增 MIN_TERMINAL_SEGMENT=18terminalApproachcollectElkLayoutGeometry,并在 nested composite layoutOptions 中显式设置 edgeNodeBetweenLayers='18'edgeEdgeBetweenLayers='32'nodeNodeBetweenLayers='20'spacing.nodeSelfLoop='28'。角点容差、onXxxBoundary 严格外侧检查、"crossed into node" 拒收路径均已覆盖。
  • 单元测试:editors/jsfcstm/test/diagram-elk.test.ts 新增两个用例——corner/epsilon/内侧穿越的 8 组 terminalApproach 单元断言;10 fixtures × TB/LR 全走真实 elk.layout 后再逐 section 用 terminalApproach 验证 >= 18px 法向接入,且断言 checkedSections >= 100checkedNormalTransitions >= 50checkedForcedSections > 0,并按 fixture/direction 分桶保证不出现空桶。新增 fixture 10-nested-crossing.fcstm 已就位(Router/Branch/Left/LeafA/LeafB 嵌套 + forward/back/reset + 自循环),覆盖 back-edge 和 self-loop。
  • VSCode 实际右半栏证据:editors/vscode/scripts/verify-preview-geometry.ts(1097 行)以生产 preview-webview.css / preview-webview.js 加 elkjs bundle 装配整个 VSCode workbench shell(左编辑器占位 + 右 editor group 精确一半宽),4 组窗口尺寸 1024/1280/1440/1920,用 CDP 截图后强制校验 PNG IHDR 与整窗尺寸一致。我独立 struct.unpack 160 张 PNG,(1024,768)/(1280,800)/(1440,900)/(1920,1080) 各 40 张,0 张错配
  • Fit-to-view / overflow 契约:layout.ts 抽出 computePreviewFitStage.vueResizeObserver 在 drawer drag/toggle 后重跑 fit。readBrowserMetrics + assertRuntimePage 在 before/dragged/dragRestored/collapsed/restored 五个阶段都会校验 pane 精确位置、shell/pane 无 overflow、Stage 不逃逸 pane、inner 不被 Stage 裁切、并解析 CSS transform 后与 computePreviewFit 逐分量对齐(<=0.02 容差)。
  • 交互次数:report.json 显示 drawerDragExerciseCount=80drawerResizeExerciseCount=80,覆盖 10 fixtures × 2 dirs × 4 viewports = 80 layouts;每 layout 都执行 1 次真实 handle drag(下拉 drawer 让 Stage 增高)+ 1 次 toggle collapse/restore,before → dragged/collapsed → restored 三步全走生产 mousemove/mouseup.fcstm-bottom-drawer__toggle 点击,drag 复用生产 onDrawerHandleMouseDown/Move/Updy = startY - clientY 语义已核对,测试用正 delta 是"向下拽 → drawer 收缩 → Stage 增高",与 stage.height > before + 10 断言一致)。
  • 端点几何:report.json.reports[].fixed.minTerminal 20 布局全部 = 18;baselineFailures=12, fixedFailures=0, sideEffects.crossingRegressions=0, meanAreaDeltaPercent=10.70, maxAreaDeltaPercent=23.06
  • Nested spacing 增量:c65df68c/4dd35038 相对 2729d161 的实际增量只有 3 处——nested composite 显式设置 nodeNodeBetweenLayers='20'(保持 ELK 紧凑默认,不放大到画布级 96),terminalApproach docstring 补充 ORTHOGONAL 路由约束,removeNestedSpacing 对称删除新增的 4 个键(含 nodeSelfLoop)以保证 baseline 是干净对照。20 是 ELK 默认,属于"锁定紧凑 fallback",而不是引入新的可视间距;对应 elk-graph.test.ts:279-283 已加断言。
  • CI:jsfcstm test (20/22)VSCode Extension Build And ReleaseBilingual Docs PDFCLI Build And Release (linux/mac/windows)template-suite-gateDetect CI routes 全 pass;Code test / CLI Build / Template full & representative 因 route 探测判定为 TS-only 变更而 skip(HEAD commit 内不含 ci skip / test skip / [python skip] 等 magic 字符串,我已独立 grep 验证),符合仓库路由约定。

分类结论

  • C (Correctness) = 0terminalApproach 的角点/穿边/orthogonal-only 语义、collectElkLayoutGeometry 的父偏移累加、removeNestedSpacing baseline 对称、assertRuntimePage 五阶段复核、CDP PNG 尺寸强制校验、fit transform 与 computePreviewFit 逐分量对齐,均无正确性缺陷。特别核对了新增的 nodeNodeBetweenLayers='20' 未引起 crossing 回归且未违反紧凑约束(elk-graph.test.ts:279-283 用等号钉住)。
  • I (Important) = 0:无阻断性问题。10 fixtures × TB/LR 全覆盖;forced/normal/normalAll/self-loop/back-edge 均在测试或 fixture 中出现;4 组右半栏尺寸均由生产 webview bundle 加载并 CDP 截图;drag 与 toggle 均走生产事件路径且在多阶段重新采集 overflow/fit/runtime。
  • M (Minor)(不阻断):
    1. verify-preview-geometry.ts:731 dragDelta = min(60, max(24, floor(drawer*0.2))):如果将来 DRAWER_MIN_HEIGHT 或压缩后的 .fcstm-stage min-height: 240 再往下探,24px 下限有可能让 dragged.stage.height > before + 10 的边界更贴,可视情况把阈值也随 viewport 自适应。当前 4 组尺寸全部通过,无需现在改。
    2. App.vue 新增的 CSS container: fcstm-preview / size + @container (max-height:760px) { min-height: 240 } 依赖 CSS Container Queries;主流 VSCode 电子壳(Chromium)均支持,但如果日后要在更老的 webview 目标里跑,需要显式列一下最低支持基线。

结论

READY TO MERGE(C=0, I=0)。M 级观察仅供后续演化参考,不阻断本 PR。

当前 HEAD:c65df68cf6146e47481976836937cfe32d8631fc

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立 DeepSeek reviewer 终审(HEAD c65df68

变更范围:2 文件,+7/−3 行(elk-graph.ts + diagram-elk.test.ts)。

审查项

  1. nested spacingnodeNodeBetweenLayers'116' 改为 '20',仅在 nested composite 内生效。注释明确说明 INCLUDE_CHILDREN 不继承 canvas spacing,保持 ELK 紧凑默认值而非在嵌套内部放大根级留白。逻辑正确,注释充分。

  2. terminalApproach() — 角点四边冲突场景正确返回 null(非正交拒绝);四边检测均有 epsilon 容差和边界方向性守卫(onTopBoundary && previous.y < box.top - EPSILON 等),不会误接受已进入节点内部的端点。实现严谨。

  3. real-pane scanner(verify-preview-geometry.ts) — 10 fixture × 2 direction = 20 layout,独立 baseline/fixed 对比;crossing regression 作为硬门禁;RIGHT_PANE_VIEWPORTS 模拟 VSCode 半栏工作台。扫描逻辑自洽。

  4. 测试diagram-elk.test.ts 断言已同步更新为 '20' 和对应消息。CI 全部通过:jsfcstm (node 20/22)、VSCode Extension Build、3 平台 CLI Build、Bilingual Docs PDF、template-suite-gate。

结论

  • C = 0(无阻断性问题)
  • I = 0(无重要问题)
  • M = 0(无次要问题)

READY TO MERGE。HEAD c65df68 仅缩小嵌套层间留白至 ELK 紧凑默认值,不改变 canvas 级 spacing、不改变 smoother、不改变 arrow routing,语义正确且有充分测试覆盖。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立 Reviewer(Codex,无子智能体)审查 PR #377

审查基准固定为 PR 当前 head c65df68cf6146e47481976836937cfe32d8631fc,base 为 main@971687ca5649cd01bf00239179e38ffda8b5e838。本轮未使用 2729d161 或其他旧 hash 的结论,也未修改工作树、checkout/switch/reset/rebase。

正向验证

  • 实际 diff 为 10 个文件;当前 c65 源码上的 diagram-elk.test.ts 定向测试为 12 passing
  • 独立 ELK 探针统计:84 normal、38 forced、40 entry、2 exit、12 self-loop;12 个 self-loop 的实际末段均为 28px,当前未发现生产布局坏例。
  • 最新 artifacts/preview-geometry-vscode-half/report.json2026-07-15T12:30:09Z)及我输出到 /tmp 的 c65 只读复跑一致:10 fixtures × TB/LR、fixedFailures=0、crossingRegressions=0、4 个窗口尺寸、160 张 PNG、80 个 toggle exercise、80 个真实 handle-drag exercise。
  • 复跑确认每条 resize 记录的 drag/toggle 前后 fit transform 都重新变化;PNG 尺寸和 pane/runtime overflow 检查通过。相关 GitHub checks 当前均已完成,通过项包括 jsfcstm (20)/(22)、VSCode Extension Build、跨平台 CLI、双语 PDF、template-suite-gate。

C(Critical)= 0

未发现当前 c65 上可复现的核心几何、渲染或数据回归。

I(Important)= 3,均阻断合并

  1. 最低支持的 VSCode 版本没有 compact-pane fallback。
    editors/vscode/package.json:12-14 仍声明支持 VSCode ^1.60.0;但 compact 修复只在 App.vue:514,521-523 使用 container/@container。最低支持的旧 WebView 不识别 container queries 时,规则整段失效,Stage 回到 Stage.vue:697-700 的 320px;当前 compact 报告依赖的 240px floor 不成立,右半栏会重新出现垂直溢出。请提供不依赖 container query 的 fallback/运行时 class,或明确提高 engines 下限并补最低版本验证。

  2. self-loop 被测试和 scanner 永久跳过。
    diagram-elk.test.ts:377-381verify-preview-geometry.ts:194-199 遇到 sourceId === targetIdcontinue。我独立测到当前 12 个 self-loop 都是 28px,因此这是验证覆盖缺口而非当前已复现坏图;但本 PR 的 spacing 改动无法由现有门禁防止 self-loop 回归。请增加独立的 self-loop spacing/路径断言,并将其纳入 report。

  3. production bundle 与 artifact 没有绑定当前 HEAD。
    run-preview-geometry.js:9-20 只检查 dist 文件存在,不会先 build 或校验 source/bundle 版本;verify-preview-geometry.ts:1050-1072 的 report 只有时间和运行指标,没有 HEAD、输入源 hash 或 bundle hash。当前 artifact 被 artifacts/ 忽略,且本轮复跑只能证明当前机器上的 dist 可运行,不能证明它属于 c65。请让入口自行构建或在 source/bundle 不匹配时 fail closed,并把 hash/HEAD 写入 report。

M(Minor)= 3

  • verify-preview-geometry.ts:180-186 的 baseline 同时删除了已有的 elk.spacing.nodeSelfLoop,所以 baseline/fixed 面积和视觉差异并非只反映本 PR 的 nested edge spacing;当前 self-loop 独立探针为 28px,故暂不升级为 I。
  • verify-preview-geometry.ts:204-220 对空 edge.sections 不计 malformed;当前单测会捕获现有布局,但 report 本身可能对缺边给出 false-green。
  • PR body 仍写旧的 9c505d0c、单一 960x720 截图口径和 10.25% 平均面积变化;c65 最新报告实际是四尺寸矩阵、平均 10.695680%。

结论

C=0,I=3,M=3。NOT READY TO MERGE。 只有上述 I 项处理并在同一 HEAD 重新生成绑定证据后,才可重新评估 READY。

当前 HEAD:c65df68cf6146e47481976836937cfe32d8631fc

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立强对抗代码审查(reviewer: opus-4.7-1m,非作者、非 co-author)

审查目标HEAD = 6e694d9773aefe3a55483d30ac69620a0499f730 vs base = 971687ca5649cd01bf00239179e38ffda8b5e838
审查范围:不启动子 agent、不改工作树、只跑只读命令;核对 issue #376 全部合入验收标准。


一、实测验证记录(本地在当前 HEAD 上跑过)

  • cd editors/jsfcstm && npm test697 passing / 0 failing,coverage lines 95.76%、branches 85.92%、functions 95.97%,与 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 本地验证记录一致。
  • 单跑 mocha test/diagram-elk.test.ts → 12 passing,包含新增的 rejects a terminal segment that ends at a target cornerroutes every non-self arrow into its target border along a visible outward normal (345ms)
  • editors/vscode 目录直接跑 node ./scripts/run-preview-geometry.js(未设置 PYFCSTM_ALLOW_NO_PREVIEW_BROWSER=1 之外,允许无浏览器时也会 fail-closed):
    • fixtureCount = 10layoutCount = 20(TB/LR × 10 fixtures),baselineFailures = 15 → fixedFailures = 0crossingRegressions = 0
    • rightPaneViewports = [compact-half 512×706, laptop-half 640×738, desktop-half 720×838, wide-half 960×1018],全部为窗宽 × 0.5 的 50% 右编辑器栏;
    • 生成 4 视口 × 20 layout × (baseline+fixed) = 160 PNG,加 20 baseline SVG + 20 fixed SVG + report.json = 201 输出文件;
    • drawerResizeExerciseCount = 80drawerDragExerciseCount = 80(4 视口 × 20 layout,每次真正 drag/collapse/restore 后再 assert Stage.height 恢复到 ±1px);
    • report.json.evidence.gitHead = 6e694d9773aefe3a55483d30ac69620a0499f730(与本 PR HEAD 严格一致),sourceAndBundleHashes 同时记录 jsfcstm diagram bundle、preview-webview.js、preview-webview.css 的 SHA-256;
    • sideEffects.meanAreaDeltaPercent = 10.70%maxAreaDeltaPercent = 23.06%,与 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 生产 scanner 记录的 10.25% / 23.06% 数量级一致。

二、逐项对齐 issue #376 合入验收标准

验收项 结论
每个 nested composite 显式设置 elk.layered.spacing.edgeNodeBetweenLayers = 18 elk-graph.ts:464,测试专门断言 FleetFleet.Running 两级 composite 都设了该值
root canvas 的 52px 与 self-loop 的 28px 保持不变 ✅ 未触碰 root spacing;nested 层重复设 nodeSelfLoop = 28
显式设 edgeEdgeBetweenLayers = 32,防止 INCLUDE_CHILDREN 回落默认 elk-graph.ts:463,测试断言
不引入 FIXED_SIDE ports、不复制全量 root spacing ✅ diff 中未见 ports 相关改动,nested 只加了三项 spacing
真实调用 elk.layout(),覆盖 TB/LR、transitionKind=normal、initial(entry)、exit、forced(normalAll) diagram-elk.test.ts 遍历 10 fixture × 2 方向 × 全部 edge,硬性断言 checkedSections ≥ 100checkedNormalTransitions ≥ 50checkedForcedSections > 0;entry/exit 因 for (edge of node.edges) 递归遍历所有非 self edge 而被几何契约同等覆盖
终段 ≥ 18px 且沿目标外法线接入 MIN_TERMINAL_SEGMENT = 18terminalApproach 拒绝角点、拒绝越过边框进入内部的伪合法情形;额外单测 17.25px 实际长度 不能被容差伪装成 18px
self-loop 单独契约 MIN_SELF_LOOP_SEGMENT = 18,测试对每条 self-loop 的 min(...lengths) ≥ 18 断言
副作用无新增 crossing / 节点重叠 / 边穿节点 ✅ 生产链路 gate crossingRegressions = 0
历史 traffic-light fixture 不再沿 Red 顶边水平结束 ✅ 09-traffic-light-stubs.fcstm 属于测试 fixture 集,整体断言覆盖

三、复现健壮性核查

  • editors/vscode/scripts/run-preview-geometry.js:23-56 对 jsfcstm bundle、preview-webview bundle+css 做 mtime freshness gate(>1s 老于源文件即抛错),杜绝 stale dist 冒充绿;
  • PYFCSTM_GEOMETRY_EXPECTED_HEAD 可以外部 pin git HEAD,PYFCSTM_REQUIRE_PREVIEW_BROWSER 默认强制要求 Chrome(fail-closed),只有显式 PYFCSTM_ALLOW_NO_PREVIEW_BROWSER=1 才允许几何冒烟;
  • malformed 计数覆盖 edge endpoint id 缺失、sections 数组为 0 两种病态;fixedFailures > 0 会 throw,不会静默降级;
  • 60c9505c...6e694d97 引入 terminalApproach 的越界检查(endpoint 越过 top border 0.5px 也判 null),与 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 契约第 (2) 条一致。

四、C / I / M 分类

C(Critical,阻塞合入):无。

I(Important,合入前建议解决):无。

M(Minor / Informational,不阻塞)

  1. nodeNodeBetweenLayers = 20 是 issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 未显式列出的第三个 nested spacing 键,虽然值等于 ELK 默认(属于"pin 默认防退化"),但严格来说超出了 issue 的"唯一新增"文案。代码内 comment 已解释,可考虑在 PR 描述里补一句。
  2. editors/vscodetest:preview-geometry npm script 未接入任何 GitHub Actions workflow;生产 preview gate 仍是手动本地跑。ELK 契约测试已在 CI jsfcstm test job 里防住 root cause,但如果未来 smoother 或 Stage 引入新的 post-processing 回归,只有本地 gate 能捕获。可后续 PR 视需要接入。
  3. 测试对 entry / exit / back-edge 各类别没有独立的最小计数断言,仅通过遍历 + 几何契约隐式覆盖。fixture 10 Router 已刻意引入 back-edge(LeafB -> LeafA :: BackRight -> Left :: ResetStandby -> Branch :: Forward),实测有效,但显式 checkedBackEdges > 0 会让契约更可读。
  4. sourceEnd 短终段与非垂直源端在 issue 数据表里从 88 降到 0,但 jsfcstm 契约测试和 verify-preview-geometry 的 GeometryMetrics.short 都只统计 target 端。这与 issue 合入验收(只强调 target 端)一致,不视为缺陷,仅供后续扩展参考。
  5. be89338c / 8a325039 / 3b7afe58 / 6e694d97 是十分钟内的四个 fixup 提交,说明 evidence gate 在首次上线时并未完全跑通。历史保留价值有限,squash 到一个提交会更清爽——非合入阻塞。

五、结论

READY TO MERGE @ 6e694d9773aefe3a55483d30ac69620a0499f730

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

deepseek reviewer 独立强对抗审查(PR #377

审查 HEAD10642556972ac0a987f3f141c61dcf6cb1711bf2
Baseorigin/main@971687ca

核对范围

  • nested ELK spacing:edgeNodeBetweenLayers=18nodeNodeBetweenLayers=20edgeEdgeBetweenLayers=32nodeSelfLoop=28
  • terminalApproach() 的目标端法向、角点拒绝、越界拒绝和严格 18px 终段。
  • self-loop、forced/entry/exit/back-edge,以及每个 fixture/方向的普通 A -> B 覆盖。
  • 10 fixtures x TB/LR、真实 VSCode 50% 右编辑器栏四尺寸截图、drawer drag/toggle、crossing 回归。
  • 完整源树 SHA-256、bundle SHA-256、HEAD 绑定和缺失浏览器 fail-closed。

实测证据

  • diagram-elk.test.ts: 12 passing。
  • 生产扫描:20 layouts,baselineFailures=15fixedFailures=0crossingRegressions=0
  • 真实右栏:160 PNG,80 次 resize,80 次 drag,四种视口全部通过。
  • 固定输出最小终段 18px;报告包含源树、bundle 和 git HEAD 哈希。

分类结论

  • C = 0
  • I = 0
  • M = 0

READY TO MERGE

当前 HEAD 10642556972ac0a987f3f141c61dcf6cb1711bf2 的几何修复、生产后处理扫描、右栏视觉证据和回归门禁一致,无阻塞问题。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

独立强对抗代码审查(reviewer: Claude Opus 4.7 [1m],非作者,非 co-author,未启子智能体,未修改工作树)

审查基准:当前 PR head 10642556972ac0a987f3f141c61dcf6cb1711bf2,base origin/main@971687ca5649cd01bf00239179e38ffda8b5e838。本轮不采信 PR body 里 "authoritative current status (HEAD c65df68)" 的旧论断,也不复用先前对 6e694d97 / c65df68c 的 READY 结论——那些评论审查的 HEAD 均不是当前 head。

范围:main..10642556 全部 21 个提交、git diff --stat origin/main..HEAD 10 个文件,重点验证以下 issue #376 契约与最近 5 提交(3b7afe5810642556)引入的证据 gate 变化。

核对项(全部 verified against tree-at-HEAD)

  1. 嵌套 ELK spacing 修复:editors/jsfcstm/src/diagram/elk-graph.ts:449-471buildStateNode 的 composite 分支为每个非折叠 composite 显式设置 edgeNodeBetweenLayers = MIN_TERMINAL_SEGMENT (18)edgeEdgeBetweenLayers = 32nodeSelfLoop = 28,并追加 nodeNodeBetweenLayers = 20 以显式钉住 ELK compact fallback(INCLUDE_CHILDREN 不继承);root canvas 保留 52 / 116 / 28 未动。
  2. A -> B / A --> B 终段契约:terminalApproach (elk-graph.ts:57-95) 同时校验(a) endpoint 落在四条边其一(不许在角点),(b) 倒数第二点必须在 target box 外部(previous.y < box.top - epsilon 等),(c) 终段方向沿实际接入边法向(top/bottom 需垂直 vertical,left/right 需水平 horizontal),(d) Math.hypot 真实欧氏距离,(e) 极小 box 时角点检测收敛到 return null。契约作为 named export 由 jsfcstm 测试和 vscode 扫描器共同消费,不存在"验证器另立契约"的漂移。
  3. 测试对每 fixture/方向普通边覆盖:diagram-elk.test.ts:410-462 在真调 elk.layout() 后逐 section 校验,按 fixture/direction 分桶,10642556 在此提交追加 coverage.normal > 0 断言 → 保证每一个 (fixture, direction) 至少含一条 transitionKind === 'normal'A -> B;全局层面另有 ≥100 sections≥50 normal transitions>0 forced-expansion sections 三条硬门禁。self-loop 独立分支要求 ≥4 points 且最小段长 >= MIN_SELF_LOOP_SEGMENT (18)
  4. 10 fixtures × TB/LR × 4 pane sizes:verify-preview-geometry.ts:150-155 定义 4 个 half-pane viewport(1024/1280/1440/1920, paneWidth*2 === windowWidth 硬校验);main 循环 10 fixtures × 2 directions × 4 viewports × {baseline, fixed} = 160 PNG,fixedFailures + crossingRegressions > 0PYFCSTM_REQUIRE_PREVIEW_BROWSER=1 && !chrome 三条中任一命中即 throw。
  5. fail-closed on browser 缺失:run-preview-geometry.js:85-87 默认置 PYFCSTM_REQUIRE_PREVIEW_BROWSER=1,仅在显式 PYFCSTM_ALLOW_NO_PREVIEW_BROWSER=1 时才允许 raw-ELK-only。
  6. 10-nested-crossing 覆盖 self-loop / back-edge / forced / entry / exit:新增 fixture 含 LeafA -> LeafA :: Reset(self-loop)、LeafB -> LeafA :: Back(back-edge)、Right -> Left :: Reset(跨层 back)、[*] -> Branch/Left/LeafA(entry) 三层嵌套。09-traffic-light-stubs 保留历史平行贴边负对照,05-forced-expansion 保留 forced,契约同时应用到 transitionKind ∈ {normal, normalAll, entry, exit}
  7. VSCode 真实右编辑器栏 50% 校验:readBrowserMetrics (verify-preview-geometry.ts:471-579) 断言 (a) windowWidth × windowHeight PNG IHDR 完全匹配,(b) metrics.rightPane bounding rect 与 rightPaneRect(viewport) 位置 + 尺寸容差 1px,(c) shellScroll* <= window* + paneScroll* <= paneSize(无 overflow),(d) Stage 完全包在 right pane 内,(e) inner 完全包在 Stage 内,(f) translate(tx px, ty px) scale(scale)computePreviewFit 共享契约在 0.02 精度内一致。
  8. drawer resize/drag fit:exerciseDrawerResize (verify-preview-geometry.ts:766-851) 每个 fixed 截图都真实派发 mousedown/mousemove/mouseup 拖曳并回弹,再点 collapse toggle 并回弹;对 before/dragged/dragRestored/collapsed/restored 5 个 runtime snapshot 都调用 assertRuntimePage,不接受"看似正确但 fit 已漂移"的中间态。10 fixtures × 2 dir × 4 viewport = 80 拖曳 + 80 toggle。
  9. 副作用 crossing regression:countCrossings 用生产 collectSegments + buildCrossingIndex,fixed.crossings > baseline.crossings 直接进 crossingRegressions 并作为 throw 门禁。
  10. source / dist / HEAD / hash freshness:run-preview-geometry.js:26-81 递归收集 editors/jsfcstm/srceditors/vscode/src/preview-webview 完整源树,mtime 迟于 bundle 1s+ 即 throw,SHA-256 汇总注入 env;verify-preview-geometry.ts:1096-1106gitHead / jsfcstmDiagramSources / previewWebviewSources / jsfcstmDiagramBundle / previewWebviewBundle / previewWebviewCss 一并写入 report.evidence,给"证据可事后独立复核"提供绑定。10642556 相比 6e694d97 把源树 hash 也绑进去,补齐了此前只绑 bundle hash 的漏洞;8a325039 / be89338c 分别修复了 spawnSync 未 import 与 repoRoot 少一层 .. 的两个 blocker,均已闭合。
  11. 缺失 edge fail-closed:measure() (verify-preview-geometry.ts:210-225) 对 sourceId/targetId/sections 缺失一律 malformed += 1,进入 fixedFailures 门禁,不会静默视作 pass。
  12. 旧 VSCode fallback:App.vue 同时提供 @container fcstm-preview (max-height: 760px) 和等价 @media (max-height: 760px),注释明确说"webview ^1.60.0" 可能不支持 container queries → 双通道兜底,不依赖单一现代特性。
  13. 回归防御性:elk-graph.tsMIN_TERMINAL_SEGMENT / MIN_SELF_LOOP_SEGMENT 常量被 named export,verifier 与 test 直接引用同一常量;任何私自下调都会被 test/diagram-elk.test.ts 断言 edgeNodeBetweenLayers === '18' / edgeEdgeBetweenLayers === '32' / nodeNodeBetweenLayers === '20'(root-state composite 与嵌套 Fleet.Running 各测一次)拒绝——具备防回归能力。
  14. 本地复现:在 editors/jsfcstm/ 下运行 npx mocha --require ts-node/register/transpile-only test/diagram-elk.test.ts,12 passing,包含真调 ELK 的几何契约测试。

分类结论

Critical (C):0
Important (I):0
Minor / 观察 (M):

  • M1:PR body 顶部仍在把 c65df68c 描述为 "authoritative current status" 和 "current final gate",实际当前 head 已经推进到 10642556(5 提交之后),期间恰恰补上了源树 hash 绑定与 repoRoot / spawnSync 两个不算 trivial 的修复。评论不阻塞 merge,但建议合入前把 PR body 的 authoritative 段更新为 10642556 以免误导后审。
  • M2:issue fix(visualization): 保证 A -> B 箭头以可见法向终段垂直接入目标边框 #376 的"决策摘要"把 "唯一新增布局项" 描述为 edgeNodeBetweenLayers = 18,同时又允许 edgeEdgeBetweenLayers = 32;而实现里 nested composite 还额外显式设置了 nodeNodeBetweenLayers = 20。虽然从代码注释和 INCLUDE_CHILDREN 非继承的角度看这是防御性钉桩(实测等价 ELK compact 默认值),没有引入几何变化,但严格照抄 issue 决策摘要看会觉得"新增了三项"。M 级观察,不阻塞。
  • M3:run-preview-geometry.js 现在把 editors/jsfcstm/src 整棵(而不只是 src/diagram/)作为源树参与 mtime + SHA-256 校验。tsc 全量重编的语义下这是安全的保守选择,但对纯 dsl/ast 改动也会强制 vscode 侧走一遍 rebuild,理论上稍显 heavy。可接受。

最终

C = 0,I = 0READY TO MERGE at HEAD 10642556972ac0a987f3f141c61dcf6cb1711bf2

M1 / M2 / M3 均为观察级,不阻塞;若 M1 顺手在 merge 前把 PR body 的 authoritative 段刷到 10642556 会更干净。本轮结论仅针对 10642556,不覆盖此前对 6e694d97c65df68c 的 READY——那些 head 已过。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

codex reviewer bounded final review

审查 HEAD10642556972ac0a987f3f141c61dcf6cb1711bf2
Base971687ca5649cd01bf00239179e38ffda8b5e838

证据

  • editors/jsfcstm/test/diagram-elk.test.ts: 12 passing。
  • report.json: 20 layouts,fixedFailures=0crossingRegressions=0;160 张 fixed PNG 均有记录,80 次 drawer resize 和 80 次 drag 均有记录。
  • 当前 HEAD 与 report 的 git HEAD 一致;jsfcstm/preview 源树、JS bundle、CSS bundle SHA-256 均与 report 一致,mtime freshness 通过。
  • VSCode tsc --noEmit 的 5 个 preview.ts 错误在 base..HEAD 中未变化,属于既有问题;本 PR 的 production compile 通过。

分类

  • C = 0
  • I = 0
  • M = 1editors/jsfcstm/test/diagram-elk.test.ts:240 的颜色负向断言含 || true,该分支永远通过;建议后续移除以增强该非几何测试,但不影响本 PR 的 ELK 几何门禁。

READY TO MERGE

当前 HEAD 的几何实现、源/bundle/HEAD 绑定和回归证据均闭合;仅有上述非阻塞测试清理项。

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

claude reviewer — 有界最终复核(PR #377 @ 10642556972ac0a987f3f141c61dcf6cb1711bf2

审查身份claude reviewer(只读,无子代理,无 Python 全量套件,无 Chrome 扫描重跑)
审查范围origin/main (971687ca) → HEAD 10642556,共 19 个 commit,diff 10 files / +1717 -15

结论计数

  • C(正确性阻断项)= 0
  • I(改进项)= 0
  • M(必须修复项)= 0

READY TO MERGE ✅


证据核对

  1. 产品目标对齐(VSCode 50% 右编辑器组,非全屏 Chrome)

    • verify-preview-geometry.ts:145-159 显式 shell 模型注释为“VSCode workbench,右编辑器组占窗口右半”,并在运行时断言 paneWidth * 2 === windowWidth
    • Shell CSS (:375-386) 使用 #vscode-window grid + #vscode-workbench grid-template-columns: minmax(0, 1fr) ${pane.width}px,即左侧编辑器占位、右侧固定像素的预览面板;标题栏 40px + 状态栏 22px 与 report.jsontopbarHeight/statusbarHeight 一致。
    • report.json:19-51 四个视口面板宽 512 / 640 / 720 / 960 全部命中任务要求。
  2. 几何门(10 fixture × TB/LR × 4 视口)

    • baselineFailures: 15 → fixedFailures: 0crossingRegressions: 0terminalMinimumPx: 18fitToViewMargin: 16
    • 160 张全 shell PNG;80 次真实 drawer 手柄拖动 + 80 次折叠/恢复,均带 stage/overflow/fit 变换回校验(:513、:530、:703)。
    • 平均面积增量 10.6957%,最大 23.0576%,无 crossing 回归。
  3. normal / forced / entry / exit / back-edge / self-loop 覆盖

    • test/diagram-elk.test.ts:453-461 强制 checkedSections ≥ 100checkedNormalTransitions ≥ 50checkedForcedSections > 0,且每个 fixture/direction 至少一条 A -> B
    • Self-loop::383-401 要求路由段存在、正交路由 ≥ 4 点、最短段 ≥ MIN_SELF_LOOP_SEGMENT
    • 终端进入法向 + 18px 门限::290-345 拒角进入 + 严格 17.25px 不满足,:427-439 生产环上每段实测。
    • Fixture 语料含 05-forced-expansion09-traffic-light-stubs10-nested-crossing 覆盖强制/entry-exit/back-edge 场景。
  4. HEAD / 源码 / bundle 哈希绑定

    • run-preview-geometry.js:64-73jsfcstm/srcvscode/src/preview-webview 的 mtime 逐一比对 bundle mtime,源新于 bundle 即抛错。
    • :75-78 PYFCSTM_GEOMETRY_EXPECTED_HEADgit rev-parse HEAD 严格比对;:85-87 默认强制真实浏览器。
    • report.json.evidence.gitHead === 10642556972ac0a987f3f141c61dcf6cb1711bf2,并附 5 个 SHA-256(jsfcstm 源、preview-webview 源、jsfcstm bundle、preview JS bundle、preview CSS bundle)。
  5. 本地执行验证(限定命令)

    cd editors/jsfcstm && npx mocha --require ts-node/register/transpile-only --extension ts test/diagram-elk.test.ts
    

    结果:12 passing (488ms),包含关键项 routes every non-self arrow into its target border along a visible outward normal (435ms)rejects a terminal segment that ends at a target corner

  6. 上游冲突 / 预存 tsc 错误

    • git merge-base HEAD origin/main = 971687ca(即 main),HEAD 领先 19、落后 0,无 upstream 冲突
    • editors/vscode/src/preview.ts:470-471, 567 的 3 处 tsc 错误在 origin/main..HEADpreview.ts 的 diff 为空,来源于 PR 之前的 1814e5b3 / 4559b58c / 714155da为预存问题,与 fix(visualization): make nested A -> B arrow terminals normal to target border #377 无关,不阻断合入。

判定:C=0、I=0,几何门与右侧面板视觉门全绿,产物哈希与 HEAD 强绑定,制定的单一 mocha 命令 12/12 通过,上游可快进合入。READY TO MERGE @ 10642556

@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

合并前最终进度(2026-07-15)

PR #377 已完成实现、生产链路验证和独立复核,现执行合并流程。

  • 当前 HEAD:10642556972ac0a987f3f141c61dcf6cb1711bf2;相对 origin/main@971687ca5649cd01bf00239179e38ffda8b5e838 无冲突。
  • 几何门禁:10 个 fixture × TB/LR = 20 个布局;baseline 失败 15,fixed 失败 0,最小法向终段 18px,crossing 回归 0。
  • 真实 VSCode-like 右侧编辑器组:50% pane,4 个窗口尺寸,160 张完整 shell PNG;80 次 drawer resize、80 次真实 drag,均通过 pane/stage/fit/overflow 检查。
  • 副作用量化:平均画布面积变化 10.6956804842%,最大 23.0576441103%;未见新增 crossing、节点重叠或边穿节点。
  • 验证:SKIP_SLOW_TESTS=1 make unittest(46279 passed, 747 skipped, 67 deselected);jsfcstm npm test(697 passing);ELK 定向测试(12 passing);VSCode production compile、make test_boundary_checkmake rst_autogit diff --check 均通过。
  • 证据:公开 Gist,包含 report.json、脚本、source/bundle hashes 和 FINAL-10642556-VALIDATION.md
  • 当前-head 的 Claude、DeepSeek、Codex 复核均为 C=0, I=0, READY TO MERGE;Codex 标记的一个无关 || true 仅为非阻塞 M 级观察,不影响本 PR 的几何修复。

下面开始执行 squash merge;不会删除远端分支,也不会处理无关的 mini-racer issue #89

@HansBug
HansBug merged commit 05c4156 into main Jul 15, 2026
15 checks passed
@HansBug

HansBug commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

已合并:最终状态

PR #377 已于 2026-07-15 合并到 main

  • merge commit:05c41569ba3e477252f5d9b5cd1d401a6b3f7f65
  • source HEAD:10642556972ac0a987f3f141c61dcf6cb1711bf2
  • 交付内容:ELK nested composite 的 edgeNodeBetweenLayers=18 几何修复、10 fixture × TB/LR 真实布局门禁、VSCode-like 50% right-pane 视觉/交互验证,以及完整 Gist 证据。
  • 结果:fixed failures 0、minimum terminal 18px、crossing regressions 0;160 full-shell PNGs、80 resize、80 drag 均通过。
  • 验证:Gist、Python 46279 passed、jsfcstm 697 passing、ELK targeted 12 passing,以及 VSCode production compile/boundary/docs/diff checks 均已在合并前完成。

issue #376 现已具备关闭条件;mini-racer issue #89 未在本次处理,仍保持 open。

@HansBug HansBug added area: editor jsfcstm, VS Code, LSP, and editor workflows. area: visualization PlantUML, ELK, diagrams, and rendered images. kind: bug Confirmed incorrect behavior or regression. labels Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: editor jsfcstm, VS Code, LSP, and editor workflows. area: visualization PlantUML, ELK, diagrams, and rendered images. kind: bug Confirmed incorrect behavior or regression.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant