Source-level environment and credential tests prove the individual loaders, but they do not prove that the published launcher runs them before Loader evaluates a shipped profile.
Start the built dsh binary with the shipped base bundle and a test-only LLM probe. Put the endpoint in $DSH_HOME/.env, put the bearer token only in $DSH_HOME/.credentials.yaml, remove inherited DeepSeek overrides, and assert the mock request received both without leaking the token. This covers launch order, profile composition, the adapter, and the credential seam without a real API.
The resolver suite previously exercised only fixture patch lists, leaving
the real shipped windows.cordis.patch.yml and the bundle→windows→user
composition ordering untested on Linux CI. The new cases load a temp
profile whose bundle layers resolve from the real dsh-base/dsh-web-app
packages (app installation anchor), apply the platform layer through the
boot's own composeEntries algorithm with the platform injected, and
assert the win32 danger-full-access roster (eight disables, three
inserts, no warnings on the web profile). A second case pins POSIX
unchanged and the base-only-profile ui-permission no-match warning as
warned-but-harmless, matching the patch header's documented contract.
win32 hosts booting a shipped profile now get pwsh-local as the ctx.bash
executor and tool-pwsh as the shell tool through the base bundle's new
windows.cordis.patch.yml platform layer, injected by the launcher between
the bundle layers and the user layers on win32. bash-sandbox, tool-bash,
permission, and ui-permission are disabled there: the POSIX-only executor
cannot run on Windows, and dsh-permission requires a confining executor.
Overriding the default is a composition decision through the user's
cordis.patch.yml; there is no environment override channel.
apps/cli and dsh-base re-declare dsh-pwsh-local/dsh-tool-pwsh so the
profile module fallback links them for cold starts (the profiles rework
had dropped them from the CLI closure).
Promotes the windows-pwsh-default Agent Note from proposed to implemented
and documents the platform layer in the base bundle README.
The profile rework left references to the old entry modes behind. Renames
the user patch-layer API and its spec file (watchPersonalPatches ->
watchUserPatches, personal-config.spec.ts -> user-patches.spec.ts) and
retargets the prose that still named `config.yaml`, `--config`, raw-config
mode, and surface overlays: repository-plugin and mcp-memory READMEs, the
credentials-local anchor into app-boot, vendor manifest items 12-13, the
vendored include/hmr comments, and install.sh.
Restores the boot-failure guard the rework dropped with raw mode: the
built-bin case now boots `--profile web --patch <invalid>` and asserts the
settled diagnostic and exit 1, so the HMR initial-scan deadlock stays
covered; its orphaned raw fixture is renamed and the unused one deleted.
The superseded personal-config Agent Note and its superseding profile note
are now cross-linked.
A profile manifest and a bundle manifest are different kinds and shared one
flat `dsh` section: `dsh.plugins` listed bundles (not plugins) and `dsh.patch`
declared a bundle's layer. Each kind now names its role — a bundle declares
`dsh.bundle.patch`, a profile declares `dsh.profile.bundles` — so a
package.json states which role it plays and the list name matches its contents.
`DEFAULT_PROFILE_PLUGINS` becomes `DEFAULT_PROFILE_BUNDLES`, and
`DshManifestSection` splits into `DshBundleManifest`/`DshProfileManifest`.
Pre-release: no compatibility shim; turtle-ui moved with it (bd5ff10).
- Relative path specs (., ../plugin, file:/link: forms) anchor to the
invoking directory before forwarding: pnpm's cwd is the profile dir, so a
bare 'add .' from a plugin checkout used to self-link the profile (exit 0,
nothing installed). Bare paths stay bare and prefixed specs keep their
prefix, preserving pnpm's link-vs-copy semantics.
- dsh.plugins reconciles against the INSTALLED state on every successful
pnpm run, not the dependency diff: an update whose new version gains
dsh.patch activates the layer; a version that drops it (or a removal)
deactivates it. Template bundles are never touched.
- A failed pnpm run now names the profile directory, and a git-spec failure
explains pnpm >=10's prepare-script block with a pointer at the profile's
pnpm-workspace.yaml allowBuilds (turtle-ui's prepare-based git install is
the reference consumer); reference README documents all three.
The old $DSH_HOME/config.yaml personal overlay returns under the profile
scheme's filename: machine-local preferences that apply to every profile,
loaded after the profile's own cordis.patch.yml (so the home layer outranks
it) and before --patch overlays and flag patches. Both user layers are
hot-reloaded on long-lived surfaces and shown in --dump-config with their
own provenance labels; the built-bin e2e covers the home layer landing live.
- Clone patch lists per generation (boot + composeLive): the include pushes
insert rows by reference and mutates them in place, so a reused object
baked user overrides into bundle rows and removal could not revert; the
built-bin hot-reload e2e now asserts an override AND its removal reverting.
- The headless runner awaits Loader settlement before prompting (its inject
gate covers only apiProxy/httpServer) and abandons cleanly when the tree
died during the wait.
- healProfilesModuleFallback walks the app's full dependency+peer closure:
out-of-tree plugins import seam packages (dsh-compact, dsh-subprocess, ...)
that only implementations reach, and peers are how seams are declared.
- Profile init writes pnpm-workspace.yaml (nodeLinker: hoisted), not .npmrc
— pnpm >=10 reads settings from the workspace manifest.
- Web dumps reject boot-only flags instead of printing a tree that differs
from the same invocation's boot; --port validates at the flag;
--dump-default-config no longer parses the (possibly broken) user layer;
trustedHosts flag derivation merges over the composed value instead of
replacing it; web-runtime gains surfaceContext (headless disables the GUI
prompt/bash-vars the old -p never mounted); 'node_modules' is a reserved
profile name; plugin-warning names the recovery step; client AGENTS.md
registration surfaces point at the web-app bundle.
- Ship session-reference/tmux-context/tool-ask-user as app dependencies for
terminal front-door patch layers (turtle-ui), same stance as mcp-client.
A custom profile that mounts no telemetry-otel row exports nothing, so
DSH_TELEMETRY_DISABLED must not fail its boot (CI exports the switch
globally, which broke the lifecycle-fixture profile). The web-app dist
resolution test accepts the fail-loud unbuilt outcome the CI coverage lane
sees before any build, and the headless spec covers the idle-anchor and
pre-start skip branches under the per-file gate.
dsh --profile <name> replaces the fixed entry modes: --config and -p are
removed, --patch adds overlays over the composed profile, a positional task
selects one-shot mode (requires the headless-runner row), and dsh web stays as
the alias for --profile web carrying the Web flag family as patches. dsh
plugin --profile <name> forwards verbatim to pnpm in the profile directory,
initializes on first use, and reconciles the dsh.plugins layer list after
add/remove (patch-less packages warn and stay plain dependencies). Config
dumps and the keyless web e2e scaffold compose the same bundle layers over the
same empty root as the boot.