fix(cli): harden managed service election#37645
Merged
Merged
Conversation
github-actions Bot
pushed a commit
to ReStranger/opencode
that referenced
this pull request
Jul 19, 2026
* upstream/v2: mini: fix shell tool output display (anomalyco#37711) fix(core): detach disposed MCP registrations from root scope (anomalyco#37660) fix(core): preserve the first terminal failure (anomalyco#37705) fix(core): continue after malformed tool input (anomalyco#37701) fix(core): safely recover malformed tool input (anomalyco#37698) fix(simulation): render screenshot symbol glyphs (anomalyco#37691) fix(core): authorize relative external paths (anomalyco#37689) fix(tui): auto-approve permissions in auto mode feat(core): allow MCP Code Mode opt-out (anomalyco#37681) fix(codemode): stop leaking undefined into tool arguments (anomalyco#37652) fix(tui): style interrupted compaction neutrally (anomalyco#37655) fix(cli): harden managed service election (anomalyco#37645)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Prevent stale or unresponsive managed-port owners from trapping service contenders, while preserving correct ownership during slow and highly concurrent startup. Incumbent recognition now has a true 15-second wall-clock bound, and only the process that wins the port bind persists the generated service password.
Before / After
Before: incumbent recognition allowed 60 retries, but each authenticated health probe could take two seconds, stretching an intended short retry window to roughly two minutes. A strict six-second replacement proved too short under Windows CI and misclassified a legitimate owner that published registration after seven seconds. Concurrent first-start contenders also generated and wrote passwords before election; they raced on the shared config temp file, causing
ENOENT, and could persist a loser password that did not authenticate the elected service.After: recognition is interrupted after 15 seconds regardless of probe latency. A legitimate owner may publish registration eight seconds after the bind race begins and is still recognized; a stale registration plus a listener that never answers health fails with an actionable conflict in bounded time. Passwords are generated in contender memory but persisted only in the elected owner’s
onListenpath, before registration, so config and registration always carry the same credential.How
packages/cli/src/server-process.ts: bound the complete incumbent-recognition retry operation withEffect.timeoutOption("15 seconds").packages/cli/src/server-process.ts: defer first-time password persistence until after successful port binding and before publishing registration.packages/cli/test/service.test.ts: model a stale registration whose exact-port listener accepts health requests but never responds.packages/cli/test/service.test.ts: deterministically wait until post-bind incumbent polling begins, delay registration for eight seconds, and verify successful adoption.packages/cli/test/service.test.ts: require all election losers to exit successfully, include their output in failures, verify persisted and registered passwords match, preserve empty-password generation behavior, and prevent cleanup assertions from masking the primary failure.packages/cli/test/service.test.ts: bind foreign listeners explicitly to127.0.0.1so the reproducer is deterministic on macOS.Scope
This builds on the port-bind election and registration lease introduced by #37572 and #37576. It deliberately does not kill an unknown port owner: without a matching authenticated registration, signaling that PID could terminate an unrelated or PID-reused process. The safe outcome is a bounded, actionable conflict.
Testing
bun run test test/service.test.tsinpackages/cli(17 passed)bun run test test/service.test.ts --test-name-pattern "concurrent service processes elect one server" --rerun-each 20inpackages/cli(20 passed)bun run test test/service.test.ts test/promise-service.test.tsinpackages/client(15 passed)bun typecheckinpackages/cliThe prior Windows run also hit an unrelated
packages/core/test/vcs.test.tstimeout at 5006.92 ms against a 5000 ms default; no files in that package are changed here.