dsh-deeppilot 0.9.3 → 0.9.5

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.
@@ -5,30 +5,204 @@
5
5
 
6
6
  ## Current decision
7
7
 
8
- - **Audited release:** `dsh-v0.2.1-alpha.1` (official release page:
9
- <https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.1>),
10
- published 2026-10-03 on the npm `alpha` dist-tag. It is **not** `latest`;
11
- `latest`/`next` still point at `0.2.0-rc.2`, which is what the current Host runs.
12
- - **Comparison baseline:** `dsh-v0.2.0-rc.2` (the previous plugin baseline).
13
- - **Plugin runtime verdict:** **no breaking change in the DSH APIs that DeepPilot
14
- currently calls.** All 12 peer packages have **zero** changed `src/` files
15
- between the two tags — each changed only its `package.json` version field and
16
- its READMEs. See the 2026-10-03 entry below for the full finding-by-finding
17
- table and for the three official upgrade guides checked against this plugin.
18
- - **Nothing was changed to support this release.** `package.json`,
19
- `package-lock.json`, generated `lib/`, `COMPATIBILITY.md`, and `PROTOCOL.md`
20
- deliberately still describe the `0.2.0-rc.2` baseline. The release-audit-only
21
- scope was agreed before the audit ran, so no version bump, no dependency
22
- install, and no `lib/` regeneration was performed.
23
- - **Peer range still admits it:** `>=0.2.0-rc.2 <0.3.0-0` satisfies
24
- `0.2.1-alpha.1` under `semver.satisfies(..., { includePrerelease: true })`,
25
- which is how DSH's `evaluatePluginCompatibility` gates activation. The same
26
- holds for the `~4.0.4` / `~3.18.4` vendor ranges against `4.0.5-alpha.1` /
27
- `3.18.5-alpha.1`. Under npm's **default** prerelease handling those three do
28
- not match, so installing on an alpha host from npm reports peer warnings.
8
+ - **Audited release:** `dsh-v0.2.1-alpha.3` (official release page:
9
+ <https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.3>),
10
+ published to npm 2026-10-14T13:32:15Z on the `alpha` dist-tag. It is **not**
11
+ `latest`; `latest`/`next` still point at `0.2.0-rc.2`. Baseline commit
12
+ `2fbf4359b82fb94c03dcce0b831660d87f12ea5c`, compared against alpha.2's
13
+ `d743267388641bc76f17c45ce8b4c231aed1d32c` (**852 commits, 3311 changed files,
14
+ 120,978 insertions, 16,632 deletions**).
15
+ - **Comparison baseline:** `dsh-v0.2.1-alpha.2` (the previous audited tag).
16
+ - **Plugin runtime verdict: four confirmed breaks with one root cause — the
17
+ alpha.3 Peer model — and the resident interaction client does not start.**
18
+ **Fixed in this audit; see "The fix" below.**
19
+ This release makes every in-process carrier name a `PeerScope`
20
+ (`principal` / `signal` / `interactions`) explicitly; nothing defaults to the
21
+ operator any more. The four landing points, each with its own evidence:
22
+
23
+ | # | Upstream change | Plugin seam | Evidence |
24
+ | --- | --- | --- | --- |
25
+ | 1 | `TypertGatewayWireStream.open`'s 4th parameter `peer` is required; `assertPeerLive()` reads `peer.signal.aborted` before anything else | `src/dsh-remote-interactions.ts:180` passes `undefined, undefined` | Reproduced against the published bundles: alpha.2 returns normally, alpha.3 throws `TypeError: Cannot read properties of undefined (reading 'signal')` |
26
+ | 2 | `$events` additionally requires `peer.interactions === 'answer'` (`requireInteractions`) | same call | Reproduced: a peer with `interactions: 'none'` gets `gateway/interactions-forbidden`; a full owner/answer peer passes on **both** versions |
27
+ | 3 | `ConnectionFetchHandler.fetch(request, peer)` now takes a required Peer | `src/dsh-remote-interactions.ts:204` calls `fetchHandler.fetch(request)` | `lib/types/rpc.d.ts` declares `peer: PeerScope` required, "never defaulted" |
28
+ | 4 | `./client` gained `node`/`browser` conditions; Node now resolves `lib/client.node.js`, which carries **no** `window.__ModuleLoader__.load` banner | `loadClientFaces()` (`src/dsh-remote-interactions.ts:117`) builds its module table from that banner | `grep -c __ModuleLoader__` is 0 in `client.node.js`, 1 in `client.browser.js` and 1 in alpha.2's `client.js` |
29
+
30
+ A fifth, latent one: `installConnection` derives
31
+ `interactions ?? (location === undefined ? 'none' : 'answer')`, and the
32
+ plugin's Node context has no `location`, so `apply(ctx)` alone always selects
33
+ `'none'`. Passing an explicit Peer is not enough by itself — the install call
34
+ must name `interactions: 'answer'` too.
35
+ - **Failure mode is degradation, not a crash — but only partly contained.**
36
+ `src/dsh-api-proxy.ts:316` wraps `startDshRemoteInteractions` in `try/catch`
37
+ and logs `[deeppilot] DSH Remote interaction client unavailable`. Breaks 3 and
38
+ 4 surface while the resident client is being assembled, so they land in that
39
+ catch. Whether break 1/2 (the Gateway opens its Remote Events generation
40
+ asynchronously) also lands there is **not confirmed** — an uncaught rejection
41
+ there would be fatal under Node's default `--unhandled-rejections=throw`.
42
+ The phone loses approval/question delivery either way.
43
+ - **Fix direction (verified compatible with both hosted versions):** build one
44
+ owner/answer Peer (`new Peer(ownerCtx, { principal: { kind: 'owner' },
45
+ interactions: 'answer' })`, falling back to `connection.operator` on alpha.2)
46
+ and name it in every carrier call — alpha.2 ignores a second `fetch` argument
47
+ and defaults an absent `open` peer to the operator, so one call form serves
48
+ both. `loadClientFaces` additionally has to accept the Node bundle's named
49
+ exports when the banner did not fire; both shapes export `apply`/`inject`
50
+ (`installConnection`/`transportError` are extra on Node).
51
+ - **Install verdict: the peer range now names this release explicitly.** It moved
52
+ from the open-ended `>=0.2.0-rc.2 <0.3.0-0` to `0.2.0-rc.2 || 0.2.1-alpha.3`
53
+ across all ten `@deepseek-ai/dsh*` peers, and the two vendor peers widened to
54
+ `~4.0.4 || ~4.0.5-alpha.1` and `~3.18.4 || ~3.18.5-alpha.1`. Because DSH
55
+ evaluates that range on every start, the next DSH release deactivates this
56
+ plugin until the list is widened deliberately — see `docs/决策记录.md`
57
+ (章节一, 条目2), which records this as a reversal of the 2026-09-29 policy.
58
+ The published CLI composed the plugin's patch cleanly before the change (see
59
+ the 2026-10-14 entry). The plugin's own `lib/client.js` shape was not
60
+ re-verified against the alpha.3 client-modules scanner.
61
+ - **Protocol verdict: no phone-protocol change.** Protocol v2, pairing state,
62
+ and device records are untouched by this release; no phone needs to re-pair.
63
+ - **Two host-side migrations this machine must apply** (independent of the
64
+ plugin): `~/.dsh/profiles/web/cordis.patch.yml` carries
65
+ `llm-deepseek-account.config.models: []`, and alpha.3 throws on any residual
66
+ `models` key — **including an empty array** (`resolveAccountOptions`). Also
67
+ `dsh --profile <name> --from-default-profile <template>` now *replaces* an
68
+ existing profile directory (it deletes the old `node_modules` and
69
+ `cordis.patch.yml`); the audit procedure recorded below used that flag.
70
+ - **Capability gaps, not regressions:** the `contextBreakdown` projection was
71
+ reshaped (`nodes[]` → `systems[]` + `surfaceTokens` + `claim`, stateVersion
72
+ 5→6) and the plugin forwards it by key, so the phone may render empty values;
73
+ the model catalog shrank (account routes return only the version-gated
74
+ `offered` subset, pi-ai routes without a resolvable key return `[]`); and
75
+ `CLIENT_UPGRADE_REQUIRED` / `MODEL_UNSUPPORTED` are new codes with no mapping
76
+ in `src/wire-errors.ts` yet.
77
+
78
+ ### Previous decision — `dsh-v0.2.1-alpha.2`
79
+
80
+ - **Audited release:** `dsh-v0.2.1-alpha.2` (official release page:
81
+ <https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.2>),
82
+ published to npm 2026-10-09T08:18:00Z on the `alpha` dist-tag. It is **not**
83
+ `latest`; `latest`/`next` still point at `0.2.0-rc.2`.
84
+ - **Comparison baseline:** `dsh-v0.2.1-alpha.1` (the previous audited tag).
85
+ - **Plugin runtime verdict: one confirmed break, fixed in this audit.**
86
+ `SessionController.projections` now requires its cancellation signal — the
87
+ published implementation calls `signal.throwIfAborted()` as the first
88
+ statement of its `try` and reads `signal.aborted` in its `catch`, while the
89
+ adapter called it with no signal at all, so every projection baseline read
90
+ failed with a `TypeError` instead of returning values. The adapter now passes
91
+ a signal and its hand-written mirror declares `signal` required. See "The one
92
+ confirmed break" in the 2026-10-09 entry for the reproduction, the fix, and
93
+ the regression test that pins it.
94
+ - **What is unaffected:** `list`, `inspect(sessionId)` (its signal is still
95
+ optional upstream), `create`, `fork`, `rename`, `cancel`, `attachment`,
96
+ `prompt`, `selectModel`, `modelCatalog`, the workspace controller, the Gateway
97
+ wire-stream seam the resident approval/question client depends on, the shared
98
+ Fetch carrier, and every peer package's exported runtime surface.
99
+ - **Install verdict:** the peer range `>=0.2.0-rc.2 <0.3.0-0` admits
100
+ `0.2.1-alpha.2` under `semver.satisfies(..., { includePrerelease: true })`,
101
+ which is how DSH's `evaluatePluginCompatibility` gates activation; npm's
102
+ **default** prerelease handling still reports peer warnings for
103
+ `@deepseek-ai/dsh`, `cordis`, and `schemastery`. Composing a real Web profile
104
+ with this plugin's `cordis.patch.yml` on the published CLI adds the plugin's
105
+ three rows plus its own config schema and no plugin-attributed diagnostic; the
106
+ four base-profile schema errors it prints reproduce identically without the
107
+ patch.
29
108
  - **Protocol verdict:** no required DeepPilot phone-protocol break. Protocol v2,
30
109
  pairing state, and device records are unchanged; already-paired phones need no
31
110
  action.
111
+ - **Capability gaps, not regressions:** the new `workingDirectory` projection,
112
+ the new `formatStatus` list field, the `mode: 'external'` subagent catalog, and
113
+ the `session/migration-required` fork refusal are all things this plugin does
114
+ not consume, and none of them change a call it already makes.
115
+
116
+ ### Plugin release verdicts
117
+
118
+ - **Release candidate (`0.9.5`, 2026-10-15):** carries the alpha.3 Peer/client
119
+ compatibility repair, the exact two-host peer policy, cross-language auth
120
+ vectors and recovery guidance, and the manual OIDC/provenance workflow.
121
+ The package and both lockfile version entries are `0.9.5`; the apply baseline
122
+ records that version. Clean install, 562 passing unit tests (zero failures,
123
+ one existing TODO), typecheck, build, the real pinned-host schema loader,
124
+ Go tests, all six helper checksums, and package inspection pass locally.
125
+ The altered-challenge test now mutates the challenge from its own socket,
126
+ eliminating the timestamp collision, and passes 20 consecutive focused runs.
127
+ Registry/provenance and clean published-profile checks remain release gates;
128
+ no physical-iPhone validation is claimed. Notes: `docs/RELEASE_NOTES-0.9.5.md`.
129
+
130
+ - **Release verdict (`0.9.4`, published 2026-10-10, tag `v0.9.4` @ `e2f77af`):**
131
+ carries the `projections` adapter fix and the Automation tasks documentation
132
+ correction described in the 2026-10-09 entry below. `package.json`, both root
133
+ `package-lock.json` version entries, and `tests/apply-baseline.snapshot.json`
134
+ are on `0.9.4`, and `lib/` is regenerated. The full checklist passed locally:
135
+ `npm ci`, 538 unit tests, typecheck, build, the config-schema check against a
136
+ real `0.2.0-rc.2` CLI, Go helper tests, helper checksums, and
137
+ `npm pack --dry-run` (65 files, 50,916,470 bytes packed). The fix was also
138
+ verified on a live `0.2.1-alpha.2` host against the published `0.9.3` as an
139
+ A/B control (25 pass / 0 fail either way, with the `sessions.projections
140
+ failed: internal` diagnostic present only on `0.9.3`).
141
+ - **Registry verification after publishing.** `dist-tags.latest` moved off
142
+ `0.9.3`; `dist.shasum` `85b3c71a802e45e42f5fb1b23444ed2e396d39dc` is
143
+ byte-identical to the local `npm pack` output at the tag; 65 files,
144
+ `dist.unpackedSize` 130,789,604. **`gitHead` is `e2f77af`, the tag commit** —
145
+ the 0.9.3 ordering deviation did not repeat, because the release notes and
146
+ every other documentation change were committed before the pack. The publish
147
+ itself needed the maintainer's one-time password, as in 0.9.3; everything up
148
+ to and after it was done here.
149
+ - **Clean-profile verification.** A fresh isolated home, built from the shipped
150
+ Web template and installed with the published `0.2.1-alpha.2` CLI
151
+ (`dsh plugin --profile verify add dsh-deeppilot@0.9.4`), composed a real
152
+ profile: `--dump-config` printed the plugin's three rows
153
+ (`deeppilot-directory-picker-browse`, `…-browse-client`, `deeppilot`) with no
154
+ missing-module or load error, the installed package's six helper checksums
155
+ verified, every helper binary embedded `go1.26.9`, and the installed
156
+ `lib/index.js` carried the projections fix. Only then was the GitHub Release
157
+ taken out of draft and marked Latest.
158
+ - **The release also had to fix the helper's toolchain and its refresh
159
+ workflow.** CI's `govulncheck` job reported 11 reachable Go standard-library
160
+ vulnerabilities for the pinned `go 1.26.6`; this is time-triggered, not
161
+ caused by the release — `0.9.3`'s CI was green on 2026-10-05 with the same
162
+ helper source. `helper/go.mod` now pins `go 1.26.9`, and the six `bin/`
163
+ binaries were rebuilt with it. That rebuild only became correct after fixing
164
+ `.github/workflows/build-helpers.yml`: each build job uploaded the whole
165
+ `bin/` tree, so every artifact also carried the checked-out copies of the
166
+ targets its runner never rebuilt, and `merge-multiple` let those stale files
167
+ overwrite the fresh ones (the first refresh PR mixed two toolchains — only
168
+ darwin-arm64 embedded `go1.26.9`). The workflow now uploads only the
169
+ per-matrix target globs; PR #19 then carried all six at `go1.26.9` and was
170
+ merged as `f0f243b`.
171
+ - **Checklist environment notes for the next release.**
172
+ (a) `~/.npm/_cacache` contains root-owned files on this machine, so `npm view`
173
+ and `npm pack` fail with `EPERM` unless `npm_config_cache` points at a
174
+ writable cache — the run above used the workspace audit cache.
175
+ (b) Go's default build cache lives outside the workspace and the file sandbox
176
+ blocks it (`open .../Library/Caches/go-build/...: operation not permitted`,
177
+ reported as `[setup failed]`); point `GOCACHE` at a workspace directory.
178
+ (c) `gh` writes its run-log cache under `~/.cache/gh` and fails the same way;
179
+ point `XDG_CACHE_HOME` at a workspace directory.
180
+ None of the three is a repository defect.
181
+ - **Release verdict (`0.9.3`, published 2026-10-05, tag `v0.9.3` @ `b85869b`):**
182
+ shipped to `latest`. Verified on the registry: version present, `dist-tags.latest`
183
+ moved off `0.9.2`, 65 files, `dist.shasum` `90f0c665c395a7043eb3689a291cf28c88bc39ab`
184
+ byte-identical to the locally verified `npm pack` output, and a clean install plus
185
+ a real `dsh --profile web --dump-config` compose (188 entries) with the plugin's
186
+ three `cordis.patch.yml` entries present and no missing-module error. Ships
187
+ issue #24 (`100.64.0.0/10` becomes a selectable pairing target) and the Windows
188
+ journal `fsync` tolerance fix. Protocol v2 unchanged; paired phones need no action.
189
+ - **`gitHead` deviation, recorded rather than hidden.** The registry records
190
+ `gitHead` `fbda6a5`, the docs-only commit that added `RELEASE_NOTES-0.9.3.md`,
191
+ while tag `v0.9.3` points at `b85869b`. The cause is ordering, not content:
192
+ the tarball was packed before that docs commit, and npm stamps `gitHead`
193
+ from HEAD at publish time. The shipped bytes are provably `b85869b`'s tree —
194
+ the published `dist.shasum` equals the local `npm pack` output and the tarball
195
+ does not contain `RELEASE_NOTES-0.9.3.md`. `fbda6a5` is `b85869b`'s child and
196
+ changes no packaged file. Versions are immutable on npm, so this cannot be
197
+ corrected retroactively; **do it differently next time: commit every doc file
198
+ before packing, and pack after the last commit.** The 0.9.2 entry below had
199
+ `gitHead` matching the tag because no commit landed between pack and publish.
200
+ - **Install caveat users will hit — DSH's release-age policy.** Confirmed again
201
+ while verifying this release: `dsh plugin add --profile web` writes a
202
+ `minimumReleaseAgeExclude` entry in `pnpm-workspace.yaml` and an explicit
203
+ `dsh-deeppilot@0.9.3` installs the new version. Expect the unversioned form to
204
+ resolve to a slightly older release for a while; this is expected DSH
205
+ behaviour, not a publishing failure.
32
206
  - **Release verdict (`0.9.2`, published 2026-10-05, tag `v0.9.2` @ `4b43671`):**
33
207
  shipped to `latest`. Verified on the registry: version present, `dist-tags.latest`
34
208
  moved off `0.9.1`, `gitHead` `4b436715` matches the tag and HEAD, 65 files,
@@ -56,15 +230,47 @@
56
230
  also `optional` in `peerDependenciesMeta`. Confirm installability by
57
231
  composing a real profile, not by iterating the peer table.
58
232
  - **Two tracked follow-ups:**
59
- 1. **Schedule documentation is now stale.** Automation tasks became a
60
- built-in Web capability and `@deepseek-ai/dsh-experimental-schedule-bundle`
61
- is in DSH's new `RETIRED_BUNDLES` set, so the user instruction "enable
62
- Automation tasks in the plugin manager" points at a retired bundle.
63
- Affects `PROTOCOL.md`, `COMPATIBILITY.md`, and the iOS copy. **Open.**
64
- 2. ~~`subagent_session` is unmapped.~~ **Resolved 2026-10-03:** mapped to
233
+ 1. ~~Projection signal.~~ **Resolved 2026-10-10:** the adapter passes
234
+ `new AbortController().signal` to `session.projections` and the mirror
235
+ declares `signal` required, so the compiler enforces it. Covered by a new
236
+ regression test that fails against the previous call form. 538 unit tests,
237
+ typecheck, and build pass with regenerated `lib/`.
238
+ 2. **Automation tasks documentation: public docs fixed, iOS copy open.**
239
+ Automation was a short-lived optional bundle: DSH 0.2.0 moved it out of the
240
+ shipped Web composition, and the following release retired
241
+ `@deepseek-ai/dsh-experimental-schedule-bundle` again — `dsh-web-app` now
242
+ mounts `schedule` and `ui-schedule` in every Web profile. `COMPATIBILITY.md`,
243
+ `PROTOCOL.md`, `docs/DEEPPILOT_FEATURE_PLAN.md`, and the schedule-service
244
+ comment in `src/dsh-api-proxy.ts` now state that; the private app still
245
+ ships `error.scheduleUnsupported` and `schedule.unsupported` copy telling
246
+ the user to enable it (`app-private/ios/L10nFragments/schedule.json` and the
247
+ generated `zh-Hans.lproj` / `en.lproj` string tables). **iOS copy still
248
+ open** — it belongs to the private repository and to its own release flow.
249
+ 3. ~~`subagent_session` is unmapped.~~ **Resolved 2026-10-03:** mapped to
65
250
  `E_PROTOCOL` in `src/wire-errors.ts` and added to the frozen expectation in
66
251
  `tests/wire-errors.test.ts`. 525 unit tests, typecheck, and build pass.
67
252
 
253
+ ### Previous decision — `dsh-v0.2.1-alpha.1`
254
+
255
+ - **Audited release:** `dsh-v0.2.1-alpha.1`, published 2026-10-03 (npm
256
+ `alpha` dist-tag, also not `latest`). Comparison baseline was
257
+ `dsh-v0.2.0-rc.2`.
258
+ - **Plugin runtime verdict:** no breaking change in the DSH APIs DeepPilot calls.
259
+ All 12 peer packages had **zero** changed `src/` files; each changed only its
260
+ `package.json` version field and its READMEs. Full table in the 2026-10-03
261
+ entry below.
262
+ - **Nothing was changed to support that release.** `package.json`,
263
+ `package-lock.json`, generated `lib/`, `COMPATIBILITY.md`, and `PROTOCOL.md`
264
+ stayed on the `0.2.0-rc.2` baseline — no version bump, no dependency install,
265
+ no `lib/` regeneration. The same held for its source diff; the alpha.2 audit
266
+ that followed then produced one adapter fix (see the 2026-10-09 entry).
267
+ - **Peer range:** `>=0.2.0-rc.2 <0.3.0-0` admitted `0.2.1-alpha.1` through
268
+ `semver.satisfies(..., { includePrerelease: true })`; the `~4.0.4` / `~3.18.4`
269
+ vendor ranges admitted `4.0.5-alpha.1` / `3.18.5-alpha.1` the same way, and
270
+ npm's default prerelease rules reported warnings for those three.
271
+ - **Protocol verdict:** no required phone-protocol break; protocol v2, pairing
272
+ state, and device records unchanged.
273
+
68
274
  ### Previous decision — `dsh-v0.2.0-rc.2`
69
275
 
70
276
  - **Audited release:** `dsh-v0.2.0-rc.2` (official release page:
@@ -100,6 +306,470 @@
100
306
  bundle" below. The plugin needs no code change; the phone's copy now points
101
307
  at the bundle.
102
308
 
309
+ ## 2026-10-14 — audit of `dsh-v0.2.1-alpha.3`
310
+
311
+ ### Scope and method
312
+
313
+ Compared `dsh-v0.2.1-alpha.2...dsh-v0.2.1-alpha.3` (**852 commits, 3311 changed
314
+ files, 120,978 insertions, 16,632 deletions**) by fetching the new tag into the
315
+ existing blobless clone (`.audit-dsh-alpha2`, whose HEAD was already the alpha.2
316
+ audit's target SHA `d7432673`), then `git diff --name-status` and per-path
317
+ content diffs into `.audit-alpha3/diffs/`. Four parallel read-only passes
318
+ covered `packages/api` + `apps/cli` + `packages/typert` + `packages/settings` +
319
+ `vendor`, `packages/client/{connection,locale,ui-settings,ui-slots}` +
320
+ `packages/boot` + `packages/bundle`, `packages/{credentials,subagent,session,
321
+ schedule}`, and `packages/{core,host,skill,llm}`. All **14** upgrade guides
322
+ added by this release (under `docs/upgrade-guide/v0.2.0-rc.2/`,
323
+ `v0.2.1-alpha.1/`, `v0.2.1-alpha.2/`) were read in Chinese in full.
324
+
325
+ The alpha.2 audit's lesson — judge the seams reached through `ctx.get`, not just
326
+ the peer list — is what caught this one: the peer list is *not* where the breaks
327
+ are. `@deepseek-ai/dsh-api-remote-access` is new, `packages/schedule/schedule`
328
+ has **zero** changed source files, and the peer packages' own exported surfaces
329
+ moved far less than the carrier contracts around them.
330
+
331
+ ### The four confirmed breaks
332
+
333
+ Every one is the same root cause: alpha.3 makes the Peer explicit. `PeerScope`
334
+ (`@deepseek-ai/dsh-typert-protocol`) gained required `principal`, `signal`, and
335
+ `interactions`; `client/connection` added a `Peer` class and deleted
336
+ `OperatorPeer`; the Gateway deleted `operatorPeer()` and its
337
+ `request.peer ?? this.operatorPeer()` fallback.
338
+
339
+ Reproduction used the **published** bundles, the same technique as the alpha.2
340
+ `projections` break, with the plugin's exact argument list
341
+ (`open('$events', payload, undefined, peer, signal)`):
342
+
343
+ ```sh
344
+ # .audit-alpha3/repro-peer-contract.mjs, against
345
+ # alpha.2: ~/.npm/_npx/b5c6312874079fe8/node_modules/@deepseek-ai/dsh-api-gateway/lib/index.js
346
+ # alpha.3: .audit-alpha3/cli-install/node_modules/@deepseek-ai/dsh-api-gateway/lib/index.js
347
+ # A. peer = undefined (what the plugin sends today)
348
+ alpha.2: 未抛错
349
+ alpha.3: 抛错 TypeError: Cannot read properties of undefined (reading 'signal')
350
+ # B. peer = { principal: owner, signal, interactions: 'none' }
351
+ alpha.2: 未抛错
352
+ alpha.3: 抛错 TypertGatewayError: $events: forwarded Remote events require interactions=answer
353
+ # C. peer = { principal: owner, signal, interactions: 'answer' }
354
+ alpha.2: 未抛错
355
+ alpha.3: 未抛错
356
+ ```
357
+
358
+ Break 4 was settled by reading the shipped artifacts rather than by inference:
359
+ `@deepseek-ai/dsh-client-connection`'s `./client` now maps `node` →
360
+ `lib/client.node.js`, and that file contains **zero** `__ModuleLoader__`
361
+ occurrences while `client.browser.js` and alpha.2's `client.js` each contain
362
+ one. `loadClientFaces` builds its module table *only* from that banner, so on
363
+ Node the table stays empty. Both shapes export `apply` and `inject`, which is
364
+ what makes a banner-optional fallback possible.
365
+
366
+ ### The fix
367
+
368
+ One call form serves both audited hosts, because alpha.2 ignores a second
369
+ `fetch` argument and defaults an absent `open` peer to the operator Peer it
370
+ holds:
371
+
372
+ - `src/dsh-remote-interactions.ts` mints one Peer through
373
+ `connection.createPeer({ principal: { kind: 'owner' }, interactions: 'answer' })`
374
+ and falls back to `connection.operator` on a Host without that entry point.
375
+ The same Peer is named on `fetch` and on `wireStream.open`.
376
+ - The Connection install prefers the explicit
377
+ `installConnection(ctx, { transport, interactions: 'answer' })`, because
378
+ `apply(ctx)` cannot name the participation and therefore always selects
379
+ `'none'` for this caller; `apply` remains the fallback for a host that predates
380
+ the installer.
381
+ - `loadClientFaces` accepts a Node bundle's named exports when the browser
382
+ banner did not register them. A banner registration still wins, because the
383
+ bundle's own internal `require()` resolves against it.
384
+
385
+ Six regression tests in `tests/dsh-wire-stream-contract.test.ts` pin the
386
+ contract: the operator fallback, the alpha.3 `createPeer` request shape and the
387
+ Peer reaching `wireStream.open`, both installer branches, and both module-table
388
+ branches. The file fails against the previous call form — verified by restoring
389
+ the source and re-running (1 failure) — and passes after the fix.
390
+
391
+ ### Peer range narrowed to the two audited hosts
392
+
393
+ By explicit user direction the DSH peer range stopped being open-ended:
394
+ `>=0.2.0-rc.2 <0.3.0-0` became `0.2.0-rc.2 || 0.2.1-alpha.3` across all ten
395
+ `@deepseek-ai/dsh*` peers, with `@deepseek-ai/cordis` widened to
396
+ `~4.0.4 || ~4.0.5-alpha.1` and `@deepseek-ai/schemastery` to
397
+ `~3.18.4 || ~3.18.5-alpha.1`. The two vendor peers are outside DSH's own peer
398
+ check — it reads only `@deepseek-ai/dsh` and `@deepseek-ai/dsh-*` — so their
399
+ ranges matter only to npm's install-time resolution, which runs without
400
+ `includePrerelease`; naming both vendored prereleases keeps a clean install on
401
+ either host. `devDependencies` stay pinned to `0.2.0-rc.2`.
402
+
403
+ The consequence is deliberate: `evaluatePluginCompatibility` runs before a
404
+ profile is composed on every start, so the next DSH release will refuse to
405
+ activate this plugin until the list is widened by an audit. That reversal of the
406
+ 2026-09-29 policy is recorded in `docs/决策记录.md` (章节一, 条目2).
407
+ `tests/compatibility-metadata.test.ts` now asserts both admissions, fifteen
408
+ rejections, and the two vendor ranges.
409
+
410
+ ### Host-side changes with no plugin impact
411
+
412
+ `packages/schedule/schedule/**` has no changed source; the schedule error-code
413
+ table is identical, so `src/wire-errors.ts`'s twelve mappings (including
414
+ `subagent_session → E_PROTOCOL`) stay correct. No session-log format migration
415
+ exists — `session-format*`, `v0-to-v1`…`v3-to-v4`, `session-persistence`,
416
+ `session-projection*`, `session-log-deepseek` changed only their version and
417
+ locale files — so phone history rendering is unaffected. The subagent descriptor
418
+ is still version 3. `host/webserver`'s `port` widening, the macOS directory
419
+ picker, the PTC call registry, and the new `job/registered` / `job/settled`
420
+ events are all unreachable or safely skipped by this plugin (`projectEvent`'s
421
+ `default: return null`). Settings surfaces are unchanged at the contract level:
422
+ `ui-settings`'s `settings.section` slot has the same SHA as alpha.2, and
423
+ `configForms.get()` kept its name. The Web composition still mounts
424
+ `schedule`/`ui-schedule`, and `directory-picker` is still disabled in place, so
425
+ the plugin's three `cordis.patch.yml` rows still land.
426
+
427
+ ### Validation evidence
428
+
429
+ - **Config schema on the published CLI:** isolated `DSH_HOME`, `dsh
430
+ --profile deeppilot-audit --from-default-profile sdk-minimal
431
+ --dump-config-schema --patch cordis.patch.yml` against
432
+ `@deepseek-ai/dsh@0.2.1-alpha.3` → exit 0, 179,851 bytes, the plugin entry at
433
+ `/37` reports `status: 'schema'` with `configRef: #/$defs/config24`, all seven
434
+ volatile assertions pass, no `deeppilot`-attributed diagnostic, and the only
435
+ warning is the `sdk-minimal` template's own missing `directory-picker` row.
436
+ - **Web composition:** the same CLI composed a real Web profile with the plugin
437
+ patch → **1225 rows versus 1214 without it**, the plugin's three rows present
438
+ at `deeppilot-directory-picker-browse` / `…-browse-client` / `deeppilot`, and
439
+ zero `unrecognized Loader tree carrier` entries on either side.
440
+ - **Plugin unit tests:** `npx tsx --test tests/*.test.ts` → **562 tests, 561
441
+ pass, 0 fail, 1 todo**, including the six new contract tests. `npm run
442
+ typecheck`, `npm run build`, and `npm run check:config-schema` (against the
443
+ pinned `0.2.0-rc.2` CLI) all pass with regenerated `lib/`.
444
+ - **Live A/B on a real `0.2.1-alpha.3` host — the evidence that matters.** An
445
+ isolated `DSH_HOME` profile built from the shipped Web template and linked to
446
+ this working tree booted under the published alpha.3 CLI (`--port 3180`; the
447
+ default 3080 was already held by this machine's own Host). The **unfixed**
448
+ `0.9.4` build printed
449
+ `[deeppilot] DSH Remote interaction client unavailable:
450
+ @deepseek-ai/dsh-typert-registry/client Client bundle returned no exports`
451
+ and the fixed build printed no such line. The checklist itself scored
452
+ **23 pass / 1 fail / 4 skipped in both runs** — see the blind spot below.
453
+ - **The regression checklist does not cover this seam.** `scripts/smoke-live.mts`
454
+ scored identically before and after the fix, because the resident Client it
455
+ exercises is only reachable through the approval/question waterfall, and step
456
+ 10 is skipped without `--interactions`. The observable difference is the Host
457
+ log line and the phone's approval/question delivery, not a checklist step. A
458
+ future revision should assert on that diagnostic directly.
459
+ - **Backward compatibility, same working tree:** the same profile booted under
460
+ the published `0.2.1-alpha.2` CLI (`--port 3181`) scored **25 pass / 0 fail /
461
+ 0 warn / 3 skipped** with no interaction-client diagnostic — the same result
462
+ the alpha.2 audit recorded, so the fix does not regress the host users run
463
+ today.
464
+ - **One checklist difference between the two host versions is real.**
465
+ `模型目录可读` passes on alpha.2 and fails on alpha.3 in the same isolated
466
+ home (`0 provider / 0 model, routable=true`): this release stops listing a
467
+ pi-ai route before its credential resolves and returns only the version-gated
468
+ subset for the account route. The home has no model credential, so the phone's
469
+ model list is empty there. That is the documented behavior change, not a
470
+ plugin defect.
471
+ - **Artifact inspection:** the `node`/`browser` conditional exports and the
472
+ banner counts above were read from the installed alpha.3 packages, not from
473
+ the diff.
474
+
475
+ ### Host-side migrations this machine must apply
476
+
477
+ Neither is a plugin defect, and neither is fixed by the plugin change:
478
+
479
+ 1. **`llm-deepseek-account`'s retired `models` key.**
480
+ `~/.dsh/profiles/web/cordis.patch.yml:77-79` sets `models: []`.
481
+ `resolveAccountOptions` throws for *any* own `models` key
482
+ (`llm-deepseek-account: models is no longer configurable…`), and the
483
+ upstream test pins `[{ models: [] }, /models is no longer configurable/]`.
484
+ The empty array is not exempt. The `desktop` profile's row carries no
485
+ `config` and is unaffected. Whether the throw takes the whole Host down or
486
+ only retires that route was **not** measured — the guide says the account
487
+ models stop appearing either way.
488
+ 2. **`--from-default-profile` now replaces.** It stages a fresh copy over the
489
+ target and deletes the previous directory, including `node_modules` and the
490
+ user's own `cordis.patch.yml`. The re-audit procedure below and the alpha.2
491
+ entry's schema command both use that flag; from now on it must be pointed at
492
+ a scratch name.
493
+
494
+ Checked and **not** required: Azure provider rename (no `azure` /
495
+ `azure-openai-responses` key exists under `~/.dsh/profiles`), credential-file
496
+ extra keys (the file's top level is already `version`/`refs`/`records`), and
497
+ the stored-credential precedence flip (no `refs` name is exported in the shell,
498
+ and the stored value won precedence over `.env` on both sides of the change).
499
+
500
+ ### Still unverified
501
+
502
+ - Whether breaks 1 and 2 surface inside `dsh-api-proxy.ts`'s catch or escape it
503
+ as an unhandled rejection — the Gateway opens its Remote Events generation
504
+ asynchronously, after `startDshRemoteInteractions` has already returned.
505
+ - Whether an old profile that predates `@deepseek-ai/dsh-api-remote-access`
506
+ still gets the row (the shipped Web template does; `dsh-base` carries it).
507
+ - How the reshaped `contextBreakdown` renders on iOS, and what the phone shows
508
+ for the unmapped `CLIENT_UPGRADE_REQUIRED` / `MODEL_UNSUPPORTED` codes.
509
+ - Whether the plugin's own `lib/client.js` (banner shape, `dsh.client.inject`,
510
+ `platform: web`) is still accepted by alpha.3's client-modules scanner; that
511
+ scanner lives in `packages/client/modules` and `packages/client/web`, which
512
+ were not read in full.
513
+
514
+ ### Re-audit procedure used
515
+
516
+ Fetch the new tag into the existing blobless clone at `.audit-dsh-alpha2`
517
+ (`git fetch --filter=blob:none --no-tags origin
518
+ '+refs/tags/<tag>:refs/tags/<tag>'`, ~3 s), slice
519
+ `git diff --name-status` per package, and write each slice's content diff to a
520
+ file before parallel reading — blobless clones fetch blobs on demand, so one
521
+ `git diff` per region is far cheaper than many small reads. Reproduce a
522
+ suspected carrier break against the **published** `lib/` bundles with a stub
523
+ `this`, as above, rather than trusting either the diff or the release notes.
524
+ `npm --cache <workspace dir>` is still required; `~/.npm/_cacache` holds
525
+ root-owned files and fails `npm view` with `EPERM`.
526
+
527
+ ## 2026-10-09 — audit of `dsh-v0.2.1-alpha.2`
528
+
529
+ ### Scope and method
530
+
531
+ Compared `dsh-v0.2.1-alpha.1...dsh-v0.2.1-alpha.2` (**669 commits, 3226 changed
532
+ files, 114,987 insertions, 28,177 deletions**) from a full clone
533
+ (`git clone --filter=blob:none --no-checkout`, unshallowed before measuring).
534
+ Baseline SHA `5badb150`, target SHA `d7432673`, annotated tag object `6851e496`.
535
+ Peer package names were mapped to monorepo paths by reading every `package.json`
536
+ in the tree, then each peer package's `src/**/*.ts` was diffed with
537
+ `git diff --name-status`. Every changed source file under `packages/api`,
538
+ `packages/session*`, `packages/client`, and `packages/core` was read in full —
539
+ including the complete 836-line session-controller diff — rather than being
540
+ summarised from release notes. All **20** upgrade guides added since the last
541
+ audit (19 under `docs/upgrade-guide/v0.2.1-alpha.1/` plus `bundled-python-tests`
542
+ under `v0.2.0-rc.2/`) were read in Chinese in full, and each one that could touch
543
+ an integration seam was traced to its implementation before being judged. No
544
+ guide from an earlier release was modified between the two tags.
545
+
546
+ ### Peer packages: 2 of 12 changed source
547
+
548
+ | Package | Path | Changed `src/` files |
549
+ | --- | --- | --- |
550
+ | `@deepseek-ai/dsh` | `apps/cli` | 1 (`src/args.ts`) |
551
+ | `@deepseek-ai/dsh-client-connection` | `packages/client/connection` | 5 (`api-request-trust.ts`, `browser-auth.ts`, `index.ts`, `rpc-host.ts`, `rpc.ts`) |
552
+ | `@deepseek-ai/dsh-api-gateway` | `packages/api/gateway` | 0 |
553
+ | `@deepseek-ai/dsh-api-remotes` | `packages/api/remotes` | 0 |
554
+ | `@deepseek-ai/dsh-client-locale` | `packages/client/locale` | 0 |
555
+ | `@deepseek-ai/dsh-client-ui-settings` | `packages/client/ui-settings` | 0 |
556
+ | `@deepseek-ai/dsh-client-ui-slots` | `packages/client/ui-slots` | 0 |
557
+ | `@deepseek-ai/dsh-settings` | `packages/settings/settings` | 0 |
558
+ | `@deepseek-ai/dsh-typert-protocol` | `packages/typert/protocol` | 0 |
559
+ | `@deepseek-ai/dsh-typert-registry` | `packages/typert/registry` | 0 |
560
+ | `@deepseek-ai/cordis` | `vendor/cordis` | 0 |
561
+ | `@deepseek-ai/schemastery` | `vendor/schemastery` | 0 |
562
+
563
+ `react` is not an upstream package and was not measured. Two non-peer packages
564
+ that the bridge reaches through `ctx.get` did change and are covered below:
565
+ `packages/api/session-controller` and `packages/client/connection`.
566
+
567
+ ### The one confirmed break
568
+
569
+ `packages/api/session-controller/src/index.ts` started dereferencing a parameter
570
+ that upstream's own published declaration already marks as required:
571
+
572
+ ```ts
573
+ async projections(request: SessionProjectionsRequest, signal: AbortSignal): Promise<SessionProjectionsValue> {
574
+ const { sessionId } = request
575
+ if (sessionId.length === 0) throw new RemoteError('gateway/bad-request', ...)
576
+ try {
577
+ signal.throwIfAborted() // now the first statement
578
+ const live = this.liveProjections(sessionId)
579
+ ...
580
+ } catch (error) {
581
+ ...
582
+ if (signal.aborted || ...) throw new RemoteError('gateway/cancelled', ...) // now unconditional
583
+ ...
584
+ }
585
+ }
586
+ ```
587
+
588
+ In alpha.1 the same method only forwarded `signal` into `observeSession` and read
589
+ `signal.aborted` inside the `catch`, so an omitted signal still survived the happy
590
+ path. In alpha.2 the omission throws before any work is done — reproduced against
591
+ the published `0.2.1-alpha.2` bundle with the real class, a stub context whose
592
+ live-projection path returns a value, and no signal:
593
+
594
+ ```sh
595
+ # published @deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2, lib/index.js
596
+ SessionController.prototype.projections.call(receiver, { sessionId: 'x' })
597
+ # → TypeError (signal is undefined at signal.throwIfAborted(), then again at signal.aborted)
598
+ SessionController.prototype.projections.call(receiver, { sessionId: 'x' }, new AbortController().signal)
599
+ # → { kind: 'sequenced', asOfSeq: 7, values: { title: 'audit' } }
600
+ ```
601
+
602
+ DeepPilot's call site is `src/dsh-api-proxy.ts:235`:
603
+
604
+ ```ts
605
+ projections: async (request) => this.call(() => this.session.projections(request.payload!)),
606
+ ```
607
+
608
+ The hand-written mirror at `src/dsh-api-proxy.ts:45` declares `signal?` optional,
609
+ which is exactly what let the omission typecheck; the upstream declaration
610
+ (`@deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2`,
611
+ `lib/types/index.d.ts:209`) requires it. `DshApiProxy.call()`
612
+ (`src/dsh-api-proxy.ts:413`) converts the `TypeError` into
613
+ `{ ok: false, error: { code: 'internal' } }`, so this degrades rather than
614
+ crashes: `HostBridge.refreshProjections` (`src/host-bridge.ts:799`) already treats
615
+ a failed baseline as a diagnostic and logs
616
+ `sessions.projections failed: internal`.
617
+
618
+ **Observable effect.** The phone's session summary loses the on-open complete
619
+ projection baseline. It still receives title, todo counts, and token usage from
620
+ the session list rows' cached projections (`src/dsh-api-proxy.ts:458`,
621
+ `src/host-bridge.ts:1546`) and from live `session/projection` frames
622
+ (`src/host-bridge.ts:453`), so nothing breaks outright; the loss shows up on
623
+ hosts where the baseline read was the only source — a session whose list row
624
+ carries no cached projections stays without those values longer than it should.
625
+ Cold history reads are **not** affected: they run through
626
+ `inspect(sessionId)`, whose signal is still optional upstream.
627
+
628
+ **Fix (applied 2026-10-10).** The adapter now passes a signal the way `list` and
629
+ `prompt` already do —
630
+ `this.session.projections(request.payload!, new AbortController().signal)` — and
631
+ the mirror's `signal` is required, so the compiler keeps it that way.
632
+ `refreshProjections` is fire-and-forget and has no cancellation source of its
633
+ own, so a dedicated long-lived controller is not warranted; following the
634
+ existing convention in the same file is enough. `lib/index.js` was regenerated.
635
+
636
+ The fix ships in `0.9.4`, published 2026-10-10 (see "Plugin release verdicts").
637
+ A host on `0.2.1-alpha.2` left on the published `0.9.3` still loses the on-open
638
+ projection baseline. That was observed directly: on a live `0.2.1-alpha.2` host
639
+ running published `0.9.3` the diagnostic appeared, and on the same host version
640
+ running this build it did not (see "Live run on a real `0.2.1-alpha.2` host"
641
+ under Validation evidence).
642
+
643
+ `tests/dsh-api-proxy.test.ts` adds "projection baseline reads pass the
644
+ AbortSignal the Host now requires": a controller stub that calls
645
+ `signal.throwIfAborted()` before returning, plus an assertion that the adapter
646
+ handed it a real `AbortSignal`. The test was verified red against the previous
647
+ call form (1 failure) and green after the fix, so it pins the behaviour rather
648
+ than the implementation coincidence.
649
+
650
+ ### Other session-controller changes, checked against this plugin
651
+
652
+ `Where` names the upstream `packages/api/session-controller/src/` file unless it
653
+ says otherwise; `lib/types` means the published declarations of
654
+ `@deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2`.
655
+
656
+ | Change | Where | DeepPilot impact |
657
+ | --- | --- | --- |
658
+ | `projections` return type is now a union: `{ kind: 'sequenced', asOfSeq, values }` / `{ kind: 'migration-required', values }` / `null` | `src/index.ts:506-541` | The mirror's flat `{ asOfSeq, values }` type is now narrower than upstream, but the plugin reads only `values`, so a successful read still folds. Type-mirror drift, not a runtime defect. |
659
+ | `inspect(sessionId, signal?)` | `packages/session/session-query` | Signal is still optional in the published declaration, so the plugin's `inspect(sessionId)` call keeps working. Cold log reading, scanner row decoding, and the V4 codec path were read and are unchanged for this caller. |
660
+ | `search(request, signal)` requires a signal | `lib/types/index.d.ts:93` | The plugin never calls `search`. |
661
+ | `page(request, signal)` requires a signal | `src/index.ts:1412` | Not called; history paging still goes through `inspect`. |
662
+ | `fork` gains `allowMigration` (default `true`) | `src/index.ts` | The plugin does not send it, so the upstream default keeps automatic migration. The new `session/migration-required` error is unreachable from the phone. |
663
+ | `skill-catalog` injects `workingDirectory` and lists skills by execution directory | `src/skill-catalog.ts` | Not consumed; the plugin lists no skills. |
664
+ | Session ownership helper | `packages/api/session-controller` | Unchanged; `session/agent-busy` still maps to `E_BUSY`. |
665
+ | Subagent catalog v2 `mode: 'external'` | `packages/subagent/subagent` | Descriptor version is still 3 and the earlier one-shot read types are retained; the plugin filters by origin/parent and does not fold catalogs. |
666
+
667
+ ### Connection carrier: unchanged on the paths this plugin uses
668
+
669
+ `packages/client/connection` gained five changed source files, including a new
670
+ `api-request-trust.ts` and browser-auth changes. Those trust, authentication, and
671
+ TLS checks run through `requestRejection`/`admit`, which guard **physical**
672
+ requests. The plugin's resident client uses
673
+ `connection.createSharedFetchHandler('/api')`, which dispatches routes directly
674
+ and never calls `admit` (`packages/client/connection/src/rpc-host.ts:136-154`), and
675
+ its stream call keeps the
676
+ five-argument `wireStream.open(endpoint, payload, uplink, peer, signal)` order.
677
+ The plugin's own LAN TLS pin check and `/phone` listener are unaffected.
678
+
679
+ ### Four-way comparison at `0.2.1-alpha.2`
680
+
681
+ | Surface | Both sides | Upstream has, plugin does not consume | Plugin has, upstream does not |
682
+ | --- | --- | --- | --- |
683
+ | Session RPCs | `list`, `inspect`, `create`, `fork`, `rename`, `cancel`, `prompt`, `attachment`, `selectModel`, `modelCatalog`, `projections` | `search`, `page`, `follow`, `updateQueue`, `initializeDefaultModel`, `canOpenWorkspacePath`, `openWorkspacePath`, `workspacePathApplications` | phone-shaped `sessions.*` envelope, history windowing, projection folding, notification suppression |
684
+ | Workspace RPCs | `create`, `archiveSession`, `unarchiveSession`, `follow` | `initializeDefault`, `rename`, `delete`, `insertBefore`, `insertSessionBefore`, `pinSession`, `unpinSession` | archived mirror, `sessionRestore` capability bit |
685
+ | Directory picker | `list`, `pick` | `createDirectory` | browse host/client patch rows, phone-side directory browsing |
686
+ | Schedule | `list`, `history`, `create`, `update`, `delete` | `catalog`, stream delivery modes | phone schedule frames, `schedule.manage` scope, prompt-free mutation journal |
687
+ | Projections | reading `values` | `workingDirectory`, `formatStatus`, the migration-required branch | `title` / `todos` / `sessionStats` / `tokenUsage` folding |
688
+ | Subagents | — | `prompt`, `interruptByParent`, catalog v2 | subagent-session exclusion and notification policy |
689
+ | Trust and auth | shared Fetch route dispatch, TLS pin check | `webStartup.trustedHosts`, listener-address validation | device records, pairing tokens, signed challenge |
690
+
691
+ Rows marked "upstream has, plugin does not consume" are capability gaps by
692
+ choice, not regressions: none of them changes a call the bridge already makes.
693
+ `neither side` is empty for every row that matters to the phone.
694
+
695
+ ### Validation evidence
696
+
697
+ - **Unit tests:** `tsx --test tests/*.test.ts` → **537 pass, 0 fail, 0 skipped**
698
+ on the source diff alone. After the adapter fix and its new regression test the
699
+ same command reports **538 pass, 0 fail, 0 skipped**, and `npm run typecheck`
700
+ and `npm run build` pass with regenerated `lib/`.
701
+ - **Config schema on the published CLI:** an isolated `DSH_HOME` and npm cache ran
702
+ `dsh --profile deeppilot-audit --from-default-profile sdk-minimal
703
+ --dump-config-schema --patch cordis.patch.yml` against
704
+ `@deepseek-ai/dsh@0.2.1-alpha.2` → the plugin entry reports
705
+ `status: 'schema'` with its own `$defs` config, the volatile-field checks pass,
706
+ and the only warning is the `sdk-minimal` template's own missing
707
+ `directory-picker` row.
708
+ - **Web composition:** the same CLI composed a real Web profile with the plugin
709
+ patch → 188 rows versus 185 without it; the plugin's three rows
710
+ (`deeppilot-directory-picker-browse`, `…-browse-client`, `deeppilot`) are
711
+ present with the plugin config schema at `#/$defs/config100`. The four
712
+ `unrecognized Loader tree carrier` errors the dump prints at `/181`–`/184`
713
+ reproduce **identically without** the patch, so they belong to the base Web
714
+ profile and not to this plugin.
715
+ - **Published client bundles:** the two resident-client tests
716
+ (`tests/dsh-wire-stream-contract.test.ts`) were re-run against the actual
717
+ published `0.2.1-alpha.2` client bundles, resolved through a temporary
718
+ resolution hook instead of the rc.2 dev pins → pass. This covers client-face
719
+ composition and the stream's abort-signal argument contract with a **mocked**
720
+ Host gateway.
721
+ - **Documentation corrected in the same change:** `COMPATIBILITY.md`,
722
+ `PROTOCOL.md`, `docs/DEEPPILOT_FEATURE_PLAN.md`, and the schedule-service
723
+ comment in `src/dsh-api-proxy.ts` no longer tell users to enable a retired
724
+ bundle. The private app's schedule-unsupported strings still do; that copy
725
+ lives in the other repository and is listed as an open follow-up above.
726
+ - **Live run on a real `0.2.1-alpha.2` host (2026-10-10).** An isolated
727
+ `DSH_HOME` (`.audit-smoke-home`, workspace root) was given a `smoke` profile
728
+ built from the shipped Web template with this working tree linked in, plus a
729
+ second profile pinning the published `dsh-deeppilot@0.9.3`. The published
730
+ `0.2.1-alpha.2` CLI booted each one in turn and
731
+ `scripts/smoke-live.mts run` drove the phone protocol against it over the LAN
732
+ TLS listener (no `--prompt`, so no model turn). **Both runs: 25 pass / 0 fail /
733
+ 0 warn / 3 skipped** — health, TLS SPKI, pairing rejection, WSS upgrade,
734
+ challenge/prove with all six scopes, session list/create/open/history,
735
+ model catalog and switch, workspace list/create, archive/unarchive, pending
736
+ snapshot, the full schedule create/history/delete flow, and reconnect replay.
737
+ The skips are the log-pin cross-check (needs `SMOKE_LOG`), the real prompt, and
738
+ the approval/question round trip.
739
+ - **The A/B result is the evidence that matters.** The published `0.9.3` host
740
+ logged `[deeppilot] sessions.projections failed: internal` while still passing
741
+ every checklist step, which is exactly the silent degradation predicted above;
742
+ the working-tree build, same checklist, same host version, logged **zero**
743
+ `sessions.projections` lines. The defect and the fix are therefore both
744
+ confirmed at runtime on `0.2.1-alpha.2`, not only in unit tests.
745
+ - **The live run also settles the automation question.** The welcome frame on a
746
+ stock Web profile advertised `schedules: true` with no bundle enabled and no
747
+ user action, and the full schedule create/history/delete flow passed against
748
+ it. That is the behaviour the corrected `COMPATIBILITY.md`, `PROTOCOL.md`, and
749
+ feature-plan copy now describe.
750
+ - **Still untested:** a real model turn inside the plugin's live path (the
751
+ isolated home has no model credentials), the Funnel/remote transport (the
752
+ isolated host ran `remote.enabled` at its default `false`; the live host's
753
+ phone path goes through the Funnel helper), and a real iPhone. The live smoke
754
+ gate still refuses to run against the machine's own host without a restart,
755
+ because `DeviceStore` snapshots `devices-v2.json` once at mount.
756
+ - **Repository pins unchanged:** `npm run check:config-schema` and the CI
757
+ `config-schema` job still target the `0.2.0-rc.2` CLI. The alpha.2 schema dump
758
+ above was run separately in an isolated home instead of re-pinning the
759
+ repository's own gate, so the gate keeps reporting its `0.2.0-rc.2` baseline
760
+ and nothing in the working tree depends on alpha.2.
761
+
762
+ ### Re-audit procedure used
763
+
764
+ The same full-clone method as the alpha.1 entry, with one addition: because two
765
+ non-peer packages (`api/session-controller`, `client/connection`) carry the
766
+ integration seams this plugin depends on, the diff was extended past the peer
767
+ set and every changed source file in `packages/api`, `packages/client`,
768
+ `packages/session*`, and `packages/core` was read in full. Judging only the peer
769
+ list would have missed the `projections` break entirely — the peer-list check
770
+ reported 10 of 12 packages clean while the defect sat in a package reached
771
+ through `ctx.get('sessionController')`.
772
+
103
773
  ## 2026-10-03 — audit of `dsh-v0.2.1-alpha.1`
104
774
 
105
775
  ### Scope and method