Linux Graph 关闭验证修复:Kun 打包 CI 门禁的超时治理与跨平台加固实战

发布时间:2026/10/12 16:40:15
Linux Graph 关闭验证修复:Kun 打包 CI 门禁的超时治理与跨平台加固实战 人工智能AI Agent自主智能体桌面应用MCP Clients【免费下载链接】KunLocal-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.项目地址https://gitcode.com/gh_mirrors/de/Kun点击查看免费下载导读本文围绕 Kun 开源仓库中的fix-linux-graph-shutdown-validation变更提案完整讲解其如何解决 Linux 打包任务在dist:linux之前因 Graph 调度器关闭恢复测试超时而阻断发布门禁的问题并同步梳理该变更在 Windows 安装器迁移、AppImage 发布门禁、桌面 smoke 数据隔离等方面的配套加固。读者读完可以掌握Kun Graph 调度器关闭语义双快照、活跃 Promise 排空、如何在不改动生产代码的前提下调整测试预算来兼顾「正常 CI 延迟」与「真实死锁检测」以及 Windows 迁移清理如何借助准备日志保持 fail-closed 授权边界。一、问题背景一次 500ms 假设引发的 Linux 发布门禁阻塞在 Kun 的 Linux PR/发布打包流程中npm run test:graph:platform必须先于npm run dist:linux执行见 kun/package.json 中 Graph 平台测试相关的脚本组织。然而原生 Linux 打包任务此前会在打包之前就失败根因不在运行时代码而在测试本身Graph 调度器的关闭恢复测试把「基于文件持久化的清理」假定为一次 500ms 量级的操作。在正常负载的 Ubuntu CI 上调度器其实能正确完成关闭但测试因超时误报从而阻塞了所有 Linux 打包与发布门禁。也就是说这是「测试预算与真实运行环境不匹配」的问题而非调度器存在死锁缺陷。变更提案的核心理念在 proposal.md 中表达得非常克制只改测试与门禁 fixture不触碰 GraphScheduler 生产行为、公开 API、electron-builder 配置和用户可见的发布说明。二、核心修复关闭恢复断言预算统一提升到 5 秒2.1 测试场景本身是什么关闭恢复测试要验证的是 Kun 调度器在「宿主关闭」时的两个关键行为首快照后的迟到准入late admission被中断调度器暂停写租约准入调用GraphScheduler.stop()再释放准入验证一个在首个关闭快照之后才被准入的尝试会被中断、并排除在有效尝试计数之外跨关闭与重启不消耗唯一的工作进程尝试maxAttempts: 1的计划在关闭时中断、重启后重试最终成功且有效尝试计数正确。对应实现位于 graph-scheduler-shutdown-recovery.test.ts其测试常量第 18-20 行如下const schedulerWaitTimeoutMs process.platform win32 ? 30_000 : 10_000 const schedulerStopTimeoutMs 5_000 const schedulerTestTimeoutMs process.platform win32 ? 60_000 : 15_000其中schedulerStopTimeoutMs 5_000正是本次修复的核心所有平台统一给GraphScheduler.stop()5 秒完成预算与 Windows 已有的宽限保持一致同时保留测试整体 15 秒超时Windows 上为 60 秒。2.2 为什么只改测试常量而不改生产代码design.md 明确给出了决策依据调度器在stop()中已经做了完整且正确的关闭编排设置停止围栏stopping fence、清空定时器、对活跃尝试逐一abort、进行第二次活跃尝试快照、中止迟到准入、Promise.allSettled等待活跃 Promise 与 peer review 收尾、停止租约心跳并排空清理任务——见 graph-scheduler.ts文件持久化在负载较高的 Ubuntu runner 上可能超过 500ms这属于正常延迟不是死锁若为「迎合测试」在生产代码中新增检查点会改变已经过审计的关闭行为且没有运行时缺陷证据移除Promise.race的方案被否决未来若调度器真的死锁测试仍应在断言边界上失败不能把死锁检测变成无限制等待。因此决策是「Change only the test constant」——保持 15 秒整体超时与完整的持久化状态断言interrupted状态、GRAPH_HOST_SHUTDOWN_ATTEMPT_FAILURE归一化失败、effectiveRunAttemptCount为 0只把 stop 断言预算从 500ms 提升到 5 秒。2.3 测试中的 no-deadlock 守卫如何工作看「catches an attempt admitted after the first shutdown snapshot」用例graph-scheduler-shutdown-recovery.test.tsconst stopping harness.scheduler.stop() releaseAdmission() await expect(Promise.race([ stopping, new Promisetimed_out((resolve) setTimeout(() resolve(timed_out), schedulerStopTimeoutMs)) ])).resolves.not.toBe(timed_out)测试先用vi.spyOn(harness.writes, acquire)挂起写租约准入触发首个关闭快照后再释放准入令「快照后的迟到准入」实际发生随后用Promise.race同时等待scheduler.stop()与 5 秒计时器——若 5 秒内 stop 未完成竞速解析为timed_out即视为疑似死锁失败。关闭完成后测试断言该尝试被持久化为expect(parked.nodes[source.id]).toMatchObject({ status: ready, attempts: [expect.objectContaining({ status: interrupted, normalizedFailure: GRAPH_HOST_SHUTDOWN_ATTEMPT_FAILURE })] }) expect(effectiveRunAttemptCount(parked)).toBe(0)即迟到尝试以「宿主关闭失败」持久化为interrupted有效尝试计数为 0——这意味着重启调度器后唯一的一次maxAttempts配额不会被关闭所消耗第一个用例验证了重启后执行两次、有效计数为 1 并完成运行。这组持久化语义来自 graph-scheduler-policy.ts 中的GRAPH_HOST_SHUTDOWN_ATTEMPT_FAILURE与effectiveRunAttemptCount。三、配套的测试预算调整监督活跃性、根 Vitest 与 Windows 保留往返3.1 监督活跃性 fixture多检查点场景让位给 60 秒 Vitest 预算Graph 监督活跃性测试graph-scheduler-supervision-liveness.test.ts驱动四个彼此独立且有界的调度器检查点两次「纯散文式」Lead 剧集重投递、评审完成运行、隔离的合成失败 fixture、独立就绪分支并发。原先的外层 20 秒超时在繁忙的 Windows runner 上可能先行耗尽——即使没有任何检查点表现出死锁。修复后supervisionLivenessTimeoutMs 60_000第 19 行即该 fixture 使用仓库既有的 60 秒 Vitest 上限每个 10 秒的调度器状态等待waitFor与全部活跃性断言保持不变。同时在异常路径上测试会输出leadEpisodes、各节点状态、obligation 的noProgressCount、lease 状态、最近事件序列等诊断信息第 155-176 行便于在慢 runner 上区分「等待未满足」与「真实死锁」。3.2 根 VitestWindows 上 15 秒/测试、保留双 worker 并发根级 Vitest 配置在 Windows 平台将每个测试的预算提升到 15 秒同时保留既有的双 worker 并发。原因是原生文件系统迁移操作与同步的 Windows PowerShell helper 在 runner 负载下可能超过跨平台的 5 秒默认值迁移断言、进程退出检查和 fail-closed 行为全部不变。3.3 Windows 保留往返测试三个原生 PowerShell 进程的 15 秒预算聚焦的迁移测试会同步启动 PowerShell 依次执行Prepare、FallbackCleanup、Restore三个原生辅助进程。原生 runner 启动可能把「正确但较慢」的往返推过 Vitest 默认 5 秒该测试单独获得 15 秒上限所有行为断言准备、清理、恢复、日志保持完整。这个 fail-closed 原则很重要放宽的是时间预算不是校验强度。四、AppImage 发布门禁显式传入解压路径4.1 问题的本质resolve(undefined)的隐式路径解析桌面 smoke 启动器现在会以「已校验的AppRunAPPDIR/APPIMAGE环境变量」的方式启动经过解压验证的 AppImage。发布门禁 fixture 此前没有显式提供解压后的 AppDir 与AppRun路径导致校验的是resolve(undefined)这种隐式解析结果而非与打包 smoke 完全一致的调用形态。4.2 fixture 中的显式路径契约在 check-extension-release-gate-packaging.mjs 中发布门禁测试通过createAppImageSmokeInvocation构造最终调用并断言其形状const invocation packagedAppImageSmokeModule.createAppImageSmokeInvocation({ appImage: /release/Kun-1.2.3-linux-x86_64.AppImage, appRoot: /extract/squashfs-root, appRun: /extract/squashfs-root/AppRun, resourcesDir: /extract/squashfs-root/resources, desktopSmokePath: /repo/scripts/smoke-packaged-extension-desktop.cjs, environment: { APPDIR: /untrusted, APPIMAGE: /untrusted, ELECTRON_RUN_AS_NODE: 1 } })随后断言命令以process.execPath启动、APPIMAGE_EXTRACT_AND_RUN与ELECTRON_RUN_AS_NODE均被清空、APPDIR解析为解压根、APPIMAGE解析为原 AppImage 文件、--desktop-executable与AppRun/resources路径出现在参数中、不携带app.asar——即「启动已校验的 AppRun同时不做 AppImage 自解压」的调用形态。配套的 smoke 实现见 smoke-packaged-extension-appimage.cjs其中inspectExtractedAppImageBundle校验解压根、AppRun常规文件与可执行位。4.3 配套断言可执行文件格式与启动参数同一门禁还校验 Linux launcher 内容包含ExecAppRun --disable-setuid-sandbox --no-first-run %Ucheck-extension-release-gate-packaging.mjs并与 electron-builder 配置中的executableArgs: [--disable-setuid-sandbox, --no-first-run]对应——这与 after-pack-launchers.cjs 中「AppImage 可能通过 PATH 调用 AppRun」的注释所描述的问题一脉相承。五、Windows 迁移辅助程序的 fail-closed 加固5.1 准备日志成为回退清理的授权记录Windows 迁移 helperwindows-installer-migration.ps1的Prepare动作在旧卸载器运行之前就已经完成源身份、安全根目录、reparse point 边界与已识别 payload 的校验helper 通过switch ($Action)分发Prepare/FallbackCleanup/Restore等动作见第 198-207 行。修复的核心变化Prepare现在记录每一个通过校验的安装源——包括没有任何未知内容需要保留的源。原因在于旧卸载器会在回退清理之前合法地删除Kun.exe身份可执行文件导致「凭身份可执行文件验证源」这条路在清理时已经失效。若没有匹配的准备记录回退清理仍然保持 fail-closed除非源仍等于安装目标且保留有效身份可执行文件否则拒绝清理有记录时只删除已识别的应用 payload。对应的需求场景写入 linux-graph-ci-validation/spec.mdWHEN准备校验了已注册源、且旧卸载器随后移除其身份可执行文件THEN回退清理通过匹配的准备记录接受该确切源且只删除已识别的 payload未准备的源若与安装目标不同或已无身份可执行文件则被拒绝。5.2 NSIS 本地恢复引号包裹的注册源electron-builder 会在启动旧卸载器之前解析其引号包裹的UninstallString但其installUtil.nsh的宏在customHeader之后才加载。Kun 因此在customHeader内自持等价的引号路径解析与父目录函数见 packaging-config.hooks.test.ts 中对ReadRegStr $R9 HKEY_CURRENT_USER ${UNINSTALL_REGISTRY_KEY} UninstallString的断言从而避免了对 electron-builder 后续宏的依赖避免了「跨进程ResolveSource 环境变量/UTF-16 结果文件」往返返回空源的失败路径。PowerShell 侧仍然对最终源执行规范化并强制全部目标、身份、payload、日志与 reparse point 检查——NSIS 只负责「在customHeader阶段自包含地恢复出非空源」校验职责不转移。5.3 仅当 smoke 主动选择时才暴露安装器诊断辅助程序会把动作边界与被捕获的错误追加到KUN_INSTALLER_DIAGNOSTIC_PATH指定的路径见 windows-installer-migration.ps1 的Write-InstallerDiagnostic以及 installer.nsh 中对该环境变量的读取与设置。Windows smoke 在自己的可清理根目录内设置该路径仅在安装器出现非预期退出码时打印日志诊断写入失败被忽略——普通安装器运行不设置该环境变量就不会产生这份日志生产行为完全不变。5.4 作用域迁移退休旧的 HKCU 注册与快捷方式当 all-users 安装成功退休经过校验的 current-user 安装时electron-builder 只会在此时调用customUnInstallCheckCurrentUser回调packaging-config.hooks.test.ts 断言脚本包含该回调。Kun 的回调在支持校验式回退清理的基础上还会读取注册的快捷方式元数据移除旧用户的已注册、规范化 Kun 与遗留 DeepSeek GUI 桌面/开始菜单快捷方式在清理成功后精确删除HKEY_CURRENT_USER下的 KunINSTALL_REGISTRY_KEY与UNINSTALL_REGISTRY_KEY测试断言DeleteRegKey HKEY_CURRENT_USER ${UNINSTALL_REGISTRY_KEY}与DeleteRegKey HKEY_CURRENT_USER ${INSTALL_REGISTRY_KEY}见第 379-382 行不删除任何动态或父级注册表路径shell 上下文在安装继续前恢复为 all-users测试同时断言DeleteRegKey SHELL_CONTEXT ...仅作用于固定键第 451-452 行。清理只发生在 fail-closed payload 清理成功之后空记录路径无未知内容也在覆盖范围内。六、桌面 smoke 的 appData 显式隔离6.1 从 Known Folder 发现依赖中解脱桌面 Chromium smoke 原本就会创建并填充隔离的 app-data 目录。修复后当专用环境变量存在时主进程 bootstrap 会在第一次app.getPath(appData)之前调用app.setPath(appData, ...)把 Electron 的appData特殊路径绑定到该目录。实现位于 app-identity.tsexport function configureDesktopSmokeAppDataPath( env: NodeJS.ProcessEnv process.env ): string | undefined { if (env.KUN_PACKAGED_EXTENSION_DESKTOP_SMOKE ! 1) return undefined const appDataPath env.APPDATA?.trim() if (!appDataPath) { throw new Error(The isolated desktop smoke requires an APPDATA path) } app.setPath(appData, appDataPath) return appDataPath }判定函数isPackagedExtensionDesktopSmoke()见 main-lifecycle.tsprocess.env.KUN_PACKAGED_EXTENSION_DESKTOP_SMOKE 1。6.2 三个边界条件有隔离目录KUN_PACKAGED_EXTENSION_DESKTOP_SMOKE1且APPDATA非空绑定后 smoke 不再依赖 Windows Known Folder 发现——这在安装器变更测试之后尤其重要因为系统级目录状态可能已不可靠无APPDATA隔离 smoke 直接 fail closed 抛出错误普通启动不设置专用环境变量绝不覆盖 Electron 的appData路径打包与日常启动行为零影响。七、验证路径本地串行复跑与原生 CI 门禁按 tasks.md 的验证计划本地验证关闭恢复测试串行跑 10 次全部通过npm run test:graph:platform下 Kun 与 renderer 的 Graph 套件通过随后跑npm run build:kun、npm run typecheck、npm run lint、npm run test、npm run build与git diff --check并重跑聚焦的打包配置与发布门禁检查原生 CI 验证当前开发宿主为 macOS arm64 且 Docker daemon 不可用因此 Linux 原生验证由 PR CI 承担——Ubuntu runner 运行的是与打包完全一致的 Node 22 依赖链及全部下游 Linux smoke/evidence 门禁提交分支codex/fix-linux-graph-shutdown-timeout面向develop开 PR绿色后才合并进发布 PR发布合并进develop、等待发布 PR 刷新并通过全部门禁后进入master验证自动化的v0.2.34版本与跨平台资产及 R2 stable/latest 提升若计算版本不一致则停止。design.md 还记录了三个风险与缓解风险缓解真实关闭死锁现在最长 5 秒才在 race 处失败而非 500ms既有 15 秒整体超时 race 之后完整的持久化状态断言兜底Mac 本地测试无法证明 Linux 原生 AppImage 行为合并与发布前强制通过原生 Linux PR 任务准备记录可能授权错误的 Windows 目录记录只在安全根、应用身份、已识别 payload、reparse point 检查全部通过后写入八、结论不改运行时用「预算治理」疏通发布流水线fix-linux-graph-shutdown-validation是一个教科书式的「测试基建修复」它没有声称调度器有缺陷而是精确诊断出「基于文件持久化的关闭清理在负载 CI 上超过 500ms」这一环境事实然后通过三组互不干扰的改动疏通流水线时间预算的合理放宽——关闭断言 5 秒、监督活跃性 60 秒、根 Vitest 与保留往返 Windows 15 秒全部保留既有断言与超时兜底门禁 fixture 的显式契约——AppImage smoke 显式传入解压appRoot/appRun与打包启动器调用形态对齐Windows 迁移的 fail-closed 增强——准备日志授权回退清理、NSIS 本地恢复引号注册源、可选诊断、HKCU 注册与快捷方式的精确退休以及桌面 smoke 的appData显式隔离。最终的验收标准是独立能力linux-graph-ci-validationspec.md所表述的原生 Linux Graph 测试必须容忍持久化关闭延迟同时仍能检测真实的关闭死锁从而放行下游 Linux 打包与 smoke 门禁。对于同样在「测试超时阻塞发布」泥潭中的项目这套「先定位假设错误、再只改测试边界、最后用原生 CI 兜底」的方法论比盲目加大超时或粗暴跳过测试都更值得借鉴。赞分享人工智能AI Agent自主智能体桌面应用MCP Clients【免费下载链接】KunLocal-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.项目地址https://gitcode.com/gh_mirrors/de/Kun点击查看免费下载相关推荐Kun 的 Linux Graph 关闭验证与跨平台发布门禁加固从超时修复到 CI 套件门禁的完整方案Kun 的 Linux Graph 关闭验证与跨平台发布门禁加固从超时修复到 CI 套件门禁的完整方案 本篇技术指南以 Kun 仓库 OpenSpec 变更记人工智能AI Agent自主智能体桌面应用MCP ClientsKun 项目 Linux Graph 关停验证修复CI 门禁超时预算与跨平台回归治理实践Kun 项目 Linux Graph 关停验证修复CI 门禁超时预算与跨平台回归治理实践 本篇文章围绕 Kun 仓库中 OpenSpec 变更 fix lin人工智能AI Agent自主智能体桌面应用MCP ClientsKun 平台关机校验修复实录Linux Graph 测试预算、AppImage 门禁与 Windows 迁移器加固Kun 平台关机校验修复实录Linux Graph 测试预算、AppImage 门禁与 Windows 迁移器加固 Kun 是一个本地优先的 AI Agent人工智能AI Agent自主智能体桌面应用MCP Clients创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询