Skip to content

Commit 50b4ebe

Browse files
lin-cocoxiaoyumuxiZJUSCODEZJUSCODEclaude
authored
feat(workspace): 支持目录索引排除并统一 Git 设置入口 (#1597)
* feat(workspace): enforce directory indexing exclusions (#958) * fix(workspace): honor indexing ownership across Windows roots Fix attached-root LSP gates and reevaluate documents when workspace roots change. Carry request-local ownership through inventory, search, replacement and incremental index caches; preserve scoped search when unrelated roots are unavailable. Disable unsupported remote indexing settings, localize settings failures, normalize both Java exclusion test expectations, and register regressions in Windows CI. * fix(workspace): 修复索引租约生命周期与跨窗口同步 * docs(workspace): 记录复审修复与 macOS 原生验收证据 * fix(lsp): 保留 Windows 冷启动索引策略的原生路径身份 * fix(macos): 保持打开身份并修复索引回归验证 将会话打开代次与资源投影代次分开,保留立即 Run 的等待意图及同根重开隔离。 避免无待确认 Java 决策时重复发布空值,并使设置与混合规则夹具等待真实生命周期边界。 为 Git 观察 CI 补齐逐项报告,按原始输出活性检测 Swift 停顿,保留既有断言与预算。 记录通过的 126 条受影响 Swift 及 116 条身份消费者验证。 * feat(plugins): 独立编辑器高亮与动作 API,复用普通函数调用 (#1617) * feat(plugins): 声明式语法高亮与运行标记(API-25/27/29) - languages 2:插件语言可带 Monarch 语法和编辑器配置,Core 校验,纯语法插件免重启 - runMarkers 1:backend 2 插件函数回答可运行行,编辑器画运行按钮,点击经 commands/didRun 带 runMarker - 契约、fixture、Agent Note、计划文档与功能矩阵同步更新;Windows 暂不支持 Refs #570 * feat(plugins): 独立编辑器高亮与动作扩展 - languages 2 提供静态语法和独立编辑配置,插件正则在可终止 worker 中运行\n- editor/registerActions 运行期注册文本动作,点击复用普通函数调用,不扩张 Run/LSP/Build/Debug 领域契约\n- 补齐文档版本、动作租约、取消与清理、独立工具后端启用及安全模式边界\n- 更新 Skill、审查规则与决策笔记,总结 #1581/#1558 的架构边界\n- 同步契约、fixtures、中英词条、功能矩阵与资源复用说明\n\nRefs #570\nReworks #1617 * docs(plugins): 明确 API-25 JSON 贡献的支持范围与局限 区分包内静态 JSON 贡献与尚未开放的运行期 Provider 注册、替换和释放;说明独立语义 Provider、新引擎适配及平台、SDK 和验收限制。同步计划、架构笔记、正式契约与功能矩阵。 * fix(plugins): 修复编辑器扩展审查中的生命周期与高亮问题 动作刷新立即确认调度并隔离非致命失败,整批收集共享八秒期限并在撤销时清理计时任务。 Core 保留校验后的 grammar 原始文本,worker 按源顺序解析 cases 并显式默认 root;配置-only 别名借用既有 tokenizer,重映射 token 语言身份以保留独立注释配置。 模块状态观察弱引用 runtime,补齐有界、确定性的 Swift/Rust/WebKit 回归与测试文档模型清理,同步契约、Agent Note 和功能矩阵。 验证:57 项 Swift、1459 项 Rust、39 项编辑器和 119 项真实 WebKit 测试通过;静态边界、i18n、只读 bundle、资源复用与矩阵门禁通过。未执行 Windows 原生构建或打包安装验收。 Refs #1617, #570 * fix(indexing): preserve scan ownership and snapshot publication Retry current-workspace indexing cancellation without retiring another owner, publish only the latest complete inventory, and restore saved sessions once per workspace. Apply scoped search before caps and cursors, preserve dotted provider configuration siblings, and normalize Windows mark identity and localized read failures. * docs(indexing): record final macOS native acceptance evidence * fix(search): localize indexing failures and synchronize deferred Run tests * test(macos): synchronize every initial Run inventory release * fix: preserve excluded history and validate indexing marks * fix(macos): 修复分支列表悬停长分支名时触控板无法滚动 (#1621) (#1625) * fix(macos): 修复分支列表悬停长分支名时触控板无法滚动 (#1621) 触控板手势绑定在开始时的窗口上。悬停扩展面板收到首个滚动事件就关闭, 导致同一手势的后续事件全部丢失。手势期间改为隐藏面板并继续转发给列表, 手势结束后再关闭。鼠标滚轮行为不变。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(matrix): 记录分支列表触控板滚动的验证要求 (#1621) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(macos): 手指停顿时保留滚动手势,点击立即回收隐藏面板 (#1621) 手势进行中的回收超时从 1 秒放宽到 10 秒,避免停顿后继续滚动时丢失手势。 隐藏面板收到点击时立即回收,并在功能矩阵补充对应验收步骤。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(macos): remove push dialog file request ordering race --------- Co-authored-by: ZJUSCODE <ZengJun326@outlook.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn> * fix(macos): forward hidden branch expansion clicks and clean up on close (#1629) * fix(workspace): preserve directory roles and indexing watcher ownership * test(windows): use canonical roots in indexing lifecycle fixtures * test(windows): identify the owned Run fixture descendant explicitly * feat(macos): 状态栏内存只显示图标,帧率去掉图标并收紧间距 (#1630) - 内存指示器只保留内存芯片图标,不再常驻显示“总计 / Lithe”数值; 悬停提示仍给出总计、Lithe、LSP、服务内存,点击打开原有的托管内存详情, VoiceOver 读出“托管内存”和当前总计。 - 帧率只显示“60 FPS”文字,去掉速度表图标。 - 新增 18pt 的状态栏紧凑图标按钮样式,只读锁和内存图标改用它, 不再沿用 28pt 工具窗口按钮留出的大块空白。 - 删除不再使用的 "Total %@" 词条并重新生成本地化资源。 - 更新 memory-monitor 功能矩阵的 macOS 证据与验收说明。 * fix(macos): 虚拟化后台服务退出列表并补充滚动性能规范 (#1627) (#1633) * fix(macos): virtualize background service exit selection (#1627) * fix(macos): keep background service accessibility selection live * fix(agent): 修复 Codex 会话启动与历史漏查,新增供应商筛选 (#1632) * fix(agent): restore Codex history and add provider filtering * fix(agent): honor manual archive collapse under provider filters * feat(macos): 彩色 LSP 状态菜单与普通日志标签页 (#1631) * feat(macos): 增加彩色 LSP 状态菜单与普通日志标签页 * fix(macos): LSP 入口常驻并仅由启用插件注入服务器 * fix(lsp): 等待重启关闭完成并保留局部索引重置状态 * fix(markdown): preserve inline HTML image layout and alignment (#1623) (#1635) * feat(markdown): render PlantUML diagrams offline on macOS (#1637) * feat(markdown): add offline chemistry formulas and subscripts on macOS (#1636) * feat(macos): 终端标签任意拖拽分屏并保留 PTY (#1638) * feat(macos): 终端标签任意拖拽分屏并保留 PTY - 终端标签改用 AppKit 拖拽会话与私有 pasteboard,避免 Monaco 接收文本拖放 - 工具窗终端可拖入编辑器边缘或空编辑器,编辑器终端可拖回工具窗 - 抽出 EditorGroupLayout.splitting 复用分屏几何,补充模型、拖拽与 PTY 测试 Refs #1622 * fix(macos): preserve terminal drop order and deduplicate split previews - Limit the native tool-window drop overlay to the canvas so tab-strip before/after insertion remains available for terminals dragged from editors. - Publish editor-group preview targets only when they change, including repeated clearing and drag lifecycle cleanup. - Add focused native overlay geometry and preview publication regressions. - Update the docking Agent Note and terminal capability matrix evidence and verification steps without claiming unperformed runtime verification. Validation: six focused Swift tests passed on Swift 6.3.3 with macOS 26.5 SDK; runtime bundle, architecture, i18n, Agent Notes and matrix static gates passed. The exact PR-based push tree also passed the static gates. Refs #1638 --------- Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn> * fix: align project JDK, isolate application sessions, and improve search (#1642) [skip ci] Use the selected project JDK in JDT configuration on both platforms. Give Windows applications independent cancellable sessions across Run, IDE API and Debug, with isolated failure output. Avoid blocking cold file/text search on content index warmup and improve macOS search sizing and result layout. Include shared fixtures, regression coverage and architecture notes. * feat(plugins): 插件注入的工具链框架与 macOS 只读环境设置页 (#1581) * feat(plugins): 插件注入的工具链框架与 macOS 只读环境设置页 (#1581) 按 #1581 评审后的改写计划重做:宿主不认识任何工具,工具定义、版本管理器 (提供方)全部由插件声明;Core 只做校验和四层解析,不解析 mise 等管理器 的私有格式。 - 契约:新增 toolDefinitions 1、toolchainProviders 1,executableResolution 增加 v2(只用于工具定义候选);同步 schema、README、toolchains.md、 fixture 和 rust-core-api.md;proposals/toolchains/ 放完整方案草案 - Rust Core:新领域模块 rust/lithe-core/src/toolchain/(定义注册表、 提供方答复校验、目录固定、固定/项目文件/全局/自动四层解析、版本匹配、 扫描计划)和六个 toolchains.* 命令,全部只处理宿主快照,不启动进程、 不扫描、不写文件 - macOS:设置 › 构建新增「运行环境」「环境来源」两页,内容来自已启用 插件的声明和磁盘上的真实文件;未实现的添加、切换、保存、查询入口不显示 - 登录 Shell:新增只读 inspect,来源页显示读取状态、变量数、耗时和 PATH 各目录来源;重新读取不改变本进程 PATH - Debug 预览入口 --toolchain-settings-demo / --toolchain-sources-demo - Agent Note、设置层级 Note、功能矩阵和中英词条同步 Refs #1514 * feat(plugins): 工具链安装同时支持声明式扫描与提供方查询 (#1581) 工具的已安装版本有两条途径,可以只用其一或同时使用: - 声明式扫描:工具定义的 discovery/homeLayouts/version 由宿主展开,不运行 插件代码;可用的根和通配写法固定,新的安装形态交给提供方回答。 - 提供方查询:toolchainProviders 通过 toolchains/query 返回安装和要求。 - watchFiles 改为可选:不声明的提供方只报告安装,答复里带版本要求会被拒绝; 定义工具的插件也可以作为自己工具的提供方。 - 自动层把两条途径合成一个候选集合:同一规范化目录只算一次,来源标注优先 提供方、其次 host:system,只用于显示;不同目录同版本仍报冲突。 - discoveryPlan 对每个工具同时列出 scans 和 providers。 - macOS 环境来源页对只报告安装的提供方显示「只报告已安装的版本」。 同时修复评审的两个 P2: - 符号链接安装在解码时已规范化,解析时却按原始路径查证据而误判 unsafePath; 现在文件证据同时证明其规范化目标,证据互相矛盾时拒绝快照。 - 环境变量候选声明的 path 布局被忽略、总取第一个布局;现在按声明的布局选择。 * feat(plugins): macOS 设置页接入声明式扫描与版本探测 (#1581) - Core 新增 toolchains.candidates:列出一个工具的登录 Shell home 变量、扫描到的安装、跳过原因和自动层的选择;和 resolve 共用事实校验,宿主分两轮调用完成版本探测,工作区内的目录在探测前拒绝 - toolchains.discoveryPlan 增加 pathPrepend 模板和各平台适用的 home 变量 - 新增 MacToolchainDiscovery:按插件声明展开扫描,只探测 Core 返回的可执行文件;扫描根、条目、文件事实、安装数、探测数、并发和总时长都有上限,超出时给出诊断 - 运行环境页:全局卡片显示 home 变量和探测到的版本,PATH 命令标为版本未验证;自动卡片列出真实安装、选中项、同版本冲突和跳过数,路径显示扫描到的条目名 - 加载器只把登记在官方目录、并经内置或插件市场安装的插件标为官方来源 - 预览改用完整的样例清单,扫描本机真实安装 - 更新契约 toolchains.md、rust-core-api.md、Agent Note、功能矩阵和中英词条 * test(macos): 设置页声明顺序与构建分组保持一致 (#1581) 运行环境、环境来源两页插在项目页和运行配置之间,使 SettingsCategory 声明顺序(搜索结果顺序)与侧栏构建分组顺序不一致, settingsCategoryPlacesProjectAndRunBetweenKeymapAndTerminal 在 CI 失败。 把两页的 case 移到项目页之前,测试改为断言 Keymap 与 Terminal 之间 恰好是构建分组,且分组以项目页、运行配置结尾。 * feat(plugins): macOS 设置页接入工具链提供方查询 (#1581) - Core: lsp.toolchainRequest 与 litheToolchains 协商,原样回传回复交 toolchains.decodeReply - Core: candidates 接受 providerInstallations 并保留提供方归属 - 修复别名链接 home 只有链接路径事实时被判 unsafePath(机器人 P2) - macOS: 启动或复用插件后端查询,全局+逐目录独立 operationID/代次,超时与取消 - macOS: watchFiles 与回复 watch 变化整份重查,短时间事件合并 - 设置页显示提供方要求、版本与查询结果;文案走 shared/i18n - 更新契约、功能矩阵与 Agent Note * feat(plugins): 运行环境页每行生效来源改由 Core toolchains.resolve 决定 (#1581) - 每个查询过的目录、每个工具调用一次 toolchains.resolve,摘要、徽标和 “第几层生效”都读它的结果,视图不再自己按固定 → 项目 → 全局 → 自动排序 - 解析与提供方查询共用同一份文件事实;同一轮查询的提供方共享代次, 每次查询仍有独立 operationID - 固定值交给 Core 校验:只有固定值的目录只解析它固定的工具(每次加载 最多 256 个),固定的 home 经 toolchains.candidates 检查后探测, 不会出现在自动检测列表;不可用时终止失败,不回落 - 未安装的项目要求记为 unmet 并退到下一层;Core 无法给出结果时给出诊断 - 已知限制写入提案 Note 并建 Issue:冲突选择与写入 #1639、 项目 wrapper #1640、PATH 命令 commandHomes #1641 - 新增文案走 shared/i18n 并重新生成 * fix(plugins): 加载途中 context 变化的回复不再显示项目要求 (#1581) - 同一目录一轮查询里,context 与本轮不一致的提供方回复不会进入 toolchains.resolve, 但它的项目要求仍被写进页面的 requirements,导致页面显示的要求和 Core 的生效结果来自 两个不同的 context;现在只记录进入本轮的回复的项目要求,全局要求和安装列表不变。 - 新增 aReplyUnderAChangedContextIsLeftOutOfItsDirectory 测试:两个提供方在加载途中拿到 不同代次的 context,断言只有进入本轮的提供方要求被显示、Core 用它解析;修复前该测试失败。 - 测试夹具 providerSource 支持声明多个提供方,原有用例行为不变。 - Agent Note 更正“每个路径只读一次”的说法:查询阶段和解析阶段分两次读取事实,单次解析 内部一致;并补充 context 变化时回复不进本轮的取舍。 * feat(plugins): 提供方超时或不可用交给 Core 记为 providerUnavailable,工具行区分未响应与不可用 (#1581) - Core 新增 toolchains.failureReply:查询超过 15 秒没有回复,或后端起不来、断开、没有协商这个提供方时,宿主用它生成标准错误回复(plugin.toolchain.timeout / plugin.toolchain.providerUnavailable),再交给 decodeReply,错误码映射只在 Core 维护一份 - toolchains.resolve 只为问过这个工具的查询记 providerUnavailable,并带上 cause 区分超时和不可用;修复一个提供方失败时把它没被问到的工具也记为缺失的问题 - macOS 接线层把 Core 的 requestTimeout 转成 MacToolchainDiscovery.QueryTimeout;取消不生成回复 - 这类回复照常进入目录轮次,下一层来源继续决定;提供方状态显示“15 秒内未响应”或后端原因 - 运行环境页工具行在结果旁写出“mise 未响应 / mise 不可用”,第 2 步在版本文件存在但提供方没回答时显示同样原因,不再显示“版本未查询”;固定值失效时不显示 - 新增中英词条三条;契约 toolchains.md、rust-core-api.md、Agent Note 与功能矩阵同步更新 - 测试:Rust 覆盖两种原因的回复往返、下一层决定、只记被问到的工具、非法输入拒绝;Swift 覆盖超时与后端停止两种提供方并存时的结果与状态 --------- Co-authored-by: Yao Jingxi <23722032@bjtu.edu.cn> Co-authored-by: FxRayHughes <25192142+FxRayHughes@users.noreply.github.com> --------- Co-authored-by: Xiaoyumuxi <23722032@bjtu.edu.cn> Co-authored-by: ZJUSCODE <152275506+ZJUSCODE@users.noreply.github.com> Co-authored-by: ZJUSCODE <ZengJun326@outlook.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: 十二翼堕落天使 <61737634+icefery@users.noreply.github.com> Co-authored-by: Lichenkang <2188718831@qq.com> Co-authored-by: 枫溪 <bushuaichen@yeah.net> Co-authored-by: FxRayHughes <25192142+FxRayHughes@users.noreply.github.com>
1 parent 589116e commit 50b4ebe

127 files changed

Lines changed: 10708 additions & 739 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/notes/implemented/architecture/2026-10-08-windows-git-status-ownership.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ Project 文件树则需要主目录所属仓库的原始相对路径。互相覆
3333
单仓库时两个投影可直接使用同一个对象;多仓库 Commit 聚合文件并保留
3434
`repositoryPath` 和仓库相对路径,Project 投影保留主目录原始路径。
3535
- `GitStatusRefreshHost` 随主布局运行,监听全部已发现仓库的变更并合并读取。
36-
元数据监听覆盖全部已知仓库;重新安装或卸载时释放每个监听标识。
36+
元数据监听由应用级控制器按存活工作区运行实例维护,覆盖主根、附加根及已知仓库;
37+
切换活动项目不释放。移除根、关闭实例或窗口才撤销对应注册。窗口身份由原生注入,
38+
每个仓库拥有独立注册 ID,规范路径共享底层监听,防止同窗多仓库相互覆盖。
39+
重新安装使用新 ID,迟到完成仍须回收;安装失败等待后续成员变化或焦点恢复重试。
40+
打开仓库子目录时同时监听其祖先规则目录;仓库链接变化重建观察并刷新策略。
3741
顶部与文件树的全局状态刷新延续原先始终更新的行为,Commit 自动刷新设置仍控制
3842
面板额外的分支、stash 等读取,不再使顶部保留旧状态。
3943
- 原始记录及相同投影复用对象,重复的相同快照不会反复重建文件列表。

‎.agents/notes/implemented/architecture/2026-10-28-workspace-indexing-exclusions.md‎

Lines changed: 428 additions & 0 deletions
Large diffs are not rendered by default.

‎.agents/notes/implemented/bug-fix/2026-10-14-child-processes-end-with-the-ide.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Windows 原来有五个漏洞,叠在一起导致“关闭 Lithe 后服务还
101101
- `rust/lithe-git-host/tests/process.rs`:`process_tree_terminate_ends_the_root_and_its_descendants` 启动一个会再启动后代的进程,确认 `terminate` 返回时两者都已结束;Windows 上 `dropping_a_process_tree_ends_it_like_a_crashed_host` 只丢弃句柄,确认内核结束整组。
102102
- `windows/tauri/src-tauri/src/run.rs` 测试(仅 Windows):关闭一个窗口只结束它自己的进程树、丢弃会话等同 Lithe 崩溃、原生列表按启动顺序包含全部会话。
103103
- Windows CI 39919376382 在关闭窗口和丢弃会话两例中,根进程已退出,但立即读取的 `sysinfo` 快照仍包含后代 PID。测试改为停止前打开后代的只等待句柄,停止后分别在两秒内等待根进程和后代的退出信号;另一窗口的后代用零等待确认仍在运行。依据 [Windows 进程终止语义](https://learn.microsoft.com/en-us/windows/win32/procthread/terminating-a-process),退出进程的对象可以因尚未关闭的句柄而继续存在,进程退出信号才是结束依据。保留原来的窗口隔离、重复关闭和崩溃清理断言,不改产品停止流程。夹具的首字节就绪信号有两秒期限,失败路径和断言展开时也按所属会话清理进程树、等待退出并释放管道和句柄;不依赖任意睡眠或 PID 重查。
104+
- Windows CI 41268131032 暴露了另一处夹具身份缺口:Job 已确认清空,但测试从同父 PID 的无序快照中任取第一个进程,无法证明所等句柄属于夹具启动的 `ping.exe`。现在要求父 PID 与进程名同时匹配且候选唯一;没有或有多个匹配时携带候选 PID、名称报错,不跳过断言。`cmd /D` 禁用用户 AutoRun,首字节仍由 ping 提供;确定性回归覆盖快照顺序、名称大小写、缺失及歧义。停止前持有句柄、窗口隔离、崩溃回收与全部等待期限不变;原日志未记录候选身份,不能据此断言误选了哪一个进程。本机不执行 Windows 测试,三个原生生命周期用例及身份选择回归由 Windows CI 验证。
104105
- `rust/lithe-core/src/lsp/interface/process.rs` 测试(仅 Windows):结束批处理启动的语言服务器时,脚本启动的子进程一起结束。
105106
- `shared/fixtures/process-lifecycle/exit-plan-v1.json`、`settle-v1.json`:运行服务在退出、关闭项目、直接停止设置和崩溃恢复下的动作;macOS `ProcessLifecycleFixtureTests` 用 Swift 类型解码同一批 fixture,并覆盖提问顺序、取消和规则失败时仍询问。
106107
- `windows/tauri/src/features/window/services/run-services-exit.test.ts`:询问、取消、停止失败后重试、直接停止、后台任务不询问、Core 不可用时询问。

‎.agents/notes/implemented/feature/2026-10-08-windows-settings-hierarchy.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Windows 的 `settings-navigation.ts` 集中定义可见分层和检索词。2026
2020
| --- | --- |
2121
| 外观与行为 | 通用、更新、编辑器、快捷键 |
2222
| 插件(一级页面,不属于任何分组) | 插件 |
23-
| 版本控制 | Git |
23+
| 版本控制 | Git(含当前项目的 Git 忽略目录索引排除) |
2424
| 构建、执行、部署 | 项目环境、运行配置、Maven |
2525
| 语言与框架 | LSP |
2626
| 工具 | 终端、日志与诊断 |

‎.agents/notes/implemented/feature/2026-10-15-macos-settings-hierarchy.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ macOS 设置侧栏现在只有七个一级入口:外观与行为、插件、
2828

2929
“运行环境”和“环境来源”是构建分组的工具链通用框架页,位于旧项目页和运行配置前;工具名称、提供方及使用方来自贡献快照,不是给某个插件开的专用设置页。提供方自己的配置继续从插件页注入。当前页面只读,旧项目 JDK/Maven 页面保留到实际消费者迁移后再删除,完整进度见[工具链提案](../../proposed/architecture/2026-10-31-plugin-injected-toolchains.md)。
3030

31+
“根据 Git 忽略规则自动排除目录”放在“版本控制 › Git”的程序与凭据区域之后,
32+
不再提供独立的“项目索引”页面。它是当前工作区的 Lithe 偏好,始终沿用原来的
33+
项目存储,不随 Git 页的全局/仓库配置范围或所选子仓库改变;未打开项目时禁用。
34+
搜索“索引”“忽略”“排除”及对应英文词也必须定位到 Git 页面。
35+
3136
AI 分组内按“AI 服务商 → Agent 管理 → 指令 → 技能 → Hooks → MCP 配置 → AI 与提交”排列:先放基础配置,再放行为规则和扩展能力,最后放具体应用功能。顺序仍只在 `SettingsGroup.categories` 中声明,搜索过滤后的侧栏也沿用这一顺序。
3237

3338
### 二级折叠:AI › 资源库

‎.agents/skills/write-stable-tests/references/macos-swift.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ local budget. Because the runner writes to a block-buffered pipe, a finish line
5050
can arrive late or be lost; the harness therefore never kills the runner on a
5151
per-test timer. Instead a stall watchdog (`--stall-timeout-seconds`, default
5252
120) terminates the runner only when it produces no output at all, and reports
53-
the tests still awaiting a result without asserting a single culprit. Reports
53+
the tests still awaiting a result without asserting a single culprit. Any
54+
nonempty stdout or stderr chunk resets that watchdog, including a partial line;
55+
only complete lines are parsed into test results. Reports
5456
are written below `.artifacts/test-stability/`. Each run
5557
produces JSON and raw logs for diagnosis, JUnit XML for CI tooling, and a
5658
self-contained HTML report for module and performance review.

‎.agents/skills/write-stable-tests/scripts/run-swift-tests-with-timing.mjs‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ function parseArguments(arguments_) {
8787
return options;
8888
}
8989

90-
export async function run(options, { runProcessImpl = runProcess } = {}) {
90+
export async function run(options, {
91+
runProcessImpl = runProcess,
92+
setTimeoutImpl = setTimeout,
93+
clearTimeoutImpl = clearTimeout,
94+
} = {}) {
9195
mkdirSync(path.dirname(options.report), { recursive: true });
9296
const logPath = options.report.replace(/\.json$/i, ".log");
9397
const log = createWriteStream(logPath, { flags: "w" });
@@ -170,8 +174,8 @@ export async function run(options, { runProcessImpl = runProcess } = {}) {
170174
}
171175
};
172176
const armStallTimer = () => {
173-
if (stallTimer) clearTimeout(stallTimer);
174-
stallTimer = setTimeout(() => {
177+
if (stallTimer) clearTimeoutImpl(stallTimer);
178+
stallTimer = setTimeoutImpl(() => {
175179
stalled = true;
176180
captureStallSample();
177181
terminateChild();
@@ -217,6 +221,12 @@ export async function run(options, { runProcessImpl = runProcess } = {}) {
217221
childPid = pid ?? null;
218222
terminateChild = terminate;
219223
},
224+
// Swift may split a timing line across writes. Silence is measured from
225+
// actual bytes, while complete lines remain the source of test results.
226+
onOutputChunk: () => {
227+
lastOutputAt = new Date().toISOString();
228+
armStallTimer();
229+
},
220230
onStdoutLine: (line) => recordLine(line, "stdout"),
221231
onStderrLine: (line) => recordLine(line, "stderr"),
222232
streamStdout: true,
@@ -231,7 +241,7 @@ export async function run(options, { runProcessImpl = runProcess } = {}) {
231241
try {
232242
result = await childPromise;
233243
} finally {
234-
if (stallTimer) clearTimeout(stallTimer);
244+
if (stallTimer) clearTimeoutImpl(stallTimer);
235245
stallTimer = null;
236246
await new Promise((resolve) => {
237247
log.once("error", (error) => {

‎.agents/skills/write-stable-tests/scripts/test-timing-lib.mjs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export function runProcess({
153153
timeoutMs,
154154
onStdoutLine = () => {},
155155
onStderrLine = () => {},
156+
onOutputChunk = () => {},
156157
onSpawn = () => {},
157158
streamStdout = false,
158159
streamStderr = false,
@@ -200,11 +201,13 @@ export function runProcess({
200201
}
201202

202203
child.stdout.on("data", (chunk) => {
204+
if (chunk.length > 0) onOutputChunk(chunk, "stdout");
203205
stdoutChunks.push(chunk);
204206
stdoutLines.push(chunk);
205207
if (streamStdout) process.stdout.write(chunk);
206208
});
207209
child.stderr.on("data", (chunk) => {
210+
if (chunk.length > 0) onOutputChunk(chunk, "stderr");
208211
stderrChunks.push(chunk);
209212
stderrLines.push(chunk);
210213
if (streamStderr) process.stderr.write(chunk);

‎.agents/skills/write-stable-tests/scripts/test-verify-test-stability.mjs‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,59 @@ try {
258258
rmSync(swiftTimeoutRoot, { recursive: true, force: true });
259259
}
260260

261+
// A write without a newline is still activity. Advance an injected clock so
262+
// the watchdog is verified independently of machine load or real sleeps.
263+
const swiftPartialOutputRoot = mkdtempSync(path.join(os.tmpdir(), "lithe-test-stability-swift-partial-"));
264+
try {
265+
let now = 0;
266+
let nextTimer = 1;
267+
let terminated = false;
268+
const timers = new Map();
269+
const advance = (milliseconds) => {
270+
now += milliseconds;
271+
for (const [id, timer] of [...timers]) {
272+
if (timer.deadline <= now) { timers.delete(id); timer.callback(); }
273+
}
274+
};
275+
const report = path.join(swiftPartialOutputRoot, "partial.json");
276+
await runSwiftTestsWithTiming({
277+
warnMs: 50, maxMs: 200, stallTimeoutMs: 100, suiteTimeoutMs: 5000,
278+
report, command: "swift", commandArguments: ["test"],
279+
}, {
280+
setTimeoutImpl: (callback, delay) => {
281+
const id = nextTimer++;
282+
timers.set(id, { callback, deadline: now + delay });
283+
return id;
284+
},
285+
clearTimeoutImpl: (id) => timers.delete(id),
286+
runProcessImpl: async ({ onSpawn, onStdoutLine, onOutputChunk }) => {
287+
onSpawn({ terminate: async () => { terminated = true; return true; } });
288+
onStdoutLine("◇ Test partialLine() started.");
289+
for (const stream of ["stdout", "stderr", "stdout"]) {
290+
advance(90);
291+
onOutputChunk(Buffer.from("partial"), stream);
292+
assert.equal(terminated, false);
293+
}
294+
onStdoutLine("✔ Test partialLine() passed after 0.001 seconds.");
295+
return { code: 0, timedOut: false, terminationConfirmed: true, durationMs: now, stdout: "", stderr: "" };
296+
},
297+
});
298+
assert.equal(timers.size, 0, "the injected watchdog must be released after completion");
299+
assert.deepEqual(JSON.parse(readFileSync(report, "utf8")).tests.map(({status}) => status), ["passed"]);
300+
} finally {
301+
rmSync(swiftPartialOutputRoot, { recursive: true, force: true });
302+
}
303+
304+
const partialEvents = [];
305+
const partialProcess = await runProcess({
306+
command: process.execPath, args: ["-e", 'process.stdout.write("partial")'], timeoutMs: 2000,
307+
onOutputChunk: (chunk, stream) => partialEvents.push(["chunk", stream, chunk.toString()]),
308+
onStdoutLine: (line) => partialEvents.push(["line", "stdout", line]),
309+
});
310+
assert.equal(partialProcess.code, 0);
311+
assert.equal(partialProcess.stdout, "partial");
312+
assert.deepEqual(partialEvents, [["chunk", "stdout", "partial"], ["line", "stdout", "partial"]]);
313+
261314
// Regression coverage for the CI misattribution incident: block-buffered pipes
262315
// can swallow a finish line, so a stall must be reported as runner silence with
263316
// the unfinished tests listed, never as "test X exceeded the budget".

‎.github/workflows/ci-macos.yml‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,9 @@ jobs:
340340
if: needs.changes.outputs.git_validation == 'true'
341341
timeout-minutes: 3
342342
run: |
343-
./scripts/macos/test/test-macos.sh \
344-
--skip-build \
343+
./.agents/skills/write-stable-tests/scripts/test-stability-macos.sh \
344+
--report .artifacts/test-stability/macos-git-observation.json \
345+
--suite-timeout-seconds 120 -- --skip-build \
345346
--filter GitStatusObservationTests
346347
347348
- name: Run stable rollback disk image integration tests

0 commit comments

Comments
 (0)