dsh-proof
给 DeepSeek Harness 补上"完成的证据":跑项目自己的检查,并让过期的通过结论自动失效。
模型最爱说"已完成"。dsh-proof 把这句话变成可验证的事实:调用一次 proof,拿到的是真实退出码、失败现场(输出尾部)、以及一个工作区指纹。代码之后再被改过,上一次的"通过"会被判为 stale,不再算证据。
它解决什么
| 场景 | 没有 dsh-proof | 有 dsh-proof |
|---|---|---|
| 模型说"测试都过了" | 你只能信,或自己再跑一遍 | proof 返回 ok:false 时你必须看到失败项与报错原文 |
| 上午跑通了,下午改了 10 个文件 | 旧结论静默过期 | 指纹变化 → stale:true,强制重跑 |
| 换个会话继续干活 | 不知道上次验证到哪 | 状态按工作区落盘,跨会话可查 |
| 团队/CI 标准不一致 | 插件自造一套判定 | 直接复用项目自身的 typecheck/lint/test/build |
设计上只做一件事:把"验证"变成一次有据可查的操作。不接管上下文、不计费、不改 UI。
与现有插件市场的差异
插件市场(截至 2026-10-31,本机 dsh-market 缓存共 70 个插件)里,记忆/上下文 11 个、模型接入 15 个、UI 14 个、费用观测 7 个、审查评审 7 个——没有一个插件的名字里带 test/verify/check/ci/quality。最接近的 dsh-answer-reviewer 是"另一个 LLM 给最终回答打分",属于主观评判且额外消耗 token。
完整对比(含三个近邻插件的官方描述引文、6 个差异维度、以及本项目明确不做的部分)见 docs/market-comparison.md。
安装
方式 A:直接从 GitHub 安装(当前推荐)
dsh plugin --profile desktop add "git+https://github.com/xwtxbzz/dsh-proof.git"
方式 B:本地目录(开发/自用)
dsh plugin --profile desktop add "file:D:\doctor\dsh-proof"
方式 C:手工挂载
- 把本目录放到 profile 的
node_modules/dsh-proof(或在 profilepackage.json的dependencies里加"dsh-proof": "file:D:\\doctor\\dsh-proof"后执行dsh plugin --profile desktop install)。 - 确认 profile 的
package.json里dsh.profile.bundles含"dsh-proof"——bundle patch 只有在 bundles 列表里才会被加载。 - 重启 DSH(服务端插件只在启动时装载;无需重建 Web 产物,本插件没有客户端部分)。
尚未发布到 npm。等你把包名
dsh-proof发到 npm 之后,dsh plugin --profile desktop add dsh-proof才会生效。
装好后新会话里应能:/proof 出现在斜杠命令里;模型系统提示末尾多一段"完成证据"契约。
60 秒上手
# 1. 让插件探测项目里能用什么检查,并写出 dsh-proof.json
/proof init
# 2. 看一眼生效配置(探测到的命令、超时、状态目录)
/proof config
# 3. 跑一遍
/proof run
# ✅ PASS · 3/3 项通过
# 工作区:D:\your-repo
# 指纹:git 9f2c1a4b3d21 · 2026-10-31T09:12:44.118Z
# - ✅ typecheck `pnpm run typecheck` 12.4s exit 0
# - ✅ lint `pnpm run lint` 3.1s exit 0
# - ✅ test `pnpm run test` 31.0s exit 0
# 4. 改两行代码后再看状态
/proof status
# ⚠️ 上一轮验证已过期 —— 工作区自那之后被改过,旧结果不算证据
模型侧不需要你提醒:系统提示里的契约要求它在宣称完成前调用 proof,失败时引用失败项与报错原文。
dsh-proof.json(/proof init 生成,可手工改):
{
"checks": [
{ "name": "typecheck", "command": "pnpm run typecheck" },
{ "name": "test", "command": "pnpm run test", "timeoutMs": 900000 }
],
"timeoutMs": 600000,
"tailLines": 30
}
参考
工具 proof
| 参数 | 类型 | 说明 |
|---|---|---|
action |
run | status |
run(默认)执行并记录;status 只读上次结论并判断是否过期 |
checks |
string[] |
按名称过滤,省略=全部 |
cwd |
string |
工作区根目录覆盖(默认取会话 cwd) |
返回值:{ ok, report, data }——report 是给人看的多行文本,data 给程序用(含每项 exitCode/timedOut/durationMs/tail、指纹、变动文件)。
命令 /proof
| 子命令 | 作用 |
|---|---|
status |
上次结论 + 是否过期 + 期间变动文件 |
run [名称...] |
立即执行(可只跑某几项) |
init |
探测并写出 dsh-proof.json(已存在则拒绝覆盖) |
config |
展示生效配置与检查项清单 |
配置(cordis.patch.yml 的 config 或 dsh-proof.json)
| 键 | 默认 | 说明 |
|---|---|---|
autoDetect |
true |
自动探测检查项 |
checks |
null |
显式检查项,优先于探测;[{name, command, timeoutMs?, cwd?}] |
timeoutMs |
600000 |
单项超时(超时即失败,不等进程退出) |
tailLines |
30 |
失败输出保留行数 |
workspace |
null |
固定工作区(默认取会话 cwd) |
stateDir |
~/.dsh/proof |
状态目录(已自动从指纹中排除) |
policy |
true |
注入"完成证据"契约;false 关闭;字符串则替换 |
探测顺序:package.json 的 typecheck → lint → test → build(按 pnpm-lock.yaml/yarn.lock/bun.lock 选包管理器)→ Cargo.toml → go.mod → pytest/Makefile 目标。探测不到就返回"未验证",绝不假装通过。
指纹与新鲜度的语义
- 有 git:
rev-parse HEAD+git status --porcelain→ sha1。已改/未跟踪都算,提交与否都敏感。 - 无 git:遍历工作区(跳过
node_modules/dist/build/.venv等),路径|大小|mtime排序后 sha1。 stale= 上次记录的指纹 ≠ 当前指纹。stale 的通过不算证据,status会直接把ok置 false。
插件自己的状态目录永远从指纹中排除——否则"写状态"会把刚记下的指纹作废。
边界与安全
- 会执行
dsh-proof.json/ 配置里的命令:只在受信工作区里用。它不联网、不改仓库文件、不提升权限,也不绕过 DSH 的审批策略。 - 超时用
taskkill /T /F(Windows)或SIGKILL尽力杀进程树;即使杀不干净也会先返回结论,不会把会话挂死。 - 子进程输出写临时文件再回收,不用管道:既避开宿主对命名管道的限制,也不会被大日志塞满缓冲区。
- 没有客户端代码:不需要
pnpm run dev:web,也不需要重建 Web 产物。
排障
| 现象 | 处理 |
|---|---|
斜杠命令里没有 /proof |
确认 profile 的 dsh.profile.bundles 含 dsh-proof,且安装后重启过 DSH |
proof 报"没有可用的验证命令" |
/proof init 后手工把命令改成项目真能跑的;或配置里写 checks |
| 每次都 stale | 检查是否有频繁变动的产物目录被纳入指纹;把产物目录移出工作区,或在状态目录外生成 |
| 检查超时 | 调大 timeoutMs(单项可在 checks[].timeoutMs 单独设) |
| 想临时关掉契约 | 配置 policy: false,工具与命令仍可用 |
开发与测试
node test/core.test.mjs # 10 个:探测/执行/超时/指纹/状态
node test/plugin.test.mjs # 9 个:注册面、run/status/init/config、契约开关
node test/sdk-smoke.mjs # 真 SDK:schema 编译、validateArgs、INVALID_ARGS、输出校验、render
sdk-smoke.mjs 默认从 ~/.dsh/profiles/node_modules/@deepseek-ai/dsh-tools/lib/index.js 取 SDK,可用 DSH_SDK_ROOT 指向别处。它会在真实 defineTool 下编译参数 DSL 与 output schema——这一步曾抓出两个真实问题(type: 'json' 不被接受、output schema 的 required 不能写在根上)。
本仓库实现只有三个文件:lib/core.js(纯逻辑,无 SDK 依赖)、lib/create.js(装配 tool/command/契约)、lib/index.js(插件入口)。
License
MIT
No comments yet. Be the first to write one.