前言¶
在 DeepSeek Harness(DSH)里做智能体或工作流时,模型经常需要回答「这段行为是从哪次提交开始坏的」。按行或按历史的 blame 类工具能说明某行是谁写的,但无法替代「在某个 commit 上真的跑一遍命令」——依赖装不上、测试超时、只在运行时才暴露的问题,单靠读历史猜不到。
dsh-culprit 是社区维护者 ca610 发布的工作流类插件(SkillHub 分类:工作流)。它接受一条有 bug 时退出非零的判据命令,在仓库 --first-parent 历史上做二分,返回肇事提交、diff、逐步探测证据,并在结束后把工作树切回原状。本文介绍其定位、安装、用法与约束;事实以 GitHub 仓库 README 与 package.json 为准。
这是什么¶
一句话:把「测试变红」当成可执行、可二分的事实,在历史提交上反复运行判据,找出第一个引入失败的 commit。
| 项 | 说明 |
|---|---|
| 包名 | dsh-culprit |
| 版本 | 0.1.0(资料来源:package.json) |
| 许可证 | MIT |
| 维护者 | ca610 |
| 目录页 | https://www.skillhub.cn/plugins/ca610/dsh-culprit |
| 源码 | https://github.com/ca610/dsh-culprit |
| Node | >=22 |
与生态里按行/历史做「这一行谁引进」的 slow-stack/euthyna 不同,本插件必须 checkout 并在各提交上执行判据,代价是会动工作树;工程上因此强调脏树拒绝、与进行中的 git bisect 互斥、以及 finally 中还原 HEAD/分支。
核心功能¶
插件向 DSH 注册工具 regression_culprit,主要能力包括:
- 自动或指定好点/坏点:默认坏点为
HEAD;可传goodRef跳过自动寻点;也可自动沿历史找健康基线。 - 二分与证据链:每步记录判据退出码、耗时、语义(good/bad/skip),收敛后给出
result.culprit、改动文件统计与 diff。 - 判据语义与 git bisect run 对齐:
0为健康,非零为有问题;125与超时均视为跳过(超时 deliberately 不算「坏」,避免把「依赖装不上」误判为肇事点)。 - HEAD 自检:默认要求坏点在当前 HEAD 上确实失败;若判据在 HEAD 为绿,返回
not-reproduced,不硬凑结论。 - 断点续跑:每探一步落盘状态(默认
%TEMP%\dsh-culprit);预算用尽时可resume: true继续;mode: "status"只读列出未完成状态。 - 安全边界:脏工作树拒绝(
dirty);已有 bisect 拒绝(bisect-in-progress);无reset --hard/clean/stash;仅--first-parent历史。
返回状态包括:found、not-reproduced、no-good-baseline、budget-exhausted、inconclusive、dirty、not-a-repo、bisect-in-progress、invalid-args。仅 found 提供 result.culprit;budget-exhausted 故意不给肇事者,避免把未收敛的「最像」当结论。
安装与启用¶
DSH 社区目录(SkillHub)与 DeepSeek / 幻方无官方从属关系;安装前请自行审阅源码与 MIT 许可证。插件在当前 DSH 进程权限下执行 git checkout 与子进程判据,等同于你在本机终端跑同类命令。
常规路径(用户自管 profile)¶
README 说明由 agent 调用插件管理器安装本地 bundle(需先将仓库放到本机路径,README 示例为 clone 后指向该目录):
plugin_manager(action: "install_bundle", target: "C:\\path\\to\\dsh-culprit")
装完需重启 profile / DSH:插件仅在启动时加载,无热更新。卸载使用 remove_bundle。
Windows 桌面版¶
desktop profile 由 Electron 应用独占管理,dsh plugin --profile desktop add 会被拒绝。README 记录的手动接法为三件:profile node_modules 下 junction 指向仓库、package.json 中 dependencies 使用 link:<仓库绝对路径>、并在 dsh.profile.bundles 追加包名(否则不会读取 cordis.patch.yml)。修改 package.json 前建议备份。
确认是否装上¶
README 提供两层自检(缺宿主 runtime 时脚本以退出码 2 跳过,跳过不算通过):
node test/profile-install.mjs
node test/host-dispatch.mjs
前者只读 profile 解析 link、bundles、junction 与 patch;后者在真实 cordis 上下文中派发一次工具并校验输出 schema 与工作树还原。
典型用法¶
最简调用:只填判据,工具自行寻好点、二分、还原工作树。
regression_culprit(predicate: "npm test -- src/clamp.test.js")
已知健康版本时指定 goodRef,通常更快也更准:
regression_culprit(predicate: "node --test calc.test.mjs", goodRef: "v2.1.0")
跑之前建议在本机 HEAD 上先执行一遍判据,确认当前确实是红的。判据写法与 git bisect run 相同;Windows 上 PowerShell 会压扁非零退出码,插件会对判据做包装以保留 125 等语义(详见 README「已知限制」)。
常用参数(节选)¶
| 参数 | 说明 |
|---|---|
predicate |
必填,判据命令 |
goodRef / badRef |
好/坏提交或 tag,坏点默认 HEAD |
root |
仓库路径,默认会话工作目录 |
mode |
bisect(默认)或 status |
resume |
续跑未完成区间 |
maxSteps |
最多探测次数,默认 16(上限 40) |
timeoutPerStep |
单步超时毫秒,默认 120000 |
totalBudgetMs |
总预算毫秒,默认 600000 |
仓库内演示(临时目录造 bug 仓库,跑完删除):
node examples/demo.mjs
配置(可选)¶
可在 cordis.patch.yml 的 bundle config 中覆盖默认值,例如:
- insert:
- id: culprit
name: dsh-culprit
config:
maxSteps: 24
timeoutPerStep: 300000
其它项包括 workdir、maxHistoryScan、maxAutoProbes、skipLimit、stateDir、allowDirty、shell、firstParent、requireBadAtStart、keepState 等,含义与默认值见 README 配置表。
适用场景与注意¶
适合:回归问题已有可脚本化判据(测试、构建、单行 repro 命令);需要在智能体工作流里把「哪次提交改挂」结构化返回(含 diff 与探测轨迹)。
不适合或需知:肇事改动若只在已合并分支内部、结果可能落在 merge commit 上(插件会警告);旧提交环境跑不起来会导致大量 skip 进而 inconclusive,可缩小区间或提供更近的 goodRef;merge commit 的 combined diff 不可用时退回 git diff <sha>^1 <sha>。
本地无宿主时可跑单元测试:
node test/culprit.test.mjs
结尾¶
dsh-culprit 把「判据在历史上的通过/失败」当作唯一事实来源,用二分加逐步证据回答「从哪次提交开始坏」,并尽量在不伤害未提交改动的前提下还原工作树。需要插件详情与分类入口见 SkillHub 目录页;安装、参数、安全保证与 Windows 退出码细节见 GitHub README。