DSH plugin 运行时不变式:InvariantInstaller 与启动自检怎么写

概念与架构发布于 2026-10-17作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH pluginInvariantInstaller运行时不变式自检invariants
DSH plugin 的 ctx.invariants 怎么用:InvariantInstaller 的 ctx 与 fail、包名保留与子 fiber 回滚、allowlist/blocklist 正则选择,以及空安装器的配套约定。

DSH plugin 的运行时不变式,是包给自己立的「运行期必须始终成立」的规矩,由 ctx.invariants 统一注册与归因。 它明确不属于三包能力 seam,也不在 agent loop 主干里——它是一个 support 组服务:注册表负责选择哪些包启用、保留包名、管理子 fiber、把失败归因到具体包;每个工作区包各自发布一个 ./invariant 配套插件,用自己确切的 npm 包名注册检查。最容易搞错的一点是检查什么:官方约定只允许断言权威事件流或可变数据之间的关系,不允许检查服务或方法是否存在——那是加载期问题,不属于不变式。

DSH plugin 的运行时不变式:检查什么、不检查什么

一句话结论:不变式守的是「数据与事件的关系」,不是「对象在不在」——存在性交给依赖声明,不要塞进检查里。

  1. 可以断言:权威事件流里不该出现的顺序、可变数据的结构关系、跨事件的计数一致性。
  2. 不要断言:某个服务是否注册、某个方法是否存在、某个包是否加载——这些由 inject 与启动日志负责。
  3. 违规即失败:条件不成立就调用 fail(),而不是打日志继续跑。
  4. 每个包一个配套插件:发布与注册是穷尽式的,但「是否安装检查」由包自己决定,不合成断言。

DSH plugin 不变式的选择:enabled 与两张正则清单

一句话结论:服务默认开启;一个包被选中要同时满足「允许列表为空或命中」且「阻止列表未命中」,且阻止列表优先。

配置项含义默认
enabled全局开关true
package_allowlist大小写敏感的正则源码,空表示全部放行空
package_blocklist允许之后再做排除,优先级更高空
  1. 匹配规则:条目按 new RegExp(source) 编译;默认不锚定,要精确匹配请自己写 ^ 与 $。
  2. 不解析 /pattern/flags 写法:想表达 flags 请用内联分组一类正则语法,而不是斜杠包裹。
  3. 启动即校验:空白、首尾带空白、重复或无效的条目会直接抛异常,而不是被跳过。
  4. 允许不命中任何已加载包:有效模式可以匹配不到任何包,这样后续加载与 HMR 的行为保持确定。
  5. 过滤器在服务生命周期内固定:不要在运行中期望它动态变化。

DSH plugin 怎么写 InvariantInstaller:ctx、fail 与 inject

一句话结论:安装器就是一个 (ctx, fail) 函数;需要服务就声明 inject,检查是同步还是异步都可以。

ts
type InvariantFailure = (message: string) => never

interface InvariantInstaller {
  (ctx: Context, fail: InvariantFailure): void | Promise<void>
  readonly inject?: Inject
}
  1. 先声明 inject:安装器只能访问它声明的服务,未声明的拿不到。
  2. 在函数体里做检查:不满足就 fail('...'),消息写「违反了哪条包契约」,不要重复标准前缀——框架会自动加上 invariant violated by "<package>": …。
  3. 异步检查可以直接 await:注册会等安装器同步或异步执行完毕,才算成功。
  4. 失败即抛:fail 的返回类型是 never,因为上报违规就是抛异常。
  5. 验证:故意制造一次违规,确认错误消息里带正确的包名与 code。

DSH plugin 注册与生命周期:包名保留、子 fiber 与失败回滚

一句话结论:注册会为完整 npm 包名保留唯一席位;安装器在自己的子 fiber 里跑,失败则原子回滚。

  1. 注册:
ts
ctx.invariants.register(packageName, installer)
// 返回绑定到 effect 的 disposer
  1. 保留是强约束:即使过滤器让安装器保持不活跃,包名保留依然成立——两个插件不可能静默认领同一个包名。
  2. 重复即报错:重复、空白或含空白字符的名称会抛异常。
  3. 在子 fiber 中运行:被启用的安装器跑在专属子 Cordis fiber 里,installer.inject 决定它能访问哪些服务。
  4. 失败回滚:安装器失败会原子地处置子 fiber 并释放保留,所以修复后可以重载再注册,不留残余。
  5. 双向清理:注册 fiber 归服务所有,返回的 disposer 又属于配套插件 fiber——卸载任一侧都会移除监听器、trace 状态与保留项。

DSH plugin 配套插件约定:空安装器也要写理由

一句话结论:每个包都要有 ./invariant;没有可检查项时,空安装器必须写明为什么。

  1. 目录与发布:每个工作区包拥有 ./invariant 配套插件,导出、发布、依赖与打包接线要完整。
  2. 有可观察关系才安装检查:包拥有某个可观察事件或可变数据关系时,才装检查。
  3. 没有就写空安装器:起始注释必须以 No runtime invariant: 开头,并针对该包具体解释原因。
  4. 机械校验:pnpm run verify-package-invariants 会拒绝「生成文件」标记、无解释的空安装器、遗漏报告器的非空安装器、错误注册名,以及不完整的发布接线。
  5. 别为了通过校验而编检查:没有真实不变式时,诚实的空安装器比一条假断言更有价值。

DSH plugin 排查注意事项

一句话结论:不变式报错先看包名归因,再区分「真违规」与「检查写错」。

  1. 报错带包名:invariant violated by "<package>" 直接告诉你该找谁。
  2. 先确认不是检查写错:条件过严会把正常状态判成违规,尤其是没锚定的正则与跨事件计数。
  3. 包名保留冲突 → 检查是否有两个插件注册了同一 npm 包名。
  4. 过滤器改了却不生效 → 过滤器在服务生命周期内固定,需要重启或重载组合。
  5. 空安装器报错 → 检查注释前缀与解释是否齐备。
  6. 修复状态如实:本文的选择规则、安装器签名、保留与回滚语义均取自官方「运行时不变式」页面与 dsh-invariants README;具体校验命令与包约定以官方仓库为准。

来源:运行时不变式、dsh-invariants README、插件与生命周期。

常见问题

DSH plugin 的运行时不变式该检查什么,不该检查什么?

DSH plugin 的运行时不变式只断言权威事件流或可变数据之间的关系,不检查服务与方法是否存在。存在性属于加载期问题,用依赖声明与启动日志解决;不变式用来守住「运行一段时间后不该出现的数据状态」。

DSH plugin 里,InvariantInstaller 的 fail 参数是做什么的,为什么它会抛异常?

DSH plugin 的 InvariantInstaller 里,fail 是绑定到注册包名的失败上报器,调用它就会抛出 InvariantError 并携带稳定 code 与包名。它设计成抛异常而不是返回布尔值,是为了让违规立刻中断、并能归因到具体包。

DSH plugin 的不变式检查失败后,注册会留下残留状态吗?

不会,DSH plugin 的安装器失败会原子地处置它的子 fiber 并释放包名保留。因此配套插件可以修复后重载并再次注册同一名称,不会出现「名称被占用」的残留。

DSH plugin 里,allowlist 和 blocklist 哪个优先?正则要加锚点吗?

DSH plugin 的不变式选择里,阻止列表匹配优先于允许列表匹配;允许列表为空表示全部放行。条目按 new RegExp(source) 编译,默认不锚定,要精确匹配请自己写 ^ 与 $。

DSH plugin 里,包没有可检查的不变式时,配套插件还需要注册吗?

需要,DSH plugin 的配套插件约定要求每个包都有 ./invariant,没有可检查项时导出空安装器。空安装器的起始注释必须以 No runtime invariant: 开头,具体说明为什么没有检查项。

相关术语

运行时不变式(runtime invariant)
运行时不变式是 DSH plugin 对权威事件流或可变数据提出的、运行期必须始终成立的条件。它由包自己发布检查,注册表只负责选择、保留与归因失败。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/invariants
InvariantInstaller(不变式安装器)
InvariantInstaller 是包提供的检查安装函数,签名为 (ctx, fail) => void | Promise<void>,可声明 inject 来访问所需服务。它在专属子 fiber 中运行,完成前注册不会被视为成功。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/invariants
InvariantError
InvariantError 是违反不变式时抛出的错误类型,继承自 Error,带稳定 code INVARIANT、所属 packageName,消息前缀为 invariant violated by "<package>"。它让违规可归因,而不需要注册表导入产品包。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/invariants
package_allowlist / package_blocklist
这两个配置项用正则源码决定哪些包的不变式被启用,大小写敏感、按 new RegExp(source) 编译。阻止列表优先于允许列表;过滤器在服务生命周期内固定不变。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/invariants

来源