dsh-culprit:用判据二分 Git 历史,定位回归肇事提交

前言

在 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,主要能力包括:

  1. 自动或指定好点/坏点:默认坏点为 HEAD;可传 goodRef 跳过自动寻点;也可自动沿历史找健康基线。
  2. 二分与证据链:每步记录判据退出码、耗时、语义(good/bad/skip),收敛后给出 result.culprit、改动文件统计与 diff。
  3. 判据语义与 git bisect run 对齐:0 为健康,非零为有问题;125 与超时均视为跳过(超时 deliberately 不算「坏」,避免把「依赖装不上」误判为肇事点)。
  4. HEAD 自检:默认要求坏点在当前 HEAD 上确实失败;若判据在 HEAD 为绿,返回 not-reproduced,不硬凑结论。
  5. 断点续跑:每探一步落盘状态(默认 %TEMP%\dsh-culprit);预算用尽时可 resume: true 继续;mode: "status" 只读列出未完成状态。
  6. 安全边界:脏工作树拒绝(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。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye