Installing a plugin puts code in the profile's node_modules; it does not add a config row. A plugin loads only when a bundle or a patch layer inserts a row that mounts it. Run dsh --profile <name> --dump-config: if the row is absent you never added it, and if it is present but wrong, a higher patch layer overrode it.
Almost every "my plugin does not work" report in the harness is one of four things, and they are distinguishable in about a minute. The root cause is usually a single wrong assumption:
Step 1 — is it in the composition at all?
dsh --profile web --dump-configThis prints every row that patches can target, after all layers have applied. Search it for your package name.
Not there → the package may be installed, but nothing mounts it. Go to cause A.
There, but configured differently than you wrote → a higher layer overrode you. Go to cause C.
There, and correct, but the capability is missing at runtime → go to cause D.
Cause A — installed, never mounted
The most common case. You ran:
dsh plugin --profile web add some-cordis-plugin...which installed the package and stopped. You still need a row that mounts it, in the profile's
patch file at $DSH_HOME/profiles/web/cordis.patch.yml. A patch targets a row by identifier and
either replaces its whole config or inserts new rows — inserting is what you want here.
Then verify with --dump-config again. Do not skip the verification: this is exactly the class of
change that silently does nothing.
Cause B — installed into a different profile
Plugins install into the profile's own node_modules. That isolation is deliberate — it lets
two profiles hold incompatible plugin sets — but it means:
dsh plugin --profile web add thing # installs into the web profile
dsh --profile headless # cannot see itCheck that the profile you installed into is the profile you are booting. If you want it in both, install it twice.
Also relevant: bundles resolve from the dsh installation first, then from the profile's
node_modules. If a name exists in both places, the installation's copy wins. A local package
intended to shadow a shipped one will not, by name alone — override the shipped row with a patch
instead.
Cause C — a higher layer is overriding you
The layer order, lowest priority first:
- each bundle the profile stacks, in sequence
$DSH_HOME/profiles/<name>/cordis.patch.yml$DSH_HOME/cordis.patch.yml--patchoverlay at runtime
Later wins. The single most common version of this bug: someone sets something in the home-level patch file months ago, forgets, then edits the profile-level file and sees nothing change.
# what a clean profile composes to
dsh --profile web --dump-default-config > /tmp/base.txt
# what you actually boot
dsh --profile web --dump-config > /tmp/mine.txt
diff /tmp/base.txt /tmp/mine.txtThat diff is precisely the set of changes your layers make. If your intended change is not in it, you did not make it. If it is in it but wrong, something later rewrote it.
Cause D — mounted, but the capability has no provider
Some tools need something behind them. The lsp tool, for example, requires a registered LSP
provider — with none, the tool mounts but has nothing to query.
The harness models capabilities as seams: a Service Definition owning ctx.<key>, one or more
Service Providers, and Consumers that inject the service. A consumer with no provider is a coherent
composition that does nothing useful, and nothing will error at boot to tell you so.
If a plugin appears in --dump-config and still does not work, ask what service it consumes and
whether anything provides it.
Install-time failures
If dsh plugin add itself fails, remember it is pnpm underneath, and the error is a pnpm error:
ERR_PNPM_FETCH_404— the package name is wrong or unpublished.- Peer dependency warnings — a plugin built against a different core version. On a v0.1 preview with expected compatibility-breaking changes, take these seriously rather than forcing past them.
- Network or registry errors — your registry config, not the harness.
The full pnpm vocabulary works, so a version specifier pins a plugin to a version that matches your harness:
dsh plugin --profile web add [email protected]A checklist
--dump-config— is the row present?- Did you install into the profile you are booting?
diffagainst--dump-default-config— is your change actually in the delta?- Is there a home-level patch layer you have forgotten about?
- Does the plugin consume a service that nothing provides?
Frequently asked
Does dsh plugin add enable the plugin?
No. It forwards to pnpm and installs the package into that profile's node_modules. Mounting it is a separate step — a config row, inserted by a bundle or by one of your patch layers.
I installed into the wrong profile. How do I tell?
Plugins are per-profile. Check that the --profile you installed with matches the one you are booting; a plugin in the web profile's node_modules is invisible to the headless profile.
Why does my plugin load but with the wrong settings?
A later patch layer replaced its row's config. Layers apply as bundles, then the profile's cordis.patch.yml, then the home-level one, then --patch — and later wins.