Repository navigation
[Bug] Plugin/profile composition drift only surfaces as a fatal crash on the next boot — needs preflight validation and a dsh profile doctor #6539
Replies: 2 comments 1 reply
|
补充一种与本文三种形态都不同的 drift:不崩溃、静默丢弃(no crash, silent drop)。 环境
现象对同一个插件跑
"dependencies": { "@toustifer/dsh-agentflow": "file:C:/Users/***/.dsh/plugins/agentflow/dsh-agentflow" }
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-harvest"]
结果:插件完全不进启动树(MCP 工具、技能、agent preset 全缺),但用户看到的是"安装成功"。再跑一次完全相同的 复现性不稳定:失败一次后,用完全相同的 remove→add 再跑一次未复现。怀疑是 pnpm 刚 materialize 包目录那一刻的时序/竞态。 代码路径
function exportsPatch(packageName, profileDir) {
let dir
try { dir = resolveBundleDir(NAME, packageName, INSTALL_ANCHOR, profileDir) }
catch { return false } // ← 解析失败被当成"没声明 dsh.bundle"
return readProfileManifest(NAME, dir).dsh?.bundle?.patch !== void 0
}若这一刻解析返回 false:① 不追加;② 第二个循环还会把同名项从 与本文三种形态的关系
建议的不变量(供 preflight /
|
|
A concrete case of form 2 that reaches end users, not only plugin developers: the published npm plugin and
Environment: Linux x86_64, Node v24.21.0, |
Uh oh!
There was an error while loading. Please reload this page.
Summary
A
dsh webprofile only boots if several mutable files stay in sync: the profilepackage.json(dependencies plus thedsh.profile.bundleslist), the composition layers (cordis.yml, usercordis.patch.yml), and — for host-side injector setups — separate plugin registries. Renames, source-directory moves, partial installs, home-directory migrations, and deleted-but-still-declared bundles desync these silently. Nothing checks that state until the next boot, where it becomes a fatal crash:That message is actionable (genuinely good!), but it arrives as a full startup failure — under a service manager this is a crash loop, discovered while unattended.
Incident forms we hit in ~3 weeks of heavy plugin development
dsh.profile.bundleswhose dependency was never installed, or whoselink:source directory was moved/deleted. The crash happens on a later restart, long after the edit that caused it.package.jsonwhosedsh.bundle.patchdeclaration was missing, pointed at a missing file, or was not a top-level array. Same deferred-crash shape.package.jsonname changed (scope rename), but the bundles list still referenced the old name; the plugin then loads without registering, and injector registries keep stale entries pointing at directories that no longer exist.We now run a custom static "doctor" script (~300 lines) that replicates
resolveBundleDir()'s resolution chain and reports dangling links, name mismatches, and stale registry entries on a schedule. It works — which is the point: the checks are mechanical and belong upstream.Asks
dsh profile doctor [--profile <name>]— validate without booting: everybundlesentry resolvable from the install anchor or profile dir;link:targets exist;dsh.bundle.patchdeclarations well-formed; dangling symlinks in the profile tree; stale third-party registry entries. Report-only by default.dsh web --dry-run): run composition + resolution and exit 0/1, so service managers and CI can smoke-test a profile right after any change.Related: #6459 covers a different layer of the same theme (multiple installations flipping the shared
profiles/node_modulesfallback tree); this post is about single-install composition integrity.Environment
macOS 15.5 arm64; dsh 0.1.x across 0.1.1-rc.1 → 0.1.5-rc.1; a web profile with ~18 bundles, mostly local
link:plugins under active development.All reactions