Skip to content

v3.16.0

Choose a tag to compare

@mrbro-bot mrbro-bot released this 05 Sep 08:15
· 203 commits to main since this release
432a014

What's new

Features

Named model profiles with per-harness routing blocks (#903) — User config can now define named profiles, and any config source, including a repository's project config, can select one by name. Agent and category overlays gain optional per-harness blocks — opencode: { model, variant } and pi: { model, thinking } — so a single systematic.jsonc routes correctly on both OpenCode and Pi. One routing resolver feeds the OpenCode config hook, the Pi delegate tool, Pi persona export, and systematic config show.

// ~/.config/opencode/systematic.jsonc
{
  "profile": "personal",
  "profiles": {
    "personal": {
      "categories": {
        "review": {
          "opencode": {"model": "anthropic/claude-opus-4", "variant": "high"},
          "pi": {"model": "github-copilot/claude-opus-4", "thinking": "high"}
        }
      }
    },
    "work": {"categories": {"review": {"model": "openai/gpt-5.2-codex"}}}
  }
}

Resolution is layered and predictable. The profile selector follows source strength (OPENCODE_CONFIG_DIR > project > user) and is tri-state, so null explicitly selects the base config. Overlays merge user base, then the selected profile, then the project's permitted fields, then custom; per target per harness the precedence is agent block, agent flat, category block, category flat. An unknown profile name warns once and falls back to the user's own default rather than base — a repository can never push a contributor off the routing they chose.

The trust boundary holds: project config may select a profile but never define one. profiles coming from project config is stripped with a warning, and model, variant, and thinking inside harness blocks are rejected from project config exactly like the flat fields already were.

One behaviour change is worth noting. A flat agents.<name>.model now routes Pi delegates too; previously it was OpenCode-only and Pi always inherited the parent model. The first dispatch of a config-routed agent says so in the tool result, naming the model, its source, and the active profile. pi_subagents.<name>.thinking is superseded by agents.<name>.pi.thinking, with the old location still working when the new one is absent and emitting one deprecation warning per written field. Every pre-existing configuration produces byte-identical OpenCode agent config, asserted byte-for-byte against a fourteen-configuration corpus.

Alongside the resolver, systematic config show now reports the active profile, its source, any fallback, and per-agent routing per harness, with --json emitting the same information as a single object. Claude Code routing, fallback_models chains, and shipping the OMO registry routing as a named profile remain out of scope.

Documentation

The OpenCode host-contract suite now has a plan for becoming CI-owned (#898). Today the integration tests that spawn OpenCode assert real host mechanics — plugin loading, config-hook merge, tool registration, system-prompt transforms, question API shapes, receipt persistence across restart — but none of it runs in CI, and the only automated effect of the version pin is a unit test that goes red on every Renovate OpenCode bump until someone re-runs a 15–17 minute suite locally. The plan makes package.json's @opencode-ai/sdk the single pin read at runtime, launches every host through bunx opencode-ai@<pin>, and adds a host-contract CI job that is a required predecessor of release with a post-run guard so a green job cannot simply be a run of skipped suites.

ARCHITECTURE.md and STRUCTURE.md were also synced to the bundled validator work (#894): the Claude Code plugin build section now documents that bin/ entries are written with executable mode and that the CI pre-publish guard asserts the bundled validator is executable rather than merely present, and the brittle unit-test file counts were refreshed.

Tests

The Claude Code integration suite now builds the real validator bundle (#895). That suite had been failing on main since the bundled validator landed: generatePluginFiles gained a required validator Buffer argument and the integration suite still called it with one argument, throwing at module load before any test ran. buildValidatorBundle is now exported and built once per module load, both call sites pass it, and a new assertion checks that the generated bin/systematic-validate-review-artifact is exactly one shebang line followed by the bundle bytes. A second latent failure — agent stem discovery picking up agents/review/README.md, which the build already excludes — was hidden behind the original error and is fixed too.

Integration runs also stop leaking OpenCode servers (#896). Both host spawn sites launched through a launcher chain and stopped only the direct child, so the server outlived every failed, timed-out, or aborted run until the machine had to be rebooted. Hosts now spawn detached in their own process group and are reaped with SIGTERM, a five-second wait, then SIGKILL of the group; startup-timeout cleanup is awaited before the failure surfaces; and a suite-level registry of live hosts is drained in afterAll so a test that aborts before its own finally still leaves nothing behind. Availability probing switched from which to running opencode --version under a throwaway HOME/XDG root, so a shim that only resolves through the redirected HOME makes the suite skip instead of spawning servers that cannot start.

Also in this release

  • ci: bfra-me/.github bumped to v4.25.1 (#899), which takes bfra-me/renovate-action 10.34.1 and restores tar in the Renovate runtime. Renovate on 10.34.0 exits before servicing any dependencies, so this pin could not self-update.
  • deps: fro-bot/agent updated to v0.107.2, v0.107.3, and v0.108.1 (#901, #902, #904).
  • dev: all non-major dependencies updated (#900).

Compare

v3.15.1...v3.16.0