Skip to content

[Bug] 0.10.0 稳定版出厂包移除了 healProfilesModuleFallback,profiles\node_modules 遗留 214 个失效链接、47 个包不可用 — 0.10.0 shipped without the profile fallback healer: 214 dead links, 47 packages unavailable #577

Description

@drakunov

DSH Desktop 0.10.0:healProfilesModuleFallback 从发行包中被移除,升级后 profiles\node_modules 链接树全部失效

本文是本 issue 的中文摘要,面向中文渠道发布与厂商反馈。完整证据、哈希与复现脚本见下方的英文报告。所有数字均为 2026-09-27 实测结果,未重算。

1. 结论

0.10.0 的发行包中,负责在已打包(app.asar)环境下重建 profiles\node_modules 链接树的机制 healProfilesModuleFallback 被整体移除:在同一个模块 @deepseek-ai\dsh-app-boot\lib\index.js 里,healProfilesModuleFallback / healProfilesModuleFallbackLocked / ensureSymlink / ensureModuleProxy / resolveModuleFallbackEntries / moduleFallbackCurrent 的出现次数全部为 0(0.9.2 中分别是 4 / 2 / 4 / 2 / 2 / 3)。

该机制所需的安装锚点在 0.10.0 中仍然存在——…\resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh\package.json(7 640 字节);缺的不是锚点,而是调用它的代码本身。因此,从 0.9.x 升级上来的用户,profiles\node_modules 中由 0.9.x 建立的链接会一次性全部变成断链,而日志里既没有修复记录,也没有报错。

另外两个打包现象——resources\app.asar.unpacked\package.json 不存在、根级 package.json 未标记解包(asarUnpack 0 次;结构化遍历头部得到 21 214 条文件记录,其中 21 210 条标记为 unpacked,真正把数据放在档案内部的只有 4 个:out/main/index.js(990 966 字节,sha256 7BF2632747D5838E9C02CDAB1ECE3738C520815070E4548214346FCC1543A4D6)、out/preload/index.cjs(62 315 字节,sha256 BEB2C4565CF4D74530F9F33FC5DB727349F4844DB72452D5194C1FD9081E34C1)、out/preload/windows-menu.cjs(12 726 字节,sha256 2F1D2A3C5D77C29833B2A4BC5A02C3218B92C0351702B6698ADFE64F991D7791)、package.json(13 421 字节,offset 1 066 007,sha256 91E4BE17839A055481E74B15ED4B5FB5B5EC64D36976A2412C2D3D3429AD9439))——是独立问题,不是本次故障的原因:0.9.2 的机制用不到这个文件。档案里的这份根级 package.json 是外壳(shell)清单(name: dsh-desktop、version: 0.10.0、main: ./out/main/index.js、242 个依赖),Electron 直接从档案内部读取它。

2. 现象

  • 我们的工作机:profiles\node_modules 有 243 条顶层记录,其中 215 个 junction,214 个断链(指向已不存在的 resources\app\node_modules\…),存活的只有 1 个——destroy → %APPDATA%\npm\node_modules\@deepseek-ai\dsh\node_modules\destroy,即全局安装的 CLI,而不是应用发行包。
  • 同层的 @deepseek-ai 有 242 条记录,其中能读到 package.json 的是 0 条;而解包发行包中 @deepseek-ai 有 284 个包。链接树里缺失 47 个包,另有 5 个链接指向 0.1.7 中已删除的包(dsh-agent-presets、dsh-code-runtime、dsh-code-runtime-worker-thread、dsh-settings-file、dsh-workflow-worker-thread)。
  • 顶层 215 个链接中,152 个的目标名在解包树中存在;63 个不存在,而这 63 个里只有 qs、zod-to-json-schema 能在 app.asar 头部找到,其余 61 个(accepts、ajv-formats、body-parser、cors、depd …)在档案和磁盘上都不存在——它们是 0.9.2 时代的遗留依赖。
  • harness.log 中 heal、healProfilesModuleFallback 出现次数为 0。应用仍能启动:@deepseek-ai/* 由 resources\host-module-fallback.mjs 兜底(该钩子只处理 @deepseek-ai/ 前缀,且只对 ERR_MODULE_NOT_FOUND / ERR_PACKAGE_PATH_NOT_EXPORTED 重试)。所以故障是静默的,只有走 profiles 解析路径的第三方包(yaml、zod、react)会解析失败——我们的构建门禁因此直接失败:npm run verify:plugins → exit 1,Error: Cannot find module '…\profiles\node_modules\yaml';npm run validate:client → exit 1,11 项中 1 项失败。
  • 每次启动 0.10.0 都会出现一对 profile inconsistency: the patch layer inserts @deepseek-ai/dsh-agent-preset, which is not installed;2026-09-27 10:15 的快照里已有六对(L891–892、L1019–1020、L1097–1098、L1174–1175、L1238–1239、L1324–1325)。

3. 测量数据

项目 0.9.2 0.10.0
resources\app 目录 存在 不存在
resources\app.asar 不存在 6 550 424 字节,sha256 2B67920B29C5BEC23CC23CC3F4B1F06875D5EF850A594173FD7B3E16AED50EEF
resources\app.asar.unpacked\package.json 存在 不存在
asar 头部 — asarUnpack 0 次;结构化遍历:21 214 条文件记录,其中 21 210 条标记 unpacked,档案内部只有 4 个文件;根级 package.json 无 unpacked 标记(size=13421 offset=1066007)
dsh-app-boot\lib\index.js 72 291 字节,sha256 4A869354830A10FCC625C462BE5D44EB6EBB0487AA5D550DA58F4A4CE3016D34 183 878 字节,sha256 3E26ABB976031117CDF08B365D2185FF12368524C5B92EE5945DA81750D4BEDD
六项机制计数(上表函数名) 4 / 2 / 4 / 2 / 2 / 3 0 / 0 / 0 / 0 / 0 / 0
安装锚点 …\app.asar.unpacked\node_modules\@deepseek-ai\dsh\package.json 存在(另一种路径) 存在(7 640 字节)
profiles\node_modules(我们的工作机) — 243 条记录 / 215 个 junction / 214 断链
@deepseek-ai 记录数(链接树 / 解包树) — 242 / 284(缺失 47,多余 5)
harness.log 中 heal 相关行 0 0

4. 干净机器上的受控复现(0.9.2 → 0.10.0 覆盖升级)

  • 环境:Windows 11 Home(build 26200),x64,per-user 安装;先安装 0.9.2 并运行 3 次,再用同一个安装包覆盖升级到 0.10.0 并运行 2 次,全程不做任何手工修复或重新链接。
  • 结果:profiles\node_modules 241 条记录、213 个 junction,断链 0 → 213(100%);accepts 的目标 …\resources\app\node_modules\accepts 由「存在」变为「不存在」;profiles\web\.dsh-module-fallback 被删除(0.9.2 中存在);resources\app.asar.unpacked\package.json 在两个阶段都不存在,根级 package.json 均未标记解包。
  • 升级前后两版 harness.log 中都没有任何「修复 / 重建链接」的行;0.10.0 在完全断链的树上照常启动(Harness is ready,ERR_MODULE_NOT_FOUND 0)。可见断链不是长期累积的垃圾,而是 resources\app 被 app.asar 取代的直接后果。

5. 请求与问题(要点;完整八条见英文报告)

  1. 主问题:这个机制是有意移除的吗? 若是方案 (1)「保留」——请把 healProfilesModuleFallback 加回发行包;若是方案 (2)「通用 fallback 已被 profiles\.generations 世代投影取代」——请为遗留的链接树提供正式迁移(或明确说明它已不再需要),并修正 Safe Mode 帮助文本中「the next launch rebuilds it」的承诺。0.10.0 的提交 3deeef37419ecf26db700a3b99d7c9827faec392(refactor: 合入 Harness 0.1.7-rc.2 底座与兼容修复 #570)看起来更像方案 (2)。
  2. 自愈必须恢复「集合」,而不只是重定向目标。 在 @deepseek-ai 区域期望的终态是 284 个链接、0 个断链(解包树里恰好 284 个包):补上缺失的 47 个,删除指向 0.1.7 已移除包的 5 个链接(它们的目标在 resources\app\node_modules\…,而该目录在 0.10.0 已不存在,无法重新指向)。
  3. 让「没有重建」变得可见: 至少写一行日志;按上游笔记的约定,应在签名之前让打包失败。
  4. 关于打包(不是本次故障的原因,但问题仍然存在):档案里的根级 package.json 未标记解包——asarUnpack 0 次,放在档案内部的只有 4 个文件,磁盘上只解包了 node_modules 分支。消费者我们已自行核查: 发行包代码里没有出现 app.asar.unpacked 字面量(out/main/index.js 里只有模板 runtimePackageRoot() → `${appPath}.unpacked`,其唯一消费者是 bundledRuntimeRoot()),asarUnpack 0 处、「deployment manifest」0 处、resources\package.json 0 处;在共查看的 11 416 个同扩展名文件中(另有 6 个超过 4 MB 的文件已跳过),app.asar.unpacked 仅出现一次——node_modules/node-pty/lib/unixTerminal.js(替换 spawn-helper 路径);asar.unpacked 共出现在 3 个文件、4 处(node-pty/lib/unixTerminal.js ×2、node-pty/lib/windowsConoutConnection.js、dsh-tool-fs-search/lib/index.js),全部都是替换为解包目录路径,与清单无关。外壳清单由 Electron 从档案内部读取(app.getAppPath()),代码中的「安装锚点」由 dsh\lib\bin.js 算到 …\node_modules\@deepseek-ai\dsh\package.json,它存在。因此不必再请厂商确认该文件「不必要」——我们要问的是另一件事:为什么上游笔记中的打包门禁(「Packaging fails before signing if either the root anchor or a required runtime entry is absent」,状态 implemented)没有拦住 0.10.0 的构建,或者该表述里的「root anchor」具体指什么;同时请确认 out\main\index.js 位于档案内部是正常的。
  5. profile inconsistency 的行是断链树的后果,还是补丁层单独的问题?若是前者,它同时也是最便宜的验收标准(修复后这些行应当消失)。
  6. 指向应用发行包之外的链接(例如 destroy)的策略是什么——覆盖,还是不动?
  7. 现在由什么在新配置文件的启动阶段链接 dsh-desktop-hmr-fallback?(该包仍在发行包中并标记为解包,但负责链接它的机制已不在。)
  8. 顶层没有目标的 63 个链接(152 个有目标、63 个没有;其中只有 2 个能在档案头部找到)应当如何处置——如果它们也属于 fallback 的职责范围,那么「0 断链」在构造上就不可能达成。

上游笔记(状态 implemented):anywhere-labs/dsh-desktop@master:.agents/notes/implemented/architecture/2026-08-15-packaged-profile-fallback.md 原文写道:「The desktop Host maps its installation anchor from app.asar/package.json to app.asar.unpacked/package.json before calling healProfilesModuleFallback;」以及「Packaging fails before signing if either the root anchor or a required runtime entry is absent」。注意:该笔记在 dataelement/dsh-desktop@main 中不存在(HTTP 404),它只存在于相邻项目。

6. 影响范围

  • 命中范围:所有「从 0.9.x(或更早)升级到 0.10.0」的安装。仅全新安装 0.10.0 的干净机器未做测试(0.9.2 会在启动时静默建立这些链接,而 0.10.0 的升级会让它们全部失效)。
  • 规模(截至 2026-09-27,GitHub REST API,仅统计 Windows 安装包下载量):0.9.2 — 2741 次;0.10.0 — 691 次。
  • 风险面:从市场安装插件,以及任何带有非 host 依赖(zod、yaml、react)的 profile 插件。

7. 我们的环境

DSH Desktop 0.10.0(harness 0.1.7-rc.2);Windows 10 Enterprise LTSC(build 19044)与 Windows 11 Home(build 26200);复现为 per-user 安装。原始测量输出留在我们本地,未公开;可按需提供去标识化副本。


Issue: version 0.10.0 removed healProfilesModuleFallback from the shipped build — the profiles\node_modules tree is left broken (214 links, 47 packages unavailable)

Date: 26.09.2026. Measurements and text updated on 27.09.2026.
Author: a DSH Desktop user (the project runs on this profile).
Build: DSH Desktop 0.10.0, core @deepseek-ai/dsh 0.1.7-rc.2, Windows x64. For comparison, the shipped DSH Desktop 0.9.2 was also unpacked and examined (same publisher; installer dsh-desktop-windows-x64-setup.exe, 170 753 576 b).
The publisher of the build is dataelement/dsh-desktop: the root package.json inside resources\app.asar yields name: dsh-desktop, version: 0.10.0, repository: git+https://github.com/dataelement/dsh-desktop.git, author: DataElement, license: MIT; this does not contradict resources\app-update.yml (provider: generic, https://dshdesktop.com/updates/latest/) or CompanyName: DataElement from VersionInfo — a generic feed by itself does not prove the publisher. The note below comes from the upstream anywhere-labs/dsh-desktop; resources\host-module-fallback.mjs arrived in the build from there as well. The request is addressed to dataelement/dsh-desktop.
Related to the upstream note "Physical packaged profile fallback" (2026-08-15, status implemented) — https://github.com/anywhere-labs/dsh-desktop/blob/master/.agents/notes/implemented/architecture/2026-08-15-packaged-profile-fallback.md

1. Essence

In the 0.9.2 build the healProfilesModuleFallback mechanism was present (module @deepseek-ai/dsh-app-boot) and was called by the desktop main process before upgrading the market mount. In the 0.10.0 build it is gone: the same module no longer contains the function itself or its dependencies (ensureSymlink, ensureModuleProxy, resolveModuleFallbackEntries, moduleFallbackCurrent). Because of this, the $DSH_HOME\profiles\node_modules tree created under 0.9.2 is rebuilt by no one: 214 links are broken (they point into the vanished resources\app\node_modules), 47 packages of the unpacked build are missing from the tree, and the Safe Mode help text in the same 0.10.0 still promises "the next launch rebuilds it".

The failure is silent: harness.log has not a single line about rebuilding the tree. The only thing the user sees is the lines profile inconsistency: the patch layer inserts … which is not installed: they are printed by a different mechanism (reportProfileConsistency → healProfileBundles), and it works with the profile's bundle layer, not with the profiles\node_modules links.

Separately, and not as a cause: after the switch to the app.asar layout, the application's root package.json is not marked as unpacked (question 4).

2. Evidence

Layout (verified by parsing the archive header):

  • resources\app.asar — 6 550 424 b; root entries are exactly node_modules, out, package.json.
  • Unpacking in asar is marked per-entry, not by a list: the archive header has 0 occurrences of asarUnpack. A structured walk of the header gives 21 214 file entries, of which 21 210 are marked unpacked (that is, they physically live in app.asar.unpacked), while exactly four files carry their data inside the archive — out/main/index.js (990 966 b, sha256 7BF2632747D5838E9C02CDAB1ECE3738C520815070E4548214346FCC1543A4D6), out/preload/index.cjs (62 315 b, sha256 BEB2C4565CF4D74530F9F33FC5DB727349F4844DB72452D5194C1FD9081E34C1), out/preload/windows-menu.cjs (12 726 b, sha256 2F1D2A3C5D77C29833B2A4BC5A02C3218B92C0351702B6698ADFE64F991D7791) and package.json (13 421 b, offset: 1 066 007, sha256 91E4BE17839A055481E74B15ED4B5FB5B5EC64D36976A2412C2D3D3429AD9439). That root package.json has no such marker (only integrity), which is why resources\app.asar.unpacked\package.json does not exist on disk; the only unpacked branch is node_modules (259 entries at the root). The root manifest itself is the shell manifest (name: dsh-desktop, version: 0.10.0, main: ./out/main/index.js, 242 dependencies), and Electron reads it from inside the archive.
  • out\main\index.js is present in the archive (990 966 b) and is likewise not marked unpacked — this is not a defect: it is read from the archive. Whether the root package.json is marked or not did not affect rebuilding the tree in 0.9.2: installAnchor is computed from the path to dsh\lib\bin.js and points to …\app.asar.unpacked\node_modules\@deepseek-ai\dsh\package.json, which does exist in 0.10.0 (key measurement below). On the second machine, where 0.10.0 was obtained by a controlled upgrade from 0.9.2, the packaging observation reproduced: resources\app.asar.unpacked\package.json is absent there as well, and the root package.json in the archive is without the unpacked marker (§7).

The shared profile tree %APPDATA%\dsh-desktop\harness\profiles\node_modules:

  • 243 top-level entries in total, of which 215 links (junction), 214 broken (no package.json inside); the remaining 28 are real scope directories (@babel, @aws, …) containing the same kind of links. In total 576 entries were checked in the tree — 548 links (reparse points) and 28 scope directories; of the links, one is live.
  • The only live link is destroy → %APPDATA%\npm\node_modules\@deepseek-ai\dsh\node_modules\destroy, that is, into the tree of the globally installed dsh CLI, and not into the application's shipped build (question 6).
  • in @deepseek-ai — 242 entries and 0 with a package.json;
  • the link target is the old path, for example %LOCALAPPDATA%\Programs\DSH Desktop\resources\app\node_modules\accepts (the resources\app directory no longer exists after 0.10.0);
  • the links were created on 26.09.2026 12:06:13 under 0.9.2 and were not touched by the update;
  • the tree's composition is outdated not only in its targets but also in its package set. In the unpacked tree @deepseek-ai holds 284 packages (all have a package.json; the same number is in the archive), while the profile tree holds 242 links, and all of them are broken. The sets are not nested: 237 names match, 47 unpacked packages are missing from the profile tree, and 5 links point to packages that do not exist in 0.1.7 at all — dsh-agent-presets, dsh-code-runtime, dsh-code-runtime-worker-thread, dsh-settings-file, dsh-workflow-worker-thread (exactly the set removed in 0.1.7; all five targets are the old …\resources\app\node_modules\@deepseek-ai\…). Hence 284 − 242 = 42, not 47. The 47 missing from the tree: dsh-agent-preset, dsh-agent-preset-registry, dsh-api-account-controller, dsh-api-job-controller, dsh-api-terminal-controller, dsh-client-shortcuts, dsh-client-store, dsh-client-ui-plugin-manager, dsh-client-ui-settings-account, dsh-client-ui-settings-agent-loop, dsh-client-ui-settings-shell, dsh-client-ui-settings-subagent, dsh-client-ui-settings-web-search, dsh-client-ui-shortcuts, dsh-client-ui-sidebar-browser, dsh-client-ui-sidebar-terminal, dsh-compaction-image-offload, dsh-config-editor, dsh-deepseek-account, dsh-deepseek-account-platform, dsh-experimental-agent-team, dsh-experimental-agent-team-profile, dsh-experimental-api-speech-to-text, dsh-experimental-auto-review, dsh-experimental-client-ui-agent-team, dsh-experimental-client-ui-voice-input, dsh-experimental-speech-to-text, dsh-experimental-speech-to-text-sensevoice, dsh-experimental-tool-agent-team, dsh-experimental-voice-input-bundle, dsh-hmr, dsh-lazy-require, dsh-llm-deepseek-account, dsh-llm-deepseek-api-key, dsh-mcp-resources, dsh-office-to-pdf, dsh-plugin-manager, dsh-ptc-runtime, dsh-ptc-runtime-node, dsh-session-format-v3-to-v4, dsh-skill-office, dsh-tool-workspace-dependencies, dsh-util-code-language, dsh-workflow-ptc, dsh-workspace-changes, libreoffice-kit, libreoffice-kit-win32-x64. So heal must be able not only to retarget the old links but also to add the 47 missing ones, and to delete the 5 "extra" ones (they cannot be retargeted: their targets do not exist in 0.1.7, otherwise we get never started again). See requests 2 and 6.
  • in harness.log — 0 occurrences of heal and healProfilesModuleFallback across the whole file (27.09.2026 10:15 snapshot: 1354 lines, 179 759 b, 20 launches — launch requested = Harness is ready = 20, of which seven are 0.10.0 launches: 26.09 16:00:35Z, 16:56:26Z, 17:32:37Z and 27.09 01:38:33Z, 01:46:33Z, 02:13:36Z, 05:14:51Z; profile inconsistency — 15 occurrences: six pairs from the next bullet plus L51 (node_modules was linked from …) and L369–370 (before the update)). Zero occurrences of the name proves nothing by itself (in 0.9.2 the call logged the result, not the name), but it confirms the main point: the tree is not rebuilt — after a rebuild the links would have been retargeted. All 12 occurrences of fallback are unrelated to repairing the tree: 4 lines host fallback from … (2 each in two 0.10.0 launches — before our preset fix from §6) and 8 frames at resolve (…host-module-fallback.mjs…).
  • The lines profile inconsistency: the patch layer inserts @deepseek-ai/dsh-agent-preset, which is not installed appear one pair per every 0.10.0 launch (except the first one: in the 16:00:35.504Z launch they are absent — the patch blocks had not been written yet). Known pairs: L891–892, 26.09 16:56:26.838Z; L1019–1020, 26.09 17:32:37.364Z; L1097–1098, 27.09 01:38:33.933Z; L1174–1175, 27.09 01:46:33.701Z; L1238–1239, 27.09 02:13:36.458Z; L1324–1325, 27.09 05:14:51.707Z (the seventh launch). That is, the defect reproduces on every start; the list above is a snapshot at 27.09.2026 10:15, it grows with every new launch, and what holds is the rule, not the number. Do not confuse this with L369–370, 09:15:08.938Z — there the text is different (dsh-tg-bridge, dsh-status-strip) and it is before the update. The package @deepseek-ai/dsh-agent-preset (singular) physically exists in …\app.asar.unpacked\node_modules\@deepseek-ai\ — it is on the list of those very 47 — but it is not linked into the profile tree, which is why the "is installed" check says "no". We consider this a consequence of the same defect; the verifiable indicator is question 5.

Key measurement: the mechanism was in 0.9.2 — and was removed in 0.10.0

Method: the 0.9.2 build was unpacked from the installer (7-Zip, NSIS payload $PLUGINSDIR\app-64.7z, 170 144 088 b) into a separate directory; both modules were read from disk — for 0.9.2 from the unpacked build directory (in 0.9.2 the build is unpacked, there is no app.asar), for 0.10.0 from the unpacked tree resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-app-boot\lib\index.js (in the archive the whole node_modules branch is marked "unpacked": true, there is no package code inside the archive). The archive itself was used only to parse the header and for cross-counting; the shipped files were not modified. The search was over raw bytes, substring-based. Compared files: 0.9.2 — 72 291 b, sha256 4A869354830A10FCC625C462BE5D44EB6EBB0487AA5D550DA58F4A4CE3016D34; 0.10.0 — 183 878 b, sha256 3E26ABB976031117CDF08B365D2185FF12368524C5B92EE5945DA81750D4BEDD.

Counts in the module @deepseek-ai\dsh-app-boot\lib\index.js:

substring 0.9.2 (72 291 b) 0.10.0 (183 878 b)
healProfilesModuleFallback 4 0
ensureSymlink 4 0
ensureModuleProxy 2 0
resolveModuleFallbackEntries 2 0
moduleFallbackCurrent 3 0
profiles/node_modules 2 0
moduleFallback (total) 10 0

In 0.9.2 the mechanism was called from the desktop main process — resources\app\out\main\index.js (974 239 b), in the market mount upgrade routine, after pnpm-workspace.yaml was written and before clearProfileInstallMarker:

await healProfilesModuleFallback({
  installAnchor,
  home: options.dshHome
});
await clearProfileInstallMarker(options.dshHome);

in the same file: import { healProfilesModuleFallback, resolveBundleDir, loadProfileDirectory, loadOverlayPatches, resolveProfileDir, PROFILE_TEMPLATES, initProfile } from "@deepseek-ai/dsh-app-boot";. The body in 0.9.2: resolveModuleFallbackEntries(installAnchor) → check moduleFallbackCurrent(modulesDir, entries) → under an inter-process lock healProfilesModuleFallbackLocked(entries, modulesDir); the links are created by ensureModuleProxy (kind: "proxy") or ensureSymlink.

In 0.10.0 healProfilesModuleFallback is no longer called from the main process, and inside @deepseek-ai/dsh-app-boot it is absent altogether. The name survives only in the text of the Safe Mode help (EPERM: operation not permitted, symlink under profiles\node_modules\, with ensureSymlink / healProfilesModuleFallbackLocked in the stack) — and that help text still says "the next launch rebuilds it".

The anchor, however, does exist. Both versions compute it the same way:

const installAnchor = join(dirname(options.dshEntryPath), "..", "package.json");
  • 0.10.0: dshEntryPath() = join(bundledRuntimeRoot(), "node_modules", "@deepseek-ai", "dsh", "lib", "bin.js"), and runtimePackageRoot(appPath, isPackaged) = isPackaged && appPath.endsWith(".asar") ? ${appPath}.unpacked : appPath ⇒ the anchor …\resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh\package.json — exists (7 640 b).
  • 0.9.2: dshEntryPath() = join(process.resourcesPath, "app", "node_modules", "@deepseek-ai", "dsh", "lib", "bin.js") ⇒ the anchor …\resources\app\node_modules\@deepseek-ai\dsh\package.json.

Hence: the defect is not in the packaging of the anchor, but in the fact that the tree rebuild was removed from the shipped build.

The only healing code left in 0.10.0 is async function healProfileBundles(dshHome, hostComposedBundles = []); it is called by reportProfileConsistency(dshHome); it works with the profile manifest (profilePackageJsonPath, inspectPackage(nodeModulesPath, packageName), "removed duplicate host-composed bundle layer(s)") and prints the profile inconsistency lines, but does not touch the profiles\node_modules links. This code lives inside the archive — in the out/main/index.js entry of the file resources\app.asar: async function healProfileBundles(dshHome, hostComposedBundles = []) @6 072 348, async function reportProfileConsistency(dshHome) @6 371 014, reportProfileConsistency: () => reportProfileConsistency(dshHome) @6 379 302 / 6 379 334, the print [desktop] profile inconsistency: @6 371 880. In the unpacked tree resources\app.asar.unpacked not one of these three substrings is present (verified with findstr /s /m across the whole tree — 0 files), therefore everywhere below "tree" means specifically the unpacked tree. The tree rebuild mechanism, moreover, is not replaced: all that is left of the old scheme is the cleanup half — LINK_PROJECTION_DIR = ".dsh-module-fallback" (@deepseek-ai\dsh-app-boot\lib\index.js:602, the vendor's comment at :601: "Directory where the link backend of the dsh 0.1.5 releases projected bundle-carried packages into a profile"), function removeLinkProjections(dir) (:610; doc at :604–:608: only symlinks inside <profile>/node_modules whose target lies in <profile>/.dsh-module-fallback/node_modules are removed, then the directory itself is deleted, while pnpm packages and all other symlinks remain), the call removeLinkProjections(dir); (:1014) inside function loadProfile(binName, name, installAnchor, home = resolveDshHome(), options = {}) (:1007) — that is, on every profile read; nearby function linkedProfileRoots(profile, profilesDir) (:637) only reads the links for resolution (called from async function createRuntimeResolution(options) (:739), line :748). Our broken links are invisible to that cleanup: they lie in the shared profiles\node_modules and point to the old installation path, not into <profile>/.dsh-module-fallback/node_modules. Mirror image: in 0.9.2 these substrings are absent altogether (removeLinkProjections — 0, LINK_PROJECTION_DIR — 0): there it was exactly the rebuilder.

Second measurement: only half of the Safe Mode fix made it into the shipped build

The 0.10.0 shell sets the Safe Mode flag and clears it for a normal profile:

// resources\app.asar, out/main/index.js
const { ELECTRON_RUN_AS_NODE: _runAsNode, DSH_DESKTOP_HOST_RESOLVED: _hostResolved, ...parentEnvironment } = environment;
// …
...profile === SAFE_MODE_PROFILE && { DSH_DESKTOP_HOST_RESOLVED: "1" }

Next to it in the archive lies a comment with the intent: "so the patched Harness resolves them from its own installation and never touches the shared profiles/node_modules fallback". But there is no reader of the flag in the shipped build: DSH_DESKTOP_HOST_RESOLVED — 0 occurrences in the entire runtime resources\app.asar.unpacked (11 583 code files: js 8 592, mjs 1 359, cjs 445, ts 1 187; plus configs: json 1 002, yml 24, yaml 273). As a control, for the remaining substrings of the mechanism in the tree resources\app.asar.unpacked: in the DSH packages (@deepseek-ai\dsh, @deepseek-ai\dsh-app-boot, versions 0.1.7-rc.2) — 0; healProfilesModuleFallback, healProfileBundles, healProfilesModuleFallbackLocked, ensureModuleProxy, resolveModuleFallbackEntries, moduleFallbackCurrent — 0 across the whole unpacked tree resources\app.asar.unpacked (the same substrings do exist inside the archive — that is the shell's healing code out/main/index.js, named in the previous section; "tree" here and below means the unpacked tree only). The only occurrences of ensureSymlink (20) belong to third-party packages: node_modules\fs-extra\lib\ensure\symlink-paths.js (2), …\fs-extra\lib\ensure\index.js (2), …\pnpm\dist\worker.js (8), …\pnpm\dist\pnpm.cjs (8). The only occurrence of moduleFallback (1) is README text: node_modules\@deepseek-ai\dsh-package-manifest\README.zh.md:57; and it adds an argument in the same direction: the internal configTrees, sessionFormatMigration and "生成的 moduleFallback 元数据" "分别由镜像打包器、目录生成器和启动器读取方拥有" — that is, the manifest-level fallback metadata in 0.1.7-rc.2 is still declared (caveat: the source is documentation, not code), even though the tree rebuilder itself has been removed from the shipped build. The same zero is confirmed in the remaining 18 files of the resources\ directory (including host-module-fallback.mjs): the two occurrences of the flag exist only in app.asar itself — these are exactly the lines quoted above, so there is no reader anywhere in the shipped build.

So only half of the "Safe Mode resolves from the installation" fix made it into the shipped build: the shell side is there, the core side that should read the flag is missing. This independently confirms the §1 conclusion (the mechanism was removed) and explains why the log is empty on this subject.

Third measurement: cross-check against the published artifacts, without installing (27.09.2026)

The measurements above were taken on an installed copy. To rule out "it is only like this for us", both published installers were unpacked directly, without installing: 7z x on an NSIS installer yields the inner payload app-64.7z, and unpacking that yields the build exactly as it is placed on disk. The installer hashes matched the published values: 0.10.0 — 255 264 432 b, sha256 0449A35CAC0AA3981844AB8C273C7AFCD3984A97DD58E850E12FEA774432984F (payload 254 380 803 b, layout resources\app.asar + resources\app.asar.unpacked\); 0.9.2 — 170 753 576 b, sha256 4B4A8884F1BCEC1CD16A092E641A680F86F964A70BB9897425285AFD53F8A6A6 (layout is the unpacked directory resources\app\, no app.asar).

check 0.10.0 (expected / actual) 0.9.2 (expected / actual)
resources\app.asar.unpacked\package.json no / no the mechanism did not use it: resources\app\node_modules\@deepseek-ai\dsh\package.json — exists
the root package.json inside app.asar is marked unpacked no / no (size=13421 offset=1066007 unpacked=false) n/a (no archive)
dsh-app-boot\lib\index.js: size / sha256 / healProfilesModuleFallback / ensureSymlink 183 878 b / 3E26ABB976031117CDF08B365D2185FF12368524C5B92EE5945DA81750D4BEDD / 0 / 0 72 291 b / 4A869354830A10FCC625C462BE5D44EB6EBB0487AA5D550DA58F4A4CE3016D34 / 4 / 4

Additionally, for the 0.10.0 archive (asar header): 6 550 424 b, headerSize 5 470 978, data from 5 470 996, root entries node_modules, out, package.json; files 21 214, directories 3 161, unpacked leaves 21 210; in node_modules\@deepseek-ai — 284 directories, all with a package.json; out\main\index.js — 990 966 b, unpacked=false. The full count of the mechanism in 0.10.0 (0.9.2) in dsh-app-boot\lib\index.js: healProfilesModuleFallback 0 (4), …Locked 0 (2), healProfileBundles 0 (0), ensureSymlink 0 (4), ensureModuleProxy 0 (2), resolveModuleFallbackEntries 0 (2), moduleFallbackCurrent 0 (3), profiles/node_modules 0 (2), moduleFallback 0 (10), removeLinkProjections 3 (0), LINK_PROJECTION_DIR 2 (0).

Artifact = installed copy. resources\app.asar from the unpacked installer and from the installed application matched byte for byte (6 550 424 b, sha256 2B67920B29C5BEC23CC23CC3F4B1F06875D5EF850A594173FD7B3E16AED50EEF); the same holds for @deepseek-ai\dsh-app-boot\lib\index.js (183 878 b, 3E26ABB9…). This means the measurements taken on the installed copy pertain to the published artifact, not to a local peculiarity of the machine.

What this measurement does not show: it confirms the composition of the release, but not the on-disk behaviour. The behaviour is covered by §7.

3. Impact

  1. Module resolution from the profile: from harness\profiles\web\node_modules\dshmarket\package.json the following currently do not resolve: yaml, zod, react, @deepseek-ai/cordis, @deepseek-ai/dsh (all MODULE_NOT_FOUND). For @deepseek-ai/* the runtime is saved by the hook resources\host-module-fallback.mjs (re-resolution from the host entry point on ERR_MODULE_NOT_FOUND), which is why the harness and the profile plugins work. For yaml, zod, react there is no such fallback: a profile plugin that imports them from its physical directory will fail. The hook's coverage is set explicitly: HOST_PACKAGE_PREFIX = '@deepseek-ai/' and HOST_RETRYABLE = new Set(['ERR_MODULE_NOT_FOUND', 'ERR_PACKAGE_PATH_NOT_EXPORTED']), and the re-resolution runs with the host entry point's parentURL (50 lines, resources\host-module-fallback.mjs). That is, yaml, zod, react are outside the coverage by construction, not by oversight: the coverage is set in the hook file itself, not in the tracker (on PR fix: resolve profile-installed plugins from cordis-plugin-loader #83 in the §5 cross-check — the same section carries the caveat about why it qualifies only as the history of the mechanism).
  2. Our build gates (project deepseek.voice):
    • npm run verify:plugins → exit 1: Error: Cannot find module '…\profiles\node_modules\yaml', scripts\verify-plugin-packaging.mjs:35;
    • npm run validate:client → exit 1, 1 of 11 failed: FAIL хост-половина импортируется — Cannot find package '@deepseek-ai/cordis' imported from …\profiles\.generations\live\dsh-status-strip+0.6.2+2d03bbc0e7f3\node_modules\dsh-status-strip\li. The string is truncated by our own check: scripts\validate-client-plugin.mjs:106 — String(error.message).slice(0, 200) (for the client bundle the same at :89, .slice(0, 160); the line :100 is the import itself, there is no truncation there). The importer is …\dsh-status-strip\lib\index.js;
    • npm run verify:copies → exit 0.
  3. Latent risk for installing plugins from the market and for any profile plugins with a non-host dependency (zod, yaml, react).

The system's operability is not impaired by this: /status — 3/3, the bot and the bridge are live, generations 7 linked / 0 unlinked.

4. How to reproduce

  1. (Done on 27.09.2026 on a separate clean machine — see §7.) A clean machine, install DSH Desktop 0.9.2, let the profile live a while (the $DSH_HOME\profiles\node_modules links are created at startup).
  2. Update the application to 0.10.0 and launch it.
  3. Check: in $DSH_HOME\profiles\node_modules the links still point to resources\app\node_modules\…, while the @deepseek-ai tree holds 242 entries against 284 packages in the unpacked tree (47 unpacked ones are missing, 5 links are extra); in harness.log there is not a single line about rebuilding the tree; in the 0.10.0 build the substrings healProfilesModuleFallback and ensureSymlink in resources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-app-boot\lib\index.js are absent, although in 0.9.2 (where the package lies unpacked in resources\app\node_modules\…) they are present — comparing the two builds is the shortest path to reproduction.

Note on evidential value (updated 27.09.2026). Steps 1–2 were performed on 27.09.2026 on a separate clean machine (Windows 11 Home, DSH Desktop had never been installed): the profiles\node_modules tree appeared there by itself — 241 entries, 213 links, 0 broken; after an in-place update to 0.10.0 all 213 became broken, the application still starts, and there is no healing in the log (the full table is in §7). This also removes the assumption of step 1, but not in the direction in which it used to be read: the links are created not by 0.10.0 but by 0.9.2 — when it is installed the profile tree appears silently, without a single line about links in the log — and it is the update to 0.10.0 that makes them broken in one stroke. Affected are all who have ever launched 0.9.x before. Installing 0.10.0 straight onto an empty machine was not tested. Scale (measured, GitHub REST API, download counter of the dataelement/dsh-desktop releases as of 27.09.2026): the Windows installer of 0.9.2 was downloaded 2741 times, that of 0.10.0 — 691. The caveat remains for the absolute numbers: 243 entries / 215 links / 214 broken / 47 missing packages and the log snapshot are our working profile; it has lived through several versions, plugins were installed and removed in it, and it has profiles\web\.generations-deferred.json and 7 generations. On the clean machine the numbers are two links lower (§7), so the conclusion is built on the share of broken links (213 of 213 versus 214 of 215), not on the absolute figure. The proof of the defect itself does not require a clean machine: the comparison of the two builds — healProfilesModuleFallback/ensureSymlink present in 0.9.2 and absent in 0.10.0 — reproduces on the release artifacts as well (the third measurement in §2).

Caveat about a "dirty home" (not reproduced in a separate experiment). The controlled experiment yields a home that has lived through one installation: before the update all 213 links there were live, their targets existed. A home that has lived through several versions and keeps links from an even older installation location was not reproduced in a separate experiment — it is observed live in our working profile: 243 entries, 215 links, of which 214 broken, 47 packages of the shipped build missing from the tree, and across seven 0.10.0 launches not a single line about healing, the state does not change. This does not affect the issue's claim: the mechanism was removed from the shipped build (measurements 1–3 in §2), and the state of the tree before and after is proven on the clean machine (§7).

5. Requests and questions for the vendor

  1. The main question: was the mechanism removed deliberately? In 0.9.2 the rebuild of the profiles\node_modules tree was in the shipped build (healProfilesModuleFallback in @deepseek-ai/dsh-app-boot, called from the main process before the market mount upgrade), in 0.10.0 it is not. If (1) it was meant to be kept — put it back into the shipped build; if (2) the shared fallback is deliberately replaced by the generation projection (profiles\.generations) — provide a proper migration for the inherited tree (or explicitly document that it is no longer needed) and fix the help text that promises the next launch will rebuild the tree: right now the user sees the promise but sees neither a rebuild nor a line in the log.
    Judging by the composition of the 0.10.0 commit itself — 3deeef37419ecf26db700a3b99d7c9827faec392 ("refactor: 合入 Harness 0.1.7-rc.2 底座与兼容修复 (refactor: 合入 Harness 0.1.7-rc.2 底座与兼容修复 #570)", 26.09.2026 07:47Z), whose sub-items include feat(packaging): archive app shell with physical runtime dependencies and fix(safe-mode): resolve Safe Mode plugins from the installation, not the module fallback — this looks like branch (2). If that is the case, request 1 boils down to a proper migration of the inherited tree and a fix to the help text; if not, to returning the mechanism to the shipped build. A one-line answer settles the question.
    A separate argument in the same direction is the second measurement in §2: the 0.10.0 shell already sets the DSH_DESKTOP_HOST_RESOLVED flag for Safe Mode, but not a single runtime file reads it. That is, only half of the "Safe Mode resolves from the installation" fix made it into the shipped build, and the question is no longer "why was it removed" but "what exactly did you intend, if half of the code is missing".
    Also, along the same branch (2): when 0.10.0 starts, the profiles\web\.dsh-module-fallback directory disappears from the profile home — before the update it was there (the cleanup half did its work, see §2 and §7). Was deleting the projection directory deliberate, and what should profiles do that never migrated into it?

  2. Make sure that startup really does heal a tree left over from the previous layout — and by composition, not only by link targets. The expected final state in the @deepseek-ai scope: 284 links, 0 broken (the unpacked tree resources\app.asar.unpacked\node_modules\@deepseek-ai holds exactly 284 packages) — to the 237 matching ones add 47 missing (among them @deepseek-ai/dsh-agent-preset, @deepseek-ai/dsh-workflow-ptc, dsh-hmr, dsh-plugin-manager, libreoffice-kit*) and delete 5 links to packages that do not exist in 0.1.7 (dsh-agent-presets, dsh-code-runtime, dsh-code-runtime-worker-thread, dsh-settings-file, dsh-workflow-worker-thread): their targets lie in resources\app\node_modules\…, and the resources\app directory no longer exists in 0.10.0 — they cannot be retargeted. Separately, please take note: if healing at startup only retargets the existing 242 links, these 47 packages will remain unavailable to the profile. We expect it to restore the tree's composition as well, and not only the link targets. This is not an "expectation" but an upstream contract marked implemented. The note anywhere-labs/dsh-desktop@master:.agents/notes/implemented/architecture/2026-08-15-packaged-profile-fallback.md (HTTP 200, status implemented) says literally: "The desktop Host maps its installation anchor from app.asar/package.json to app.asar.unpacked/package.json before calling healProfilesModuleFallback;" and "Packaging fails before signing if either the root anchor or a required runtime entry is absent". A caveat about location: in dataelement/dsh-desktop@main this note does not exist (HTTP 404) — it lives only in the neighbouring project, there is no need to look for it locally. Hence two facts the vendor can verify: (a) the anchor mapping and the healProfilesModuleFallback call were designed as a pair — in 0.10.0 only the mapping remains (function runtimePackageRoot(appPath, isPackaged) { return isPackaged && appPath.endsWith(".asar") ? ${appPath}.unpacked : appPath; }), and there is no call; (b) the packaging gate, according to that same note, must reject a build without the physical manifest — yet in the 0.10.0 shipped build resources\app.asar.unpacked\package.json is absent (verified: Test-Path → False). That is, a contract marked as fulfilled upstream is violated — and this is not "we hope that is how it was intended". The same note also says what the mechanism provides: "Every startup can repair links left by a prior installation location without modifying a selected profile's manifest or patch layer" — exactly the behaviour that 0.10.0 lacks.

  3. Make the absence of the rebuild loud: right now the log has not a single line, while the help text promises a rebuild. The minimum is a line in the log, and per the upstream note (link at the top) — a build failure before signing if the expected mechanism is missing from the shipped build.

  4. Separately about packaging (not the cause of the current state, but the question remains): the root package.json inside app.asar is not marked unpacked — 0 occurrences of asarUnpack; only four files carry their data inside the archive (out/main/index.js, out/preload/index.cjs, out/preload/windows-menu.cjs, package.json), and on disk only the node_modules branch is unpacked. We checked the consumers ourselves: the literal app.asar.unpacked never occurs in the shipped code (in out/main/index.js there is only the template runtimePackageRoot() → `${appPath}.unpacked`, and its only consumer is bundledRuntimeRoot()), asarUnpack — 0 matches, "deployment manifest" — 0, resources\package.json — 0; in the unpacked tree, out of the 11 416 files of those extensions examined (6 files larger than 4 MB skipped), app.asar.unpacked occurs once — node_modules/node-pty/lib/unixTerminal.js (it substitutes the path to spawn-helper); the asar.unpacked literal appears in three files and four places (node-pty/lib/unixTerminal.js ×2, node-pty/lib/windowsConoutConnection.js, dsh-tool-fs-search/lib/index.js), and all of them substitute a path into the unpacked tree — none of them concerns the manifest. Electron reads the shell manifest from inside the archive (app.getAppPath()), while the "installation anchor" in the code is computed from dsh\lib\bin.js to …\node_modules\@deepseek-ai\dsh\package.json and does exist. So there is nothing left to ask confirmation for — we ask something else: explain why the gate from the upstream note ("Packaging fails before signing if either the root anchor or a required runtime entry is absent", status implemented) did not reject the 0.10.0 build, or what "root anchor" means in that wording; and along the way confirm that out\main\index.js inside the archive is normal.

  5. A question linking §2 and §6: the lines profile inconsistency: the patch layer inserts @deepseek-ai/dsh-agent-preset, which is not installed — such a pair appears on every 0.10.0 launch; by the 27.09.2026 10:15 snapshot there are already six of them (L891–892, L1019–1020, L1097–1098, L1174–1175, L1238–1239, L1324–1325; see §2) — is this a consequence of the broken profile tree or a separate patch-layer problem? If the former, it is also the cheapest acceptance criterion: after the fix these lines must disappear.

  6. Question: the heal policy for links that point not into the application's shipped build. We have exactly one live such link — destroy → %APPDATA%\npm\node_modules\@deepseek-ai\dsh\node_modules\destroy (the tree of the globally installed dsh CLI). Should such targets be overwritten or left alone? We need an explicit answer, not a guess.

  7. A one-line question: what links dsh-desktop-hmr-fallback now at the start of a new profile — or has the CI smoke test for which PR fix: pin the profile's pnpm store, and restyle the update card #148 declared the dependency been removed as well? (The package is present in the 0.10.0 shipped build and marked unpacked, while the mechanism that linked it is not; see the cross-check below.)

  8. A separate question — top-level links outside the @deepseek-ai scope. There are 215 of them in the profile tree; for 152 of them the target's name exists in the unpacked build tree, and for 63 it does not. Of those 63, only two names occur in the resources\app.asar header (qs, zod-to-json-schema), the other 61 appear neither in the archive nor on disk — they are a dependency of the 0.9.2 layout (accepts, ajv-formats, body-parser, cors, depd, …), which does not exist in 0.10.0. Should such links be considered part of the fallback — in which case "0 broken" is unattainable by construction — or should they be deleted during the repair? (Method: each link's target name was checked against the root of app.asar.unpacked\node_modules; the presence of the name was checked against the app.asar header read in Latin1.)

Cross-check against the public tracker and the vendor's releases (27.09.2026)

Taken on 27.09.2026 from the public tracker and releases of dataelement/dsh-desktop (GitHub REST API). This is not a §2 measurement, but an external support for the questions below. Provenance labels: measured — with the command and its output; visible in the shipped build — with a path and an offset; assumption — not verified. Below, the supports are labelled with these marks so that the unverified is not read as fact.

6. Second finding (not about packaging): the agent presets referenced a package removed from 0.1.7

Symptom in the log (in the 16:00:35Z launch — lines 747, 758-759, 770, 781-782, 793, 824; before our fix there were 4 errors and 2 warnings per start):

error group: Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@deepseek-ai/dsh-workflow-worker-thread'
warn agent-preset-registry: agent preset tester/dev: workflow-worker-thread … never started

Where the reference came from. The entry - id: workflow-worker-thread / name: '@deepseek-ai/dsh-workflow-worker-thread' stood in our preset files
%APPDATA%\dsh-desktop\harness\.agent-presets\dev\agent.cordis.yml and …\.agent-presets\tester\agent.cordis.yml. On the first 0.10.0 launch the desktop
imported the directory into the harness home (log, line 8: web import: .agent-presets) — from that moment the source of the presets is the harness home; at that time the directory disappeared from the project, but on 27.09.2026 the master copies were returned to the project (directory .agent-presets\, 8 files, hashes matching the home byte for byte). The directory migration starts on every
start, but converts once: 16:00:35Z — converted 2 legacy agent presets for the web Profile; originals and backup retained (log 714–717),
16:56:26Z — preset migrations done already without converted (log 882–884). The live copy is the block # dsh-desktop legacy presets begin … end in
profiles\web\cordis.patch.yml (at the time of the check on 26.09 — lines 76–715, the file 715 lines; after our fixes on 27.09 the catalog begins at line 110, the file 749 lines); the references were at :357-358 (preset dev) and :670-671 (preset tester). The backup is
%APPDATA%\dsh-desktop\harness\.agent-presets.pre-0.1.7-backup\ (9 files: 6 under dev, 3 under tester).

The same error has a second path. The profile tree still holds a broken link @deepseek-ai\dsh-workflow-worker-thread — one of those very 5 "extra" ones from §2, pointing to the vanished
…\resources\app\node_modules\@deepseek-ai\dsh-workflow-worker-thread. That is, the package was pulled in through the dead link as well, and not only through the preset entry. Our fix closed the mounting;
the link itself should go away together with the other four as part of the fix under §2 (request 2).

The package was replaced, not merely removed. In 0.1.7 the old package does not exist: 0 mentions both in the unpacked @deepseek-ai tree (284 packages) and in the archive itself.
But there is a proper replacement @deepseek-ai/dsh-workflow-ptc v0.1.7-rc.2, and the vendor mounts exactly that one — @deepseek-ai\dsh-web-app\presets\standard.patch.yml:119-122:
- id: workflow-ptc / name: '@deepseek-ai/dsh-workflow-ptc' / config: { provider: spawn } (this is verbatim). In ptc.patch.yml:119-123 the same entry, but
with disabled: true — here this is a retelling, not a verbatim quote. dsh-tool-workflow remains alongside.

Impact. Only this entry in the two presets failed to come up; the rest of the roster was mounted. The cost is 4 ERR_MODULE_NOT_FOUND errors and 2 warnings
agent preset tester/dev: workflow-worker-thread … never started on every start.

Fixed on our side 26.09.2026, 22:20; verified by a restart. The replacement workflow-worker-thread → workflow-ptc with the same config: { provider: spawn } was made in both places:
the sources — %APPDATA%\dsh-desktop\harness\.agent-presets\dev\agent.cordis.yml:260-263 (324 lines) and %APPDATA%\dsh-desktop\harness\.agent-presets\tester\agent.cordis.yml:234-237 (267 lines); the live copy —
cordis.patch.yml:357-360 and :670-673 (numbers as of the fix on 26.09; the file was 715 lines — after the fixes on 27.09 the mounts stand at :391-392 and :704-705, the file 749 lines). Verification (restart on 26.09 22:32; launch requested — line 1001, 17:32:37Z; projected generations: 7 linked, 0 unlinked;
Harness is ready 17:32:54.981Z): in this launch there is not a single never started line, not a single ERR_MODULE_NOT_FOUND about this package, and 0 fallback lines as well —
the legacy transport is closed. The former symptom in the log remains history: all 16 mentions of workflow-worker-thread are from before the fix.

Request (soft, non-blocking). When migrating inherited presets, check their composition against the installed core and write one clear line
("preset dev: package X is absent in this version"), and, if a proper replacement exists, suggest it, instead of a module resolution error on every start.

7. Controlled upgrade 0.9.2 → 0.10.0 on a clean machine (27.09.2026)

The §2 measurements were taken on a machine where 0.10.0 was already installed, so from them it was impossible to tell "the update broke the links" from "the links were already broken before it". To close this question, the experiment was repeated on a separate machine where DSH Desktop had never been installed (Windows 11 Home, build 26200, x64): 0.9.2 was installed, a snapshot was taken read-only by the collector, the same installer updated it in place to 0.10.0, and a second snapshot was taken. Nothing was repaired, deleted or relinked.

before (0.9.2) after (0.10.0)
build layout resources\app present, no app.asar resources\app absent, app.asar 6 550 424 b, sha256 2B67920B29C5BEC23CC23CC3F4B1F06875D5EF850A594173FD7B3E16AED50EEF
profiles\node_modules: entries / links / broken 241 / 213 / 0 241 / 213 / 213
link target (example accepts) …\resources\app\node_modules\accepts — exists the same path — does not exist
scope directories @* in the profile tree: entries with a package.json 1 of 1 0 of 1
healProfilesModuleFallback / …Locked 4 / 2 0 / 0
ensureSymlink / ensureModuleProxy / resolveModuleFallbackEntries / moduleFallbackCurrent 4 / 2 / 2 / 3 0 / 0 / 0 / 0
removeLinkProjections / LINK_PROJECTION_DIR 0 / 0 3 / 2
heal / projected generations in the log 0 / 0 0 / 0
launches / "Harness is ready" 3 / 3 (+30.6, +17.6, +18.6 s) 2 / 2 (+23.9 and +17.1 s)
ERR_MODULE_NOT_FOUND / EPERM / symlink / profile inconsistency 0 / 0 / 0 / 0 0 / 0 / 0 / 0

What this adds to §2:

  • on the clean machine the tree existed and all 213 targets were live before the update, and the update made all 213 (100 %) broken in one stroke — so this is not accumulated debris and not a consequence of our actions, but a direct consequence of replacing resources\app with app.asar;
  • in the 0.9.2 log there is also no creation of links (0 occurrences of heal, projected generations = 0): the 0.9.2 mechanism worked silently, which is why "the log is empty" by itself proves neither creation nor absence of healing — the proof is given by the state of the tree before and after (the same remark as in §2);
  • the profiles\web\.dsh-module-fallback directory disappeared from the profile home (it was there before the update) — this is the cleanup half from §2 at work; it does not affect the profiles\node_modules tree itself, all 213 links remained in place;
  • the 0.10.0 application starts with a completely broken tree (two launches, Harness is ready both times, 0 ERR_MODULE_NOT_FOUND, 0 EPERM) — the defect does not bring down startup, it leaves an inconsistent state that the user does not see; there is not a single line about rebuilding or healing the links in the log after the update;
  • on the same machine the packaging observation from §2 was confirmed as well: after the controlled upgrade resources\app.asar.unpacked\package.json is absent, and the root package.json in the archive is without the unpacked marker;
  • the numbers of the two machines agree in substance, not literally: on the clean machine there are 213 links and all 213 (100 %) became broken, while in our working profile (§2) there are 215 links, of which 214 are broken and one is live (destroy — it points into the globally installed CLI, see §5 item 6). The difference of a couple of entries is a trace of our profile being older (plugins were installed and removed, 7 generations), so the conclusion is built on the share of broken links, not on the absolute number.

Artifacts (taken read-only, kept locally and not published; sizes and sha256 are given so that the recipient can verify): the "before" report 12 043 b — 650C6A77D350F61BC606D644389ECA3D837CE1729A87A61E58B6EF86461C8BE3, the "after" report 13 973 b — AEB6B013B1FEA3F32EC8D6BF064428B8134E86AAB8314EE245953576A67DBE86, the 0.9.2 log (8 539 b, 3 launches) — 09682BD5846AEC4C4EBC18E7CDDF6515BB5D75FB02424C093A1C9DDB7BF38597, the 0.10.0 log (8 037 b, 2 launches) — 06E8B2FCB906FBEB124C940A4175B91B229CE8DDC593BB85CEBB146E42F25573, the "before" numbers summary 8 220 b — 1F3597EDBA007E7C3BCD3C3A5C6DCB5271856092DD8F9ABC54B84D0928524493, the verdict 11 067 b — 6ACD37DD6D317B852293635C20C0A16035044B1E0FC7198C966DF7CE786682EF, the collector 18 768 b — 7C0D8D0185734D114AB6A8CB9566CDBCCAD4BE1085341D6BFC1B3E95AAFC457A (this hash matched the reference one against which both snapshots were verified). The harness home was preserved during the update (sessions, attachments, cache, .credentials.yaml are in place), the installation directory was recreated by the installer; both installers were verified against the reference sha256 (0.9.2 — 170 753 576 b, 0.10.0 — 255 264 432 b).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions