1
0
Fork 0
forked from dries/ocman

fix(opencode): refresh port discovery during tmux launch #3

Closed
peter wants to merge 0 commits from codex/opencode-fresh-port-discovery into codex/tmux-no-server
Owner

Summary

  • Refresh OpenCode port discovery during tmux launch/wait flows so newly-started opencode --port 0 instances are found promptly.
  • Invalidate stale port cache entries after tmux launch/restart actions.
  • Normalize symlinked directories in port discovery and cover the fresh lookup behavior with focused tests.

Current stack

This branch is rebased on latest upstream main (97fa317). The earlier tmux availability fix has already merged upstream, so this PR's intended review delta is only the OpenCode port-discovery launch fix.

Follow-up stack branch: codex/opencode-port-affinity, which handles duplicate same-directory OpenCode servers by keeping a session's live traffic on one selected port.

Actual issue encountered

After fixing the earlier tmux availability issue, creating a new session could still fail even though ocman successfully started OpenCode in tmux. The UI got past launch/wait and then failed while creating the session:

Starting OpenCode in tmux
Waiting for OpenCode to come up
Creating session
OpenCode did not start in time. Check the tmux window for errors.

This was a real session-start failure observed while using ocman, not a theoretical cleanup. The important clue was that the tmux launch step succeeded, but ocman still could not create a session against the newly-started OpenCode server.

What we observed while debugging

  • The frontend retries session creation while OpenCode is unreachable, then shows OpenCode did not start in time... after the retry window expires.
  • OpenCode is launched with opencode --port 0, so ocman has to discover the assigned HTTP port after launch.
  • OpenCode port discovery uses lsof and keeps a short-lived directory -> port cache for normal dashboard/read paths.
  • A lookup can happen before the newly-launched OpenCode process has bound its random port.
  • Before this patch, that transient "not found yet" result could be cached and reused by the launch/create flow.
  • Symlinked project paths can also prevent matching unless the requested directory and the process cwd are normalized the same way.

What we inferred / assumed

  • We inferred this failure was caused by reusing cached port-discovery state during the launch/wait/create path.
  • We assumed launch and restart actions should invalidate the shared OpenCode port cache because they materially change the process set.
  • We assumed launch/wait/create flows should use fresh discovery that does not cache misses, because a miss immediately after process launch is often transient.
  • We assumed successful fresh hits should refresh the shared cache so subsequent read-heavy callers can reuse the fresh snapshot.
  • We kept the normal cached lookup for dashboard-style callers so regular polling still avoids excessive lsof scans.

Important caveats / implications

  • Fresh discovery can run more lsof scans during launch/wait flows, especially if OpenCode takes several polling attempts to bind. Normal dashboard discovery still uses the cache.
  • Cache invalidation is global rather than per-directory. Launching or restarting OpenCode for one project clears the whole directory -> port snapshot, which may cause one extra fresh scan for another project.
  • This does not fix OpenCode failing to start, exiting immediately, or never binding a port. In those cases the UI should still time out, but for the real startup failure.
  • This does not choose between multiple already-running OpenCode servers for the same directory. A follow-up PR handles project-directory port affinity.
  • Symlink normalization helps when paths resolve cleanly. If EvalSymlinks fails because a path is missing or inaccessible, ocman falls back to the cleaned path, so unusual path cases can still miss.

Validation

Run on codex/opencode-fresh-port-discovery:

  • go test ./internal/platforms/opencode -run 'Test(InvalidateOpenCodePortCache|DiscoverOpenCodePortFresh)'
  • go test ./internal/platforms/opencode
  • go test ./internal/mcp ./internal/server
  • go test ./...
  • make test on the rebased stack tip after the latest upstream rebase.
## Summary - Refresh OpenCode port discovery during tmux launch/wait flows so newly-started `opencode --port 0` instances are found promptly. - Invalidate stale port cache entries after tmux launch/restart actions. - Normalize symlinked directories in port discovery and cover the fresh lookup behavior with focused tests. ## Current stack This branch is rebased on latest upstream `main` (`97fa317`). The earlier tmux availability fix has already merged upstream, so this PR's intended review delta is only the OpenCode port-discovery launch fix. Follow-up stack branch: `codex/opencode-port-affinity`, which handles duplicate same-directory OpenCode servers by keeping a session's live traffic on one selected port. ## Actual issue encountered After fixing the earlier tmux availability issue, creating a new session could still fail even though ocman successfully started OpenCode in tmux. The UI got past launch/wait and then failed while creating the session: ```text Starting OpenCode in tmux Waiting for OpenCode to come up Creating session OpenCode did not start in time. Check the tmux window for errors. ``` This was a real session-start failure observed while using ocman, not a theoretical cleanup. The important clue was that the tmux launch step succeeded, but ocman still could not create a session against the newly-started OpenCode server. ## What we observed while debugging - The frontend retries session creation while OpenCode is unreachable, then shows `OpenCode did not start in time...` after the retry window expires. - OpenCode is launched with `opencode --port 0`, so ocman has to discover the assigned HTTP port after launch. - OpenCode port discovery uses `lsof` and keeps a short-lived directory -> port cache for normal dashboard/read paths. - A lookup can happen before the newly-launched OpenCode process has bound its random port. - Before this patch, that transient "not found yet" result could be cached and reused by the launch/create flow. - Symlinked project paths can also prevent matching unless the requested directory and the process cwd are normalized the same way. ## What we inferred / assumed - We inferred this failure was caused by reusing cached port-discovery state during the launch/wait/create path. - We assumed launch and restart actions should invalidate the shared OpenCode port cache because they materially change the process set. - We assumed launch/wait/create flows should use fresh discovery that does **not** cache misses, because a miss immediately after process launch is often transient. - We assumed successful fresh hits should refresh the shared cache so subsequent read-heavy callers can reuse the fresh snapshot. - We kept the normal cached lookup for dashboard-style callers so regular polling still avoids excessive `lsof` scans. ## Important caveats / implications - Fresh discovery can run more `lsof` scans during launch/wait flows, especially if OpenCode takes several polling attempts to bind. Normal dashboard discovery still uses the cache. - Cache invalidation is global rather than per-directory. Launching or restarting OpenCode for one project clears the whole directory -> port snapshot, which may cause one extra fresh scan for another project. - This does not fix OpenCode failing to start, exiting immediately, or never binding a port. In those cases the UI should still time out, but for the real startup failure. - This does not choose between multiple already-running OpenCode servers for the same directory. A follow-up PR handles project-directory port affinity. - Symlink normalization helps when paths resolve cleanly. If `EvalSymlinks` fails because a path is missing or inaccessible, ocman falls back to the cleaned path, so unusual path cases can still miss. ## Validation Run on `codex/opencode-fresh-port-discovery`: - `go test ./internal/platforms/opencode -run 'Test(InvalidateOpenCodePortCache|DiscoverOpenCodePortFresh)'` - `go test ./internal/platforms/opencode` - `go test ./internal/mcp ./internal/server` - `go test ./...` - `make test` on the rebased stack tip after the latest upstream rebase.
peter force-pushed codex/opencode-fresh-port-discovery from 265b9a9457 to dbd9ae0045 2026-06-23 19:21:01 +02:00 Compare
peter force-pushed codex/opencode-fresh-port-discovery from dbd9ae0045 to bbc152ee10 2026-06-23 19:48:49 +02:00 Compare
peter force-pushed codex/opencode-fresh-port-discovery from bbc152ee10 to efa335f30b 2026-06-24 09:01:37 +02:00 Compare
peter force-pushed codex/opencode-fresh-port-discovery from efa335f30b to 1086db8131 2026-06-24 12:00:55 +02:00 Compare
peter closed this pull request 2026-06-24 12:01:02 +02:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
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
peter/ocman!3
No description provided.