Skip to content

Bind requests to the invoking workspace; persist server ownership (#29)#37

Merged
TheRealDinghyDog merged 1 commit into
opencode-conversionfrom
fix/issue-29-directory-ownership
Jul 10, 2026
Merged

Bind requests to the invoking workspace; persist server ownership (#29)#37
TheRealDinghyDog merged 1 commit into
opencode-conversionfrom
fix/issue-29-directory-ownership

Conversation

@TheRealDinghyDog

Copy link
Copy Markdown
Owner

Fixes #29 (review finding H-04).

Problem

  • The client never sent the directory query parameter, so against an external server (OPENCODE_COMPANION_SERVER_URL) every project-scoped request — session create/list/resume and the /event subscription — resolved against the server process's launch directory, not the invoking repo. A review could inspect, and a write task could edit, the wrong repository. (Verified live: a session created without the query inherits the server's cwd; with the query it scopes to the given path, and listing filters by it.)
  • Job records persisted serverUrl but not ownership. Cancel recomputed serverExternal by comparing the recorded URL with the current process's environment — a cancel process without the job-start env var concluded "not external" and sent /global/dispose to a user-managed server.

Fix

  • OpencodeServerClient takes a directory option and appends the canonical (realpath) workspace directory to every non-/global/ route — all client routes accept it per the pinned 1.17.15 OpenAPI document. /global/health and /global/dispose stay unscoped.
  • All client construction sites (turns, auth status, aborts, cancel) pass the invoking workspace.
  • getSessionRuntimeStatus now distinguishes env-configured (external) from persisted plugin-owned servers; background jobs persist serverExternal alongside serverUrl at job start, and runServerTurn progress events carry it so foreground job records get it too.
  • interruptServerTurn accepts the persisted flag and no longer infers ownership from the environment. Cancel teardown runs only when the job explicitly recorded plugin ownership (serverExternal === false); a recorded URL with unknown ownership (legacy records) fails safe and leaves the server running.

Tests

  • Two workspaces sharing one external fixture server: each gets a session scoped to its own repo, and both event subscriptions carry the right workspace (fixture now honors and records the directory query).
  • The review's exact cancel scenario: a job started with an external server, cancelled from an environment without the env var — the session is aborted, the job is cancelled, and the server stays alive (the fixture exits on dispose, so any regression fails the health assertion).
  • Client unit test: project routes carry directory, /global/* never does.
  • Live check against real OpenCode 1.17.15: POST /session?directory=X and scoped listing behave exactly as coded.
  • npm test: 92 passed, 0 failed, 0 skipped.

Server-PID identity verification on teardown remains #31.

🤖 Generated with Claude Code

External-server mode had two workspace-safety gaps. First, the client
never sent the `directory` query, so every project-scoped request (and
the /event subscription) resolved against the server process's launch
directory — a review could inspect, and a write task could edit, the
wrong repository. Second, job records stored only serverUrl; cancel
recomputed "external" by comparing against the CURRENT process's
OPENCODE_COMPANION_SERVER_URL, so a cancel process without the job-start
environment would send /global/dispose to a user-managed server.

The client now appends the canonical (realpath) workspace directory to
every non-/global/ route — all of them accept it per the 1.17.15 OpenAPI
document, and live checks confirm session creation and listing scope to
the query value. Job records persist `serverExternal` at job start (and
via progress updates for foreground runs); cancel passes the persisted
flag through interruptServerTurn, which no longer infers ownership from
the environment. Teardown runs only when the job explicitly recorded
plugin ownership; a recorded URL with unknown ownership (legacy records)
fails safe and leaves the server running.

The fixture now honors the directory query on session create/list and
records the scope of event subscriptions. New tests: two workspaces
sharing one external server get correctly-scoped sessions and event
streams; cancelling a job whose environment differs from the job-start
environment aborts the session but leaves the external server alive
(the fixture exits on dispose, so a regression fails the health check);
client unit coverage that project routes carry the directory and
/global routes never do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TheRealDinghyDog
TheRealDinghyDog merged commit ba7e063 into opencode-conversion Jul 10, 2026
1 check passed
@TheRealDinghyDog
TheRealDinghyDog deleted the fix/issue-29-directory-ownership branch July 11, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant