dsh-plugin-upgrade 2.0.0 → 2.0.2

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/lib/route.mjs CHANGED
@@ -39,7 +39,11 @@ export const CORRIDORS = [
39
39
  },
40
40
  ]
41
41
 
42
- /** Accepts a corridor id ('legC'), a full span, or a bare mention of a line. */
42
+ /**
43
+ * Accepts a corridor id ('legC'), a full span, or a bare mention of a line.
44
+ * @param {string} id - the `--span` value, a corridor id, or a bare line mention.
45
+ * @returns {Corridor | undefined} the matching corridor, or undefined when nothing matches.
46
+ */
43
47
  export function corridorById(id) {
44
48
  if (typeof id !== 'string' || id.length === 0) return undefined
45
49
  const wanted = id.trim().toLowerCase()
@@ -90,7 +94,11 @@ export function resolveCorridor(input = {}) {
90
94
  return CORRIDORS.find(c => c.id === 'legAB')
91
95
  }
92
96
 
93
- /** Reads `--span <value>` out of an argv array without touching the other flags. */
97
+ /**
98
+ * Reads `--span <value>` out of an argv array without touching the other flags.
99
+ * @param {string[]} argv - the raw CLI argument list (no `node`/script prefix).
100
+ * @returns {string | undefined} the span value, or undefined when the flag is absent.
101
+ */
94
102
  export function spanFromArgv(argv) {
95
103
  const index = argv.findIndex(a => a === '--span')
96
104
  if (index >= 0 && typeof argv[index + 1] === 'string' && !argv[index + 1].startsWith('--')) return argv[index + 1]
@@ -98,7 +106,11 @@ export function spanFromArgv(argv) {
98
106
  return inline ? inline.slice('--span='.length) : undefined
99
107
  }
100
108
 
101
- /** Reads `--repo <value>` (or `--repo=<value>`) out of an argv array. */
109
+ /**
110
+ * Reads `--repo <value>` (or `--repo=<value>`) out of an argv array.
111
+ * @param {string[]} argv - the raw CLI argument list (no `node`/script prefix).
112
+ * @returns {string | undefined} the repo path, or undefined when the flag is absent.
113
+ */
102
114
  export function repoFromArgv(argv) {
103
115
  const index = argv.findIndex(a => a === '--repo')
104
116
  if (index >= 0 && typeof argv[index + 1] === 'string') return argv[index + 1]
@@ -106,7 +118,11 @@ export function repoFromArgv(argv) {
106
118
  return inline ? inline.slice('--repo='.length) : undefined
107
119
  }
108
120
 
109
- /** Loads the catalog module the corridor points at. */
121
+ /**
122
+ * Loads the catalog module the corridor points at.
123
+ * @param {Corridor} corridor - the corridor whose `catalog` path is imported.
124
+ * @returns {Promise<any>} the catalog module namespace (`main`, `SEAMS`, `render`, …). `any` is the honest type here: the specifier is a runtime string, so the namespace shape is not knowable statically.
125
+ */
110
126
  export async function loadCatalog(corridor) {
111
127
  return import(new URL(corridor.catalog, import.meta.url).href)
112
128
  }
@@ -75,7 +75,17 @@ const SCAN_EXT = /\.(ts|tsx|mts|cts|mjs|cjs|js|jsx|json|yml|yaml)$/
75
75
  * @property {(line: string) => boolean} [lineFilter]
76
76
  * @property {(lines: string[], i: number) => boolean} [windowFilter]
77
77
  * @property {(text: string, file: string) => boolean} [fileCheck]
78
- * @property {(text: string, file: string) => boolean} [downgradeIf] file-level guard recognition
78
+ * @property {(text: string) => boolean} [downgradeIf] file-level guard recognition
79
+ */
80
+
81
+ /**
82
+ * @typedef {object} Hit
83
+ * @property {string} seam
84
+ * @property {string} severity
85
+ * @property {string} file
86
+ * @property {number} line
87
+ * @property {string} snippet
88
+ * @property {string} detail
79
89
  */
80
90
 
81
91
  /** @type {Seam[]} */
@@ -145,6 +155,12 @@ const STRUCTURED = new Set(['E2'])
145
155
  */
146
156
  export const CARD_ONLY = SEAMS.filter(s => s.test === null && !STRUCTURED.has(s.id)).map(s => s.id)
147
157
 
158
+ /**
159
+ * Yield every scannable file under `dir`, depth-limited and read-only.
160
+ * @param {string} dir
161
+ * @param {number} [depth]
162
+ * @returns {Generator<string, void, void>}
163
+ */
148
164
  function* walk(dir, depth = 0) {
149
165
  if (depth > 8) return
150
166
  let ents
@@ -159,7 +175,12 @@ function* walk(dir, depth = 0) {
159
175
  }
160
176
  }
161
177
 
162
- /** 1-based line number of the first line containing `needle`, or 1. */
178
+ /**
179
+ * 1-based line number of the first line containing `needle`, or 1.
180
+ * @param {string} text
181
+ * @param {string} needle
182
+ * @returns {number}
183
+ */
163
184
  function lineOf(text, needle) {
164
185
  const lines = text.split(/\r?\n/)
165
186
  const i = lines.findIndex(l => l.includes(needle))
@@ -172,6 +193,9 @@ function lineOf(text, needle) {
172
193
  * registration call after it. `register()` return values are flagged regardless
173
194
  * of whether they reach `ctx.effect()`: the card instructs the author to hand
174
195
  * them to the effect.
196
+ * @param {string} text
197
+ * @param {string} file
198
+ * @returns {Hit[]}
175
199
  */
176
200
  function checkAsyncApply(text, file) {
177
201
  const lines = text.split(/\r?\n/)
@@ -224,7 +248,7 @@ function checkAsyncApply(text, file) {
224
248
  * Scan one repo.
225
249
  * @param {string} repoDir
226
250
  * @param {{ seams?: string[] }} [options]
227
- * @returns {{ repo: string, scannedAt: string, files: number, hits: any[], bySeam: Record<string, number> }}
251
+ * @returns {{ repo: string, scannedAt: string, files: number, hits: Hit[], bySeam: Record<string, number> }}
228
252
  */
229
253
  export function scanRepo(repoDir, options = {}) {
230
254
  const wanted = options.seams && options.seams.length ? new Set(options.seams) : null
@@ -257,12 +281,17 @@ export function scanRepo(repoDir, options = {}) {
257
281
  }
258
282
  if (!wanted || wanted.has('E2')) hits.push(...checkAsyncApply(text, file))
259
283
  }
284
+ /** @type {Record<string, number>} */
260
285
  const bySeam = {}
261
286
  for (const h of hits) bySeam[h.seam] = (bySeam[h.seam] || 0) + 1
262
287
  return { repo: repoDir, scannedAt: new Date().toISOString(), files, hits, bySeam }
263
288
  }
264
289
 
265
- /** Human-readable rendering. Order derives from the catalog, never a copy. */
290
+ /**
291
+ * Human-readable rendering. Order derives from the catalog, never a copy.
292
+ * @param {{ repo: string, files: number, hits: Hit[] }} report
293
+ * @returns {string}
294
+ */
266
295
  export function render(report) {
267
296
  const L = []
268
297
  L.push(`# scan-0.1.6 · ${report.repo}`)
@@ -286,7 +315,13 @@ export function render(report) {
286
315
  return L.join('\n')
287
316
  }
288
317
 
318
+ /**
319
+ * CLI entry point.
320
+ * @param {string[]} argv
321
+ * @returns {number} the process exit code.
322
+ */
289
323
  export function main(argv) {
324
+ /** @type {{ repo: string, json: string | null, seams: string[] | null, quiet: boolean }} */
290
325
  const args = { repo: process.cwd(), json: null, seams: null, quiet: false }
291
326
  for (let i = 0; i < argv.length; i++) {
292
327
  const a = argv[i]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-plugin-upgrade",
3
- "version": "2.0.0",
4
- "description": "Plugin-author upgrade skill for DeepSeek Harness: the merged version-locked 0.1.3-alpha.1 -> 0.1.5-rc.1 corridor card plus a zero-dependency seam scanner over one 20-seam catalog leg A (V3 session format, assistant/message.stream, SessionHandleReadResult, ctx.agent, Inbox, SubprocessHandle.pid, SystemPrompt persona, PTC rename, EpochHeader.system, and the tsconfig stale-path false green) and leg B (the removed bare `conversation` client slot, the sidebar textpreview -> documentpreview package rename, the stale-type-line false green, the new global main-panel model and usePanelInfo standard prop, the new `present` tool key, two new session event types, and the peer-range trap where >=0.1.2-rc.1 <0.2.0 alone rejects 0.1.5-rc.1) packaged as a bundle skill and an npx CLI.",
3
+ "version": "2.0.2",
4
+ "description": "Plugin-author upgrade skill for DeepSeek Harness: one package, one corridor index - the scanner detects the caller peer band and routes to the matching closed corridor card (0.1.3-alpha.1 -> 0.1.5-rc.1 as legs A+B, and 0.1.5-rc.2 -> 0.1.6-alpha.2 as leg C), with a zero-dependency seam scanner shipped as a bundle skill and an npx CLI.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/PerryLink/dsh-plugin-upgrade.git"
@@ -77,14 +77,16 @@
77
77
  "compatibility": {
78
78
  "dshVersions": [
79
79
  "0.1.2-rc.1",
80
- "0.1.5-rc.2"
80
+ "0.1.5-rc.2",
81
+ "0.1.6-alpha.2",
82
+ "0.1.7-alpha.2"
81
83
  ]
82
84
  },
83
85
  "capability": {
84
86
  "id": "plugin-upgrade",
85
87
  "kind": "service",
86
88
  "invocation": "ctx.skills.get('plugin-upgrade') resolves a complete SkillDefinition",
87
- "expected": "the session skill catalog lists plugin-upgrade and the loaded body carries the merged 0.1.3-alpha.1 -> 0.1.5-rc.1 corridor card with a directory resourceBase"
89
+ "expected": "the session skill catalog lists plugin-upgrade and the loaded body carries the corridor index (lib/route.mjs) with both closed corridor cards - legAB 0.1.3-alpha.1 -> 0.1.5-rc.1 and legC 0.1.5-rc.2 -> 0.1.6-alpha.2 - over a directory resourceBase"
88
90
  },
89
91
  "evidence": {
90
92
  "install": null,
@@ -112,13 +114,13 @@
112
114
  "packageManager": "pnpm@11.7.0",
113
115
  "peerDependencies": {
114
116
  "@deepseek-ai/cordis": "^4.0.2",
115
- "@deepseek-ai/dsh-skill": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0",
117
+ "@deepseek-ai/dsh-skill": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0",
116
118
  "@deepseek-ai/schemastery": "^3.18.2"
117
119
  },
118
120
  "devDependencies": {
119
- "@deepseek-ai/cordis": "^4.0.2",
120
- "@deepseek-ai/dsh-skill": "0.1.5-rc.2",
121
- "@deepseek-ai/schemastery": "^3.18.2",
121
+ "@deepseek-ai/cordis": "^4.0.4",
122
+ "@deepseek-ai/dsh-skill": "0.1.7-alpha.2",
123
+ "@deepseek-ai/schemastery": "^3.18.4",
122
124
  "@types/node": "^22.0.0",
123
125
  "typescript": "^5.9.0"
124
126
  },
@@ -126,6 +128,7 @@
126
128
  "test": "node --test \"test/*.test.mjs\"",
127
129
  "check": "tsc -p tsconfig.check.json",
128
130
  "typecheck:ci": "tsc -p tsconfig.check.ci.json",
131
+ "typecheck:checkout": "tsc -p tsconfig.checkout.json --noEmit",
129
132
  "scan": "node scripts/scan-0.1.5.mjs",
130
133
  "check:readmes": "node scripts/check-readme-sync.mjs",
131
134
  "verify:self-contained": "node scripts/verify-self-contained.mjs",
@@ -61,10 +61,11 @@ try {
61
61
  failures.push(`packaged entry failed to import: ${detail.slice(0, 200)}`)
62
62
  }
63
63
 
64
- // The packaged SKILL.md must keep its merged-corridor frontmatter.
64
+ // The packaged SKILL.md must keep its corridor-index frontmatter (both corridors).
65
65
  const skill = readFileSync(join(pkgRoot, 'skills/plugin-upgrade/SKILL.md'), 'utf8')
66
66
  if (!/^name:\s*plugin-upgrade\s*$/m.test(skill)) failures.push('packaged SKILL.md lost its frontmatter name')
67
- if (!/^ corridor:\s*"0\.1\.3-alpha\.1 -> 0\.1\.5-rc\.1"\s*$/m.test(skill)) failures.push('packaged SKILL.md lost its merged corridor frontmatter')
67
+ if (!/^ corridors:\s*"legAB `0\.1\.3-alpha\.1 -> 0\.1\.5-rc\.1`/m.test(skill)) failures.push('packaged SKILL.md lost its legAB corridor frontmatter')
68
+ if (!/legC `0\.1\.5-rc\.2 -> 0\.1\.6-alpha\.2`/m.test(skill)) failures.push('packaged SKILL.md lost its legC corridor frontmatter')
68
69
 
69
70
  // cordis.patch.yml must stay a top-level YAML ARRAY of loader patch entries:
70
71
  // a mapping (`insert:` at column 0) mounts nothing and dsh reports
@@ -1,18 +1,17 @@
1
1
  ---
2
2
  name: plugin-upgrade
3
- description: Migrate a DeepSeek Harness plugin repo across the merged 0.1.3-alpha.1 -> 0.1.5-rc.1 corridor. Runs a zero-dependency seam scanner over one 20-seam catalog leg A (V3 session format, assistant/message.stream, SessionHandleReadResult, ctx.agent, Inbox, SubprocessHandle.pid, SystemPrompt persona, PTC rename, EpochHeader.system, and the tsconfig stale-path false green) plus leg B (the removed bare `conversation` client slot, the sidebar textpreview -> documentpreview package rename, the stale-type-line false green, the new global main-panel model and usePanelInfo standard prop, the new `present` tool key, two new session event types, and the peer-range trap) then walks the fix-and-verify loop with a real-host smoke, a resume round-trip for log writers and a real browser assertion for a client half.
4
- whenToUse: Use when a DSH plugin must reach the DeepSeek Harness 0.1.5-rc.1 line (or any host line in >=0.1.2-rc.1 <0.2.0) while keeping the older peer band working. Read leg A first if the peer band is below 0.1.5-alpha.1 or the target is 0.1.5-alpha.1; read leg B if the target is 0.1.5-rc.1 or the plugin has a client/browser half. Not for 0.1.1 -> 0.1.2 migrations (use the community convergence skill), not for a hop after 0.1.5-rc.1 (a new corridor is a new package) and not for the DSH user-facing upgrade/repair path.
3
+ description: Migrate a DeepSeek Harness plugin repo across one of the two closed corridors this package carries. A zero-dependency seam scanner reads the target repo's declared band and routes to the matching corridor, then you walk the fix-and-verify loop. Corridor `legAB` (`0.1.3-alpha.1 -> 0.1.5-rc.1`, 20 seams) holds leg A (V3 session format, assistant/message.stream, SessionHandleReadResult, ctx.agent, Inbox, SubprocessHandle.pid, SystemPrompt persona, PTC rename, EpochHeader.system, and the tsconfig stale-path false green) plus leg B (the removed bare `conversation` client slot, the sidebar textpreview -> documentpreview package rename, the stale-type-line false green, the new global main-panel model and usePanelInfo standard prop, the new `present` tool key, two new session event types, and the peer-range trap). Corridor `legC` (`0.1.5-rc.2 -> 0.1.6-alpha.2`, 5 seams `E1`-`E5`) holds the `agent/created` listener-throw chain, the post-`await` apply race, the removed `settings.plugin.item` slot and `SessionListState.current`, the removed `sessions.open/openSubagent/clear` client API, and the removed model literals.
4
+ whenToUse: Use when a DSH plugin must reach the DeepSeek Harness 0.1.5-rc.1 line (or any host line in >=0.1.2-rc.1 <0.2.0), or the 0.1.6-alpha.2 line, while keeping the older peer band working. Read leg A if the peer band is below 0.1.5-alpha.1 or the target is 0.1.5-alpha.1; read leg B if the target is 0.1.5-rc.1 or the plugin has a client/browser half; read leg C if the target band is 0.1.6-alpha.2 (or the repo already sits on 0.1.5-rc.2). Not for 0.1.1 -> 0.1.2 migrations (use the community convergence skill) and not for the DSH user-facing upgrade/repair path. A hop past every corridor carried here is a new corridor, not a wider card.
5
5
  metadata:
6
- corridor: "0.1.3-alpha.1 -> 0.1.5-rc.1"
7
- legs: "leg A 0.1.3-alpha.1 -> 0.1.5-alpha.1 (S1-S10, M1) · leg B 0.1.5-alpha.1 -> 0.1.5-rc.1 (C1, C2, C4, C5, H1-H4, P1); leg B's C3 is leg A's M1"
8
- host-baseline: "0.1.5-rc.1 (tag dsh-v0.1.5-rc.1 = 183f08e9c6dde7e36cd2318eaee70b0da08fb35e); leg A baseline 0.1.5-alpha.1 (tag dsh-v0.1.5-alpha.1 = 5dda764ed3aa172535a7967b06ff95d9cbfe536a, checkout 19d2e38480)"
9
- evidence: "leg A: 2026-09-09 wave over 40 plugin repos · leg B: tag-range diff + public slot/service catalogs re-read 2026-09-10; family workspace sweep (15 repos with a client half, 8 slot keys). Both recorded in docs/EVIDENCE.md"
6
+ corridors: "legAB `0.1.3-alpha.1 -> 0.1.5-rc.1` (leg A S1-S10 + M1 · leg B C1, C2, C4, C5, H1-H4, P1; leg B's C3 is leg A's M1) · legC `0.1.5-rc.2 -> 0.1.6-alpha.2` (E1-E5, all error)"
7
+ host-baseline: "0.1.5-rc.1 (tag dsh-v0.1.5-rc.1 = 183f08e9c6dde7e36cd2318eaee70b0da08fb35e) for legAB; 0.1.6-alpha.2 (tag dsh-v0.1.6-alpha.2) for legC. leg A baseline 0.1.5-alpha.1 (tag dsh-v0.1.5-alpha.1 = 5dda764ed3aa172535a7967b06ff95d9cbfe536a, checkout 19d2e38480)"
8
+ evidence: "leg A: 2026-09-09 wave over 40 plugin repos · leg B: tag-range diff + public slot/service catalogs re-read 2026-09-10; family workspace sweep (15 repos with a client half, 8 slot keys) · leg C: 2026-09-19 sweep, dsh-v0.1.6-alpha.2. All recorded in docs/EVIDENCE.md (sections A, 1-10 and 11)"
10
9
  status: "published"
11
- supersedes: "the version-locked packages dsh-plugin-upgrade (leg A) and dsh-plugin-upgrade-rc1 (leg B)"
10
+ supersedes: "the retired version-locked packages dsh-plugin-upgrade (leg A) and dsh-plugin-upgrade-rc1 (leg B), plus the never-published dsh-plugin-upgrade-016 corridor now folded in as leg C; the two published retired names stay on the registry, deprecated"
12
11
  user-invocable: true
13
12
  ---
14
13
 
15
- # Plugin upgrade · 0.1.3-alpha.1 → 0.1.5-rc.1 (one package, two legs)
14
+ # Plugin upgrade · one package, one corridor index (two closed corridors)
16
15
 
17
16
  You are migrating **one plugin repository** across the merged DSH span `0.1.3-alpha.1 → 0.1.5-rc.1`. The goal is not "make typecheck pass" — it is "prove the plugin still works on the target host". Local gates are necessary but not sufficient, and two classes of failure survive a green gate: **stale-type false green** (the local gate compiles an old type line) and **silent non-mount** (the host drops a contribution with no error, no log line and no failed build). Read the leg that matches your peer band; both legs share one scanner and one catalog.
18
17
 
@@ -82,7 +81,8 @@ Both legs' frontmatter `whenToUse` were routing hints; they cannot both live in
82
81
 
83
82
  ## 5. Reference
84
83
 
85
- - `./references/v0.1.3-alpha.1-to-v0.1.5-rc.1.md` — the merged version card: preamble, **§1 Leg A** (10 seams with host path + commit + minimal fix + regression, and the 4 that no community PR covers yet), **§2 Leg B** (the from→to mapping, the `conversation` → `main.conversation` rewrite recipe, the boundary list), **§3 the 20-seam merged index**.
86
- - `./scripts/scan-0.1.5.mjs` — the detector (zero dependency, `file:line`, exit 1 on error-severity hits, `--seams` to filter). It is a thin wrapper over the package's own `lib/scan.mjs`, shipped inside the skill directory so relative paths resolve.
87
- - The package's own test suite (`node --test`) synthetic bad/good fixtures **for both legs** (`fixtures/leg-a-*` for leg A, `fixtures/bad-repo` / `fixtures/good-repo` for leg B), a card↔catalog id-parity gate, and a live negative on a family repo.
88
- - `docs/EVIDENCE.md` — the command→output record behind every card claim (§A is leg A's provenance, §1–§10 the leg-B records).
84
+ - `./references/v0.1.3-alpha.1-to-v0.1.5-rc.1.md` — the `legAB` version card: preamble, **§1 Leg A** (10 seams with host path + commit + minimal fix + regression, and the 4 that no community PR covers yet), **§2 Leg B** (the from→to mapping, the `conversation` → `main.conversation` rewrite recipe, the boundary list), **§3 the 20-seam merged index**.
85
+ - `./references/v0.1.5-rc.2-to-v0.1.6-alpha.2.md` — the `legC` version card: what breaks across `0.1.5-rc.2 0.1.6-alpha.2` (all silent or runtime-only), the five-seam index `E1`–`E5` with evidence and fix/verify recipes, and the family-side facts. `CARD_ONLY = []` there: every `legC` seam has a detector.
86
+ - `./scripts/scan-0.1.5.mjs` the router plus detector: it resolves the corridor through `lib/route.mjs` (declared band, or `--span legAB|legC|span`) and hands the same argv to that corridor's own catalog `main()` `lib/scan.mjs` for `legAB`, `lib/scan-0.1.6.mjs` for `legC`. Zero dependency, `file:line` output, exit `1` on error-severity hits, `--seams` to filter. The file name is historical; it ships inside the skill directory so relative paths resolve.
87
+ - The package's own test suite (`node --test`)synthetic bad/good fixtures **for both `legAB` legs** (`fixtures/leg-a-*` for leg A, `fixtures/bad-repo` / `fixtures/good-repo` for leg B), a card↔catalog id-and-severity parity gate **for each corridor**, and a live negative on a family repo.
88
+ - `docs/EVIDENCE.md` — the command→output record behind every card claim: §A is leg A's provenance, §1–§10 the leg-B records, §11 the `legC` records.
@@ -1,7 +1,7 @@
1
1
  # 版本卡 · `0.1.5-rc.2` → `0.1.6-alpha.2`
2
2
 
3
- > 本卡只对 **`0.1.5-rc.2 → 0.1.6-alpha.2`** 这一个封闭走廊负责。上一个走廊(`0.1.3-alpha.1 0.1.5-rc.1`)由 `dsh-plugin-upgrade-015` 承担;本走廊是一个**新包**(`dsh-plugin-upgrade-016`),五条接缝与旧目录零交集,两包不共享任何 SEAMS 清单。
4
- > 走廊方法论不变:走廊永不加宽,一个 hop 加一条接缝就是一个新包。
3
+ > 本卡只对 **`0.1.5-rc.2 → 0.1.6-alpha.2`** 这一个封闭走廊负责。走廊方法论不变:**走廊永不加宽**——一条走廊一张卡、一套证据、一套 fixture 与一个回滚点;绝不把卡撑成「所有版本」。
4
+ > **归属(2026-09-19 业主决定,已取代旧规则)**:本走廊与上一个走廊(`0.1.3-alpha.1 → 0.1.5-rc.1`,`legAB`)现在由**同一个包** `dsh-plugin-upgrade` 通过走廊索引(`lib/route.mjs`)承担,本卡是它的第二条索引行(`legC`)。旧措辞「一个 hop 加一条接缝就是一个新包」与 `dsh-plugin-upgrade-016` 这个名字都已作废,`-016` 从未发布到 registry。两条走廊**不共享 SEAMS 清单**——本卡五条接缝与旧目录零交集,各自有独立的目录模块与 parity 门禁。
5
5
 
6
6
  ## 1. 这个跨度里什么会坏(全部是**静默**或**运行时**失效)
7
7