dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
验收标准驱动的目标完成:锁定可验证的验收标准、跟踪任务依赖,以 reviewer 正式验收作为完成门禁(18 个 MCP 工具)。

English | 中文
Acceptance-criteria-driven goal completion for autonomous AI agents.
Prevents agents from prematurely declaring "done" by locking immutable acceptance criteria before work begins, tracking validation status with evidence, linking criteria to task progress, respecting dependency ordering, and enforcing completion checks.
One package, four runtimes, 22 install targets:
| Platform | How it connects | Turn-stopping enforcement |
|---|---|---|
| Claude Code | MCP stdio server (cli mcp) | Model voluntarily calls tools |
| Cursor | MCP stdio server (cli mcp) | Model voluntarily calls tools |
| Devin | MCP stdio server (cli mcp) | Model voluntarily calls tools |
| OpenClaw | Native plugin (openclaw-dist/, in-process, no stdio) | Model voluntarily calls tools |
| DeepSeek Harness | Cordis plugin (dist/dsh-plugin.js) | Yes — agent.steer() forces continuation |
| Any MCP client | stdio MCP server (cli mcp) | Model voluntarily calls tools |
| Any Agent Plugins client | plugin.json + mcp.json + skills | Model voluntarily calls tools |
| Any JS/TS runtime | Core engine (src/plugin/engine/, bundled in the CLI) | Programmatic — you control it |
The core state machine is zero-dependency. The cli mcp server, the dsh
Cordis plugin, and the OpenClaw native plugin all call the same shared engine
and goal manager — one implementation, four surfaces.
dist/cli.js install --target <name> registers the plugin for 22 clients:
Each adapter writes the client's native config (MCP server entries, skills,
plugin dirs, marketplace shims) from one generated portable package
(plugin.json / mcp.json / .mcp.json / skills/ / openclaw.plugin.json /
openclaw-dist/).
The CLI exposes 19 tools covering the full goal-acceptance lifecycle:
See MCP Tools below for the full list.
Each goal has its own event file under ${PLUGIN_DATA}/goals/{goalId}.json —multiple projects and windows can share one server without lock conflicts:
set_acceptance_criteria auto-creates a goal when none is activestart_goal begins a new independent goal (fresh criteria + task plan)switch_goal moves between goals; list_goals shows all with statusreset_goal deletes a messed-up goal so you can start overcurrent-goal.txt)set_acceptance_criteria accepts a role parameter (agent / reviewer /
dual). When role=agent, validate_criterion marks passed as
selfClaimed=true — can_complete_goal blocks completion until a reviewer
formally confirms. This breaks the "self-grading" loop where an agent both
does the work and signs off on it.
validate_criterion accepts evidence_type (command / file / url /
text). text evidence is flagged lowConfidence=true so reviewers can
spot subjective claims at a glance. command evidence (test output, CLI
results) is high-confidence.
set_task_plan lets you decompose a goal into atomic tasks, each with a
concrete deliverable. The engine validates: unique IDs, unambiguous
descriptions, non-empty deliverables, no self-dependencies, no unknown
dependencies, and no dependency cycles (including indirect cycles).
All state changes are append-only events. The engine replays events on every read, enabling durable persistence, exact state restoration across restarts, and a full audit trail of every decision.
MCP tool responses are slim by default (4-field summary). Pass verbose=true
for the full summary. This minimizes token overhead during normal operation.
src/
├── framework/ # Zero-dependency scaffold (manifest, registry, CLI,
│ # wizard, mcp-runtime, hook-runtime, installers)
├── plugin/
│ ├── engine/ # Event-sourced state machine (core)
│ ├── goal-manager.ts # Multi-goal manager (shared by all paths)
│ ├── tools.ts # 19 ToolDefs (manifest.tools data source)
│ ├── manifest.ts # Single source of identity
│ ├── dsh-plugin.ts # DeepSeek Harness Cordis plugin
│ ├── openclaw-plugin.ts # OpenClaw native plugin (typebox, in-process)
│ ├── openclaw-session-sync.ts # Sync active goal to OpenClaw SessionEntry.goal
│ ├── prompt.ts # dsh system-prompt guidance
│ ├── invariant.ts # dsh session invariant companion
│ └── targets/ # 22 install adapters
└── assets/ # SKILL.md + 8 companion skills ({{placeholders}})
build.mjs → dist/cli.js (CLI + MCP) + dist/hook.cjs + dist/dsh-plugin.js
+ dist/openclaw-plugin.js → openclaw-dist/
+ plugin.json / mcp.json / .mcp.json / marketplace.json
/ cordis.patch.yml / openclaw.plugin.json / skills/
All identity files are generated from src/plugin/manifest.ts at build
time and committed, so the repo is a valid install source for every client.
node dist/cli.js install # interactive wizard
node dist/cli.js install --target claude
node dist/cli.js install --target openclaw
node dist/cli.js list-targets # show all 22
This repo doubles as a self-hosted plugin marketplace — the plugin source is
./ (the repo root), so adding the repo makes the plugin discoverable:
# Claude Code (reads .claude-plugin/marketplace.json)
claude plugin marketplace add cckyros/goal-acceptance
# then: /plugin install goal-acceptance@cckyros
# Codex CLI / ChatGPT (reads .agents/plugins/marketplace.json)
codex plugin marketplace add cckyros/goal-acceptance
# then: codex plugin add goal-acceptance@cckyros
# Cursor (reads .cursor-plugin/marketplace.json): add the repo as a plugin
# marketplace in Settings → Plugins
The repo also carries server.json, the descriptor for the official MCP
registry (mcp-publisher publish after mcp-publisher login github —
namespace io.github.cckyros/goal-acceptance).
{
"mcpServers": {
"@cckyros/goal-acceptance": {
"type": "stdio",
"command": "node",
"args": ["/path/to/goal-acceptance/dist/cli.js", "mcp"],
"env": { "PLUGIN_DATA": "/path/to/persistent/data" }
}
}
}
Standalone:
# In-memory (resets on restart)
node dist/cli.js mcp
# Persistent across restarts
PLUGIN_DATA=/path/to/data node dist/cli.js mcp
If PLUGIN_DATA is not set, state is in-memory only (lost on restart).
set_acceptance_criteria with role=reviewer (you verify) or role=agent (agent self-claims, you confirm later)set_task_plan to decompose the goal into atomic tasks with deliverables and dependenciesupdate_task_status as tasks progress (pending — in_progress — completed)validate_criterion with evidence_type=command for high-confidence evidence. Default role=agent: passed criteria are self-claimed.confirm_criterion (independent reviewer agent only) with fresh evidence_type=command/file/url evidence. Converts self-claimed to formal pass.can_complete_goal to verify all required criteria are formally passedOrchestrated task loop (worker + reviewer subagents, single-layer review):
claim_task marks the next dependency-ready task in_progress and returns it with its linked criteria (the task-level acceptance contract)review_task with per-criterion verdicts. Verdicts are formal: a single validation layer, no self-claim/confirm chainin_progress, blockingCriteria is the rework feedback; fix and review_task again. On full pass the task completes and nextTask is returned, ready to claimcan_complete_goal (or goal-acceptance check) still guards overall goal completionopenclaw-dist/ carries the in-process plugin (bundle + minimal package.json
with the openclaw.extensions contract) and openclaw.plugin.json declares
the 18-tool contracts:
openclaw plugins install @cckyros/goal-acceptance
# or, for a local build:
openclaw plugins install /path/to/goal-acceptance/openclaw-dist
openclaw gateway restart
openclaw plugins list # goal-acceptance: loaded
The 19 tools are available in OpenClaw sessions. Shape: non-capability is
normal for tool plugins — tools are registered via defineToolPlugin.
After every tool that mutates goal state (start_goal, set_acceptance_criteria,
validate_criterion, confirm_criterion, amend_acceptance_criteria,
update_task_status, set_task_plan, run_and_validate, quick_start_goal,
switch_goal, reset_goal), the plugin mirrors the active acceptance goal into
OpenClaw's canonical SessionEntry.goal slot. This means OpenClaw's built-in
get_goal and update_goal tools see a live snapshot of the acceptance goal.
The mirror is write-through: calling update_goal directly updates
SessionEntry.goal, and subsequent plugin tools re-sync the slot from the
plugin's own event-sourced state.
dist/dsh-plugin.js is the only variant that can force the agent to
continue working when it tries to stop early. It intercepts
agent/turn-stopping and steers the agent back with dependency-aware priority
ordering.
# cordis.patch.yml (generated)
- insert:
- id: @cckyros/goal-acceptance
name: @cckyros/goal-acceptance
config: {}
The plugin:
policy:goal-acceptance system prompt section with task progress and next-actionable orderingagent/turn-stopping and steers the agent back with dependency-aware
priority ordering for pending work and self-claimed criteria awaiting reviewer confirmationNote: The dsh plugin requires the DeepSeek Harness host packages as peer dependencies (
@deepseek-ai/*), injected at runtime by the dsh profile.@deepseek-ai/*imports are devDependencies here for types only.
The zero-dependency state machine lives in src/plugin/engine/ and is
bundled into the CLI. Use it programmatically from this repo or copy the
folder into your project:
import { GoalAcceptanceEngine, InMemoryAcceptanceStore } from './src/plugin/engine/index.ts'
const engine = new GoalAcceptanceEngine(new InMemoryAcceptanceStore())
// Lock criteria before work begins
await engine.setCriteria([
{ id: 'api-200', description: 'GET /health returns 200', required: true, method: 'test', taskIds: ['task-1', 'task-2'] },
{ id: 'docs', description: 'README updated', required: false, method: 'manual', dependsOn: ['api-200'] },
])
// Update task progress as work proceeds
await engine.updateTaskStatus({ taskId: 'task-1', status: 'completed' })
await engine.updateTaskStatus({ taskId: 'task-2', status: 'completed' })
// When all linked tasks are done, the criterion is "ready to validate"
const summary = engine.summarize()
console.log(summary.readyToValidate.map(c => c.id)) // ['api-200']
// Record validation with evidence
await engine.validateCriterion({
criterionId: 'api-200',
status: 'passed',
evidence: 'curl /health -> HTTP 200 OK',
})
// Check if goal can complete
const { allowed, reason } = engine.canComplete()
console.log(allowed, reason)
// true, undefined
check and status expose the completion verdict without speaking MCP, so
external loop-drivers (ralph-orchestrator hooks, CI gates, shell scripts) can
use can_complete_goal as a hard backpressure gate. Both resolve the data
directory exactly like the MCP server (PLUGIN_DATA env / pluginData
config) — point the orchestrator and the agent's MCP server at the same dir.
goal-acceptance check # exit 0 when the goal may complete; exit 1 + the
# blocking criteria list on stderr otherwise
goal-acceptance status # JSON summary: goalId, allowed, criteria, counts
Wire check into ralph's pre.loop.complete hook with on_error: block:
the agent works the criteria via the goal-acceptance MCP server inside the
loop, and any LOOP_COMPLETE claim the criteria don't back is rejected, so
the loop keeps grinding until the goal is genuinely done. A ready-made
config lives at examples/ralph/ralph.yml:
hooks:
enabled: true
events:
pre.loop.complete:
- name: goal-acceptance-gate
command: ["goal-acceptance", "check"]
on_error: block
| Tool | Description |
|---|---|
set_acceptance_criteria | Lock the criteria list. Each criterion may link to task IDs and declare dependencies. Optional role parameter (agent/reviewer/dual, default agent) controls self-claim behavior. With the default, passed criteria require independent reviewer confirmation. Must be called before implementation. |
get_acceptance_criteria | Read current criteria, task progress, summary, task plan, ready-to-validate list, and next-actionable ordering. Optional verbose parameter (default true; pass false for slim summary only). |
set_task_plan | Set and lock the task decomposition plan. Each task must have a unique id, unambiguous description, and concrete deliverable. Dependency cycles are rejected. Requires criteria to be locked first. |
get_task_plan | Read the current task decomposition plan with live task statuses. |
validate_criterion | Record status (pending/in_progress/passed/failed/blocked/not_run) and evidence. passed and failed require evidence. Optional evidence_type (command/file/url/text, default text). When role=agent (default), passed is marked self-claimed. Optional verbose (default false). |
confirm_criterion | Reviewer-only. Confirm a self-claimed passed criterion with independent re-verification evidence. Requires evidence_type of command/file/url (text rejected). Converts self-claimed to formal pass, unblocking can_complete_goal. Must be called by an independent reviewer agent, not the agent that did the work. |
update_task_status | Update a linked task's status (pending/in_progress/completed/failed). When all tasks linked to a criterion are completed, it becomes ready to validate. Optional verbose (default false). |
claim_task | Task dispatch node: claim the next dependency-ready task (or a specific task_id) for a worker — marks it in_progress and returns the task + its linked criteria as the work contract. |
review_task | Task acceptance node: an independent reviewer records formal verdicts on the task's linked criteria (single validation layer — never self-claimed). All linked required criteria formally passed → task completes and dependents unlock (nextTask returned); otherwise blockingCriteria is the rework feedback for the next round. |
amend_acceptance_criteria | Append new criteria after the initial lock. Requires a reason. Existing criteria are not modified. |
update_criterion | Patch an existing criterion's declaration (description/required/method/verify_command/artifacts) with a required audit reason. Patching artifacts or verify_command on a passed criterion resets it to pending (forces re-validation). verify_command: null clears the pin. |
can_complete_goal | Check whether all required criteria are formally passed (self-claimed does not count). Returns { allowed: boolean, reason?: string }. |
start_goal | Start a new independent goal with fresh state (optional title). The new goal becomes active. Use when the current goal is locked and you need a new task. |
create_goal | Create a new named goal (name required, description optional) and set it as active. Always starts a fresh goal; metadata is shown by list_goals. |
list_goals | List all goals with ID, title, criteria counts, and active flag. |
switch_goal | Switch the active goal to an existing goal by ID. |
quick_start_goal | Convenience fast path: start or rotate a goal, lock acceptance criteria, and optionally set a task plan in one call. |
run_and_validate | Execute a shell command and validate a criterion in one call. Captures stdout/stderr/exitCode as evidence_type=command evidence. |
reset_goal | Delete the current goal and all its data permanently. |
The plugin ships 9 skills (built from src/assets/ with placeholder filling):
skills/
├── goal-acceptance/SKILL.md # Main skill: workflow + companion index
├── goal-planning/ # Decompose into coverage-complete criteria first
├── set-acceptance-criteria/ # Lock criteria (criterion quality rules)
├── amend-acceptance-criteria/ # Append criteria when requirements expand
├── get-acceptance-criteria/ # Read current criteria and progress
├── update-task-status/ # Track task status linked to criteria
├── validate-criterion/ # Record status + evidence from real execution
├── confirm-criterion/ # Independent reviewer confirmation (fresh evidence)
└── can-complete-goal/ # Completion gate; self-claimed passes block
pending -> in_progress -> passed | | +-> failed +-> selfClaimed -> confirm_criterion -> formal pass | +-> blocked +-> not_run (non-required criteria only)
| Status | Meaning | Evidence required |
|---|---|---|
pending | Not yet started | No |
in_progress | Being worked on | No |
passed | Verified successful | Yes |
failed | Verified unsuccessful | Yes |
blocked | Cannot verify in current environment | No |
not_run | Explicitly skipped (non-required only) | No |
canComplete() returns { allowed: boolean, reason?: string }:
passed (not self-claimed), or no criteria are locked.pending, in_progress, failed,
blocked, or not_run.passed but some are
selfClaimed=true (set by an agent, not yet reviewer-confirmed). The reason
will indicate how many are awaiting reviewer confirmation.The engine is event-sourced. The store holds an append-only list of:
goal-acceptance/set —locks the criteria list (with role)goal-acceptance/task-plan —locks the task decomposition plangoal-acceptance/validate —updates one criterion's status (with evidence type, self-claimed flag)goal-acceptance/task-update —updates a linked task's statusgoal-acceptance/amend —appends new criteria after the initial lockOn every read, the engine replays events from the store. This enables:
Implement GoalAcceptanceStore for your persistence backend:
import type { GoalAcceptanceStore, GoalAcceptanceEvent } from './src/plugin/engine/index.ts'
class MyDbStore implements GoalAcceptanceStore {
get events(): readonly GoalAcceptanceEvent[] {
// Return all events in append order
}
async append(event: GoalAcceptanceEvent): Promise<void> {
// Persist the event
}
}
| Capability | dsh Cordis plugin | CLI MCP server | Agent Plugin | OpenClaw native |
|---|---|---|---|---|
| Model tools | 19 tools (see MCP Tools) | 19 tools (see MCP Tools) | same as MCP | same as MCP (in-process) |
| System prompt / Skills | policy:goal-acceptance | skills/ | skills/ | skills/ |
| Turn-stopping enforcement | yes (agent.steer(), dependency-aware) | no | no | no |
| Cross-client portable | no (Harness only) | yes (any MCP client) | yes (any Agent Plugins client) | no (OpenClaw only) |
| Persistent state | dsh-session log | $PLUGIN_DATA/goals/ | same as MCP | same as MCP |
| Dual-role validation | yes (role parameter) | yes (role parameter) | yes | yes |
| Typed evidence | yes (evidence_type parameter) | yes (evidence_type parameter) | yes | yes |
| Task decomposition plan | yes (set_task_plan / get_task_plan) | yes (set_task_plan / get_task_plan) | yes | yes |
| Slim responses | no | yes (verbose parameter) | yes | yes |
| In-process calls (no stdio) | yes | no | no | yes |
The dsh Cordis plugin is the only variant that can force the agent to continue working when it tries to stop early. The MCP, Agent Plugin, and OpenClaw native variants rely on the model voluntarily calling the tools and following skill instructions.
src/
├── cli-entry.ts # CLI entry (bundled to dist/cli.js)
├── hook-entry.ts # Read hook (bundled to dist/hook.cjs)
├── framework/ # Scaffold framework (manifest, registry, CLI,
│ # wizard, mcp-runtime, hook-runtime, paths, cache)
├── plugin/
│ ├── manifest.ts # Identity single source (name, tools, markers, config)
│ ├── engine/ # Event-sourced state machine (zero-dependency)
│ ├── goal-manager.ts # Multi-goal manager + stores (shared by all paths)
│ ├── tools.ts # 19 ToolDefs
│ ├── dsh-plugin.ts # Cordis plugin (service, tools, steer, prompt)
│ ├── openclaw-plugin.ts # OpenClaw native plugin
│ ├── openclaw-session-sync.ts # Sync active goal to OpenClaw SessionEntry.goal
│ ├── prompt.ts # dsh system-prompt guidance
│ ├── invariant.ts # dsh session invariant
│ └── targets/ # 22 install adapters
└── assets/ # SKILL.md + 8 companion skills + cover image
tests/ # node --test suites (engine, mcp, dsh, targets...)
build.mjs # esbuild bundles + identity file generation
dist/ # cli.js + hook.cjs + dsh-plugin.js (committed)
openclaw-dist/ # openclaw-plugin.js + package.json (committed)
pnpm install
npm run verify # build → typecheck → node --test
node dist/cli.js --help
MIT