You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
每次启动 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)。
上游笔记(状态 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),它只存在于相邻项目。
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 contradictresources\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 upstreamanywhere-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).
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.jsondoes 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:
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:
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:
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)
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
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).
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.
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
(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).
Update the application to 0.10.0 and launch it.
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
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?
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.jsonis 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.
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.
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.
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.
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.
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.)
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.
Nothing newer than 0.10.0 has been released; there is no public data about preparation of the next release (measured — tags and releases via the GitHub REST API) (the vendor does not publish a roadmap or drafts — that is "unknown", not "nothing"). The latest tag is v0.10.0 (commit c12911a9b510add7fb2d7467e85cc9ababbdfca6); the tag v0.10.0-beta points to the same commit (beta published on 26.09 at 09:33Z, the release at 12:35Z). On main after the release there is exactly one commit — eec5d57e658f63431ab312dae3dd30d9d03c4cd4 ("fix(release): restore supported summary model (fix(release): restore supported summary model #546)"), unrelated to packaging; there are no 0.10.1/0.11 tags. The update feed (https://dshdesktop.com/updates/latest/latest.yml) is served by a redirect to a ModelScope mirror.
What is still left in the 0.10.0 shipped build itself (visible in the shipped build): the package dsh-desktop-hmr-fallback is still shipped — 3 matches in the archive (one is the header entry "dsh-desktop-hmr-fallback":{"files":{"index.js":{"size":3577,"unpacked":true,…}}}, two are the name in the dependency line "dsh-desktop-hmr-fallback": "file:packages/dsh-desktop-hmr-fallback" of the shell's root package.json, key and value); on disk this is resources\app.asar.unpacked\node_modules\dsh-desktop-hmr-fallback\index.js (3 577 b) and its package.json (416 b). At the same time healProfilesModuleFallback in the archive has 2 matches, both in the text of the Safe Mode help; there, and likewise only in the help text, ensureSymlink (2) and healProfilesModuleFallbackLocked (2) also occur, while in the code they are absent; 0.1.1-rc.1.patch and @deepseek-ai+dsh+ in the archive — 0 matches, but the patch file is not placed into the shipped build (it is a build input), so the archive cannot be used to judge it. Bottom line: the package for which the dependency was declared is present and marked unpacked, while the mechanism that linked it at the start of a new profile has been removed.
There is no separate issue about this removal in the tracker, but the mechanism itself is there — and as a live tool. (measured — GitHub REST API queries, named below) It was announced in PR fix: pin the profile's pnpm store, and restyle the update card #148 (author yaojin3616, association CONTRIBUTOR, merged 22.08.2026T10:16:54Z, "fix: pin the profile's pnpm store, and restyle the update card") — this is a change accepted into upstream, not a maintainer's action. The query slice is named so that the figure does not contradict itself: search/issues?q=repo:dataelement/dsh-desktop+healProfilesModuleFallback → total_count: 1 (issues and PRs together) — the match lies precisely in the PR. The declaration is still alive today: in patches/@deepseek-ai+dsh+0.1.7-rc.2.patch on main (1 410 b) there is the line + "dsh-desktop-hmr-fallback": "0.1.0",, whereas the patch name from that PR (patches/@deepseek-ai+dsh+0.1.1-rc.1.patch) no longer exists on main (404). A caveat about the line: that patch is addressed to the @deepseek-ai+dsh+0.1.1-rc.1 line (August), while the removal is recorded on the 0.1.7-rc.2 base, so the PR proves that the tool was alive, but does not prove that it survived until 0.10.0 — and that is our question. The meaning of the line is "so that at the start of a new profile healProfilesModuleFallback links this package correctly" (that is how the failure of the Windows smoke test in CI was healed). The nearest relatives (all measured via the API) — and none of them is about removing the mechanism:
插件市场装的插件在 v2.0.0 中无法加载:loader 从 app.asar.unpacked 解析,插件在 profile 的 node_modules #73 (closed) → PR fix: resolve profile-installed plugins from cordis-plugin-loader #83 (closed without a merge: merged: false, mergeable_state: "dirty", the PR's subject is the profile's node_modules directories, not a list of host names) — the history of the same mechanism: an attempt to add a fallback to the profile's node_modules for ESM resolution via an --import hook. It does not qualify as a support for §3: the file resources\host-module-fallback.mjs does not exist in the public repository (contents/resources → 404; it is a build directory), and the hook's coverage is proven by the file itself — 50 lines, HOST_PACKAGE_PREFIX and HOST_RETRYABLE — and not by the tracker.
Independent confirmation that the app.asar layout is an 0.10.0 innovation: (measured — issue [Bug] Windows 任务栏中 DSH Desktop 没有名称:主窗口标题被硬编码清空 #575, environment table) in issue [Bug] Windows 任务栏中 DSH Desktop 没有名称:主窗口标题被硬编码清空 #575 (opened 26.09.2026 13:18Z) the environment table records DSH Desktop 0.10.0 and the delivery form resources/app.asar ("0.10.0 起由解包目录改为 asar"), separately noting that on 0.8.1/0.8.2 the layout was unpacked.
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.
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).
DSH Desktop 0.10.0:
healProfilesModuleFallback从发行包中被移除,升级后profiles\node_modules链接树全部失效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未标记解包(asarUnpack0 次;结构化遍历头部得到 21 214 条文件记录,其中 21 210 条标记为unpacked,真正把数据放在档案内部的只有 4 个:out/main/index.js(990 966 字节,sha2567BF2632747D5838E9C02CDAB1ECE3738C520815070E4548214346FCC1543A4D6)、out/preload/index.cjs(62 315 字节,sha256BEB2C4565CF4D74530F9F33FC5DB727349F4844DB72452D5194C1FD9081E34C1)、out/preload/windows-menu.cjs(12 726 字节,sha2562F1D2A3C5D77C29833B2A4BC5A02C3218B92C0351702B6698ADFE64F991D7791)、package.json(13 421 字节,offset 1 066 007,sha25691E4BE17839A055481E74B15ED4B5FB5B5EC64D36976A2412C2D3D3429AD9439))——是独立问题,不是本次故障的原因: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)。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 项失败。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. 测量数据
resources\app目录resources\app.asar2B67920B29C5BEC23CC23CC3F4B1F06875D5EF850A594173FD7B3E16AED50EEFresources\app.asar.unpacked\package.jsonasarUnpack0 次;结构化遍历:21 214 条文件记录,其中 21 210 条标记unpacked,档案内部只有 4 个文件;根级package.json无unpacked标记(size=13421 offset=1066007)dsh-app-boot\lib\index.js4A869354830A10FCC625C462BE5D44EB6EBB0487AA5D550DA58F4A4CE3016D343E26ABB976031117CDF08B365D2185FF12368524C5B92EE5945DA81750D4BEDD…\app.asar.unpacked\node_modules\@deepseek-ai\dsh\package.jsonprofiles\node_modules(我们的工作机)@deepseek-ai记录数(链接树 / 解包树)harness.log中 heal 相关行4. 干净机器上的受控复现(0.9.2 → 0.10.0 覆盖升级)
profiles\node_modules241 条记录、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_FOUND0)。可见断链不是长期累积的垃圾,而是resources\app被app.asar取代的直接后果。5. 请求与问题(要点;完整八条见英文报告)
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)。@deepseek-ai区域期望的终态是 284 个链接、0 个断链(解包树里恰好 284 个包):补上缺失的 47 个,删除指向 0.1.7 已移除包的 5 个链接(它们的目标在resources\app\node_modules\…,而该目录在 0.10.0 已不存在,无法重新指向)。package.json未标记解包——asarUnpack0 次,放在档案内部的只有 4 个文件,磁盘上只解包了node_modules分支。消费者我们已自行核查: 发行包代码里没有出现app.asar.unpacked字面量(out/main/index.js里只有模板runtimePackageRoot()→`${appPath}.unpacked`,其唯一消费者是bundledRuntimeRoot()),asarUnpack0 处、「deployment manifest」0 处、resources\package.json0 处;在共查看的 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位于档案内部是正常的。profile inconsistency的行是断链树的后果,还是补丁层单独的问题?若是前者,它同时也是最便宜的验收标准(修复后这些行应当消失)。destroy)的策略是什么——覆盖,还是不动?dsh-desktop-hmr-fallback?(该包仍在发行包中并标记为解包,但负责链接它的机制已不在。)6. 影响范围
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
healProfilesModuleFallbackfrom the shipped build — theprofiles\node_modulestree 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; installerdsh-desktop-windows-x64-setup.exe, 170 753 576 b).The publisher of the build is
dataelement/dsh-desktop: the rootpackage.jsoninsideresources\app.asaryieldsname: dsh-desktop,version: 0.10.0,repository: git+https://github.com/dataelement/dsh-desktop.git,author: DataElement,license: MIT; this does not contradictresources\app-update.yml(provider: generic,https://dshdesktop.com/updates/latest/) orCompanyName: DataElementfrom VersionInfo — a generic feed by itself does not prove the publisher. The note below comes from the upstreamanywhere-labs/dsh-desktop;resources\host-module-fallback.mjsarrived in the build from there as well. The request is addressed todataelement/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
healProfilesModuleFallbackmechanism 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_modulestree created under 0.9.2 is rebuilt by no one: 214 links are broken (they point into the vanishedresources\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.loghas not a single line about rebuilding the tree. The only thing the user sees is the linesprofile 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 theprofiles\node_moduleslinks.Separately, and not as a cause: after the switch to the
app.asarlayout, the application's rootpackage.jsonis 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 exactlynode_modules,out,package.json.asarUnpack. A structured walk of the header gives 21 214 file entries, of which 21 210 are markedunpacked(that is, they physically live inapp.asar.unpacked), while exactly four files carry their data inside the archive —out/main/index.js(990 966 b, sha2567BF2632747D5838E9C02CDAB1ECE3738C520815070E4548214346FCC1543A4D6),out/preload/index.cjs(62 315 b, sha256BEB2C4565CF4D74530F9F33FC5DB727349F4844DB72452D5194C1FD9081E34C1),out/preload/windows-menu.cjs(12 726 b, sha2562F1D2A3C5D77C29833B2A4BC5A02C3218B92C0351702B6698ADFE64F991D7791) andpackage.json(13 421 b,offset: 1 066 007, sha25691E4BE17839A055481E74B15ED4B5FB5B5EC64D36976A2412C2D3D3429AD9439). That rootpackage.jsonhas no such marker (onlyintegrity), which is whyresources\app.asar.unpacked\package.jsondoes not exist on disk; the only unpacked branch isnode_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.jsis 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 rootpackage.jsonis marked or not did not affect rebuilding the tree in 0.9.2:installAnchoris computed from the path todsh\lib\bin.jsand 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.jsonis absent there as well, and the rootpackage.jsonin the archive is without theunpackedmarker (§7).The shared profile tree
%APPDATA%\dsh-desktop\harness\profiles\node_modules:package.jsoninside); 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.destroy→%APPDATA%\npm\node_modules\@deepseek-ai\dsh\node_modules\destroy, that is, into the tree of the globally installeddshCLI, and not into the application's shipped build (question 6).@deepseek-ai— 242 entries and 0 with apackage.json;%LOCALAPPDATA%\Programs\DSH Desktop\resources\app\node_modules\accepts(theresources\appdirectory no longer exists after 0.10.0);@deepseek-aiholds 284 packages (all have apackage.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. Sohealmust 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 getnever startedagain). See requests 2 and 6.harness.log— 0 occurrences ofhealandhealProfilesModuleFallbackacross 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 offallbackare unrelated to repairing the tree: 4 lineshost fallback from …(2 each in two 0.10.0 launches — before our preset fix from §6) and 8 framesat resolve (…host-module-fallback.mjs…).profile inconsistency: the patch layer inserts @deepseek-ai/dsh-agent-preset, which is not installedappear 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 noapp.asar), for 0.10.0 from the unpacked treeresources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-app-boot\lib\index.js(in the archive the wholenode_modulesbranch 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, sha2564A869354830A10FCC625C462BE5D44EB6EBB0487AA5D550DA58F4A4CE3016D34; 0.10.0 — 183 878 b, sha2563E26ABB976031117CDF08B365D2185FF12368524C5B92EE5945DA81750D4BEDD.Counts in the module
@deepseek-ai\dsh-app-boot\lib\index.js:healProfilesModuleFallbackensureSymlinkensureModuleProxyresolveModuleFallbackEntriesmoduleFallbackCurrentprofiles/node_modulesmoduleFallback(total)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, afterpnpm-workspace.yamlwas written and beforeclearProfileInstallMarker: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)→ checkmoduleFallbackCurrent(modulesDir, entries)→ under an inter-process lockhealProfilesModuleFallbackLocked(entries, modulesDir); the links are created byensureModuleProxy(kind: "proxy") orensureSymlink.In 0.10.0
healProfilesModuleFallbackis no longer called from the main process, and inside@deepseek-ai/dsh-app-bootit is absent altogether. The name survives only in the text of the Safe Mode help (EPERM: operation not permitted, symlinkunderprofiles\node_modules\, withensureSymlink/healProfilesModuleFallbackLockedin 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:
dshEntryPath()=join(bundledRuntimeRoot(), "node_modules", "@deepseek-ai", "dsh", "lib", "bin.js"), andruntimePackageRoot(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).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 byreportProfileConsistency(dshHome); it works with the profile manifest (profilePackageJsonPath,inspectPackage(nodeModulesPath, packageName), "removed duplicate host-composed bundle layer(s)") and prints theprofile inconsistencylines, but does not touch theprofiles\node_moduleslinks. This code lives inside the archive — in theout/main/index.jsentry of the fileresources\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 treeresources\app.asar.unpackednot one of these three substrings is present (verified withfindstr /s /macross 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_moduleswhose target lies in<profile>/.dsh-module-fallback/node_modulesare removed, then the directory itself is deleted, while pnpm packages and all other symlinks remain), the callremoveLinkProjections(dir);(:1014) insidefunction loadProfile(binName, name, installAnchor, home = resolveDshHome(), options = {})(:1007) — that is, on every profile read; nearbyfunction linkedProfileRoots(profile, profilesDir)(:637) only reads the links for resolution (called fromasync function createRuntimeResolution(options)(:739), line:748). Our broken links are invisible to that cleanup: they lie in the sharedprofiles\node_modulesand 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:
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_modulesfallback". But there is no reader of the flag in the shipped build:DSH_DESKTOP_HOST_RESOLVED— 0 occurrences in the entire runtimeresources\app.asar.unpacked(11 583 code files:js8 592,mjs1 359,cjs445,ts1 187; plus configs:json1 002,yml24,yaml273). As a control, for the remaining substrings of the mechanism in the treeresources\app.asar.unpacked: in the DSH packages (@deepseek-ai\dsh,@deepseek-ai\dsh-app-boot, versions0.1.7-rc.2) — 0;healProfilesModuleFallback,healProfileBundles,healProfilesModuleFallbackLocked,ensureModuleProxy,resolveModuleFallbackEntries,moduleFallbackCurrent— 0 across the whole unpacked treeresources\app.asar.unpacked(the same substrings do exist inside the archive — that is the shell's healing codeout/main/index.js, named in the previous section; "tree" here and below means the unpacked tree only). The only occurrences ofensureSymlink(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 ofmoduleFallback(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 internalconfigTrees,sessionFormatMigrationand "生成的moduleFallback元数据" "分别由镜像打包器、目录生成器和启动器读取方拥有" — that is, the manifest-level fallback metadata in0.1.7-rc.2is 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 theresources\directory (includinghost-module-fallback.mjs): the two occurrences of the flag exist only inapp.asaritself — 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 xon an NSIS installer yields the inner payloadapp-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, sha2560449A35CAC0AA3981844AB8C273C7AFCD3984A97DD58E850E12FEA774432984F(payload 254 380 803 b, layoutresources\app.asar+resources\app.asar.unpacked\); 0.9.2 — 170 753 576 b, sha2564B4A8884F1BCEC1CD16A092E641A680F86F964A70BB9897425285AFD53F8A6A6(layout is the unpacked directoryresources\app\, noapp.asar).resources\app.asar.unpacked\package.jsonresources\app\node_modules\@deepseek-ai\dsh\package.json— existspackage.jsoninsideapp.asaris markedunpackedsize=13421 offset=1066007 unpacked=false)dsh-app-boot\lib\index.js: size / sha256 /healProfilesModuleFallback/ensureSymlink3E26ABB976031117CDF08B365D2185FF12368524C5B92EE5945DA81750D4BEDD/ 0 / 04A869354830A10FCC625C462BE5D44EB6EBB0487AA5D550DA58F4A4CE3016D34/ 4 / 4Additionally, for the 0.10.0 archive (asar header): 6 550 424 b,
headerSize5 470 978, data from 5 470 996, root entriesnode_modules,out,package.json; files 21 214, directories 3 161, unpacked leaves 21 210; innode_modules\@deepseek-ai— 284 directories, all with apackage.json;out\main\index.js— 990 966 b,unpacked=false. The full count of the mechanism in 0.10.0 (0.9.2) indsh-app-boot\lib\index.js:healProfilesModuleFallback0 (4),…Locked0 (2),healProfileBundles0 (0),ensureSymlink0 (4),ensureModuleProxy0 (2),resolveModuleFallbackEntries0 (2),moduleFallbackCurrent0 (3),profiles/node_modules0 (2),moduleFallback0 (10),removeLinkProjections3 (0),LINK_PROJECTION_DIR2 (0).Artifact = installed copy.
resources\app.asarfrom the unpacked installer and from the installed application matched byte for byte (6 550 424 b, sha2562B67920B29C5BEC23CC23CC3F4B1F06875D5EF850A594173FD7B3E16AED50EEF); 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
harness\profiles\web\node_modules\dshmarket\package.jsonthe following currently do not resolve:yaml,zod,react,@deepseek-ai/cordis,@deepseek-ai/dsh(allMODULE_NOT_FOUND). For@deepseek-ai/*the runtime is saved by the hookresources\host-module-fallback.mjs(re-resolution from the host entry point onERR_MODULE_NOT_FOUND), which is why the harness and the profile plugins work. Foryaml,zod,reactthere 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/'andHOST_RETRYABLE = new Set(['ERR_MODULE_NOT_FOUND', 'ERR_PACKAGE_PATH_NOT_EXPORTED']), and the re-resolution runs with the host entry point'sparentURL(50 lines,resources\host-module-fallback.mjs). That is,yaml,zod,reactare 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).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:100is theimportitself, there is no truncation there). The importer is…\dsh-status-strip\lib\index.js;npm run verify:copies→ exit 0.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
$DSH_HOME\profiles\node_moduleslinks are created at startup).$DSH_HOME\profiles\node_modulesthe links still point toresources\app\node_modules\…, while the@deepseek-aitree holds 242 entries against 284 packages in the unpacked tree (47 unpacked ones are missing, 5 links are extra); inharness.logthere is not a single line about rebuilding the tree; in the 0.10.0 build the substringshealProfilesModuleFallbackandensureSymlinkinresources\app.asar.unpacked\node_modules\@deepseek-ai\dsh-app-boot\lib\index.jsare absent, although in 0.9.2 (where the package lies unpacked inresources\app\node_modules\…) they are present — comparing the two builds is the shortest path to reproduction.5. Requests and questions for the vendor
The main question: was the mechanism removed deliberately? In 0.9.2 the rebuild of the
profiles\node_modulestree was in the shipped build (healProfilesModuleFallbackin@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 includefeat(packaging): archive app shell with physical runtime dependenciesandfix(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_RESOLVEDflag 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-fallbackdirectory 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?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-aiscope: 284 links, 0 broken (the unpacked treeresources\app.asar.unpacked\node_modules\@deepseek-aiholds 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 inresources\app\node_modules\…, and theresources\appdirectory 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 markedimplemented. The noteanywhere-labs/dsh-desktop@master:.agents/notes/implemented/architecture/2026-08-15-packaged-profile-fallback.md(HTTP 200, statusimplemented) says literally: "The desktop Host maps its installation anchor fromapp.asar/package.jsontoapp.asar.unpacked/package.jsonbefore callinghealProfilesModuleFallback;" and "Packaging fails before signing if either the root anchor or a required runtime entry is absent". A caveat about location: indataelement/dsh-desktop@mainthis 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 thehealProfilesModuleFallbackcall 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 buildresources\app.asar.unpacked\package.jsonis 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.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.
Separately about packaging (not the cause of the current state, but the question remains): the root
package.jsoninsideapp.asaris not marked unpacked — 0 occurrences ofasarUnpack; 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 thenode_modulesbranch is unpacked. We checked the consumers ourselves: the literalapp.asar.unpackednever occurs in the shipped code (inout/main/index.jsthere is only the templateruntimePackageRoot()→`${appPath}.unpacked`, and its only consumer isbundledRuntimeRoot()),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.unpackedoccurs once —node_modules/node-pty/lib/unixTerminal.js(it substitutes the path tospawn-helper); theasar.unpackedliteral 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 fromdsh\lib\bin.jsto…\node_modules\@deepseek-ai\dsh\package.jsonand 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", statusimplemented) did not reject the 0.10.0 build, or what "root anchor" means in that wording; and along the way confirm thatout\main\index.jsinside the archive is normal.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.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 installeddshCLI). Should such targets be overwritten or left alone? We need an explicit answer, not a guess.A one-line question: what links
dsh-desktop-hmr-fallbacknow 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.)A separate question — top-level links outside the
@deepseek-aiscope. 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 theresources\app.asarheader (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 ofapp.asar.unpacked\node_modules; the presence of the name was checked against theapp.asarheader 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.v0.10.0(commitc12911a9b510add7fb2d7467e85cc9ababbdfca6); the tagv0.10.0-betapoints to the same commit (beta published on 26.09 at 09:33Z, the release at 12:35Z). Onmainafter the release there is exactly one commit —eec5d57e658f63431ab312dae3dd30d9d03c4cd4("fix(release): restore supported summary model (fix(release): restore supported summary model #546)"), unrelated to packaging; there are no0.10.1/0.11tags. The update feed (https://dshdesktop.com/updates/latest/latest.yml) is served by a redirect to a ModelScope mirror.dsh-desktop-hmr-fallbackis still shipped — 3 matches in the archive (one is the header entry"dsh-desktop-hmr-fallback":{"files":{"index.js":{"size":3577,"unpacked":true,…}}}, two are the name in the dependency line"dsh-desktop-hmr-fallback": "file:packages/dsh-desktop-hmr-fallback"of the shell's rootpackage.json, key and value); on disk this isresources\app.asar.unpacked\node_modules\dsh-desktop-hmr-fallback\index.js(3 577 b) and itspackage.json(416 b). At the same timehealProfilesModuleFallbackin the archive has 2 matches, both in the text of the Safe Mode help; there, and likewise only in the help text,ensureSymlink(2) andhealProfilesModuleFallbackLocked(2) also occur, while in the code they are absent;0.1.1-rc.1.patchand@deepseek-ai+dsh+in the archive — 0 matches, but the patch file is not placed into the shipped build (it is a build input), so the archive cannot be used to judge it. Bottom line: the package for which the dependency was declared is present and marked unpacked, while the mechanism that linked it at the start of a new profile has been removed.yaojin3616, association CONTRIBUTOR, merged 22.08.2026T10:16:54Z, "fix: pin the profile's pnpm store, and restyle the update card") — this is a change accepted into upstream, not a maintainer's action. The query slice is named so that the figure does not contradict itself:search/issues?q=repo:dataelement/dsh-desktop+healProfilesModuleFallback→total_count: 1(issues and PRs together) — the match lies precisely in the PR. The declaration is still alive today: inpatches/@deepseek-ai+dsh+0.1.7-rc.2.patchonmain(1 410 b) there is the line+ "dsh-desktop-hmr-fallback": "0.1.0",, whereas the patch name from that PR (patches/@deepseek-ai+dsh+0.1.1-rc.1.patch) no longer exists onmain(404). A caveat about the line: that patch is addressed to the@deepseek-ai+dsh+0.1.1-rc.1line (August), while the removal is recorded on the0.1.7-rc.2base, so the PR proves that the tool was alive, but does not prove that it survived until 0.10.0 — and that is our question. The meaning of the line is "so that at the start of a new profilehealProfilesModuleFallbacklinks this package correctly" (that is how the failure of the Windows smoke test in CI was healed). The nearest relatives (all measured via the API) — and none of them is about removing the mechanism:profiles\web\node_modules\<plugin>on an old generation, and the code is silently served from it; the workaround in the report iscmd /c rmdir <link>+New-Item -ItemType Junction+ restart. If our state reproduces on 0.10.0 as well, this is a regression of [Bug][Windows] Plugin migration freeze keeps profiles/web/node_modules junction on stale generation - sidebar 'Open with' silently loads unpatched client code #391 — that is what it should be called.node_modules/@scope/*remain on old store entries after a version change; you must manuallyrmdir+mklink /J"; it also mentions the dual-home situation ofharnessand~/.dsh.merged: false,mergeable_state: "dirty") —pnpm-runner.mjssilently exited with code 0 when launched through a link; this is a description of the problem, not a confirmed fix.merged: false,mergeable_state: "dirty", the PR's subject is the profile'snode_modulesdirectories, not a list of host names) — the history of the same mechanism: an attempt to add a fallback to the profile'snode_modulesfor ESM resolution via an--importhook. It does not qualify as a support for §3: the fileresources\host-module-fallback.mjsdoes not exist in the public repository (contents/resources→ 404; it is a build directory), and the hook's coverage is proven by the file itself — 50 lines,HOST_PACKAGE_PREFIXandHOST_RETRYABLE— and not by the tracker.app.asarlayout is an 0.10.0 innovation: (measured — issue [Bug] Windows 任务栏中 DSH Desktop 没有名称:主窗口标题被硬编码清空 #575, environment table) in issue [Bug] Windows 任务栏中 DSH Desktop 没有名称:主窗口标题被硬编码清空 #575 (opened 26.09.2026 13:18Z) the environment table records DSH Desktop 0.10.0 and the delivery formresources/app.asar("0.10.0 起由解包目录改为 asar"), separately noting that on 0.8.1/0.8.2 the layout was unpacked.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):
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.ymland…\.agent-presets\tester\agent.cordis.yml. On the first 0.10.0 launch the desktopimported 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 everystart, 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 donealready withoutconverted(log 882–884). The live copy is the block# dsh-desktop legacy presets begin … endinprofiles\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 underdev, 3 undertester).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-aitree (284 packages) and in the archive itself.But there is a proper replacement
@deepseek-ai/dsh-workflow-ptcv0.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). Inptc.patch.yml:119-123the same entry, butwith
disabled: true— here this is a retelling, not a verbatim quote.dsh-tool-workflowremains 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_FOUNDerrors and 2 warningsagent preset tester/dev: workflow-worker-thread … never startedon every start.Fixed on our side 26.09.2026, 22:20; verified by a restart. The replacement
workflow-worker-thread→workflow-ptcwith the sameconfig: { 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-360and: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-392and: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 ready17:32:54.981Z): in this launch there is not a singlenever startedline, not a singleERR_MODULE_NOT_FOUNDabout this package, and 0fallbacklines as well —the legacy transport is closed. The former symptom in the log remains history: all 16 mentions of
workflow-worker-threadare 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.
resources\apppresent, noapp.asarresources\appabsent,app.asar6 550 424 b, sha2562B67920B29C5BEC23CC23CC3F4B1F06875D5EF850A594173FD7B3E16AED50EEFprofiles\node_modules: entries / links / brokenaccepts)…\resources\app\node_modules\accepts— exists@*in the profile tree: entries with apackage.jsonhealProfilesModuleFallback/…LockedensureSymlink/ensureModuleProxy/resolveModuleFallbackEntries/moduleFallbackCurrentremoveLinkProjections/LINK_PROJECTION_DIRheal/projected generationsin the logERR_MODULE_NOT_FOUND/EPERM/symlink/profile inconsistencyWhat this adds to §2:
resources\appwithapp.asar;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);profiles\web\.dsh-module-fallbackdirectory disappeared from the profile home (it was there before the update) — this is the cleanup half from §2 at work; it does not affect theprofiles\node_modulestree itself, all 213 links remained in place;Harness is readyboth times, 0ERR_MODULE_NOT_FOUND, 0EPERM) — 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;resources\app.asar.unpacked\package.jsonis absent, and the rootpackage.jsonin the archive is without theunpackedmarker;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.yamlare 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).