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.
- Parse — system.usm and service files
- Generate — SKILL.md frontmatter: name usm-workflow, description front-loaded with trigger keywords (feature, bug, spec, .usm) and when to invoke
- Generate — SKILL.md body: pre-flight check (read spec before code), draft-before-build loop, update-status-after-build, feedback protocol pointer
- 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.
- Generate — .opencode/usm-instructions.md — max ~30 lines: the 5 iron rules only
- Read — existing opencode.json (project root or .opencode/) if present
- Merge — instructions array — append .opencode/usm-instructions.md if absent; never remove or reorder existing entries; never touch any other config field
- 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.
- Rerun — usm generate
- Verify — skill + instructions files regenerated with current workflow text
- 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