Skip to content

feat(core): #1662 同步规则桥 lithe-core-rules(WS-17、LSP-16、WS-04) - #1771

Merged
1lck merged 6 commits into
previewfrom
feat/issue-1662-sync-rules
Nov 6, 2026
Merged

1lck merged 6 commits into
previewfrom
feat/issue-1662-sync-rules

Conversation

@1lck

@1lck 1lck commented Nov 6, 2026

Copy link
Copy Markdown
Owner

#1662:新增同步规则桥 lithe-core-rules,把 WS-17(双击 Shift)、LSP-16(编辑器分组状态)、WS-04(隐藏路径规则)这三条两端重复的规则下沉到 Core;同时修复 windows/tauri/src-tauri/Cargo.lock 落后、导致 cargo build --locked 失败的问题。

为什么要这座桥

Windows 前端调用 Core 只能用异步的 Tauri invoke,有几条规则卡在同步热路径上,一直没法下沉:

  • LSP-16:zustand set() 里同步规范化 pane 树;
  • WS-04:文件树渲染时同步判断某个路径是否隐藏;
  • WS-17:每个按键事件都要同步跑一遍状态机。

结果只能两端各写一份,再靠共享 fixture 对照,而这正是 #1662 要消除的重复。macOS 通过 FFI 同步调用 Core,没有这个问题。

做法:这类纯规则放进新的纯 Rust crate。lithe-core 依赖它,macOS 和 Windows 的 Rust 后端照旧走 Core 通道;同一份源码再编译成 wasm,给 Windows 前端同步调用。规则只有一份源码。

同步桥本身

  • 新 crate rust/lithe-core-rules
    • 只依赖 serde 和 serde_json,不碰文件系统、进程、线程和时钟(时间由调用方传入)。
    • lithe-core 的 JSON 分发把这些命令转给它;命令名、envelope、错误码都和其他 Core 命令一致。
  • wasm ABI
    • 不用 wasm-bindgen,只导出四个 extern "C" 函数:lithe_rules_alloc、lithe_rules_dealloc、lithe_rules_execute、lithe_rules_result_len。
    • 输入输出都是 Core envelope 的 JSON。
    • release 构建用 opt-level="s"、LTO、strip。
  • Windows 加载器 src/core/core-rules.ts
    • WebView2 不允许在主线程同步编译大于 4KB 的 wasm,所以 main.tsx 先异步 instantiate,成功后才渲染 React;之后 executeCoreRuleSync 都是同步调用。
    • 加载失败时显示本地化错误(新词条 coreRules.initFailed、coreRules.notReady),不会回退到 TS 算法。
    • Tauri 没有配置 CSP,加载 wasm 不需要放宽任何安全策略。
  • 构建
    • scripts/windows/prepare/prepare-core-rules-wasm.mjs 生成 windows/tauri/public/core-rules/lithe_core_rules.wasm。产物在 gitignore 里,不提交。
    • 接入 dev 和 build;desktop:build 通过 beforeBuildCommand 间接包含。
  • CI
    • ci-windows.yml 的前端 job 安装 wasm32-unknown-unknown 并构建 wasm。
    • release-windows.yml 和 release-preview-windows.yml 的 Rust target 都加了 wasm32。
    • classify-ci-changes.sh 登记了新脚本。
  • 工作树资源:worktree-resources.json 把 core-rules-wasm 登记为不可复用,因为产物没有 identity stamp,各 worktree 自己重建。已同步更新测试和 docs/ci-builds.md 的说明。
  • 运行时只读:wasm 是打包进前端的只读输入,运行时不写入,不影响签名,也不影响 Sparkle delta。
  • 检查覆盖:注释检查、rustfmt、日志检查和 Rust 单测都已纳入新 crate。
  • 文档:新增 Agent Note .agents/notes/implemented/architecture/2026-11-05-core-rules-wasm-sync-bridge.md,写明问题、方案、边界(只放纯计算、小体量、处在同步热路径上的规则)、被否方案、构建与验证。develop-lithe Skill 加了一行指引。

WS-17 双击 Shift 手势状态机

  • 新增 Core 命令 keymap.modifierGestureStep:输入 {state?, event, timestamp},返回 {state, triggered}。
  • macOS DoubleShiftGestureRecognizer 和 Windows double-shift-recognizer.ts 都删掉了状态机算法,只保留薄适配层。
  • macOS 每个按键事件做一次同步 FFI,实测约 0.1 ms。
  • 原来的“WS-17 两端各留一份”例外说明已经从功能矩阵、契约和代码注释里删除。
  • 两端都逐步跑同一份 fixture shared/fixtures/keymap/modifier-gesture-v1.json,判定逻辑不变。

LSP-16 编辑器标签和分组的状态规则

  • 新增 Core 命令 editor.workbenchState.reduce。
    • 规范的“组成员状态”取两端的并集:有序去重的成员、选中项、MRU、固定、预览。
    • 支持的动作:normalize、open、close、activate、move、reorder、固定和预览相关操作、mergeGroup。
    • 返回新状态和 removedGroupIds。
  • Windows
    • 删除 pane-tree.ts 里的成员算法和 workspace-pane-session.ts 里自己做的去重和选中计算。
    • 改为在 zustand set() 里经适配层同步调用 wasm,每个 store 动作只调用一次 Core。合并关闭 pane 的标签以前是每个标签调用一次,现在整体只调用一次。
    • 调整比例、平均分配、拖动都不调用 Core,有测试计数保证。
    • 布局树只根据 removedGroupIds 修剪。
    • 新增 pane-tree、pane.store、workbench-state、workspace-pane-session 四个测试文件,之前没有任何 pane 测试。
  • macOS
    • EditorTabOrderFeatureModel 的合并(reconcile)和选中规则已删除,每个用户动作做一次同步 FFI。
    • EditorGroupLayout 只保留树结构和比例。
    • MRU、固定、预览传空值,因为 macOS 没有这些概念。
  • 行为:用户看到的行为不变。“选中的标签离开分组后选中谁”两端原本就不同:macOS 回到第一个标签,Windows 选补到原位置的相邻标签。现在由请求里的 selectionFallback(first / adjacent)分别保留。规则只有 Core 这一份。
  • fixture shared/fixtures/editor/workbench-state-v1.json 有 56 个用例,同时驱动 Rust、macOS(真实 FFI)和 Windows(真实 wasm)的测试。
  • 没有改动 feat(workbench): 支持 macOS 与 Windows 紧凑模式 #1651(紧凑模式)涉及的文件。

WS-04 隐藏路径规则

  • 内置的隐藏目录和文件模式、glob 匹配、目录与文件的区别,现在只在 rust/lithe-core-rules/src/visibility.rs 有一份。
    • Core 的 files.rs 直接复用它,BUILT_IN_HIDDEN_DIRECTORIES、glob_matches 等第二份实现已删除。
    • 文件夹名也会命中同名文件,例如 linked worktree 的 .git 文件。
  • 新增 Core 命令:
    • workspace.classifyVisibility:一批路径一次调用,返回 hidden[] 和 watchedDespiteHiding[]。后者标记 .lithe 配置目录:树里隐藏,但文件事件仍要监听。
    • workspace.visibilityDefaults:返回内置列表,供设置页展示,宿主不再保留副本。
  • macOS
    • LitheCoreContracts 不链接 Rust bridge,所以只定义 FileVisibilityEngine 协议,由 App 里的 RustFileVisibilityEngine 实现。
    • 首次使用时按 ObjC 类名发现实现;找不到会直接报错,没有第二份实现可以回退。
    • FileVisibilityRules 只保存用户条目并转发给引擎;插件目录列表和 watcher 改成批量调用。
  • Windows
    • 文件树和“打开全部文件”都按目录批量分类,结果按“目录 + 规则版本”缓存。
    • 删除 file-explorer-tree.tsx 里本地的 ignore 判断,以及 default-settings.ts 里的默认列表。
  • Windows 默认值变化(新安装时;以 Core 和 macOS 为准)
    • 新增默认隐藏:node_modules、target、build、dist、.venv 等内置目录,以及 .lithe/**、*.pyc。
    • 不再默认隐藏:.hg、.idea、.mypy_cache、.pytest_cache、.ruff_cache、CVS、_svn、*.pyo、*.rbc、*.yarb、*~、vssver*.scc。
    • 已经保存过的用户设置不会被改写,会继续作为用户条目生效。
  • fixture shared/fixtures/workspace/visibility-v1.json 同时驱动 Rust、macOS 和 Windows 的测试。
  • feat(core): #1662 第二轮 搜索与可见性(WS-03/04) #1766 已经让 Windows 的搜索、Quick Open 清单和本地历史请求带上隐藏规则,本 PR 补上了剩余部分。

Cargo.lock 修复

第一轮 #1758 给 lithe-core 加 similar 依赖时,只更新了 rust/Cargo.lock,windows/tauri/src-tauri/Cargo.lock 一直缺 similar 和 bstr 两个条目,导致 cargo build --locked 在 preview 上失败。这次补上这两个条目和 lithe-core-rules,版本与 rust/Cargo.lock 一致,没有升级其他依赖。

wasm 体积

只有 WS-17 时约 141 KB;加上 LSP-16 和 WS-04 后实测 288 KB(Mac 构建)/ 290 KB(Windows 构建)。

宿主语言检查(#1495 的棘轮)

  • 扫描范围和 workflow 触发路径加入 rust/lithe-core-rules/src。新 crate 仍属于宿主 Core,不能绕开检查。
  • 基线按代码搬迁记录:
    • files.rs:隐藏目录表(因为含 .gradle,计为 toolTables 19)和 1 个 words 移出,记为 {"words":6};
    • rust/lithe-core-rules/src/visibility.rs:接收这两项,记为 {"words":1,"toolTables":19};
    • FileVisibilityRules.swift:1 个 words 已删除,删掉该条目。
    • 合计少 1 个,没有新增任何语言或工具名。
  • 预期会红的一项:CI 用 --base HEAD^1 按文件比较基线,会把 visibility.rs 报成“新文件增长”。这是按文件计数时代码搬迁的必然结果。要消掉它,只能让插件声明隐藏目录(属于 [Feature] macOS 存量重构计划:清理废弃实现,将 Maven/Java 领域逻辑完整迁入插件后端 #1495 插件化的范围),或者放宽检查规则,这两件都不在本 PR 里做。不带 --base 的检查和 verify-core.sh 都通过。

测试隔离修正

  • core-rules.ts 新增 resetCoreRulesForTests()。
  • pane.store 的“每个动作只调用一次 Core”计数测试先重置,再在自己的计数包装下加载 wasm。
  • 原因:Bun 在一个进程里跑多个测试文件,如果更早的文件已经加载过实例,这个测试就会计到 0 次(只在多文件一起跑时出现)。

本地验证

Mac(本机)

  • scripts/architecture/verify-core.sh 通过(注释、rustfmt、日志、Rust 单测、契约、宿主语言检查)。
  • bun run typecheck(tsc)通过。
  • 本 PR 改动的 13 个 Windows 测试文件一起跑:184 个全部通过。
  • 间接用到 pane store 的 jump-navigation、bottom-pane-terminal-lifecycle 单独运行通过。
  • 宿主语言检查脚本自测 17/17 通过,test-classify-ci-changes.sh 通过。
  • test-macos.sh:
    • 相关 suite 用 --filter 运行通过,包括真实 FFI 驱动的 workbench-state、visibility、手势 fixture,以及 LitheCoreVerifier。
    • 全量运行时在 AgentManagementFeatureModelTests 遇到 actor 执行器断言崩溃(signal 6)。这个文件本 PR 没有改动,单独运行 8/8 通过;preview 基线的全量运行也会卡住,判断是并发负载下的已有问题。
    • 有 74 个负载类失败,用 filter 复跑后与 preview 基线一致。
  • 前端 bun 全量:6 个已有失败,与 preview 一致。

Windows(lithe-dev 真机,提交 1e1d40f)

  • cargo build --locked 通过(修复前在 preview 上失败)。
  • wasm 构建 290 KB。
  • bun:只有 agent-hooks-settings 失败,与 preview 一致。
  • 最后一个提交只改了测试、加载器的测试重置函数和检查脚本,已在 Mac 上验证。

待真机确认

lithe-dev 没有图形会话,所以 WebView2 实际加载 wasm、双击 Shift 能否弹出搜索,这两项要在合并后到有图形界面的 Windows 机器上确认。加载失败会显示错误,不会悄悄回退。

🤖 Generated with Claude Code

1lck and others added 6 commits November 5, 2026 19:25
- 新增纯 Rust crate rust/lithe-core-rules:只依赖 serde,不碰 I/O 和时钟,能编译到 wasm32;lithe-core 依赖它,并通过现有 JSON 分发暴露 keymap.modifierGestureStep,envelope 与错误码和其他 Core 命令一致
- wasm ABI 不用 wasm-bindgen:导出 alloc/dealloc/execute/result_len,输入输出为 Core envelope JSON;release 约 141 KB
- Windows 新增 src/core/core-rules.ts:启动时异步 instantiate(WebView2 不允许同步编译大模块),之后 executeCoreRuleSync 同步调用;加载失败显示本地化错误,不回退 TS 算法
- 新增 scripts/windows/prepare/prepare-core-rules-wasm.mjs,接入 dev/build、ci-windows 前端 job 和两个 Windows 发布工作流;产物不提交
- 注释检查、rustfmt、日志检查和 Rust 单测纳入新 crate
- 工作树资源清单登记不可复用的 core-rules-wasm,补测试和 docs/ci-builds.md
- 新增 coreRules.* 中英词条

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- macOS DoubleShiftGestureRecognizer 和 Windows double-shift-recognizer.ts 只保留薄适配层,状态机和阈值由 keymap.modifierGestureStep 提供;macOS 经 FFI 同步调用,Windows 经 wasm 同步调用
- 共享 fixture modifier-gesture-v1.json 由 Rust 测试、macOS 测试(真实 FFI)和 Windows bun 测试(真实 wasm)逐步驱动
- 删除功能矩阵、契约文档和代码注释里的 WS-17 例外说明,契约补充 keymap.modifierGestureStep
- 新增 Agent Note 2026-11-05-core-rules-wasm-sync-bridge.md,并在 develop-lithe Skill 加一行指引

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
lithe-core 新增 similar(diff.compareTexts,#1758)时只更新了 rust/Cargo.lock,
windows/tauri/src-tauri/Cargo.lock 缺少 similar 与 bstr 条目,导致
`cargo build --locked` 在 preview 上报 cannot update the lock file。
补上这两个条目,版本与 rust/Cargo.lock 一致,不升级其他依赖。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 新增 Core 命令 editor.workbenchState.reduce(rust/lithe-core-rules,同时经 lithe-core JSON 分发):规范“组成员状态”取两端并集(有序去重成员、选中项、MRU、固定、预览),动作含 normalize、open、close、activate、move、reorder、固定、预览、mergeGroup,返回新状态和 removedGroupIds
- 两端在选中项离开分组时的既有行为不同,用 selectionFallback(adjacent / first)保留:Windows 选相邻标签,macOS 回到第一个标签
- Windows:pane-tree.ts 的 normalizeGroup、成员打开/关闭/移动/固定/预览等算法删除,经 workbench-state.ts 适配层在 zustand set() 里同步调用 wasm,每个动作一次 Core 调用;布局树只按 removedGroupIds 修剪;会话恢复不再自己去重
- macOS:EditorTabOrderFeatureModel 的 reconcileGroups、drop、insert、move 的归属与选中规则改为经 RustEditorWorkbenchState 调用 Core,EditorGroupLayout 只保留树结构和比例;MRU、固定、预览传空值
- 共享 fixture shared/fixtures/editor/workbench-state-v1.json(56 例)由 Rust、macOS(真实 FFI)和 Windows(真实 wasm)测试驱动;补 pane-tree、pane.store(含 Core 调用次数、拖动比例不调用 Core)、会话测试,三个 buffer 测试加载真实 wasm
- 契约、功能矩阵 workbench-panes 和同步桥 Note 已更新

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 新增 workspace.classifyVisibility(按一批路径一次判断)和 workspace.visibilityDefaults,默认列表、glob 匹配、目录/文件区别只在 rust/lithe-core-rules/src/visibility.rs 一份;lithe-core 的 files.rs 复用同一份,删除第二份
- macOS:LitheCoreContracts 只定义 FileVisibilityEngine 协议,App 内 RustFileVisibilityEngine 经 FFI 实现;删除 Swift 默认列表和匹配算法;插件目录列表按目录批量分类
- Windows:文件树按目录批量调用 wasm 并按“目录 + 规则版本”缓存;删除 ignore 判断和 default-settings.ts 的默认列表(新装默认为空,已保存值不变)
- 共享 fixture visibility-v1.json 同时驱动 Rust、macOS(真实 FFI)和 Windows(真实 wasm)测试
- 更新功能矩阵、契约文档和同步桥 Note

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 宿主语言检查的扫描范围和 workflow 触发路径加入 rust/lithe-core-rules/src。
- 基线按代码搬迁记录:files.rs 的 toolTables 19 和 1 个 words 随隐藏目录表移到
  lithe-core-rules 的 visibility.rs;FileVisibilityRules.swift 的 1 个 words 已删除,总数减 1。
- file-tree-visibility:workspaceRootFor 允许返回 undefined;默认值测试补上类型参数。
- core-rules 增加 resetCoreRulesForTests()。pane.store 的 Core 调用计数测试先重置再加载,
  避免同一 Bun 进程里更早的测试文件已经加载了不计数的实例。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 6, 2026
@1lck
1lck marked this pull request as ready for review November 6, 2026 04:36
@1lck
1lck merged commit 053a9f2 into preview Nov 6, 2026
9 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant