dsh-molbio-tools 0.20.0 → 0.21.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.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,117 @@
10
10
  改为按**包名**引用后模块缓存问题随之消失——没有拷贝,也就没有需要保持同步的版本目录。
11
11
  下文历史条目里出现的 `vN` 目录保持原样,作为当时的记录。
12
12
 
13
+ ## [0.21.0] — 2026-10-23(DSH 0.2.1-rc.2 适配;两条 benchmark 欠账清零)
14
+
15
+ **工具数不变(60),插件行为零改动。** 这是一次"只跟上游走"的版本:0.20.1 之后本机 harness
16
+ 升到 0.2.1-rc.2,`npm test` 立刻红了三处——组合漂移、契约断言、以及一条依赖旧 fixture 的
17
+ 漂移探针。三处都不是插件退化,但每一处都属于**守卫在说谎**,所以按发布阻断处理。
18
+
19
+ ### 1. preset 行清单按 0.2.1-rc.2 逐行重抄(12 处结构差异)
20
+
21
+ - 上游 `standard` **新增** `time-context`、`tool-schedule`、`tool-plugin-market`;
22
+ - **删除** `tool-subagent-codex` / `tool-subagent-claude-code`(两个 `disabled: true` 的可选
23
+ provider)与 `tool-ralph`;
24
+ - **去掉** persona 的 `suffix: Your working directory is {{cwd}}.`,以及 `tool-subagent` /
25
+ `tool-subagent-fork` 的 `backgroundMode: continuable`;
26
+ - plan-mode 的段落文案改为 "until the user approves your plan through exit_plan_mode…"。
27
+
28
+ `agent.cordis.yml` 现在是上游 `config.plugins:` 块的**整体去缩进** + 末尾一行 `tool-molbio`;
29
+ 0.2.1 的上游文件不带行内注释,因此行内注释随之消失,来源与升级规则集中写在文件头部。
30
+ `preset.patch.yml` 由 `build/preset-patch.mjs` 重新生成。
31
+
32
+ ### 2. `test/contract.mjs`:两条断言不再锚在 minified 字面量上
33
+
34
+ - **client 入口**:改为按包自己的 `exports["./client"]`(`browser` 条件)解析。
35
+ 0.2.1-rc.2 把 `@deepseek-ai/dsh-client-connection` 移到 `lib/client.browser.js`,旧断言
36
+ 硬编码 `lib/client.js`,会把一个加载正常的包判死;同时 Web shell 已是打包产物、不再出现
37
+ `require("包名")`,"seeded into the shell" 兜底只剩代码路径。新增两个对照:没有
38
+ `./client` 导出的包必须解析为 `undefined`,以及移动过的入口必须解析到真实文件。
39
+ - **`presentationMeta`**:0.2.1 删除了 `exec.parent === void 0` 闸门(投影移进
40
+ `createSuccessResult`,对每次成功执行都做)。断言改挂到卡片真正消费的东西:投影被
41
+ `snapshotProjection` 记录、并以 `meta` 附到成功结果上。
42
+
43
+ ### 3. `drift-probe`:突变样本换成上游仍存在的行
44
+
45
+ 上游删掉 `tool-ralph` 后,那条"上游 `disabled: true` 被丢掉"的突变在**空转**(`withRow`
46
+ 找不到目标,什么也没改,守卫却"通过")。改用上游仍以 `disabled: true` 发布的
47
+ `tool-plugin-manager`;14 条突变全部仍能被抓住。
48
+
49
+ ### 4. benchmark:profile 重建、两条欠账清零
50
+
51
+ - preset 新增的两行宿主可承载行(`time-context`、`tool-schedule`)进入 headless profile,
52
+ `benchmark/profile.mjs` 重建 `molbio-bench`:**12 行宿主行,仍注册 60 个工具**。
53
+ - `methylation-block` 与 `qpcr-primers` 的冻结响应停在 0.16.0 之前,`benchmark-score` 每次
54
+ 都点名。本版用一次完整真实模型跑重录,并用 `_freeze-trace.mjs --replace` **只替换这两条**,
55
+ 其余 47 条保持原样;`run.amendments` 记录每条替换来自哪一次跑分。fixture 现为
56
+ **49 条记录、0 条 suspended**。
57
+ - 完整跑分(2026-10-23,DSH 0.2.1-rc.2,61 题):**58/61** 通过,tool selection **60/61**、
58
+ arguments **60/61**、final answer **59/61**;11.2M tokens / 2161 s。
59
+ - `protein-props` 的失败是**断言不认千分位逗号**(答案写 `1,490`,断言写 `/1490/`)——
60
+ 与 0.20.0 修过的 `/3935/` 同一类,当时漏了这一条。已改为 `1[,.]?490`,`--replay` 后
61
+ **59/61**。
62
+ - 剩余两条是**真实的模型遗漏**,不是产品缺陷:`dilution-math` 答案正确(50 µL + 950 µL)
63
+ 却从未调用 `molbio_lab_math`;`batch-analysis` 写出了 CSV,但最终回答只说"报告已生成",
64
+ 没有报出指令要求的长度/特征数。
65
+ - `_freeze-trace.mjs` 新增 `--replace id[,id…]`:只重录被修复作废的条目,其余记录与顺序原样
66
+ 保留,并在 `run.amendments` 里写明每条替换的来源跑分——**替换的是真实录制,不是手改**。
67
+
68
+ ### 5. 未做 / 边界
69
+
70
+ - **`png_path` 仍然不做**:`contract` 的"工作区仍不能存二进制文件"断言在 0.2.1-rc.2 上**仍然
71
+ 通过**,说明上游 fs 缝依旧是 text-only;触发条件未到。
72
+ - **面板版本保持 0.1.3**:`lib/client.js` 逐字节未变,`release-preflight` 复核为
73
+ "already published with identical bytes",因此不 bump。
74
+
75
+ ### 6. 版本与验证
76
+
77
+ - 根包 `0.20.1 → 0.21.0`;DSH 基线钉 **0.2.1-rc.2**(`.github/workflows/{test,release}.yml`、
78
+ preset 头注、`docs/workflow.md`、`docs/roadmap.md`、`docs/maintainer.md`)。
79
+ - `npm test` 全绿:contract 16/16、drift-probe 14 条突变、preset-health 30 行全过、release
80
+ 13 项、benchmark profile/score/coverage/changed、`bench --offline` 61 题。
81
+ - 文档:roadmap 状态、history 的 v0.21.0 段(三条教训)、workflow 的升级提示同步。
82
+
83
+ ## [0.20.1] — 2026-10-18(插件专属图标)
84
+
85
+ **工具数不变(60),运行时零改动;包内只多一个 746 B 的 SVG。**
86
+
87
+ ### 1. 插件管理器为什么一直显示默认插画
88
+
89
+ DSH 的插件管理器从包根的清单 `icon` 或导出的 `./icon` 读取展示图,读到的字节会转成
90
+ base64 data URL 交给 `PackageArtwork`;两者都没有时保留面板默认插画。本包此前既没有
91
+ `icon` 字段也没有 `./icon` 导出,因此卡片和插件行一直是默认图——不是缓存,也不是
92
+ 安装坏了,而是**展示元信息根本没声明**。
93
+
94
+ ### 2. 图标:透明底双螺旋,按 30/36 px 的展示尺寸设计
95
+
96
+ `icon.svg`(64×64 viewBox,746 B)是原创的双螺旋:青绿 `#0D9488` 与蓝 `#2563EB`
97
+ 两条主链、四条琥珀 `#F59E0B` 碱基对,圆头描边、无渐变/字体/外部引用。透明底是刻意的:
98
+ 插件管理器把它放进 40×40(行)和 48×48(卡片)的圆角框里,图本身再加一层背景会变成
99
+ “框中框”;不留背景也让同一张图在浅色和深色主题下都成立。主链可视范围约 28×58(64 单位
100
+ 画布),在 36 px 卡片和 30 px 行图标上仍能读出双螺旋,20 px 时退化为可辨的轮廓。
101
+
102
+ ### 3. 接入:导出、白名单,以及一个 gitignore 例外
103
+
104
+ - `package.json` 的 `exports` 增加 `"./icon": "./icon.svg"`,`files` 白名单加入
105
+ `icon.svg`;本包 `files` 是白名单而不是默认全带,漏了它 `npm pack` 就会静默丢掉图标。
106
+ - 根目录的 `/*.svg` 规则原本用来挡住工具生成的图(`logo.svg`、质粒图等),新增
107
+ `!/icon.svg` 例外,图标才能作为源文件提交;git 忽略与 npm `files` 是两套规则,不能只改
108
+ 一处。
109
+ - 不新增依赖、不改 `dsh.client`、不改 preset 行清单;已安装的 profile 是指向本目录的
110
+ junction,重新读取 manifest 即可看到,不需要重装。
111
+
112
+ ### 4. 验证
113
+
114
+ - `createRequire(...).resolve('dsh-molbio-tools/icon')` 解析到包内 `icon.svg`;
115
+ `npm pack --dry-run --json` 的 118 个文件中包含它。
116
+ - 用本包自己的 `svgpng.mjs` 光栅化:64×64,`unsupported: []`;另按 36/30/20 px
117
+ 下采样检查过轮廓。
118
+ - `test/contract.mjs` 16/16;`test/release.mjs` 13/13(本机沙箱下按该测试自带的
119
+ `npm_config_user_agent` 兜底跑通)。
120
+ - 根包 `0.20.0 → 0.20.1`;面板 `0.1.3` 不动(`lib/client.js` 未变,发布前置检查不会
121
+ 要求面板 bump)。标题/描述仍回退到 `package.json` 的包名与长描述;本次没有新增
122
+ `locale/*.json`,如果要本地化展示文案,应作为独立改动。
123
+
13
124
  ## [0.20.0] — 2026-10-17(Cas 家族几何、qPCR 订购/配平、批量分析与静默位点突变;并入 0.19.1 硬化)
14
125
 
15
126
  **工具数 58 → 60,benchmark 56 → 61 题(60/60 覆盖)。** 这一版把 0.19.1 计划的四项硬化直接并入,
package/README.md CHANGED
@@ -44,21 +44,35 @@
44
44
 
45
45
  需要一台已装好 [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) 的机器。
46
46
 
47
- ### 推荐:专属模式 preset(2 步)
47
+ ### 推荐:在 DSH 插件页安装(3 步)
48
48
 
49
49
  装完后,预设选择器里会出现 **Molecular Biology Lab** 模式;**60 个工具只在这个模式里出现**,
50
- 不会污染你其它会话(这点很重要:工具多了会占提示预算)。
50
+ 不会污染你其它会话(这点很重要:工具多了会占提示预算)。**右栏的 Molbio / Papers 两个面板也随这个包
51
+ 一起装好**,不需要再装 `dsh-molbio-panel`。
51
52
 
52
- ```powershell
53
- # 1. 把包装进你的 profile(web 是默认 profile 名,按需替换)
54
- dsh plugin --profile web add D:\path\to\dsh-molbio-tools
53
+ 1. 打开 DSH Web GUI,点侧栏的 **插件**(Plugins)。
54
+ 2. 点 **添加插件**(Add plugin),在「包名或地址」里输入 `dsh-molbio-tools`,点 **安装**(Install);
55
+ 装完点 **立即启用**(Enable now)——关掉对话框只会装好、不会启用,之后也可以在插件页打开它的开关。
56
+ 3. 重启该 profile,然后在预设选择器里选 "Molecular Biology Lab" 新建会话。
57
+
58
+ ### 次选:命令行安装
55
59
 
56
- # 2. 重启该 profile,然后在预设选择器里选 "Molecular Biology Lab" 新建会话
60
+ 没有图形界面时(例如服务器上只用 CLI),用这条命令;它和插件页安装等价,都从 npm registry 取包:
61
+
62
+ ```bash
63
+ # web 是默认 profile 名,按需替换;装完同样要重启该 profile
64
+ dsh plugin --profile web add dsh-molbio-tools
57
65
  ```
58
66
 
59
- **升级**同样只有一条命令,不用重新复制、也不用改配置:
67
+ > 只有在改本仓库源码、还没发布到 npm 时,才需要从本地目录安装
68
+ > (路径按实际情况替换,Windows 示例):
69
+ > ```powershell
70
+ > dsh plugin --profile web add D:\path\to\dsh-molbio-tools
71
+ > ```
60
72
 
61
- ```powershell
73
+ **升级**(图形界面装的也适用)同样只有一条命令,不用重新复制、也不用改配置:
74
+
75
+ ```bash
62
76
  dsh plugin --profile web update dsh-molbio-tools
63
77
  ```
64
78
 
@@ -102,15 +116,20 @@ dsh plugin --profile web update dsh-molbio-tools
102
116
  > 如果你确实想要"工具对所有会话全局可见",那需要把工具行加到宿主层——但那不是本包的设计,
103
117
  > 且会让专属模式里的同一行变成死行(0.1.7 上表现为选择器显示"加载失败")。
104
118
 
105
- ### 只要浏览器面板(可选)
119
+ ### 只要浏览器面板
106
120
 
107
121
  如果只想要图形面板、不想让该 profile 装 60 个工具(连模式都不装),装面板专用包即可
108
- (见[浏览器面板](#浏览器面板)):
122
+ (见[浏览器面板](#浏览器面板可选))。**它和 `dsh-molbio-tools` 二选一**:两者注册同一组右栏 tab
123
+ (`dsh-molbio-tools` / `dsh-molbio-tools/papers`),同时装会重复注册并报
124
+ `tab type id ... is already registered`。同样在 **插件 → 添加插件** 里输入 `dsh-molbio-panel`,
125
+ **安装** → **立即启用**。命令行等价于:
109
126
 
110
- ```powershell
111
- dsh plugin --profile <profile> add D:\path\to\dsh-molbio-tools\packages\molbio-panel
127
+ ```bash
128
+ dsh plugin --profile <profile> add dsh-molbio-panel
112
129
  ```
113
130
 
131
+ > 从本地源码目录安装时,把包名换成 `<包目录>/packages/molbio-panel`。
132
+
114
133
  ---
115
134
 
116
135
  ## 它会做什么
@@ -159,6 +159,7 @@ benchmark/
159
159
  ├── _probe.mjs record ground truth (original suite)
160
160
  ├── _probe-all.mjs record ground truth (the rest of the catalog)
161
161
  └── _freeze-trace.mjs freeze a run's responses into the scorer's regression fixture
162
+ (`--replace id[,id]` re-records only the named entries and keeps the rest)
162
163
  ```
163
164
 
164
165
  A task's expected text lives in `tasks/`; the input that produces it lives in
@@ -12,6 +12,20 @@
12
12
  * Run this only when you WANT to move the golden fixture forward (for example
13
13
  * after deliberately changing a task). Test failures point here when a recorded
14
14
  * response stops satisfying its task.
15
+ *
16
+ * ── amending a subset (`--replace`) ─────────────────────────────────────────
17
+ *
18
+ * Re-recording the one or two entries a fix invalidated used to mean freezing a
19
+ * whole new run and discarding every other recording. `--replace` takes just
20
+ * those responses from the new report and keeps the rest, and records where each
21
+ * amended response came from in `run.amendments` — explicit provenance beats a
22
+ * fixture that pretends to be a single run:
23
+ *
24
+ * node benchmark/_freeze-trace.mjs benchmark/reports/<stamp>-suite.json \
25
+ * --replace methylation-block,qpcr-primers
26
+ *
27
+ * The replacement is a REAL recording from the named run, never a hand edit: the
28
+ * original ids, order and every untouched response survive.
15
29
  */
16
30
 
17
31
  import { readFile, writeFile } from 'node:fs/promises';
@@ -19,7 +33,16 @@ import { resolve } from 'node:path';
19
33
 
20
34
  const source = process.argv[2];
21
35
  if (source === undefined) {
22
- console.error('usage: node benchmark/_freeze-trace.mjs <report.json>');
36
+ console.error('usage: node benchmark/_freeze-trace.mjs <report.json> [--replace id[,id…]]');
37
+ process.exit(2);
38
+ }
39
+
40
+ const replaceAt = process.argv.indexOf('--replace');
41
+ const replaceIds = replaceAt === -1
42
+ ? undefined
43
+ : (process.argv[replaceAt + 1] ?? '').split(',').map((id) => id.trim()).filter((id) => id !== '');
44
+ if (replaceAt !== -1 && (replaceIds === undefined || replaceIds.length === 0)) {
45
+ console.error('--replace needs a comma-separated list of task ids');
23
46
  process.exit(2);
24
47
  }
25
48
 
@@ -29,23 +52,74 @@ if (!Array.isArray(report.results) || report.results.length === 0) {
29
52
  process.exit(1);
30
53
  }
31
54
 
32
- const out = {
33
- $comment:
34
- 'Real responses from one benchmark run, frozen so the scorer can be regression-tested without spending API budget. See test/benchmark-score.mjs for why each defect these cover was invisible to synthetic traces.',
35
- run: {
36
- recorded_at: report.startedAt,
37
- harness_version: report.harnessVersion,
38
- profile: report.profile,
39
- model: report.model,
40
- summary: { total: report.summary.total, passed: report.summary.passed },
41
- responses: report.results.map((result) => ({
42
- id: result.id,
43
- tool_calls: result.toolCalls,
44
- answer: result.answer,
45
- })),
46
- },
47
- };
55
+ const responses = report.results.map((result) => ({
56
+ id: result.id,
57
+ tool_calls: result.toolCalls,
58
+ answer: result.answer,
59
+ }));
48
60
 
49
61
  const target = resolve(import.meta.dirname, '..', 'test', 'fixtures', 'benchmark-traces.json');
62
+
63
+ /** The whole-fixture shape: one run, every response from it. */
64
+ function wholeRun() {
65
+ return {
66
+ $comment:
67
+ 'Real responses from one benchmark run, frozen so the scorer can be regression-tested without spending API budget. See test/benchmark-score.mjs for why each defect these cover was invisible to synthetic traces.',
68
+ run: {
69
+ recorded_at: report.startedAt,
70
+ harness_version: report.harnessVersion,
71
+ profile: report.profile,
72
+ model: report.model,
73
+ summary: { total: report.summary.total, passed: report.summary.passed },
74
+ responses,
75
+ },
76
+ };
77
+ }
78
+
79
+ /** The amended shape: keep the fixture, swap the named entries, say so. */
80
+ async function amendedRun() {
81
+ const existing = JSON.parse(await readFile(target, 'utf8'));
82
+ const byId = new Map(existing.run.responses.map((response) => [response.id, response]));
83
+ // Replacing the same id twice must not pile up amendment entries or repeat
84
+ // the provenance sentence: this command is re-run whenever a report is rebuilt.
85
+ const amendments = (existing.run.amendments ?? []).filter((entry) => !replaceIds.includes(entry.id));
86
+ for (const id of replaceIds) {
87
+ const fresh = responses.find((response) => response.id === id);
88
+ if (fresh === undefined) {
89
+ console.error(`--replace: the report has no result for "${id}" — nothing was written`);
90
+ process.exit(1);
91
+ }
92
+ if (!byId.has(id)) console.log(` note: "${id}" was not in the fixture; appending it`);
93
+ byId.set(id, fresh);
94
+ amendments.push({
95
+ id,
96
+ recorded_at: report.startedAt,
97
+ harness_version: report.harnessVersion,
98
+ model: report.model,
99
+ });
100
+ }
101
+
102
+ const ordered = existing.run.responses.map((response) => byId.get(response.id));
103
+ for (const id of replaceIds) {
104
+ if (!existing.run.responses.some((response) => response.id === id)) ordered.push(byId.get(id));
105
+ }
106
+
107
+ return {
108
+ $comment: existing.$comment.includes('run.amendments names each one')
109
+ ? existing.$comment
110
+ : `${existing.$comment} Some responses were re-recorded later; run.amendments names each one and the run it came from, and every other response is unchanged. A replaced entry loses any suspension marker it carried, because the replacement is a fresh recording.`,
111
+ run: {
112
+ ...existing.run,
113
+ amendments,
114
+ responses: ordered,
115
+ },
116
+ };
117
+ }
118
+
119
+ const out = replaceIds === undefined ? wholeRun() : await amendedRun();
50
120
  await writeFile(target, `${JSON.stringify(out, null, 2)}\n`, 'utf8');
51
- console.log(`froze ${out.run.responses.length} response(s) into ${target}`);
121
+ console.log(
122
+ replaceIds === undefined
123
+ ? `froze ${out.run.responses.length} response(s) into ${target}`
124
+ : `amended ${replaceIds.length} response(s) (${replaceIds.join(', ')}) in ${target}; ${out.run.responses.length} total`,
125
+ );
@@ -44,7 +44,8 @@
44
44
  },
45
45
  {
46
46
  "where": "answer",
47
- "pattern": "1490"
47
+ "pattern": "1[,.]?490",
48
+ "note": "the extinction coefficient; the same thousands-separator tolerance the MW assertion already carries (0.20.0 fixed /3935/, not this one)"
48
49
  }
49
50
  ]
50
51
  },
@@ -201,7 +201,7 @@ export function tagVersion(tag) {
201
201
  * `rc.10 < rc.2`, so a release of `rc.2` after `rc.10` would look like a
202
202
  * DOWNGRADE and the gate would refuse a legitimate publish (or, in the other
203
203
  * direction, wave through a stale one). This repository does cut prereleases
204
- * (the harness baseline itself is `0.2.0-rc.2`), so the rule is implemented
204
+ * (the harness baseline itself is `0.2.1-rc.2`), so the rule is implemented
205
205
  * rather than approximated.
206
206
  *
207
207
  * Semver's rule: split on dots; numeric identifiers compare numerically and rank
package/docs/history.md CHANGED
@@ -16,6 +16,8 @@
16
16
 
17
17
  | 包版本 | preset 版本 | 内容 | 工具数 |
18
18
  | --- | --- | --- | --- |
19
+ | 0.21.0 | — | **DSH 0.2.1-rc.2 适配**:preset 行清单逐行重抄;contract 两条断言重锚到行为;两条 suspended 记录用真实模型重录 | 60 |
20
+ | 0.20.1 | — | 插件专属图标(透明底双螺旋 SVG),运行时零改动 | 60 |
19
21
  | 0.20.0 | — | **Cas 家族几何**(SpCas9/SaCas9/Cas12a/Cas13a/b/d)+ 反向链脱靶定位修复;TaqMan MGB/订购 CSV;multiplex 退火温度/浓度;批量分析 CSV;静默位点突变;并入 0.19.1 硬化 | 60 |
20
22
  | 0.19.0 | — | 环形/扇形的**旋转多行标签**(画面正确性);新增 `molbio_alignment_postprocess`(共识/修剪/同一性矩阵/覆盖度) | 58 |
21
23
  | 0.16.0 | — | **修掉 benchmark 交出的前两条缺陷**:引物朝向、甲基化参考表(各带串联/来源回归) | 57 |
@@ -41,6 +43,36 @@
41
43
 
42
44
  ---
43
45
 
46
+ ## v0.21.0(2026-10-23,包 0.21.0)
47
+
48
+ 纯适配版:DSH 0.2.0-rc.2 → 0.2.1-rc.2。工具行为零变化,但上游动了三样本仓库**以为不会动**的东西,
49
+ 每条教训对应一道守卫。
50
+
51
+ **(1) 上游可以在 patch 级版本里重写 preset 行清单。**
52
+ 0.2.1-rc.2 的 `standard` 比 0.2.0-rc.2 少了 `tool-subagent-codex` / `tool-subagent-claude-code` /
53
+ `tool-ralph`,少了 persona 的 `suffix` 与两处 `backgroundMode`,多了 `time-context` /
54
+ `tool-schedule` / `tool-plugin-market`,还改了 plan-mode 的文案——12 处结构差异。`preset-health`
55
+ 把这些当**发布阻断**,所以第一分钟就红了;但 `drift-probe` 里那条"上游 `disabled` 被丢掉"的
56
+ 突变仍拿 `tool-ralph` 当样本,**守卫自己**先失效(上游已经没有这一行)。教训:**fixture 也要
57
+ 跟着上游走**——突变样本必须取自当前上游仍然存在、且仍为 `disabled` 的行,否则"证明了守卫会失败"
58
+ 会变成空话。
59
+
60
+ **(2) 把 contract 断言锚在 minified 字面量上,会把"上游改了写法"报成"产品坏了"。**
61
+ `dsh-tools` 的 `exec.parent === void 0` 在 0.2.1 消失:`presentationMeta` 的投影移进
62
+ `createSuccessResult`,不再按父调用过滤。面板依赖的东西(`meta` 落到成功结果上)没变,变的
63
+ 是我们测量它的方式。同源的第二条:`@deepseek-ai/dsh-client-connection` 的 client 入口从
64
+ `lib/client.js` 改成条件导出 `lib/client.browser.js`,而 Web shell 变成打包产物、不再有
65
+ `require("包名")` 字样——"seeded into the shell"这条兜底判据因此整体失效。教训:断言要问**行为**
66
+ (入口文件存在、`meta` 附在结果上),不要问上游当前的拼写;文件名与导出形状应当**读包清单**,
67
+ 而不是猜。
68
+
69
+ **(3) 两条欠账不是"记着",是"花一次 token 消掉"。**
70
+ `methylation-block` 与 `qpcr-primers` 的冻结响应停在 0.16.0 之前,`benchmark-score` 每次运行
71
+ 都点名它们。这一版用一次完整真实模型跑(61 题)重录,并以 `_freeze-trace --replace` 只替换这两条
72
+ (其余 47 条不动,`run.amendments` 记下来源),悬挂条目归零;这次跑分同时作为 0.21.0 的发布证据。
73
+ **fs 侧仍然不能写二进制**,所以 `png_path` 依旧不做——`contract` 里那条断言仍然是绿的,
74
+ 这就是"触发条件没到"的证据。
75
+
44
76
  ## v0.20.0(2026-10-17,包 0.20.0)
45
77
 
46
78
  这一版把 0.19.1 的四项硬化并入 0.20.0,并落地 2.3 / 2.5 / 2.7 / 2.8 与 survey #8。**最重要的教训是
@@ -32,7 +32,7 @@
32
32
 
33
33
  - **包**:`dsh-molbio-tools`,零依赖 DSH 插件包,**60 个 `molbio_*` 工具**(其中
34
34
  `molbio_plot` 一个工具承载 8 种图表)。
35
- - **基线**:DSH **0.2.0-rc.2**;`package.json` 版本 0.20.0。
35
+ - **基线**:DSH **0.2.1-rc.2**;`package.json` 版本 0.21.0。
36
36
  - **两条分发渠道**:根包(工具 + 面板)与 `packages/molbio-panel`(只面板),
37
37
  **两个独立的 npm 条目、两条独立的版本线**,必须分别发布。
38
38
  - **工具只在专属模式里出现**(设计如此):bundle 的 `cordis.patch.yml` 是**空列表**,
package/docs/roadmap.md CHANGED
@@ -10,12 +10,12 @@ L = 需要新机制或外部资源)。
10
10
 
11
11
  ---
12
12
 
13
- ## 1. 当前状态(0.20.0)
13
+ ## 1. 当前状态(0.21.0)
14
14
 
15
15
  - 60 个 `molbio_*` 工具,零依赖,随 preset 分发(见 [README](../README.md))。
16
16
  - 离线测试金字塔 + 组合漂移守卫全绿;`benchmark/` 提供**模型使用**维度的评估
17
17
  (见 [benchmark/README.md](../benchmark/README.md))。
18
- - 基线:DSH **0.2.0-rc.2**。
18
+ - 基线:DSH **0.2.1-rc.2**。
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 已修,套件不再依赖宿主分隔符。
@@ -27,6 +27,11 @@ L = 需要新机制或外部资源)。
27
27
  `molbio_alignment_postprocess`(共识/修剪/同一性矩阵/覆盖度)。
28
28
  - **2.3 / 2.5 / 2.7 / 2.8 与 survey #8 已在 0.20.0 交付**:批量分析 CSV、Cas12a/Cas13 家族几何
29
29
  (含反向链脱靶定位修复)、multiplex 退火温度/浓度建议、TaqMan MGB/订购 CSV、静默位点突变。
30
+ - **0.21.0 是纯适配版**:基线 0.2.0-rc.2 → 0.2.1-rc.2。preset 行清单按上游**逐行重抄**
31
+ (+`time-context` / `tool-schedule` / `tool-plugin-market`;−两个可选 subagent provider 与
32
+ `tool-ralph`;去掉 persona `suffix` 与两处 `backgroundMode`),`contract` 两条断言从上游
33
+ minified 字面量改挂到**行为**,两条 suspended 的 benchmark 记录用真实模型重录。
34
+ 工具数与插件行为零变化。
30
35
 
31
36
  ---
32
37
 
package/docs/workflow.md CHANGED
@@ -425,7 +425,7 @@ git status --short # lib/client.js 与 packages/molbio-panel/lib/clien
425
425
  `standard`** 逐行对比——0.1.7-alpha.1 起上游基线是
426
426
  `<harness>/node_modules/@deepseek-ai/dsh-web-app/presets/standard.patch.yml`
427
427
  (旧的 `@deepseek-ai/dsh-agent-presets/presets/standard/agent.cordis.yml` 已随该包一并消失)。
428
- 只应剩 `skill-filesystem`、`tool-skill`、`tool-molbio` 这三行差异。
428
+ 只应剩 `tool-molbio` 这一行差异(0.2.1-rc.2 起上游自己也带 `skill-filesystem` 与 `tool-skill`)。
429
429
 
430
430
  客户端半的改动还有两条**专门针对"会弄坏 GUI"**的确认:
431
431
 
@@ -456,15 +456,17 @@ 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.0-rc.2**)。它不会自动跟随 DSH 升级,因此每次升级
459
+ `tool-molbio`(当前基线:**dsh 0.2.1-rc.2**)。它不会自动跟随 DSH 升级,因此每次升级
460
460
  DSH 后:
461
461
 
462
462
  1. 取新版的 shipped `standard`:
463
463
  `<harness>/node_modules/@deepseek-ai/dsh-web-app/presets/standard.patch.yml`;
464
464
  2. 与 `preset/molbio-lab/agent.cordis.yml` 对比(把上游的 `config.plugins:` 块与我们的行清单
465
465
  对齐看),**逐行吸收上游改动**(新增/删除的行、注释、key 顺序、配置契约变化),只保留
466
- `skill-filesystem` / `tool-skill` / `tool-molbio` 这几处有意差异与头部注释。**照抄上游文本,
467
- 不要手写"看起来等价"的行**——上游在 0.1.6-alpha.1 用 `workflow-worker-thread` 教过一次;
466
+ `tool-molbio` 这一处有意差异与头部注释。**照抄上游文本,不要手写"看起来等价"的行**——
467
+ 上游在 0.1.6-alpha.1 用 `workflow-worker-thread` 教过一次。上游这份文件**不一定带行内
468
+ 注释**(0.2.1-rc.2 就是纯行清单):照抄即可,别把我们自己的解释混进去;`preset-health`
469
+ 只比结构,注释漂移抓不住,但下一次人工 diff 会;
468
470
  3. 跑 `node build/preset-patch.mjs` 重新生成 `preset/molbio-lab/preset.patch.yml`;
469
471
  4. 跑 `node test/preset-health.mjs` 直到 `OK`(它把任何结构差异当**发布阻断**,不只是打印
470
472
  note),再跑 `node test/drift-probe.mjs` 确认守卫仍能抓到漂移;
package/icon.svg ADDED
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <line x1="24.26" y1="15" x2="39.74" y2="15" stroke="#F59E0B" stroke-width="2.6" stroke-linecap="round"/>
3
+ <line x1="24.81" y1="24" x2="39.19" y2="24" stroke="#F59E0B" stroke-width="2.6" stroke-linecap="round"/>
4
+ <line x1="24.81" y1="40" x2="39.19" y2="40" stroke="#F59E0B" stroke-width="2.6" stroke-linecap="round"/>
5
+ <line x1="24.26" y1="49" x2="39.74" y2="49" stroke="#F59E0B" stroke-width="2.6" stroke-linecap="round"/>
6
+ <path d="M32 6C16 13 16 25 32 32C48 39 48 51 32 58" fill="none" stroke="#2563EB" stroke-width="6" stroke-linecap="round"/>
7
+ <path d="M32 6C48 13 48 25 32 32C16 39 16 51 32 58" fill="none" stroke="#0D9488" stroke-width="6" stroke-linecap="round"/>
8
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-molbio-tools",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Zero-dependency molecular-biology plugin bundle for the DeepSeek Harness: sequence analysis, primer design (incl. cross-intron qPCR primers, TaqMan hydrolysis/MGB probes with ordering CSV and multiplex compatibility), in-silico PCR, cloning simulation, SnapGene .dna parsing and plasmid maps, Sanger verification, protein tools (incl. helical wheel and hydropathy plots), methylation-aware restriction analysis with double-digest buffer checks, qPCR analysis and plotting, multiple sequence alignment that conserves every input residue, conservation analysis, sequence logos, codon-usage analysis, phylogenetic trees with bootstrap support, Newick I/O and long-name label wrapping, CpG-island/GC-skew composition analysis, read-level FASTQ QC, batch file analysis with CSV export, silent restriction-site mutagenesis, family-aware CRISPR gRNA design (SpCas9/SaCas9/Cas12a/Cas13) with off-target search, an opt-in hand-off that shows a generated plot to the model as an attached image, plus a browser-side right-sidebar panel (plasmid maps and sequence logos drawn in the Web GUI) and a literature/protocol/experiment record system.",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -11,7 +11,8 @@
11
11
  ".": "./index.mjs",
12
12
  "./client": {
13
13
  "default": "./lib/client.js"
14
- }
14
+ },
15
+ "./icon": "./icon.svg"
15
16
  },
16
17
  "scripts": {
17
18
  "build:client": "node build/client-bundle.mjs",
@@ -76,6 +77,7 @@
76
77
  "batch.mjs",
77
78
  "silent-site.mjs",
78
79
  "host.mjs",
80
+ "icon.svg",
79
81
  "build",
80
82
  "lib",
81
83
  "packages",