Skip to content

[FEATURE]:Allow V1 and V2 to use fully separate configuration roots (and enable strict validation for the V2 root) #46757

Open

Description

@ratermir

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Context

Design feedback based on the published V2 documentation and on existing issues,
not a bug report. I am currently on V1 (1.18.26) and have not run V2 — the
concern below is what is keeping me from trying it. Please treat the
observations as questions about intended design.

Problem

V1 and V2 are installed side by side and run as opencode and opencode2, but
they discover configuration from the same locations. The compatibility is
asymmetric: V2 reads the V1 format, V1 cannot read the native V2 format.

The consequence is that a shared configuration is pinned to the V1 format for as
long as V1 is used anywhere. That includes a project's checked-in .opencode/,
where part of a team may still be on V1. Side-by-side installation exists to
enable gradual migration, but the shared configuration root prevents exactly
that.

The docs already acknowledge the collision, advising users not to point V1 at
configuration converted to the native V2 shape. That is guidance where a
mechanism would do.

What already exists, and why it is not enough

OPENCODE_CONFIG_DIR exists, but its semantics are inconsistent and contested:
per #32825, the old app loader treats it as an additive config directory while
the v2/core loader resolves it as a replacement for the XDG global directory.
PR #32824, which would have made it additive, is closed.

Two problems for this use case:

  1. The semantics are unsettled, so the isolation it provides cannot be relied on.
  2. It only addresses the global layer. Project configuration is discovered by
    walking up from the working directory to the project root, so an absolute
    path in an environment variable cannot express it.

This is a tree, not a file

Renaming the JSON file would only address a fraction of it. The configuration
root also contains agents/, commands/, skills/, plugins/, and
AGENTS.md, both globally and under a project .opencode/. Agent frontmatter
has V1 and V2 semantics (prompt/system, permission/permissions), and
plugins are a declared breaking change — V1 plugins do not work in V2 — yet both
runtimes still read the same plugins/ directory.

Proposal

A profile selector rather than a file rename or a bare path — proposed as a new
variable OPENCODE_PROFILE_NAME, not as another meaning for
OPENCODE_CONFIG_DIR. One value resolving to both layers:

  • global: ~/.config/<profile>/
  • project: <project>/.<profile>/

Default opencode, so existing behavior is unchanged and this is not a breaking
change. A beta user sets OPENCODE_PROFILE_NAME=opencode2 (or any name) and
gets a fully isolated tree. V1 does not recognize the variable and stays on its
default paths, so isolation works during side-by-side operation without any V1
change.

Advantages over the alternatives:

  • Covers the whole tree, not just the JSON file.
  • Expresses the project layer, which an absolute path cannot.
  • A separate variable avoids adding a fourth interpretation to
    OPENCODE_CONFIG_DIR, whose semantics are already inconsistent between
    loaders ([BUG]: OPENCODE_CONFIG_DIR replaces global config in v2 services #32825) and unresolved (fix(config): support additive config directories #32824 closed).
  • _NAME rather than _DIR signals that the value is an identifier, not a
    path.
  • Version-neutral: the default name stays opencode and needs no rename once V1
    is retired, and a future change to the V2 directory layout does not require
    renaming anything.

The main benefit: strict validation becomes possible

This is the part I care about most, and it is not achievable without separation.

As long as one tree must be readable by both runtimes, V2 cannot validate it
strictly. It has to accept V1 keys, normalize them in memory, and stay silent —
so unknown or misplaced keys cannot be rejected. The result today is that a key
can fall into one of three states, and only two of them are visible to the user:

  1. Recognized and applied — silent, as expected.
  2. Recognized but unsupported — ignored with a warning
    (compaction.tail_turns, compaction.prune, logLevel). Honest.
  3. Recognized, preserved, but not applied by the runner — silent. The Agents
    doc documents this for per-agent request overlays: the schema accepts them,
    the session runner preserves them on the agent definition, but does not apply
    them to model requests.

With an isolated V2 root, the V2 loader owes nothing to V1 compatibility and can
reject unknown keys outright instead of quietly normalizing or ignoring them.
Strict validation of valid and effective keys becomes a design option rather
than something structurally excluded. Category 3 could then be a load-time error
or an explicit "recognized, not currently applied" notice instead of silence.

Note on the desktop client

Since V2 is a service plus clients, and cli.json is owned by the terminal
client and not loaded by the background service, the configuration root has to
resolve in the service. A CLI flag would not propagate on its own. The cleanest
shape is probably a service startup option, with clients selecting which
instance to connect to — which also gives the desktop app something it can
express as an instance switcher rather than a command-line flag.

Related: #32825, #32824, #39431
Prior discussion, closed without a mechanism: #41081, #12607

Activity

  1. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed design write-up!

    To restate it: you're asking for a way to give v2 a fully separate configuration tree from v1, at both the global and the project level. You suggest a profile name such as OPENCODE_PROFILE_NAME=opencode2, which would map to ~/.config/<profile>/ and <project>/.<profile>/ and be resolved by the background service. With a separate tree, v2 could validate its own config strictly and reject unknown keys, or flag keys it accepts but doesn't apply, instead of quietly normalizing v1 keys. Maintainers will consider it.

    This can't be done as a plugin, because config locations are resolved before plugins load.

    Related issues that may interest you:

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions