dsh-plugin-wsl-env 0.15.0 → 0.17.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +144 -15
  2. package/CONTRIBUTING.md +25 -33
  3. package/README.md +10 -10
  4. package/SECURITY.md +6 -4
  5. package/SUPPORT.md +8 -8
  6. package/agent/native/linux-aarch64/dsh-wsl-agent +0 -0
  7. package/agent/native/linux-x86_64/dsh-wsl-agent +0 -0
  8. package/agent/wsl-agent.sh +51 -12
  9. package/cordis.patch.yml +23 -10
  10. package/docs/ARCHITECTURE.md +56 -41
  11. package/docs/COMPATIBILITY.md +121 -0
  12. package/docs/CONFIGURATION.md +275 -273
  13. package/docs/LIMITATIONS.md +114 -38
  14. package/docs/MIGRATION.md +16 -13
  15. package/docs/PARITY.md +4 -3
  16. package/docs/PERFORMANCE.md +11 -9
  17. package/docs/QUICKSTART.md +46 -21
  18. package/docs/README.zh.md +7 -7
  19. package/docs/RELEASING.md +257 -0
  20. package/docs/REQUIREMENTS.zh.md +77 -0
  21. package/docs/ROADMAP-1.0.md +105 -61
  22. package/docs/ROADMAP.md +15 -9
  23. package/docs/UPSTREAM-FILE-REFERENCE-SEAM.md +3 -3
  24. package/docs/UPSTREAM-FSIO-EXPORT.md +7 -7
  25. package/docs/UPSTREAM-OPEN-IN-APP.md +57 -0
  26. package/docs/UPSTREAM-SESSION-NOTICE.md +4 -4
  27. package/docs/UPSTREAM-TERMINAL-TITLE.md +10 -12
  28. package/docs/VALUE.zh.md +144 -0
  29. package/docs/adr/0002-native-agent.md +348 -0
  30. package/docs/root-fs-routing.md +186 -0
  31. package/docs/screenshots/picker.png +0 -0
  32. package/docs/screenshots/preview.png +0 -0
  33. package/docs/screenshots/sandbox.png +0 -0
  34. package/docs/screenshots/terminal.png +0 -0
  35. package/lib/agent-exec.d.ts +7 -3
  36. package/lib/agent-protocol.d.ts +18 -5
  37. package/lib/agent-protocol.js +11 -6
  38. package/lib/agent-shared.js +1 -0
  39. package/lib/agent.d.ts +28 -9
  40. package/lib/agent.js +74 -14
  41. package/lib/fs-routing.d.ts +2 -1
  42. package/lib/fs-routing.js +13 -1
  43. package/lib/fs-substrate.d.ts +11 -9
  44. package/lib/fs-substrate.js +6 -9
  45. package/lib/fsio-agent.d.ts +2 -3
  46. package/lib/fsio-agent.js +2 -3
  47. package/lib/git-exec.d.ts +117 -0
  48. package/lib/git-exec.js +105 -0
  49. package/lib/git-route.d.ts +43 -13
  50. package/lib/git-route.js +54 -15
  51. package/lib/index.d.ts +20 -13
  52. package/lib/index.js +18 -11
  53. package/lib/session-runtime.d.ts +5 -3
  54. package/lib/session-runtime.js +5 -4
  55. package/lib/subprocess.d.ts +21 -7
  56. package/lib/subprocess.js +63 -9
  57. package/locale/en.json +1 -1
  58. package/locale/zh.json +1 -1
  59. package/package.json +13 -4
  60. package/scripts/diagnose.sh +1 -1
  61. package/scripts/doctor.mjs +23 -3
@@ -1,11 +1,21 @@
1
1
  # Roadmap 1.0: the WSL plugin the DSH ecosystem should have
2
2
 
3
- Status: working plan, 2026-10-19. Baseline: `0.12.0`.
3
+ Status: working plan, last refreshed 2026-10-31 against `0.15.0`.
4
+ The 0.12.0 baseline that motivated the plan is kept below as history.
4
5
  Audience: maintainers and contributors. This is a plan, not a release promise.
5
6
  The short version: **from "the only WSL plugin" to "the default WSL plugin"** —
6
7
  first run works, every distro and workspace is served by its own environment,
7
8
  every DSH surface has a story, and every claim has a test or a probe.
8
9
 
10
+ ## 0. 核心价值(需求裁决标准)
11
+
12
+ 本插件只做两件事,其余一切都是为这两件事服务的适配:
13
+
14
+ 1. **原生 Linux 工具链,LLM 生成的质量更高。** 模型在正确的生态里生成与执行——bash 而非 PowerShell、GNU 工具链、项目真实的构建系统。凡能让 distro 内工具链更完整、对模型更可见的,直接服务此价值(doctor 工具链检查、bootstrap-import、六发行版矩阵)。
15
+ 2. **避开 Windows 与 WSL 的跨环境文件操作,大幅提升性能。** 每一次跨 9p/UNC 边界的文件操作都是性能税。凡消除或减少跨界往返的,直接服务此价值(picker 的 agent 化、#31 批量读、workspace-changes 的 git 经 `ctx.subprocess` 在 distro 内原生运行——已与上游实现对账)。
16
+
17
+ 任何新提案先用这两条裁决:不服务者不进路线图。
18
+
9
19
  ## 1. What "must-have" means
10
20
 
11
21
  "Best" is not "most features". It is five acceptance lines:
@@ -34,25 +44,41 @@ North-star metrics:
34
44
  | Real-machine probe suite | 100% on Tier 1, nightly |
35
45
  | Known documentation drift | 0 |
36
46
 
37
- ## 2. Where 0.12.0 stands
47
+ ## 2. Where 0.15.0 stands
48
+
49
+ > WSL-101 landed on 2026-10-19: the distro follows the workspace and the
50
+ > per-distro caches are in place. 0.15.0 (2026-10-27) shipped the compatibility
51
+ > matrix, container legs, qemu aarch64, doctor, preset helper, and
52
+ > `interopVisibility`. The table that *motivated* the plan is frozen as the
53
+ > 0.12.0 baseline; the 0.15.0 row is what a reader should act on.
38
54
 
39
- > WSL-101 is implemented in the working tree (2026-10-19): the distro follows the
40
- > workspace and the per-distro caches are in place. The table below records the
41
- > 0.12.0 baseline that motivated the plan.
55
+ ### 0.15.0 (current)
42
56
 
43
- | Area | Strong today | Gap to "must-have" |
57
+ | Area | Strong today | Remaining gap |
44
58
  |---|---|---|
45
- | Execution | Native agent is the default; systemd session scope; background processes survive | The distro is pinned by configuration. The picker lists every distro, but a workspace in a non-default distro hits the cross-distro guard ([`src/index.ts:838`](../src/index.ts), [`src/paths.ts:227`](../src/paths.ts)) |
46
- | Files | ext4 substrate, root-plane `fs-routing`, kernel-enforced confined writes | GUI previews take roughly four to six agent round trips ([`LIMITATIONS.md:122`](LIMITATIONS.md)); `@` completion still reads through 9p |
47
- | Sandbox | `bubblewrap`, fail-closed, honest `enforcement: partial`, `maskWindowsDrive` | systemd and Linger are default prerequisites, but neither `diagnose` nor `bootstrap` checks them |
48
- | Onboarding | `bootstrap`, `diagnose`, session-start preflight, package-family install commands | No single "check and fix" command; the preflight only probes bwrap; the preset override is a trap |
49
- | Configuration | Every shipped key is documented | `wsl-shell` and `wsl-fs` are nested in `preset-wsl`; an id patch only logs a warning and changes nothing ([`CONFIGURATION.md:64`](CONFIGURATION.md)) |
50
- | Quality | 33 unit-test files, 12 real-machine probes, build/type/coverage gates | Probes are manual; aarch64 is build-only ([`ROADMAP.md:45`](ROADMAP.md)); `fs-routing` is outside `test:unit`/`test:coverage` ([`REQUIREMENTS.zh.md:72`](REQUIREMENTS.zh.md)) |
51
- | Ecosystem | npm package, bundle patch, bilingual locale, substantial docs | The upstream seams are fragile: the `ensureSession` monkey patch, argv-shape search interception, and the missing `FileReferenceTraversal` seam ([`upstream/promotion-map.md`](upstream/promotion-map.md)) |
52
-
53
- The engineering base is already in the top tier of comparable projects. The
54
- remaining gap is productization: **multi-distro correctness, first-run
55
- friction, configuration ergonomics, and ecosystem presence**.
59
+ | Execution | Native agent default; systemd session scope; `distro: auto` coordinate routing; concurrent distros | Physical aarch64 WSL (out of scope); non-systemd real-machine session probe unrun |
60
+ | Files | in-distro agent substrate, root-plane `fs-routing` in unit+coverage, kernel-enforced confined writes | GUI previews still extra agent round trips (measured, deferred); `@` completion still 9p until the upstream seam |
61
+ | Sandbox | `bubblewrap`, fail-closed, `enforcement: partial`, `interopVisibility` / `maskWindowsDrive` | Plugin keys do not disable WSL interop; distro `[interop] enabled=false` must be configured separately |
62
+ | Onboarding | `doctor`, `bootstrap` / `bootstrap-import`, session preflight, `profile-preset` helper | Clean-machine first-run timing still unmeasured; session UI has no toast seam |
63
+ | Configuration | Every shipped key documented; nested `preset-wsl` rows edited through the helper | An id patch of `wsl-shell` / `wsl-fs` still only warns |
64
+ | Quality | Unit files including `fs-routing`; coverage 85/84/70; qemu aarch64 18-check; five-family container agent; COMPATIBILITY.md | Nightly `probes` job is GitHub-hosted `windows-latest` (skips without WSL); physical aarch64 out of scope |
65
+ | Ecosystem | npm package, bundle patch, bilingual locale, issue templates | Upstream seams remain: `ensureSession` monkey patch, argv-shape search interception, missing `FileReferenceTraversal` |
66
+
67
+ ### 0.12.0 baseline (historical)
68
+
69
+ | Area | Strong then | Gap that motivated the plan |
70
+ |---|---|---|
71
+ | Execution | Native agent is the default; systemd session scope; background processes survive | Distro pinned by configuration; non-default workspace hit the cross-distro guard |
72
+ | Files | ext4 substrate, root-plane `fs-routing`, kernel-enforced confined writes | GUI extra round trips; `@` completion through 9p |
73
+ | Sandbox | `bubblewrap`, fail-closed, honest `enforcement: partial`, `maskWindowsDrive` | systemd and Linger unchecked by diagnose/bootstrap |
74
+ | Onboarding | `bootstrap`, `diagnose`, session-start preflight | No single doctor; preflight only probed bwrap; preset override trap |
75
+ | Configuration | Every shipped key documented | Nested `preset-wsl` rows; an id patch warned and changed nothing |
76
+ | Quality | 33 unit-test files, 12 real-machine probes, build/type/coverage gates | Probes manual; aarch64 build-only; `fs-routing` outside `test:unit` / `test:coverage` |
77
+ | Ecosystem | npm package, bundle patch, bilingual locale | Fragile upstream seams (same three as today) |
78
+
79
+ The remaining gap after 0.15.0 is **upstream seams, honest probe collection,
80
+ and 1.0 empirical legs** (clean-machine timing, 30 days without a P0) — not
81
+ the 0.12.0 productization list.
56
82
 
57
83
  ## 3. Design principles
58
84
 
@@ -141,12 +167,12 @@ may move if the compatibility matrix exposes a higher-severity failure.
141
167
  |---|---|---|---|
142
168
  | Time to first command | Not systematically measured | p50 <= 5 min, p90 <= 15 min | Ten clean-machine runs |
143
169
  | Tier 1 first-run success | Not measured | >= 95% | Compatibility matrix |
144
- | Real-machine probes | 14, manual | 100%, nightly | Self-hosted Windows runner |
170
+ | Real-machine probes | 15 with `--include-fs` (14 base scripts incl. `fs-routing.sh`, plus `run.sh`); nightly job on hosted `windows-latest` skips without WSL | 100% on a Windows+WSL runner | Dispatch or a self-hosted runner that actually has WSL |
145
171
  | FS single-op p95 | ~22ms host round trip | <= 30ms | `fs-queue.mjs` |
146
172
  | 16 concurrent reads p95 | 2.4-8.2ms | <= 20ms | `fs-queue.mjs` |
147
173
  | GUI preview p95 | Backend 63.5 ms/MiB (service round trips on top) | <= 1s/MiB | `probe:gui-preview` |
148
174
  | Agent recovery | One rebuild | < 2s | Fault injection |
149
- | Documentation drift | Three known | 0 | Release checklist |
175
+ | Documentation drift | Closed 2026-10-30 (aarch64 qemu, 0.15 baseline, WSL-202 boxes) | 0 | Release checklist |
150
176
  | Release cadence | Ad hoc | Every 2-4 weeks | Calendar |
151
177
  | Adoption (lagging) | Not established | +50% per quarter | npm download stats |
152
178
 
@@ -187,8 +213,13 @@ acceptance list in each issue is the contract; filter by priority:
187
213
 
188
214
  ### WSL-101 — Multi-distro coordinate routing
189
215
 
190
- **Status:** implemented in the working tree (2026-10-19); unit-covered, with the
191
- real-machine two-distro probe still to run.
216
+ **Status:** shipped in 0.13.0 (2026-10-19), unit-covered. The
217
+ `fs-routing.sh` probe (added with P0.4) runs on the real machine: it serves a
218
+ workspace in its own distro, reads a drive-letter path on the host plane, and
219
+ refuses another distro's UNC — one leg of the acceptance below has real-machine
220
+ evidence (a 2026-10-31 run passed all 15 probes on one machine; not a matrix
221
+ result). The concurrent two-distro leg still has no dedicated probe and has
222
+ not been run; the checkboxes stay unchecked until that leg exists.
192
223
 
193
224
  **Priority:** P0 · **Size:** L · **Labels:** `area:multi-distro`,
194
225
  `type:feature`, `priority:P0`
@@ -245,7 +276,7 @@ current diagnostics do not cover the second.
245
276
  run on this single configured machine).
246
277
 
247
278
  **Tests/probes:** process-level tests against fake `wsl.exe` outputs
248
- ([`test/doctor.test.mjs`](../test/doctor.test.mjs)); real-machine run on the
279
+ ([`test/doctor.test.mjs`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/doctor.test.mjs)); real-machine run on the
249
280
  Tier 1 image; `diagnose` remains a read-only alias.
250
281
 
251
282
  **Depends on:** none.
@@ -279,7 +310,7 @@ failure.
279
310
 
280
311
  **Tests/probes:** generated YAML compared byte-for-byte against the shipped
281
312
  block (the row the real composition loads); the CLI's print/write/check paths
282
- run in [`test/profile-preset.test.mjs`](../test/profile-preset.test.mjs).
313
+ run in [`test/profile-preset.test.mjs`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/profile-preset.test.mjs).
283
314
 
284
315
  **Depends on:** none.
285
316
 
@@ -288,8 +319,10 @@ run in [`test/profile-preset.test.mjs`](../test/profile-preset.test.mjs).
288
319
  **Status:** implemented (2026-10-19): the three drifts are fixed, `fs-routing`
289
320
  and `distro-route` are in both test lists, `scripts/check-links.mjs` gates
290
321
  relative links from `pnpm test`, and `probe:fs-routing` is written and wired
291
- into the real-machine suite. The routing probe has not been executed yet (it
292
- needs the app closed); its first run stays on the release checklist.
322
+ into the real-machine suite. The routing probe's first real-machine run
323
+ happened by 2026-10-31 (a user-supplied log shows the full `--include-fs`
324
+ suite, 15 probes, 0 failures on one machine — an environment-specific result,
325
+ not a matrix).
293
326
 
294
327
  **Priority:** P0 · **Size:** S · **Labels:** `area:docs`, `area:quality`,
295
328
  `priority:P0`
@@ -323,15 +356,15 @@ remedy for `no-systemd`, `no-user-manager`, `no-systemd-run` and `no-linger`
323
356
  (`sessionSandbox: false` on the row skips the session half). The advisory is
324
357
  logged **and** published as a session-scoped `wsl-preflight` runtime-context
325
358
  entry, so it is visible in the session transcript rather than only in the app
326
- log. DSH 0.2.1 has no toast/banner seam; the exact ask is written up (not yet
327
- filed — no issue channel here) in
359
+ log. DSH 0.2.1 has no toast/banner seam; the exact ask was filed as
360
+ [#10263](https://github.com/deepseek-ai/deepseek-harness/discussions/10263) and is recorded in
328
361
  [UPSTREAM-SESSION-NOTICE.md](UPSTREAM-SESSION-NOTICE.md), and
329
362
  [LIMITATIONS.md](LIMITATIONS.md#session-start-advisories) records the boundary.
330
363
  Unit coverage drives every probe verdict through a fake `wsl.exe`; the
331
364
  real-machine missing-bwrap and non-systemd legs are still manual.
332
365
 
333
- **Why:** The session-start preflight currently only probes bwrap and writes to
334
- `logger.warn`; the other default prerequisite is discovered at the first
366
+ **Why:** Before this work, session-start preflight only probed bwrap and wrote
367
+ to `logger.warn`; the other default prerequisite was discovered at the first
335
368
  command.
336
369
 
337
370
  **Acceptance:**
@@ -391,20 +424,30 @@ probe remains unrun (no non-systemd distro here).
391
424
  **Priority:** P1 · **Size:** L · **Labels:** `area:compatibility`,
392
425
  `area:quality`, `priority:P1`
393
426
 
394
- **Why:** The support matrix is prose, and the aarch64 binary is built but never
395
- executed.
427
+ **Status:** implemented (0.15.0 / 2026-10-27) except the scope decisions in
428
+ [COMPATIBILITY.md](COMPATIBILITY.md#scope-decisions): physical aarch64 WSL and
429
+ a separate Windows 10 matrix are out of scope; the non-systemd real WSL
430
+ terminal-session legs remain unrun (Alpine is covered for the in-distro
431
+ suite and `sessionSandbox: auto`).
432
+
433
+ **Why:** The support matrix used to be prose, and the aarch64 binary was built
434
+ but never executed.
396
435
 
397
436
  **Acceptance:**
398
437
 
399
- - [ ] Tier 1 and Tier 2 are defined and tested: Ubuntu LTS, Debian stable,
400
- Fedora, Arch, Alpine (musl), openSUSE; systemd and non-systemd;
401
- x86_64 and aarch64; Windows 10/11; current WSL.
402
- - [ ] The native 18-check suite runs for aarch64 under qemu in CI.
403
- - [ ] The shell agent runs in Linux containers for the non-Ubuntu families.
404
- - [ ] The support table states "supported", "partial" and "unsupported" with
405
- evidence.
438
+ - [x] Tier 1 and Tier 2 are defined and tested on x86_64 WSL2 / Windows 11:
439
+ Ubuntu, Debian stable, Fedora, Arch, Alpine (musl), openSUSE; systemd
440
+ and non-systemd (Alpine). Evidence: [COMPATIBILITY.md](COMPATIBILITY.md).
441
+ - [x] The native 18-check suite runs for aarch64 under qemu in CI (built
442
+ artifact and committed `linux-aarch64` binary).
443
+ - [x] The shell agent runs in Linux containers for the non-Ubuntu families
444
+ (`agent-containers` job, 35/35).
445
+ - [x] The support table states "supported", "partial" and "unsupported" with
446
+ evidence (COMPATIBILITY.md). Physical aarch64 and Windows 10 are
447
+ documented as out of scope rather than untested claims.
406
448
 
407
- **Tests/probes:** container matrix, qemu spike, self-hosted nightly.
449
+ **Tests/probes:** container matrix, qemu spike, COMPATIBILITY.md; nightly
450
+ `probes` job is GitHub-hosted `windows-latest` and skips without WSL.
408
451
 
409
452
  **Depends on:** WSL-201 for non-systemd; WSL-203 for packaging.
410
453
 
@@ -413,7 +456,7 @@ executed.
413
456
  **Priority:** P1 · **Size:** M · **Labels:** `area:release`,
414
457
  `area:quality`, `priority:P1`
415
458
 
416
- **Status:** implemented (2026-10-19): [`test/e2e/install.sh`](../test/e2e/install.sh)
459
+ **Status:** implemented (2026-10-19): [`test/e2e/install.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/e2e/install.sh)
417
460
  (`pnpm run e2e:install`) packs the tree, creates a fresh `DSH_HOME` under
418
461
  `%TEMP%`, initializes a profile from the shipped `web` template, installs the
419
462
  tarball, asserts `--dump-config` composes the plugin rows with no skipped-patch
@@ -422,7 +465,7 @@ installs the previous minor (0.11.0), plants a marker in the profile layer,
422
465
  upgrades to the packed version, and asserts the marker survives, the installed
423
466
  version is current, and the installed CHANGELOG carries the migration note.
424
467
  The run passed 10/10 on this machine (the app may stay running: plugin
425
- management is local). [`test/packaging.test.mjs`](../test/packaging.test.mjs)
468
+ management is local). [`test/packaging.test.mjs`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/packaging.test.mjs)
426
469
  guards the packed file list in `pnpm test`. A self-hosted Windows runner job is
427
470
  still unconfigured; the local command is the documented path.
428
471
 
@@ -453,8 +496,8 @@ silently leave an obsolete workaround behind.
453
496
  cost (20 dirs / 520 files: 9p walk median 84.7 ms vs ext4 1.5 ms, ~56×; 9p
454
497
  samples 63–192 ms) measured by `pnpm run probe:file-reference`. The seam
455
498
  contract (three functions, Dirent/error/coordinate semantics) is already
456
- written in [rfc-wsl-workspaces.md](upstream/rfc-wsl-workspaces.md); the
457
- consumer [`src/file-reference-wsl.ts`](../src/file-reference-wsl.ts) is built
499
+ written in [rfc-wsl-workspaces.md](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/docs/upstream/rfc-wsl-workspaces.md); the
500
+ consumer [`src/file-reference-wsl.ts`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/src/file-reference-wsl.ts) is built
458
501
  and unit-tested but deliberately unwired. The fallback is to keep the 9p walk
459
502
  until upstream exposes the funnel; the trigger and the follow-up probe are
460
503
  recorded.
@@ -511,7 +554,7 @@ no prototype while the trigger is unmet.
511
554
  **Priority:** P1 · **Size:** M · **Labels:** `area:performance`,
512
555
  `area:gui`, `priority:P1`
513
556
 
514
- **Status:** implemented (2026-10-19): [`test/probe/gui-preview.sh`](../test/probe/gui-preview.sh)
557
+ **Status:** implemented (2026-10-19): [`test/probe/gui-preview.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/gui-preview.sh)
515
558
  (`pnpm run probe:gui-preview`) measures the three routed backend operations a
516
559
  preview pays — `stat`, `listDir`, and a 1 MiB `readByteRange` — with p50/p95 and
517
560
  budgets (stat 100 ms, listing 500 ms, page 1 s/MiB). Measured on this machine
@@ -578,7 +621,7 @@ mismatch); a real-machine kill/restart probe is still unrun.
578
621
  `area:docs`, `priority:P1`
579
622
 
580
623
  **Status:** implemented except the distro-disabled leg (2026-10-19):
581
- [CONFIGURATION](../docs/CONFIGURATION.md#the-strict-profile) documents the strict
624
+ [CONFIGURATION](../docs/CONFIGURATION.md#windows-interop) documents the strict
582
625
  profile, its trade-offs and the visible/masked/disabled matrix;
583
626
  `pnpm run probe:sandbox` now also runs the masked profile (14 expectations:
584
627
  `/mnt` hidden, the Windows binary unreachable, the workspace still writable). The
@@ -620,8 +663,8 @@ workflow itself has not run in this environment (no Actions runner here), and
620
663
  opening an issue on a red run was left out because the workflow has no issue
621
664
  permission — the failing run plus its delta is the signal.
622
665
 
623
- **Why:** The peer pins are exact; a DSH release can break the plugin before a
624
- user reports it.
666
+ **Why:** The development peer pins are exact, while accepted peer ranges can
667
+ admit newer releases; a DSH release can break the plugin before a user reports it.
625
668
 
626
669
  **Acceptance:**
627
670
 
@@ -647,7 +690,7 @@ user reports it.
647
690
  fragile parts.
648
691
 
649
692
  **Status:** packaging plan written (2026-10-19):
650
- [docs/upstream/promotion-pack.md](upstream/promotion-pack.md) records the
693
+ [docs/upstream/promotion-pack.md](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/docs/upstream/promotion-pack.md) records the
651
694
  `packages/wsl/*` layout (ten capability packages plus a thin integration
652
695
  package, each with its existing tests), the seam/RFC index with the current
653
696
  state of each seam, what `test/composition.test.mjs` boots today and the gaps a
@@ -683,18 +726,17 @@ install, first session, recipes for Node/Python/Rust/Go and a monorepo, an
683
726
  honest Tier 1/Tier 2 table); [docs/PERFORMANCE.md](PERFORMANCE.md) is the
684
727
  9p-vs-agent page with the A6/A7 numbers and the GUI-preview baseline, and
685
728
  `pnpm run probe:gui-preview` / `fs-queue.mjs` reproduce the latter. No GIF or
686
- screenshot exists yet (capturing the GUI is not something this repository has
687
- recorded), so that acceptance item is explicitly open. No DSH plugin
729
+ screenshot existed on 2026-10-19. Four screenshots were added on 2026-10-27;
730
+ a GIF remains open. No DSH plugin
688
731
  marketplace was identified, so nothing was submitted.
689
732
 
690
733
  **Why:** "Best" also means easy to find and evaluate.
691
734
 
692
735
  **Acceptance:**
693
736
 
694
- - [x] A one-page entry with a five-minute quickstart, compatibility table and
695
- screenshots (text only; screenshots are the open item).
696
- - [ ] A demo or GIF showing the picker, terminal, preview and sandbox fact
697
- (not produced; recorded here and in QUICKSTART).
737
+ - [x] A one-page entry with a quickstart, compatibility table and screenshots.
738
+ The five-minute clean-machine target remains unmeasured.
739
+ - [ ] A GIF showing the picker, terminal, preview and sandbox fact.
698
740
  - [x] Recipes for Node, Python, Rust/Go and a monorepo; a 9p-vs-plugin
699
741
  benchmark page.
700
742
  - [ ] The DSH plugin directory/marketplace is updated if one exists (none
@@ -741,17 +783,19 @@ removals need a boot-time refusal, meaning changes need a migration note; exact
741
783
  peer pins are tested by the canary), the deprecation path with the
742
784
  `substrate: "share"` and `distro: ''` cases, and the release-gate table with the
743
785
  manual legs marked unrun. RELEASING and SUPPORT link it. The empirical legs
744
- (Tier 1 matrix, aarch64 execution, 30 days without a P0) and the final `1.0.0`
745
- release notes remain open.
786
+ (clean-machine first-run timing, 30 days without a P0) and the final `1.0.0`
787
+ release notes remain open. The Tier 1 matrix and aarch64 qemu execution
788
+ closed in 0.15.0.
746
789
 
747
790
  **Acceptance:**
748
791
 
749
792
  - [x] Configuration schema frozen; new keys require a minor.
750
793
  - [x] Migration and deprecation policy documented.
751
- - [ ] Tier 1 green, aarch64 executed, install E2E green, no P0 bugs for 30
752
- days (install E2E is 10/10; the other three are open/unrun).
794
+ - [x] Tier 1 matrix green and aarch64 executed under qemu (0.15.0);
795
+ install E2E is 10/10. Physical aarch64 WSL is out of scope.
796
+ - [ ] No P0 bugs for 30 days; clean-machine first-run timing still unrun.
753
797
  - [ ] `1.0.0` release notes list the supported matrix and the known
754
- limitations (draft waits for the matrix).
798
+ limitations (COMPATIBILITY.md + LIMITATIONS.md are the draft).
755
799
 
756
800
  **Tests/probes:** [MIGRATION.md](MIGRATION.md#gates-a-release-must-clear);
757
801
  release gate; full probe suite.
@@ -776,5 +820,5 @@ A 1.0 release additionally requires the WSL-304 checklist.
776
820
  - Known boundaries: [LIMITATIONS.md](LIMITATIONS.md)
777
821
  - Current technical roadmap: [ROADMAP.md](ROADMAP.md)
778
822
  - Requirements traceability: [REQUIREMENTS.zh.md](REQUIREMENTS.zh.md)
779
- - Upstream mapping: [upstream/promotion-map.md](upstream/promotion-map.md)
780
- - Real-machine suite: [`test/probe/run-all-when-closed.sh`](../test/probe/run-all-when-closed.sh)
823
+ - Upstream mapping: [upstream/promotion-map.md](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/docs/upstream/promotion-map.md)
824
+ - Real-machine suite: [`test/probe/run-all-when-closed.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/run-all-when-closed.sh)
package/docs/ROADMAP.md CHANGED
@@ -20,6 +20,8 @@ section.
20
20
  | A4 | the native-session probe (scope, EXEC, FS, KILL, digest, teardown, 8/8) in the real-machine suite; the warm-cache and stdin-EPIPE fixes |
21
21
  | A5 | the native agent is the documented default; 0.10.0 published (npm + GitHub Release) |
22
22
  | A6 | `WslAgent.metrics()` and `test/probe/fs-queue.mjs`; the shared shell read agent is the measured batch bottleneck |
23
+ | A7 | native shared read agent (v5); 16 concurrent shared reads 2.4–4.1 ms; shell remains the fallback |
24
+ | A8 | aarch64 18-check under qemu-user in CI (built binary and committed `linux-aarch64` artifact); physical aarch64 WSL out of scope, see COMPATIBILITY.md |
23
25
 
24
26
  ## Next
25
27
 
@@ -30,9 +32,10 @@ The shared unconfined read agent was the only remaining measured bottleneck
30
32
  0.2-0.3 ms). A7 swaps its shell for the native binary (v5) while keeping the
31
33
  shell as the fallback:
32
34
 
33
- - **selection:** the same `agentBackend` semantics — `auto` prepares the
34
- packaged binary for the distro's architecture, `native` refuses instead of
35
- falling back, `shell` forces the shell; the dev override still wins;
35
+ - **selection:** `shell` forces the shell; otherwise the shared agent tries
36
+ the packaged binary for the distro's architecture (or the dev override).
37
+ Unlike session-runtime preparation with `agentBackend: native`, shared reads
38
+ fall back to the shell when native preparation fails;
36
39
  - **preparation:** `ensureNativeBackend` once per distro (cache/digest/exec
37
40
  probe); the `WslAgent` with `expectedDigest` and `signalable: false`; on a
38
41
  preparation or HELLO failure the shell agent serves the reads — fail-open to
@@ -46,11 +49,13 @@ shell as the fallback:
46
49
  pays the ensure/cache cost (~70 ms once). The shell still serves the reads
47
50
  under `agentBackend: shell` and after a native failure.
48
51
 
49
- ### A8 — aarch64 functional validation
52
+ ### A8 — aarch64 functional validation — done
50
53
 
51
- The aarch64 binary is built by CI but never executed there. Run the 18-check
52
- spike suite and the native-session probe on real aarch64 WSL or qemu; record
53
- the result in the ADR.
54
+ The `native packages` job in CI builds static-musl aarch64 and runs the
55
+ 18-check spike suite under qemu-user against both the freshly built binary
56
+ and the committed `agent/native/linux-aarch64/dsh-wsl-agent`. Physical
57
+ aarch64 WSL is out of scope (COMPATIBILITY.md). The native-session probe on
58
+ real aarch64 hardware is not planned.
54
59
 
55
60
  ### A9 — maintenance
56
61
 
@@ -66,8 +71,9 @@ the result in the ADR.
66
71
  measurably or the ERR/env framing is needed for another reason.
67
72
  - **Event stream.** No current consumer: the `fs.watch` contract is coarse and
68
73
  the inotify watcher satisfies it; terminal activity and ports poll.
69
- - **Shell retirement.** Not planned. The shell agent remains the fallback and
70
- the shared read path until A7 lands, and the protocol/script stays supported.
74
+ - **Shell retirement.** Not planned. The shell agent remains the fallback
75
+ (`agentBackend: shell`, and after a native HELLO/prepare failure) and the
76
+ protocol/script stays supported.
71
77
 
72
78
  ## Not planned
73
79
 
@@ -10,7 +10,7 @@ decision.
10
10
 
11
11
  ## The measured cost
12
12
 
13
- Probe: [`test/probe/file-reference-9p.sh`](../test/probe/file-reference-9p.sh)
13
+ Probe: [`test/probe/file-reference-9p.sh`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/test/probe/file-reference-9p.sh)
14
14
  (three runs, `node:fs` readdir walk, no ranking engine). Tree: 20 directories,
15
15
  520 files, one level of subdirectories; the same tree walked from Windows over
16
16
  `\\wsl.localhost\<distro>` and from inside the distro on ext4.
@@ -31,7 +31,7 @@ seam must keep.
31
31
 
32
32
  ## The seam
33
33
 
34
- RFC addendum ① (in [rfc-wsl-workspaces.md](upstream/rfc-wsl-workspaces.md))
34
+ RFC addendum ① (in [rfc-wsl-workspaces.md](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/docs/upstream/rfc-wsl-workspaces.md))
35
35
  proposes exposing the three module-internal funnels as an injectable strategy:
36
36
 
37
37
  ```ts
@@ -55,7 +55,7 @@ with three semantics that matter for a remote world:
55
55
 
56
56
  ## The plugin side is ready
57
57
 
58
- [`src/file-reference-wsl.ts`](../src/file-reference-wsl.ts) implements that
58
+ [`src/file-reference-wsl.ts`](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/src/file-reference-wsl.ts) implements that
59
59
  interface over the resident agent: one `find -printf` per directory, a
60
60
  single-exec segment walk for `resolveDisplayDirectory` (instead of N sequential
61
61
  `lstat`s), and the UNC/POSIX conversions. It is unit-tested against a fake
@@ -3,9 +3,9 @@
3
3
  Status: filed upstream as
4
4
  [deepseek-ai/deepseek-harness#10262](https://github.com/deepseek-ai/deepseek-harness/discussions/10262)
5
5
  (Ideas); the filing convention is [UPSTREAM-TERMINAL-TITLE.md](UPSTREAM-TERMINAL-TITLE.md).
6
- Targeting `deepseek-harness` @ `packages/fs/fs-local`, the `0.2.1-rc.x` line
7
- this plugin pins exactly (`@deepseek-ai/dsh-fs-local@0.2.1-rc.2`, in both the
8
- dev and peer dependency blocks of `package.json`). This is the Shape-A
6
+ Targeting `deepseek-harness` @ `packages/fs/fs-local`, the `0.2.1-rc.x` line.
7
+ The proposal was measured against `@deepseek-ai/dsh-fs-local@0.2.1-rc.2`;
8
+ the current development pin and peer range are in `package.json`. This is the Shape-A
9
9
  upstream half of the in-distro I/O refactor: with the fsio text mechanics
10
10
  importable, `dsh-plugin-wsl-env` deletes its copies of them.
11
11
 
@@ -146,13 +146,13 @@ import("koffi")`, `:25`), so no native dependency is evaluated at import time.
146
146
  layer maps the `code` token and the message, not the error class.
147
147
  - Semver: an additive export. On the `0.2.0-rc.x` line it ships in the next
148
148
  pre-release — the `0.2` position does not move, so this is not a minor bump
149
- of a released line; and because the consuming plugin pins the exact version,
150
- adoption is an explicit pin bump, never an automatic upgrade.
149
+ of a released line. Adoption by the plugin requires an explicitly verified
150
+ development pin and a peer minimum that includes the export.
151
151
 
152
152
  ## Landing it (consumer side)
153
153
 
154
- 1. Bump both pins in `package.json` (`devDependencies` and `peerDependencies`)
155
- to the peer that carries the export.
154
+ 1. Update the development pin and peer minimum in `package.json` to a verified
155
+ version that carries the export.
156
156
  2. `pnpm run build` — `lib/` is committed, and `test/build-freshness.mjs`
157
157
  enforces that it is a byte-identical, closed image of `src/`.
158
158
  3. Re-run the comparison in [PEER-PARITY.md](PEER-PARITY.md) (its Maintenance
@@ -0,0 +1,57 @@
1
+ # Upstream: Host "Open in app" inherits `ELECTRON_RUN_AS_NODE`
2
+
3
+ Status: observed on DSH Desktop `0.2.1-rc.3` (Windows x64, 2026-10-30);
4
+ **not a defect of this plugin.** The Host process's open-in-app launcher
5
+ inherits `ELECTRON_RUN_AS_NODE=1`, so Electron editors (VS Code, Cursor)
6
+ start as Node, exit 1, and the UI reports launch-failed. File Explorer and
7
+ other non-Electron launchers work. The failure is the same on a Windows
8
+ folder and on a WSL UNC workspace.
9
+
10
+ User-facing note: [LIMITATIONS.md](LIMITATIONS.md#host-open-in-app).
11
+
12
+ ## Environment
13
+
14
+ - DSH Desktop 0.2.1-rc.3, Windows 11
15
+ - Reproduced on both `\\wsl.localhost\...` workspaces and plain Windows
16
+ paths; 100% of VS Code launches failed
17
+
18
+ ## What happens
19
+
20
+ The desktop window's overflow menu "Open in app" → VS Code shows an "打开失败"
21
+ / launch-failed toast. VS Code never opens.
22
+
23
+ ## Cause
24
+
25
+ 1. The desktop app starts the backend Host through
26
+ `desktopNodeEnvironment()` (`lib/main.js`) with
27
+ `ELECTRON_RUN_AS_NODE: "1"`.
28
+ 2. Open-in-app's `launchDetachedApp()` (`@deepseek-ai/dsh-host-open-in-app`)
29
+ builds the child environment with `scrubbedParentEnv()`, which strips
30
+ keys matching `/KEY|PASSWORD|SECRET|TOKEN/i` and `DSH_*`.
31
+ `ELECTRON_RUN_AS_NODE` is left in place.
32
+ 3. Host then spawn's `Code.exe <workspace>`. VS Code (Electron) sees
33
+ `ELECTRON_RUN_AS_NODE=1`, runs as plain Node, opens no window, and
34
+ exits 1 inside the launch watch window → `POST /open-in-app/open`
35
+ returns 502 `launch-failed`.
36
+
37
+ `dsh-subprocess-local` already clears `ELECTRON_RUN_AS_NODE` when it
38
+ spawns the PTC runtime, so the Host carrying that variable is a known
39
+ fact; open-in-app's scrub list does not follow it.
40
+
41
+ ## Reproduction
42
+
43
+ Using the app's bundled `node.exe`, spawn `Code.exe` detached with stdio
44
+ ignored and a scrubbed parent env:
45
+
46
+ - without `ELECTRON_RUN_AS_NODE`: `Code.exe` stays running → launched
47
+ - with `ELECTRON_RUN_AS_NODE=1`: process exits quickly, code 1 → 502
48
+
49
+ ## Ask
50
+
51
+ Drop `ELECTRON_RUN_AS_NODE` in `launchDetachedApp()` (or in the shared
52
+ `scrubbedParentEnv()`), matching `dsh-subprocess-local`. Any Electron
53
+ target (VS Code, Cursor, …) hits the same path.
54
+
55
+ This repository will not work around the Host spawn: the plugin does not
56
+ own that launcher, and a WSL-specific env filter would not help Windows
57
+ workspaces.
@@ -1,10 +1,10 @@
1
1
  # Upstream proposal: a session-visible notice seam for plugin advisories
2
2
 
3
- Status: proposed upstream; **not filed yet** — this environment has no issue
4
- channel, so the text below is ready for the maintainers to file against
5
- `deepseek-ai/deepseek-harness` (version line `0.2.1-rc.x`; the gap was checked
3
+ Status: filed upstream as
4
+ [deepseek-ai/deepseek-harness#10263](https://github.com/deepseek-ai/deepseek-harness/discussions/10263)
5
+ (version line `0.2.1-rc.x`; the gap was checked
6
6
  against `dsh-v0.2.1-rc.2` on 2026-10-19). The exact filing payload (title, body,
7
- command) is [proposal-session-notice.md](upstream/proposal-session-notice.md). The plugin ships the workaround
7
+ command) is [proposal-session-notice.md](https://github.com/Big-Dao/dsh-plugin-wsl-env/blob/main/docs/upstream/proposal-session-notice.md). The plugin ships the workaround
8
8
  described at the end: the advisory goes to the log **and** into the session's
9
9
  runtime context, which is model-visible and appears in the session transcript,
10
10
  but is not a dedicated banner.
@@ -107,17 +107,15 @@ next touched:
107
107
 
108
108
  ## Workarounds until then
109
109
 
110
+ - The plugin now ships the candidate list itself: `shellCandidates` is
111
+ `[wsl.exe, powershell, cmd]` in [`cordis.patch.yml`](../cordis.patch.yml),
112
+ with the `shell` block kept verbatim. `wsl.exe` dedupes into the default's
113
+ `wsl` kind; selecting `powershell` or `cmd` starts a shell the terminal
114
+ really launches (host sessions do outright) and titles the tab after itself.
115
+ Nothing remains for a user layer to add unless it wants different entries.
110
116
  - Double-click a tab's title to rename it (a shipped client feature; the
111
117
  controller's `rename` Remote API accepts 1–120 characters).
112
- - Re-declare the `terminal-controller` row in a user layer with a non-empty
113
- `shellCandidates`: a manually selected shell is titled after itself. A patch
114
- config replaces the whole config rather than deep-merging it
115
- (`packages/boot/app-boot/src/config-schema/document.ts`), so the override has
116
- to carry this plugin's `shell` block verbatim — `path: wsl.exe`, `name: WSL`,
117
- `args: []` — or the terminal stops being a distro terminal. Expect the menu
118
- to grow the host shells discovery finds on the root provider, which receives
119
- no directory: executables this terminal never starts (the second bullet of
120
- the GUI-terminal comment in [`cordis.patch.yml`](../cordis.patch.yml)).
121
- [LIMITATIONS.md](LIMITATIONS.md) still carries the older one-line form of
122
- this tip, which drops the `shell` block; the version above is the one that
123
- works.
118
+
119
+ Both cover only manually selected shells. The DEFAULT entry — what a new tab
120
+ gets before anyone chooses — is still titled from the static profile, which is
121
+ exactly the gap the upstream change closes.