Blog

DeepSeek Harness: a hot-swappable loop is not a production workflow

August 19, 2026

中文

In mid-August DeepSeek shipped V4-Pro and DeepSeek Harness (dsh) together. The repo crossed 100k GitHub stars in a few days. The story became “open-source Claude Code.” The slogans are sticky: agent = model + harness, and everything is a plugin—model, tools, session, sandbox, even the agent loop—all Cordis plugins you can swap from config.

deepseek-ai/deepseek-harness

MIT-licensed developer preview: everything is a plugin, including the model adapter, tool registry, session log, and the agent loop.

View on GitHub

Stars prove hunger for a harness you can take apart. They do not prove this preview can run a stable job. The README says it in capitals: THERE WILL BE COMPATIBILITY-BREAKING CHANGES. The core repo is not taking external PRs. I am reading official architecture docs and the Cordis paper, not reporting a bake-off. The rest of this post stays in that scope.

The paper is about plugins, not a smarter agent

The underlying paper is by Peking University + DeepSeek, A Programming Paradigm for Spatiotemporal Composability (preprint, 13 Aug 2026). It does not claim a SWE-bench lift. It formalizes two properties:

  • Temporal composability: unmount a component and its side effects fully revert.
  • Spatial composability: components declare dependencies; the kernel wires and rewires them.

Someone on HN called Cordis a DI container with destructor propagation. That is rude and directionally right. Cordis is about mounting and unmounting in-process without leftover registrations. That is not the same problem as “the agent got more reliable.”

Making the loop a plugin only makes sense if the harness itself is the experimental variable: swap the loop, swap the tool surface, let the agent mount its own plugins. Self-evolving harnesses are a use case in the paper, not a side demo. A lab wants that freedom. Production orchestration usually wants the opposite: freeze the loop and pin the invariants.

A “cognitive trajectory” is not a chat log

The second design rule is the one worth stealing: every run is traceable. The invariant is strict—if the model saw it, it must be in the log. LLM message history is derived from an append-only session log. It is not stored as a separate chat.

That is a different layer from “the user said X, the assistant said Y.” Suppose the user only says: “fix the failing test.”

The chat log is: ran the tests, patched foo.ts, done.

The trajectory records the exact bundle sent to the model on each call:

  1. The assembled system prompt (whichever plugins were mounted)
  2. The tool schemas present on that call
  3. Injections the user never typed: AGENTS.md, a skill, a file-change notice
  4. Raw tool arguments as the model emitted them, not a cleaned rewrite
  5. The truncated test failure fed back to the model—not the full log on disk
  6. If compaction ran: which prior messages were replaced by a summary

Same user sentence, drop web search, mount a skill, or swap the loop, and the chat looks identical while the trajectory is not. If you later train on “saw this → called that tool,” you are training on the bundle, not on the user’s one-liner.

For a lab that trains models, making trajectories portable is a coherent motive. The public text says trace, replay, fork. I have no evidence of their internal post-training pipe, and I will not promote “this is the RL factory” to a verified fact. What is solid: the same log serves eval, debugging, and—if they want it—post-training.

Hot-plug: the next call sees a different world

People flatten hot-plug to “the tool list can change every round.” That is the visible part. It is not the whole part.

Same “fix the failing test.” Step 1 runs Standard loop + bash + editor + web search. After npm test, you (or the agent) unmount search and mount an internal jira plugin without restarting the process. Cordis has to unwind search’s schemas, prompt sections, and listeners, then mount jira. The second model call sees a different tool surface.

Round 1 tool surfaceRound 2 tool surfacebasheditorweb searchbasheditorjiraunmount searchmount jirasame process, no restart — the model only sees the surface on the right

More extreme: replace agent-loop with Code mode. Later steps are no longer one tool_call after another. The model writes TypeScript that drives existing tools inside an isolated worker. Still no restart.

That is not “install MCP and open a new chat.” It is changing the next model-visible bundle in a live run. Unmount has to revert cleanly, or a ghost schema from the last round poisons the next one—and then both eval and post-training eat dirty trajectories.

The cost is obvious. Mid-run plugin changes make the same task hard to reproduce unless you pin the plugin tree that produced it. The preview also promises breaking changes. That is not sloppiness. It is what you get when the harness is a bench, not a frozen control plane.

“The model writes code that becomes a tool” is two features

Marketing often describes Code mode as the model inventing tools. Split it.

Code mode does not register a new tool. Existing tools become a TypeScript SDK. The model writes an ephemeral program for this task:

const fail = await tools.bash("npm test")
const files = await tools.grep("expected", "src/")
await tools.edit("src/foo.ts", old, neu)

Five model round-trips collapse into one call. The program does not join the toolbox.

Self-mounting a plugin is the actual “write a tool”: author a Cordis plugin, hot-mount it, and the next round’s schema grows an entry. That is hot-plug, not Code mode.

The first saves round-trips; evaluate it as an optional capability. The second changes the capability boundary, which is also where task instability comes from. Neither is a workflow in the Temporal sense.

Three logs. Do not weld a fourth.

I maintain two harness-shaped systems. Kocoro is the single-agent runtime on the machine / IM / Desktop; the loop in internal/agent is frozen on purpose. Shannon is server-side multi-agent orchestration; the Go orchestrator runs on Temporal. DSH is a third thing: a local kernel for the lab.

Kocoro-lab/Kocoro

Go single-agent runtime: MCP-compatible, the loop pinned, checkpointed for long jobs.

View on GitHub
DSH session logKocoro sessionShannon’s Temporal history
RecordsEvery model-visible stepUser dialogue + compaction checkpointControl flow: activities, child workflows, timers, signals
AnswersWhat bundle the model actually sawWhat was said, and whether this turn can resumeWhere the job stuck, who approved, why it retried
After a crashThe app restores the sessionThe app restores the turnThe infra replays history and continues
FitsEval, replay, harness editsA continuous product conversationLong jobs, humans in the loop, multi-agent

DSH’s “model-visible must be logged” and Temporal’s “execution must be replayable” rhyme. They are not the same scope. One protects the prompt. The other protects orchestration. Cordis handles in-process mount/unmount. Temporal handles surviving across processes. A DI kernel does not replace a durable workflow engine.

Shannon already swaps strategies as Temporal workflows (DAG / ReAct / Swarm) and versions them with GetVersion(). That is the production form of a “replaceable loop”: replayable and versioned, not hot-mounted. Kocoro keeps cache, compaction, approvals, and permissions inside the loop because those invariants drift the moment they become config soup—I wrote about that discipline in mid-turn checkpointing and the Kocoro harness post. Plugin-izing the loop is an invitation for that drift.

The same kinds of swap, two contracts

DSH makes everything a plugin because it wants to be a framework. One Cordis kernel, swap a profile or bundle, and you get Standard, Minimal, or Creator—or nest Claude Code as a sub-agent. The user is a developer recomposing agents.

Kocoro’s user is different: one Mac, several IM routes, local files, and GUI at once. The extension surface is already layered; it just is not called a plugin. Tools register local-first, then MCP, then gateway, and each run clones runtime config so concurrent sessions do not share mutable state. What lines up with DSH plugins is those layers, not an unloadable loop:

What gets swappedIn DSHIn Kocoro
Knowledge / procedureskill pluginSkill (marketplace + ClawHub)
Out-of-process toolstool / MCP pluginMCP
Cloud toolsswap an adapterGateway / integration
Persona and trimmingpreset / bundleNamed agent
Execution gearswap the loop or a profileExecution profile
The agent loopalso a plugin, hot-unmountablepinned in loop.go

For a framework, an unloadable loop is a feature. For this local contract, the loop is the contract: iteration caps, mid-turn drain, date rollover, stale-screenshot filtering, cache_break. Permissions, approvals, memory, and channel binding fail closed in the same way—they are not flavor settings. Tool schemas also have to stay in stable order for the prompt cache; scramble that with hot-plug and the hit rate falls over.

So the contrast is not “DSH is more open.” It is who is allowed to recompose the system. IM connectors, model adapters, and per-scene tool packs can still be explicit install surfaces. They do not need to drop the loop, permissions, and cache into one reversible-effect kernel. Cordis-style spatiotemporal composition is a TypeScript runtime. Mapping that onto a Go daemon is a different product, not a smaller refactor.

Stars measure hunger, not readiness

DSH ships four modes: Standard, Code, Minimal, Creator. Minimal (bash + editor) is a clean eval harness; that is genuinely useful. Creator for composing presets is fine. What I did not find in the repo docs is an official, pinned-composition bake-off against Claude Code or Codex. Without that, do not read the star count as “already better to use.”

The sandbox docs are also precise: filesystem tiers (read-only / workspace-write / full access) are one vocabulary. Network and process visibility sit outside it. Enough for a coding assistant in a disposable directory. Not enough for a stable workflow that has to survive crashes, human approval, budgets, and audit. That is why Shannon puts orchestration on Temporal, not on a plugin tree.

What is actually attractive in DSH is still the hard log, and the way Minimal / Code narrow the tool surface for a job. That does not imply the loop should unmount, and it does not make Cordis and Temporal the same layer.

A framework wins on recomposition. A local agent wins on contract. Labs can hot-plug. A workflow that has to overnight on IM and the desktop needs the loop pinned first.