← Back to home@xisnn

harness2-dsh

Convert a Google Research Harness2 harness tree into a DeepSeek Harness (DSH) agent-preset bundle

Stars
0
Language
Python
Created
Oct 22, 2026
Updated
Oct 22, 2026

Introduction

harness2-dsh

Turn a Google Research Harness² harness tree into a DeepSeek Harness (DSH) agent-preset bundle you can install and select like any other preset.

Harness² searches for a better agent harness around a frozen model and writes the winner to disk as a plain directory. DSH runs agents from declarative presets. This tool is the bridge between the two: it reads the harness directory and emits an installable DSH bundle.

harness2 run  ──▶  harness tree (systemprompt.md, guardrails.md, skills/, scripts/, ...)
                         │
                         ▼  harness2-dsh convert
                   DSH bundle (package.json + cordis.patch.yml + skills/ + scripts/)
                         │
                         ▼  dsh plugin --profile <p> add <bundle>
                   a selectable agent preset

Why

Running Harness² end to end means OpenCode or Codex, a Linux toolchain, and model credentials. If you already run DSH, you can keep the part that matters — the harness it found — and run it natively.

Why not just install one of the existing Harness² DSH plugins?

As of 2026-10-22 there are about seven community attempts at "Harness² on DSH", all started within days of Harness² itself and all at 0–2 stars. None appears in the official DSH plugin catalog (3,261 entries, zero Harness2 matches), and each falls into one of three shapes:

ApproachWhat it doesWhy it is not enough on its own
Sidecar Python driverReuses Harness²'s own recursion loop, drives dsh as the executorPOSIX-only; needs a separate venv and a second install step
In-tree Cordis pluginReimplements propose–probe–compose inside DSHLarge surface; the ones published so far ship a demo adapter, not a task executor
This toolTransports a harness Harness² already found into a DSH presetDoes not run the search itself — and that is the point

This project deliberately does one thing: it is the missing transport step. It does not reimplement Harness², does not call a model, and has no opinion about whether a harness is good. That makes it small enough to be correct, testable without DSH installed, and usable alongside whichever search approach you pick — run Harness² wherever you like, then convert the winner.

It also catches two failure modes that silently produce a broken preset:

  • DSH requires kebab-case skill names; Harness² accepts underscores. ledger_math is silently dropped by DSH. This tool renames it and rewrites the frontmatter to match.
  • DSH does not advertise a skill without a description:. This tool synthesises one and tells you.

Install

pip install harness2-dsh
# or, from a checkout:
pip install -e .

Python 3.10+, no dependencies.

Use

# 1. see what a harness contains
harness2-dsh inspect ./library/k1/agg/accounting/harnesses/ledger-2

# 2. convert it
harness2-dsh convert ./library/k1/agg/accounting/harnesses/ledger-2 \
    --out ./bundles/ledger-recon \
    --preset-id ledger-recon \
    --name "Ledger Recon"

# 3. install and pick it in a new session
dsh plugin --profile web add ./bundles/ledger-recon

convert accepts three input shapes, so you rarely need to hunt for the tree by hand:

You point atIt converts
a harness directory (systemprompt.md present)that tree
a library entry (harness/ + EXPERIENCE.md)the adopted harness
a task run directory (summary.json + harnesses/)the harness selection actually adopted
a domain library directory with one entrythat entry

Options

FlagDefaultMeaning
--preset-idfrom --name / output dirlowercase letters, digits, hyphens
--namethe iddisplay name in the preset picker
--descriptiongeneratedpreset description
--order50roster position
--persona-modeappendappend keeps DSH's own system prompt and adds the harness text; replace makes the harness text the whole system prompt
--skill-path-modepackagepackage resolves the installed bundle by name, so the bundle stays portable; absolute bakes the resolved path (this machine only); dsh-home emits dshHomePath(...) for bundles under $DSH_HOME
--with-subagentsoffconvert sub_agents/ into child presets
--no-scriptsoffskip scripts/
--profilewebprofile named in the generated install hint

What maps to what

Harness² componentBecomes in DSH
systemprompt.md, guardrails.md, LongTermMEMORY.md, tool_descriptions/tool_guidance.mdone always-on block in the preset's dsh-persona prefix, in Harness²'s own order
skills/<n>/SKILL.md + skills/<n>/scripts/real DSH skills under the bundle, wired through dsh-skill-filesystem.customSkillDirs
scripts/<n>.pycopied into the bundle; the persona block names where they live
sub_agents/<n>/AGENT.mda child agent preset plus a dsh-tool-subagent row (opt-in)
plugin/<n>.tsnot converted — OpenCode plugin modules are not Cordis plugins. Reported, never silently dropped

Portable by default

A converted bundle contains no machine-specific paths, so you can commit it, hand it to a colleague, or download someone else's. The preset locates its skills by asking the Loader to resolve the bundle's own package name from the profile's node_modules:

customSkillDirs:
  - !!js process.getBuiltinModule('node:path').join(
      process.getBuiltinModule('node:path').dirname(
        process.getBuiltinModule('node:module').createRequire(baseUrl)
          .resolve('dsh-harness2-<preset-id>/package.json')),
      'skills')

This is the same mechanism the shipped standard preset uses for its bundled skills. It was verified by moving a generated bundle to a different directory, installing it there, and loading its skill.

Use --skill-path-mode absolute only when you want a fixed path (single-machine use, or the bundle lives somewhere the package resolver cannot see). --skill-path-mode dsh-home remains available for bundles installed under $DSH_HOME.

Note: dsh-agent-preset needs the agentPresets service, which the web profile provides and the headless profile does not. A converted preset is selectable in the Web UI; a headless one-shot run cannot mount it. Plan verification accordingly.

Two normalisations worth knowing

  • Skill names. DSH requires kebab-case (^[a-z0-9]+(?:-[a-z0-9]+)*$); Harness² accepts underscores. ledger_math becomes ledger-math, and the generated SKILL.md frontmatter is rewritten to match.
  • Missing descriptions. DSH silently does not advertise a skill without a description:. The converter synthesises one from the body and records a note, rather than shipping an invisible skill.

Verify the install

dsh --profile <p> --dump-config | grep -A3 preset-<id>

Then start a new session and choose the preset. To confirm the skills are actually visible to the model, ask it to load one by name.

Development

pip install -e '.[dev]'
pytest -q
ruff check .

The test suite covers the YAML emitter (including !!js tags and literal blocks), tree parsing, every conversion path, the run/library layout resolver, and the CLI. It needs no DSH installation and no network.

Limitations

  • One-way. Converting back from a preset to a Harness² tree is not implemented, because a preset is a composition, not a file tree.
  • plugin/*.ts has no automatic equivalent; porting is manual.
  • --skill-path-mode absolute binds the bundle to the machine that produced it. The default package mode avoids that, but it needs the bundle installed through the package manager (a plain copy into node_modules by hand will still resolve, as long as the package name is findable).
  • The converter does not run Harness² and does not evaluate whether the harness is any good. It only transports what Harness² produced.

Licence

Apache-2.0. Harness² is Apache-2.0 (Google LLC); a generated bundle contains text derived from it, so keep the attribution when you redistribute a bundle.

This is a community tool. It is not affiliated with Google or DeepSeek.