[GH-ISSUE #3318] [Bug] OpenCode V2 registration stays disabled after setup refresh #1189

Closed
opened 2026-10-15 18:31:52 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3318

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Setup, packaging, or upgrade

Observed behavior

Approved setup refreshes the selected OpenCode V2 registration but leaves it disabled when an earlier user configuration supplies disabled: true. The first run reports configured_opencode while its final registration check remains stale. Repeating the recommended setup command plans no changes and cannot repair the registration. Both calls exit 1 with status: "needs_human".

The lower-priority file keeps its disabled flag, and the new higher-priority entry does not override it. The final doctor result recommends refreshing the registration and restarting the client, but another refresh makes no change. Both files are valid JSON, and setup creates an exact backup of the upper file before changing it.

Changing only the lower fixture's disabled value to false in a fresh disposable home made the first run return ready, exit 0, and report the registration as aligned. Its second run also returned ready with no planned actions. This control used the same CLI and environment. No OpenCode process was started; the stale merged-registration result was directly observed through REA's own setup and doctor projection.

Reproduction

  1. Prepare a clean source checkout and build its runtime. The dependency and build commands are prerequisites for a clean checkout; the observed CLI calls used an already installed and built runtime.
git clone https://github.com/morluto/rea.git rea-opencode-repro
cd rea-opencode-repro
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. From the repository root, run the following command. It creates every input file inline in a disposable home and excludes ambient client-location overrides. It does not change the real user configuration.
python3 - <<'PYCODE'
import json
import os
from pathlib import Path
import subprocess
import tempfile

with tempfile.TemporaryDirectory() as directory:
    home = Path(directory) / "home"
    folder = home / ".config" / "opencode"
    folder.mkdir(parents=True)
    lower = folder / "config.json"
    upper = folder / "opencode.json"
    lower.write_text('{"mcp":{"servers":{"rea":{"type":"local","command":["rea","mcp"],"disabled":true}}}}\n')
    upper.write_text('{"mcp":{"servers":{}}}\n')
    environment = {
        "PATH": os.environ["PATH"],
        "HOME": str(home),
        "HOPPER_LAUNCHER_PATH": str(home / "absent-hopper"),
    }
    command = ["node", "scripts/rea.mjs", "setup", "--client", "opencode", "--skill=false", "--yes", "--json"]
    for attempt in (1, 2):
        process = subprocess.run(command, env=environment, capture_output=True, text=True)
        report = json.loads(process.stdout)
        registration = next(row for row in report["doctor"]["identity"]["registrations"] if row["client"] == "opencode")
        print(json.dumps({"attempt": attempt, "exit": process.returncode, "status": report["status"], "appliedActions": report["appliedActions"], "plannedActionCount": len(report["plannedActions"]), "registrationState": registration["state"]}))
    print("lower disabled:", json.loads(lower.read_text())["mcp"]["servers"]["rea"]["disabled"])
    print("upper defines disabled:", "disabled" in json.loads(upper.read_text())["mcp"]["servers"]["rea"])
PYCODE
  1. Observe the two failed setup results and retained lower-layer flag shown under Relevant output or evidence. For the discriminating control, repeat step 2 in a fresh disposable home after changing only "disabled":true to "disabled":false in the lower.write_text input. That control returned ready, exit 0 and an aligned registration on both calls, with no planned actions on the second call.

Expected behavior

Explicitly selecting OpenCode for approved setup should produce usable MCP registration settings for that client. docs/installation.md:39-40 promises MCP access for selected clients. docs/client-configuration.md:365-373 describes native OpenCode V2 registration and merged user-registration checks. With a native mcp.servers table, setup should register REA there and verify the effective merged user registration. A lower-priority disabled flag should not leave an explicitly refreshed REA registration disabled. Unrelated servers and settings should remain intact.

Impact and scope

Impact is P2, supported. Users refreshing a disabled native OpenCode V2 registration in layered user settings cannot complete setup through the suggested retry. The defect is bounded to the inherited disabled setting, and the observations show no data loss.

A workaround is available. Changing the lower fixture to disabled: false restored aligned, idempotent setup in the executed control. A user can back up and change that one REA activation setting without removing unrelated registrations.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js 26.10.0 on macOS 27.0.1, using the built source-checkout CLI. Architecture was not recorded. No analysis engine or running OpenCode client is required for these observations.

Target and artifact context

Synthetic layered OpenCode V2 user configuration in .config/opencode/config.json and .config/opencode/opencode.json under a disposable home. The lower file contains the native REA registration with disabled: true; the upper file initially contains an empty native server table. No binary target or analysis artifact is required.

Interface

CLI

Earliest failure stage

Setup or provider admission

Relevant output or evidence

The two executed CLI setup calls and stored-file inspection printed:

{"attempt": 1, "exit": 1, "status": "needs_human", "appliedActions": ["configured_opencode"], "plannedActionCount": 1, "registrationState": "stale"}
{"attempt": 2, "exit": 1, "status": "needs_human", "appliedActions": [], "plannedActionCount": 0, "registrationState": "stale"}
lower disabled: True
upper defines disabled: False

The public setup result, stored client files, and repeated-call behavior provide the primary evidence. The reproduction is complete and was locally reproduced.

Typed outcome or recovery expected

Run the two-call reproduction with the lower disabled: true setting. The first approved call must produce an enabled, aligned effective registration and pass its selected-client readiness check. The second call must remain aligned and idempotent rather than reporting stale with an empty repair plan. Preserve the lower layer's unrelated settings and servers, verify the backup, and keep the disabled: false control passing. A successful write alone does not restore this contract.

Analysis or suspected cause

Source-derived reasoning: src/application/ClientConfigurationDocument.ts:1460-1468 produces the native V2 entry without an activation override. src/application/ClientConfigurationFiles.ts:119-133 and src/application/ClientConfigurationFiles.ts:161-173 retain scalar settings omitted by later layers. src/application/SetupClientConfiguration.ts:639-648 checks the written layer rather than effective activation when deciding that a subsequent refresh is current. This explains how the lower disabled flag can survive a refreshed upper registration and why the retry has no repair action.

Additional context

  • #816 covers V1 versus V2 format compatibility; this reproduction already uses the native V2 server table and fails on inherited activation after refresh.
  • #1606 covers preservation of valid environment overrides; this report concerns overriding an inherited disabled flag for the explicitly selected REA registration.
  • #2665 covers runtime-invalid registration overrides incorrectly reported aligned; this report uses valid JSON and an activation setting that remains stale after approved setup.
  • #3009 covers version drift and doctor diagnostics; this report concerns a refresh that writes the registration while leaving it disabled, followed by an empty repair plan.
  • #3205 covers accepting an invalid output-schema profile; this report requires restoring effective activation for a valid inherited setting and preserving setup idempotence.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3318 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Setup, packaging, or upgrade ### Observed behavior Approved setup refreshes the selected OpenCode V2 registration but leaves it disabled when an earlier user configuration supplies `disabled: true`. The first run reports `configured_opencode` while its final registration check remains `stale`. Repeating the recommended setup command plans no changes and cannot repair the registration. Both calls exit 1 with `status: "needs_human"`. The lower-priority file keeps its disabled flag, and the new higher-priority entry does not override it. The final doctor result recommends refreshing the registration and restarting the client, but another refresh makes no change. Both files are valid JSON, and setup creates an exact backup of the upper file before changing it. Changing only the lower fixture's `disabled` value to `false` in a fresh disposable home made the first run return `ready`, exit 0, and report the registration as aligned. Its second run also returned `ready` with no planned actions. This control used the same CLI and environment. No OpenCode process was started; the stale merged-registration result was directly observed through REA's own setup and doctor projection. ### Reproduction 1. Prepare a clean source checkout and build its runtime. The dependency and build commands are prerequisites for a clean checkout; the observed CLI calls used an already installed and built runtime. ```sh git clone https://github.com/morluto/rea.git rea-opencode-repro cd rea-opencode-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. From the repository root, run the following command. It creates every input file inline in a disposable home and excludes ambient client-location overrides. It does not change the real user configuration. ```bash python3 - <<'PYCODE' import json import os from pathlib import Path import subprocess import tempfile with tempfile.TemporaryDirectory() as directory: home = Path(directory) / "home" folder = home / ".config" / "opencode" folder.mkdir(parents=True) lower = folder / "config.json" upper = folder / "opencode.json" lower.write_text('{"mcp":{"servers":{"rea":{"type":"local","command":["rea","mcp"],"disabled":true}}}}\n') upper.write_text('{"mcp":{"servers":{}}}\n') environment = { "PATH": os.environ["PATH"], "HOME": str(home), "HOPPER_LAUNCHER_PATH": str(home / "absent-hopper"), } command = ["node", "scripts/rea.mjs", "setup", "--client", "opencode", "--skill=false", "--yes", "--json"] for attempt in (1, 2): process = subprocess.run(command, env=environment, capture_output=True, text=True) report = json.loads(process.stdout) registration = next(row for row in report["doctor"]["identity"]["registrations"] if row["client"] == "opencode") print(json.dumps({"attempt": attempt, "exit": process.returncode, "status": report["status"], "appliedActions": report["appliedActions"], "plannedActionCount": len(report["plannedActions"]), "registrationState": registration["state"]})) print("lower disabled:", json.loads(lower.read_text())["mcp"]["servers"]["rea"]["disabled"]) print("upper defines disabled:", "disabled" in json.loads(upper.read_text())["mcp"]["servers"]["rea"]) PYCODE ``` 3. Observe the two failed setup results and retained lower-layer flag shown under Relevant output or evidence. For the discriminating control, repeat step 2 in a fresh disposable home after changing only `"disabled":true` to `"disabled":false` in the `lower.write_text` input. That control returned `ready`, exit 0 and an aligned registration on both calls, with no planned actions on the second call. ### Expected behavior Explicitly selecting OpenCode for approved setup should produce usable MCP registration settings for that client. `docs/installation.md:39-40` promises MCP access for selected clients. `docs/client-configuration.md:365-373` describes native OpenCode V2 registration and merged user-registration checks. With a native `mcp.servers` table, setup should register REA there and verify the effective merged user registration. A lower-priority disabled flag should not leave an explicitly refreshed REA registration disabled. Unrelated servers and settings should remain intact. ### Impact and scope Impact is P2, supported. Users refreshing a disabled native OpenCode V2 registration in layered user settings cannot complete setup through the suggested retry. The defect is bounded to the inherited disabled setting, and the observations show no data loss. A workaround is available. Changing the lower fixture to `disabled: false` restored aligned, idempotent setup in the executed control. A user can back up and change that one REA activation setting without removing unrelated registrations. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js 26.10.0 on macOS 27.0.1, using the built source-checkout CLI. Architecture was not recorded. No analysis engine or running OpenCode client is required for these observations. ### Target and artifact context Synthetic layered OpenCode V2 user configuration in `.config/opencode/config.json` and `.config/opencode/opencode.json` under a disposable home. The lower file contains the native REA registration with `disabled: true`; the upper file initially contains an empty native server table. No binary target or analysis artifact is required. ### Interface CLI ### Earliest failure stage Setup or provider admission ### Relevant output or evidence The two executed CLI setup calls and stored-file inspection printed: ```text {"attempt": 1, "exit": 1, "status": "needs_human", "appliedActions": ["configured_opencode"], "plannedActionCount": 1, "registrationState": "stale"} {"attempt": 2, "exit": 1, "status": "needs_human", "appliedActions": [], "plannedActionCount": 0, "registrationState": "stale"} lower disabled: True upper defines disabled: False ``` The public setup result, stored client files, and repeated-call behavior provide the primary evidence. The reproduction is complete and was locally reproduced. ### Typed outcome or recovery expected Run the two-call reproduction with the lower `disabled: true` setting. The first approved call must produce an enabled, aligned effective registration and pass its selected-client readiness check. The second call must remain aligned and idempotent rather than reporting stale with an empty repair plan. Preserve the lower layer's unrelated settings and servers, verify the backup, and keep the `disabled: false` control passing. A successful write alone does not restore this contract. ### Analysis or suspected cause Source-derived reasoning: `src/application/ClientConfigurationDocument.ts:1460-1468` produces the native V2 entry without an activation override. `src/application/ClientConfigurationFiles.ts:119-133` and `src/application/ClientConfigurationFiles.ts:161-173` retain scalar settings omitted by later layers. `src/application/SetupClientConfiguration.ts:639-648` checks the written layer rather than effective activation when deciding that a subsequent refresh is current. This explains how the lower disabled flag can survive a refreshed upper registration and why the retry has no repair action. ### Additional context - [#816](https://github.com/morluto/rea/issues/816) covers V1 versus V2 format compatibility; this reproduction already uses the native V2 server table and fails on inherited activation after refresh. - [#1606](https://github.com/morluto/rea/issues/1606) covers preservation of valid environment overrides; this report concerns overriding an inherited disabled flag for the explicitly selected REA registration. - [#2665](https://github.com/morluto/rea/issues/2665) covers runtime-invalid registration overrides incorrectly reported aligned; this report uses valid JSON and an activation setting that remains stale after approved setup. - [#3009](https://github.com/morluto/rea/issues/3009) covers version drift and doctor diagnostics; this report concerns a refresh that writes the registration while leaving it disabled, followed by an empty repair plan. - [#3205](https://github.com/morluto/rea/issues/3205) covers accepting an invalid output-schema profile; this report requires restoring effective activation for a valid inherited setting and preserving setup idempotence.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1189
No description provided.