Skip to content

Latest commit

 

History

History
663 lines (481 loc) · 36.4 KB

File metadata and controls

663 lines (481 loc) · 36.4 KB

2026-03-10 v1.5.38 - Multi-Terminal WASM Parity Implementation Plan (Mobile Bottleneck Closure)

English Document

Goal

Use a single WASM compute strategy to reduce mobile-inherent bottlenecks while preserving deterministic behavior across desktop web, Tauri desktop, Capacitor mobile, and Tauri Android runtimes.

Mobile Inherent Problems (Current)

  1. Main-thread contention during heavy graph/layout compute can freeze interaction.
  2. Worker startup + JS serialization overhead can dominate on mobile CPUs for sparse graphs.
  3. Memory pressure and GC spikes increase crash/jank probability on constrained devices.
  4. Capability variance across WebView runtimes creates nondeterministic behavior without explicit probes.

Multi-Terminal Strategy

  1. One capability contract:
    • Runtime exposes supports_mobile_wasm_compute and mobile_wasm_reason.
    • Routing remains deterministic with explicit fallback reason tracking.
  2. One compute routing model:
    • Preferred: wasm-adapter
    • Fallback: worker
    • Final fallback: single-thread
  3. One artifact governance path:
    • Canonical WASM artifact probe + strict gate scripts + CI regression barriers.

Phased Execution Plan

  1. Phase A (Capability and Diagnostics) [Completed baseline]:
    • Add runtime probe for mobile WASM readiness.
    • Expose capability and reason in runtime caps.
    • Keep existing behavior unchanged if capability is unavailable.
  2. Phase B (Routing Integration) [Active]:
    • Thread mobile capability signal into on-device build stats.
    • Add build-mode detail tags for mobile telemetry (worker-wasm-ready, worker-wasm-not-ready, fallback reasons).
    • Keep deterministic fallback behavior.
  3. Phase C (Kernel Expansion):
    • Move additional heavy kernels to WASM where correctness is contract-proven.
    • Prioritize graph build hot spots that currently consume most mobile CPU time.
  4. Phase D (Artifact Provisioning per Terminal):
    • Validate artifact packaging for:
      • desktop web bundle
      • Tauri desktop sidecar/runtime paths
      • Capacitor mobile asset/runtime paths
      • Tauri Android runtime paths
  5. Phase E (Performance and Stability Hard Gates):
    • Enforce p95/p99 guardrails for mobile-oriented workloads.
    • Enforce no-regression equivalence contracts between worker and WASM output.

Acceptance Criteria

  1. Runtime can always explain why WASM is enabled/disabled on mobile (mobile_wasm_reason).
  2. Mobile build path remains functional when WASM is unavailable (deterministic fallback verified).
  3. Migration gate suite remains fully green after each routing change.
  4. Bilingual docs remain synchronized for all plan/TODO/test-report updates.

中文文档

目标

通过统一的 WASM 计算策略,缓解移动端固有瓶颈,并在桌面 Web、Tauri 桌面、Capacitor 移动端、Tauri Android 多终端之间保持可预测的一致行为。

当前移动端固有问题

  1. 重图计算/布局计算容易占用主线程,导致交互卡顿。
  2. 在稀疏图场景下,Worker 启动与 JS 序列化开销可能高于实际计算收益。
  3. 受限设备上内存压力与 GC 抖动更明显,稳定性风险更高。
  4. 不同 WebView 运行时能力差异较大,若缺少显式探测会造成行为不确定。

多终端统一策略

  1. 统一能力契约:
    • 运行时暴露 supports_mobile_wasm_computemobile_wasm_reason
    • 计算路由保留明确的回退原因,保证可诊断性。
  2. 统一计算路由模型:
    • 首选:wasm-adapter
    • 回退:worker
    • 最终回退:single-thread
  3. 统一工件治理链路:
    • 标准 WASM 工件探针 + 严格门禁脚本 + CI 回归屏障。

分阶段执行计划

  1. 阶段 A(能力探测与诊断)[基线已完成]:
    • 增加移动端 WASM 就绪探测。
    • 在 runtime caps 中暴露能力与原因。
    • 能力不可用时保持既有行为不变。
  2. 阶段 B(路由集成)[进行中]:
    • 将移动端 WASM 能力信号接入本地构建统计。
    • 增加移动构建模式细分标签(worker-wasm-readyworker-wasm-not-ready、回退原因)。
    • 保持确定性回退链路。
  3. 阶段 C(内核扩展):
    • 将更多重计算内核迁移到 WASM,并以契约验证正确性。
    • 优先处理当前移动端 CPU 占用最高的图构建热点。
  4. 阶段 D(多终端工件落地):
    • 分别验证以下终端的工件打包与加载路径:
      • 桌面 Web 资源包
      • Tauri 桌面 sidecar/运行时路径
      • Capacitor 移动端资源/运行时路径
      • Tauri Android 运行时路径
  5. 阶段 E(性能与稳定性硬门禁):
    • 对移动端典型负载执行 p95/p99 门禁约束。
    • 强制 worker 与 WASM 输出一致性无回归。

验收标准

  1. 移动端必须能明确解释 WASM 启用/禁用原因(mobile_wasm_reason)。
  2. WASM 不可用时移动端构建链路仍可工作(确定性回退已验证)。
  3. 每一轮路由调整后,迁移门禁套件保持全绿。
  4. 所有计划/TODO/测试报告的中英文文档保持同步更新。

2026-03-04 v1.5.13 - Tauri Bridge-First Implementation Plan Update

English Document

Scope Alignment

This update aligns the implementation plan with the current Electron-to-Tauri migration strategy:

  • Tauri as the primary desktop shell.
  • Godot as the Path Mode interactive surface.
  • Node sidecar as the graph build and runtime service.
  • Bridge-first message flow (Godot <-> PathBridge <-> Backend) as the default path.

Completed in Current Migration Cycle

  • Runtime path unification for sidecar execution and frontend asset resolution has been integrated across desktop runtime paths.
  • Worker path resolution has been stabilized for packaged sidecar execution to avoid MODULE_NOT_FOUND in worker threads.
  • Knowledge Base folder loading is now anchored to the configured project root path and no longer depends on Electron-only assumptions.
  • The Path Mode configuration migration has moved core controls into Godot-side UI while preserving browser toolbar behavior for browser mode.

Open Gaps and Risk Items

  • Cache-exists decision flow still requires strict regression verification in Tauri mini GPU runs to ensure users are prompted to reuse or rebuild.
  • Duplicate load cycles must remain guarded to prevent repeated build/restore actions after a single user click.
  • WebSocket client lifecycle still needs hardening to avoid redundant early connect/disconnect churn under startup timing races.
  • History tracking for center-node switches in Godot requires final behavioral verification.

Next Execution Steps

  1. Lock cache prompt + single-execution semantics with dedicated regression tests.
  2. Finalize websocket lifecycle guard rails and startup sequencing.
  3. Complete task-level parity checks for Electron IPC replacements and remove remaining implicit Electron dependencies.
  4. Keep dual-output mobile strategy: maintain Capacitor output while also enabling Tauri Android build path.

中文文档

范围对齐

本次更新将实施计划与当前 Electron 到 Tauri 的迁移策略对齐:

  • 以 Tauri 作为桌面主壳层。
  • 以 Godot 作为 Path Mode 交互界面。
  • 以 Node Sidecar 作为图构建与运行时服务。
  • 默认采用 Bridge-first 消息链路(Godot <-> PathBridge <-> Backend)。

当前迁移周期已完成项

  • 已完成 Sidecar 运行路径与前端资源路径的统一,提升桌面运行一致性。
  • 已稳定 Worker 路径解析,避免打包 Sidecar 下线程出现 MODULE_NOT_FOUND
  • Knowledge Base 文件夹加载已锚定到配置的项目根路径,不再依赖 Electron 专属假设。
  • Path Mode 关键配置已迁移到 Godot 侧 UI,同时保留浏览器模式下 Web 工具栏行为。

仍需收敛的缺口与风险

  • 在 Tauri mini GPU 运行中,缓存存在时“复用或重建”提示流程仍需严格回归验证。
  • 需要持续防止单次点击触发重复加载(重复 build/restore)。
  • WebSocket 客户端生命周期仍需加固,避免启动阶段时序竞争导致早期重复连接/断开。
  • Godot 中心节点切换的 History 记录仍需最终行为验收。

下一步执行

  1. 通过专用回归测试锁定缓存提示与单次执行语义。
  2. 完成 websocket 生命周期防护与启动时序收敛。
  3. 完成 Electron IPC 替代项的逐任务一致性核验,并移除残余隐式 Electron 依赖。
  4. 保持移动端双输出策略:继续保留 Capacitor,同时并行支持 Tauri Android 产物链路。

Implementation Plan - Implementing Lazy Loading for Prerequisites

The goal is to allow users to investigate incomplete In-Degree information by explicitly expanding the context of a specific node, without overloading the view with the entire graph.

Proposed Changes

1. Backend Logic (src/frontend/libs/path_core.js)

  • Unrestricted Context Expansion:
    • In diffusionLearning, iterate through forcedExpansionSet.
    • For each node in the set, retrieve all incoming edges (getIncomingEdges), regardless of their completion status or relevance to the original path.
    • Add the source nodes of these edges to the finalPathNodes list.
    • Constraint: Do not recursively fetch parents of these new nodes (Level -1 only).
    • Flagging: Mark the expanded target node with isExpanded: true in the output.

2. Data Bridge (src/frontend/path_worker.js)

  • Pass State Flags:
    • Ensure the isExpanded flag matches the forcedExpansionSet state.
    • Pass this flag to the Godot client in the treeLayout payload.

3. Visualization State Machine (path_mode/scripts/tree_renderer.gd)

3. Visualization State Machine (path_mode/scripts/tree_renderer.gd)

  • Smart Toggle Logic (Left Side):
    • Pre-calculation: At the start of _draw_layout_mode, iterate _layout_edges to build a visible_in_counts dictionary (NodeID -> Count).
    • Decision Logic (in Node Loop):
      • Let global_in = node.inDegree (from backend).
      • Let visible_in = visible_in_counts[node.id].
      • Let is_expanded = node.isExpanded (flag from backend).
    • States:
      1. Expanded State: If is_expanded is true:
        • Draw (-) button.
        • Click Action: Emit node_collapse_prereqs_requested.
      2. Expandable State: Else if visible_in < global_in:
        • Draw (+) button.
        • Click Action: Emit node_expand_prereqs_requested.
      3. Complete State: Else (Visible == Global):
        • Draw nothing (or disabled indicator).

4. Interaction Logic (tree_view_panel.gd & path_app.js)

  • Collapse Handling:
    • Implement collapsePrereqs(nodeId) in path_app.js: Remove ID from forcedExpansionSet and trigger update.
    • Wire up the new Godot signal to this backend method.

UI Inconsistency Fixes

1. Statistics Panel Resizing

  • Problem: The "Incoming" and "Outgoing" lists in the Node Statistics Popup do not resize proportionally when the popup is resized using the drag handle.
  • Fix: Modify src/frontend/styles.css.
    • Change .stat-lists from fixed height: 150px to flex: 1; min-height: 150px.
    • Ensure parent containers (.popup-content) allow expansion.

2. Edge Visibility

  • Problem: Edges are visible by default on load, creating clutter.
  • Fix:
    • in src/frontend/styles.css: Set .link default stroke-opacity to 0.
    • in src/frontend/app.js: Ensure updateVisibility() is called immediately after graph initialization to enforce the visibility logic (hiding edges unless focused/hovered).

3. In-Degree Number Mismatch

  • Problem: The number displayed next to "In-Degree" (Red) in the popup often differs from the count of items in the "Incoming" list.
  • Verification: Locate showNodePopup in app.js to see if it uses node.inDegree (metadata) vs node.incoming (actual edges).
  • Fix:
    • If the metadata is correct (global truth), keep it.
    • If the list is incomplete (due to filtering/culling), add a label "(Visible: X)" or ensure the list matches filters.
    • Current hypothesis: The metadata inDegree is the ground truth from the backend, while the client-side links array might be filtered or optimized (limit 20000 edges), causing a mismatch. functionality to show "Total" vs "Visible".

4. In-Degree Display Setting (Electron)

  • Goal: Allow user to toggle between showing "Visible Inbound Nodes" (calculated from current graph) or "Total Statistical Inbound" (from backend metadata).

  • Default: Visible Inbound Nodes.

  • Changes:

    • src/frontend/index.html: Add a toggle/select in the Settings Modal (e.g., "Degree Count: Visible | Total").
    • src/frontend/app.js:
      • Update showNodePopup to check settingsManager.get('visuals', 'degreeMode').
      • If 'visible': Show inNeighbors.length.
      • If 'total': Show node.inDegree (with (visible) suffix if different? Or just strict switch?). User asked for "whether the inbound count should be shown as the number of nodes or the statistical number". I will implement a strict switch but maybe keep the tooltip or subtle indicator if they differ significantly.
      • Wire up the new setting in initSettingsUI.
  • Simplify Lazy Loading UI (Godot)

    • Update tree_renderer.gd:
      • Remove separate (+)/(-) buttons.
      • Implement unified [ Count ] button (e.g., circle with number).
      • Button toggles forcedExpansion state.
      • Default state is collapsed (colored/styled to indicate expandable).
    • Ensure path_app.js handles the toggle correctly (clear vs add to forcedExpansionNodes).
  • Tree View Visual & Interaction Overhaul

    • Visual Cleanup (Godot)
      • Remove (+)/(-) and [Count] buttons from tree_renderer.gd.
      • Remove separate click areas for these buttons.
    • Interaction Update (Godot)
      • Double Click: Change to Toggle Expansion (Emit expand/collapse).
      • Right Click: Toggle Expansion (Same as Dbl Click).
      • Middle Click: Collapse All (Emit new signal collapse_all_requested).
      • Long Press: Implement Navigation (Switch Central).
        • Add _process check for hold duration.
        • Draw Progress Ring during hold.
        • Trigger navigation on completion.
    • Focus Mode (Godot)
      • Add "Focus on this node" checkbox to settings_panel.tscn.
      • Implement focus_node_id state in tree_renderer.gd.
      • Update _draw to dim nodes/edges not connected to focus_node_id when enabled.
    • Backend Updates
      • Add collapseAll handler in path_app.js.

Data Validation

  • Disable Path Mode if No Data:
    • Update app.js to check graphDataExists or nodes.length before entering Path Mode.
    • Show alert if data missing.

Bug Fixes

  • Fix Missing Edges in Tree Layout:
    • Cause: d3.forceSimulation in app.js mutates graphData.links, replacing ID strings with Node Objects.
    • Effect: Graph.js in path_worker.js uses these Objects as keys/IDs, breaking adjacency map lookups (which expect strings).
    • Fix: In path_app.js, sanitize links before sending to worker: l.source.id || l.source.

Verification Plan

  1. Initial State Check:
    • Navigate to a node with high In-Degree (e.g., "Beta", In-Degree 18).
    • Verify the unified [ Count ] button appears on the left if < 18 lines are visible.
  2. Expansion Test:
    • Click the [ Count ] button.
    • Verify the tree rebuilds.
    • Verify previously hidden nodes (e.g., "Fair Value") appear as prerequisites.
    • Verify the [ Count ] button changes its state/appearance to indicate expansion.
  3. Collapse Test:
    • Click (-).
    • Verify the extra nodes disappear and view returns to original state.

Bug Fixes

  • Fix Missing Edges in Tree Layout:
    • Fix: In path_app.js, sanitize links before sending to worker.
  • Fix Tree View Interactions:
    • Right-Click Toggle: Ensure isExpanded is passed from path_core.js to visual node.
    • Collapse All: Update PathBridge.ts to relay collapseAll message. Add UI button.

Phase 2: Spine & Tributaries Layout (v1.4.2)

Goal

Implement a stable, tree-like layout where the "Main Learning Path" (Spine) remains linear and stationary, while prerequisites (Tributaries) expand laterally without disrupting the spine.

Proposed Changes

1. Core Algorithm (src/frontend/libs/path_core.js)

getTreeLayout(centralId, learningPath)

  • Step 1: Identify Spine:
    • Determine the "Critical Path" from learningPath.nodes (using isCritical flag or diffusionLearning result).
    • Assign Level (X-coordinate) to Spine nodes based on distance from Start.
    • Fix Spine Y coordinates to 0.

assignTributaryPositions(spineNodes, allNodes)

  • Step 2: Slot Management:
    • Create a SlotManager to track occupied (X, Y) positions.
    • Mark key Spine positions as occupied.
  • Step 3: Lateral Expansion:
    • Iterate through nodes in Topological Order (or Spine Order).
    • For each node N, identify its unplaced prerequisites P.
    • Placement Logic:
      • Target X: N.Level - 1 (Standard dependency inflow).
      • Target Y: Find nearest available vertical slot relative to N.Y.
      • Preference: Alternating Up/Down (+1, -1, +2, -2...) * Y_SPACING.
      • Stability: Once placed, a node's position is locked (isPlaced = true) and will not be moved by subsequent expansions.

2. Frontend Integration

  • Ensure switchCentral triggers a re-layout using the new algorithm.
  • Pass the stable layout to Graph.js / Godot via PathBridge.

Verification Plan

  1. Spine Stability:
    • Load a path. Center on a Spine node.
    • Expand a prerequisite.
    • Verify the Spine node DOES NOT move.
  2. Lateral Unfolding:
    • Verify prerequisites appear above/below the spine, not inline.
  3. Complex Chain:
    • Expand a prerequisite's prerequisite.
    • Verify it flows backwards (Left) and finds a clear slot.

实施计划 - 实现前置节点懒加载 (Implementation Plan - Implementing Lazy Loading for Prerequisites)

目标是允许用户通过显式扩展特定节点的上下文来调查不完整的入度信息,而无需加载整个图表。

建议更改 (Proposed Changes)

1. 后端逻辑 (src/frontend/libs/path_core.js)

  • 无限制上下文扩展:
    • 在 diffusionLearning 中,迭代 forcedExpansionSet
    • 对于集合中的每个节点,检索 所有 入边 (getIncomingEdges),无论其完成状态或与原始路径的相关性如何。
    • 将这些边缘的源节点添加到 finalPathNodes 列表中。
    • 约束: 不要递归获取这些新节点的父节点(仅限 Level -1)。
    • 标记: 在输出中用 isExpanded: true 标记已扩展的目标节点。

2. 数据桥接 (src/frontend/path_worker.js)

  • 传递状态标志:
    • 确保 isExpanded 标志与 forcedExpansionSet 状态匹配。
    • treeLayout以此传递此标志给 Godot 客户端。

3. 可视化状态机 (path_mode/scripts/tree_renderer.gd)

  • 智能切换逻辑 (左侧):
    • 预计算: 在 _draw_layout_mode 开始时,迭代 _layout_edges 以构建 visible_in_counts 字典。
    • 决策逻辑:
      • global_in = node.inDegree (来自后端)。
      • visible_in = visible_in_counts[node.id].
      • is_expanded = node.isExpanded.
    • 状态:
      1. 已展开: 如果 is_expanded 为真:绘制 (-) 按钮。点击发射 node_collapse_prereqs_requested
      2. 可展开: 否则如果 visible_in < global_in:绘制 (+) 按钮。点击发射 node_expand_prereqs_requested
      3. 完整: 否则(可见 == 全局):绘制无(或禁用)。

4. 交互逻辑 (tree_view_panel.gd & path_app.js)

  • 折叠处理:
    • 在 path_app.js 中实现 collapsePrereqs(nodeId):从 forcedExpansionSet 中移除 ID 并触发更新。
    • 将新的 Godot 信号连接到此后端方法。

UI 不一致修复 (UI Inconsistency Fixes)

1. 统计面板调整大小

  • 修复: 修改 src/frontend/styles.css,使用 flex: 1 确保列表按比例调整大小。

2. 边缘可见性

  • 修复: 默认隐藏边缘,仅在悬停/聚焦时显示。

3. 入度数字不匹配

  • 修复: 添加设置以切换“可见”与“总计”入度显示。

第二阶段:主干与支流布局 (Phase 2: Spine & Tributaries Layout) (v1.4.2)

目标

实现稳定的树状布局,其中“主要学习路径”(主干)保持线性和静止,而前置节点(支流)在不破坏主干的情况下横向扩展。

建议更改

1. 核心算法 (src/frontend/libs/path_core.js)

getTreeLayout(centralId, learningPath)
  • 步骤 1: 识别主干:
    • learningPath.nodes 确定“关键路径”。
    • 根据与起点的距离为主干节点分配 Level (X坐标)。
    • 将主干 Y 坐标固定为 0
assignTributaryPositions(spineNodes, allNodes)
  • 步骤 2: 插槽管理:
    • 创建 SlotManager 以跟踪占用的 (X, Y) 位置。
    • 标记关键主干位置为已占用。
  • 步骤 3: 横向扩展:
    • 拓扑顺序 迭代节点。
    • 对于每个节点 N,识别其未放置的前置节点 P
    • 放置逻辑:
      • Target X: N.Level - 1
      • Target Y: 相对于 N.Y 找到最近的可用垂直插槽。
      • 偏好: 交替上/下 (+1, -1, +2, -2...) * Y_SPACING
      • 稳定性: 节点一旦放置,其位置即被锁定 (isPlaced = true)。

2. 前端集成

  • 确保 switchCenter 使用新算法触发重新布局。
  • 通过 PathBridge 将稳定布局传递给 Godot。

验证计划

  1. 主干稳定性: 加载路径,居中主干节点,展开前置节点。验证主干节点 不移动
  2. 横向展开: 验证前置节点出现在主干的上方/下方,而不是内联。
  3. 复杂链: 展开前置的前置,验证其向后(左)流动并找到清晰的插槽。

Phase 3: 9-Rule Tree Layout Engine (v1.4.3)

Date: 2026-02-26

Goal

Port the 9-rule expansion/claiming/visibility engine from tree_path_mockup.html into production code (path_core.js, tree_renderer.gd, path_app.js). This replaces the simple contour-based layout with a full ownership/claiming system for intelligent node management.

Gap Analysis: Mockup vs Production

Missing Rules

# Rule Mockup Function Production Status
1 Expansion Order (FIFO claiming) processExpansions() + expansionOrder[] ❌ Missing
2 Preceding Immunity (effective index) tryClaim() + getEffectiveSpineIndex() ❌ Missing
3 Following Migration (spine+followers) claimSpineChain() ❌ Missing
4 Single Appearance (owner-based) currentOwner priority check ⚠️ Partial (placedNodeIds)
5 Cross-Tributary Isolation (edge filter) drawEdges() owner check ❌ Missing
6 Spine Always Visible (return on collapse) determineVisibility() spine pass ❌ Missing
7 Sticky Claim (configurable) stickyClaimEnabled toggle ❌ Missing
8 Unit Migration (recursive claim) claim() recursive tributaries ❌ Missing
9 Tributary Hierarchy Immunity getTributaryRootSpineIndex() ❌ Missing

Missing Concepts

Concept Mockup Production
Node Ownership currentOwner, ownerPriority None
Expansion Order expansionOrder[] (ordered) forcedExpansionNodes (unordered Set)
Effective Spine Index getEffectiveSpineIndex() Fixed spineIndex only
Visibility Chain isOwnerChainVisible() recursive Binary collapsed/expanded
Hull-Node Avoidance Convex hull with padding Basic hull, no collision check

Existing Features to Preserve

  • ✅ Spine identification via isCritical flag
  • ✅ Contour-based collision avoidance for spine spacing
  • ✅ Recursive tributary placement
  • ✅ Hull/bubble drawing around tributary groups
  • ✅ Collapsed/expanded state per node
  • ✅ Godot WebSocket bridge communication
  • ✅ Tree renderer with bezier edges, styled nodes, pan/zoom

Proposed Changes (13 Steps)

Component 1: Core Algorithm

[MODIFY] path_core.js

Step 1: Add expansion order tracking to getTreeLayout() (L742-1133)

  • Add expansionOrder parameter (ordered array of expanded node IDs)
  • Replace unordered collapsedSet with ordered expansionOrder for FIFO claiming

Step 2: Implement node ownership system

  • Add currentOwner, ownerPriority, _isOnSpine to each layout node
  • Track claims during processExpansions() matching mockup logic

Step 3: Implement tryClaim() with all 9 rules

  • Rule 1: Owner priority check
  • Rule 2: getEffectiveSpineIndex() comparison (inherits owner index)
  • Rule 3+8: claimSpineChain() for following migration
  • Rule 4: Single appearance via owner check
  • Rule 5: Cross-tributary edge filtering
  • Rule 6: Spine always visible on collapse
  • Rule 7: Sticky claim toggle
  • Rule 9: getTributaryRootSpineIndex() for hierarchy immunity

Step 4: Implement determineVisibility() + isOwnerChainVisible()

  • Two-pass: spine always visible, non-spine follows recursive owner chain

Step 5: Update edge generation — filter edges between different owners (Rule 5)

Step 6: Update hull generation to group by owner

Component 2: Frontend Bridge

[MODIFY] path_app.js

Step 7: Track expansion ORDER (not just Set)

  • forcedExpansionNodes: new Set()expansionOrder: []
  • Update expandPrereqs(), collapsePrereqs(), collapseAll()
  • Pass expansionOrder to worker

Step 8: Add sticky claim setting + pass to worker

Component 3: Godot Tree Renderer

[MODIFY] tree_renderer.gd

Step 9: Edge rendering — skip edges where src.currentOwner != tgt.currentOwner

Step 10: Hull collision avoidance with rounded padding

Step 11: Node type coloring (spine=green, tributary=blue, shared=purple, migrated=orange)

Step 12: Expansion indicator badge (in-degree count circle)

Component 4: Worker Communication

[MODIFY] path_worker.js

Step 13: Pass expansionOrder and stickyClaimEnabled to getTreeLayout()

Verification Plan

  1. Expand Calculus → verify Optimization migrates (Rule 3)
  2. Expand Optimization → verify Diff Eq cannot claim Calculus (Rule 2+9)
  3. Collapse Calculus → verify spine nodes return (Rule 6)
  4. Toggle sticky claim → verify non-spine revert/persist (Rule 7)
  5. Check hull boundaries don't overlap nodes

第三阶段:9 规则树形布局引擎 (v1.4.3)

日期: 2026-02-26

目标

tree_path_mockup.html 中的 9 规则展开/认领/可见性引擎移植到生产代码(path_core.jstree_renderer.gdpath_app.js)中。用完整的所有权/认领系统替换简单的基于轮廓的布局。

差距分析:原型 vs 生产代码

缺失规则

# 规则 原型函数 生产代码状态
1 展开顺序(FIFO 认领) processExpansions() ❌ 缺失
2 前置免疫(有效索引) tryClaim() + getEffectiveSpineIndex() ❌ 缺失
3 后续迁移(脊柱+后续) claimSpineChain() ❌ 缺失
4 单次出现(基于所有者) currentOwner 优先级检查 ⚠️ 部分存在
5 跨支流隔离(边过滤) drawEdges() 所有者检查 ❌ 缺失
6 脊柱始终可见(折叠时返回) determineVisibility() ❌ 缺失
7 粘性认领(可配置) stickyClaimEnabled 开关 ❌ 缺失
8 单元迁移(递归认领) claim() 递归支流 ❌ 缺失
9 支流层级免疫 getTributaryRootSpineIndex() ❌ 缺失

缺失概念

概念 原型 生产代码
节点所有权 currentOwner, ownerPriority
展开顺序 expansionOrder[](有序) forcedExpansionNodes(无序 Set)
有效脊柱索引 getEffectiveSpineIndex() 固定 spineIndex
可见性链 isOwnerChainVisible() 递归 二元折叠/展开
Hull-节点避让 凸包 + 填充 基础 hull,无碰撞检查

建议更改(13 个步骤)

组件 1: 核心算法 — path_core.js(步骤 1-6)

  • 步骤 1: 添加展开顺序追踪
  • 步骤 2: 实现节点所有权系统
  • 步骤 3: 实现 tryClaim() 包含所有 9 条规则
  • 步骤 4: 实现 determineVisibility() + isOwnerChainVisible()
  • 步骤 5: 更新边生成 — 基于所有者过滤
  • 步骤 6: 更新 hull 生成 — 按所有者分组

组件 2: 前端桥接 — path_app.js(步骤 7-8)

  • 步骤 7: 有序展开追踪
  • 步骤 8: 添加粘性认领设置

组件 3: Godot 树渲染器 — tree_renderer.gd(步骤 9-12)

  • 步骤 9: 跨所有者边过滤
  • 步骤 10: Hull 碰撞避让
  • 步骤 11: 节点类型着色
  • 步骤 12: 展开指示器徽章

组件 4: Worker 通信 — path_worker.js(步骤 13)

  • 步骤 13: 传递 expansionOrderstickyClaimEnabled

验证计划

  1. 展开"微积分" → 验证"优化"迁移(规则3)
  2. 展开"优化" → 验证"微分方程"不能认领"微积分"(规则2+9)
  3. 折叠"微积分" → 验证脊柱节点返回(规则6)
  4. 切换粘性认领 → 验证非脊柱节点还原/保持(规则7)
  5. 检查 Hull 边界不与节点重叠