dsh-molbio-tools 0.22.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +51 -19
  3. package/benchmark/README.md +7 -7
  4. package/benchmark/fixtures.mjs +32 -4
  5. package/benchmark/run.mjs +1 -0
  6. package/benchmark/sequences.mjs +1 -0
  7. package/benchmark/tasks/02-primers.json +3 -3
  8. package/benchmark/tasks/06-analysis.json +111 -1
  9. package/benchmark/tasks/09-formats.json +44 -1
  10. package/benchmark/verifications.mjs +13 -0
  11. package/build/client-entry.mjs +80 -1
  12. package/build/panel-core.mjs +85 -0
  13. package/build/release-preflight.mjs +1 -1
  14. package/contamination.mjs +229 -0
  15. package/cordis.patch.yml +9 -5
  16. package/design.mjs +111 -78
  17. package/docs/capability-gap-survey.md +5 -0
  18. package/docs/client-panel.md +9 -7
  19. package/docs/history.md +62 -0
  20. package/docs/maintainer.md +8 -5
  21. package/docs/roadmap.md +33 -15
  22. package/docs/rules.md +5 -2
  23. package/docs/store-evidence.md +30 -23
  24. package/docs/store-preset-exemption-issue.md +1 -1
  25. package/docs/store-preset-exemption.md +1 -1
  26. package/docs/v24-plan.md +178 -0
  27. package/docs/workflow.md +11 -10
  28. package/dotplot.mjs +257 -0
  29. package/fastq-qc.mjs +6 -9
  30. package/fastq-trim.mjs +238 -0
  31. package/index.mjs +470 -24
  32. package/lib/client.js +327 -93
  33. package/package.json +8 -3
  34. package/preset/molbio-lab/agent.cordis.yml +19 -13
  35. package/preset/molbio-lab/preset.yml +1 -1
  36. package/protein-charge.mjs +158 -0
  37. package/protein.mjs +36 -14
  38. package/seqio.mjs +10 -0
  39. package/test/client.mjs +12 -5
  40. package/test/contract.mjs +44 -12
  41. package/test/drift-probe.mjs +15 -1
  42. package/test/map-card.mjs +139 -1
  43. package/test/preset-health.mjs +39 -0
  44. package/test/smoke.mjs +288 -11
package/docs/roadmap.md CHANGED
@@ -10,12 +10,12 @@ L = 需要新机制或外部资源)。
10
10
 
11
11
  ---
12
12
 
13
- ## 1. 当前状态(0.22.2)
13
+ ## 1. 当前状态(0.23.0)
14
14
 
15
- - 60 个 `molbio_*` 工具,零依赖,随 preset 分发(见 [README](../README.md))。
15
+ - 64 个 `molbio_*` 工具,零依赖,随 preset 分发(见 [README](../README.md))。
16
16
  - 离线测试金字塔 + 组合漂移守卫全绿;`benchmark/` 提供**模型使用**维度的评估
17
17
  (见 [benchmark/README.md](../benchmark/README.md))。
18
- - 基线:DSH **0.2.1-rc.5**;DSH 桌面版内嵌 Node 24.21.0,`engines.node` 与 CI 矩阵都按这个下限。
18
+ - 基线:DSH **0.2.2-rc.1**;DSH 桌面版内嵌 Node 24.21.0,`engines.node` 与 CI 矩阵都按这个下限。
19
19
  - **CI 已在位**(`.github/workflows/test.yml`,0.18.2 起):push / PR / 手动都跑全套;harness 装
20
20
  **钉死版本**,并用 `npm prefix -g` 显式导出 `DSH_HARNESS_ROOT`。0.18.4 起**同时跑
21
21
  `ubuntu-latest` 与 `windows-latest`**——3.6 已修,套件不再依赖宿主分隔符。
@@ -45,6 +45,19 @@ L = 需要新机制或外部资源)。
45
45
  的 key 解析顺序变为 **`api_key` 参数 → DSH 凭据库 → 环境变量**(环境变量会被 agent 自己的 shell
46
46
  子进程继承,凭据库不会);同时插件详情页出现 **PubMed API key** 表单,用户不必改 YAML。
47
47
  没有凭据库的组合行为与 0.22.0 相同。
48
+ - **0.24.0 是数据卫生版**:新增 survey #11 载体/接头污染筛查与 #12 FASTQ 修剪/过滤;
49
+ 同时修掉 FASTQ QC 接头表里 Small RNA 5' 的 RNA→DNA 归一化静默错(旧表在 DNA reads 上
50
+ 只能命中反向互补)。工具 62 → 64,benchmark 63 → 65;客户端产物仅因
51
+ `seqio.mjs` 新增 `toFastq` 重建,面板行为未变(面板包 0.1.8 → 0.1.9)。
52
+ - **0.23.0 是工具行为版**:修掉 2.0 的遗留(跨内含子引物现在报告的真实反向分子、F 在 R 上游),
53
+ 落地 2.9a(sequence logo / gRNA 的调用卡),并从候选池新增两个轻量工具:
54
+ `molbio_protein_charge`(survey #34,电荷-pH 滴定 + 可切换 pK 表)与
55
+ `molbio_dotplot`(survey #23,k-mer 直接/反向重复)。工具 60 → 62,benchmark 61 → 63 题。
56
+ - **0.23.0 同版完成 0.2.2-rc.1 适配(工具行为零变化)**:基线 rc.5 → 0.2.2-rc.1。上游把
57
+ `list_agents` 并入 `dsh-tool-subagent-control` 并删除 `./list-agents` 子路径,preset 删掉该行
58
+ (30 → 29)、`tool-subagent` 改用 `modelSelection`/`modelDiscovery`、`tool-fs` 补
59
+ `applyPatch: false`;`preset-health` 增加真实 specifier 解析、`contract` 三处断言改回契约。
60
+ 两份 `dshReleases` 增加 `0.2.2-rc.1: compatible`,CI 随基线钉到该版本。
48
61
  - **0.22.2 是 rc.5 纯适配版(工具行为零变化)**:基线 rc.3 → rc.5。上游 `standard` 行清单
49
62
  **逐字节未变**;真正的变化在**打包形态**——桌面版 `app.asar` 裁掉 `lib/types`,于是
50
63
  `contract` 的声明守卫改为"npm 包读 `.d.ts`,桌面版读 `typert.host.js` / runtime 内嵌副本",
@@ -64,13 +77,17 @@ L = 需要新机制或外部资源)。
64
77
  `forward`/`reverse` 曾**标反**(F 在下游、R 在上游),按原样送进 `molbio_pcr_simulate`
65
78
  得到 **0 个产物**。修法与"为什么只有串联测试看得见"见 [CHANGELOG 0.16.0](../CHANGELOG.md)。
66
79
 
67
- **遗留的一条同源问题(未改,故意)**:`molbio_design_intron_primers` 用的是**另一个约定**——
68
- 它把两条引物都报成**剪接后转录本的 sense 子串**(这样 `exon` / `junction_left` /
69
- `junction_right` 几何、剪接 vs 基因组双坐标错配报告、3' 尾错配检查才能统一读在**一条**序列上)。
70
- 代价是:它报的 `reverse` **不是**要订购的那条分子,真正的反向引物是它的反向互补,
71
- 而且 `forward` 在 `reverse` **下游**。这是**可用性瑕疵、不是沉默的错**
72
- (没有任何工具把这个对送进模拟器),所以没有跟 `designPrimerPairs` 在同一版里一起改。
73
- 要动它就得同时改渲染、错配报告与 `test/smoke.mjs` 的 sense-substring 断言。
80
+ ### 2.0b ✅ 已修(0.23.0)跨内含子引物的朝向
81
+
82
+ `molbio_design_intron_primers` 曾把两条引物都报成剪接后转录本的 sense 子串:报告的 `reverse`
83
+ 不是要订购的分子(真正的反向引物是它的反向互补),而且 `forward` 在 `reverse` 下游。
84
+ 0.23.0 起反向候选在**剪接转录本的反向互补**上扫描,因此 3' 端约束(GC clamp、末端稳定性、
85
+ 末端 GC、self-end、发夹)落在**真正会订购的那条分子**上;命中再经 `anchor` 映回 sense 坐标。
86
+ 报告的 `reverse.sequence` 是可直接下单的 5'→3' 分子,F 在 R 上游,剪接扩增子从 F 的 5' 端到
87
+ R 的 5' 端。`exon` 只出现在正向引物上,`exons`/`junction_*` 只出现在反向引物上;错配的
88
+ `template_base` 对反向引物是转录本碱基的**互补**。这次同时改了渲染、错配报告、
89
+ `test/smoke.mjs` 的断言与 benchmark `intron-qpcr` 的措辞;物理扩增子
90
+ (320-406 spliced / 387 bp genomic)保持不变。
74
91
 
75
92
  ### 2.1 ✅ 已修(0.16.0)`molbio_methylation_check` 的表与 NEB 不一致
76
93
 
@@ -145,8 +162,9 @@ v16 的 CRISPR 已有订购 CSV 先例,可直接复用格式。
145
162
 
146
163
  ### 2.9 浏览器面板的候选(客户端半,不动 preset 目录)
147
164
 
148
- - 给 `molbio_sequence_logo` / `molbio_grna_design` 等工具加**调用卡**(同一套
149
- `presentationMeta` + 卡片模式,上线前先跑 `test/client.mjs` 的抢座位顺序那一层);
165
+ - ✅ **已做(0.23.0)**:`molbio_sequence_logo` 与 `molbio_grna_design` 有调用卡
166
+ (`molbio-figure` 投影 + `FIGURE_TOOL_KEYS`;logo 画图,gRNA 画图谱并列出前 5 条 guide)。
167
+ `test/map-card.mjs` 现在同时跑两种卡片,`contract` 从产物里的两张 key 表反推投影数。
150
168
  - 文献库**写回**需先定并发契约(当前只读);
151
169
  - 结构文件的浏览器内预览(`.pdb/.cif/.sdf/.mol`):`ctx.documentPreviews.register` +
152
170
  keyed 座位是**容器**,不是现成的 3D 查看器——要么只做 2D 投影(Cα 轨迹/二级结构条带),
@@ -190,7 +208,7 @@ v18 想做"工具写一个工作区 PNG 文件"而**不能**:`dsh-fs` 明文
190
208
 
191
209
  ### 3.2 benchmark 的覆盖面
192
210
 
193
- - 61 条任务覆盖 **60/60 个工具**(0.15.0 起由 `test/benchmark-coverage.mjs` 机器断言;
211
+ - 65 条任务覆盖 **64/64 个工具**(0.15.0 起由 `test/benchmark-coverage.mjs` 机器断言;
194
212
  core 档 14 题)。**但多数工具只出现在一条任务里,覆盖是浅的**——见
195
213
  [benchmark/README.md](../benchmark/README.md) 的"What this benchmark does NOT measure"。
196
214
  - 没有断言 **`attach_image` 交付的图片内容**(只当参数用),也不加载客户端产物。
@@ -262,7 +280,7 @@ Linux runner 上这些断言会因**分隔符**而失败——那是在报告宿
262
280
  缺陷。`test/client-mount.mjs` 还有一处同类问题(写死 `%APPDATA%\npm`),**0.18.2 已修**,那也
263
281
  正是 workflow 第一次运行全红的原因。
264
282
 
265
- **改法(已设计,未做)**:在每个受影响的测试文件顶部加三个助手,把"输入路径"和"期望值"都从
283
+ **改法(已在 0.18.4 落地,此处保留施工记录)**:在每个受影响的测试文件顶部加三个助手,把"输入路径"和"期望值"都从
266
284
  同一个根推导出来:
267
285
 
268
286
  ```js
@@ -290,7 +308,7 @@ Node 做路径运算(Windows 是 `<prefix>/node_modules`,POSIX 是 `<prefix>
290
308
  - **agent 驱动浏览器终端**:无 `dsh-tool-terminal`,且面板明确不把输出转给模型。
291
309
  `ctx.computerUse`/`ctx.browserUse` 这类 seam 只允许**一个** provider 注册,
292
310
  第三方插件即便在未来版本也不能自带一个并行实现去抢。
293
- - **把 57 个工具做成全局可见**:bundle 安装 ≠ 工具全局可见是**设计如此**(工具多了会占
311
+ - **把 64 个工具做成全局可见**:bundle 安装 ≠ 工具全局可见是**设计如此**(工具多了会占
294
312
  提示预算)。真要全局可见需要自己加一层宿主行,而那会让专属模式里的同一行变成死行。
295
313
  - **持久 shell 进 preset**:收益有限、风险明确(工具名冲突要 disable 一次性那行,
296
314
  且需要未经验证的 `isolate: { terminals: true }` 分组),详见
package/docs/rules.md CHANGED
@@ -45,7 +45,10 @@ curl.exe -s -b jar -X POST http://127.0.0.1:3099/api/agentPresets/list `
45
45
  只发 `{}` 只会得到 `arguments-invalid`。验证完删掉 scratch profile。
46
46
 
47
47
  `test/preset-health.mjs` 现在直接盯着这条规则:组合里出现任何 `./plugins/…` 形式的
48
- specifier 就 FAIL,并已用突变实验证明它会失败。
48
+ specifier 就 FAIL,并已用突变实验证明它会失败。同一套守卫还按 **Loader 的方式解析每个行名**
49
+ (`createRequire(...).resolve`,受包 `exports` 映射约束):0.2.2-rc.1 删掉
50
+ `dsh-tool-subagent-control/list-agents` 子路径时,只查"包目录存在"的旧检查是绿的,
51
+ 而那一行在预设挂载时根本 import 不了。
49
52
 
50
53
  ---
51
54
 
@@ -147,7 +150,7 @@ ctx.effect(() => ctx.slots.inject(座位, () => ctx.slots.register({name: 座位
147
150
 
148
151
  这条规则有两道**机器守卫**,它们让"忘了"变成"红":
149
152
 
150
- - `test/benchmark-coverage.mjs` 断言 **60/60 个已注册工具都被至少一个任务覆盖**。
153
+ - `test/benchmark-coverage.mjs` 断言 **64/64 个已注册工具都被至少一个任务覆盖**。
151
154
  新增工具而没加任务 = **直接 FAIL**。它同时守住 fixture 的前提(Golden Gate 的载体必须
152
155
  无 BsaI 位点、诱变模板必须仍能设计出引物)与 tier/占位符的健全性。
153
156
  - `test/benchmark-score.mjs` 用**真实模型响应的冻结副本**(`test/fixtures/benchmark-traces.json`)
@@ -20,7 +20,8 @@
20
20
  "0.2.1-rc.2": "compatible",
21
21
  "0.2.1-rc.3": "compatible",
22
22
  "0.2.1-rc.4": "unknown",
23
- "0.2.1-rc.5": "compatible"
23
+ "0.2.1-rc.5": "compatible",
24
+ "0.2.2-rc.1": "compatible"
24
25
  }
25
26
  }
26
27
  }
@@ -31,7 +32,7 @@
31
32
  `@deepseek-ai/dsh-app-boot` 的 `unmetRequirements`):带 prerelease 选项时 `<0.3.0`
32
33
  会放行 `0.3.0-alpha.1`,而 `-0` 不会。
33
34
 
34
- ### 为什么 rc.2 / rc.3 / rc.5 是 compatible、rc.1 / rc.4 是 unknown
35
+ ### 为什么 rc.2 / rc.3 / rc.5 / 0.2.2-rc.1 是 compatible、rc.1 / rc.4 是 unknown
35
36
 
36
37
  | 版本 | 记录 | 依据 |
37
38
  | --- | --- | --- |
@@ -39,15 +40,16 @@
39
40
  | 0.2.1-rc.2 | compatible | 0.21.0 / 0.21.1 的发布基线;本文件第 3 节的一次性 Profile 流程在 rc.2 上跑过;`contract` / `preset-health` / `smoke` 均通过 |
40
41
  | 0.2.1-rc.3 | compatible | 0.22.0 的发布基线;同一套一次性 Profile 流程在 rc.3 上重跑通过(安装 → 启动 HTTP 200 → 卸载无残留);`contract` / `preset-health` / `drift-probe` / `benchmark-profile` / `smoke` 在 rc.3 上全绿。上游 `standard` 行清单与 rc.2 **逐字节一致** |
41
42
  | 0.2.1-rc.4 | unknown | 没有在真实安装上跑过;不能因为"补丁号在 `engines.dsh` 范围内"就写成 compatible |
42
- | 0.2.1-rc.5 | compatible | 0.22.2 的发布基线(CI 钉该版本)。在本机 DSH 桌面版 0.2.1-rc.5 上重跑:安装 → 启动 HTTP 200 → `agentPresets/list` 的 `broken` 为空且 molbio-lab 的 `tool-molbio` 为 `fiberPhase: "active"` → 卸载无残留;`contract` 17 / `preset-health` 30 行 / `drift-probe` 16 / `smoke` 60 工具 / `bench:offline` 61 题全绿。上游 `standard` 与 rc.3 **逐字节一致**(6799 B,同一 sha256) |
43
+ | 0.2.1-rc.5 | compatible | 0.22.2 的发布基线(CI 曾钉该版本)。在本机 DSH 桌面版 0.2.1-rc.5 上重跑:安装 → 启动 HTTP 200 → `agentPresets/list` 的 `broken` 为空且 molbio-lab 的 `tool-molbio` 为 `fiberPhase: "active"` → 卸载无残留;`contract` 17 / `preset-health` 30 行 / `drift-probe` 16 / `smoke` 60 工具 / `bench:offline` 61 题全绿。上游 `standard` 与 rc.3 **逐字节一致**(6799 B,同一 sha256) |
44
+ | 0.2.2-rc.1 | compatible | 0.23.0 的发布基线(CI 钉该版本)。这一版上游删除了 `@deepseek-ai/dsh-tool-subagent-control/list-agents` 导出(`list_agents` 并入主入口),preset 因此删掉该行(30 → 29),`tool-subagent` 改用 `modelSelection`/`modelDiscovery`、`tool-fs` 补 `applyPatch: false`。在**桌面版 `app.asar` 与 npm 全局安装**两份 0.2.2-rc.1 上核对(两者 `standard.patch.yml` **逐字节一致**,6858 B,sha256 `3006CC3780CDDAA7FB0089E5854E6BBEF4409CF1D425930FD835226B95660AFD`);一次性 tarball Profile:安装 → 启动 HTTP 200(40 311 B,boot 图含 `dsh-molbio-tools/client.js`)→ `agentPresets/list` 的 `broken` 为空、`tool-molbio` 为 `fiberPhase: "active"` → 卸载无残留;`contract` 17 / `preset-health` 29 行 / `drift-probe` 16 项(15 个突变)/ `smoke` 62 工具 / `bench:offline` 63 题全绿;定向真实模型跑分 `intron-qpcr` / `protein-charge` / `dotplot` 3 题 3/3(2026-11-05,541,946 tokens) |
43
45
 
44
46
  ## 2. 环境
45
47
 
46
48
  | 项 | 值 |
47
49
  | --- | --- |
48
- | DSH | `0.2.1-rc.5`(桌面版 `app.asar`,同一份代码也是 CI 的 npm 基线) |
49
- | Node.js | `v24.21.0`(DSH 桌面版内嵌的 Node,Electron 44.7.0) |
50
- | pnpm | `11.28.5`(DSH bundle 内) |
50
+ | DSH | `0.2.2-rc.1`(桌面版 `app.asar` 与 npm 全局安装各一份,`standard.patch.yml` 逐字节一致) |
51
+ | Node.js | `v26.12.1`(命令行验证;桌面运行时内嵌 `24.21.0`,Electron 44.7.0) |
52
+ | pnpm | `12.15.0`(`dsh plugin` 安装用) |
51
53
  | 平台 | Windows(win32 x64) |
52
54
 
53
55
  ## 3. 一次性 Profile 的完整流程
@@ -56,9 +58,9 @@
56
58
 
57
59
  ```bash
58
60
  export DSH_HOME=<workspace>/.tmp/dsh-store-check
59
- npm pack --pack-destination "$DSH_HOME" # 得到 dsh-molbio-tools-0.22.2.tgz
61
+ npm pack --pack-destination "$DSH_HOME" # 得到 dsh-molbio-tools-0.23.0.tgz
60
62
  dsh --profile molbio-tgz-check --from-default-profile web --dump-config
61
- dsh plugin --profile molbio-tgz-check add "$DSH_HOME/dsh-molbio-tools-0.22.2.tgz"
63
+ dsh plugin --profile molbio-tgz-check add "$DSH_HOME/dsh-molbio-tools-0.23.0.tgz"
62
64
  dsh --profile molbio-tgz-check --dump-config # 安装后的组合树
63
65
  dsh --profile molbio-tgz-check --no-open --port 0 # 启动,打印 URL
64
66
  curl "http://127.0.0.1:<port>/?token=<token>" # 读回页面
@@ -69,20 +71,20 @@ dsh --profile molbio-tgz-check --dump-config # 卸载后的组合树
69
71
  安装用 **tarball** 而不是目录,因为 tarball 就是发布物本身:它能同时证明 `files`
70
72
  白名单带全了运行文件(`host.mjs`、`lib/client.js`、单个 `cordis.patch.yml` 的两个 insert、preset)。
71
73
 
72
- tarball 是 **115 个文件 / 约 1.02 MB**(`npm pack` 实测约 1,024,000 B,解包约 3.3 MB;再改文档会让字节数
74
+ tarball 是 **117 个文件 / 1 049 323 B**(`npm pack` 实测,解包约 3.4 MB;再改文档会让字节数
73
75
  小幅浮动,文件数不变),且 `package/packages/**` 为 **0**:根包不再内嵌
74
76
  `packages/molbio-panel`,一个 tarball 里只有一个插件清单(面板包按自己的地址单独发布)。
75
77
  面板本身没有因此消失——根包的 `lib/client.js` 就带着它,第 3.2 节的回读可以作证。
76
78
 
77
- 下面这次是在 **0.22.2 的 tarball** 上、用 **dsh 0.2.1-rc.5** 的 CLI 重跑的(2026-10-30),
79
+ 下面这次是在 **0.23.0 的 tarball** 上、用 **dsh 0.2.2-rc.1** 的 CLI 重跑的(2026-11-05),
78
80
  文件数与发布物一致;更早的记录只差版本号与文件数,流程未变。
79
81
 
80
82
  ### 3.1 安装
81
83
 
82
84
  ```
83
- + dsh-molbio-tools file:D:/.../dsh-molbio-tools-0.22.2.tgz
85
+ + dsh-molbio-tools file:D:/.../dsh-molbio-tools-0.23.0.tgz
84
86
  Packages: +1
85
- Done in 571ms
87
+ Done in 384ms using pnpm v12.15.0
86
88
  ```
87
89
 
88
90
  安装后的 `--dump-config` 出现 bundle 层,且 `host.mjs` 解析到**已安装包内**:
@@ -102,24 +104,29 @@ Done in 571ms
102
104
  ### 3.2 启动
103
105
 
104
106
  ```
105
- dsh web: http://127.0.0.1:50071/?token=...
107
+ dsh web: http://127.0.0.1:64001/?token=...
106
108
  ```
107
109
 
108
- 读回结果:`HTTP 200`、38180 字节;页面里的 `window.__DSH_BOOT__` 图包含
110
+ 读回结果:`HTTP 200`、40311 字节;页面里的 `window.__DSH_BOOT__` 图包含
109
111
  `dsh-molbio-tools/client.js&rev=…`(rev 是内容哈希,随产物变化),即浏览器半被宿主 Loader 发现并进了客户端图——
110
112
  这是"启动成功"的可观察证据,而不只是进程没退出。(先用 token 换 cookie,再带 cookie 读 `/`,
111
113
  否则第一跳是 303。)
112
114
 
113
115
  再用同一 cookie POST `/api/agentPresets/list` 与 `/api/pluginInventory/list`(envelope 见
114
- [rules.md](rules.md) 规则 1):`broken` 为空(空数组时服务端直接省略该键),molbio-lab 的
115
- `tool-molbio` 为 `fiberPhase: "active"`——preset 里的 60 个工具**真的挂上了**,
116
- 而不只是组合树正确。
116
+ [rules.md](rules.md) 规则 1):`broken` 为空(空数组时服务端直接省略该键),`presets` 里
117
+ `molbio-lab` 的 `order` 为 5,molbio-lab 的 `tool-molbio` 为 `fiberPhase: "active"`——
118
+ preset 里的 62 个工具**真的挂上了**,而不只是组合树正确。
119
+
120
+ > 0.2.2-rc.1 的这一步顺带证实了 0.23.0 的修复:同一份 preset 在修复前含
121
+ > `@deepseek-ai/dsh-tool-subagent-control/list-agents`,该子路径已随 `list_agents` 并入主入口而删除,
122
+ > 解析即 `ERR_PACKAGE_PATH_NOT_EXPORTED`,`broken` 会有内容。现在 `preset-health` 用
123
+ > `createRequire(...).resolve` 把这种行挡在仓库内。
117
124
 
118
125
  ### 3.3 卸载
119
126
 
120
127
  ```
121
- - dsh-molbio-tools file:D:/.../dsh-molbio-tools-0.22.2.tgz
122
- Done in 421ms
128
+ - dsh-molbio-tools file:D:/.../dsh-molbio-tools-0.23.0.tgz
129
+ Done in 123ms using pnpm v12.15.0
123
130
  ```
124
131
 
125
132
  卸载后四项同时回到初始状态:
@@ -142,10 +149,10 @@ pnpm 的 hoisted linker 会在 `node_modules/` 留下一个指向仓库目录的
142
149
 
143
150
  ```
144
151
  node test/contract.mjs → 17 checks passed(host 契约、client 产物、tarball 覆盖、声明回退的突变自检)
145
- node test/preset-health.mjs → all 30 rows load on dsh 0.2.1-rc.5
146
- node test/smoke.mjs → registered 60 tools, all smoke tests passed
147
- node test/drift-probe.mjs → 16/16(14 个突变全部被抓到)
148
- node benchmark/run.mjs --offline → 61 tasks checked against the shipped tools
152
+ node test/preset-health.mjs → all 29 rows load on dsh 0.2.2-rc.1
153
+ node test/smoke.mjs → registered 62 tools, all smoke tests passed
154
+ node test/drift-probe.mjs → 16/16(15 个突变全部被抓到)
155
+ node benchmark/run.mjs --offline → 63 tasks checked against the shipped tools
149
156
  ```
150
157
 
151
158
  `test/release.mjs` 在完整权限的本地会话里整套通过(包括末尾的 npm 版本探测);
@@ -37,7 +37,7 @@ disabled、没有覆盖官方行。
37
37
  `@deepseek-ai/dsh-client-modules` 的 `exactPackageSpecifier` 只接受 `@scope/name` 或裸包名,任何含
38
38
  `/` 的非 scoped 说明符返回 `undefined`,`locatePkgJson` 随即返回 `undefined`,该行永远不会被客户端
39
39
  扫描发现,右栏 Molbio/Papers 两个 tab 会静默消失。改成包根 `dsh-molbio-tools` 也不可行:根模块是
40
- 工具入口,会把 60 个工具注册进该 profile 的每个会话——这正是本包要避免的。
40
+ 工具入口,会把 64 个工具注册进该 profile 的每个会话——这正是本包要避免的。
41
41
 
42
42
  请求:允许「`name` 以 `./` 开头、解析基准为 manifest 所在包目录、解析结果留在包内且文件已分发」的
43
43
  自引用 insert 行;或把本固定 commit 送入人工专项审阅,按实际内容判定。
@@ -163,7 +163,7 @@ function exactPackageSpecifier(specifier) {
163
163
  避免的故障。路径型(`./`、`file:`、绝对路径)是唯一既能挂一个惰性锚点、又能被扫描器解析的写法,
164
164
  而它恰好被身份规则拒绝。
165
165
 
166
- 改成「包根本身」也不行:根模块是 `index.mjs`,一旦作为宿主行挂载就会把 60 个工具注册进该 profile
166
+ 改成「包根本身」也不行:根模块是 `index.mjs`,一旦作为宿主行挂载就会把 64 个工具注册进该 profile
167
167
  的每个会话——这是本包刻意避免的。
168
168
 
169
169
  关键点:**这条正则扫的是 patch 原始文本**,所以它无法区分
@@ -0,0 +1,178 @@
1
+ # 0.24.0 施工计划(已实施)
2
+
3
+ > 状态:**已实施(0.24.0)**。实际范围 = survey #11 + #12 + 接头表 RNA→DNA 静默错修复;
4
+ > #9 / 2.9b / 2.9c 未纳入。下面的探索记录与备选方案保留原样,便于对照“计划 vs 落地”。
5
+ > 0.23.0 已提交并推送(`4050aa6`)。
6
+
7
+ ## 0. 一句话结论
8
+
9
+ **0.24.0 建议做 survey #11 + #12(若接受边界,再加 #9 的 perfect-duplex 子集);
10
+ 2.9c 放到 0.25.0;2.9b 暂缓,先解决宿主侧写回 RPC。**
11
+
12
+ | 候选 | 是否进 0.24.0 | 理由 |
13
+ | --- | --- | --- |
14
+ | survey #11 载体/接头污染筛查 | **建议加入** | 纯计算、已有接头表可复用、S–M、可直接加 benchmark |
15
+ | survey #12 FASTQ 修剪/过滤 | **建议加入** | 现有解析器齐备,只缺写出与修剪层;M、用户价值高 |
16
+ | survey #9 双链热力学 | **可选(只做 perfect duplex)** | 现有 `duplexThermo` 可导出;完整错配表/DMSO 是另一项工作 |
17
+ | 2.9c 结构预览 | **0.25.0** | 可行且不需要宿主 RPC,但解析器 + 客户端查看器是 M–L,和上面三项同版风险偏高 |
18
+ | 2.9b 文献库写回 | **暂缓** | `workspaceFiles` 明确只读;需要新增宿主 Typert Remote + 并发契约,属于架构项 |
19
+
20
+ ## 1. 评估口径
21
+
22
+ 沿用本仓库的既有约束:零运行时依赖、纯 JS、无外部二进制/数据库;工具行为变化必须
23
+ 更新 benchmark;客户端改动必须重建 `lib/client.js` 并通过四个客户端套件;不允许为了
24
+ 方便绕过 `ctx.fs`/sandboxPolicy。
25
+
26
+ ## 2. 逐项探索
27
+
28
+ ### 2.1 2.9b 文献库写回:被“只读 Remote”挡住
29
+
30
+ 现状:
31
+
32
+ - `molbio_paper_*` 工具通过 `papers.mjs` 的 `loadLibrary`/`saveLibrary` 读写 `papers.json`;
33
+ `addPapers`/`updatePaper`/`removePaper` 已定义去重与字段合并语义。
34
+ - 面板的 Papers 页只调用 `ctx.remote.workspaceFiles.read`,是刻意只读的
35
+ ([client-panel.md](client-panel.md) §3)。
36
+ - DSH 的 `workspaceFiles` 服务在包 README 里写明 **“this service exposes no mutations”**,
37
+ 类型里也只有 `read` / `readBytes` / `stat` / `list` / `changes`。
38
+ - 本包客户端半**没有**宿主侧 RPC;这是为了避开 Typert Remote 的版本耦合而做的取舍
39
+ ([client-panel.md](client-panel.md) §3)。
40
+
41
+ 所以“面板里改笔记/标签”不是一个 UI 小功能,而是:
42
+
43
+ 1. 在宿主侧新增一个 Typert Remote 服务(或让面板走与工具相同的写路径);
44
+ 2. 定义乐观版本号/串行化队列,避免与 `molbio_paper_update` 并发时丢更新;
45
+ 3. 客户端增加对该 Remote 的注入与失败降级;
46
+ 4. 处理 panel-only 包在没有该宿主服务时的行为。
47
+
48
+ **结论:不在 0.24.0 做。** 如果要做,先做一个只含 `describe`/`mutate(expectedVersion)` 的
49
+ 宿主 Remote spike,再谈 UI;不要先做表单。
50
+
51
+ ### 2.2 2.9c 结构预览:可行,且不需要宿主 RPC
52
+
53
+ DSH 的文档预览容器提供了两条可复用路径:
54
+
55
+ - `ctx.documentPreviews.register({ id, extensions, priority, title, loading })` 注册元数据;
56
+ - 在 keyed 座位 `sidebar.right.tab.document` 下用同一 `id` 注册正文;
57
+ - 正文收到 `resourceAddress` 与 `content`;`loading: 'bytes-complete'` 时
58
+ `content.kind === 'bytes'`,可直接 `TextDecoder` 解码 PDB/mmCIF/SDF/MOL 文本。
59
+
60
+ 这与本包“浏览器里跑自己的纯模块”的模式一致,不需要新增宿主 RPC。需要新建:
61
+
62
+ - `structure-preview.mjs`:PDB `ATOM/HETATM`、mmCIF `_atom_site`、SDF/MOL V2000
63
+ (V3000 可后续)解析;Cα 轨迹/键的 2D 投影(PCA 或给定 2D 坐标);SVG。
64
+ - 客户端正文:注册 preview + keyed body;空/坏/超大文件降级为说明;不抛异常。
65
+ - 测试:纯解析器已知坐标/键数;客户端 stub 驱动 preview 注册与渲染;contract 计数更新。
66
+ - 文档与 CHANGELOG;面板包 `0.1.8 → 0.1.9`(客户端产物变了)。
67
+
68
+ **结论:技术上可行,但规模是 M–L,建议单独作为 0.25.0 的主功能。** 0.24.0 若也做,
69
+ 会把“纯工具版”变成“工具 + 新查看器”双线,测试面太大。
70
+
71
+ ### 2.3 survey #9 双链热力学:可做子集,完整版不急
72
+
73
+ 现状:
74
+
75
+ - `lib.mjs` 已有 SantaLucia 1998 NN 表、`primerTm`(含 von Ahsen Mg²⁺ 等效盐校正)、
76
+ `duplexThermo`、`deltaG37`、`duplexTmFromThermo`;后三者目前是模块私有。
77
+ - `molbio_primer_check` 只报 3' 端 5-mer 的 `end_stability_kcal`,没有整条双链的
78
+ ΔH/ΔS/ΔG°37。
79
+
80
+ 可低成本交付的 **perfect-duplex 子集**:
81
+
82
+ - 输入一条链(另一条按反向互补生成),或两条链并校验完全互补;
83
+ - 输出 `delta_h_kcal`、`delta_s_cal`、`delta_g37_kcal`、`tm_celsius`、盐条件;
84
+ - 自互补链的 `Ct` 因子单独处理;
85
+ - 明确写“只支持完全互补双链;错配、凸起、给定二级结构、DMSO 未建模”。
86
+
87
+ 完整版需要 Allawi/SantaLucia 错配表(转写 + 独立校验)、loop 表与 DMSO 经验校正,
88
+ 是 M 级且容易“数字看起来对、其实不可互换”。**建议:0.24.0 只在接受上述边界时加入;
89
+ 否则留给后续专门版本。**
90
+
91
+ ### 2.4 survey #11 载体/接头污染筛查:最合适的 0.24.0 候选
92
+
93
+ 现状:
94
+
95
+ - `fastq-qc.mjs` 已有 `ADAPTERS`(Illumina Universal/Small RNA、Nextera、PolyA/PolyG)
96
+ 与 read-level 的 adapter content 面板。
97
+ - **已发现一个现有静默错**:Small RNA 5' 的条目是 RNA(`GUUCAG…`,含 U),而
98
+ `adapterContent` 直接拿它去 `indexOf` DNA reads;正向分子永远匹配不上,只有它的反向互补
99
+ 能命中其中一条。实测两条(DNA 正向/反向互补)只报 1 条。修法是筛查前把表归一化成 DNA
100
+ (U→T)或分别生成 DNA 正向/反向 needle;#11 应把这条一起修,并加双向断言。
101
+ - 但没有独立工具,也没有载体骨架签名;不能对单条序列/组装结果做筛查。
102
+
103
+ 建议实现:
104
+
105
+ - 新建 `contamination.mjs`:把 `ADAPTERS` 移到这里并从 `fastq-qc.mjs` 再导出(保持兼容),
106
+ 增加 `VECTORS`(pUC/pBR322、pET、pcDNA、Gateway attB/attR 等短签名)。
107
+ - `screenContamination(sequenceOrEntries, { maxMismatches, sampleReads })`:
108
+ 正/反向互补、位置、链、错配数、命中条目;序列输入与 FASTA/FASTQ 路径都支持。
109
+ - 工具 `molbio_contamination_screen`;默认 exact,`max_mismatches` 可选 0–2。
110
+ - 输出明确写“短签名筛查,不是 BLAST/全库比对;未命中不等于无污染”。
111
+
112
+ 规模 S–M;新增 1 个工具、1 条 benchmark;不需要客户端改动。
113
+ 风险是短签名假阳性/假阴性,必须把表来源、长度和局限写进描述与 README。
114
+
115
+ ### 2.5 survey #12 FASTQ 修剪/过滤:用户价值最高,但要把边界切清楚
116
+
117
+ 现状:
118
+
119
+ - `seqio.mjs` 有 `parseFastq`(容忍多行)与 `toFasta`,**没有** `toFastq` 与修剪层;
120
+ - `molbio_fasta_fastq` 只做 stats/extract/convert/qc;`molbio_fastq_qc` 只读不写。
121
+
122
+ 建议先做**一个**工具 `molbio_fastq_trim`,覆盖:
123
+
124
+ - 3' 滑窗质量修剪(窗口均值阈值)与 5' 端低质量修剪;
125
+ - poly-G / poly-A 尾部修剪;
126
+ - 3' 接头 / 5' 引物精确或 seed 匹配修剪;
127
+ - 最小长度、最大 N、可选 subsample;
128
+ - Phred+33/+64 输入(输出保持同一编码);
129
+ - 写 FASTQ 或 FASTA(`ctx.fs` + sandboxPolicy)。
130
+
131
+ 明确不做:paired-end overlap merge、复杂 semiglobal adapter alignment、UMI 处理
132
+ (这些是另一个规模;不要在一个工具里假装做了)。
133
+
134
+ 规模 M;新增 1 个工具、1 条 benchmark;不需要客户端改动。
135
+ 测试应钉住:每条 read 的修剪后长度/质量、poly-G/A 边界、adapter 位置、过滤计数、
136
+ 输出 FASTQ 四行结构与 round-trip。
137
+
138
+ ## 3. 推荐的 0.24.0 范围
139
+
140
+ **主选(低风险、纯工具):#11 + #12。**
141
+
142
+ - 工具数 62 → **64**;benchmark 63 → **65**;覆盖 64/64。
143
+ - 新增 `contamination.mjs`、`fastq-trim.mjs`;`seqio.mjs` 增加 `toFastq`。
144
+ - `seqio.mjs` 新增 `toFastq` 会进入浏览器半,因此两个客户端产物需重建;面板包 0.1.8 → 0.1.9(行为未变,只是产物字节变了)。
145
+ - 文档四处计数 + README 工具表 + benchmark tasks + CHANGELOG/history/roadmap。
146
+
147
+ **可选加项:** #9 的 perfect-duplex 子集(工具数 65、benchmark 66),前提是接受
148
+ “不支持错配/DMSO/指定二级结构”的边界,并在描述里写明。
149
+
150
+ **不在 0.24.0:** 2.9b(宿主 RPC + 并发)、2.9c(M–L 客户端查看器)、#9 完整错配表。
151
+
152
+ ## 4. 施工顺序(若批准主选)
153
+
154
+ 1. `contamination.mjs` + 表 + `molbio_contamination_screen`;smoke 已知值;
155
+ 2. `fastq-trim.mjs` + `toFastq` + `molbio_fastq_trim`;smoke 已知值;
156
+ 3. 两条 benchmark 任务 + `verifications.mjs` invocation;`--offline` 先绿;
157
+ 4. `test/benchmark-coverage` 64/64;`test/benchmark-profile` 工具数;
158
+ 5. README/preset 描述/index 顶部注释/agent.cordis.yml 注释四处同步;
159
+ 6. `npm test`、`bench --offline`、定向模型 benchmark(`--tools contamination_screen,fastq_trim`);
160
+ 7. CHANGELOG 0.24.0、docs/history、roadmap;版本 0.24.0;commit。
161
+
162
+ ## 5. 风险与开放问题
163
+
164
+ - **#11 的签名表**:短签名会假阳性;必须给出来源、长度、默认 exact、未命中声明。
165
+ 需要避免复制有许可证负担的长载体序列。
166
+ - **#12 的“修剪到什么程度”**:滑窗阈值、poly-G 最小 run、adapter 最小匹配长度都要
167
+ 有默认值与边界测试;不要一次把 Trimmomatic/fastp 的所有功能塞进来。
168
+ - **#9 的自互补 Ct 因子**:若做,需用已知序列钉住公式,否则会和 `primerTm` 的
169
+ 非自互补假设悄悄分叉。
170
+ - **2.9c 的 documentPreviews 注入**:若在 0.25.0 做,先验证 `ctx.inject(['documentPreviews'])`
171
+ 在没有该服务的 panel-only profile 中会安全跳过;不要把整个面板 entry 变成硬依赖。
172
+ - **benchmark 成本**:每条新任务都是 full-only;发版前用 `--tools` 定向跑,不默认全量。
173
+
174
+ ## 6. 备选方案(若 0.24.0 想要“看得见”的功能)
175
+
176
+ 把 0.24.0 改成 **2.9c + #11**:结构预览作为主功能,#11 作为配套纯工具;
177
+ #12 与 #9 推到 0.24.1/0.25.0。这个方案的用户可见价值更高,但客户端测试面明显更大,
178
+ 且 0.24.0 的发版周期会更长。默认不推荐。
package/docs/workflow.md CHANGED
@@ -20,13 +20,13 @@
20
20
  | 使用 | 模型找得到、用得对吗? | `benchmark/`(见 [benchmark/README.md](../benchmark/README.md)) |
21
21
 
22
22
  ```bash
23
- node test/smoke.mjs # 插件:mock 注册表跑全部 60 个工具 + 输出 schema 校验
23
+ node test/smoke.mjs # 插件:mock 注册表跑全部 64 个工具 + 输出 schema 校验
24
24
  node test/svgpng.mjs # 光栅化器:PNG 结构 + inflate 回像素断言 + 真实渲染器子集检查
25
25
  node test/svgio.mjs # 共享绘图助手:几何手算值 + 每种助手拼一张文档后光栅化必须干净
26
26
  node test/charts.mjs # 图表:CSV/TSV 读取层 + 八种图表(含色带)的统计量与光栅化
27
27
  node test/client.mjs # 客户端产物:按加载器方式执行 + 面板数据通路(无浏览器)
28
28
  node test/panel-render.mjs # 面板组件:最小钩子宿主里跑真实组件(无 React、无 DOM)
29
- node test/map-card.mjs # 图谱调用卡:跨界 meta 投影断言
29
+ node test/map-card.mjs # 调用卡(图谱 + logo/gRNA):跨界 meta 投影断言
30
30
  node test/client-mount.mjs # 客户端挂载:复刻宿主侧图扫描,核对 web profile 的行与依赖
31
31
  node test/contract.mjs # DSH 契约:官方是否改了规则或 API
32
32
  node test/preset-health.mjs # 组合:逐行按该包自己的 Config schema 校验 preset 可挂载性
@@ -50,7 +50,7 @@ node benchmark/run.mjs --offline # benchmark 的期望值是否仍等于工具
50
50
 
51
51
  ### 各套件到底在防什么
52
52
 
53
- - **`smoke.mjs`** 证明**插件**可用:mock 注册表运行全部 60 个工具,并用 harness 自身的
53
+ - **`smoke.mjs`** 证明**插件**可用:mock 注册表运行全部 64 个工具,并用 harness 自身的
54
54
  `assertSupportedJsonSchema` / `validateJsonSchemaValue` 校验每个输出 schema 与返回值;
55
55
  覆盖已知值用例(EcoRI 酶切、ΔΔCt=-3 → fold 8、GenBank/SnapGene 解析、引物对一致性、
56
56
  SVG 文件写入与无旋转标签断言、克隆模拟手算序列比对、合成 ABIF 夹具、环状参考跨原点
@@ -218,7 +218,7 @@ benchmark 不是"每天跑一遍"的门禁,而是**行为的验收测试**—
218
218
 
219
219
  | 这次改了什么 | benchmark 要不要动 | 为什么 |
220
220
  | --- | --- | --- |
221
- | 新增一个工具 | **必须**:加任务 + `verifications.mjs` 的 invocation | `test/benchmark-coverage.mjs` 会**直接 FAIL**(`60/60 tools covered` 是断言) |
221
+ | 新增一个工具 | **必须**:加任务 + `verifications.mjs` 的 invocation | `test/benchmark-coverage.mjs` 会**直接 FAIL**(`64/64 tools covered` 是断言) |
222
222
  | 改工具的参数、默认值、输出字段、渲染文本 | **必须**:更新受影响任务的断言,重跑 `--offline`,再重跑该任务 | 断言比对的是**渲染文本**,改了文本就改了对错 |
223
223
  | 改工具的描述或参数说明(prompt 面) | **必须**:重跑对应任务 | 这正是 benchmark 唯一能测的东西——模型是否还选得对 |
224
224
  | 改 `preset/molbio-lab/agent.cordis.yml`(行清单) | **必须**:先 `node test/benchmark-profile.mjs`,再重建 profile | 行清单变了,benchmark 的 headless profile 是**推导**出来的,守卫会 FAIL |
@@ -237,7 +237,7 @@ benchmark 不是"每天跑一遍"的门禁,而是**行为的验收测试**—
237
237
 
238
238
  ```bash
239
239
  node benchmark/run.mjs --offline # 每条 where:"tool" 断言是否仍等于工具的真实渲染输出
240
- node test/benchmark-coverage.mjs # 60/60 工具有任务覆盖、fixture 前提仍成立、tier 与占位符健全
240
+ node test/benchmark-coverage.mjs # 64/64 工具有任务覆盖、fixture 前提仍成立、tier 与占位符健全
241
241
  node test/benchmark-score.mjs # 判分器对冻结的真实响应仍判对,且仍能判错
242
242
  ```
243
243
 
@@ -254,7 +254,7 @@ offline 证明"期望值仍然为真",model 测量"模型是否仍然会用"
254
254
  | 改了**几个**工具 | `--tools a,b,c`(也接受 `molbio_` 前缀与 `primer_*` 通配) | 并集 |
255
255
  | 改了**一个**已知任务 | `--task <id>` | 一题 |
256
256
  | 改了**共享模块**,想知道影响面 | `--list --changed`(不花钱) | 0 |
257
- | 发版前 | `node --run bench:full` | 61 题 |
257
+ | 发版前 | `node --run bench:full` | 65 题 |
258
258
  | 只是提交文档 | `--changed` 会回答"无事可跑" | 0 |
259
259
 
260
260
  ```bash
@@ -299,7 +299,7 @@ $ touch lib.mjs && node benchmark/run.mjs --list --changed
299
299
  1. **不可判定 → 全量**:没有 git / 没有 HEAD / 读不到插件结构,一律回落全量并打印原因;
300
300
  2. **认不出的文件 → 全量**:新模块、重命名的文件(`not in the plugin's module set`);
301
301
  3. **`index.mjs` → 全量**:它是绑定全部工具的那个入口;
302
- 4. **映射到的工具有题没覆盖 → 全量**(并由 `test/benchmark-coverage.mjs` 断言 60/60 覆盖)。
302
+ 4. **映射到的工具有题没覆盖 → 全量**(并由 `test/benchmark-coverage.mjs` 断言 64/64 覆盖)。
303
303
 
304
304
  反过来,**唯一会返回"无事可跑"的情况**是改动**可证明**不影响工具行为:`docs/`、
305
305
  `CHANGELOG.md`、`README.md`、`test/`、`benchmark/`、`package.json`。这不是走后门——
@@ -363,7 +363,7 @@ node test/client.mjs && node test/panel-render.mjs && node test/map-card.mjs &&
363
363
  两条实现纪律:投影**必须廉价且不抛**(它跑在调用成功之后,抛错会把这次调用标成失败;
364
364
  图谱标记因此走一份有界内存缓存而不是读文件),卡片**必须校验而非信任** meta(缺失/异种/
365
365
  异形/超限/失败一律降级为提示,绝不在对话里抛错)。新增卡片时:把工具名加进
366
- `build/client-entry.mjs` 的 `MAP_TOOL_KEYS`,并在 `test/map-card.mjs` 里补一条跨界断言。
366
+ `build/client-entry.mjs` 的 `MAP_TOOL_KEYS`(图谱)或 `FIGURE_TOOL_KEYS`(图形),并在 `test/map-card.mjs` 里补一条跨界断言。
367
367
  另注意 `define()` 包装器会转发 `presentationMeta`——若哪天再包一层,别忘了这条。
368
368
 
369
369
  ### 安装与修复面板
@@ -456,7 +456,7 @@ git status --short # lib/client.js 与 packages/molbio-panel/lib/clien
456
456
  ### DSH 升级后:preset 组合的维护(必做)
457
457
 
458
458
  `preset/molbio-lab/agent.cordis.yml` 是官方 `standard` 预设的**行清单**副本 + 末尾一行
459
- `tool-molbio`(当前基线:**dsh 0.2.1-rc.5**)。它不会自动跟随 DSH 升级,因此每次升级
459
+ `tool-molbio`(当前基线:**dsh 0.2.2-rc.1**)。它不会自动跟随 DSH 升级,因此每次升级
460
460
  DSH 后:
461
461
 
462
462
  1. 取新版的 shipped `standard`:
@@ -469,7 +469,8 @@ DSH 后:
469
469
  只比结构,注释漂移抓不住,但下一次人工 diff 会;
470
470
  3. 跑 `node build/preset-patch.mjs` 重新生成根目录的 `cordis.patch.yml`;
471
471
  4. 跑 `node test/preset-health.mjs` 直到 `OK`(它把任何结构差异当**发布阻断**,不只是打印
472
- note),再跑 `node test/drift-probe.mjs` 确认守卫仍能抓到漂移;
472
+ note;它还会按 Loader 的方式解析每个行名,上游删掉的子路径会在这里直接 FAIL),
473
+ 再跑 `node test/drift-probe.mjs` 确认守卫仍能抓到漂移;
473
474
  5. 更新 `agent.cordis.yml` 头部"Baseline: …"那行里的 DSH 版本号;
474
475
  6. 跑 `node test/benchmark-profile.mjs`:benchmark 的 headless profile 是从这份行清单**推导**
475
476
  出来的,上游加行、改 config 都会在这里失败(它会把每一行逐字段比对),按提示把新行分类