dsh-deeppilot 0.9.2 → 0.9.4
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/COMPATIBILITY.md +43 -17
- package/bin/SHA256SUMS +6 -6
- package/bin/darwin-amd64/dsh-deeppilot-tunnel +0 -0
- package/bin/darwin-arm64/dsh-deeppilot-tunnel +0 -0
- package/bin/linux-amd64/dsh-deeppilot-tunnel +0 -0
- package/bin/linux-arm64/dsh-deeppilot-tunnel +0 -0
- package/bin/windows-amd64/dsh-deeppilot-tunnel.exe +0 -0
- package/bin/windows-arm64/dsh-deeppilot-tunnel.exe +0 -0
- package/docs/DSH_RELEASE_MEMORY.md +428 -34
- package/lib/index.d.ts +33 -3
- package/lib/index.js +177 -34
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/COMPATIBILITY.md
CHANGED
|
@@ -12,7 +12,7 @@ range before the plugin will install there.
|
|
|
12
12
|
| Component | Current contract | Evidence |
|
|
13
13
|
|---|---|---|
|
|
14
14
|
| Node.js | 22 or newer | Package engine and CI |
|
|
15
|
-
| DSH CLI and Host API | `>=0.2.0-rc.2 <0.3.0-0` (
|
|
15
|
+
| DSH CLI and Host API | `>=0.2.0-rc.2 <0.3.0-0` (dev pins `0.2.0-rc.2`; source audits through `0.2.1-alpha.2`) | Peer metadata, source typecheck, unit tests, bundle build, and config schema projection against a real 0.2.0-rc.2 CLI, plus an isolated compose with the published 0.2.1-alpha.2 CLI |
|
|
16
16
|
| iOS bridge | DeepPilot protocol v2 | Bridge protocol tests; device behavior must be checked with the running Host and app |
|
|
17
17
|
| Remote access | Tailscale Funnel on ports 443, 8443, or 10000 | Helper and supervisor tests |
|
|
18
18
|
|
|
@@ -21,7 +21,7 @@ range before the plugin will install there.
|
|
|
21
21
|
- The Host adapter uses Session and Workspace controllers, Gateway Remote Events, and `typertGateway.wireStream.open(endpoint, payload, uplink, peer, signal)`. Its in-process carrier passes `undefined` for `uplink` and `peer` and the cancellation signal in argument five.
|
|
22
22
|
- The resident Client sends relative RPC paths such as `api/$events/result`. The in-process transport resolves those paths to a local URL before constructing a Node `Request`; the shared Fetch handler dispatches it without a network request. This is required for iOS approval and question answers to settle at the Gateway.
|
|
23
23
|
- The client settings page binds `configForms.get('deeppilot')` when the service becomes available. The Host reads volatile config references from its profile entry and reconciles transports on `loader/volatile-update`.
|
|
24
|
-
- Typert strict codecs publish `create()` factories. Session opening reads `session.projections` for a complete baseline.
|
|
24
|
+
- Typert strict codecs publish `create()` factories. Session opening reads `session.projections` for a complete baseline; that controller requires a cancellation signal from `0.2.1-alpha.2` on, so the adapter always passes one.
|
|
25
25
|
- The Host adapter retains a stable bridge-facing API so the phone protocol does not depend directly on DSH controller shapes. This adapter is an internal design boundary, not support for an older DSH Host.
|
|
26
26
|
|
|
27
27
|
## Separate protocol and data boundaries
|
|
@@ -31,28 +31,30 @@ range before the plugin will install there.
|
|
|
31
31
|
- The plugin's persisted device and Funnel state migration remains separate from DSH API version support. Session logs written by earlier DSH builds are readable only when the current Host's own history reader accepts them; this plugin does not rewrite those logs.
|
|
32
32
|
- An unavailable optional OS facility, transport, or controller disables its dependent capability without crashing the Host.
|
|
33
33
|
|
|
34
|
-
## Schedules
|
|
34
|
+
## Schedules ship with the Web composition
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
Reminders need no user action on a current host. DSH 0.2.0 briefly moved
|
|
37
|
+
automation out of the shipped Web composition into the optional
|
|
38
|
+
`@deepseek-ai/dsh-experimental-schedule-bundle`, and the following release
|
|
39
|
+
removed that bundle again: it is listed in `RETIRED_BUNDLES`,
|
|
40
|
+
`@deepseek-ai/dsh-web-app` mounts `schedule` and `ui-schedule` in every Web
|
|
41
|
+
profile, and loading a profile strips the retired entry from
|
|
42
|
+
`dsh.profile.bundles`. The plugin manager no longer shows an **Automation
|
|
43
|
+
tasks** switch, so no profile is expected to enable anything for reminders.
|
|
41
44
|
|
|
42
|
-
The plugin
|
|
45
|
+
The plugin needed no change for either step. It resolves the service through
|
|
43
46
|
`ctx.get('schedule')`, never declares `schedule` in `inject`, and reports
|
|
44
47
|
`welcome.capabilities.schedules = false` with a stable `E_UNSUPPORTED` for every
|
|
45
|
-
schedule frame when the service is absent.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
schedule frame when the service is absent. On a Web host the service is mounted,
|
|
49
|
+
so the capability bit is `true` by default; a profile that drops the rows still
|
|
50
|
+
reports `false` and still degrades cleanly.
|
|
48
51
|
|
|
49
52
|
The capability is resolved lazily rather than captured when the bridge is
|
|
50
53
|
built. The bridge mounts on `sessionController` / `connection` /
|
|
51
|
-
`typertGateway`, all of which become ready before the
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
list/create/history/delete flow passes.
|
|
54
|
+
`typertGateway`, all of which become ready before the Schedule service finishes
|
|
55
|
+
its own initialization, so a probe taken at that moment would report `false` for
|
|
56
|
+
the bridge's whole lifetime. On a live host with the service mounted the bit is
|
|
57
|
+
`true` and the schedule list/create/history/delete flow passes.
|
|
56
58
|
|
|
57
59
|
## Configuration migration
|
|
58
60
|
|
|
@@ -62,6 +64,30 @@ carries the old top-level `debug: true` is accepted but that value is ignored:
|
|
|
62
64
|
verbose logging must be turned on again from the settings page. No other config
|
|
63
65
|
field changed shape, and the old key is not read anywhere in this version.
|
|
64
66
|
|
|
67
|
+
## 0.2.1-alpha.2 audit outcome
|
|
68
|
+
|
|
69
|
+
The source-level audit of `dsh-v0.2.1-alpha.2` against `dsh-v0.2.1-alpha.1`
|
|
70
|
+
(669 commits, 3226 changed files) found the peer set almost untouched — 10 of
|
|
71
|
+
the 12 peer packages have zero changed source files — but one real break
|
|
72
|
+
outside it: `SessionController.projections` now aborts before it reads
|
|
73
|
+
anything, so the adapter's signal-less call lost the entire projection baseline.
|
|
74
|
+
That is fixed here: `src/dsh-api-proxy.ts` passes a signal, its controller
|
|
75
|
+
mirror declares one required, and a regression test fails against the previous
|
|
76
|
+
call form. Install and phone-protocol risk are unchanged — the peer range admits
|
|
77
|
+
`0.2.1-alpha.2`, and protocol v2, pairing state, and device records are
|
|
78
|
+
untouched.
|
|
79
|
+
|
|
80
|
+
A live `0.2.1-alpha.2` host has now exercised the bridge. An isolated profile
|
|
81
|
+
built from the shipped Web template, booted by the published CLI, passed the
|
|
82
|
+
phone-protocol checklist over the LAN TLS listener — health, SPKI, pairing
|
|
83
|
+
rejection, WSS upgrade, challenge/prove with all six scopes, session and
|
|
84
|
+
workspace RPCs, archive/unarchive, the schedule create/history/delete flow, and
|
|
85
|
+
reconnect replay (25 pass, 0 fail, 3 skipped). Running the published `0.9.3` on
|
|
86
|
+
the same host version instead logged
|
|
87
|
+
`[deeppilot] sessions.projections failed: internal` while still passing every
|
|
88
|
+
step, which is the silent degradation this release fixes. A real model turn, the
|
|
89
|
+
Funnel transport, and a real iPhone are still unverified.
|
|
90
|
+
|
|
65
91
|
## 0.2.0-rc.2 audit outcome
|
|
66
92
|
|
|
67
93
|
The source-level audit of `dsh-v0.2.0-rc.2` against `dsh-v0.2.0-rc.1` found no
|
package/bin/SHA256SUMS
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
c70401fd9530f2fae6abe7aec5eacacca239ba315cedc7197db530df8c6f97bd ./darwin-amd64/dsh-deeppilot-tunnel
|
|
2
|
+
45ff6b53d3e27fe6242e4904055d647d8b220a5c56ca76e542ffcd861ad3bbad ./darwin-arm64/dsh-deeppilot-tunnel
|
|
3
|
+
8d4937de90564327dcf8392dba6241e4facc30044a48447b22de99f3e62c9808 ./linux-amd64/dsh-deeppilot-tunnel
|
|
4
|
+
cd4638f9aa3e60340d4f446301b2b6f6452f5cb89fc012884e5a893ab8dbd577 ./linux-arm64/dsh-deeppilot-tunnel
|
|
5
|
+
d5435d4e0c02056b3fac39a5f289acb404e6f060dc566e5dea724ee2b86a4296 ./windows-amd64/dsh-deeppilot-tunnel.exe
|
|
6
|
+
7d79c848b2b74286764bd284005edbcfdf539be90ce0b160ea464ae3af70e005 ./windows-arm64/dsh-deeppilot-tunnel.exe
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -5,50 +5,173 @@
|
|
|
5
5
|
|
|
6
6
|
## Current decision
|
|
7
7
|
|
|
8
|
-
- **Audited release:** `dsh-v0.2.1-alpha.
|
|
9
|
-
<https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.
|
|
10
|
-
published 2026-10-
|
|
11
|
-
`latest`/`next` still point at `0.2.0-rc.2
|
|
12
|
-
- **Comparison baseline:** `dsh-v0.2.
|
|
13
|
-
- **Plugin runtime verdict
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
its
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
8
|
+
- **Audited release:** `dsh-v0.2.1-alpha.2` (official release page:
|
|
9
|
+
<https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.2>),
|
|
10
|
+
published to npm 2026-10-09T08:18:00Z on the `alpha` dist-tag. It is **not**
|
|
11
|
+
`latest`; `latest`/`next` still point at `0.2.0-rc.2`.
|
|
12
|
+
- **Comparison baseline:** `dsh-v0.2.1-alpha.1` (the previous audited tag).
|
|
13
|
+
- **Plugin runtime verdict: one confirmed break, fixed in this audit.**
|
|
14
|
+
`SessionController.projections` now requires its cancellation signal — the
|
|
15
|
+
published implementation calls `signal.throwIfAborted()` as the first
|
|
16
|
+
statement of its `try` and reads `signal.aborted` in its `catch`, while the
|
|
17
|
+
adapter called it with no signal at all, so every projection baseline read
|
|
18
|
+
failed with a `TypeError` instead of returning values. The adapter now passes
|
|
19
|
+
a signal and its hand-written mirror declares `signal` required. See "The one
|
|
20
|
+
confirmed break" in the 2026-10-09 entry for the reproduction, the fix, and
|
|
21
|
+
the regression test that pins it.
|
|
22
|
+
- **What is unaffected:** `list`, `inspect(sessionId)` (its signal is still
|
|
23
|
+
optional upstream), `create`, `fork`, `rename`, `cancel`, `attachment`,
|
|
24
|
+
`prompt`, `selectModel`, `modelCatalog`, the workspace controller, the Gateway
|
|
25
|
+
wire-stream seam the resident approval/question client depends on, the shared
|
|
26
|
+
Fetch carrier, and every peer package's exported runtime surface.
|
|
27
|
+
- **Install verdict:** the peer range `>=0.2.0-rc.2 <0.3.0-0` admits
|
|
28
|
+
`0.2.1-alpha.2` under `semver.satisfies(..., { includePrerelease: true })`,
|
|
29
|
+
which is how DSH's `evaluatePluginCompatibility` gates activation; npm's
|
|
30
|
+
**default** prerelease handling still reports peer warnings for
|
|
31
|
+
`@deepseek-ai/dsh`, `cordis`, and `schemastery`. Composing a real Web profile
|
|
32
|
+
with this plugin's `cordis.patch.yml` on the published CLI adds the plugin's
|
|
33
|
+
three rows plus its own config schema and no plugin-attributed diagnostic; the
|
|
34
|
+
four base-profile schema errors it prints reproduce identically without the
|
|
35
|
+
patch.
|
|
29
36
|
- **Protocol verdict:** no required DeepPilot phone-protocol break. Protocol v2,
|
|
30
37
|
pairing state, and device records are unchanged; already-paired phones need no
|
|
31
38
|
action.
|
|
32
|
-
- **
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
- **Capability gaps, not regressions:** the new `workingDirectory` projection,
|
|
40
|
+
the new `formatStatus` list field, the `mode: 'external'` subagent catalog, and
|
|
41
|
+
the `session/migration-required` fork refusal are all things this plugin does
|
|
42
|
+
not consume, and none of them change a call it already makes.
|
|
43
|
+
|
|
44
|
+
### Plugin release verdicts
|
|
45
|
+
|
|
46
|
+
- **Release verdict (`0.9.4`, prepared 2026-10-10, NOT published):** carries the
|
|
47
|
+
`projections` adapter fix and the Automation tasks documentation correction
|
|
48
|
+
described in the 2026-10-09 entry below. `package.json`, both root
|
|
49
|
+
`package-lock.json` version entries, and `tests/apply-baseline.snapshot.json`
|
|
50
|
+
are on `0.9.4`, and `lib/` is regenerated. The full checklist passed locally:
|
|
51
|
+
`npm ci`, 538 unit tests, typecheck, build, the config-schema check against a
|
|
52
|
+
real `0.2.0-rc.2` CLI, Go helper tests, helper checksums, and
|
|
53
|
+
`npm pack --dry-run` (65 files, 50,916,470 bytes packed). The fix was also
|
|
54
|
+
verified on a live `0.2.1-alpha.2` host against the published `0.9.3` as an
|
|
55
|
+
A/B control (25 pass / 0 fail either way, with the `sessions.projections
|
|
56
|
+
failed: internal` diagnostic present only on `0.9.3`). Nothing was
|
|
57
|
+
published, tagged, or pushed; npm `latest` still points at `0.9.3`, so users on
|
|
58
|
+
`0.2.1-alpha.2` keep hitting the projections defect until this ships.
|
|
59
|
+
- **The release also had to fix the helper's toolchain and its refresh
|
|
60
|
+
workflow.** CI's `govulncheck` job reported 11 reachable Go standard-library
|
|
61
|
+
vulnerabilities for the pinned `go 1.26.6`; this is time-triggered, not
|
|
62
|
+
caused by the release — `0.9.3`'s CI was green on 2026-10-05 with the same
|
|
63
|
+
helper source. `helper/go.mod` now pins `go 1.26.9`, and the six `bin/`
|
|
64
|
+
binaries were rebuilt with it. That rebuild only became correct after fixing
|
|
65
|
+
`.github/workflows/build-helpers.yml`: each build job uploaded the whole
|
|
66
|
+
`bin/` tree, so every artifact also carried the checked-out copies of the
|
|
67
|
+
targets its runner never rebuilt, and `merge-multiple` let those stale files
|
|
68
|
+
overwrite the fresh ones (the first refresh PR mixed two toolchains — only
|
|
69
|
+
darwin-arm64 embedded `go1.26.9`). The workflow now uploads only the
|
|
70
|
+
per-matrix target globs; PR #19 then carried all six at `go1.26.9` and was
|
|
71
|
+
merged as `f0f243b`.
|
|
72
|
+
- **Checklist environment notes for the next release.**
|
|
73
|
+
(a) `~/.npm/_cacache` contains root-owned files on this machine, so `npm view`
|
|
74
|
+
and `npm pack` fail with `EPERM` unless `npm_config_cache` points at a
|
|
75
|
+
writable cache — the run above used the workspace audit cache.
|
|
76
|
+
(b) Go's default build cache lives outside the workspace and the file sandbox
|
|
77
|
+
blocks it (`open .../Library/Caches/go-build/...: operation not permitted`,
|
|
78
|
+
reported as `[setup failed]`); point `GOCACHE` at a workspace directory.
|
|
79
|
+
(c) `gh` writes its run-log cache under `~/.cache/gh` and fails the same way;
|
|
80
|
+
point `XDG_CACHE_HOME` at a workspace directory.
|
|
81
|
+
None of the three is a repository defect.
|
|
82
|
+
- **Release verdict (`0.9.3`, published 2026-10-05, tag `v0.9.3` @ `b85869b`):**
|
|
83
|
+
shipped to `latest`. Verified on the registry: version present, `dist-tags.latest`
|
|
84
|
+
moved off `0.9.2`, 65 files, `dist.shasum` `90f0c665c395a7043eb3689a291cf28c88bc39ab`
|
|
85
|
+
byte-identical to the locally verified `npm pack` output, and a clean install plus
|
|
86
|
+
a real `dsh --profile web --dump-config` compose (188 entries) with the plugin's
|
|
87
|
+
three `cordis.patch.yml` entries present and no missing-module error. Ships
|
|
88
|
+
issue #24 (`100.64.0.0/10` becomes a selectable pairing target) and the Windows
|
|
89
|
+
journal `fsync` tolerance fix. Protocol v2 unchanged; paired phones need no action.
|
|
90
|
+
- **`gitHead` deviation, recorded rather than hidden.** The registry records
|
|
91
|
+
`gitHead` `fbda6a5`, the docs-only commit that added `RELEASE_NOTES-0.9.3.md`,
|
|
92
|
+
while tag `v0.9.3` points at `b85869b`. The cause is ordering, not content:
|
|
93
|
+
the tarball was packed before that docs commit, and npm stamps `gitHead`
|
|
94
|
+
from HEAD at publish time. The shipped bytes are provably `b85869b`'s tree —
|
|
95
|
+
the published `dist.shasum` equals the local `npm pack` output and the tarball
|
|
96
|
+
does not contain `RELEASE_NOTES-0.9.3.md`. `fbda6a5` is `b85869b`'s child and
|
|
97
|
+
changes no packaged file. Versions are immutable on npm, so this cannot be
|
|
98
|
+
corrected retroactively; **do it differently next time: commit every doc file
|
|
99
|
+
before packing, and pack after the last commit.** The 0.9.2 entry below had
|
|
100
|
+
`gitHead` matching the tag because no commit landed between pack and publish.
|
|
101
|
+
- **Install caveat users will hit — DSH's release-age policy.** Confirmed again
|
|
102
|
+
while verifying this release: `dsh plugin add --profile web` writes a
|
|
103
|
+
`minimumReleaseAgeExclude` entry in `pnpm-workspace.yaml` and an explicit
|
|
104
|
+
`dsh-deeppilot@0.9.3` installs the new version. Expect the unversioned form to
|
|
105
|
+
resolve to a slightly older release for a while; this is expected DSH
|
|
106
|
+
behaviour, not a publishing failure.
|
|
107
|
+
- **Release verdict (`0.9.2`, published 2026-10-05, tag `v0.9.2` @ `4b43671`):**
|
|
108
|
+
shipped to `latest`. Verified on the registry: version present, `dist-tags.latest`
|
|
109
|
+
moved off `0.9.1`, `gitHead` `4b436715` matches the tag and HEAD, 65 files,
|
|
110
|
+
and a clean DSH profile install of `dsh-deeppilot@0.9.2` composes with the
|
|
111
|
+
plugin's three `cordis.patch.yml` entries present and no missing-module error.
|
|
112
|
+
`scripts/smoke-live.mts` passed with **0 failures** against a live
|
|
113
|
+
**`dsh-v0.2.1-alpha.1`** web profile — the first live-host verification since
|
|
114
|
+
the 0.9.1 entry, retiring the gate it left open ("pending separate
|
|
115
|
+
authorization before the `0.9.1` release ships"), on a newer host than the
|
|
37
116
|
rc.2 that entry contemplated, which is strictly stronger evidence: the
|
|
38
117
|
`0.2.1-alpha.1` source-diff above found zero changed `src/` files across all
|
|
39
118
|
12 peer packages, and the live run agrees. The plugin's own build and type
|
|
40
|
-
baselines stay pinned to `0.2.0-rc.2`; nothing about the pin changed
|
|
41
|
-
release
|
|
119
|
+
baselines stay pinned to `0.2.0-rc.2`; nothing about the pin changed.
|
|
120
|
+
- **Install caveat users will hit — DSH's release-age policy.** On a clean
|
|
121
|
+
profile, `dsh plugin add dsh-deeppilot` installed **`0.9.1`**, not the just
|
|
122
|
+
published `0.9.2`, even though `latest` already pointed at `0.9.2`. DSH
|
|
123
|
+
deliberately prefers a slightly older release and offers an exact-version
|
|
124
|
+
path instead; `dsh plugin add dsh-deeppilot@0.9.2` installs the new one.
|
|
125
|
+
This is expected DSH behaviour, not a publishing failure — do not read the
|
|
126
|
+
older version in the installer as a broken release.
|
|
127
|
+
- **Peer-range note for future audits.** `evaluatePluginCompatibility` checks
|
|
128
|
+
each peer against the *running DSH version*, but `cordis` and `schemastery`
|
|
129
|
+
are host-vendored third parties and `react` comes from the Web app. Judging
|
|
130
|
+
those three against a DSH version number reports false mismatches. They are
|
|
131
|
+
also `optional` in `peerDependenciesMeta`. Confirm installability by
|
|
132
|
+
composing a real profile, not by iterating the peer table.
|
|
42
133
|
- **Two tracked follow-ups:**
|
|
43
|
-
1.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
2.
|
|
134
|
+
1. ~~Projection signal.~~ **Resolved 2026-10-10:** the adapter passes
|
|
135
|
+
`new AbortController().signal` to `session.projections` and the mirror
|
|
136
|
+
declares `signal` required, so the compiler enforces it. Covered by a new
|
|
137
|
+
regression test that fails against the previous call form. 538 unit tests,
|
|
138
|
+
typecheck, and build pass with regenerated `lib/`.
|
|
139
|
+
2. **Automation tasks documentation: public docs fixed, iOS copy open.**
|
|
140
|
+
Automation was a short-lived optional bundle: DSH 0.2.0 moved it out of the
|
|
141
|
+
shipped Web composition, and the following release retired
|
|
142
|
+
`@deepseek-ai/dsh-experimental-schedule-bundle` again — `dsh-web-app` now
|
|
143
|
+
mounts `schedule` and `ui-schedule` in every Web profile. `COMPATIBILITY.md`,
|
|
144
|
+
`PROTOCOL.md`, `docs/DEEPPILOT_FEATURE_PLAN.md`, and the schedule-service
|
|
145
|
+
comment in `src/dsh-api-proxy.ts` now state that; the private app still
|
|
146
|
+
ships `error.scheduleUnsupported` and `schedule.unsupported` copy telling
|
|
147
|
+
the user to enable it (`app-private/ios/L10nFragments/schedule.json` and the
|
|
148
|
+
generated `zh-Hans.lproj` / `en.lproj` string tables). **iOS copy still
|
|
149
|
+
open** — it belongs to the private repository and to its own release flow.
|
|
150
|
+
3. ~~`subagent_session` is unmapped.~~ **Resolved 2026-10-03:** mapped to
|
|
49
151
|
`E_PROTOCOL` in `src/wire-errors.ts` and added to the frozen expectation in
|
|
50
152
|
`tests/wire-errors.test.ts`. 525 unit tests, typecheck, and build pass.
|
|
51
153
|
|
|
154
|
+
### Previous decision — `dsh-v0.2.1-alpha.1`
|
|
155
|
+
|
|
156
|
+
- **Audited release:** `dsh-v0.2.1-alpha.1`, published 2026-10-03 (npm
|
|
157
|
+
`alpha` dist-tag, also not `latest`). Comparison baseline was
|
|
158
|
+
`dsh-v0.2.0-rc.2`.
|
|
159
|
+
- **Plugin runtime verdict:** no breaking change in the DSH APIs DeepPilot calls.
|
|
160
|
+
All 12 peer packages had **zero** changed `src/` files; each changed only its
|
|
161
|
+
`package.json` version field and its READMEs. Full table in the 2026-10-03
|
|
162
|
+
entry below.
|
|
163
|
+
- **Nothing was changed to support that release.** `package.json`,
|
|
164
|
+
`package-lock.json`, generated `lib/`, `COMPATIBILITY.md`, and `PROTOCOL.md`
|
|
165
|
+
stayed on the `0.2.0-rc.2` baseline — no version bump, no dependency install,
|
|
166
|
+
no `lib/` regeneration. The same held for its source diff; the alpha.2 audit
|
|
167
|
+
that followed then produced one adapter fix (see the 2026-10-09 entry).
|
|
168
|
+
- **Peer range:** `>=0.2.0-rc.2 <0.3.0-0` admitted `0.2.1-alpha.1` through
|
|
169
|
+
`semver.satisfies(..., { includePrerelease: true })`; the `~4.0.4` / `~3.18.4`
|
|
170
|
+
vendor ranges admitted `4.0.5-alpha.1` / `3.18.5-alpha.1` the same way, and
|
|
171
|
+
npm's default prerelease rules reported warnings for those three.
|
|
172
|
+
- **Protocol verdict:** no required phone-protocol break; protocol v2, pairing
|
|
173
|
+
state, and device records unchanged.
|
|
174
|
+
|
|
52
175
|
### Previous decision — `dsh-v0.2.0-rc.2`
|
|
53
176
|
|
|
54
177
|
- **Audited release:** `dsh-v0.2.0-rc.2` (official release page:
|
|
@@ -84,6 +207,252 @@
|
|
|
84
207
|
bundle" below. The plugin needs no code change; the phone's copy now points
|
|
85
208
|
at the bundle.
|
|
86
209
|
|
|
210
|
+
## 2026-10-09 — audit of `dsh-v0.2.1-alpha.2`
|
|
211
|
+
|
|
212
|
+
### Scope and method
|
|
213
|
+
|
|
214
|
+
Compared `dsh-v0.2.1-alpha.1...dsh-v0.2.1-alpha.2` (**669 commits, 3226 changed
|
|
215
|
+
files, 114,987 insertions, 28,177 deletions**) from a full clone
|
|
216
|
+
(`git clone --filter=blob:none --no-checkout`, unshallowed before measuring).
|
|
217
|
+
Baseline SHA `5badb150`, target SHA `d7432673`, annotated tag object `6851e496`.
|
|
218
|
+
Peer package names were mapped to monorepo paths by reading every `package.json`
|
|
219
|
+
in the tree, then each peer package's `src/**/*.ts` was diffed with
|
|
220
|
+
`git diff --name-status`. Every changed source file under `packages/api`,
|
|
221
|
+
`packages/session*`, `packages/client`, and `packages/core` was read in full —
|
|
222
|
+
including the complete 836-line session-controller diff — rather than being
|
|
223
|
+
summarised from release notes. All **20** upgrade guides added since the last
|
|
224
|
+
audit (19 under `docs/upgrade-guide/v0.2.1-alpha.1/` plus `bundled-python-tests`
|
|
225
|
+
under `v0.2.0-rc.2/`) were read in Chinese in full, and each one that could touch
|
|
226
|
+
an integration seam was traced to its implementation before being judged. No
|
|
227
|
+
guide from an earlier release was modified between the two tags.
|
|
228
|
+
|
|
229
|
+
### Peer packages: 2 of 12 changed source
|
|
230
|
+
|
|
231
|
+
| Package | Path | Changed `src/` files |
|
|
232
|
+
| --- | --- | --- |
|
|
233
|
+
| `@deepseek-ai/dsh` | `apps/cli` | 1 (`src/args.ts`) |
|
|
234
|
+
| `@deepseek-ai/dsh-client-connection` | `packages/client/connection` | 5 (`api-request-trust.ts`, `browser-auth.ts`, `index.ts`, `rpc-host.ts`, `rpc.ts`) |
|
|
235
|
+
| `@deepseek-ai/dsh-api-gateway` | `packages/api/gateway` | 0 |
|
|
236
|
+
| `@deepseek-ai/dsh-api-remotes` | `packages/api/remotes` | 0 |
|
|
237
|
+
| `@deepseek-ai/dsh-client-locale` | `packages/client/locale` | 0 |
|
|
238
|
+
| `@deepseek-ai/dsh-client-ui-settings` | `packages/client/ui-settings` | 0 |
|
|
239
|
+
| `@deepseek-ai/dsh-client-ui-slots` | `packages/client/ui-slots` | 0 |
|
|
240
|
+
| `@deepseek-ai/dsh-settings` | `packages/settings/settings` | 0 |
|
|
241
|
+
| `@deepseek-ai/dsh-typert-protocol` | `packages/typert/protocol` | 0 |
|
|
242
|
+
| `@deepseek-ai/dsh-typert-registry` | `packages/typert/registry` | 0 |
|
|
243
|
+
| `@deepseek-ai/cordis` | `vendor/cordis` | 0 |
|
|
244
|
+
| `@deepseek-ai/schemastery` | `vendor/schemastery` | 0 |
|
|
245
|
+
|
|
246
|
+
`react` is not an upstream package and was not measured. Two non-peer packages
|
|
247
|
+
that the bridge reaches through `ctx.get` did change and are covered below:
|
|
248
|
+
`packages/api/session-controller` and `packages/client/connection`.
|
|
249
|
+
|
|
250
|
+
### The one confirmed break
|
|
251
|
+
|
|
252
|
+
`packages/api/session-controller/src/index.ts` started dereferencing a parameter
|
|
253
|
+
that upstream's own published declaration already marks as required:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
async projections(request: SessionProjectionsRequest, signal: AbortSignal): Promise<SessionProjectionsValue> {
|
|
257
|
+
const { sessionId } = request
|
|
258
|
+
if (sessionId.length === 0) throw new RemoteError('gateway/bad-request', ...)
|
|
259
|
+
try {
|
|
260
|
+
signal.throwIfAborted() // now the first statement
|
|
261
|
+
const live = this.liveProjections(sessionId)
|
|
262
|
+
...
|
|
263
|
+
} catch (error) {
|
|
264
|
+
...
|
|
265
|
+
if (signal.aborted || ...) throw new RemoteError('gateway/cancelled', ...) // now unconditional
|
|
266
|
+
...
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
In alpha.1 the same method only forwarded `signal` into `observeSession` and read
|
|
272
|
+
`signal.aborted` inside the `catch`, so an omitted signal still survived the happy
|
|
273
|
+
path. In alpha.2 the omission throws before any work is done — reproduced against
|
|
274
|
+
the published `0.2.1-alpha.2` bundle with the real class, a stub context whose
|
|
275
|
+
live-projection path returns a value, and no signal:
|
|
276
|
+
|
|
277
|
+
```sh
|
|
278
|
+
# published @deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2, lib/index.js
|
|
279
|
+
SessionController.prototype.projections.call(receiver, { sessionId: 'x' })
|
|
280
|
+
# → TypeError (signal is undefined at signal.throwIfAborted(), then again at signal.aborted)
|
|
281
|
+
SessionController.prototype.projections.call(receiver, { sessionId: 'x' }, new AbortController().signal)
|
|
282
|
+
# → { kind: 'sequenced', asOfSeq: 7, values: { title: 'audit' } }
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
DeepPilot's call site is `src/dsh-api-proxy.ts:235`:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
projections: async (request) => this.call(() => this.session.projections(request.payload!)),
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The hand-written mirror at `src/dsh-api-proxy.ts:45` declares `signal?` optional,
|
|
292
|
+
which is exactly what let the omission typecheck; the upstream declaration
|
|
293
|
+
(`@deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2`,
|
|
294
|
+
`lib/types/index.d.ts:209`) requires it. `DshApiProxy.call()`
|
|
295
|
+
(`src/dsh-api-proxy.ts:413`) converts the `TypeError` into
|
|
296
|
+
`{ ok: false, error: { code: 'internal' } }`, so this degrades rather than
|
|
297
|
+
crashes: `HostBridge.refreshProjections` (`src/host-bridge.ts:799`) already treats
|
|
298
|
+
a failed baseline as a diagnostic and logs
|
|
299
|
+
`sessions.projections failed: internal`.
|
|
300
|
+
|
|
301
|
+
**Observable effect.** The phone's session summary loses the on-open complete
|
|
302
|
+
projection baseline. It still receives title, todo counts, and token usage from
|
|
303
|
+
the session list rows' cached projections (`src/dsh-api-proxy.ts:458`,
|
|
304
|
+
`src/host-bridge.ts:1546`) and from live `session/projection` frames
|
|
305
|
+
(`src/host-bridge.ts:453`), so nothing breaks outright; the loss shows up on
|
|
306
|
+
hosts where the baseline read was the only source — a session whose list row
|
|
307
|
+
carries no cached projections stays without those values longer than it should.
|
|
308
|
+
Cold history reads are **not** affected: they run through
|
|
309
|
+
`inspect(sessionId)`, whose signal is still optional upstream.
|
|
310
|
+
|
|
311
|
+
**Fix (applied 2026-10-10).** The adapter now passes a signal the way `list` and
|
|
312
|
+
`prompt` already do —
|
|
313
|
+
`this.session.projections(request.payload!, new AbortController().signal)` — and
|
|
314
|
+
the mirror's `signal` is required, so the compiler keeps it that way.
|
|
315
|
+
`refreshProjections` is fire-and-forget and has no cancellation source of its
|
|
316
|
+
own, so a dedicated long-lived controller is not warranted; following the
|
|
317
|
+
existing convention in the same file is enough. `lib/index.js` was regenerated.
|
|
318
|
+
|
|
319
|
+
The fix ships in `0.9.4`, which is prepared but not published (see "Plugin release
|
|
320
|
+
verdicts"); until it is published, a host on `0.2.1-alpha.2` running the
|
|
321
|
+
published `0.9.3` still loses the on-open projection baseline. That was observed
|
|
322
|
+
directly: on a live `0.2.1-alpha.2` host running published `0.9.3` the diagnostic
|
|
323
|
+
appeared, and on the same host version running this build it did not (see "Live
|
|
324
|
+
run on a real `0.2.1-alpha.2` host" under Validation evidence).
|
|
325
|
+
|
|
326
|
+
`tests/dsh-api-proxy.test.ts` adds "projection baseline reads pass the
|
|
327
|
+
AbortSignal the Host now requires": a controller stub that calls
|
|
328
|
+
`signal.throwIfAborted()` before returning, plus an assertion that the adapter
|
|
329
|
+
handed it a real `AbortSignal`. The test was verified red against the previous
|
|
330
|
+
call form (1 failure) and green after the fix, so it pins the behaviour rather
|
|
331
|
+
than the implementation coincidence.
|
|
332
|
+
|
|
333
|
+
### Other session-controller changes, checked against this plugin
|
|
334
|
+
|
|
335
|
+
`Where` names the upstream `packages/api/session-controller/src/` file unless it
|
|
336
|
+
says otherwise; `lib/types` means the published declarations of
|
|
337
|
+
`@deepseek-ai/dsh-api-session-controller@0.2.1-alpha.2`.
|
|
338
|
+
|
|
339
|
+
| Change | Where | DeepPilot impact |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| `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. |
|
|
342
|
+
| `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. |
|
|
343
|
+
| `search(request, signal)` requires a signal | `lib/types/index.d.ts:93` | The plugin never calls `search`. |
|
|
344
|
+
| `page(request, signal)` requires a signal | `src/index.ts:1412` | Not called; history paging still goes through `inspect`. |
|
|
345
|
+
| `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. |
|
|
346
|
+
| `skill-catalog` injects `workingDirectory` and lists skills by execution directory | `src/skill-catalog.ts` | Not consumed; the plugin lists no skills. |
|
|
347
|
+
| Session ownership helper | `packages/api/session-controller` | Unchanged; `session/agent-busy` still maps to `E_BUSY`. |
|
|
348
|
+
| 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. |
|
|
349
|
+
|
|
350
|
+
### Connection carrier: unchanged on the paths this plugin uses
|
|
351
|
+
|
|
352
|
+
`packages/client/connection` gained five changed source files, including a new
|
|
353
|
+
`api-request-trust.ts` and browser-auth changes. Those trust, authentication, and
|
|
354
|
+
TLS checks run through `requestRejection`/`admit`, which guard **physical**
|
|
355
|
+
requests. The plugin's resident client uses
|
|
356
|
+
`connection.createSharedFetchHandler('/api')`, which dispatches routes directly
|
|
357
|
+
and never calls `admit` (`packages/client/connection/src/rpc-host.ts:136-154`), and
|
|
358
|
+
its stream call keeps the
|
|
359
|
+
five-argument `wireStream.open(endpoint, payload, uplink, peer, signal)` order.
|
|
360
|
+
The plugin's own LAN TLS pin check and `/phone` listener are unaffected.
|
|
361
|
+
|
|
362
|
+
### Four-way comparison at `0.2.1-alpha.2`
|
|
363
|
+
|
|
364
|
+
| Surface | Both sides | Upstream has, plugin does not consume | Plugin has, upstream does not |
|
|
365
|
+
| --- | --- | --- | --- |
|
|
366
|
+
| 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 |
|
|
367
|
+
| Workspace RPCs | `create`, `archiveSession`, `unarchiveSession`, `follow` | `initializeDefault`, `rename`, `delete`, `insertBefore`, `insertSessionBefore`, `pinSession`, `unpinSession` | archived mirror, `sessionRestore` capability bit |
|
|
368
|
+
| Directory picker | `list`, `pick` | `createDirectory` | browse host/client patch rows, phone-side directory browsing |
|
|
369
|
+
| Schedule | `list`, `history`, `create`, `update`, `delete` | `catalog`, stream delivery modes | phone schedule frames, `schedule.manage` scope, prompt-free mutation journal |
|
|
370
|
+
| Projections | reading `values` | `workingDirectory`, `formatStatus`, the migration-required branch | `title` / `todos` / `sessionStats` / `tokenUsage` folding |
|
|
371
|
+
| Subagents | — | `prompt`, `interruptByParent`, catalog v2 | subagent-session exclusion and notification policy |
|
|
372
|
+
| Trust and auth | shared Fetch route dispatch, TLS pin check | `webStartup.trustedHosts`, listener-address validation | device records, pairing tokens, signed challenge |
|
|
373
|
+
|
|
374
|
+
Rows marked "upstream has, plugin does not consume" are capability gaps by
|
|
375
|
+
choice, not regressions: none of them changes a call the bridge already makes.
|
|
376
|
+
`neither side` is empty for every row that matters to the phone.
|
|
377
|
+
|
|
378
|
+
### Validation evidence
|
|
379
|
+
|
|
380
|
+
- **Unit tests:** `tsx --test tests/*.test.ts` → **537 pass, 0 fail, 0 skipped**
|
|
381
|
+
on the source diff alone. After the adapter fix and its new regression test the
|
|
382
|
+
same command reports **538 pass, 0 fail, 0 skipped**, and `npm run typecheck`
|
|
383
|
+
and `npm run build` pass with regenerated `lib/`.
|
|
384
|
+
- **Config schema on the published CLI:** an isolated `DSH_HOME` and npm cache ran
|
|
385
|
+
`dsh --profile deeppilot-audit --from-default-profile sdk-minimal
|
|
386
|
+
--dump-config-schema --patch cordis.patch.yml` against
|
|
387
|
+
`@deepseek-ai/dsh@0.2.1-alpha.2` → the plugin entry reports
|
|
388
|
+
`status: 'schema'` with its own `$defs` config, the volatile-field checks pass,
|
|
389
|
+
and the only warning is the `sdk-minimal` template's own missing
|
|
390
|
+
`directory-picker` row.
|
|
391
|
+
- **Web composition:** the same CLI composed a real Web profile with the plugin
|
|
392
|
+
patch → 188 rows versus 185 without it; the plugin's three rows
|
|
393
|
+
(`deeppilot-directory-picker-browse`, `…-browse-client`, `deeppilot`) are
|
|
394
|
+
present with the plugin config schema at `#/$defs/config100`. The four
|
|
395
|
+
`unrecognized Loader tree carrier` errors the dump prints at `/181`–`/184`
|
|
396
|
+
reproduce **identically without** the patch, so they belong to the base Web
|
|
397
|
+
profile and not to this plugin.
|
|
398
|
+
- **Published client bundles:** the two resident-client tests
|
|
399
|
+
(`tests/dsh-wire-stream-contract.test.ts`) were re-run against the actual
|
|
400
|
+
published `0.2.1-alpha.2` client bundles, resolved through a temporary
|
|
401
|
+
resolution hook instead of the rc.2 dev pins → pass. This covers client-face
|
|
402
|
+
composition and the stream's abort-signal argument contract with a **mocked**
|
|
403
|
+
Host gateway.
|
|
404
|
+
- **Documentation corrected in the same change:** `COMPATIBILITY.md`,
|
|
405
|
+
`PROTOCOL.md`, `docs/DEEPPILOT_FEATURE_PLAN.md`, and the schedule-service
|
|
406
|
+
comment in `src/dsh-api-proxy.ts` no longer tell users to enable a retired
|
|
407
|
+
bundle. The private app's schedule-unsupported strings still do; that copy
|
|
408
|
+
lives in the other repository and is listed as an open follow-up above.
|
|
409
|
+
- **Live run on a real `0.2.1-alpha.2` host (2026-10-10).** An isolated
|
|
410
|
+
`DSH_HOME` (`.audit-smoke-home`, workspace root) was given a `smoke` profile
|
|
411
|
+
built from the shipped Web template with this working tree linked in, plus a
|
|
412
|
+
second profile pinning the published `dsh-deeppilot@0.9.3`. The published
|
|
413
|
+
`0.2.1-alpha.2` CLI booted each one in turn and
|
|
414
|
+
`scripts/smoke-live.mts run` drove the phone protocol against it over the LAN
|
|
415
|
+
TLS listener (no `--prompt`, so no model turn). **Both runs: 25 pass / 0 fail /
|
|
416
|
+
0 warn / 3 skipped** — health, TLS SPKI, pairing rejection, WSS upgrade,
|
|
417
|
+
challenge/prove with all six scopes, session list/create/open/history,
|
|
418
|
+
model catalog and switch, workspace list/create, archive/unarchive, pending
|
|
419
|
+
snapshot, the full schedule create/history/delete flow, and reconnect replay.
|
|
420
|
+
The skips are the log-pin cross-check (needs `SMOKE_LOG`), the real prompt, and
|
|
421
|
+
the approval/question round trip.
|
|
422
|
+
- **The A/B result is the evidence that matters.** The published `0.9.3` host
|
|
423
|
+
logged `[deeppilot] sessions.projections failed: internal` while still passing
|
|
424
|
+
every checklist step, which is exactly the silent degradation predicted above;
|
|
425
|
+
the working-tree build, same checklist, same host version, logged **zero**
|
|
426
|
+
`sessions.projections` lines. The defect and the fix are therefore both
|
|
427
|
+
confirmed at runtime on `0.2.1-alpha.2`, not only in unit tests.
|
|
428
|
+
- **The live run also settles the automation question.** The welcome frame on a
|
|
429
|
+
stock Web profile advertised `schedules: true` with no bundle enabled and no
|
|
430
|
+
user action, and the full schedule create/history/delete flow passed against
|
|
431
|
+
it. That is the behaviour the corrected `COMPATIBILITY.md`, `PROTOCOL.md`, and
|
|
432
|
+
feature-plan copy now describe.
|
|
433
|
+
- **Still untested:** a real model turn inside the plugin's live path (the
|
|
434
|
+
isolated home has no model credentials), the Funnel/remote transport (the
|
|
435
|
+
isolated host ran `remote.enabled` at its default `false`; the live host's
|
|
436
|
+
phone path goes through the Funnel helper), and a real iPhone. The live smoke
|
|
437
|
+
gate still refuses to run against the machine's own host without a restart,
|
|
438
|
+
because `DeviceStore` snapshots `devices-v2.json` once at mount.
|
|
439
|
+
- **Repository pins unchanged:** `npm run check:config-schema` and the CI
|
|
440
|
+
`config-schema` job still target the `0.2.0-rc.2` CLI. The alpha.2 schema dump
|
|
441
|
+
above was run separately in an isolated home instead of re-pinning the
|
|
442
|
+
repository's own gate, so the gate keeps reporting its `0.2.0-rc.2` baseline
|
|
443
|
+
and nothing in the working tree depends on alpha.2.
|
|
444
|
+
|
|
445
|
+
### Re-audit procedure used
|
|
446
|
+
|
|
447
|
+
The same full-clone method as the alpha.1 entry, with one addition: because two
|
|
448
|
+
non-peer packages (`api/session-controller`, `client/connection`) carry the
|
|
449
|
+
integration seams this plugin depends on, the diff was extended past the peer
|
|
450
|
+
set and every changed source file in `packages/api`, `packages/client`,
|
|
451
|
+
`packages/session*`, and `packages/core` was read in full. Judging only the peer
|
|
452
|
+
list would have missed the `projections` break entirely — the peer-list check
|
|
453
|
+
reported 10 of 12 packages clean while the defect sat in a package reached
|
|
454
|
+
through `ctx.get('sessionController')`.
|
|
455
|
+
|
|
87
456
|
## 2026-10-03 — audit of `dsh-v0.2.1-alpha.1`
|
|
88
457
|
|
|
89
458
|
### Scope and method
|
|
@@ -201,6 +570,31 @@ in the same class as the other input-shaped Schedule refusals. Without the
|
|
|
201
570
|
mapping `wireErrorOf` falls through to `WIRE_ERROR_FALLBACK` (`E_INTERNAL`), which
|
|
202
571
|
would tell the client the failure was worth retrying.
|
|
203
572
|
|
|
573
|
+
### Cross-platform trap found while shipping 0.9.2
|
|
574
|
+
|
|
575
|
+
CI's `plugin` job hung on `npm test` four times while shipping 0.9.2. Worth
|
|
576
|
+
recording because macOS could never reproduce it and the fix is one line each.
|
|
577
|
+
|
|
578
|
+
`tests/journal-parity.test.ts` used `join('/proc/definitely/not/writable', …)`
|
|
579
|
+
as its "unwritable path" probe, in two places. `/proc` is a Linux-only pseudo
|
|
580
|
+
filesystem: on macOS the path simply does not exist, so the branch had only ever
|
|
581
|
+
been exercised on macOS and had never run on Linux at all. On Linux, writing
|
|
582
|
+
under `/proc` does not return an error — and because `dispatch-journal`'s
|
|
583
|
+
`save()` is entirely synchronous `fs` calls (`mkdirSync`/`openSync`/
|
|
584
|
+
`writeFileSync`/`fsyncSync`/`renameSync`), a single write blocks the whole event
|
|
585
|
+
loop. Even `setTimeout` stops firing, and node's `--test-timeout` can only report
|
|
586
|
+
a file-level timeout. Both sites now build the unwritable target inside the
|
|
587
|
+
scratch directory (a file-with-a-child shape that yields `ENOTDIR` identically on
|
|
588
|
+
every platform); recorded values are unchanged because all three candidate paths
|
|
589
|
+
returned the same status on macOS.
|
|
590
|
+
|
|
591
|
+
Two lessons for future audits: the workflow sets no `timeout-minutes`, so a hang
|
|
592
|
+
costs the full 6-hour default before failing — cancel and re-run rather than
|
|
593
|
+
waiting; and reproduce Linux-only behaviour in a container instead of inferring
|
|
594
|
+
it. `docker run --rm --cpus=2 -v $PWD:/app -w /app node:22` matches the runner
|
|
595
|
+
closely enough (2 cores, and the same `v22.23.x` that `node-version: 22` floats
|
|
596
|
+
to).
|
|
597
|
+
|
|
204
598
|
### Re-audit procedure used
|
|
205
599
|
|
|
206
600
|
Same blobless-clone method as the rc.2 and rc.1 entries, with two refinements:
|