Bundle
dsh-plugin-verdict-guard
Native DeepSeek Harness plugin: a turn that states a verdict without evidence does not end — it is steered back for proof.
- Source
- sagetta1
- License
- MIT
- Updated
- Updated 18 days ago
Readme
# verdict-guard
English | [中文](README.zh.md)
A native [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin.
**A turn that states a verdict does not close until the verdict carries something a reader can follow.**
```
model: "The filter does not work. Closing this direction."
guard: held back — a verdict without evidence.
model: (runs the check, quotes the output — or withdraws the claim)
```
## Why
The most expensive thing an agent produces is not a wrong fix. It is a wrong
**closing verdict**: *"it doesn't work"*, *"no bug here"*, *"not reproducible"*.
A false *"it works"* is caught by the next run — it leaves a trace.
A false *"it doesn't work"* leaves none. The direction is closed quietly, and
nobody re-opens a question that already has an answer. The two error classes
are not equally priced, so they should not carry the same standard of proof.
This plugin makes the asymmetry mechanical: at the turn's stop boundary it
reads what the model is about to close on, and what the turn **actually did**.
## What counts as evidence
| Signal | Where it comes from |
|---|---|
| a fenced block, an exit code, a test tally, an HTTP status | the answer's text |
| `file.ts:88`, a path, a log name, `journalctl`, `pytest`, … | the answer's text |
| a verifying tool result — `bash`, `read`, `grep`, `test`, … | **the session log for that turn** |
The third row is why this is a plugin and not a shell hook. A text-only checker
sees what the model *wrote*. The session log says what actually **ran**.
That gives one check a hook cannot make:
> The answer pastes a command transcript — a fenced block, `12 passed, 0 failed`,
> an exit code — while the turn called **no tool at all**. Nothing produced that
> text except the model. It is held back with a sharper message than a plain
> missing citation.
A bare file path is deliberately *not* enough to trigger that stricter case: a
path can honestly be recalled from earlier in the conversation, a transcript cannot.
## Install
```sh
dsh plugin add dsh-plugin-verdict-guard
```
That is the whole install. The package declares a `dsh.bundle` manifest, so it
enters your profile as its own bundle layer with every option at its default —
no row to write by hand. Verify it composed into the tree:
```sh
dsh --profile headless --dump-config | grep -A 3 verdict-guard
```
To change a default, add a row to your profile's own patch layer
(`$DSH_HOME/profiles/<profile>/cordis.patch.yml`), which is applied after every
bundle layer:
```yaml
- id: verdict-guard
config:
locale: en
requireToolEvidence: false
maxInterventionsPerTurn: 2
```
## Config
| Option | Default | Meaning |
|---|---|---|
| `locale` | `both` | which verdict vocabularies participate — `en`, `ru`, `both` |
| `requireToolEvidence` | `true` | a verdict needs a **verifying** tool result, not merely any tool result — writing a file is not checking a claim |
| `verifyingTools` | bash/read/grep/test/… | tool names that count as verification |
| `extraVerdictPatterns` | — | regex sources appended to the built-in verdict vocabulary |
| `extraEvidencePatterns` | — | regex sources appended to the built-in evidence vocabulary |
| `verdictPatterns` | — | replace the built-in verdict vocabulary entirely |
| `evidencePatterns` | — | replace the built-in evidence vocabulary entirely |
| `maxInterventionsPerTurn` | `1` | how many times one turn may be held back |
| `maxInterventionsPerSession` | `6` | total holds per session |
| `verbose` | `false` | log every pass decision at debug level |
An empty list is read as *"not supplied"*, never as *"match nothing"* — so a
config that fills unset array options with `[]` cannot silently disarm the guard.
## What this is not
It does not verify that a claim is *true* — no oracle, no acceptance criteria,
no contract. It checks one thing: whether the answer offers anything to check
at all. A wrong claim with a real command output beside it passes here and is
caught by the reader; a right claim with nothing beside it is held.
It also holds only **once** per turn by default. It is a speed bump on the way
out, not a gate you have to satisfy.
## How it works
One listener on `agent/turn-stopping`, the harness's own stop boundary:
```
agent/turn-stopping → read the turn's closing assistant text
→ read the turn's tool/call + tool/result events
→ verdict? evidence? → agent.steer(reason)
```
Steering makes the machine observe pending input and run another step, which is
the documented way for a listener to object at that boundary. Nothing in the
agent loop is modified.
**The guard caps itself.** The harness has no loop guard here yet: the loop
carries a `TODO(stop-loop-guard)`, and the Claude Code hook bridge reports
`stop_hook_active: false` unconditionally, so a listener that steers must limit
itself or it can hold a turn open forever. `maxInterventionsPerTurn` (default
`1`) is that limit — one hold, then the turn closes whatever the model says.
## Compatibility
Built and tested against `0.1.0-rc.8`.
⚠️ **The `latest` dist-tag on the harness's own sub-packages is stale.**
`@deepseek-ai/dsh-tools`, `-llm`, `-session` and friends publish their current
line under `next` (`0.1.0-rc.8`) while `latest` still points at `0.0.1-rc.1`.
Installing them without an explicit range mixes two incompatible generations —
and some `0.0.1-rc.1` packages depend on `@deepseek-ai/dsh-bash`, which no
longer exists. Pin `^0.1.0-rc.8`, or install with `@next`.
## Development
```sh
npm install
npm test # 24 tests: the detector, plus the plugin on a real agent loop
npm run build
```
The integration tests drive the real `AgentLoop` with a scripted model adapter,
so both outcomes — held and closed — are proven without a provider key.
## Who wrote this
Built by Sagit Nabiev while running agents against production systems, where one
confident unsupported verdict costs a day. If you want something like this built
for your own harness or agent stack — a gate, a bridge, a plugin — write to
**founder@perpreconcile.com**.
## License
MIT
[](https://github.com/deepseek-ai/deepseek-harness)
Install
dsh plugin --profile web add github:sagetta1/dsh-verdict-guard
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-verdict-guard from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.