deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
1473382/dsh-popper
DeepSeek Harness plugin (not MCP): a falsification-driven correction loop — explicit claims, deterministic gates, mutually exclusive hypotheses — that turns agent retry loops into auditable evidence for better development quality.
PROJECT TOPICS
PROJECT README
中文版见 README.zh.md.
Where it lives. Popper is a plugin for DeepSeek Harness — not an MCP server, not a standalone CLI, not a skill. You mount it into a harness profile as a cordis layer; it runs inside the harness's own agent loop and gates real sessions. Its job is to raise development quality on DeepSeek Harness: it replaces retry-style debugging with an enforced falsification loop, so a failed attempt becomes auditable evidence instead of a repeated guess. If you do not run DeepSeek Harness, this plugin has nothing to attach to.
Stop retrying. Start falsifying.
LLM coding agents don't fail like compilers do. They drift: the model latches onto a root-cause guess, slaps a patch, the build fails — and it retries the same guess in new wording, burning tokens while compounding the wrong direction. Popper replaces blind retry with forced falsification. Before any risky change, the agent commits an explicit, checkable claim: a root-cause hypothesis and a predicted outcome bound to a deterministic gate. The gate verifies it. When a claim is falsified, Popper demands at least two mutually exclusive replacement hypotheses, each carrying its own discriminating experiment — and a falsified experiment can never be reused. The loop ends by frontier exhaustion, not by retry counts, so "give up" is a logical state, not an arbitrary number. Every step lands in an append-only SHA-256 evidence ledger you can audit. For DeepSeek Harness agents, Popper turns token burn into evidence, and babysitting into review.
Popper is a falsification-driven correction loop for agent sessions. Instead of letting a coding agent retry after a failure, it commits the agent to an explicit, evidence-checkable claim before risky work, runs a deterministic gate on that work, and when the gate falsifies the claim, forces the agent to propose mutually exclusive replacement hypotheses with discriminating experiments. What survives the loop is a fact; what dies is a hypothesis — on purpose.
The name is an homage: the loop is Karl Popper's method, enforced on an LLM coding session.
Model debug loops drift: an agent that failed a build tends to make the same guess with a new coat of paint. Popper replaces retry with falsify-and-revise:
predictedObservable and a discriminating experimentCommand.backstopRounds is only a cost safety valve.Trust-bearing parts are deterministic code (state machine, ledger, gate executor). Creative parts stay with the model, bounded by the corridor of falsified evidence.
cordis.yml starts it in observe mode: evidence recorded, nothing gated.strict. No contract -> pure observation; contract present -> strict. Both states are recorded, so any session can be replayed and the answer to "why was this step not gated" is a lookup, not a guess.write/exec/core changes), at step boundaries (hard gate at the end of an LLM turn), and at milestones (full verification).No polling, no per-token scanning. Listeners sit on the event stream; cost is paid only at the boundaries being gated. Arming itself is an event in the audit chain — who armed it, when, with which contract.
| Role | What it is | What it does |
|---|---|---|
| Judge | The plugin itself (plain JS, no thinking) | Parses claims, runs gates (deterministic commands), compares prediction vs result, writes the ledger, gates the next step |
| Thinker | The session's LLM | Proposes hypotheses and discriminating experiments — structurally constrained, not free-form |
| Infrastructure | Snapshot / git | True rollback |
| Observer | Human / interaction | Steps in at frontier stall |
Every trust-bearing decision is code: gate pass/fail vs predicted pass/fail, compared, judged. The plugin never asks a model "is this right?". The model still thinks in the same session context, but inside a structural corridor — when a gate falsifies a claim, the plugin injects a forced protocol message ("claim X was falsified by evidence E; give >=2 mutually exclusive hypotheses, each with a discriminating experiment") and enforces it two ways:
This is one brain plus an external mechanical judge — not "another agent takes over". When should a separate agent appear? At frontier stall: the in-session model's bias lives in its own context — the "confirmed" conclusion was its own, and rephrasing still enumerates its own guesses. Popper then spawns a cold subagent with no session seed, fed only the falsification ledger, to propose hypotheses from zero. Cold start discards sunk-cost bias — the correct antidote to confirmation bias. Its output writes back to the same ledger; no parallel world. (Planned — see Known limitations.)
The plugin evolves — but inside the skeleton, with evidence anchored outside it.
The only way recursion could self-destruct is the plugin rewriting its own verification rules. Four iron rules prevent it:
self-modification capability).In one sentence: evolution happens inside the skeleton; evidence is anchored outside it. The plugin can get smarter (choose gates, tune strategy, absorb history), but it can never convince itself to remove the gate.
Popper installs as a bundle: the package ships a cordis.patch.yml that inserts two plugin rows (popper and its popper-invariant companion) into a profile.
From GitHub (recommended — the repository ships prebuilt lib/):
dsh plugin --profile demo add github:1473382/dsh-popper
From a release tarball (see the Releases page):
dsh plugin --profile demo add ./deepseek-ai-dsh-popper-0.1.0-rc.7.tgz
The first add initializes the profile with @deepseek-ai/dsh-base; dsh appends the bundle to dsh.profile.bundles because the package declares dsh.bundle. Verify with dsh --profile demo --dump-config (you should see a # == @deepseek-ai/dsh-popper layer) and boot with dsh --profile demo.
Requirements: the host dsh installation provides the in-box packages (cordis, dsh-tools, dsh-llm, dsh-session, dsh-agent, dsh-invariants); the package declares them as peers. A profile that composes @deepseek-ai/dsh-base already satisfies them.
Publishing under a different npm scope: replace
@deepseek-aiwith your scope inpackage.json(name, deps, peers), incordis.patch.ymlrow names, and in thedsh-plugininstall command.
| Key | Default | Meaning |
|---|---|---|
mode |
observe |
observe: record the model's protocol without executing gates; strict: arm and execute gates |
backstopRounds |
6 |
consecutive protocol-invalid rounds before escalation |
noveltyRejectLimit |
3 |
consecutive novelty rejections -> frontier exhausted |
riskyTools |
[write, exec, patch, delete] |
tool names that trigger gate validation |
gateRegistry |
[] |
deterministic gates: { id, command, timeoutMs? }. In strict, must be non-empty; commands come only from the task contract, the model references gate ids |
falsificationToolName |
falsification |
model-facing protocol tool name |
gateTimeoutMs |
120000 |
gate execution timeout |
outputCapChars |
2000 |
gate output injected back (head + tail) |
strict with an empty gateRegistry or empty riskyTools fails loud at load.
Example cordis.patch.yml override (a later patch layer restates the whole row):
- id: popper
name: '@deepseek-ai/dsh-popper'
config:
mode: strict
gateRegistry:
- id: typecheck
command: pnpm run typecheck
timeoutMs: 120000
The model speaks through one tool, falsification, with three actions:
claim — commit root-cause hypothesis + predictedGateId + predictedOutcome before risky workhypotheses — after a falsification: >=2 mutually exclusive alternatives, each with predictedObservable + experimentCommandexperiment — run one whitelisted experiment command; results are gatedNotice texts (pinned, model-visible):
Popper armed (strict). Gates: {ids}. Risky calls must be backed by a committed falsification claim first. / Popper observing: recording evidence only, no gating. Set mode: strict plus gateRegistry to arm the loop.You performed the risky {tool} call without committing a claim. Next risky change must start with falsification action claim: root-cause hypothesis + predicted gate outcome (gate id from the task contract).Claim {id} was falsified by gate {gateId}. Your next action must be falsification action hypotheses: >=2 mutually exclusive new hypotheses, each with predictedObservable and experimentCommand. Repair is only allowed as a corollary of a chosen hypothesis.Experiment {cmd} is not among your selected hypotheses. Pick one of: {allowed}.A falsification step is pending: commit the demanded claim or hypotheses before further risky changes.Every ledger entry is appended to the session log as an falsification/ledger event (model-visible <=> logged). The hash chain, novelty accounting, and verdicts are deterministic and never rendered to the model.
plan/mode is active (folded from the session log, replay-safe, last wins); plan off suspends gating. Without plan-mode, strict config arms on first use.ctx.userQuestions (optional service): Resume resets the frontier budget, Disarm revokes the contract. With no answerer, one latched escalation required entry is recorded and no further risky change passes until a human decides.seq, sha256 prevHash chain with key-order-canonical hashing so the chain survives session-log round trips; verifyChain() detects tamper.spawn(..., { shell: true }) with a timeout and head/tail output truncation. There is no sandbox; gate commands are arbitrary local commands and must come only from the task contract's gateRegistry. Treat the gate allowlist with the same care as a build script.lib/; no prepare build runs. If you fork and rebuild, run pnpm run build in the package before installing from your fork.Observe mode adds ~0. Strict mode's overhead is dominated by extra turns of re-reading context: roughly +2–4% (0 falsifications), +10–20% (2–4), +30–60% (>=6); a pathological loop is rule-terminated. Full model in popper-plugin-design.md (repository root).
pnpm exec vitest run packages/guard/popper — 18 tests: state machine transitions (falsify -> hypotheses -> experiment), novelty rejection -> frontier exhaustion, contract revocation, resume budget reset, plan-arming fold, ledger tamper detection, and a real-composition suite driving a full agent loop with a scripted mock adapter.tsc -b packages/guard/popper/tsconfig.json builds types; tsdown bundles the publish entries (lib/index.js, lib/invariant.js).src/state-machine.ts (FSM), src/ledger.ts, src/plan-arming.ts, src/tool.ts (falsification tool), src/wiring.ts (agent-loop seam, gates, escalation), src/invariant.ts (companion).plan/mode activation; an explicit plan-approval event is not part of the signal.falsification/ledger, written with the envelope's ignorable: true marker. A host that does not know the type skips the event and loads the session normally; a host whose vocabulary includes it (deepseek-harness KNOWN_SESSION_EVENT_TYPES, regenerated by pnpm run verify-persistence-catalog) interprets the ledger in full. Merging this plugin into a harness fork requires that catalog step, or history loads still succeed but ledger events stay opaque.The GitHub dsh-plugin topic is the auto-indexing aggregation point; this repository already carries the topic, so topic-based channels pick it up without any application:
dsh-plugin topic (Oh-My-DSH syncs every 4 hours; dsh-community-plugins discovers through topic, npm, and dshmarket). No PR is needed.See also the official packaging guide: docs/user/develop/basic/publish.md in the DeepSeek-Harness repository.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。