You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
opencode2 service status prints stopped when the registration points to a healthy managed daemon running an older version than the installed CLI. The daemon is not stopped: opencode2 api get /api/health can connect to it because the API command intentionally ignores managed-service version mismatches.
This makes a normal deferred-update state look like a missing or corrupt service and obscures which process clients are actually using.
Environment
opencode version: reproduced across 0.0.0-next-15258 → next-15277 and next-15293 → next-15295
The status command applies the current CLI's exact version gate. A healthy older service therefore becomes indistinguishable from no service:
$ opencode2 --version
opencode2 v0.0.0-next-15295
$ opencode2 service status
stopped
$ opencode2 api get /api/health
{"healthy":true,"version":"0.0.0-next-15293","pid":81804}
Additional Context
Lifecycle
sequenceDiagram
participant Old as Running daemon A
participant Disk as Global installation
participant Status as service status
participant API as api get
participant Client as Normal client B
Old->>Disk: Install version B
Note over Old: Process image remains version A
Status->>Old: Probe with exact version B
Old-->>Status: Healthy version A
Status-->>Status: Print stopped
API->>Old: Probe without version gate
Old-->>API: Healthy version A
Client->>Old: Replace mismatched daemon
Client->>Disk: Start daemon B
Loading
Installing an update does not replace an already-running process image. Existing clients may continue using daemon A. A later normal client uses the default mismatch: "replace" policy, terminates daemon A, and starts daemon B. That deferred replacement is working; the incorrect part is the status projection during the mismatch window.
Timestamped log
The original incident:
2026-07-10T02:36:23Z managed daemon starts on next-15258 (run=34241966)
2026-07-10T13:32:36Z daemon installs next-15277; running process remains next-15258
2026-07-10T13:42:37Z old daemon installs next-15277 again
2026-07-10T13:47:41Z installed CLI reports next-15277
2026-07-10T13:47:41Z service status runs and prints stopped
2026-07-10T13:47:42Z api health reaches the healthy next-15258 daemon
2026-07-10T13:49:02Z normal next-15277 TUI starts
2026-07-10T13:49:03Z next-15258 daemon begins shutting down its location graphs
2026-07-10T13:49:04Z next-15277 service contender starts
Independent reproduction:
2026-07-10T15:43:31Z managed daemon next-15293 starts (run=8d63fdf7, PID 81804)
2026-07-10T15:46:27Z updater sees next-15295
2026-07-10T15:46:30Z updater installs next-15295
2026-07-10T15:46:38Z installed CLI reports next-15295; service status prints stopped
2026-07-10T15:46:39Z api health reports next-15293, PID 81804
2026-07-10T15:46:42Z normal next-15295 client starts
2026-07-10T15:46:43Z next-15293 daemon shuts down
2026-07-10T15:46:44Z next-15295 daemon starts (run=b72556c5, PID 86661)
2026-07-10T15:47:36Z service status reports the URL; api health reports next-15295
Source path
ServiceConfig.options() supplies the current InstallationVersion to discovery.
Service.discover(options) rejects registrations and health responses with a different version.
The status handler maps an unsuccessful exact-version discovery directly to stopped.
The API handler deliberately resolves with mismatch: "ignore", which removes the version gate and reuses the healthy old daemon.
Normal clients default to mismatch: "replace", which stops the old daemon and starts the installed version.
Registration remains coherent throughout; this is not service-election corruption.
Proposed fix
Add a read-only service status result that probes a healthy registration without applying the compatibility gate, then emit an explicit JSON status with server identity and compatibility. Keep exact-version discovery and replacement behavior unchanged.
Summary
opencode2 service statusprintsstoppedwhen the registration points to a healthy managed daemon running an older version than the installed CLI. The daemon is not stopped:opencode2 api get /api/healthcan connect to it because the API command intentionally ignores managed-service version mismatches.This makes a normal deferred-update state look like a missing or corrupt service and obscures which process clients are actually using.
Environment
0.0.0-next-15258 → next-15277andnext-15293 → next-15295TERM=xterm-256color,COLORTERM=truecolor/bin/zshnextshare-with-team.ts,debug-workspace-plugin.ts,@warp-dot-dev/opencode-warp(not involved in service discovery)Reproduction
opencode2 --versionreports version B.opencode2 service status.opencode2 api get /api/health.The deterministic regression test uses a real local HTTP health endpoint and a service registration whose healthy server version differs from the CLI:
Expected Behavior
service statusshould distinguish these states:{ "status": "running", "url": "http://127.0.0.1:4096", "pid": 81804, "version": "0.0.0-next-15293", "compatible": false, "clientVersion": "0.0.0-next-15295" }Actual Behavior
The status command applies the current CLI's exact version gate. A healthy older service therefore becomes indistinguishable from no service:
Additional Context
Lifecycle
Installing an update does not replace an already-running process image. Existing clients may continue using daemon A. A later normal client uses the default
mismatch: "replace"policy, terminates daemon A, and starts daemon B. That deferred replacement is working; the incorrect part is the status projection during the mismatch window.Timestamped log
The original incident:
Independent reproduction:
Source path
ServiceConfig.options()supplies the currentInstallationVersionto discovery.Service.discover(options)rejects registrations and health responses with a different version.stopped.mismatch: "ignore", which removes the version gate and reuses the healthy old daemon.mismatch: "replace", which stops the old daemon and starts the installed version.Proposed fix
Add a read-only service status result that probes a healthy registration without applying the compatibility gate, then emit an explicit JSON status with server identity and compatibility. Keep exact-version discovery and replacement behavior unchanged.