dsh-plugin-upgrade 0.1.3 → 2.0.1

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.
@@ -0,0 +1,423 @@
1
+ # Evidence · `dsh-plugin-upgrade` (merged `0.1.3-alpha.1` → `0.1.5-rc.1`)
2
+
3
+ Every upstream claim in the merged version card
4
+ (`skills/plugin-upgrade/references/v0.1.3-alpha.1-to-v0.1.5-rc.1.md`) and every
5
+ `action` string in `lib/scan.mjs` traces to a command recorded here, re-run on
6
+ **2026-09-09** (leg A's wave) and **2026-09-10** (leg B's tag-range diff) against the read-only
7
+ harness checkout at `D:\deepseek-harness`, the leg-A checkout at `19d2e38480`, and the
8
+ read-only family workspace at `D:\Projects\dsh\plugins`.
9
+
10
+ This file holds the records of both legs of the merged span:
11
+
12
+ - **§1–§10 below are leg B's records** (`0.1.5-alpha.1 → 0.1.5-rc.1`), kept verbatim from the
13
+ retired `dsh-plugin-upgrade-rc1`, against the merged card's **§2 Leg B**;
14
+ - **§A at the end is leg A's provenance pointer** (`0.1.3-alpha.1 → 0.1.5-alpha.1`), against
15
+ the merged card's **§1 Leg A**.
16
+
17
+ Toolchain used: `node v22.22.3`, `npm 11.16.0`, `pnpm 11.21.0` (Windows).
18
+
19
+ Nothing in this file is copied from another project's documentation; each block is the
20
+ observed output of the command above it. Where a claim could not be verified it is marked
21
+ **unverified** and is not used by the card.
22
+
23
+ ## 1. Anchors and range size
24
+
25
+ ```console
26
+ $ git -C D:\deepseek-harness tag -l "dsh-v0.1.5*"
27
+ dsh-v0.1.5-alpha.1
28
+ dsh-v0.1.5-alpha.2
29
+ dsh-v0.1.5-rc.1
30
+
31
+ $ git -C D:\deepseek-harness rev-list -n1 dsh-v0.1.5-alpha.1
32
+ 5dda764ed3aa172535a7967b06ff95d9cbfe536a
33
+ $ git -C D:\deepseek-harness rev-list -n1 dsh-v0.1.5-rc.1
34
+ 183f08e9c6dde7e36cd2318eaee70b0da08fb35e
35
+
36
+ $ git -C D:\deepseek-harness log --oneline dsh-v0.1.5-alpha.1..dsh-v0.1.5-rc.1 | wc -l
37
+ 279
38
+ $ git -C D:\deepseek-harness log --oneline dsh-v0.1.5-alpha.2..dsh-v0.1.5-rc.1 | wc -l
39
+ 17
40
+
41
+ $ git -C D:\deepseek-harness diff --shortstat dsh-v0.1.5-alpha.1..dsh-v0.1.5-rc.1
42
+ 1506 files changed, 31725 insertions(+), 11448 deletions(-)
43
+
44
+ $ git -C D:\deepseek-harness show dsh-v0.1.5-alpha.1:package.json # .version
45
+ 0.1.5-alpha.1
46
+ $ git -C D:\deepseek-harness show dsh-v0.1.5-rc.1:package.json # .version
47
+ 0.1.5-rc.1
48
+ ```
49
+
50
+ ## 2. Client slot catalog: 57 → 61 keys, `conversation` deleted
51
+
52
+ `packages/extensions/cordis-client-runner/src/client/slot-catalog.ts` is the public catalog a
53
+ plugin author reads through Inspect `Slots.listSubTree`. Extracting every `key: '<name>'`
54
+ and diffing the two tags:
55
+
56
+ ```console
57
+ alpha1 count: 57 rc1 count: 61
58
+ --- removed ---
59
+ conversation
60
+ --- added ---
61
+ main, main.conversation, rightbar.session, sidebar.panellist, sidebar.right.tab.document
62
+ ```
63
+
64
+ Implementation side, same file path at both tags
65
+ (`packages/client/ui-conversation/src/client/apply.ts`):
66
+
67
+ ```console
68
+ $ dsh-v0.1.5-alpha.1:.../apply.ts:217 name: 'conversation',
69
+ $ dsh-v0.1.5-rc.1:.../apply.ts:220 name: 'main.conversation',
70
+ $ dsh-v0.1.5-rc.1:.../apply.ts:388 slots.inject('main', function* () {
71
+ $ dsh-v0.1.5-rc.1:.../apply.ts:390 name: 'main',
72
+ $ dsh-v0.1.5-rc.1:.../apply.ts:391 key: 'conversation',
73
+ $ dsh-v0.1.5-rc.1:.../apply.ts:392 children: { 'main.conversation': { kind: 'single', scope: 'session-maybe' } },
74
+ ```
75
+
76
+ No alias survives:
77
+
78
+ ```console
79
+ $ git -C D:\deepseek-harness grep -n "name: 'conversation'" dsh-v0.1.5-rc.1 -- packages
80
+ dsh-v0.1.5-rc.1:packages/client/ui-agent-preset/tests/apply.client.spec.ts:135: name: 'conversation',
81
+ ```
82
+
83
+ That single hit is a test fixture, not an implementation. The public contract that makes the
84
+ removal silent is quoted verbatim from the same tag's
85
+ `packages/extensions/cordis-client-runner/src/client/api-catalog.ts` (`SERVICE_API`, key
86
+ `slots`):
87
+
88
+ > `inject(key: keyof SlotMap & string, callback: () => SlotInjectionEffect): () => void`
89
+ > "Install an effect for each declaration lifetime of a slot. The callback runs synchronously
90
+ > when the declaration already exists; otherwise it runs inside the declaring `register()`
91
+ > call after the declaration is committed."
92
+
93
+ Upstream ships **no** deprecation or migration note for the removal: the only
94
+ `docs/capability-seams.md` change in the range is one added service row (§4 below), and no
95
+ document names `conversation`.
96
+
97
+ ## 3. Package rename
98
+
99
+ ```console
100
+ $ git show dsh-v0.1.5-alpha.1:packages/client/ui-sidebar-textpreview/package.json # .name
101
+ @deepseek-ai/dsh-client-ui-sidebar-textpreview
102
+ $ git show dsh-v0.1.5-rc.1:packages/client/ui-sidebar-documentpreview/package.json # .name
103
+ @deepseek-ai/dsh-client-ui-sidebar-documentpreview
104
+
105
+ $ git ls-tree -d --name-only dsh-v0.1.5-alpha.1 packages/client/ | Select-String sidebar
106
+ packages/client/ui-sidebar-files
107
+ packages/client/ui-sidebar-right
108
+ packages/client/ui-sidebar-textpreview
109
+ packages/client/ui-sidebar
110
+ $ git ls-tree -d --name-only dsh-v0.1.5-rc.1 packages/client/ | Select-String sidebar
111
+ packages/client/ui-sidebar-documentpreview
112
+ packages/client/ui-sidebar-files
113
+ packages/client/ui-sidebar-right
114
+ packages/client/ui-sidebar
115
+ ```
116
+
117
+ The old directory and package name are gone in rc.1 with no shim package.
118
+
119
+ ## 4. Host-side additions
120
+
121
+ ```console
122
+ $ git diff dsh-v0.1.5-alpha.1..dsh-v0.1.5-rc.1 -- packages/core/session/src
123
+ known-event-types.ts | + 'deliverables/presented',
124
+ known-event-types.ts | + 'subagent/catalog',
125
+ types.ts | - ... In v2 the ... + ... The ...
126
+
127
+ $ git ls-tree -d --name-only dsh-v0.1.5-rc.1 packages/fs/
128
+ ...,packages/fs/tool-present,... # absent from the alpha.1 listing
129
+ $ git show dsh-v0.1.5-rc.1:packages/fs/tool-present/src/index.ts # line 39
130
+ name: 'present',
131
+
132
+ $ git show dsh-v0.1.5-rc.1:packages/client/ui-deliverables/src/client/index.ts # line 62
133
+ { name: 'tool.call.toolview', key: 'present', locale: NS }, PresentRow,
134
+
135
+ # conversation.chat.node's already-taken keyDomain, both tags
136
+ alpha.1: ... already taken: ask_user_question, bash, ..., grep, read, read_image, ...
137
+ rc.1: ... already taken: ask_user_question, bash, ..., grep, present, read, read_image, ...
138
+
139
+ $ git diff <range> -- docs/capability-seams.md
140
+ + pkg_command_feedback["command-feedback"]
141
+ + svc_sessionFeedback["ctx.sessionFeedback<br/>Session-level feedback recorder"]
142
+ + | `ctx.sessionFeedback` | `core` | [`command-feedback`](...) | ... |
143
+
144
+ $ git ls-tree --name-only dsh-v0.1.5-alpha.1 docs/session-format-status.md # (no output)
145
+ $ git show dsh-v0.1.5-rc.1:docs/session-format-status.md # new file
146
+ latestReleasedVersion: 3
147
+ evidenceTag: dsh-v0.1.5-alpha.1
148
+ ```
149
+
150
+ `docs/session-format-status.md` is **new in rc.1** and its release record still points at
151
+ `dsh-v0.1.5-alpha.1`; it states "An alpha, beta, or release-candidate product publication
152
+ establishes released Session-format obligations."
153
+
154
+ The `ctx.layout` / `ctx.workspaces` / `TYPE_API` additions are quoted in the card; their
155
+ source is the `api-catalog.ts` diff of the same range, which adds
156
+ `selectPanel(panelId: MainPanelId | null): void`, `beginNavigation(): AbortSignal`,
157
+ `openSession(sessionId: SessionId): void`,
158
+ `openWorkspace(workspaceId: WorkspaceId, beforeOpen?: (sessionId: SessionId) => void): Promise<void>`,
159
+ `forkSession(sessionId: SessionId): Promise<void>` and
160
+ `export type MainPanelId = Branded<'MainPanelId'>;`.
161
+
162
+ ## 5. Negative evidence (what did **not** change)
163
+
164
+ ```console
165
+ $ git -C D:\deepseek-harness grep -l "usePanelInfo" dsh-v0.1.5-alpha.1 -- packages apps | wc -l
166
+ 0
167
+ $ git -C D:\deepseek-harness grep -l "usePanelInfo" dsh-v0.1.5-rc.1 -- packages apps | wc -l
168
+ 47
169
+
170
+ $ git diff --stat dsh-v0.1.5-alpha.1..dsh-v0.1.5-rc.1 -- packages/client/ui-slots
171
+ packages/client/ui-slots/package.json | 2 +-
172
+ 1 file changed, 1 insertion(+), 1 deletion(-)
173
+ ```
174
+
175
+ The session-format seams **leg A** owns (`assistant/message.stream`, `SessionHandleReadResult`,
176
+ `EpochHeader.system`, `ctx.agent`, `Inbox`, `SystemPrompt.persona`, the V3 log generation) have
177
+ **no** implementation change in this range: the whole `packages/core/session/src` diff is the
178
+ two added event-type literals and one comment line shown above. `docs/web-styling.md` (theme
179
+ tokens) has zero changes in the range. The scanner therefore reports no leg-A hit here that is
180
+ not also a leg-A fact, and the card says so explicitly in §2's scope statement.
181
+
182
+ ## 6. Peer-range semantics (semver 7.8.5, measured)
183
+
184
+ ```console
185
+ $ node -e "const s=require('.../semver'); ..."
186
+ semver version: 7.8.5
187
+ ">=0.1.2-rc.1 <0.2.0" -> false
188
+ ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0" -> true
189
+ ">=0.1.5-alpha.1 <0.2.0" -> true
190
+ ">=0.1.5-rc.1 <0.2.0" -> true
191
+ ">=0.1.5-alpha.1 <0.2.0 || >=0.1.5-rc.1 <0.2.0" -> true
192
+ alpha.1 satisfies >=0.1.5-alpha.1 <0.2.0 -> true
193
+ ```
194
+
195
+ This is npm semver's prerelease-tuple rule: a prerelease version only satisfies a set when
196
+ some comparator on the same `[major, minor, patch]` tuple carries a prerelease. It is the
197
+ same rule behind the workspace's recorded `>=0.1.0-rc.8 <0.2.0` incident.
198
+
199
+ ## 7. Real-world cross-check on the family workspace
200
+
201
+ Slot usage was extracted from **tracked** sources only (`git ls-files`, production files,
202
+ `node_modules`/`lib`/`dist`/docs snapshots excluded) across the 47 repositories under
203
+ `D:\Projects\dsh\plugins`:
204
+
205
+ ```console
206
+ == PRODUCTION: 8 distinct keys ==
207
+ conversation.input.left <- dsh-autotier, dsh-talk
208
+ conversation.session.header.actions <- dsh-auto-review, dsh-session-pin, dsh-ticktick
209
+ settings.plugin.item <- dsh-github, dsh-ticktick
210
+ settings.plugins.tab <- dsh-autotier, dsh-budget, dsh-checkpoint-rewind,
211
+ dsh-draw, dsh-mcp-panel, dsh-reach, dsh-talk
212
+ settings.section <- dsh-background-agents, dsh-memento,
213
+ dsh-permission-rules, dsh-wechat
214
+ shell.overlay <- dsh-session-pin
215
+ sidebar.footer.action <- dsh-background-agents, dsh-session-pin
216
+ tool.call.toolview <- dsh-draw
217
+ == TESTS: 1 distinct key ==
218
+ session-1 <- dsh-auto-review:test/client-registration.spec.ts
219
+ (a Remote face's `options.inject(...)`, not the
220
+ slots service — false positive, excluded)
221
+
222
+ $ # tracked sources across all 47 repos, same pattern
223
+ inject('conversation' | name: 'conversation' | sidebar-textpreview
224
+ (zero hits)
225
+ ```
226
+
227
+ 8 production keys across 15 repositories; the rc.1 deletion set is `{conversation}`, so all
228
+ 8 survive. This is why the family's rc.1 CHANGELOG entries are pin-only, e.g.
229
+ `dsh-autotier/CHANGELOG.md:14`, `dsh-mcp-panel/CHANGELOG.md:16`,
230
+ `dsh-plugin-guide/CHANGELOG.md:22`, `dsh-permission-rules/CHANGELOG.md:69` (identical
231
+ wording). **The prior research pass recorded nine keys**; the ninth it listed,
232
+ `conversation.chat.node`, occurs in this workspace only inside
233
+ `dsh-plugin-guide/references/official-docs/` (a downloaded official-docs snapshot), never in
234
+ family plugin code. The card states the measured eight.
235
+
236
+ Related recorded behaviour, unchanged by this hop: `dsh-permission-rules/CHANGELOG.md:100`
237
+ describes `isUnmarkedHostVersion` treating the `0.1.5-alpha` line as non-stamping, so an
238
+ envelope-level `ignorable` write is silently dropped there. Since
239
+ `packages/session` + `packages/core` carry no substantive `ignorable` change in this range,
240
+ rc.1 inherits that behaviour.
241
+
242
+ ## 8. Model catalog default
243
+
244
+ ```console
245
+ $ git log --oneline -1 bc5fd3b8dc
246
+ bc5fd3b8dc feat(llm): default Chat Completions to DeepSeek V41 Flash
247
+
248
+ $ git show dsh-v0.1.5-alpha.1:packages/llm/llm-deepseek/src/index.ts # DEFAULT_MODELS ids
249
+ id: 'deepseek-v4-flash' / 'deepseek-v4-pro' / 'deepseek-v4-flash-vision-exp'
250
+ $ git show dsh-v0.1.5-rc.1:packages/llm/llm-deepseek/src/index.ts # DEFAULT_MODELS ids
251
+ id: 'deepseek-flash' (name: 'DeepSeek-V41-Flash') / 'deepseek-v4-flash' / 'deepseek-v4-pro' /
252
+ 'deepseek-v4-flash-vision-exp'
253
+ ```
254
+
255
+ The rc.1 README's `models` default row reads "V41 Flash + V4 Flash + V4 Pro + V4 Flash
256
+ Vision Exp". **Unverified:** that position 0 of the advisory catalog is *formally* the
257
+ selection default — no `DEFAULT_MODELS[0]` read appears in the adapter source. The card
258
+ therefore states what was measured (the catalog contents and the commit subject) and does
259
+ not claim a selection rule.
260
+
261
+ ## 9. Package name availability
262
+
263
+ Re-measured for the merged package name on 2026-09-11 (the merged repository keeps this
264
+ record; the two retired names are the controls that prove the registry answers):
265
+
266
+ ```console
267
+ $ npm view dsh-plugin-upgrade version
268
+ npm error code E404
269
+ npm error 404 Not Found - GET https://registry.npmjs.org/dsh-plugin-upgrade - Not found
270
+ npm error 404 The requested resource 'dsh-plugin-upgrade@*' could not be found or you do not have permission to access it.
271
+ (exit 1)
272
+
273
+ $ npm view dsh-plugin-upgrade version dist-tags.latest # control: retired leg A still exists
274
+ version = '0.1.3'
275
+ dist-tags.latest = '0.1.3'
276
+ $ npm view dsh-plugin-upgrade-rc1 version dist-tags.latest # control: retired leg B still exists
277
+ version = '0.1.0'
278
+ dist-tags.latest = '0.1.0'
279
+ $ npm view @deepseek-ai/dsh-skill@0.1.5-rc.2 version # control: the merged dev pin is published
280
+ 0.1.5-rc.2
281
+ ```
282
+
283
+ ### 9.1 The original name was rejected by npm (403 spam detection)
284
+
285
+ The first release attempt under the name `dsh-plugin-upgrade-0.1.3-0.1.5` failed at the
286
+ publish step of `release.yml` (run `34606797772`, 2026-09-11). The gate and the changelog
287
+ check both passed; only the registry write was refused:
288
+
289
+ ```console
290
+ $ npm publish # inside .github/workflows/release.yml, NPM_CONFIG_USERCONFIG written from the repo secret
291
+ npm notice name: dsh-plugin-upgrade-0.1.3-0.1.5
292
+ npm notice version: 0.1.0
293
+ npm notice package size: 88.5 kB
294
+ npm notice total files: 25
295
+ npm notice publish Signed provenance statement with source and build information from GitHub Actions
296
+ npm notice publish Provenance statement published to transparency log: https://search.sigstore.dev/?logIndex=2794018273
297
+ npm error code E403
298
+ npm error 403 403 Forbidden - PUT https://registry.npmjs.org/dsh-plugin-upgrade-0.1.3-0.1.5 - Package name triggered spam detection; if you believe this is in error, please contact support at https://npmjs.com/support
299
+ (exit 1)
300
+
301
+ $ curl -s -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/dsh-plugin-upgrade-0.1.3-0.1.5
302
+ 404 # the 403 lands before any write, so the name was never created
303
+ ```
304
+
305
+ The rejected name embedded **two dotted version numbers** (`0.1.3-0.1.5`), which npm's spam
306
+ heuristics read as a version-shaped suffix. The package was therefore named
307
+ `dsh-plugin-upgrade` — matching its own skill id (`plugin-upgrade`) and bin
308
+ (`dsh-plugin-upgrade-scan`). The control is on the same account: the retired
309
+ `dsh-plugin-upgrade-rc1` (a single non-dotted suffix) published cleanly, so the **suffix
310
+ shape**, not the account or the plugin family, is what the detector reacted to.
311
+
312
+ ## 10. Not verified (kept out of the card's claims)
313
+
314
+ - **L9 (real browser).** No browser assertion was executed in the environment that produced
315
+ this package; the card marks it as a hard requirement for the reader and records it as
316
+ unexecuted here.
317
+ - **L11 (real model credentials).** Not reproducible without credentials.
318
+ - Whether any family repository ever pinned `0.1.5-alpha.2` (not checked per repository).
319
+ - Whether `packages/client/*` contains plugin-author-facing breakage outside the public slot
320
+ and service catalogs (no full export diff of the 432 changed client files).
321
+ - Whether upstream intends to publish a migration note for the `conversation` removal; the
322
+ card records the absence of one as of 2026-09-10.
323
+
324
+ ## A. Leg A provenance (`0.1.3-alpha.1` → `0.1.5-alpha.1`)
325
+
326
+ Leg A's provenance does **not** live in this file. It lives in the retired
327
+ `dsh-plugin-upgrade` package's version card,
328
+ `skills/plugin-upgrade/references/v0.1.3-alpha.1-to-v0.1.5-alpha.1.md`, which the merge
329
+ brought in as the merged card's **§1 Leg A** — verbatim, including its `path:line` citations,
330
+ its measured scale and its boundary notes.
331
+
332
+ What that leg's provenance records, so a reader of this file can find it without the retired
333
+ package:
334
+
335
+ | Item | Leg A's recorded value |
336
+ |---|---|
337
+ | Card that holds the evidence | retired `dsh-plugin-upgrade` card, now merged card §1 Leg A |
338
+ | Run | 2026-09-09 wave over **40 real plugin repositories** |
339
+ | Leg-A host checkout | `0.1.5-alpha.1`, HEAD `19d2e38480`; official tag `dsh-v0.1.5-alpha.1` = `5dda764ed3` |
340
+ | Seams measured | `S1`–`S10` + `M1` (the merged card's §1 sections 1–5, with the per-seam host path and commit) |
341
+ | Reproduce | CLI `dsh-plugin-upgrade-scan --repo <repo>` (then `dsh-plugin-upgrade-scan`), `file:line` output |
342
+ | `M1` scale | **11 of 40** repos hit the stale type line; fixing the paths exposed real TypeScript errors in **3** repos that had been "green" |
343
+ | `S3` scale | one measured case (`dsh-claude-move`: import succeeded, `Session.fromRestore` refused to resume) |
344
+ | `S8` scale | `dsh-background-agents` production code `src/tools.ts:713` (`TS2740`) plus two tests; `dsh-output-styles` `scripts/verify-session-log.mjs` threw a TypeError |
345
+ | `S9` scale | four repos' test fixtures (`dsh-data-quality`, `dsh-industry-research`, `dsh-research-report`, `dsh-fast`; `TS2353`) |
346
+ | Leg-A official precedent cited | `apps/cli/tests/profiles/headless/tests/harness.ts:59` |
347
+ | Leg-A unverified | real-browser L9 and real-model L11 were not executed there either (recorded in that card's §7) |
348
+
349
+ The merged repository re-measured leg A's seams against its own fixtures
350
+ (`fixtures/leg-a-bad-repo` / `fixtures/leg-a-good-repo`) rather than re-running the 40-repo
351
+ wave: the fixture pair pins the same error-severity seams the card names
352
+ (`S3`, `S8`, `S9`, `S4`, `S5`, `S6`, `M1` in the bad fixture; zero error hits in the good one).
353
+ Nothing in leg A's provenance was discarded by the merge, and nothing in it was re-derived
354
+ here — where the two legs disagree, the leg that measured the seam wins.
355
+
356
+
357
+ ---
358
+
359
+ ## Section 11 - leg C (0.1.5-rc.2 -> 0.1.6-alpha.2)
360
+
361
+ > Verbatim from the dsh-plugin-upgrade-016 repository's docs/EVIDENCE.md, moved here when that corridor was folded into this package as the second index row (2026-09-19). Its sub-headings keep their own numbering.
362
+
363
+ # EVIDENCE · dsh-plugin-upgrade-016
364
+
365
+ > 走廊方法论要求:卡片与扫描器里的每一条事实都能回溯到一条命令 + 输出。
366
+ > 本文件记录 leg C(`0.1.5-rc.2 → 0.1.6-alpha.2`)五条接缝的出处。
367
+ > 标注纪律:`已实测` = 本建包会话在 `D:\deepseek-harness`(tag `dsh-v0.1.6-alpha.2`,HEAD `ddefc45`)上重跑过;`转引已核实` = 方案文件 R3/A02/A01 已在研究期回源码核实,本会话不重复跑;`未验证` = 未跑。
368
+
369
+ ## E1 · `agent/created` 串行派发(error)— 已实测
370
+
371
+ ```console
372
+ $ git -C D:\deepseek-harness grep -n "agent/created" ddefc45 -- "packages/core/agent/src/*.ts"
373
+ ddefc45:packages/core/agent/src/index.ts:109: * child plugins) exists before `session/created`, `agent/created`,
374
+ ddefc45:packages/core/agent/src/index.ts:176: * `agent/created` listeners before releasing queued work. The sequence is
375
+ ddefc45:packages/core/agent/src/index.ts:417: * serial `agent/created` listener fails. Emits `agent/disposed`
376
+ ddefc45:packages/core/agent/src/index.ts:547: await this.ctx.serial(entry.carrier, 'agent/created', {
377
+ ```
378
+
379
+ 断言:`:547` 的 `ctx.serial(..., 'agent/created', ...)` 证明监听器在创建链上**串行 await** —— 一个抛错的监听器直接中断创建。A1 语义转引 `[A02]`/`[A05]`(已核实)。
380
+
381
+ ## E2 · 异步 apply 竞态(error)— 转引已核实
382
+
383
+ 宿主 fiber 在卸载窗口内对迟到的 `ctx.effect` 抛 `INACTIVE_EFFECT`(抛点 `fiber.ts` 已由方案 `[A02]` 回源码核实);`register()` 返回值丢弃 = 旧闭包继续生效(反例 doublecheck `src/guard/index.ts:664`,正例 `:685-688`)。本卡检测面 = 「`export async function apply` 首个 `await` 之后出现 `ctx.effect|ctx.on|ctx.provide|ctx.plugin|*.register`」。本会话未真机复现卸载时序(`未验证`,扫描器是静态检测)。
384
+
385
+ ## E3 · `settings.plugin.item` 与 `SessionListState.current`(error)— 已实测
386
+
387
+ ```console
388
+ $ git -C D:\deepseek-harness grep -n "settings\.plugin\.item" ddefc45 -- "*.ts"
389
+ ddefc45:packages/client/ui-settings-models/src/client/slot-contract.ts:11: * the namespace follows `settings.plugin.item`, and the key domain stays the
390
+ $ git -C D:\deepseek-harness grep -n "'plugins\.item'" ddefc45 -- "*.ts"
391
+ ddefc45:packages/client/ui-plugin-manager/src/client/slot-contract.ts:32: 'plugins.item': { kind: 'list'; scope: 'root'; owner: PluginConfigViewProps }
392
+ ddefc45:packages/client/ui-plugin-manager/src/client/index.ts:89: 'plugins.item': { kind: 'list', scope: 'root' },
393
+ ```
394
+
395
+ 断言:`settings.plugin.item` 只剩一条**注释**(旧名说明),槽契约里已无该 key;新契约 = `plugins.item`(list/root)。`SessionListState.current` 的删除与 7 仓命中由 `[R3]` §② 穷尽差集核实(主控已回源码复核全部命中仓;`未验证` 本会话未重跑该差集)。
396
+
397
+ ## E4 · `sessions.open/openSubagent/clear`(error)— 已实测
398
+
399
+ ```console
400
+ $ git -C D:\deepseek-harness grep -n "openSubagent" ddefc45 -- "*.ts"
401
+ (no matches)
402
+ $ git -C D:\deepseek-harness grep -n "retain" ddefc45 -- "packages/api/session-controller/src/client/sessions/manager.ts"
403
+ ddefc45:packages/api/session-controller/src/client/sessions/manager.ts:149: throw new Error(`sessions.retain: unknown session ${id}`)
404
+ ```
405
+
406
+ 断言:`openSubagent` 在 alpha.2 源码零命中;替代面 `retain` 存在。`open`/`clear` 的删除与三个消费方的形态(session-pin 响亮 TypeError、background-agents 吞错、claude-move 特性探测)由 `[R3]` §② 核实。
407
+
408
+ ## E5 · 模型字面量 `deepseek-v4-flash*`/`deepseek-v4-vision-exp`(error)— 已实测
409
+
410
+ ```console
411
+ $ git -C D:\deepseek-harness grep -rn "deepseek-v4-flash" ddefc45 -- "packages"
412
+ (no matches)
413
+ $ git -C D:\deepseek-harness grep -rn "deepseek-v4-pro" ddefc45 -- "packages" | head -3
414
+ ddefc45:packages/client/ui-model-selection/src/client/index.ts:52: 'deepseek-official/deepseek-v4-pro': 'option.deepseekV4Pro.description',
415
+ ```
416
+
417
+ 断言:被删 id 在 alpha.2 源码零命中,`deepseek-v4-pro` 仍在册。目录 4→2 与「未编目 id 透传 text-only」(上游 `llm-deepseek/README.md:48` 原文)由 `[A03]`/`[主控 M2]` 核实;非测试区 3 仓引用 + autotier README×5 与 8 个测试文件硬编码由 `[R4]` 新发现核实。
418
+
419
+ ## 家族面事实(W0 钉死,本卡前置)
420
+
421
+ - 事件词表 58(+`workspace/changes`)、槽表 70 键、默认模型目录 2 条:`[A01]` §3 核实。
422
+ - peer canonical 三段式:`dsh-plugin-kit/data/peer-range.json`(W0 0-K 合入,tripwire exit 0 已实测)。
423
+ - 未验证项(诚实):0.1.6 走廊卡在真实 45 仓上的全量扫描回归(等 W1 修复落地后跑);本包真宿主冒烟(首次 publish 前执行)。
package/index.mjs CHANGED
@@ -1,11 +1,14 @@
1
+ // SPDX-License-Identifier: Apache-2.0
1
2
  // dsh-plugin-upgrade bundle entry point.
2
3
  //
3
- // Publishes the packaged 0.1.3-alpha.1 -> 0.1.5-alpha.1 plugin-upgrade skill as an
4
- // on-demand agent skill named `plugin-upgrade-015`. The skill body is this
5
- // package's `skills/plugin-upgrade-015/SKILL.md`; its relative references
6
- // (`./references/...`) and scripts (`./scripts/...`) resolve against the packaged
7
- // skills directory through the directory resourceBase, so the agent loads the
8
- // version card and the scanner only when a task needs them.
4
+ // Publishes the merged `0.1.3-alpha.1 -> 0.1.5-rc.1` corridor as an on-demand
5
+ // agent skill named `plugin-upgrade`. The corridor is one span carried by
6
+ // two closed legs (leg A `0.1.3-alpha.1 -> 0.1.5-alpha.1`, leg B
7
+ // `0.1.5-alpha.1 -> 0.1.5-rc.1`); the skill body is this package's
8
+ // `skills/plugin-upgrade/SKILL.md`, and its relative references
9
+ // (`./references/...`) and scripts (`./scripts/...`) resolve against the
10
+ // packaged skills directory through the directory resourceBase, so the agent
11
+ // loads the merged version card and the scanner only when a task needs them.
9
12
  //
10
13
  // The package imports nothing from the harness beyond the injected `skills`
11
14
  // service, so the cordis peer stays metadata-only.
@@ -28,7 +31,7 @@ export const Config = Schema.object({
28
31
  /** Register the packaged skill (default true). */
29
32
  enabled: Schema.boolean().default(true),
30
33
  /** Skill name published to the model catalog. Defaults to the packaged corridor name. */
31
- skillName: Schema.string().default('plugin-upgrade-015'),
34
+ skillName: Schema.string().default('plugin-upgrade'),
32
35
  /** Skill root inside the package; must contain `<skillName>/SKILL.md`. */
33
36
  skillsRoot: Schema.string().default(join(packageRoot, 'skills')),
34
37
  /** Mark the skill user-invocable in addition to model-invocable (default true). */
@@ -40,8 +43,8 @@ export const Config = Schema.object({
40
43
  * and body. Line endings are normalized first: a Windows checkout with
41
44
  * `core.autocrlf=true` hands us CRLF, and the frontmatter delimiters are `\n`.
42
45
  * A missing block falls back to the full text as the body.
43
- * @param text - raw SKILL.md content.
44
- * @returns the parsed description/whenToUse (when present) and the instruction body.
46
+ * @param {string} text - raw SKILL.md content.
47
+ * @returns {{ description: string | undefined, whenToUse: string | undefined, body: string }} the parsed description/whenToUse (when present) and the instruction body.
45
48
  */
46
49
  export function splitFrontmatter(text) {
47
50
  const source = text.replace(/\r\n/g, '\n')
@@ -50,7 +53,8 @@ export function splitFrontmatter(text) {
50
53
  if (end < 0) return { description: undefined, whenToUse: undefined, body: source }
51
54
  const meta = source.slice(4, end)
52
55
  const body = source.slice(end + 4).replace(/^\n+/, '')
53
- const scalar = (key) => new RegExp(`^${key}:\\s*(.+)$`, 'm').exec(meta)?.[1]?.trim()
56
+ /** @param {string} key @returns {string | undefined} */
57
+ const scalar = (key) => new RegExp(`^${key}:\\s*(.+)$`, 'm').exec(meta)?.[1]?.trim().replace(/^["']|["']$/g, '')
54
58
  return { description: scalar('description'), whenToUse: scalar('whenToUse'), body }
55
59
  }
56
60
 
@@ -58,9 +62,9 @@ export function splitFrontmatter(text) {
58
62
  * Read and validate the packaged skill bundle. Fails loud: a missing SKILL.md,
59
63
  * an empty body, or a missing frontmatter `name` aborts the mount instead of
60
64
  * registering an empty skill.
61
- * @param skillsRoot - root directory holding `<skillName>/SKILL.md`.
62
- * @param skillName - expected skill directory name.
63
- * @returns the frontmatter name, routing fields, body, and the skill directory.
65
+ * @param {string} skillsRoot - root directory holding `<skillName>/SKILL.md`.
66
+ * @param {string} skillName - expected skill directory name.
67
+ * @returns {{ frontmatterName: string, description: string | undefined, whenToUse: string | undefined, body: string, skillDir: string }} the frontmatter name, routing fields, body, and the skill directory.
64
68
  */
65
69
  export function readSkillBundle(skillsRoot, skillName) {
66
70
  const skillPath = join(skillsRoot, skillName, 'SKILL.md')
@@ -81,13 +85,13 @@ export function readSkillBundle(skillsRoot, skillName) {
81
85
  /**
82
86
  * Register the packaged skill. Registration is an effect: the disposer returned
83
87
  * by `ctx.skills.register()` removes the contribution on unload.
84
- * @param ctx - Cordis context with the injected `skills` service.
85
- * @param config - validated plugin configuration.
88
+ * @param {{ effect: (factory: () => unknown) => unknown, skills: { register: (registration: Record<string, unknown>) => unknown } }} ctx - Cordis context with the injected `skills` service.
89
+ * @param {{ enabled?: boolean, skillName?: string, skillsRoot?: string, userInvocable?: boolean }} [config] - validated plugin configuration.
86
90
  */
87
91
  export function apply(ctx, config = {}) {
88
92
  const resolved = {
89
93
  enabled: config.enabled ?? true,
90
- skillName: config.skillName ?? 'plugin-upgrade-015',
94
+ skillName: config.skillName ?? 'plugin-upgrade',
91
95
  skillsRoot: config.skillsRoot ?? join(packageRoot, 'skills'),
92
96
  userInvocable: config.userInvocable ?? true,
93
97
  }
@@ -97,7 +101,7 @@ export function apply(ctx, config = {}) {
97
101
  ctx.skills.register({
98
102
  name: frontmatterName,
99
103
  source: 'bundled',
100
- description: description ?? 'DSH plugin upgrade · 0.1.3-alpha.1 -> 0.1.5-alpha.1: seam scanner and version card.',
104
+ description: description ?? 'DSH plugin upgrade · 0.1.3-alpha.1 -> 0.1.5-rc.1 (merged corridor): seam scanner and corridor card.',
101
105
  ...whenToUse !== undefined ? { whenToUse } : {},
102
106
  content: body,
103
107
  // The base is the skill's own directory, so `./references/...` and
package/lib/route.mjs ADDED
@@ -0,0 +1,112 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * The corridor index: one package, one entry point, several closed corridors.
4
+ *
5
+ * Owner decision (2026-09-19): the family's earlier "a hop is a new package" rule
6
+ * is superseded. This package carries a corridor index instead, and the scanner
7
+ * picks the corridor that matches the repository under inspection. What does NOT
8
+ * change: each corridor keeps its OWN evidence-bound catalog module - the seam
9
+ * arrays are never merged, so every card claim stays traceable to its measurement.
10
+ *
11
+ * Routing is deliberately a heuristic over the target repository's declared band
12
+ * (`engines.dsh`, dependencies on `@deepseek-ai/dsh*`), because the scanner is
13
+ * read-only, dependency-free and offline: no registry lookup happens here. The
14
+ * "which line is newest" question belongs to the agent running the skill, not to
15
+ * this scanner. `--span <id>` overrides the guess, and an unknown band falls back
16
+ * to the older corridor, which is what a repository predating 0.1.6 needs anyway.
17
+ */
18
+ import { readFileSync } from 'node:fs'
19
+
20
+ /** @typedef {{ id: string, span: string, catalog: string, card: string, baselineTags: string[], evidence: string }} Corridor */
21
+
22
+ /** @type {Corridor[]} */
23
+ export const CORRIDORS = [
24
+ {
25
+ id: 'legAB',
26
+ span: '0.1.3-alpha.1 -> 0.1.5-rc.1',
27
+ catalog: '../lib/scan.mjs',
28
+ card: 'skills/plugin-upgrade/references/v0.1.3-alpha.1-to-v0.1.5-rc.1.md',
29
+ baselineTags: ['dsh-v0.1.5-alpha.1', 'dsh-v0.1.5-rc.1'],
30
+ evidence: 'docs/EVIDENCE.md section A (leg A provenance) and sections 1-10 (leg B records)',
31
+ },
32
+ {
33
+ id: 'legC',
34
+ span: '0.1.5-rc.2 -> 0.1.6-alpha.2',
35
+ catalog: '../lib/scan-0.1.6.mjs',
36
+ card: 'skills/plugin-upgrade/references/v0.1.5-rc.2-to-v0.1.6-alpha.2.md',
37
+ baselineTags: ['dsh-v0.1.6-alpha.2'],
38
+ evidence: 'docs/EVIDENCE.md section 11 (leg C records)',
39
+ },
40
+ ]
41
+
42
+ /** Accepts a corridor id ('legC'), a full span, or a bare mention of a line. */
43
+ export function corridorById(id) {
44
+ if (typeof id !== 'string' || id.length === 0) return undefined
45
+ const wanted = id.trim().toLowerCase()
46
+ return CORRIDORS.find(
47
+ c => c.id.toLowerCase() === wanted || c.span.toLowerCase() === wanted || c.span.toLowerCase().includes(wanted),
48
+ )
49
+ }
50
+
51
+ /** The 0.1.6 line marker. Kept as one regex so the heuristic is auditable. */
52
+ const LEG_C_MARKER = /0\.1\.6-(alpha|beta|rc|0)/
53
+
54
+ /**
55
+ * Reads the target repository's declared band: the text of every field that
56
+ * carries a dsh version, or '' when nothing declares one.
57
+ * @param {string} repoDir
58
+ * @returns {string}
59
+ */
60
+ export function declaredBand(repoDir) {
61
+ try {
62
+ const manifest = JSON.parse(readFileSync(`${repoDir.replace(/[\\/]+$/, '')}/package.json`, 'utf8'))
63
+ const parts = []
64
+ if (manifest.engines && typeof manifest.engines.dsh === 'string') parts.push(manifest.engines.dsh)
65
+ for (const field of ['dependencies', 'devDependencies', 'peerDependencies']) {
66
+ const block = manifest[field]
67
+ if (!block || typeof block !== 'object') continue
68
+ for (const [name, range] of Object.entries(block)) {
69
+ if (name.startsWith('@deepseek-ai/dsh')) parts.push(String(range))
70
+ }
71
+ }
72
+ return parts.join(' ')
73
+ } catch {
74
+ return ''
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Chooses the corridor for a repository, or for an explicit span.
80
+ * @param {{ repoDir?: string, span?: string }} [input]
81
+ * @returns {Corridor}
82
+ */
83
+ export function resolveCorridor(input = {}) {
84
+ if (input.span) {
85
+ const explicit = corridorById(input.span)
86
+ if (explicit) return explicit
87
+ }
88
+ const band = input.repoDir ? declaredBand(input.repoDir) : ''
89
+ if (LEG_C_MARKER.test(band)) return CORRIDORS.find(c => c.id === 'legC')
90
+ return CORRIDORS.find(c => c.id === 'legAB')
91
+ }
92
+
93
+ /** Reads `--span <value>` out of an argv array without touching the other flags. */
94
+ export function spanFromArgv(argv) {
95
+ const index = argv.findIndex(a => a === '--span')
96
+ if (index >= 0 && typeof argv[index + 1] === 'string' && !argv[index + 1].startsWith('--')) return argv[index + 1]
97
+ const inline = argv.find(a => a.startsWith('--span='))
98
+ return inline ? inline.slice('--span='.length) : undefined
99
+ }
100
+
101
+ /** Reads `--repo <value>` (or `--repo=<value>`) out of an argv array. */
102
+ export function repoFromArgv(argv) {
103
+ const index = argv.findIndex(a => a === '--repo')
104
+ if (index >= 0 && typeof argv[index + 1] === 'string') return argv[index + 1]
105
+ const inline = argv.find(a => a.startsWith('--repo='))
106
+ return inline ? inline.slice('--repo='.length) : undefined
107
+ }
108
+
109
+ /** Loads the catalog module the corridor points at. */
110
+ export async function loadCatalog(corridor) {
111
+ return import(new URL(corridor.catalog, import.meta.url).href)
112
+ }