Skip to content

usm/opencode-integration [built]

First-class opencode support in the rules-files generator — emits a usm-workflow skill (description visible every message) plus an always-on instructions entry (injected into every system prompt), so the spec-first workflow is reinforced per-message instead of being read once at session start and forgotten.

Status: built

Why this exists

Agents drift off the spec-first workflow in long sessions: AGENTS.md is loaded once and diluted by accumulated context, and models stop consulting .usm before code changes. opencode offers two mechanisms that persist on every message — instructions files (injected into every request's system prompt) and skills (whose descriptions are visible every message). The rules generator has no opencode target, so the fastest-growing agent runtime gets no per-message reinforcement.

Design decisions

instructions-plus-skill-not-plugin

Decision: Use opencode instructions array + skill, not a plugin with experimental.chat.system.transform

Rationale: Plugin system-transform hooks are experimental and require TypeScript runtime code in the consumer repo — fragile distribution. instructions injection is stable config, and the skill description is visible every message anyway. Together they cover the every-message requirement without shipping code.

short-dedicated-file-not-agents-md

Decision: Generate a dedicated short instructions file rather than reusing AGENTS.md

Rationale: opencode already injects AGENTS.md every message — the drift problem is dilution by length. A ≤30-line iron-rules file stays salient where the full AGENTS.md does not.

skill-file-usm-owned

Decision: The skill and instructions files are fully USM-owned (wholesale regeneration); opencode.json is merge-touched only in instructions

Rationale: Skills are single-purpose; unlike CLAUDE.md there is no human content to preserve inside them. opencode.json is user-authored config and must be treated as hand-written.

How it works

Generate the usm-workflow skill (generate-opencode-skill)

During usm generate, emit .opencode/skills/usm-workflow/SKILL.md — a skill whose frontmatter description is the enforcement nudge (visible in the system prompt every message) and whose body is the full spec-first checklist.

  1. Parse — system.usm and service files
  2. Generate — SKILL.md frontmatter: name usm-workflow, description front-loaded with trigger keywords (feature, bug, spec, .usm) and when to invoke
  3. Generate — SKILL.md body: pre-flight check (read spec before code), draft-before-build loop, update-status-after-build, feedback protocol pointer
  4. Write — .opencode/skills/usm-workflow/SKILL.md (USM-owned file, fully regenerated)

Wire the iron-rules file into every-message instructions (wire-always-on-instructions)

Generate a short iron-rules markdown file and register it in opencode.json instructions so its content is appended to the system prompt on every request — the mechanism that actually counters mid-session drift.

  1. Generate — .opencode/usm-instructions.md — max ~30 lines: the 5 iron rules only
  2. Read — existing opencode.json (project root or .opencode/) if present
  3. Merge — instructions array — append .opencode/usm-instructions.md if absent; never remove or reorder existing entries; never touch any other config field
  4. Validate — resulting opencode.json parses as JSON with $schema preserved

Regenerate without breaking user config (regenerate-idempotent)

Repeated usm generate runs replace the USM-owned skill and instructions files wholesale but leave all other opencode.json content untouched.

  1. Rerun — usm generate
  2. Verify — skill + instructions files regenerated with current workflow text
  3. Verify — opencode.json: only the instructions entry is owned; user fields (mcp, permission, model, plugin) byte-identical

Guarantees

skill-description-is-the-nudge

The skill's frontmatter description must carry the workflow trigger by itself, since it is the only part visible before invocation

Acceptance criteria:

  • [ ] Description front-loads trigger keywords and filenames (.usm, feature, spec)
  • [ ] Description states when to invoke: before starting feature work, when unsure a change needs a spec, when the session has drifted
  • [ ] Body contains the full checklist including read-before-code, draft-before-build, show-human-review, update-status-after

instructions-injected-every-request

The iron-rules file must be registered in opencode.json instructions so opencode appends it to every system prompt

Acceptance criteria:

  • [ ] opencode.json instructions array contains .opencode/usm-instructions.md
  • [ ] The file is short (≤ ~30 lines) — iron rules only, no system description
  • [ ] Content is the same workflow taught by all other rules files

user-config-preserved

The generator must never damage user-authored opencode config

Acceptance criteria:

  • [ ] Existing instructions entries preserved in order
  • [ ] No other top-level field added, removed, or modified
  • [ ] $schema field preserved; valid JSON output; works when opencode.json does not yet exist (creates minimal file) and when it exists at root or .opencode/

drift-countermeasure

The per-message reinforcement must explicitly address mid-session drift, not just initial onboarding

Acceptance criteria:

  • [ ] Iron rules phrased as per-message self-checks (e.g. 'Before ANY code change: does a .usm spec exist for this?')
  • [ ] Skill description mentions re-anchoring a drifted session

Test specifications

skill-file-generated

Given:

  • system_usm: true
  • run_generate: true

Then:

  • assertion: .opencode/skills/usm-workflow/SKILL.md exists
  • assertion: frontmatter has name and description
  • assertion: body includes the spec-first checklist

instructions-wired

Given:

  • run_generate: true

Then:

  • assertion: .opencode/usm-instructions.md exists and is ≤ 40 lines
  • assertion: opencode.json instructions contains .opencode/usm-instructions.md

existing-config-preserved

Given:

  • opencode_json_with_mcp_and_custom_instructions: true

Then:

  • assertion: custom instructions entries still present, order unchanged
  • assertion: mcp block untouched
  • assertion: output is valid JSON

workflow-consistent

Given:

  • all_rules_outputs_generated: true

Then:

  • assertion: skill body and instructions file mention draft_feature workflow and human review step
  • assertion: feedback routing matches the scope-aware protocol

Implementation

  • Primary: src/generators/rulesFiles.ts
  • Test code: tests/opencodeFeedback.test.ts
  • Test code status: manual

See Also

  • usm/gen-rules-files
  • usm/feedback-upstream-routing