Skip to content

fix(schema): publish config schema that accepts V1 and V2 shapes - #55524

Merged
rekram1-node merged 7 commits into
devfrom
unified-config-schema
Oct 14, 2026
Merged

rekram1-node merged 7 commits into
devfrom
unified-config-schema

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

https://opencode.ai/config.json is the $schema used by every opencode.json(c), and both V1 and V2 read that file. Today it's a static V1 file that was frozen into packages/console/app/public when packages/opencode was removed from dev. As a result, editors flag valid V2 configs as errors: permissions, agents, providers, mcp.servers, skills: [], update, warming, and others.

This PR builds the file during the deploy, so it now matches how V2 parses config:

  • V2 shape: generated from Config.Info in @opencode/schema/config.
  • V1 shape: taken from a frozen snapshot of the last V1 schema (packages/schema/script/config-v1.json, moved here and refreshed from today's v1 branch; only the autoupdate description changed). Fields only V1 understands are marked deprecated. This matches ConfigNormalize, which accepts V1 and V2 fields mixed in one file.
  • Keys both versions share: they keep the V2 shape. Shared objects also accept their V1 properties (mcp legacy server entries, compaction.reserved, experimental.mcp_timeout, ...). skills changed type, so it accepts either shape.
  • Schema structure: the root is a flat object schema rather than a root $ref. allowComments and allowTrailingCommas stay.

Deployment

  • Console app: its build runs the generator after vite build and writes .output/public/config.json. This is the console's build script before 5caf1f9f6f, now pointed at the new generator. The dev and production deploys serve it at opencode.ai/config.json. The v1 branch only deploys app.opencode.ai, so it never touched this file.
  • Docs site: the packages/web build hook (docs.opencode.ai/config.json) now runs the generator instead of copying the static file.
  • Staying current: every deploy regenerates the file from Config.Info, so future V2 config changes need no manual step.

Other changes

  • V2 descriptions: top-level V2 descriptions are copied from the type-side document, because fields declared as optional lose their annotations on the encoded side.
  • Skill doc: the OpenCode skill no longer says the schema "may describe V1".

Known gaps (follow-ups)

  • Nested descriptions: nested V2 fields still have no descriptions; most of these schemas are annotated after optional. Where V1 had a matching field, its description is used instead.
  • Legacy MCP names: a legacy MCP server literally named servers or timeout validates against the V2 shape.

Validation

Done locally; no test is added to the PR. The deploy builds fail loudly if the generator throws.

  • Real builds: bun run build in packages/console/app and packages/web both emit config.json, byte-identical to the generator output.
  • No regressions against the live schema: all 2,143 opencode.json examples in the V1 (all locales) and V2 docs, plus 680 random V1-valid configs from a fuzzer. Nothing the live schema accepts is rejected.
  • V2 coverage: 400 random values generated from Config.Info, encoded, all accepted. The samples cover every top-level key.
  • Agreement with the V2 runtime: about 50 hand-written V1/V2/mixed edge cases checked against ConfigNormalize. The only disagreements are the known gaps above, plus unknown keys, which the schema rejects and the runtime ignores.
  • Future changes:
    • a new V2 field (top-level or nested) appears automatically;
    • a V2 key that reuses a V1 name with a new type becomes anyOf;
    • a V1/V2 definition-name clash fails the build;
    • converting Config.Info from a Class to a Struct still generates identical output.
  • bun run check passes.

Issues

Fixes #53398
Fixes #50724
Fixes #48812
Fixes #43748
Fixes #41014
Fixes #46248

Related: #53744, #52177

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks sound. I checked it with ajv against the base's static config.json and against the schema generated from this branch. The base rejects V2 configs, including the reproductions from the linked reports (permissions, skills: [], update, mcp.servers, agents, providers). With this PR they validate, V1 and mixed configs still validate, and unknown keys are still rejected. bun test and typecheck in packages/schema pass.

Public API: only the published config schema at opencode.ai/config.json and docs.opencode.ai/config.json changes.

  • Added: the V2 top-level keys (permissions, policies, agents, commands, plugins, providers, update, snapshots, warming, worktree, ...), and V2 sub-shapes such as mcp.servers and mcp.timeout.
  • Modified: V1-only keys (permission, agent, provider, autoupdate, ...) are still accepted but marked deprecated. skills accepts either the array or the V1 { paths, urls } object. The root is now a flat object schema instead of a top-level $ref.
  • Removed: nothing.

This is backwards compatible: existing V1 configs keep validating.

No blocking concerns.

Fixes #53398
Fixes #50724
Fixes #48812
Fixes #43748
Fixes #41014
Fixes #46248
Related: #53744, #52177

@ssc-esiemiat

Copy link
Copy Markdown

When will this take effect?

@rekram1-node

Copy link
Copy Markdown
Contributor Author

it is deployed rn

@ssc-esiemiat

Copy link
Copy Markdown

Nice! I checked this morning and it wasn't working. Looks good now though. Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment