关于 Dunstan
Dunstan 使用确定性规则,将编码 Agent 的拉取请求交接报告中的声明与 GitHub 仓库记录核对。它面向审查 Agent 完成工作的团队;记录无法回答的声明会标为无法验证,判定不由模型作出。
主要功能
- 01根据 PR 记录核对声明
- 02检查文件、提交和 CI 运行
- 03将缺乏依据的声明标为无法验证
- 04生成可复现的验证记录
- 05提示交接报告遗漏的声明
- 06提供 MCP 服务和 GitHub Action
适用场景
- 01核实 Agent 报告的文件和头部提交
- 02检查所述测试和拉取请求状态
- 03在报告 PR 完成前审查声明
- 04将检查器设为分支保护必需检查
MCP 相关
Dunstan 将 `dunstan_check_handback` 和 `dunstan_suggest_declarations` 作为 MCP 工具提供。Agent 可检查已声明的交接内容,并找出正文中未列入的声明;判定依据是对 GitHub 记录的确定性检查。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
2收录的工具列表 · 更新于 4 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| block | object | 否 | dunstan-handback 区块,格式为 JSON 对象。提供此项或报告,不能两者都提供。 |
| report | string | 否 | 报告文本,且恰好包含一个 ```dunstan-handback 囲栏。提供此项或 block,不能两者都提供。 |
| outPath | string | 否 | 将记录写入此路径(该路径必须不存在)。不提供此项则不会写入任何内容。 |
| repository | string | 是 | GitHub 仓库,格式为 owner/name。 |
| pullRequest | integer | 是 | 此 handback 所涉及的 pull request 编号。 |
详细介绍
Dunstan
Dunstan 会将编码 Agent 的完成报告记录在案。
Agent 完成工作后会写一份交接报告:更改的文件、运行的测试、已关闭的引用、头部提交、合并时间。Dunstan 会读取交接报告中声明的区块,并使用确定性检查逐项核对每条声明与代码库自身记录(拉取请求状态、更改的文件、提交、CI 运行情况)。它会生成一份记录,任何人都可以在自己的机器上重新运行检查,并得到相同的结论。
- 不由模型决定通过还是失败。
- 默认判定为失败:记录无法回答的声明为
unverifiable,绝不会因为没有记录而判定为通过。 - 证据,而非门禁:Dunstan 会返回一份记录;是否阻止合并由你的分支保护规则决定。
声明格式、Agent 声明的区块以及 Dunstan 写入的记录,均采用开放规范:spec/claim-format.md,JSON Schema 位于 spec/schema/,示例位于 spec/examples/。记录是一个 in-toto Statement。关于它在 in-toto、SLSA 和 PER 中的含义,以及哪些内容无法沿用,请参阅 docs/interop.md(仅供参考;目前不支持导出)。
与 Claude 搭配使用
Dunstan 以 Claude Code 插件的形式提供:包含两个 MCP 工具,以及一个让 Claude 在宣布完成前声明、核查并报告拉取请求交接情况的技能(plugins/dunstan/)。
安装。 在 Claude Code 中:
/plugin marketplace add BargLabs/dunstan
/plugin install dunstan@barglabs
该插件会通过 npx -y [email protected] mcp 运行 MCP 服务,使用的是固定为该版本的 dunstan npm 包:单个自包含文件,无依赖项。
令牌。 启用插件时,Claude Code 会询问是否提供可选的 GitHub 令牌,并将其保存在系统的安全凭据存储中。请使用只读权限的细粒度个人访问令牌,将权限限定在要检查的仓库,并设置有效期;权限要求见 docs/mcp.md。留空即可匿名读取公开仓库。GitHub 的 GraphQL API 不允许匿名调用,因此没有令牌时,closes 引用会标记为 unverifiable,而非 fail。插件绝不会读取环境变量中的令牌。
读取内容及来源。 服务在你的机器上运行,并从本机直接调用 GitHub API:读取拉取请求、更改的文件、提交、检查运行、工作流运行和测试构件、部署,以及区块所引用的问题。所有请求均为读取操作。不会向 GitHub 或代码库写入任何内容,也不会向 Barg Labs 发送任何内容。
试用。 针对 Barg Labs 的一个公开拉取请求,尝试以下三个提示词:
使用 Dunstan 检查 BargLabs/cejel#408 的交接报告:头部提交 35d6201d00cf926f9af10df61bfec05b6624ee12,更改的文件为 .github/workflows/verify-published-windows-binary.yml 和 scripts/validate-distribution-metadata.mjs,合并时间为 2026-10-04T13:30:48Z。每条声明均成立,因此结论为pass。在我将 BargLabs/cejel#408 报告为完成之前,请使用 Dunstan 列出此报告中交接区块未声明的声明:,后接报告及其dunstan-handback区块。Dunstan 会列出区块遗漏的每条文字声明,例如测试数量或closes,并指出可用于声明该内容的字段。这不会给出结论。使用 Dunstan 检查 BargLabs/cejel#408 是否关闭了 #407,头部提交为 35d6201d00cf926f9af10df61bfec05b6624ee12,文件与之前相同。没有令牌时,closes行会标记为unverifiable(source_unreadable:closing_references):记录无法核实此事,但这不等于结果为假。有令牌时则为pass。
支持。 GitHub issues 或 [email protected]。 隐私政策和服务条款。
检查器
pnpm install && pnpm build # writes dist/dunstan.mjs and prints its SHA-256
# Check a pull request against the report it names (exactly one report source).
GITHUB_TOKEN=... dist/dunstan.mjs check --repo <owner/name> --pr <n> --report-pr-body --out record.json
# or --report-file <path|->, or --report-comment-author <login> (that author's latest comment)
dist/dunstan.mjs verify record.json # offline: recompute claims, verdict and digests
dist/dunstan.mjs rerun record.json # online: re-read the sources, report evidence_changed
退出代码:0 表示通过,1 表示失败,2 表示无法验证,3 表示无记录:使用错误、无法读取的拉取请求、检查期间发生变动且再次检查时又发生变动的拉取请求(pull_request_moved),或内部错误(verify:0 表示已验证,1 表示未验证)。
--sign-key <path> --signer <principal> 会将记录写为 JCS 字节,并附上独立的 SSH 签名;verify --sig <file> --allowed-signers <file> 会进行检查。
dist/dunstan.mjs mcp 会将同一检查器作为 MCP 工具 dunstan_check_handback 提供,Agent 会在宣布完成前,将其区块交由该工具检查;此外还有 dunstan_suggest_declarations(见下文):docs/mcp.md。
记录中的 checker.digest 是已运行的单文件打包文件 dist/dunstan.mjs 的 SHA-256 值:其中包含检查器代码、其依赖项,以及在构建时内嵌的 spec/schema/ 中的两个架构。
Dunstan 不检查什么
Dunstan 会根据记录检查交接报告中声明的内容,但不会检查交接报告是否涵盖了问题所要求的内容:即使报告准确描述了错误的工作,也会通过。
应声明的内容:docs/agents.md。
完成前先声明
对已声明内容的检查是精确的。读者往往无法从文本中找出这些声明:将正确段落交给托管模型评判器后,它能排除的虚假报告仍然少得多;Dunstan 自带的提取器也漏掉了测试中故意植入的虚假声明。两者的问题都出在阅读,而非检查。撰写报告的 Agent 是最适合阅读报告的人,而且在交接前它仍在其中。因此,Dunstan 让编写区块几乎不费力,接着询问作者区块未涵盖的文本内容:
dist/dunstan.mjs template # the block for HEAD, filled from local git
dist/dunstan.mjs suggest --report-file report.md # prose claims the block does not declare
template 会填写 headCommit 和 filesChanged(HEAD 与其相对于远程默认分支的合并基点进行比较,或使用 --base),并且仅根据 JUnit 文件和与其一同指定的 CI 记录填写 tests。它绝不会填写 references 或 mergedAt;如果 git 无法提供答案,它就不会输出区块。suggest 会对围栏外的文本运行建议性提取器,并列出它建议声明、但区块未声明的每项内容,供你声明或改写。它绝不是判定,不会向记录写入任何内容,并且始终以 0 退出;MCP 服务会通过 dunstan_suggest_declarations 提供该功能。该流程、可复制到 AGENTS.md 或 CLAUDE.md 的指令区块,以及输出示例:docs/agents.md。
GitHub Action
工作流中的一个步骤,分支保护中的一项必需检查:
permissions: { contents: read, pull-requests: read, issues: read, deployments: read, checks: write, actions: read }
steps:
- uses: BargLabs/dunstan@<full commit SHA>
with:
report-source: pr-body # or comment:<author-login>, or file:<path>
它会在头部提交上写入一个 dunstan 检查运行,其摘要为声明表;将记录作为工件 dunstan-record 保存,并在作业摘要中写入同一张表。只有 pass 会得出 success;fail、unverifiable(除非设置 unverifiable-conclusion: neutral)以及任何错误都会得出 failure。它绝不会发表评论、添加标签、审核或合并。设置方法、分支保护设置以及每种结论的含义:docs/action.md。
托管层
这是本地工具之外的可选服务,本地工具绝不会使用它。向 /v0.1/check 发送包含报告或区块以及拉取请求的 POST 请求;Barg Labs 的只读 GitHub App 会读取该拉取请求的元数据(绝不读取文件内容);同一检查器会返回一份使用 SSHSIG 签名的记录,而 dunstan verify 会离线重新运行检查,得出相同判定。记录默认保留 90 天,报告文本绝不保留。读取和保留的内容、Contents:read 注意事项以及 API:docs/hosted.md。Worker 位于 hosted/;由操作人员负责配置和部署(docs/runbooks/hosted-provisioning.md)。
面向读者主张的检索(DRAFT)
对于一份散文报告,读者会将文本转化为带类型的主张,而 Dunstan 必须找出每项主张涉及哪些记录条目。src/retrieval/ 不借助模型完成这项工作:先匹配精确标识符,再使用 BM25,并设定明确的下限。检索只会提出候选项。检查会根据记录保留的候选项作出判断,因此 dunstan verify 无需再次检索即可复现判定。dunstan-eval retrieval 会衡量 k = 5 时的召回率。记录格式为 DRAFT 0.2.0,尚未确定。详情:docs/retrieval.md。
来自散文的提示(DRAFT)
dunstan check --advisory 或 Action 中的 advisory: true 也会读取报告的散文内容。src/advisory/ 中的提取器使用书面语法且不借助模型,将每个路径、#N、SHA、计数或时间戳关联到带有断言动词的分句,并丢弃所有无法关联的标记。它会使用门自身的检查方式,将剩余内容与证据进行比较。每项结果都会记录为提示:agrees、differs:<reason> 或 unanswered:<reason>;如果文件仅通过名称或部分路径命名,则记录为 agrees_by_name。提示绝不会改变判定,也绝不会是 fail。记录会在提示旁标明测得的数据;每项数据仅适用于实际测量所用的精确提取器和比较方式,否则标为“未测量”(见下文“测得的数据”)。详情:docs/advisory.md。
differs 注释会显示为 “可能存在分歧,未经核实”,绝不会显示为指控。测量前预先登记的一条决策规则规定,如果测得的准确率低于 0.50,differs 注释就会采用这种标签。该规则已触发。CLI、Action 的摘要和 MCP 工具的文本都会使用新的措辞,随后说明记录显示的内容,并在提示旁打印一条固定说明。根据已发布的数据,说明内容为:“提示绝不会影响判定。提取精确率为 0.80(24/30,95% 置信区间 0.63–0.90)。可能存在的分歧未经核实:在我们自己的 170 个 Agent PR 中,有 20 个被标记,其中 0 个误报;在记录能够检查的 44 条提示中,没有一条是误报(0/44)。” 对于尚未测量过的后续提取器或比较方式,则不提供数据。记录仍会写明 differs:<reason>(docs/advisory.md,“differs 注释的显示方式”)。
经测量的数据
发布日期:2026-10-05:仅公布数值、n 和方法,不公布报告、报告条款或判定标签。
| 数据 | 数值 | n | Wilson 95% 区间 | 适用范围 |
|---|---|---|---|---|
| 提取精确率 | 0.80(24 条真实声明) | 30 份公告 | [0.627, 0.905] | 提取器 0.1.1,摘要 ab77ce47…c360 |
标记了真实虚假声明的 differs 注释 | 0 | 20 份 differs 公告 | [0, 0.161] | 提取器 0.1.1 和比较器 0.2.0 |
| 基准率:真实虚假的完成声明 | 0 | 44 份可核查公告(共 149 份) | [0, 0.080] | 提取器 0.1.1 和比较器 0.2.0 |
方法:提取器 0.1.1 在 Barg Labs 自己合并的 170 个 Agent 拉取请求中提出了 149 份公告。操作员对其中抽取的 30 份留出样本进行了判定,以计算提取精确率;并对每一份 differs 公告(共 20 份)进行了普查,判断其是否标记了真实虚假声明。计算基准率时,记录可以核查 149 份中的 44 份(其中 24 份与记录一致,20 份不一致),且没有一份是假声明;其余 105 份无法与记录比较,也未作判定。每项数据都绑定到提取器摘要,而 differs 数据和基准率也绑定到比较器版本。任何后续提取器或比较器在重新测量之前都属于“未测量”,绝不继承已有数据。目前运行的提取器是 0.1.3。自 0.1.2 起,它不再将关于其他拉取请求、尚未完成的更改、基线、否定列表或故意测试失败的陈述视为声明;自 0.1.3 起,它可以识别冒号后的文件列表或合并时间(docs/advisory.md,「归属」和「列表、合并时间及自身引用」)。提取器 0.1.4 的规则无法推广到留出拉取请求,因此已回退(「标签、使用过的名称和描述」)。目前未公布 0.1.3 的数据,因此它写入的记录会注明“未测量”。目前运行的比较器是 0.4.0:对于不在已更改文件中的文件声明,仅当该路径在提交顶端是一个文件时才视为可能存在分歧;如果该位置不存在任何内容,或是一个目录,则视为未作答(「提交顶端不存在的路径」)。closes 声明则根据谁关闭了目标来判定,就像门禁的判定方式一样(「根据关闭目标的人判定 Closes」)。它也尚未测量:differs 数据仍绑定到比较器 0.2.0。
基准率始终与 differs 数据一同展示:在记录可以核查的 44 份公告中,没有一份是假声明(0/44),因此 0/20 表示可供发现的内容很少,而不是检查未能发现问题。记录无法核查的 105 份公告未作判定,因此其中若有虚假声明,也不会被发现。只有在包含虚假声明的语料上达到预注册的门槛后,才有依据将 differs 注释称为不匹配(2026-10-05 确定)。目前尚未达到该条件。详情请见:docs/advisory.md「经测量的数据」。
验收标准及其预注册测试样例:docs/acceptance/preregistration-0.1.md、fixtures/。
状态:正在开发中。声明格式规范仍是审核中的草案。
许可证:检查器和工具采用 AGPL-3.0-only(LICENSE);spec/ 中的声明格式规范采用 Apache-2.0(spec/LICENSE)。贡献需签署 CLA(CLA.md)。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。