Skip to content

t1k:opencode-harness

FieldValue
Modulet1k-harness
Version1.2.1
Effortlow
Tools—

Keywords: AGENTS.md, convert, cross-harness, harness, migrate, multi-harness, opencode, opencode.json, plugin, portable, zen

/t1k:opencode-harness
[paths | plugins | mapping | drift]

OpenCode Harness — T1K’s Second Target

Section titled “OpenCode Harness — T1K’s Second Target”

T1K is authored for Claude Code and shipped to more than one harness. This skill is the standing reference for the OpenCode target, so nobody re-researches it.

The facts are not in this file. They live in a pinned fixture with a companion check, because a reference doc that nobody re-verifies is a doc that quietly goes wrong:

You needRead
Scan paths, per asset class, per scopedata/opencode-paths.json (the fixture — SSOT)
The same, in prose, with the upstream citationreferences/paths-and-formats.md
Plugin API — events, signatures, what has no Claude equivalentreferences/plugin-api.md
Claude asset → OpenCode asset, field by fieldreferences/claude-mapping.md

Never quote a path from memory or from opencode.ai docs. Quote the fixture.

1. Skills need no conversion. OpenCode scans Claude’s skill directories directly. A T1K skill works in OpenCode unchanged. Converting them is not just wasted work — it creates a second copy that drifts from the first.

2. That native fallback hides broken conversions. Because OpenCode also reads .claude/skills/ and CLAUDE.md, a converter writing skills to the wrong directory still appears to work. The feature is being served by the fallback, not by the code under test. Verify a conversion by asking OpenCode what it loaded, never by checking that a file exists. This happened for real: .opencode/skill shipped singular against a harness that scans skills/, in two separate registries, with no test pinning either form and no CI gate running the suite that would have noticed — invisible precisely because the native fallback served the same content anyway.

3. Frontmatter does not survive a copy. Claude and OpenCode accept different field sets on an agent. A byte-for-byte copy is not a conversion; it is a file the target parses under different rules. See references/claude-mapping.md for the field-by-field table and the fields that have no counterpart.

4. Hooks are the real boundary. Claude’s hooks are subprocesses with a stdin-JSON / exit-code contract. OpenCode’s plugins are in-process JS/TS modules on a different event set. Tool events map cleanly. Prompt-time injection has plausible candidates that nobody has yet run. SubagentStop and the model-router’s PreToolUse: Task interception have no identified counterpart at all. references/plugin-api.md holds the current mapping and names each gap by name — read it before promising any hook-dependent behavior on OpenCode.

  1. Read the fixture for the target paths. Do not hardcode them.
  2. Skip skills — they are already portable. Converting them is a defect.
  3. Convert agent and command frontmatter per references/claude-mapping.md; a straight copy is not acceptable.
  4. Rules and CLAUDE.md merge into AGENTS.md. Content naming Claude-only machinery (hooks, Claude-specific tool names, slash commands) must be rewritten or dropped — not silently carried across.
  5. Verify by discovery, not by presence: launch OpenCode against the output and confirm it lists the asset.

Check in this order: (a) is it in a directory the fixture says is scanned, at the right scope? (b) does the frontmatter validate against OpenCode’s schema, not Claude’s? (c) is a native Claude-path fallback silently serving an older copy of the same asset from .claude/? (d) has upstream moved — run the drift check.

Terminal window
node "$CLAUDE_DIR/scripts/harness-drift-check.cjs" --harness opencode

On failure, per pinned-baseline-test-companion.md, determine the direction before touching the fixture:

  • Upstream moved — read the upstream diff, update the fixture AND every converter that depended on the old value, in the same commit.
  • Our pin was always wrong — this is a defect that has been shipping. Find what it broke, and check whether a native fallback was masking it.
  • Unreachable — a network failure is not a drift. Do not re-pin.

Never set the fixture to whatever the run just reported. That converts a finding into a baseline.

  • The fixture is the single source of truth for paths. A path string anywhere else in T1K is a duplicate to be removed.
  • Every pinned value records the upstream file and commit it came from. A value without a citation is unverified and must be marked as such.
  • Assets that cannot port declare themselves by name in the fixture’s unmappable list. Omitting them reads as full support.
  • wired-not-just-present.md · green-that-proves-nothing.md — why “the file is there” is not evidence.
  • pinned-baseline-test-companion.md — the fixture’s companion obligation.