Skip to content

refactor(run): 自动识别的运行配置只存在内存里,不再写进用户工程 - #1580

Merged
1lck merged 10 commits into
previewfrom
refactor/run-config-in-memory
Oct 29, 2026
Merged

1lck merged 10 commits into
previewfrom
refactor/run-config-in-memory

Conversation

@1lck

@1lck 1lck commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

背景

打开项目后,Lithe 识别运行入口时会往用户工程里写 .lithe/run/generated.json、.lithe/toolchains/requirements.json、.lithe/project.json 和 .lithe/.gitignore。在写这些文件之前,Run 和 Debug 都不可用,需要先手动点“识别项目”。这带来几个问题:只读检出会失败,版本控制里多出用户没要的文件,文件监听把这些写入当成工程变化;两端还各自维护了“generated.json 损坏后重新生成”的入口。

本 PR 参考 openidea 的做法:检测结果只放在内存里,只有用户保存的配置才写进工程。

改动

Rust Core

  • 新增 execution/generated_layer.rs:按规范化后的工作区路径,在进程内存中保存最近一次 runConfig.generate 的结果(运行配置文档和工具链需求)。
  • inspect、resolve、创建用户配置时的 id 冲突检查、toolchain_diagnostics 都改为读内存,不再读 .lithe/run/generated.json 和 .lithe/toolchains/requirements.json。本进程还没识别过的工作区,inspect 返回 status: "missing"。
  • GenerateRequest 新增可选的 cacheDirectory:生成结果写到 <cacheDirectory>/run-configurations/<sha256(工作区路径)>.json。JDT 还没回答时,生成器按“本进程的结果 → 应用缓存 → 旧版本留下的 generated.json(只读)”的顺序取上次的 Java 入口,运行列表不会因 JDT 冷启动而变空。缓存读写失败只是少一份加速,不会报错。
  • resolve:project.json 没写 defaultRunConfiguration 时,由 Core 选默认项——框架服务(Spring Boot、Quarkus、Micronaut)优先,否则选第一个不是 Current File 的检测入口。这条规则以前两端各写了一份,并在第一次生成时写进 project.json。
  • read_requirements / runConfig.selectJava:还没识别时,直接从 pom.xml、.java-version、Maven Wrapper 推导最低 JDK 版本,供 JDT 启动前选 JDK 使用,不再读一份可能过期的 requirements.json。
  • 删除不再使用的 read_document。

Windows

  • 删除 Tauri 命令 run_write_generated 及 write_generated_documents、WriteGeneratedArgs、pretty_json,tauri-core.ts 中的路由同步删除。
  • generateRunConfiguration 通过 appCacheDir() 传 cacheDirectory。
  • run.store.ts:loadProject 遇到 missing 时直接调用 generate(root, undefined, { automatic: true }),不再停在“请生成”状态。loadProject 只在打开运行面板、设置、Agent 查询或启动 Java 时触发,打开工程本身不会触发。自动识别不显示“识别到 N 个入口”的提示;JDT 就绪后的刷新沿用发起那次识别的标记。编辑器保存后重新加载时,遇到 missing 也会先识别。
  • 删除 defaultGeneratedConfigurationId(改由 Core 选),恢复路径列表中去掉 generated.json 和 requirements.json。

macOS

  • 删除 MacRunConfigurationStore.writeGenerated 和 writeJSONObject;generate 传 storage.cacheDirectory()。.lithe/.gitignore 仍由保存本机配置时的 ensureLocalRunIgnored 维护。
  • AppModel.loadProjectServices:工作区快照就绪后,如果状态是 missing,自动调用 generateFromJavaEntrypoints(..., automatic: true)。automatic 参数一路传到 RunService.generateRunConfigurations:自动识别成功后 generationState 保持 .idle,没有入口时仍显示 .noEntries。
  • RunView 删除“重新生成损坏的 generated.json / requirements.json”按钮(这两个文件不再存在);“打开配置”的默认路径改为 .lithe/run/configurations.json;恢复路径列表同步去掉这两个文件。

界面文案(共享 i18n)

  • 三条 macOS 文案和 Windows 的 run.missingMessage 不再提 generated.json,例如“Identify the project to enable Run and Debug…”/“识别项目后即可使用运行和调试…”。已重新生成 Localizable.strings 和 shared.generated.ts。

文档

  • 新增 Agent Note 2026-10-29-run-configuration-detection-in-memory.md:记录决策、被否方案,以及缓存文件的生命周期、所有权和可写位置。缓存位于应用缓存目录,不影响 app bundle、代码签名和 Sparkle 增量更新。
  • shared/contracts/rust-core-api.md 更新 generate、resolve、selectJava 的行为说明;功能矩阵 run-discovery 补充说明和证据。
  • 这个缓存是应用运行时缓存,不是构建期 .artifacts 资源,因此不涉及 worktree-resources.json。

兼容性

  • 旧版本已经写进工程的 .lithe/run/generated.json、requirements.json 不会被删除或改写,只作为上次 Java 入口的只读来源。
  • .lithe/project.json 仍是用户可编辑的团队文档,其中的 defaultRunConfiguration 继续优先。
  • 远程工作区不受影响:macOS runtimeRequirements 仍读取远端文件,远程运行配置发现本来就未接入。

仓库 Skill 自审与修复(2026-10-29)

结论:已修复一项 P1 回归;最终改动未发现需要阻塞合并的问题。 按 lithe-code-review、develop-lithe、write-stable-tests 和 agent-notes 完成自审。

发现并修复的问题

保存过构建任务的工程重启后,内存检测层消失,但 inspect 因保存的任务仍返回 ready,两端便跳过自动识别。实测重启前为 Spring Boot 服务 + 保存任务,重启后只剩保存任务。问题由本 PR 将生成层改为进程内存后引入。

修复提交 145ddc33d:Core 返回独立的 requiresGeneration 标记;Mac 和 Windows 在已保存任务可用时仍按该标记补齐自动入口,完成识别后不重复生成。Windows 保存配置后的重新加载也处理该标记。共享契约、fixture、功能矩阵、Agent Note 及两端回归测试同步更新。已合入最新 preview d3072abd4 和 #1573 的 e57008986。

范围与验证

  • 范围:preview ← perf(lsp): 修复补全/跳转卡顿与 pom 修改后的全量重导,调优 jdtls JVM #1573 ← 本 PR;重点审查生成层、应用缓存、显式保存、默认配置选择、JDK 选择、自动识别与过期结果隔离。保留 JDT 对 Java 入口的所有权,未新增本地 Java 语义扫描器。
  • 完整 verify-rust-core.sh 通过:1349 个 Core 单元测试、其他 Rust targets、Swift 编译、C ABI、符号和执行集成验证。
  • macOS 相关 428 个测试通过,9 个桥接集成测试通过;新增/修改的 Swift 回归用例耗时 3ms、5ms。
  • Windows 前端 311 个测试通过,类型检查通过。新 head 的 Windows 原生测试待 GitHub CI,未启动 Windows 虚拟机。
  • Rust 两项重点回归通过逐项有界执行并生成 HTML/JUnit,耗时 112ms、17ms。
  • Core/service/shared contracts/Windows 边界、测试稳定性、Rust 注释、i18n、Agent Notes、平台矩阵与变更门禁、bundle 只读验证及 diff 检查通过。
  • 观察项:检测层按进程缓存工作区,关闭工作区时不释放;多工作区长会话的内存占用尚未实测。真实新工程/大项目整机体验仍未验收,矩阵保持 pending。

🤖 Generated with Claude Code

真实 macOS 应用验收补充(2026-10-29)

在包含两项 PR 的 145ddc33d 预览包中实际操作 RuoYi-Vue(13db1fc,266 个 Java 源文件):打开项目自动出现运行配置并默认选中 ruoyi-admin;Java 导入完成;输入 SpringApplication.r 得到 run 方法补全,F12 能进入 Spring 依赖源码。独立 Hello.java 样例经界面完成编译、启动、输出和停止,未留下样例进程。

完整退出并启动新进程后,使用 Core 文档转换准备的已保存 build.provider 测试任务与自动检测的 8 个 Java/服务入口同时显示,无需手动识别,验证了本次重启回归修复。此项未覆盖通过 UI 保存任务。操作前后 1,394 个 bundle 文件哈希一致;基线在初次启动后采集,因此不将此结果描述为完整启动期间的哈希验证。验收后应用与 Java 子进程全部退出。

限制:未运行 Windows 虚拟机,未做前后版本性能基准,也未启动依赖数据库/Redis 的若依服务。真实界面还暴露出既有检测器把继承 Spring Boot 插件的库模块列作服务的问题;该检测器不在两项 PR 的改动中,保留后续优化项。自动识别未创建 generated.json、requirements.json、project.json;项目仍会出现原有的 local.json。不能据此声称所有 Windows 体验问题已解决。

1lck and others added 4 commits October 28, 2026 22:44
…n actor

The engine copied the whole client state for every request, document
change, and server message while holding the session locks. That state
holds the full text of every open document, so a jdtls project import,
which publishes thousands of messages, kept completion and navigation
waiting behind the copies. Open document text is now shared (Arc<str>,
still a plain JSON string), and each server message is parsed once,
outside the state lock, instead of up to three times.

macOS drained Core's event queue on the main actor every 10 ms while
requests were pending, waiting on the same session lock. The drain now
runs off the main actor; a send waits for a drain already in flight, so
a drain still never returns a reply before its operation is registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The JDT -data fingerprint no longer includes build-file mtime or size.
jdtls already detects pom and Gradle script edits itself
(MavenProjectImporter.needsMavenUpdate, GradleProjectImporter.
checkGradlePersistence), so switching to a fresh -data directory on every
edit only turned an incremental update into a full re-import. The Gradle
wrapper distribution stays part of the key because jdtls does not check it.

jdtls now launches with the GC and logging defaults vscode-java and the
upstream CI use (ParallelGC with GCTimeRatio=4 and
AdaptiveSizePolicyWeight=90, -Xlog:disable, DetectVMInstallationsJob
disabled). A plugin option with the same key replaces each default, and a
plugin collector choice replaces ParallelGC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… workspace

Identifying a project wrote .lithe/run/generated.json, toolchains/
requirements.json, project.json and .gitignore into the user's workspace, and
Run and Debug stayed unavailable until the user pressed "Identify project".

Core now owns the detected layer:
- runConfig.generate stores its result per workspace in process memory
  (execution/generated_layer.rs); inspect, resolve, user-configuration id
  checks and toolchain diagnostics read it from there.
- An optional cacheDirectory records the result in the application cache so a
  new session can list JDT's previous Java entry points before JDT answers.
  A .lithe/run/generated.json left by older versions is only read as that
  seed, never written.
- resolve picks the default configuration (framework service first, else the
  first detected entry other than Current File) when project.json names none.
- selectJava derives the project JDK minimum from pom.xml / .java-version /
  the Maven wrapper before any generation.

Hosts stop writing detection files and identify automatically:
- Windows removes run_write_generated; the run store identifies a project on
  load when inspect reports missing, without the entry-count notice.
- macOS removes MacRunConfigurationStore.writeGenerated, passes the cache
  directory, identifies after the workspace snapshot is applied, and drops the
  "regenerate broken generated.json" action.
- UI copy no longer mentions generated.json (shared i18n regenerated).

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 Oct 29, 2026
@1lck
1lck marked this pull request as ready for review October 29, 2026 10:36
@lithe-code-review

lithe-code-review Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⛔ 有阻塞
范围: preview ← refactor/run-config-in-memory · head: 1ec2281

Findings

  1. [P1] 自动识别仍通过旧迁移器写入用户工程
    macos/Sources/Lithe/Models/AppModel/AppModel+RunConfiguration.swift:170

    • 实际影响:首次打开工程仍会创建 .lithe/run/local.json;只读工程会因此识别失败,Run/Debug 无法进入就绪状态。
    • 触发条件:工程没有本机配置,且没有对应的迁移完成标记。
    • 复现步骤:以新路径打开没有 .lithe 的工程。新增自动入口调用 RunService.generateRunConfigurations,其第 689 行仍调用 migrateLegacySettings。MacRunConfigurationStore.swift:362 在没有任何旧设置时也会创建空文档;禁止工程写入后,该错误进入识别失败分支。
    • 来源:本次加剧。原本需要显式识别才能触发的写入,现在会在加载工程时自动发生。新增测试仅调用 store.generate,没有覆盖后续迁移。
  2. [P1] 保存过构建运行配置的工程会跳过检测层初始化
    rust/lithe-core/src/execution/configuration.rs:1218

    • 实际影响:重启后,自动发现的 Java、npm 等运行入口消失;已有检测入口的覆盖也可能变成孤立覆盖。
    • 触发条件:本机配置包含 user: ID 的 build.provider 条目,新进程尚未生成检测层。
    • 复现步骤:在已有检测入口的工程保存一个构建运行配置,然后重启。内存层为空,但 inspect 第 582 行因 has_saved_build_run 返回 ready,两端只在 missing 时执行的自动识别不会触发;随后 resolve 第 1221 行使用空检测层,只剩保存的配置。
    • 来源:本次引入。此前重启仍能读取磁盘检测层,现在该层只存在于进程内存。
  3. [P2] 识别前的 JDK 选择忽略独立 Java 工程的版本声明
    rust/lithe-core/src/execution/configuration.rs:3124

    • 实际影响:即使安装了兼容 JDK,首次选择仍可能采用过旧的 JAVA_HOME,且没有版本不兼容警告。
    • 触发条件:工程包含 Java 源码和 .java-version,没有 Maven/Gradle 文件,也没有内存检测层。
    • 复现步骤:工程放置 App.java 和内容为 21 的 .java-version,向 runConfig.selectJava 提供优先级 0 的 JDK 8、优先级 1 的 JDK 21,并以 JDK 8 为 fallback。此处将 has_java_ecosystem 判为 false,又传入空配置列表;detect_requirements 虽读到版本,仍不插入 project-jdk,最终选择 JDK 8。
    • 来源:本次引入。新的识别前推导路径没有保留此前生成需求文档对这类工程的版本约束。
  4. [P2] 自动识别没有恢复 macOS 已保存的运行目标
    macos/Sources/Lithe/Models/AppModel/AppModel+RunConfiguration.swift:170

    • 实际影响:重启后选中的运行目标被默认项或临时的 Current File 替代,并可能覆盖原有选择偏好。
    • 触发条件:用户上次选择了非默认目标,新进程加载时检测层为空。
    • 复现步骤:在包含多个入口的工程选择非默认目标并重启。RunService.loadProject 只有在检查结果为 ready 时才读取持久化选择(第 533 行);现在加载走 missing → 自动识别,而生成流程第 697、724 行只读取进程内选择表和默认项。selectedConfigurationID.didSet 随后保存替代后的选择。
    • 来源:本次引入。此前重启读取磁盘检测层,会进入恢复持久化选择的加载分支。

Scope Check

  • 未找到可验证的关联 Issue;按 PR 描述核查内存检测层、自动识别和兼容行为。
  • Core 内存存储、平台写入入口删除、默认项归并和共享文案已实现;完整加载流程仍存在上述缺口。
  • Java 入口继续复用 JDT,本次未发现新增并行语义实现。
  • 四项问题均由本次行为变化引入或加剧,属于本次重构直接影响的调用链。

Suggestions

  1. 没有实际旧数据时禁止迁移器创建文档;补充覆盖完整自动识别流程的只读工程测试。
  2. 单独表达检测层是否初始化,保留已保存构建配置可运行的能力,同时让宿主补齐检测层;覆盖保存后重启的混合配置场景。
  3. 让明确的 Java 版本声明能够产生 project-jdk 需求,补充无构建文件的识别前选择测试。
  4. 自动识别沿用持久化选择,并保证 JDT 后续刷新也保留该选择。
  5. 请补充 Run 空状态、恢复操作及设置确认弹窗的前后截图;当前未提供截图,视觉效果尚未验证。

Summary

内存检测层及平台接口的调整方向一致,但首次加载仍有工程写入,保存构建配置后的重启也会漏掉检测入口。这两项影响核心运行流程,需要修复后合并。

Verification

  • 静态检查了指定 base...head 的 Core 检测、缓存、解析和 JDK 选择,两端加载与生成调用链、相关测试、契约、i18n 源文件及 Agent Note。按要求未读取生成文件正文。
  • 作者报告:Rust 1345 项、Windows 运行功能 310 项、macOS 聚焦测试 317 项通过,以及契约、边界、i18n 和 bundle 不变性检查通过;本次未独立执行。
  • 提供的 head 检查中,Rust、Swift、Windows 前端及 Windows host 测试均为 skipped;门禁成功不能替代这些测试的执行结果。
  • 本次未运行任何代码、测试或应用,未访问网络。仍需完整加载流程回归、Windows 原生验证、只读工程手动验收及界面截图。

@1lck
1lck merged commit fbb5e57 into preview Oct 29, 2026
17 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