Engineering-discipline guard: grill the requirements before the first edit, enforce red/green test evidence gates, and audit the delivery with a forked adversary (grill-requirements skill + tool-policy gates).
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:PerryLink/dsh-doublecheck
GitHub-sourced plugins run build scripts on your machine at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
Double-check before you ship: grill the requirements, test the implementation, prove the delivery.
An engineering-discipline bundle for DeepSeek Harness. Agents love to start coding; requirements hate being assumed. dsh-doublecheck installs a discipline loop that makes the agent grill the requirements before the first edit, and prove the delivery instead of claiming it — re-implemented natively on DSH's own extension points (skill registry, tool policy pipeline, approval seam, subagent and workflow seams, session log), not on borrowed prompt files. Tested against DSH 0.1.0-rc.6.
The methodology is inspired by obra/superpowers and TimothyVang/Grill-me. Every prompt, term, example, and file in this package is written from scratch — nothing is copied from either project.
Why
- Vague tasks produce wrong software. A brief request ("帮我做一个功能") hides six unsettled decisions; the agent currently guesses all of them and charges you for the guess.
- Disciplined teams do this in humans: requirements review → failing test → passing test → self-review → delivery proof. Agents deserve the same loop, enforced by the harness, not by vibes.
The discipline loop
grill ──▶ design ──▶ red ──▶ green ──▶ review ──▶ verify
│ │
│ (v0.1) (v0.2+) (v0.3) (v0.4)
│
└─ requirements furnace: six dimensions, consensus gate,
structured spec committed to the session and the workspace
| Stage | Meaning | Status |
|---|---|---|
| grill | Interrogate the six requirement dimensions; refuse to implement until consensus. | ✅ v0.1 |
| design | Spec committed via doublecheck_spec. |
✅ v0.1 |
| red | A failing test run proves the gap; implementation edits need it on record. | ✅ v0.2 |
| green | A passing test run after the edits closes the loop. | ✅ v0.2 |
| review | A forked adversary critic audits the delivery against the spec. | ✅ v0.3 |
| verify | doublecheck_report + a per-dimension verification workflow prove the delivery. |
✅ v0.4 |
Features (v0.5)
- 🔥
grill-requirementsskill — a bundled Agent-Skills-format skill that interrogates the task across six dimensions (goal, scope, acceptance criteria, failure modes, priorities, non-goals) using DSH's nativeask_user_questionUI, refuses to write code until consensus, and records the contract. - 🧰 Stage skills for the whole loop —
red-green-tdd(write the failing test, run red, implement, run green),delivery-review(adversarial self-review against the spec once green), anddelivery-proof(consolidate the evidence into the delivery report before claiming completion) joingrill-requirements, so all six stages have model guidance, not just the first. - 📜
doublecheck_spectool — commits the grilled spec to the session log and writes a markdown copy to the workspace, so the contract survives the conversation. - 🔄 Task-change re-grill — a committed spec covers its own task: a new direct-user request after the latest spec commit reopens the grill gate for that follow-up instead of silently inheriting the old contract.
- 🛡️ Discipline guard — a soft gate on the tool policy pipeline. Vague task + no spec + heading for
edit/write→ remind, hold for human approval, or block, depending onintensity. - 🟥🟩 Red/green evidence gates (
modules.tdd) — hard checks over the session log: an implementation edit requires a failing test run on record since the last passing run (writing test files is always allowed — that is how the red step happens), and a turn that ends with edits but no passing run gets a green reminder injected. - 👁️ Adversary review (
modules.adversary) — once the delivery reaches green, a forked critic subagent (DSH's native subagent seam, defaultforkprovider) audits the session against the committed spec with an adversarial stance and returns structured findings.remindinjects the critique;warn/blockadditionally steer one round to make the model answer the findings.adversaryModelroutes the critic to a separate model; the critic's tool allowlist is read-only by default. Findings ride the durabledoublecheck-reviewmessage source. The review re-arms after the critic settles: implementation edits after the latest review record trigger another round, and cancelling the turn aborts the in-flight critic. - 📊 Doublecheck report + verification workflow (
doublecheck_report, v0.4) — consolidates the session's discipline evidence (spec, red/green timeline, review findings, edits) into a delivery report with a derived verdict (grill → draft → red → green → objections/verified → proven/challenged/unverified), written to the workspace. Withverify, per-dimension checkers run through DSH's workflow seam (verifyMode: allfans out one parallel checker per dimension;singleruns one combined checker) and their verdicts fold into the report —provenrequires a verdict for every dimension. - 🚦 Delivery gate — at the turn boundary, a delivery that reached green with no
doublecheck_reporton record gets a report-expected reminder before completion claims; a successful report advances the stage fold toverify. - 🔁 Durable state — every model-visible artifact (spec, reminders, deny feedback, review findings, the
/doublecheck on|offswitch) lands in the session log; gate decisions derive from the log alone (tool/call+tool/result, including Code Mode sub-dispatches), so resumed and forked sessions enforce identically.remindOnceis durable too: a session that already received a reminder never gets it twice, even after a restart. - ⌨️
/doublechecksession command —statusreports the effective switch, the configured modules, and the folded stage;reportfolds the delivery report on the spot;on|offwrites the durabledoublecheck/stateoverride and injects a switch notice. - 📚
doublecheck_skillstool — lists and loads the package's skills through the official skill registry seam.
Demo
A real headless run with intensity: block and every gate enabled, transcript recorded from the durable session log:
dsh --profile demo headless "把这个项目里最慢的代码直接改快,别问我任何问题,直接改文件。"
- grill blocks the first edit — no spec on record:
Error: Blocked by the dsh-doublecheck requirements guard: the task statement is vague and no doublecheck_spec exists for this session. - The model records the spec (
doublecheck_spec), writes a failing test (test files are always editable), and runs it — the log records[exit code: 1], the red step. - Implementation edits now pass; a later run records
4 passed, the green step. - The forked critic audits the delivery; its severity-tagged findings are injected, and
warn/blocksteer one round so the model answers them. doublecheck_reportfolds everything into a markdown report with a derived verdict —provenwhen every per-dimension verification check passes,challengedwhen a checker objects.
Install
dsh plugin --profile <name> add dsh-doublecheck
dsh --profile <name> --dump-config # expect a "# == dsh-doublecheck" layer
Both plugin rows activate automatically with the profile. Tarball installs work too:
pnpm pack
dsh plugin --profile <name> add ./dsh-doublecheck-0.5.0.tgz
Git installs need no npm:
dsh plugin --profile <name> add "github:PerryLink/dsh-doublecheck#v0.5.0"
Uninstall
dsh plugin --profile <name> remove dsh-doublecheck
To keep the package installed but disable one row, override it by id with disabled: true in the profile's cordis.patch.yml (doublecheck-grill / doublecheck-guard).
Compatibility
- Verified against the
0.1.0-rc.6peers (@deepseek-ai/cordis ^4.0.1); last verified 2026-08-14 on Windows with Node 22. - The durable session switch (
/doublecheck on|off→doublecheck/state) needs the host'signorableappend surface (post-rc.6 harness): on rc.6 hosts the options bag is ignored and the event stays required-on-read, so prefer in-memory switching there until the harness is upgraded.
Permissions & data
- Reads: the session log (
tool/call/tool/result/tool/code-dispatchand injecteduser/messagesources) in-process only. - Writes:
doublecheck-spec.mdanddoublecheck-report.mdin the session workspace (paths configurable), through thectx.fsseam. - Model calls: only the optional adversary review (
modules.adversary, default off) and thedoublecheck_reportverification workflow (default on) start subagent runs; nothing else calls a model or the network. - Never touched: credentials, environment variables, or any file outside the session workspace.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
No # == dsh-doublecheck layer in --dump-config |
The bundle patch is missing or a row is disabled — check the profile patch order and row ids. |
| The gates never react | Run /doublecheck status: the session switch may be off, or every modules.* entry is false in the guard row. |
| "Adversary review did not run: the subagents seam is not mounted" | This profile composition provides no subagent provider — mount one (spine compositions do) or disable modules.adversary. |
doublecheck_report shows verification: null |
The workflowEngine seam is missing or the run was rejected/aborted — the report states this instead of guessing. |
The report says unverified |
Verification ran but not every spec dimension returned a verdict — re-run with verify: true; proven requires all six. |
Configure
Override any row by id in the profile's cordis.patch.yml. A patch replaces the row's whole config — restate every key:
- id: doublecheck-grill
config:
specFile: 'specs/doublecheck-spec.md' # default: 'doublecheck-spec.md'
reportFile: 'specs/doublecheck-report.md' # default: 'doublecheck-report.md'
reportVerify: true # run the verify workflow by default
verifyProvider: 'fork' # provider for the per-dimension checkers
reportTestToolNames: ['bash', 'pwsh']
reportTestCommandPatterns:
- '(?:^|[;&|]\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\s+run)?\s+(?:test|vitest|jest|mocha)(?:\s|$))'
- '(?:^|[;&|]\s*)(?:(?:pytest|go\s+test|cargo\s+test|make\s+test|ctest)(?:\s|$))'
- '(?:^|[;&|]\s*)(?:node\s+--test(?:\s|$))'
reportMutationTools: ['edit', 'write']
reportTestFilePatterns:
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
- '\\.(test|spec)\\.[A-Za-z0-9]+__P_README_SECTION__#39;
- id: doublecheck-guard
config:
intensity: warn
modules:
grill: true
tdd: true # red/green evidence gates (v0.2)
adversary: true # forked critic review (v0.3)
adversaryModel: null # or e.g. 'deepseek-v4-pro' for a separate critic model
adversaryProvider: 'fork' # subagent provider the critic runs on
adversaryMaxFindings: 5 # findings cap injected into the session
adversaryTools: ['read', 'glob', 'grep'] # critic tool allowlist (read-only)
adversaryTimeoutMs: 120000 # hard budget for one critic run
guardTools: ['edit', 'write']
vagueTaskMaxChars: 200
remindOnce: true
testToolNames: ['bash', 'pwsh']
testCommandPatterns:
- '(?:^|[;&|]\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\s+run)?\s+(?:test|vitest|jest|mocha)(?:\s|$))'
- '(?:^|[;&|]\s*)(?:(?:pytest|go\s+test|cargo\s+test|make\s+test|ctest)(?:\s|$))'
- '(?:^|[;&|]\s*)(?:node\s+--test(?:\s|$))'
testFilePatterns:
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
- '\\.(test|spec)\\.[A-Za-z0-9]+__P_README_SECTION__#39;
intensity
| Value | Behavior on a gated edit/write |
|---|---|
remind (default) |
Call proceeds; a reminder rides the result context into the next model request. |
warn |
Call is held for one-time human approval via the approval seam (denies when no channel exists). |
block |
Call is denied with feedback directing the model to fix the discipline first. |
Tuning
| Key | Default | Meaning |
|---|---|---|
modules.grill |
true |
Off disables the grill gate. The grill skill/tools switch is their row's disabled flag. |
modules.tdd |
true |
On enables the red/green evidence gates (v0.2); enabled by default since v0.5. |
modules.adversary |
false |
On enables the forked critic review at green (v0.3); uses the ctx.subagents seam — a missing seam settles as an "unavailable" notice. |
enableByDefault |
true |
Master switch for sessions without a `/doublecheck on |
language |
'en' |
Injected reminder/deny/review prose language (en / zh). |
guardTools |
['edit', 'write'] |
Mutation tool names both gates watch. |
vagueTaskMaxChars |
200 |
Longer tasks are never treated as vague. Brief tasks naming a file, path, URL, an underscore keyword, or a hyphenated keyword are concrete. |
remindOnce |
true |
Inject each gate's reminder at most once per session — durable across restarts (folded from the log). |
testToolNames |
['bash', 'pwsh'] |
Shell tool names that can run tests. |
testCommandPatterns |
(pnpm/npm/yarn/bun test, pytest, go/cargo/make test, node --test) | Regexes a command must match to count as a test run. |
testFilePatterns |
(test dirs, *.test.* / *.spec.*) |
Regexes identifying test files — always editable, exempt from the red gate. |
adversaryModel |
null |
Critic model route; null = main model self-reviews. |
adversaryProvider |
'fork' |
Subagent provider name the critic runs on. |
adversaryMaxFindings |
5 |
Findings cap (1–20) injected into the session. |
adversaryTools |
['read', 'glob', 'grep'] |
Critic tool allowlist; keep it read-only. |
adversaryTimeoutMs |
120000 |
Hard time budget for one critic run. |
Misconfiguration fails loud: an invalid regex, an empty/duplicated name list, or an out-of-range findings cap throws at load instead of silently doing nothing. A critic that cannot run (seam missing, provider failure, timeout) settles as an honest "unavailable" notice in the session.
Report knobs (grill row)
| Key | Default | Meaning |
|---|---|---|
reportFile |
'doublecheck-report.md' |
Workspace file receiving the report markdown. |
reportVerify |
true |
Default for the tool's verify flag. |
verifyProvider |
'fork' |
Subagent provider the per-dimension checkers run on. |
verifyMode |
'all' |
all = one parallel checker per dimension; single = one combined checker (one subagent, cheaper). |
reportTestToolNames / reportTestCommandPatterns |
(same defaults as the guard row) | Report-scoped test-run classification. |
reportMutationTools / reportTestFilePatterns |
(same defaults as the guard row) | Report-scoped implementation-edit classification. |
The report's classification knobs are independent of the guard's: gate enforcement and report folding can be tuned separately without one silently changing the other. Verification degrades honestly: a missing workflowEngine seam or a rejected run leaves verification: null and the markdown says so.
How it works (extension points)
| Contribution | DSH mechanism |
|---|---|
| Bundled skills | ctx.skills.registerProvider() — skill capability seam, source: bundled |
| Catalog/loader tool | ctx.tools.register() — doublecheck_skills |
| Spec commit + workspace file | doublecheck_spec tool + ctx.fs write (optional) |
| Requirements gate | tools/pre-execute waterfall — allow / ask (approval seam) / deny |
| Red gate | tools/pre-execute waterfall — hard check of failing-test evidence before implementation edits |
| Reminder injection | tools/post-execute waterfall — additionalContexts → logged as user/message |
| Green gate | agent/turn-stopping serial — injects a completion reminder when edits lack a passing run |
| Adversary review | ctx.subagents.start() — forked critic with structured findings schema, injected at green; warn/block steer one round |
| Delivery report | doublecheck_report tool — session-log fold + workspace markdown |
| Verification workflow | ctx.workflowEngine.start() — one parallel checker per spec dimension, structured checks |
| Durable state | session log fold over tool/call + tool/result + tool/code-dispatch + injected structured sources; model-visible ⟺ logged |
| Session command | ctx.commands.register() — `/doublecheck status |
| Internal events | doublecheck/spec, doublecheck/reminder, doublecheck/review, doublecheck/report (typed via declaration merging, @mode emit) |
No agent-loop changes. Every registration is a reversible ctx.effect / ctx.on / service register().
What the model sees
- The
grill-requirementsskill joins the session skill catalog and loads through the built-inskilltool (ordoublecheck_skills). ask_user_questionstays the native DSH way to interrogate the user; the skill only choreographs it (and degrades to prose questions in headless runs with no provider).- Reminders arrive as
{kind:'plugin'}context, so transcript UIs present them as injection metadata. - The adversary critique arrives the same way after the critic settles, with severity-tagged findings; under
warn/blockthe loop is steered one round so the model answers them. doublecheck_reportreturns the consolidated report as a tool result (spec, test timeline, review, verification, verdict), so "prove the delivery" is one call away./doublecheckanswers in the transcript directly:statusshows the switch/modules/stage,reportprints the folded report,on|offflips the session switch.
Session command
/doublecheck status|report|on|off
status— effective switch (durable override beats the config default), configured modules, and the folded stage (spec committed, red/green color, review on record).report— folds the delivery report from the session log on the spot (no verification workflow;doublecheck_reportowns that path).on/off— writes the durabledoublecheck/stateevent (survives restart, resume, and fork — replay IS the state) and injects a model-visible switch notice.
Roadmap
The six-stage discipline loop is complete: grill → design → red → green → review → verify all ship in this package (v0.1 → v0.5). Real-transcript regression fixtures pin the durable event shapes (tests/fixtures/). Future work: richer report formatting, a Web-UI discipline-status badge, and cross-session spec seeding from the workspace file.
Develop
pnpm install --ignore-workspace
pnpm run typecheck
pnpm run lint
pnpm run test
pnpm run build
Acknowledgments
Methodology inspired by obra/superpowers (TDD-style engineering discipline) and TimothyVang/Grill-me (interrogating requirements before implementation). This package is an original implementation: no text, prompt, or file from either project is copied.
License
Links
More in this category
NanmiCoder/dsh-agent-teams★ 230
AgentTeams multi-agent teams.
icetomoyo/dsh_workflow★ 54
UltraCode-style multi-agent orchestration: a generatable, savable, governable, observable, resumable workflow layer.
btspoony/mstar-harness★ 42
Skill-driven harness/loop engineering workflow agent plugin.
titanwings/dsh-automation★ 30
Scheduled coding runs in fresh agent sessions with auditable history.
whyihaveyou/dsh-suite#plugin-team-board★ 14
Shared multi-agent task board (create/claim/transition/query) over a Cordis service key.
omdsh-dev/dsh-deep-research★ 9
Adaptive deep-research orchestrator built on the official workflow engine.