refactor(bundle): fold the Windows shell platform layer into the base rows

Entry \disabled\ interpolation makes the launcher's separate platform layer
unnecessary: the base bundle's cordis.patch.yml now gates both shell stacks
on its own rows — bash-sandbox/tool-bash disable on win32, and their twins
pwsh-sandbox/tool-pwsh mount only there with the inverted expression — so
exactly one shell stack mounts per host from one shared patch file.

windows.cordis.patch.yml and the launcher's windows-shell.ts injection (boot,
live recomposition, config dumps) are deleted, with the workspace-constraints
entry and the dsh-base exports/files entries following. The windows-shell spec
pins the effective per-platform roster through the real bundle layers, and
base.spec pins the four symmetric gates. The superseded active notes are
updated and cross-linked; the loader note records the fold itself.
This commit is contained in:
Huanqi Cao
2026-08-11 15:11:45 +08:00
parent f00a29111b
commit 4308f91e88
27 changed files with 173 additions and 283 deletions
@@ -12,9 +12,8 @@ The harness's shipped execution profile is bash-first on every platform. Windows
Windows hosts booting a shipped profile (`dsh web`, `dsh --profile headless`, one-shot tasks) get the PowerShell stack by default; POSIX hosts are unchanged.
- **The platform layer is a data file, not a roster rewrite.** `@deepseek-ai/dsh-base` ships [`windows.cordis.patch.yml`](../../../../packages/bundle/base/windows.cordis.patch.yml) alongside its universal `cordis.patch.yml`: it disables `bash-sandbox`/`tool-bash` (the POSIX-only executor and its dialect tool) and inserts `pwsh-local`/`tool-pwsh`. Windows has no OS sandbox runner (landlock/bwrap/seatbelt are POSIX-only), so the layer drops the sandbox stack entirely — `sandbox`, `sandbox-policy`, and `fs-sandbox` are disabled and the unconfined `dsh-fs-local` provides `ctx.fs` — and degrades to danger-full-access: `permission`/`ui-permission` leave the roster (dsh-permission requires a confining executor — presets bundle a sandbox mode the unconfined executor cannot honor; see its constructor guard — and the client knob would advertise a boundary that does not exist), and the `approval` service is disabled — nothing in the Windows roster asks for approval, so the model is never told approval exists or that asks are auto-rejected. Keeping fs-only path rules would be theater: the unconfined shell can bypass them with one command, so the honest Windows posture is full access rather than a boundary only the fs tools pretend to enforce.
- **The launcher injects the layer by platform.** `apps/cli/src/windows-shell.ts` resolves it from the base bundle layer's `packageDir` between the bundle layers and the user layers on `win32` hosts, in every composition path (boot, config-only HMR recomposition, config dumps). Overriding the shipped default is a composition decision: a Windows host that prefers the bash stack — or confinement — re-enables the bash rows through its profile or home `cordis.patch.yml`. Custom profiles without the base bundle are skipped (they own their shell stack); a base bundle that ships no Windows shell patch fails loud.
- **Module resolution is restored for cold starts.** The profiles-rework CLI dropped the pwsh packages from `apps/cli`'s dependency closure, so `healProfilesModuleFallback` never linked them into `$DSH_HOME/profiles/node_modules` and a fresh Windows host could not resolve the inserted rows. `apps/cli` and `dsh-base` re-declare `dsh-pwsh-local`/`dsh-tool-pwsh`, and `dsh-base` also declares `dsh-fs-local`; the base bundle lists every row plugin as a dependency by house style.
- **The base patch gates both shell stacks on its own rows** (the [loader `disabled` interpolation](../process/2026-08-11-loader-entry-disabled-interpolation.md) note records the mechanism and the platform-layer fold): `bash-sandbox`/`tool-bash` carry `disabled: !!js process.platform === 'win32'` (bash has no Windows runner), and their twins `pwsh-sandbox`/`tool-pwsh` mount only on win32 with the inverted expression — one shared patch file, exactly one shell stack per host. The confined pwsh stack runs over the ACL restricted-token runner, and the permission surface stays exactly as on POSIX (the [Windows ACL restricted-token sandbox](2026-08-08-windows-acl-restricted-token-sandbox.md) note owns that roster). Overriding the shipped default is a composition decision: a Windows host that prefers the bash stack or an unconfined pwsh executor overrides these rows through its profile or home `cordis.patch.yml` (the bash-restore recipe must be complete: disable `pwsh-sandbox`/`tool-pwsh` AND re-enable `bash-sandbox`/`tool-bash` — both executor families register the same `bash` service, so an incomplete recipe fails loud at load) — composition config is the one override channel. The separate `windows.cordis.patch.yml` layer and the launcher's `apps/cli/src/windows-shell.ts` injection are deleted; the layer existed only because entry metadata was static.
- **Module resolution is restored for cold starts.** The profiles-rework CLI dropped the pwsh packages from `apps/cli`'s dependency closure, so `healProfilesModuleFallback` never linked them into `$DSH_HOME/profiles/node_modules` and a fresh Windows host could not resolve the pwsh rows. `apps/cli` and `dsh-base` declare `dsh-pwsh-sandbox`/`dsh-tool-pwsh`; the base bundle lists every row plugin as a dependency by house style.
The pwsh GUI rendering shipped earlier with the [pwsh UI presentation matches bash decision](2026-08-05-pwsh-ui-bash-parity.md); the [pwsh tool bash parity decision](2026-08-02-pwsh-tool-bash-parity.md) ships the tool's surface. Nothing in this decision changes POSIX behavior.
@@ -32,13 +31,12 @@ The pwsh GUI rendering shipped earlier with the [pwsh UI presentation matches ba
## Consequences
- A Windows host running a shipped `dsh` surface gets `pwsh` as its shell tool and PowerShell as the `ctx.bash` executor without configuration; `bash` is absent from the model-visible roster there (its tool row is disabled).
- Windows has no sandbox at all: the fs tools run unconfined (`dsh-fs-local`), the approval service is absent (nothing asks for approval, and the model is never told approval exists), and the permission switcher is gone. The model-visible posture is honest full access rather than a boundary the shell can bypass.
- POSIX hosts are unchanged: the platform layer never applies, and the bash stack remains the universal `cordis.patch.yml` rows.
- Windows hosts that prefer the bash stack (e.g. with WSL/Git-Bash on PATH) override the shipped default through their profile or home `cordis.patch.yml` — disabling `pwsh-local`/`tool-pwsh` and re-enabling `bash-sandbox`/`tool-bash` (both executors register the same `bash` service, so an incomplete recipe fails loud at load) — composition config is the one override channel.
- A Windows host running a shipped `dsh` surface gets the confined `pwsh` as its shell tool and PowerShell as the `ctx.bash` executor without configuration; `bash` is absent from the model-visible roster there (its tool row is disabled).
- POSIX hosts mount the bash stack as before; the pwsh rows sit disabled in their composition, because the one shared patch file lists both stacks and each row gates itself.
- A Windows host that prefers the bash stack (e.g. with WSL/Git-Bash on PATH) overrides the shipped rows through its profile or home `cordis.patch.yml` — disabling `pwsh-sandbox`/`tool-pwsh` and re-enabling `bash-sandbox`/`tool-bash` (both executors register the same `bash` service, so an incomplete recipe fails loud at load) — composition config is the one override channel.
## Verification
- Unit: `apps/cli/tests/windows-shell.spec.ts` pins the win32 default, the custom-profile skip, and the missing-patch failure with the platform injected, and composes the REAL shipped bundle layers (dsh-base + dsh-web-app resolved from the app installation) through the boot's patch algorithm to assert the win32 danger-full-access roster and the base-only-profile warning; `packages/bundle/base/tests/base.spec.ts` pins the shipped Windows patch file shape (disables, inserts, and the absent approval service).
- Keyless: a win32 `dsh --profile <name> --dump-config` shows the pwsh rows with `windows.cordis.patch.yml` provenance and the bash rows disabled; the POSIX dump (CI Linux) is unchanged.
- Unit: `apps/cli/tests/windows-shell.spec.ts` composes the REAL shipped bundle layers (dsh-base + dsh-web-app resolved from the app installation) through the boot's patch algorithm and pins the effective per-platform roster — the win32 pwsh roster, the POSIX bash roster, and the base-only profile — plus the preset-level tool-bash gate and the cold-start resolution closure; `packages/bundle/base/tests/base.spec.ts` pins the four shell rows' symmetric `!!js` platform gates and that no separate platform patch ships.
- Keyless: a `dsh --profile <name> --dump-config` shows both stacks in the one shared patch layer, with each row's own `disabled` expression deciding the roster at mount.
- The real-composition smoke boots the web profile on win32 with the pwsh stack mounted (the exact roster this note describes).