Skip to content

Releases: lovy2004/dsh-molbio-tools

v0.22.1

Choose a tag to compare

@github-actions github-actions released this 25 Oct 07:18

工具数不变(60);没有凭据库的组合,行为与 0.22.0 相同。 0.22.0 只从 api_key 参数与
process.env 读 key;这一版把 DSH 的凭据 seam(ctx.credentials)插到两者之间,并把环境变量
降为最后一级。

1. 环境变量为什么不够好

环境变量会被每个子进程继承——包括 agent 自己的 shell 工具:一句 echo $env:NCBI_API_KEY
就能把 key 读进对话;它还会出现在进程环境块、崩溃转储与用户的注册表环境里(Windows 的
HKCU\Environment 是明文)。对任何云端凭证这都是不必要的暴露面;NCBI 的 key 虽然只用于
归属与限速,泄漏仍意味着别人可以用你的配额、顶着你的身份发请求。

DSH 自带的凭据库(@deepseek-ai/dsh-credentials-local)就是为这件事存在的:值存在
~/.dsh/.credentials.yaml 里,不注入环境、文件仅属主可读、改动热重载,
describe() 只报告是否已设置与来源、从不回显值,而且存储值优先于启动环境。

2. 新的解析顺序

优先级 来源 说明
1 api_key 参数 单次调用显式指定;会出现在对话记录与工具卡片里
2 凭据库 NCBI_API_KEY 推荐;不进入环境,agent 的 shell 子进程读不到
3 凭据库 MOLBIO_NCBI_API_KEY 同上,插件专用命名
4 进程环境 NCBI_API_KEY / MOLBIO_NCBI_API_KEY 兼容 0.22.0 的用法;暴露面最大

实现上调用 ctx.credentials.resolve(ref)(存在性检查后才调用),因此凭据库自身的回退层
(启动环境、项目 .env、harness home 的 .env)也一并继承;provider 不存在或拒绝该引用时
静默回退到 process.env,没有凭据库的组合行为不变。

3. key 放进凭据库的三种方式

  1. 插件详情页(推荐):插件 → 已安装插件 → dsh-molbio-tools → PubMed API key 一节,
    粘贴后点 Save;见 §4。
  2. 编辑 ~/.dsh/.credentials.yaml(产品创建、仅属主可读、热重载):在 refs: 下加一行
    NCBI_API_KEY: 你的key;下一次工具调用即生效,无需重启。
  3. 在 DSH 的设置界面保存同名凭据(该界面只显示“是否已设置/来源”,不显示值)。

这不是对 agent 的硬隔离。 凭据库自己的文档写得很清楚:agent 的工具进程以同一个 OS 用户
运行,刻意去读那个文件仍然读得到。它解决的是自动泄漏:环境变量会被每个 shell 子进程
无条件继承,而凭据库不会。真正要把密钥挡在 agent 之外,需要换提供方(OS 钥匙串/KMS),
当前产品没有随附这类 provider。

4. 图形入口:插件详情页里的 PubMed API key

「改 YAML」对没有计算机基础的用户不是可接受的入口。浏览器半因此注册了一个
plugins.detail.section 条目(id: molbio-ncbi-key),只在本包的 bundle 页与它声明的行页
上渲染,其它插件返回 null:

  • 输入框是 type=password,只写不读;状态行来自 ctx.remote.credentials.describe(['NCBI_API_KEY']),
    只显示「已配置 / 未配置 / 来源」,不回显值。
  • Save 走 ctx.remote.credentials.set('NCBI_API_KEY', value),Remove 走 unset;
    保存后输入框立即清空,成功/拒绝消息原样显示(credential/rejected 的 provider 原文)。
  • 它和 CLI/工具读的是同一个凭据引用,所以两边不需要同步;没有凭据 provider 的部署会显示
    Unavailable,而工具仍按 §2 的顺序回退到环境变量。

测试中发现并修掉的一处真实缺陷(同一版内):第一版把 ctx.remote.credentials 当成普通
属性直接访问,而它是 fiber injection——官方设置页在 inject 列表里显式声明
remote.credentials。没有声明时该命名空间是 undefined,点 Save 什么也不会写(状态看起来
只是"没保存")。现在整段的注册被 ctx.inject(['remote.credentials'], …) 门控:有命名空间才
出现,没有则整段不注册(面板不受影响);写入之后还会重新 describe,若 Host 仍报"未配置"
会把这句话显示出来,而不是假装成功。

5. 边界与验证

  • 默认路径(无 key)仍是 web 检索;有 key 仍是 esearch + esummary + efetch,速率上限不变。
  • 客户端产物因此变了:lib/client.js 与 packages/molbio-panel/lib/client.js 重新构建,
    面板包 0.1.5 → 0.1.6。
  • smoke 新增三条:凭据库压过环境变量、显式参数压过凭据库、provider 抛错时回退到环境;
    原有的“无 key 不碰 E-utilities”与“坏 key 报错”保留。test/client.mjs 新增详情页座位的
    等待/注册、本包 subject 渲染、其它 subject 返回 null,以及凭据视图/失败信息三个纯函数。
  • npm test 全绿;client-bundle.mjs --check 与 preset-patch.mjs --check 与源一致。

npm entries published by this release

  • dsh-molbio-tools@0.22.1 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.6 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 06:19

工具数不变(60)。新增一条通路:molbio_pubmed_search / molbio_pubmed_abstract 可以带着
NCBI API key 走 PubMed 官方 E-utilities。
没有 key 时两条工具的行为与 0.21.1 相同——检索仍走
harness 的 web 检索,摘要仍走 ctx.web.fetch。

1. 为什么要有官方通路

原来的检索完全依赖 harness 的 web 检索服务:它返回的是搜索引擎索引到的页面,不是 PubMed 的
题录。三个具体代价:命中的 PMID 要从 URL 里猜、拿不到期刊/年份/作者/DOI 这些结构化字段、也
无法用 NCBI 的 API key 提升速率上限(无 key 3 次/秒,有 key 10 次/秒)。官方 API 是两步:
esearch(term → PMID 列表)→ esummary(PMID → 题录),摘要另走 efetch。

2. 通路选择与 key 的来源

条件 检索通路 结果字段
有 key(参数或环境变量) 官方 E-utilities:esearch + esummary PMID、标题、期刊、年份、作者、DOI,另含 total_matches
无 key 原来的 harness web 检索(行为不变) 标题、URL、snippet、从 URL 提取的 PMID

key 的优先级是 api_key 参数 > NCBI_API_KEY > MOLBIO_NCBI_API_KEY;空白值视为未提供
(不会发出 api_key= 这种被 NCBI 以 400 拒绝的空参数)。一次带 key 的检索是 2 个请求,
molbio_pubmed_abstract 是 1 个 efetch 请求。

内联传 key 会出现在对话记录和工具调用卡片里,这是参数通路的固有代价;长期使用应设环境
变量。工具的输出、渲染和错误信息都不回显 key。

3. 实现

  • papers.mjs 增加纯函数:pubmedApiKey(参数/环境变量优先级)、pubmedSearchUrl /
    pubmedSummaryUrl / pubmedFetchUrl(三个 URL 构造器,统一带 tool=dsh-molbio-tools)、
    parseEsearch(ids + count)、parseEsummaryRecords(按 uids 顺序归一化题录,
    DOI 从 articleids 里取 idtype: doi)、esummarySource(转成工具返回的 source 形状)。
  • 传输仍走 ctx.web.fetch:官方 API 不是一条绕开沙箱/同意机制的新网络通路。没有 web.fetch
    而用户又给了 key 时,工具明确报「需要 ctx.web.fetch」,而不是悄悄退回 web 检索。
  • 错误处理:NCBI 的 {"error":"API key invalid"} 会变成 PubMed E-utilities error: API key invalid,不会被当成「没有结果」——「搜不到」和「key 错了」是两件事。空结果返回
    sources: [] 与 total_matches: 0。
  • 输出 schema 扩展:backend(pubmed-eutils / web-search)、total_matches,以及 source 上的
    doi / authors / journal / year;全部为可选字段,旧调用的输出形状不变。

4. rc.3 适配:这次上游什么都没动,但仍要走完流程

  • @deepseek-ai/dsh-web-app@0.2.1-rc.3 的 standard.patch.yml 与 rc.2 逐字节一致(git diff --no-index 验证),所以 preset 行清单、cordis.patch.yml、preset-health 的期望都没有改。
  • 在 rc.3 上全套守卫全绿:preset-health(30 行)、drift-probe(14 条突变)、contract(16 项,
    含客户端入口与 presentationMeta 的行为断言)、benchmark-profile(12 行 / 60 工具)。
  • 声明:engines.dsh 仍为 >=0.2.1-rc.2 <0.3.0-0(rc.2 仍可用);dshReleases 把 rc.3 改成
    compatible;CI 的 test/release 两条工作流钉到 0.2.1-rc.3;面板包 0.1.4 → 0.1.5(清单的
    兼容声明变了,客户端产物字节未变,因此只 bump 面板版本线)。
  • 证据用 rc.3 的 CLI 重跑:docs/store-evidence.md(0.22.0 tarball 安装 → 启动 → 卸载)。

5. benchmark 的处理(以及为什么没有重跑模型)

  • 默认通路(无 key)的输出与 0.21.1 相同,bench --offline 的 61 题期望值逐条照跑。
  • literature-search 仍是 NETWORK_TASKS 里唯一的一条:它断言的是模型的最终回答(PMID、标题、
    摘要措辞),工具结果本来就是当天的 live PubMed 数据,没有可以钉住的值。
  • 带 key 的新通路由 test/smoke.mjs 覆盖,而不是 benchmark:benchmark 不能要求真实 key、
    也不该把 live 记录冻结成期望值。smoke 用 mock 的 esearch/esummary/efetch 断言了四件事——
    key 同时出现在两个请求里、题录字段解析正确、环境变量能回退、无效 key 会报错;另有一条
    「无 key 时不碰 E-utilities 传输」的对照。

6. 版本与验证

  • 根包 0.21.1 → 0.22.0;面板 0.1.4 → 0.1.5。
  • npm test(DSH 0.2.1-rc.3)全绿:smoke、画面四套、客户端四套、contract 16、drift-probe 14 条
    突变、preset-health 30 行、release 13 项、benchmark 四道守卫 + bench --offline 61 题。
  • node build/preset-patch.mjs --check 与 node build/client-bundle.mjs --check 均报告与源一致
    (rc.3 适配没有动任何生成物)。
  • 一次性 Profile:tarball 115 个文件 / 约 1.0 MB;安装 571 ms;启动 HTTP 200、38180 字节,
    __DSH_BOOT__ 含 dsh-molbio-tools/client.js&rev=a25c3919676b;卸载 421 ms,molbio 引用 0、
    lockfile 与 node_modules 无残留。

npm entries published by this release

  • dsh-molbio-tools@0.22.0 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.5 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.21.1

Choose a tag to compare

@github-actions github-actions released this 25 Oct 05:56

工具数不变(60),插件运行时行为零改动。 这一版不加任何计算能力,只把"这个包以什么形状提交"
改成 DSH STORE 的提交预检与 DSH 自身的清单契约都能接受、且不再互相矛盾的那一种。三处改动都
来自同一次上架提交:预检里能靠改包解决的两条已经解决,剩下两条的原理与请求另写在
docs/store-preset-exemption.md。

1. 两层 patch 合并成单个 cordis.patch.yml

0.21.0 的 dsh.bundle.patch 是有序数组:["./cordis.patch.yml", "./preset/molbio-lab/preset.patch.yml"]。
DSH 运行时接受这种写法(@deepseek-ai/dsh-app-boot 的 bundlePatchPaths:one file, or an
ordered list of files),官方 @deepseek-ai/dsh-web-app 自己就列了 5 个文件;但 DSH STORE 的提交
预检(scripts/check-plugin-submission.mjs 的 safePatchPath)只接受字符串,数组直接判
SUBMISSION_BUNDLE_PATCH_INVALID。两边都自称官方契约,解法不必等预检放宽:

  • build/preset-patch.mjs 现在直接产出根目录的 cordis.patch.yml,内容是两个 insert:
    molbio-client → ./host.mjs(惰性宿主锚点)在前,preset-molbio-lab(由
    preset/molbio-lab/agent.cordis.yml 生成的 preset 行)在后;顺序即应用顺序。
  • 不再生成 preset/molbio-lab/preset.patch.yml(文件已删);package.json 的
    dsh.bundle.patch 是单字符串。
  • 四个读者改为跟单文件走:test/preset-health.mjs、test/drift-probe.mjs、
    preset/install.mjs、test/preset-rows.mjs(后者在同一个文件的两个 insert 里找 preset 段,
    不假设"preset.patch.yml 才是 preset")。

这一条同时守住一条更早的教训:锚点行不能因为"文件变少了"而消失。0.15.2 的两个侧栏 tab
就是锚点丢失后静默消失的;test/client-mount.mjs 仍然逐包断言"被选中的包必须有一个宿主行
能解析到它",test/contract.mjs 仍然断言"产物里的每一处 register 都在 inject 里"。

2. engines.dsh 与逐版本 dshReleases

两个 manifest 各声明一份:

"engines": { "node": ">=26", "dsh": ">=0.2.1-rc.2 <0.3.0-0" },
"dsh": { "compatibility": { "dshReleases": {
  "0.2.1-rc.1": "unknown", "0.2.1-rc.2": "compatible", "0.2.1-rc.3": "unknown" } } }

上界写 <0.3.0-0 而不是 <0.3.0:DSH 用 semver.satisfies(..., { includePrerelease: true })
判定(@deepseek-ai/dsh-app-boot 的 unmetRequirements),带 prerelease 选项时 <0.3.0
会放行 0.3.0-alpha.1,-0 不会。rc.1 早于本包基线、rc.3 未取得也未验证,两者都只能
标 unknown——范围不等于验收:engines 是安装时的粗筛,商城的 dshReleases 记录才是
逐版本的验收结论。

面板包随之 0.1.3 → 0.1.4:它的版本线独立于根包,但清单变了,不发这个版本号就没有能承载新
声明的发布物。

3. 根 tarball 不再内嵌面板包

files 白名单去掉 packages。此前根 tarball 里同时躺着 package.json 与
packages/molbio-panel/package.json 两个 DSH 插件清单,还重复携带 427 KB 相同的 lib/client.js;
商城把"一个包里出现第二个插件清单"视为提交歧义是对的。现在根 tarball 只有一个插件清单:
118 → 115 个文件、约 1.1 MB → 约 999 kB(多出的 1 个文件是本次提交的
docs/store-preset-exemption-issue.md)。面板没有因此消失——根包的 lib/client.js 本来就带着它
(第 4 节的启动回读可以作证);面板包继续按自己的地址单独发布。

test/contract.mjs 的打包断言随之改向:不再要求 files 必须包含 packages/,而是断言
一个 tarball 里恰好只有一个 DSH 清单(把 packages 加回白名单会立刻 FAIL)。

4. 一次性 profile 证据(tarball 就是发布物)

证据记录在 docs/store-evidence.md,用临时 DSH_HOME 重跑,不碰真实
~/.dsh:npm pack → --from-default-profile web 建 profile → plugin add tarball →
--dump-config(molbio-client 解析到已安装包内,preset 行带 tool-molbio)→
--no-open --port 0 启动 → 带 token 回读页面(HTTP 200,__DSH_BOOT__ 图里有
dsh-molbio-tools/client.js)→ plugin remove → 再 --dump-config(molbio 引用 0 处)。
装 tarball 而不是目录,是因为 tarball 同时证明 files 白名单带全了运行文件。

5. 未做 / 边界

  • 0.2.1-rc.3 没有验证,因此标 unknown。拿到该版本后按
    docs/workflow.md 第 4 节的升级流程重跑 preset-health / drift-probe /
    一次性 profile,再把它改成 compatible。
  • 预检的另外两条(保护命名空间、身份一致)不是改包能解决的:本包已自行消掉
    SUBMISSION_BUNDLE_PATCH_INVALID;剩下两条的证据、代码位置与三个可执行请求写在
    docs/store-preset-exemption.md,可直接贴到 issue 的短版在
    docs/store-preset-exemption-issue.md。
  • 工具行为与 benchmark 一律不动:60 个工具的输出与 0.21.0 相同,按规则 6 的"没变就不必跑
    模型";bench --offline 仍在 npm test 里,期望值照样逐条校验。

6. 版本与验证

  • 根包 0.21.0 → 0.21.1;面板 0.1.4(见 §2)。
  • npm test 全绿:smoke、画面四套、客户端四套、contract 16 项、drift-probe 14 条突变、
    preset-health 30 行、release 13 项、benchmark 四道守卫 + bench --offline 61 题。
  • node build/preset-patch.mjs --check 与 node build/client-bundle.mjs --check 均报告与源一致。
  • node build/release-preflight.mjs --tag v0.21.1:本地不变量全过;npm 上 dsh-molbio-tools
    已有 0.21.0、本版按 publish 处理;面板 0.1.4 尚未发布。
  • 文档同步:maintainer 的版本号、roadmap 的当前状态、history 的版本一览与本段、store-evidence
    的 tarball 名与文件数。

npm entries published by this release

  • dsh-molbio-tools@0.21.1 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.4 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 13:23

工具数不变(60),插件行为零改动。 这是一次"只跟上游走"的版本:0.20.1 之后本机 harness
升到 0.2.1-rc.2,npm test 立刻红了三处——组合漂移、契约断言、以及一条依赖旧 fixture 的
漂移探针。三处都不是插件退化,但每一处都属于守卫在说谎,所以按发布阻断处理。

1. preset 行清单按 0.2.1-rc.2 逐行重抄(12 处结构差异)

  • 上游 standard 新增 time-context、tool-schedule、tool-plugin-market;
  • 删除 tool-subagent-codex / tool-subagent-claude-code(两个 disabled: true 的可选
    provider)与 tool-ralph;
  • 去掉 persona 的 suffix: Your working directory is {{cwd}}.,以及 tool-subagent /
    tool-subagent-fork 的 backgroundMode: continuable;
  • plan-mode 的段落文案改为 "until the user approves your plan through exit_plan_mode…"。

agent.cordis.yml 现在是上游 config.plugins: 块的整体去缩进 + 末尾一行 tool-molbio;
0.2.1 的上游文件不带行内注释,因此行内注释随之消失,来源与升级规则集中写在文件头部。
preset.patch.yml 由 build/preset-patch.mjs 重新生成。

2. test/contract.mjs:两条断言不再锚在 minified 字面量上

  • client 入口:改为按包自己的 exports["./client"](browser 条件)解析。
    0.2.1-rc.2 把 @deepseek-ai/dsh-client-connection 移到 lib/client.browser.js,旧断言
    硬编码 lib/client.js,会把一个加载正常的包判死;同时 Web shell 已是打包产物、不再出现
    require("包名"),"seeded into the shell" 兜底只剩代码路径。新增两个对照:没有
    ./client 导出的包必须解析为 undefined,以及移动过的入口必须解析到真实文件。
  • presentationMeta:0.2.1 删除了 exec.parent === void 0 闸门(投影移进
    createSuccessResult,对每次成功执行都做)。断言改挂到卡片真正消费的东西:投影被
    snapshotProjection 记录、并以 meta 附到成功结果上。

3. drift-probe:突变样本换成上游仍存在的行

上游删掉 tool-ralph 后,那条"上游 disabled: true 被丢掉"的突变在空转(withRow
找不到目标,什么也没改,守卫却"通过")。改用上游仍以 disabled: true 发布的
tool-plugin-manager;14 条突变全部仍能被抓住。

4. benchmark:profile 重建、两条欠账清零

  • preset 新增的两行宿主可承载行(time-context、tool-schedule)进入 headless profile,
    benchmark/profile.mjs 重建 molbio-bench:12 行宿主行,仍注册 60 个工具。
  • methylation-block 与 qpcr-primers 的冻结响应停在 0.16.0 之前,benchmark-score 每次
    都点名。本版用一次完整真实模型跑重录,并用 _freeze-trace.mjs --replace 只替换这两条,
    其余 47 条保持原样;run.amendments 记录每条替换来自哪一次跑分。fixture 现为
    49 条记录、0 条 suspended。
  • 完整跑分(2026-10-23,DSH 0.2.1-rc.2,61 题):58/61 通过,tool selection 60/61、
    arguments 60/61、final answer 59/61;11.2M tokens / 2161 s。
    • protein-props 的失败是断言不认千分位逗号(答案写 1,490,断言写 /1490/)——
      与 0.20.0 修过的 /3935/ 同一类,当时漏了这一条。已改为 1[,.]?490,--replay 后
      59/61。
    • 剩余两条是真实的模型遗漏,不是产品缺陷:dilution-math 答案正确(50 µL + 950 µL)
      却从未调用 molbio_lab_math;batch-analysis 写出了 CSV,但最终回答只说"报告已生成",
      没有报出指令要求的长度/特征数。
  • _freeze-trace.mjs 新增 --replace id[,id…]:只重录被修复作废的条目,其余记录与顺序原样
    保留,并在 run.amendments 里写明每条替换的来源跑分——替换的是真实录制,不是手改。

5. 未做 / 边界

  • png_path 仍然不做:contract 的"工作区仍不能存二进制文件"断言在 0.2.1-rc.2 上仍然
    通过
    ,说明上游 fs 缝依旧是 text-only;触发条件未到。
  • 面板版本保持 0.1.3:lib/client.js 逐字节未变,release-preflight 复核为
    "already published with identical bytes",因此不 bump。

6. 版本与验证

  • 根包 0.20.1 → 0.21.0;DSH 基线钉 0.2.1-rc.2(.github/workflows/{test,release}.yml、
    preset 头注、docs/workflow.md、docs/roadmap.md、docs/maintainer.md)。
  • npm test 全绿:contract 16/16、drift-probe 14 条突变、preset-health 30 行全过、release
    13 项、benchmark profile/score/coverage/changed、bench --offline 61 题。
  • 文档:roadmap 状态、history 的 v0.21.0 段(三条教训)、workflow 的升级提示同步。

npm entries published by this release

  • dsh-molbio-tools@0.21.0 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.3 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.20.1

Choose a tag to compare

@github-actions github-actions released this 21 Oct 00:39

工具数不变(60),运行时零改动;包内只多一个 746 B 的 SVG。

1. 插件管理器为什么一直显示默认插画

DSH 的插件管理器从包根的清单 icon 或导出的 ./icon 读取展示图,读到的字节会转成
base64 data URL 交给 PackageArtwork;两者都没有时保留面板默认插画。本包此前既没有
icon 字段也没有 ./icon 导出,因此卡片和插件行一直是默认图——不是缓存,也不是
安装坏了,而是展示元信息根本没声明。

2. 图标:透明底双螺旋,按 30/36 px 的展示尺寸设计

icon.svg(64×64 viewBox,746 B)是原创的双螺旋:青绿 #0D9488 与蓝 #2563EB
两条主链、四条琥珀 #F59E0B 碱基对,圆头描边、无渐变/字体/外部引用。透明底是刻意的:
插件管理器把它放进 40×40(行)和 48×48(卡片)的圆角框里,图本身再加一层背景会变成
“框中框”;不留背景也让同一张图在浅色和深色主题下都成立。主链可视范围约 28×58(64 单位
画布),在 36 px 卡片和 30 px 行图标上仍能读出双螺旋,20 px 时退化为可辨的轮廓。

3. 接入:导出、白名单,以及一个 gitignore 例外

  • package.json 的 exports 增加 "./icon": "./icon.svg",files 白名单加入
    icon.svg;本包 files 是白名单而不是默认全带,漏了它 npm pack 就会静默丢掉图标。
  • 根目录的 /*.svg 规则原本用来挡住工具生成的图(logo.svg、质粒图等),新增
    !/icon.svg 例外,图标才能作为源文件提交;git 忽略与 npm files 是两套规则,不能只改
    一处。
  • 不新增依赖、不改 dsh.client、不改 preset 行清单;已安装的 profile 是指向本目录的
    junction,重新读取 manifest 即可看到,不需要重装。

4. 验证

  • createRequire(...).resolve('dsh-molbio-tools/icon') 解析到包内 icon.svg;
    npm pack --dry-run --json 的 118 个文件中包含它。
  • 用本包自己的 svgpng.mjs 光栅化:64×64,unsupported: [];另按 36/30/20 px
    下采样检查过轮廓。
  • test/contract.mjs 16/16;test/release.mjs 13/13(本机沙箱下按该测试自带的
    npm_config_user_agent 兜底跑通)。
  • 根包 0.20.0 → 0.20.1;面板 0.1.3 不动(lib/client.js 未变,发布前置检查不会
    要求面板 bump)。标题/描述仍回退到 package.json 的包名与长描述;本次没有新增
    locale/*.json,如果要本地化展示文案,应作为独立改动。

npm entries published by this release

  • dsh-molbio-tools@0.20.1 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.3 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.20.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 15:44

工具数 58 → 60,benchmark 56 → 61 题(60/60 覆盖)。 这一版把 0.19.1 计划的四项硬化直接并入,
并落地 roadmap 2.3 / 2.5 / 2.7 / 2.8 与 survey #8。Cas12a/Cas13 不是"再加一个 PAM 字符串":
原来的 findProtospacers 把 3' PAM、20 nt 和 3' 种子写死在几何里,对 Cas12a/Cas13 会给出生物学上错误的
坐标与评分,因此这一版把家族几何做成一张表,并补上了反向链脱靶定位的静默错误。

1. 并入的 0.19.1 硬化(原计划单发 patch,现随 0.20.0 一起)

  • Smith-Waterman 的 DP 矩阵上限:molbio_align 在分配前拒绝超过 50,000,000 cells 的输入对,
    而不是让两个几百 kb 的序列把宿主进程的内存吃光;工具描述同步说明"对齐窗口或改用
    molbio_verify_sanger"。test/smoke.mjs 用 10 kb × 10 kb 的输入证明拒绝发生在分配之前。
  • SnapGene/PubMed XML 数值实体:&#999999999; 曾让 String.fromCodePoint 抛出 RangeError
    并逃出整个解析器;现在越界码点与代理项按字面文本保留,合法实体(含星形平面)照常解码。
  • molbio_sequence_logo 的并发声明:它写共享默认路径 logo.svg,之前却默认 safe;
    现与其他画图工具一致声明 safe: false。
  • npm files 白名单:不再整目录带 benchmark,改为 benchmark/*.mjs、benchmark/tasks、
    benchmark/README.md,因此 .gitignore 里的本机 benchmark/reports/ 不会再被打进包
    (此前约 1.4 MB)。test/contract.mjs 直接禁止裸 benchmark 条目。

2. roadmap 2.5:Cas12a/Cas13 家族几何(以及反向脱靶定位缺陷)

  • crispr.mjs 新增 CAS_FAMILIES 表:SpCas9(3' NGG / 20 nt / 3' 种子)、SaCas9
    (3' NNGRRT / 21 nt)、Cas12a(5' TTTV / 23 nt / 5' 种子)、Cas13a(RNA / 3' H PFS / 28 nt)、
    Cas13b(RNA / 5' D + 3' NAN / 30 nt)、Cas13d(RNA / 无 PFS / 22 nt)。PAM 校验现在按
    PAM 的哪一侧贴近 protospacer 检查简并位,而不是一律要求首位为 N——TTTV 因此不再被误拒。
  • Cas13 不是 PAM:它有 PFS 偏好,工具报告 PFS、按缺失扣分并明确说明"不是硬性 PAM";
    只有 require_pfs=true 才过滤。Cas13 的 tm_celsius 返回 null,因为对 RNA guide 报 DNA
    NN Tm 是另一个分子;工具描述与渲染都写明这一点。
  • 反向链脱靶定位是真 bug:索引把每个 k-mer 记在 top-strand 起点,旧 finalizeSites 却对
    反向命中减去 guideLength-1,使它们整体偏移 19 nt 并大多越界丢弃——正向 fixture 永远看不见。
    现在两种链都从 entry.position 起算,PAM 按 5'/3' 侧与链向取 top-strand 窗口;新增的
    smoke fixture 在 forward guide 与 reverse perfect copy 之间互指,断言反向脱靶 start/end/pam
    与 0 mismatch。
  • 新 benchmark:crispr-cas12a(5' TTTV、23 nt、坐标 9-31)与 crispr-cas13(RNA、PFS A、28 nt)。
    test/smoke.mjs 另钉住 Cas12a/Cas13a 的几何、PFS 过滤与错误路径。

3. roadmap 2.8:TaqMan MGB 与订购 CSV

  • probe_chemistry: 'mgb' 使用 13-20 nt、未修饰 NN Tm 48-60 °C 的窗口;MGB 修饰的 Tm 不被
    NN 模型预测
    ,因此报告的是未修饰 Tm,并以 tm_model 与 notes 明确要求向供应商复核。
    MGB 不施加"探针 Tm 高于引物"的未修饰差值罚分(那会把每个 MGB 候选都罚掉)。
  • 新增 taqmanOrderRows / taqmanOrderCsv:每条 assay 三行(F/R/P),列含 5'/3' 修饰、规模、
    纯化与说明;MGB 默认 NFQ-MGB,5' 默认 FAM。save_path 写 CSV;不传则只返回结构化 order。
  • 标准 chemistry 的默认值、排序与已发布数值保持不变(smoke 的 9 assays 与 pinned probe 仍绿)。

4. roadmap 2.7:多重 PCR 的退火温度与浓度配平建议

  • checkMultiplex 新增 annealing:最低引物 Tm − annealing_offset_c(默认 3 °C),
    给出 ±annealing_window_c 范围、最低 Tm 引物、Tm 离散度与是否超过警告阈值。
  • 新增 primer_concentrations 与 concentration_note:默认 200 nM;当扩增子大小比 ≥ 2 时,
    最短的降到 150 nM、最长的升到 300 nM,并明确这是起始点而非实测最优。
  • 这些是清晰标注的规则性建议,notes 写明"不是热循环优化,需跑梯度"。

5. roadmap 2.3:批量分析 + CSV/TSV

  • 新模块 batch.mjs 与新工具 molbio_batch_analyze:扫描目录(可递归)或显式 paths,复用
    同一批 FASTA/FASTQ/GenBank/SnapGene 解析器,输出一张 CSV/TSV。
  • 每行:格式、拓扑、条目数、总长、GC%、N%、特征数、平均长度;analysis: orf 加 ORF 数/最长
    ORF,analysis: enzymes 加指定酶的位点数,all 两者都加。单个坏文件变成一行 error,
    其余文件继续;文件/字节上限与 truncated 都显式报告。
  • 拓扑按 GenBank/SnapGene 声明优先(环状质粒的位点计数不会被当成线性);无声明时才用
    circular 参数。新 benchmark batch-analysis 同时读 pUC118.gb 与 pUC118.dna。

6. survey #8:静默酶切位点突变

  • 新模块 silent-site.mjs 与新工具 molbio_silent_site_mutagenesis:只在同义密码子内枚举,
    因此蛋白序列按构造不变;输出逐碱基变化、密码子前后与氨基酸、可直接喂给
    molbio_mutagenesis_primers 的 mutation texts、以及突变后序列。
  • 支持 remove/add、frame 1-3、可选 host(按 e_coli/yeast/human 密码子偏好排序)与区域限制;
    只接受识别序列无简并碱基的酶(GGTNACC 这类直接拒绝而不是猜)。
  • 新 benchmark silent-site 钉住 EcoRI 的 C12T(TTC→TTT,Phe)与突变后序列。

7. 版本、基线与文档

  • 根包 0.19.0 → 0.20.0,面板 0.1.2 → 0.1.3(客户端产物因 crispr/taqman/multiplex 改动而变,
    两个 npm 条目需分别发布)。
  • DSH 基线 0.1.7-rc.1 → 0.2.0-rc.2:.github/workflows/test.yml 的 pin、roadmap/maintainer/
    workflow 与 preset 头注释同步;preset-health 在 0.2.0-rc.2 上仍为 30 行全过(行清单无需改动)。
  • README/preset 描述/工具表/benchmark 计数同步到 60 tools / 61 tasks;新增模块进入 npm
    files 白名单与目录树。
  • 未做:benchmark-score 里 2 条 0.16.0 之前的冻结响应(methylation-block、qpcr-primers)
    仍是 suspended;本次没有用真实模型重录,测试输出会继续点名这两条,而不是假装它们已通过。

8. 发布前完整 benchmark(2026-10-17,真实模型)

  • 61 题首跑 55/61:tool selection 61/61,arguments 59/61,final answer 57/61;
    12.7M tokens / 1747 s。
  • 新功能任务全部通过:taqman-mgb、crispr-cas12a、crispr-cas13、silent-site、
    batch-analysis。multiplex-panel 在把"退火温度/浓度"明确写进指令后复跑通过。
  • 4 个失败是断言写死格式,不是产品缺陷:plasmid-digest(模型写 2,840)、
    protein-props(3,935)、codon-optimize(EcoRI-free)、chart-volcano
    (任务要求 de.svg,断言却写 volcano.svg)。修正后 --replay 四题全部转为 PASS。
  • 1 个真实可用性缺陷:molbio_extract_region 的 schema 广告 sequence,但
    resolveVector 只读 vector,于是文档里写的参数被静默忽略、调用报
    "provide either vector or vector_path"。现在 vector/sequence/reference 三个已声明别名
    都接受,smoke 增加回归断言。该任务的真实模型复跑因 DSH 沙箱对 headless 子进程的审批限制
    未完成,因此发布时的实测口径是 60/61(这一题以 smoke 回归 + 修复代码为准,而不是假装已跑)。
  • benchmark 工具本身未改;全量首跑使用了一次性更宽沙箱来启动 headless DSH 子进程。

npm entries published by this release

  • dsh-molbio-tools@0.20.0 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.3 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.19.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 12:28

工具数 57 → 58。 这一版是 roadmap 2.4 与 2.2 两条候选的落地:
前者修一个画面正确性缺陷,后者补一个比对之后该问的问题;另外把发布本身做成了 CD。

1. 环形/扇形布局的长叶名曾整行画出画布(roadmap 2.4)

症状:molbio_phylogenetic_tree 的 rectangular 布局早在 v20 就把长叶名折成
<tspan> 多行,而 circular / fan 一直是整行旋转着画出去的。位置靠
labelLength * leafFontSize * 0.62 这个猜出来的余量决定,名字比它长就直接跑到画布
外面——没有任何裁剪、也没有任何提示,只是墨迹落在框外。

修法(三层):

  • font-metrics.mjs 新增 rotatedDepth(width, angle, height):一段文字绕基点转 θ 后
    在原竖直方向上占的深度是 W·|cos θ| + H·|sin θ|。这个数是"标签还剩多少地方"
    的度量,也是折行预算与旋转之间唯一正确的换算。
  • svgio.mjs:textSpanLines 增加 rotate——transform 加在父 <text> 上,而不是
    <tspan> 上。这不是选择:光栅化器把 <text> 的 transform 应用到它排出的每一行,而
    对 <tspan> 自带的 transform 是计入 unsupported 并上报(不猜位置)。wrapTextLines
    增加 depth / angle,把深度预算按 1/cos θ 换算回基线宽度。
  • phylo.mjs 的径向分支:折行 → 以锚点自身为旋转中心旋转整块 → 用四个角是否都
    落在绘图矩形内
    做精确判定 → 不满足就逐级缩小字号。半径不再是面板的固定比例,而是
    由实测的标签深度反推(halfExtent - deepestLabelDepth)。

实现时才暴露的四个偏差(计划里没有的,每一个都是量出来的、不是想出来的):

  1. 余量公式的坐标系搞错了。"标签沿半径还有多少地方"是旋转后局部 x 轴上的参数,
    不是页面上的像素距离;把两者混着算,就会在接近竖直的方向算出 1.4 px 而真实答案是
    149 px——于是每个折行预算都被夹到下限,长名字变成 6 px 的窄列。
  2. axisDistanceToEdge 的分子写反了:dy < 0(朝上)时用了下边界的距离。这条
    与上面那条同源,症状也一样。
  3. 折行宽度不能随角度变。试过三种角度相关规则,全部实测失败并被记录在代码注释里:
    按深度预算折(有的角度窄列、有的角度长行)、按射线余量折(竖直方向射线极长,50 字符
    名字折成九行柱)、以及最小化 W·|cos θ| + H·|sin θ|(数学上的极值,也是窄列)。
    眼睛要的是每个角度都差不多长的几行,所以宽度是固定的,随角度变的只是行数,
    由角点判定与缩字循环处理。
  4. 旋转文字的墨迹比几何宽度宽约 1 px。光栅化器用 max(0.7, size·0.11) 宽的圆角描边
    画字形,所以预算必须再留 strokeMargin + LABEL_INSET——这是逐标签量像素发现的,
    靠推公式发现不了。

回归守卫(test/svgpng.mjs,22 → 23 项):12 个叶、50 字符名字的树,断言每个标签都是
多行 <tspan> 块、每行共享同一个基线 x、非水平方向的标签真的带 rotate 与正确的
anchor,并且没有任何标签墨迹进入面板边框带——后者与"只有面板装饰"的同结构文档比对,
所以标题和边框不会被误当标签。这条守卫能证明自己会失败:把 anchor 全部强制成
start(v19 的症状)即红。同一批改动里还试过"关掉缩字循环"与"去掉余量内缩"两种变异,
前者不红——因为 12 个叶的 fixture 在 11 px 本来就放得下——于是把 fixture 从 8 个叶改成
12 个,并改用请求 15 px 的对照来钉住"能放下就不缩"这一侧。

2. 新增 molbio_alignment_postprocess:比对之后该问的四件事(roadmap 2.2)

新模块 align-post.mjs(纯计算、零依赖,IUPAC 一律从 lib.mjs 取,不新增第八份字母表):

  • 共识,三种口径由 consensus_mode 选:plurality(默认,得票达
    consensus_threshold 就给该残基,否则给观察到的碱基的并集码)、union(一律并集码)、
    strict(达阈值给残基,否则给 N——混合位点绝不能伪装成一个碱基)。平票取字母序
    首个
    ,与 conservationAnalysis 一致,是确定性约定而不是并集码。
  • 按缺口比例修剪:max_gap_fraction(默认 0.5)以上的列被报出,列号是修剪前的
    原始 1-based 坐标
    ,所以与输入仍然对得上;只报"哪些列会走、还剩多少列",
    不返回修剪后的比对。
  • 两两同一性矩阵:分母是"两行中至少一行有残基"的列,双缺口列被排除。
  • 逐列覆盖度:非缺口比例、均值/最小值、完全覆盖列数、低于 coverage_threshold
    (默认 0.8)的列清单。

为什么另起一个工具而不是扩 molbio_conservation:其中两件事需要 msa.mjs 刻意
没有的语义,改动它就会悄悄挪动已发布版本报出的数字。文档与渲染文本都点名了这一点:

统计量 本工具 molbio_conservation
同一性分母 非双缺口列 全部列
同一 fixture 的均值 37.17% 32%
逐列 identity 均值 —— 70.34%(第三个不同的统计量)

test/smoke.mjs 把这三个数并排钉住,附逐对手算表(1v2 80、1v3 60、1v4 40、2v3 75、
3v4 66.67…),因此"两套口径合并"会红。超 50 条序列时矩阵省略而非截断,摘要仍覆盖每一对。

benchmark:新增 alignment-post 任务(56 题,58/58 覆盖)。它刻意钉住两个阈值互相
独立
:fixture 里 103 列低于 0.8 覆盖度,但缺口比例只有 0.25,默认阈值下一列都不修剪
——把两者混为一谈的模型会报出一个被修剪的比对。期望值全部由 benchmark/_probe.mjs 实测,
两条离线门(--offline 与 benchmark-coverage)全绿。

4. 持续交付(CD):推一个 tag 就发完

首发必须手动做一次,这是 npm 可信发布的固有限制,不是配置错误。 推 tag 跑 CD 会以
npm error code ENEEDAUTH / need auth 失败——Trusted Publisher 是逐包在 npmjs.com 上
配置的,包名还不存在时 npm 没有可认证的对象。0.19.0 的第一次真实运行就是这样失败的。
更值得记的是当时的 preflight 已经检测出"包不存在",却只打印一条 note 就放行:一个检测到
条件却不据此行动的守卫不是守卫,只是一行日志。现在 publishPlan() 把每条注册表状态落成
publish / skip / refuse 三个动作,首发与"npm 已有更新版本"都是 refuse,workflow 的发布
步骤读 steps.preflight.outputs.publish_*,于是在碰注册表之前停下并打印准确的首发步骤。
完整顺序(含必要时如何安全地强制移动 tag)见 workflow.md 第 4 节。

.github/workflows/release.yml 把「发布流程」第 4 节那套手工步骤变成流水线。触发条件是推
v* tag
,不是推 main——版本号就是"这是一次发布"的决定,而 tag 是唯一不可能被顺手推出去的东西。

流水线在被打 tag 的那个提交上重跑全套 npm test(tag 可以指向任何提交,而 test.yml 是被
并行触发的,所以"测试过了"不是这个 job 能假设的前提),再过发布前置检查,然后分两步发布
两个 npm 条目(在仓库根跑两次 npm publish 会把根包发两遍——这是 v20 已经记录过的坑),
最后把 CHANGELOG 里该版本那一段抽出来建成 GitHub Release。

凭据是没有凭据:用 npm 的 Trusted Publishing——job 出示 GitHub OIDC token
(id-token: write),npm 换成一张限于本仓库 + release.yml 的短期凭证。仓库里不存在任何
长期密钥。代价是 npm 必须先知道这个包,所以两个包的首次发布必须由人手动做一次,然后在
npmjs.com 上为每个包配置 Trusted Publisher。三条坑都写进了守卫或文档:

  • 设了 NODE_AUTH_TOKEN 反而会关掉可信发布(npm 见到 token 就不走 OIDC),所以 workflow
    故意不设它,test/release.mjs 直接断言文件里没有对它的赋值、也没有任何 secrets. 引用;
  • 首发时 npm publish 的报错是 404,看不出真正原因——所以 release-preflight.mjs 先问注册表
    "这个包存在吗",包不存在就明确说"这是首发,必须手动做一次";
  • 面板包的版本线是独立的(v20 计划:0.1.1 → 0.1.2),但两者共用同一份客户端源码,所以
    浏览器半一改面板产物就变,版本不动就会被 npm 以"同版本不可重复发布"拒绝。preflight 因此把
    已发布版本的 lib/client.js 通过注册表 tarball 抓下来,在进程内解 tar,与工作区逐字节比对。

新增 build/release-preflight.mjs(只检查只有发布时才会问的问题;产物新鲜度与 files
白名单已经在 test/contract.mjs 里,不重复)、build/release-notes.mjs(发布说明从 CHANGELOG
派生,不存在第二份描述)与 test/release.mjs(把每个决定函数用变异输入驱动:tag 写错、
版本漂移、CHANGELOG 缺段、面板版本没 bump、tar 读错文件、根变成 workspace、workflow 里出现
token——见「守卫必须被证明会失败」)。

刻意不做:自动版本号(版本是人的决定)、自动回滚(npm 的 unpublish 有 72 小时窗口且会
破坏已装用户;正确做法是发更高版本,必要时 npm deprecate 标记坏版本)。

3. 文档漂移与平台(0.18.4 的同类,本次一并结清)

  • font-metrics.mjs 被两处现行规则文档写成"宿主侧专用、不进客户端产物"——它明明在产物里。
    build/client-bundle.mjs --check 列出它在 19 个模块之中,test/contract.mjs 断言它
    必须在模块图里,而它自己的头注释就写着"This module IS part of the client bundle"。
    已改正 docs/rules.md 第 5 节(并写明照错的声明去"修好"它就会删掉一个面板必需的模块)、
    docs/maintainer.md 与 README.md 的目录树。历史文档(CHANGELOG、v19-plan、
    v20-plan)保持原样。
  • docs/maintainer.md 的包版本 0.18.3 → 0.19.0。
  • CI 的 node 矩阵、engines.node 与该处注释三处一致,都是 26(本机开发版本),
    不再是"矩阵测下限、注释说下限、engines 写下限"的三方各说各话。
  • 工具数变化同步的四处(README 表与计数、preset.yml 描述、index.mjs 顶部注释、
    agent.cordis.yml 的 tool-molbio 注释)与 benchmark 任务数(55 → 56)一并更新。

npm entries published by this release

  • dsh-molbio-tools@0.19.0 — npm install -g dsh-molbio-tools
  • dsh-molbio-panel@0.1.2 (the panel package; its version line is independent of the root package) — npm install -g dsh-molbio-panel

Install in the harness — dsh plugin --profile <profile> add dsh-molbio-tools; the tools load only in the Molecular Biology Lab mode. See README.md for the panel-only package and the manual install path.

Full entry — CHANGELOG.md.

v0.7.3 — 真机验证:面板与图谱调用卡在页面里确认可用

Choose a tag to compare

@lovy2004 lovy2004 released this 11 Sep 11:01

内容

本版本没有产品改动,只增加两件东西:真机验证的记录,以及做这次验证的工具。

  • build/cdp.mjs:最小的 Chrome DevTools Protocol 驱动(导航 / 求值 / 截图),
    基于 Node 自带的全局 WebSocket——本仓库没有浏览器自动化依赖,而"页面里到底长什么样"
    是任何 Node 侧测试都回答不了的问题。
  • build/cdp-drive.mjs:把提示词真正打进输入框、发送、等到这一轮结束,再回报对话内容。
    一个坑值得记下来:composer 是 contenteditable 的 div,textContent = … 不会被
    React 看到(发送按钮会一直是禁用的),必须走 document.execCommand('insertText')。

真机验证结论(专用 profile:dsh-base + dsh-web-app + dsh-molbio-tools,独立实例与 token)

  1. 启动正常:页面 boot 成功,boot graph 含本包,没有 Failed to load plugins
    ——即 v0.7.1 的故障面不复现;
  2. 图谱调用卡:驱动一次真实对话调用 molbio_plasmid_map,卡片在对话里画出图谱——
    摘要 pCARD-LIVE · 212 bp · circular · 1 feature(s) · 2 cut mark(s)、
    1 个 viewBox="0 0 840 840" 的 SVG(520×520 渲染)、以及写入路径;
  3. 右栏面板:引导页出现 Molbio 与 Papers 两个胶囊及其描述。

工具数量不变(46 个);客户端产物与 v0.7.2 逐字节相同。

安装请使用 v0.7.3(或 v0.7.2)。不要使用 v0.7.1:它会让 Web GUI 拒绝启动。

v0.7.1 — ⚠️ DEPRECATED(会弄坏 Web GUI 启动,请用 v0.7.2)

Choose a tag to compare

@lovy2004 lovy2004 released this 11 Sep 10:52

Warning

不要安装这个版本。 v0.7.1 会让整个 DSH Web GUI 拒绝启动(顶栏 HARNESS / Failed to load plugins)。
请使用 v0.7.2 或更新版本。

出了什么问题

v0.7.1 为图谱调用卡注册 tool.call.toolview 座位时用了裸 slots.register。该座位是
dsh-client-ui-tool 的 conversation.chat.node entry 声明的子座位,只在其声明之后才存在;
本包与那个包的 entry 之间没有加载顺序保证,于是注册早于声明,抛出:

slot "tool.call.toolview" is not declared (a parent entry's children table must declare it)

异常从插件浏览器半的 apply() 逃出 → 客户端加载器把该 entry 标为失败 → shell 中止 boot,
整个 Web GUI 无法启动。工具侧(46 个 molbio 工具)不受影响;从 profile 里移除该插件行并重启即可恢复。

修复

v0.7.2(提交 aba2c0b)把全部座位注册改为
ctx.effect(() => ctx.slots.inject(座位, () => ctx.slots.register({ name: 座位, … }, 组件)), 标签),
即官方所有客户端包使用的"先等声明、再注册"形状。

防复发(0.7.2 起生效)

  • test/slots-stub.mjs:复刻 shell 的 SlotCore 守卫与 inject 的等待/重放语义;
  • test/client.mjs:从零座位已声明的最坏顺序启动,断言 apply() 不抛、调用卡在 ui-tool 声明前保持等待;
  • test/contract.mjs:第 10 项钉住"产物里每一处 slots.register 都落在对应座位的 slots.inject 里",
    第 11 项(后续补)每次重建产物并比对哈希,堵住"改了源码忘重建产物"。

完整事故记录(症状/根因/定位过程/验证/防复发):仓库 D:\deepseek-harness\崩溃历史\2026-09-11-tool-call-toolview-seat-race.md。