dsh-plugin-wsl-env 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,160 @@ were part of the record.
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [0.13.0] - 2026-10-19
20
+
21
+ This release ships the P0/P1 hardening batch: session-scoped `auto`
22
+ sandboxing, resident-agent recovery with doctor coverage, the packed-install
23
+ E2E, measured GUI-preview and `@`-reference probes, and the docs/community
24
+ entry points.
25
+
26
+ ### Added
27
+
28
+ - **`pnpm run preset` — the `preset-wsl` override helper.** `wsl-shell` and
29
+ `wsl-fs` sit inside `preset-wsl`'s `config.plugins`, so a patch that targets
30
+ them by id is ignored with only a loader warning. The helper prints the
31
+ complete `preset-wsl` block, applies `distro`/`sessionSandbox`/`sandbox`/
32
+ `maskWindowsDrive`/`agentBackend`, and `--check` fails loudly with the exact
33
+ block to paste when a layer carries the nested id patch. See
34
+ [docs/CONFIGURATION.md](docs/CONFIGURATION.md#overriding-the-rows-inside-preset-wsl).
35
+
36
+ - **Session-notice upstream payload ready.** The exact title, body and `gh`
37
+ command for the session-visible notice proposal are in
38
+ `docs/upstream/proposal-session-notice.md`; it remains unfiled (a maintainer
39
+ action).
40
+
41
+ - **Configuration stability and migration policy.** `docs/MIGRATION.md` records
42
+ the below-1.0 freeze (new keys preserve behavior, removals refuse at boot with
43
+ a migration, meaning changes get a note), the deprecation path with the
44
+ `substrate: "share"` and `distro: ''` cases, and the release-gate table with
45
+ the manual legs (probe suite, clean machine, aarch64) marked unrun.
46
+ RELEASING and SUPPORT link it.
47
+
48
+ - **Upstream promotion pack documented.** `docs/upstream/promotion-pack.md`
49
+ records the `packages/wsl/*` layout (ten capability packages plus an
50
+ integration package), the seam/RFC index, what the composition boot test
51
+ covers and its gaps, and the install proof. Promotion stays packaging and
52
+ proof, not a dependency.
53
+
54
+ - **`@`-reference traversal cost measured; seam deferred.** The Windows walk
55
+ over the WSL share is ~56× the ext4 walk on a 520-entry tree (84.7 ms vs
56
+ 1.5 ms) and is now reproducible with `pnpm run probe:file-reference`.
57
+ `docs/UPSTREAM-FILE-REFERENCE-SEAM.md` records the three-function seam, the
58
+ ready (unwired) plugin consumer, and the decision to keep the 9p walk until
59
+ upstream exposes the funnel.
60
+
61
+ - **Community loop: issue forms, triage targets and a contributor path.**
62
+ Feature-request and setup-report forms (the setup form requires
63
+ `doctor --json`), a chooser that disables blank issues, SUPPORT's P1
64
+ three-business-day target and two-to-four-week cadence, and a
65
+ `good first issue` path in CONTRIBUTING.
66
+
67
+ - **Quickstart and performance entry pages.** `docs/QUICKSTART.md` is the
68
+ five-minute path (prerequisites, install, first session, Node/Python/Rust/Go
69
+ and monorepo recipes, honest Tier 1/Tier 2 table); `docs/PERFORMANCE.md`
70
+ records the 9p-vs-agent numbers and the GUI-preview baseline with reproduction
71
+ commands. The visual demo is explicitly still missing.
72
+
73
+ - **GUI-preview latency has a measured baseline.** `pnpm run probe:gui-preview`
74
+ drives the routed backend's `stat`, `listDir` and 1 MiB `readByteRange` with
75
+ p50/p95 budgets (stat 100 ms, listing 500 ms, page 1 s/MiB). On this machine:
76
+ p95 2.7 ms / 5.1 ms / 63.5 ms per MiB. No optimization follows; the
77
+ `workspace-files` row is untouched.
78
+
79
+ - **LSP seam researched and deferred.** `docs/UPSTREAM-LSP-SEAM.md` records that
80
+ the harness ships `dsh-lsp`/`dsh-lsp-stdio`/`dsh-tool-lsp` but the 0.2.1-rc.2
81
+ base composition mounts none of them, and that a WSL provider needs a
82
+ preset-scoped subprocess provider plus URI translation. The prototype is
83
+ deferred behind an explicit trigger; no LSP claim is made in the README.
84
+
85
+ - **Peer-upgrade canary.** A weekly workflow resolves the `next` dist-tag of
86
+ every pinned peer, tests that line in a scratch copy (`pnpm test` + the
87
+ composition boot test), and publishes the exact version delta in the run
88
+ summary; a moved line that fails fails the run. `pnpm run canary:peers`
89
+ performs the check locally.
90
+
91
+ - **The strict profile is documented and probed.** `maskWindowsDrive: true` with
92
+ `sandbox: true` hides `/mnt` from confined profiles; CONFIGURATION describes
93
+ the trade-offs and the visible/masked/disabled interop matrix, and
94
+ `probe:sandbox` now asserts the masked leg (empty `/mnt`, unreachable Windows
95
+ binary, writable workspace). Enforcement stays `partial`; the binfmt content
96
+ escape and the distro-level closure are stated.
97
+
98
+ - **Install/upgrade/uninstall E2E.** `pnpm run e2e:install` packs the tree,
99
+ installs it into a fresh `%TEMP%` `DSH_HOME`, composes `--dump-config`, removes
100
+ it, and upgrades from the previous minor with a marker proving the profile
101
+ layer survived. `test/packaging.test.mjs` keeps the packed file list honest in
102
+ `pnpm test`.
103
+
104
+ - **The doctor probes the resident agent.** `agent.handshake` starts the
105
+ shipped shell agent in the distro, reads its HELLO (pid, protocol, script
106
+ digest) and stops it, so a missing or stale agent script is a named check
107
+ instead of a first-command failure. It also reports the agent cache and the
108
+ live/stale `dsh-wsl-*.scope` counts.
109
+
110
+ - **`pnpm run doctor` — one command for the environment prerequisites.** Reports
111
+ Windows/WSL versions, the distro and its login shell, bubblewrap usability,
112
+ systemd / the user manager / `systemd-run` / Linger, the optional tools, peer
113
+ versions, the native-agent cache, stale `dsh-wsl-*.scope` units and the
114
+ profile-patch trap that silently ignores `wsl-shell`/`wsl-fs` overrides. Each
115
+ failure carries one copy-paste remedy; `--json` is the machine shape, and
116
+ `--fix` only sweeps stale scopes unless `--install` plus a confirmation allows
117
+ a package install. `diagnose` is now the read-only alias.
118
+
119
+ ### Changed
120
+
121
+ - **`sessionSandbox: auto` — the session runtime is an optimization, not a
122
+ prerequisite.** `wsl-shell` and `wsl-fs` accept `true`, `false` or `"auto"`;
123
+ `auto` uses the systemd user scope when it is available and otherwise falls
124
+ back to the per-command `bwrap` profile — still confined, never unconfined —
125
+ reporting `sessionScope: "session" | "per-command"` in the command result.
126
+ A failed verdict is re-probed at most once a minute, so installing systemd or
127
+ enabling Linger upgrades without an app restart. `true` keeps refusing with
128
+ the existing remedy; `false` is unchanged.
129
+
130
+ - **The session preflight also checks the session-runtime prerequisites.** It
131
+ now probes systemd as PID 1, the user manager, `systemd-run` and Linger —
132
+ not only `bubblewrap` — when a distro session opens, and publishes the same
133
+ advisory to the session's runtime context as well as the log. `sessionSandbox:
134
+ false` on the `wsl-preflight` row skips the session half. DSH 0.2.1 has no
135
+ session-notice seam; the ask is recorded in
136
+ [docs/UPSTREAM-SESSION-NOTICE.md](docs/UPSTREAM-SESSION-NOTICE.md) and the
137
+ boundary in [docs/LIMITATIONS.md](docs/LIMITATIONS.md#session-start-advisories).
138
+ Advisory only: nothing is installed and fail-closed behavior is unchanged.
139
+
140
+ - **The distro follows the workspace.** `distro: ''`/`'auto'` now selects the
141
+ distro from the request workdir or target UNC; `'default'` forces WSL's
142
+ default, and an explicit name still pins. Shell, files, root-plane routing,
143
+ the GUI terminal and `shell-env` all use the same rule, and the shared agent,
144
+ substrate, home and session-runtime state are cached per distro. A pinned
145
+ profile still refuses a foreign UNC with `FS_OUTSIDE_DISTRO`. This changes the
146
+ previous meaning of `distro: ''` (WSL's default): set `'default'` to keep that
147
+ posture.
148
+
149
+ ### Fixed
150
+
151
+ - **A dead resident revives with bounded backoff instead of staying dead for
152
+ the process lifetime.** A failure that spends the one permitted rebuild now
153
+ leaves the agent `dead` with a revival scheduled (2 s, 4 s, 8 s); the next
154
+ call after the delay gets a fresh transport and its own single rebuild, and
155
+ nothing from the dead generation is replayed. After three deaths the state is
156
+ permanent and the refusal names the restart remedy. `status()` exposes the
157
+ live state, pid, last error and pending count for diagnostics.
158
+
159
+ - **A cancelled command's signal can no longer kill the next one.** The
160
+ out-of-band USR1 carries no request id: a signal meant for a timed-out
161
+ command could land after the agent had moved on, and the next command then
162
+ died with exit 138 but reported `completed`. A host timeout no longer sends
163
+ the signal at all (the in-distro watcher is the one that kills that
164
+ command), and an explicit cancel now waits briefly and is dropped once the
165
+ request answered or a newer request is running.
166
+
167
+ - **A missing workdir is reported with its path on the native agent.** The
168
+ native agent reports the failed spawn as reason `spawn`, without naming the
169
+ directory; the executor now treats that as its own error class and retries
170
+ the command on the one-shot path, whose `chdir(<path>) failed` warning names
171
+ the directory.
172
+
19
173
  ## [0.12.0] - 2026-10-19
20
174
 
21
175
  The GUI file service keeps the shipped `workspace-files` row: its server-only
package/CONTRIBUTING.md CHANGED
@@ -172,11 +172,14 @@ terminal if you would rather not see the prompt.
172
172
 
173
173
  | Command | Covers | Needs a harness |
174
174
  |---|---|---|
175
- | `pnpm test` | style checks, syntax pass, build consistency, type check, and unit tests | no |
175
+ | `pnpm test` | style checks, relative links, syntax pass, build consistency, type check, and unit tests | no |
176
+ | `pnpm run lint:links` | relative Markdown links in README, CONTRIBUTING, AGENTS and `docs/**` | no |
176
177
  | `pnpm run test:coverage` | unit tests with coverage | no |
177
178
  | `pnpm run lint:style` | style and lint rules | no |
178
179
  | `pnpm run lint:build` | the build-freshness check: every source has its artifacts and the committed bytes match a real build | no |
179
180
  | `pnpm run lint:types` | the TypeScript check over `test/**` and the ambient types (`tsc --noEmit`) | no |
181
+ | `pnpm run e2e:install` | packed-tarball install/upgrade/uninstall into a temporary `DSH_HOME` | yes; Windows + WSL |
182
+ | `pnpm run doctor` | the environment prerequisites, peers and caches (`--json` for scripts; `diagnose` is the read-only alias) | no; inside the distro |
180
183
  | `pnpm run probe` | filesystem behaviour against a real distro | yes |
181
184
  | `pnpm run probe:sandbox` | what bubblewrap confines and what it does not | no |
182
185
  | `pnpm run probe:sandbox-shell` | the confined executor through a real boot | yes |
@@ -187,11 +190,14 @@ terminal if you would rather not see the prompt.
187
190
  | `pnpm run probe:exec` | the agent-backed execution handle: timeout, kill, cwd failure | no; inside the distro |
188
191
  | `pnpm run probe:missing-wsl` | how a `wslPath` that cannot start is reported | yes |
189
192
  | `pnpm run probe:picker` | the picker's root level, its refusals and its cap | yes |
193
+ | `pnpm run probe:gui-preview` | the routed backend's stat/listing/1 MiB page-read latency (p50/p95 budgets) | no; inside the distro |
194
+ | `pnpm run probe:file-reference` | the `@`-reference traversal: 9p walk vs the distro's ext4 | no; inside the distro |
195
+ | `pnpm run probe:fs-routing` | the root-plane router: distro coordinate, host plane, cross-distro fence | yes |
190
196
  | `pnpm run probe:mode` | which POSIX-mode facts survive the share | no; Windows Node |
191
197
  | `pnpm run probe:sandbox-off` | the documented opt-out on both providers | yes |
192
198
 
193
199
  Every script above is wired in `package.json`. `pnpm test` runs `lint:style`,
194
- `test:syntax`, `lint:build`, `lint:types` and `test:unit` in that order.
200
+ `lint:links`, `test:syntax`, `lint:build`, `lint:types` and `test:unit` in that order.
195
201
  `prepublishOnly` runs `pnpm test` again at publish time, so a broken gate stops a
196
202
  release before the upload.
197
203
 
@@ -253,10 +259,10 @@ must skip itself — print a `SKIP` line and exit 0 — the way that file does o
253
259
 
254
260
  Probes are shell scripts, plus the small plugin files they load: the `*-probe.mjs`
255
261
  and `*-probe.yml` files in the same directory. Two shapes. The harness probes
256
- (`run.sh`, `terminal.sh`, `sandbox-shell.sh`, `picker.sh`, `missing-wsl.sh`,
257
- `sandbox-off.sh`) boot a throwaway profile whose setup steps are in the header of
258
- `test/probe/run.sh`; the five others reuse the profile `run.sh` creates rather
259
- than making their own. The distro probes (`substrate.sh`, `watch.sh`, `agent.sh`,
262
+ (`run.sh`, `terminal.sh`, `sandbox-shell.sh`, `picker.sh`, `fs-routing.sh`,
263
+ `missing-wsl.sh`, `sandbox-off.sh`) boot a throwaway profile whose setup steps
264
+ are in the header of `test/probe/run.sh`; the six others reuse the profile
265
+ `run.sh` creates rather than making their own. The distro probes (`substrate.sh`, `watch.sh`, `agent.sh`,
260
266
  `exec.sh`) boot nothing: they run from a distro terminal and drive the resident
261
267
  agent through the `wsl.exe` interop transport. `sandbox.sh` and `mode.sh` sit
262
268
  outside both groups — no harness boot, no profile.
@@ -311,6 +317,22 @@ assertions; 15 shell-probe checks; 9 picker checks; 6 missing-executable checks;
311
317
  5 mode checks; 5 opt-out checks and 10 sandbox expectations plus the recorded
312
318
  escape; and 3 terminal assertions.
313
319
 
320
+ ## Good first issues and the contributor path
321
+
322
+ The repository labels self-contained changes `good first issue`. Typical shapes:
323
+
324
+ - a recipe or troubleshooting row in `README.md` or `docs/QUICKSTART.md`
325
+ (the link check gates it; one commit);
326
+ - a probe report fixture under `test/probe/` that follows an existing
327
+ `*-report.txt` shape;
328
+ - an issue-template wording fix, a cross-reference, or a missing error-code row
329
+ in `docs/ARCHITECTURE.md`.
330
+
331
+ To claim one, comment on the issue. Before opening a PR run `pnpm test` (and
332
+ `pnpm run test:coverage` for a `lib/` change), then make one logical commit whose
333
+ body records the check results — the conventions below. Probes need a real
334
+ Windows + WSL machine; unit tests, docs and packaging changes do not.
335
+
314
336
  ## Commit and pull request conventions
315
337
 
316
338
  - Write an imperative subject line: `Fix the npm readme`, not `Fixed` or `Fixes`.
package/README.md CHANGED
@@ -12,6 +12,8 @@ The harness process stays on Windows. Commands, file operations, the folder pick
12
12
 
13
13
  Windows + WSL 2 only.
14
14
 
15
+ **New here?** Start with [docs/QUICKSTART.md](docs/QUICKSTART.md) — five minutes, prerequisites, recipes and the honest compatibility table.
16
+
15
17
  ## Why this plugin
16
18
 
17
19
  A Windows coding tool that works on WSL projects has three options: run the whole tool inside the distro, connect to a helper server inside the distro, or let the plugin provide the distro environment. This plugin is the third option. One workflow covers both kinds of session, with the distro's real tools and the command sandbox intact.
@@ -72,7 +74,7 @@ Install `bubblewrap` before the first command:
72
74
  wsl.exe -d <distro> -u root -- apt-get install -y bubblewrap
73
75
  ```
74
76
 
75
- From a checkout, `pnpm run bootstrap -- <distro>` checks the four distro tools and prints the install command for the distro's package manager. Add `--install` to run it.
77
+ From a checkout, `pnpm run doctor` is the one-command environment check: Windows/WSL versions, the distro and its shell, bubblewrap usability, systemd / the user manager / Linger, the optional tools, peer versions, the agent cache, a fresh-agent handshake, stale session scopes and the known profile-patch trap. Each failure prints one copy-paste remedy; `--json` is for scripts, and `--fix` sweeps stale scopes (package installation needs `--fix --install` and an explicit confirmation). `pnpm run diagnose` is its read-only alias. `pnpm run bootstrap -- <distro>` remains the narrower tool-install helper.
76
78
 
77
79
  Upgrade by running the `add` command again. Uninstall with `dsh plugin --profile wsl remove dsh-plugin-wsl-env`; the agent cache under `~/.cache/dsh-wsl` can then be deleted. If you added a local `workspace-files-wsl` workaround to your profile to restore previews, remove it after upgrading: the plugin no longer mounts that row.
78
80
 
@@ -80,6 +82,7 @@ Upgrade by running the `add` command again. Uninstall with `dsh plugin --profile
80
82
 
81
83
  Open a distro folder as the workspace, for example `\\wsl.localhost\ubuntu\home\you\project`.
82
84
 
85
+ - Opening a folder in any distro runs there, even when it is not WSL's default.
83
86
  - Commands run in the distro's login shell. `/home/you/x` and `\\wsl.localhost\ubuntu\home\you\x` are the same file.
84
87
  - **New terminal** opens a shell in the same distro and directory.
85
88
  - The model sees listening ports and can return the URL of a dev server. WSL 2 forwards localhost to Windows.
@@ -91,18 +94,18 @@ Settings live on named rows in `$DSH_HOME/profiles/<name>/cordis.patch.yml`. The
91
94
 
92
95
  | Row | Key | Default | Purpose |
93
96
  |---|---|---|---|
94
- | `wsl-shell` | `distro` | `''` | distro to use; empty means the WSL default |
97
+ | `wsl-shell` | `distro` | `''` | distro mode; empty/`auto` follows the workspace, `default` forces WSL's default |
95
98
  | | `sandbox` | `true` | confine commands with `bubblewrap` |
96
99
  | | `sessionSandbox` | `true` | run the session's commands in one systemd user scope |
97
100
  | | `agentBackend` | `auto` | `auto`, `native` or `shell` for the in-distro agent |
98
101
  | | `maskWindowsDrive` | `false` | hide `/mnt` from confined commands |
99
- | `wsl-fs` | `distro` | `''` | distro for file operations |
100
- | | `restrictToDistro` | `true` | refuse paths that belong to another distro |
102
+ | `wsl-fs` | `distro` | `''` | distro mode; empty/`auto` follows each target path |
103
+ | | `restrictToDistro` | `true` | under a fixed distro, refuse paths that belong to another distro |
101
104
  | | `sandbox` | `true` | check confined writes against the policy |
102
105
  | | `sessionSandbox` | `true` | run confined writes in the session runtime |
103
106
  | | `substrate` | `agent` | in-distro agent; `share` is rejected at startup |
104
107
  | `directory-picker-wsl` | `includeHostHome` | `true` | also list the Windows home directory |
105
- | `subprocess-wsl` | `distro` | `''` | distro used by the GUI terminal |
108
+ | `subprocess-wsl` | `distro` | `''` | distro mode; empty/`auto` follows the launch directory |
106
109
 
107
110
  `wsl-shell` and `wsl-fs` are inside the shipped `wsl` preset, so overriding them by id does not work. Override the whole `preset-wsl` row instead; [docs/CONFIGURATION.md](docs/CONFIGURATION.md) has the recipe and the remaining keys. [`cordis.patch.yml`](cordis.patch.yml) is the commented reference for every shipped value.
108
111
 
@@ -145,9 +148,10 @@ Enforcement is reported as `partial`. A command inside the distro can still star
145
148
  | Symptom | Fix |
146
149
  |---|---|
147
150
  | every command reports `SANDBOX_UNAVAILABLE` | install `bubblewrap` in the distro, or set `sandbox: false` |
148
- | the session refuses before the first command | enable systemd and linger, or set `sessionSandbox: false` on `wsl-shell` and `wsl-fs` |
151
+ | the session refuses before the first command | run `pnpm run doctor`; enable systemd and linger, or set `sessionSandbox: false` on `wsl-shell` and `wsl-fs` |
149
152
  | `glob` and `grep` are slow in the distro | install `ripgrep` with `pnpm run bootstrap -- <distro> --install` |
150
153
  | `dsh plugin add` prints nothing | the plugin is already installed; this is success |
154
+ | a profile patch for `wsl-shell`/`wsl-fs` changes nothing | those rows are nested inside `preset-wsl`. Run `pnpm run preset -- --check <patch>`; it fails with the complete block to paste |
151
155
  | changes under `lib/` have no effect | run `pnpm run sync:windows`, then restart the app |
152
156
 
153
157
  More error codes and refusals are in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md#error-codes). Everything the plugin does not do is in [docs/LIMITATIONS.md](docs/LIMITATIONS.md).
@@ -170,10 +174,13 @@ Real-machine probes run on Windows + WSL only. See [CONTRIBUTING.md](CONTRIBUTIN
170
174
 
171
175
  | Document | Content |
172
176
  |---|---|
177
+ | [docs/QUICKSTART.md](docs/QUICKSTART.md) | five-minute first run, recipes, compatibility |
178
+ | [docs/PERFORMANCE.md](docs/PERFORMANCE.md) | ext4 agent vs 9p, measured latency, how to reproduce |
173
179
  | [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | every configuration key and how to override it |
174
180
  | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | services, path routing, sandbox, error codes |
175
181
  | [docs/LIMITATIONS.md](docs/LIMITATIONS.md) | what the plugin does not do |
176
182
  | [docs/ROADMAP.md](docs/ROADMAP.md) | current plan and deferred work |
183
+ | [docs/ROADMAP-1.0.md](docs/ROADMAP-1.0.md) | the 1.0 and ecosystem plan |
177
184
  | [docs/VALUE.zh.md](docs/VALUE.zh.md) | value and trade-offs in Chinese |
178
185
  | [CONTRIBUTING.md](CONTRIBUTING.md) | development loop, gates, probes |
179
186
  | [SECURITY.md](SECURITY.md) | private vulnerability reporting |
package/SECURITY.md CHANGED
@@ -88,8 +88,18 @@ This is a documented design boundary, not a vulnerability by itself. The command
88
88
  distro, where `bubblewrap` and `node` must be installed; the README states it in
89
89
  its Sandbox section.
90
90
 
91
+ The strict profile (`maskWindowsDrive: true` with `sandbox: true`) narrows this:
92
+ confined profiles shadow `/mnt` with an empty tmpfs, so the Windows executables
93
+ cannot be launched from there. It does not close the hole — a Windows executable
94
+ written into the workspace still runs, because WSL's `binfmt_misc` dispatches on
95
+ content, not location — so the reported enforcement stays `partial`. The complete
96
+ closure is distro-level (`[interop] enabled=false` in `/etc/wsl.conf`). The three
97
+ states and their evidence are tabulated in
98
+ [docs/CONFIGURATION.md](docs/CONFIGURATION.md#the-strict-profile).
99
+
91
100
  A report is still welcome if you find an escape that does not depend on interop,
92
- or if the plugin ever reports `full` while interop is reachable.
101
+ if the masked profile is bypassable from `/mnt`, or if the plugin ever reports
102
+ `full` while interop is reachable.
93
103
 
94
104
  ## Credentials
95
105
 
package/SUPPORT.md CHANGED
@@ -7,6 +7,12 @@
7
7
  | Operating system | Windows 10 and Windows 11 |
8
8
  | Linux side | WSL2 with at least one distro, and systemd as PID 1 with a lingering user manager (`loginctl enable-linger`) for the shipped session runtime. Set `sessionSandbox: false` on the preset's `wsl-shell` to keep the per-command sandbox instead |
9
9
  | DSH packages | the `0.2.1-rc.2`-era packages listed in `peerDependencies` |
10
+
11
+ The DSH row is checked weekly: `.github/workflows/peer-canary.yml` resolves the
12
+ `next` dist-tag of every pinned peer, installs that line into a scratch copy and
13
+ runs `pnpm test` plus the composition boot test. A successful run prints the
14
+ exact version delta in its summary; update the row above from that summary. A
15
+ red run means the plugin has not been validated against the new line yet.
10
16
  | Plugin version | the latest published version only |
11
17
  | Node | 22.19+ (`^22.19.0`), or 24 and later (`>=24.0.0`) |
12
18
 
@@ -22,9 +28,14 @@ supported configuration.
22
28
 
23
29
  Open an issue in the repository:
24
30
 
25
- https://github.com/Big-Dao/dsh-plugin-wsl-env/issues
31
+ https://github.com/Big-Dao/dsh-plugin-wsl-env/issues/new/choose
32
+
33
+ The chooser offers a **feature request** and a **setup report**; the setup
34
+ report requires the `doctor --json` output, which is what makes a first-run
35
+ problem diagnosable without a round trip. For a security issue, use the private
36
+ advisory link instead (see [SECURITY.md](SECURITY.md)).
26
37
 
27
- Use it for bug reports and for questions about configuration. Search the open and
38
+ Use issues for bug reports and for questions about configuration. Search the open and
28
39
  the closed issues first, and check whether the answer is already written down:
29
40
  [docs/LIMITATIONS.md](docs/LIMITATIONS.md) lists the known design boundaries, and
30
41
  the Troubleshooting table in [README.md](README.md#troubleshooting) covers the
@@ -56,6 +67,22 @@ loudly instead of quietly.
56
67
 
57
68
  There is no long-term support branch. Fixes go to the latest version.
58
69
 
70
+ ## Triage and release cadence
71
+
72
+ - **Roadmap.** The public plan is [docs/ROADMAP-1.0.md](docs/ROADMAP-1.0.md);
73
+ the open issues #5–#22 map to its WSL-1xx/2xx/3xx blocks.
74
+ - **Triage target.** A `priority:P1` report gets its first response within three
75
+ business days, best effort. Security reports follow the response targets in
76
+ [SECURITY.md](SECURITY.md).
77
+ - **Cadence.** Releases ship when there is a change worth shipping, roughly every
78
+ two to four weeks; the tag workflow and its gates are in
79
+ [docs/RELEASING.md](docs/RELEASING.md), and the configuration-stability and
80
+ migration rules are in [docs/MIGRATION.md](docs/MIGRATION.md). There is no
81
+ long-term support branch.
82
+ - **Good first issues.** The `good first issue` label marks changes that need no
83
+ prior context; [CONTRIBUTING.md](CONTRIBUTING.md#good-first-issues-and-the-contributor-path)
84
+ lists the usual shapes and how to claim one.
85
+
59
86
  ## What a good bug report contains
60
87
 
61
88
  The one-command path: `pnpm run diagnose` (from a checkout of the plugin, or
package/cordis.patch.yml CHANGED
@@ -66,6 +66,8 @@
66
66
  - id: subprocess-wsl
67
67
  name: 'dsh-plugin-wsl-env/subprocess'
68
68
  config:
69
+ # auto: follow the launch directory's UNC distro; "default" forces WSL's
70
+ # default, and a name pins. Empty is auto.
69
71
  distro: ''
70
72
 
71
73
  # ═════════════════════════════════════════════════════════════════════════════
@@ -135,6 +137,8 @@
135
137
  - id: fs-routing
136
138
  name: 'dsh-plugin-wsl-env/fs-routing'
137
139
  config:
140
+ # auto: route each UNC target to its own distro; "default" forces WSL's
141
+ # default, and a name pins. Empty is auto.
138
142
  distro: ''
139
143
  wslPath: wsl.exe
140
144
  sandbox: true
@@ -223,6 +227,8 @@
223
227
  - id: wsl-shell
224
228
  name: 'dsh-plugin-wsl-env'
225
229
  config:
230
+ # auto: follow the request workdir's UNC distro; "default"
231
+ # forces WSL's default, and a name pins. Empty is auto.
226
232
  distro: ''
227
233
  loginShell: true
228
234
  timeoutMs: 120000
@@ -237,14 +243,15 @@
237
243
  - id: wsl-fs
238
244
  name: 'dsh-plugin-wsl-env/fs'
239
245
  config:
246
+ # auto: follow each target path's UNC distro; "default"
247
+ # forces WSL's default, and a name pins. Empty is auto.
240
248
  distro: ''
241
249
  restrictToDistro: true
242
250
  # agent (the default): reads/writes/identities run inside the
243
251
  # distro over the resident agent (lib/fs-substrate.js) —
244
252
  # native symlinks and mode bits, the write guard survives to
245
253
  # publication, and confined mutations are kernel-enforced.
246
- # share: the opt-out — the Windows-side host fs stack over
247
- # the 9p share.
254
+ # "share" is retired; a profile carrying it is refused at boot.
248
255
  substrate: agent
249
256
  # Confined writes ride the session runtime (ADR 0001), so they
250
257
  # share the command's real /tmp and workspace mounts.
@@ -66,7 +66,7 @@ functions.
66
66
  |---|---|---|---|
67
67
  | `dsh-plugin-wsl-env/picker` | `WslDirectoryPicker` | [`lib/picker.js`](../lib/picker.js) | Adds the installed distros to the folder picker, next to the Windows home directory, and lists distro levels from inside the distro (the resident's `ls`, one-shot `wsl.exe` when it is out). |
68
68
  | `dsh-plugin-wsl-env/auto-preset` | `auto-preset` | [`lib/auto-preset.js`](../lib/auto-preset.js) | Binds the `wsl` agent preset when a new session's workspace is inside a distro. |
69
- | `dsh-plugin-wsl-env/shell-env` | `wsl-shell-env` | [`lib/shell-env.js`](../lib/shell-env.js) | Contributes `DSH_WSL_DISTRO`, `DSH_WSL_SHELL`, `DSH_WSL_HOME` and `DSH_WSL_PORTS` to the managed `DSH_*` namespace. The port list is a snapshot, refreshed every `portsRefreshMs` (10000 by default), so a server started moments ago may not be listed yet. |
69
+ | `dsh-plugin-wsl-env/shell-env` | `wsl-shell-env` | [`lib/shell-env.js`](../lib/shell-env.js) | Contributes `DSH_WSL_DISTRO`, `DSH_WSL_SHELL`, `DSH_WSL_HOME` and `DSH_WSL_PORTS` to the managed `DSH_*` namespace, choosing the session workspace's distro (default/pinned facts are probed at mount; other distros are warmed when their session appears). The port list is a snapshot per active distro, refreshed every `portsRefreshMs` (10000 by default), so a server started moments ago may not be listed yet. |
70
70
 
71
71
  Three modules support them and import no DSH package.
72
72
  [`lib/paths.js`](../lib/paths.js) translates paths.
@@ -87,7 +87,7 @@ The environment is per session, not per process:
87
87
  | `subprocess-wsl` | at the app level, called the composition; the shipped `subprocess` row is disabled | because no session identity reaches the terminal controller, see below |
88
88
  | `workspace-files` | shipped, not replaced | its client half is the browser `file` resource provider the Sidebar previews need; it reads WSL paths through the routed root filesystem |
89
89
  | `fs-routing` | at the app level; the shipped `fs-sandbox` row is disabled | the root `ctx.fs` is session-less, and its write fence has no root-plane consumers |
90
- | `directory-picker-wsl`, `wsl-shell-env`, `auto-preset` | at the app level | they answer questions that are not scoped to a session |
90
+ | `directory-picker-wsl`, `wsl-shell-env`, `auto-preset` | at the app level | they answer questions that are not scoped to a single preset; `shell-env` resolves its facts per session workspace |
91
91
 
92
92
  The preset mount requires the isolate realms. Without them the registry rejects the
93
93
  providers with `Preset services require isolate realms: shell, fs`.
@@ -295,10 +295,10 @@ own dialect, described after the table.
295
295
  |---|---|---|---|
296
296
  | `SANDBOX_UNAVAILABLE` | `WslSandbox.confine` | the requested mode cannot be enforced: no usable `bwrap` in the distro | install `bubblewrap` there (a failed probe is re-run, so no restart is needed), or set `sandbox: false` |
297
297
  | `FS_SANDBOX_DENIED` | `WslFileSystem.checkedTarget`, and `lib/fsio-agent.js` (a write the confined resident's mount table refused, classified from the kernel's `read-only file system`) | the file-effect policy refused the write: `read-only`, or a target outside the writable roots — or the kernel refused it at the resident's profile | a wider permission for that one call, or a session workspace that contains the target |
298
- | `FS_OUTSIDE_DISTRO` | `WslFileSystem.worldPath` | `restrictToDistro` is on and the path names **another distro**'s share — a configuration fence, not a sandbox decision | open a session in that distro, or set `restrictToDistro: false`. A wider permission does **not** lift it |
298
+ | `FS_OUTSIDE_DISTRO` | `WslFileSystem.worldPath` | `distro` is fixed (`default` or a name), `restrictToDistro` is on, and the path names **another distro**'s share — a configuration fence, not a sandbox decision. With `distro: ''`/`auto` a UNC target is served by its own distro, so the fence has nothing foreign to refuse | set `distro` to `''`/`auto` (the default), or open a session in that distro. A wider permission does **not** lift it |
299
299
  | `FS_NOT_OBSERVED` | `lib/fs-substrate.js` (`writeText`; the agent's `exists` conclusion, mapped in `lib/fsio-agent.js`) | a guarded create (`createIfAbsent`) targeted a file that exists and had not been read first | read the file, then overwrite with the version guard or edit it |
300
300
  | `FS_STALE_VERSION` | `lib/fs-substrate.js` (`writeText`, `editText`; the agent's `stale` conclusion, mapped in `lib/fsio-agent.js`) | the version the caller holds no longer matches: the file changed, or is gone | read it again and retry with the fresh version |
301
- | `FS_IO_ERROR` | the filesystem stack: `lib/fs-decisions.js` (the substrate is unavailable), `lib/fsio-agent.js` (a `perm`/`loop`/`io` reason or a malformed record), `lib/index.js` (a path that cannot be mapped, or a `SandboxUnavailableError`), and `WslFileSystem.watch` | the catch-all I/O failure — most often the resident agent is out or the distro returned an I/O error, the "agent outage fails closed" case above. A watch target outside the pinned distro is the narrow one | get the resident back (check `wsl.exe -l -v`, then restart the session); for a watch, aim it at a distro path — the watch itself runs where the files live (`lib/watcher.js`) |
301
+ | `FS_IO_ERROR` | the filesystem stack: `lib/fs-decisions.js` (the substrate is unavailable), `lib/fsio-agent.js` (a `perm`/`loop`/`io` reason or a malformed record), `lib/index.js` (a path that cannot be mapped, or a `SandboxUnavailableError`), and `WslFileSystem.watch` | the catch-all I/O failure — most often the resident agent is out or the distro returned an I/O error, the "agent outage fails closed" case above. A watch target outside the selected distro is the narrow one | get the resident back (check `wsl.exe -l -v`, then restart the session); for a watch, aim it at a distro path — the watch itself runs where the files live (`lib/watcher.js`) |
302
302
  | `FS_NOT_FOUND` | `lib/fsio-agent.js` and `lib/fs-substrate.js` (the agent's `notfound`/`notdir` conclusion, an empty path, or a missing edit target), and the host filesystem stack behind a drive path | the path does not exist — the ordinary answer | — |
303
303
  | `FS_NOT_REGULAR_FILE` | `lib/fsio-agent.js`, `lib/fs-substrate.js` | the target is not a regular file where one is required: a directory, symlink or device on a read, or a non-file target on a write | point the call at a regular file |
304
304
  | `FS_NOT_DIRECTORY` | `lib/fsio-agent.js` (`listChildren`) | the target is not a directory, so there are no children to list | list a directory |
@@ -351,7 +351,7 @@ step with the checkout, and `pnpm run sync:windows` runs that script.
351
351
  | Type check | `tsc --noEmit` over `test/**` and `types/*.d.ts` (`src/` is checked by the build itself) | no |
352
352
  | Unit tests | `test/*.test.mjs` | no |
353
353
  | Sandbox probe | [`test/probe/sandbox.sh`](../test/probe/sandbox.sh) | no; it applies the profile arguments directly |
354
- | Harness probes | [`test/probe/run.sh`](../test/probe/run.sh), `terminal.sh`, `sandbox-shell.sh`, `picker.sh`, `missing-wsl.sh`, `sandbox-off.sh` | yes: Windows, WSL2, a mounted profile, and a linked checkout |
354
+ | Harness probes | [`test/probe/run.sh`](../test/probe/run.sh), `terminal.sh`, `sandbox-shell.sh`, `picker.sh`, `fs-routing.sh`, `missing-wsl.sh`, `sandbox-off.sh` | yes: Windows, WSL2, a mounted profile, and a linked checkout |
355
355
  | Distro probes | [`test/probe/substrate.sh`](../test/probe/substrate.sh), `watch.sh`, `agent.sh`, `exec.sh` | Windows and WSL2, but no harness boot and no profile: they run from a distro terminal with Node available, reaching `wsl.exe` through interop |
356
356
  | Host probe | [`test/probe/mode.sh`](../test/probe/mode.sh) | Windows Node, no harness |
357
357
  | Probe plugins | the `*-probe.mjs` and `*-probe.yml` files in `test/probe/` | loaded by the scripts above |
@@ -416,6 +416,8 @@ src/auto-preset.ts per-session environment selection
416
416
  src/bwrap.ts the bwrap command line every confinement site
417
417
  assembles (no imports, so a bare checkout can
418
418
  assert it)
419
+ src/distro-route.ts the pure distro-mode selector (pinned / workspace /
420
+ default) shared by shell, fs, preflight and env
419
421
  src/file-reference-wsl.ts the distro-side `@`-completion traversal — built,
420
422
  tested, not yet wired (see above)
421
423
  src/fs.ts the ctx.fs subpath entry point