Summary
The OpenCode provider can run in the original project checkout even when T3 Code believes the provider cwd is a selected worktree.
This appears to affect OpenCode session directory handling, and may affect both newly-created sessions and resumed sessions.
Impact
High severity: T3 shows a worktree-isolated thread, but OpenCode can edit the main checkout.
Environment
- T3 Code around
v0.0.28 / current main
- OpenCode
1.17.13
- T3 dependency currently resolved to
@opencode-ai/sdk@1.15.13
- Latest npm versions are:
opencode-ai@1.17.13
@opencode-ai/sdk@1.17.13
- Provider: OpenCode
- Mode: worktree-isolated thread
- OS: Linux
Evidence
T3 provider runtime state showed the intended worktree cwd:
runtime_cwd = /home/user/.t3/worktrees/scanner/t3code-d448123c
but OpenCode's own session metadata showed the same T3 session bound to the main checkout:
directory = /home/user/pj/macs/scanner
Example diagnostic query on T3 state:
SELECT
t.thread_id,
t.worktree_path,
json_extract(r.runtime_payload_json, '$.cwd') AS runtime_cwd,
json_extract(r.resume_cursor_json, '$.sessionId') AS opencode_session_id,
r.status,
r.last_seen_at
FROM projection_threads t
JOIN provider_session_runtime r ON r.thread_id = t.thread_id
WHERE r.provider_name = 'opencode'
ORDER BY r.last_seen_at DESC;
OpenCode session metadata can then be compared via the OpenCode server session API, e.g.:
GET /session?directory=/home/user/pj/macs/scanner
The OpenCode session id from T3 appears under the main checkout instead of the T3 worktree.
Expected
For a worktree-isolated T3 thread:
T3 thread worktree_path == T3 provider runtime cwd == OpenCode session directory
Actual
T3 thinks the provider is in the worktree:
T3 provider runtime cwd == /home/user/.t3/worktrees/scanner/t3code-d448123c
but OpenCode is actually associated with the original checkout:
OpenCode session directory == /home/user/pj/macs/scanner
Suspected Causes
1. OpenCode SDK / server version skew
T3 is using @opencode-ai/sdk@1.15.13 while current OpenCode is 1.17.13.
Updating T3 to @opencode-ai/sdk@1.17.13 seems appropriate.
2. T3 relies on client-level x-opencode-directory
T3 creates the SDK client with a directory option, which sets x-opencode-directory.
Current SDK behavior rewrites x-opencode-directory into ?directory= only for GET / HEAD requests. Important provider operations are POST calls, including:
session.create
session.promptAsync
So relying only on client-level directory / x-opencode-directory is not sufficient. T3 should pass directory explicitly to the session API calls.
3. Resume cursor directory mismatch
T3 resumes OpenCode sessions by id without verifying that the returned OpenCode session directory matches the requested T3 cwd.
If the resume cursor points to an OpenCode session in the main checkout, T3 can continue it while believing it is operating in the selected worktree.
Proposed Fix
- Update
@opencode-ai/sdk to match current OpenCode, e.g. 1.17.13.
- Pass
directory explicitly on OpenCode session calls:
session.create
session.get
session.promptAsync
session.messages
session.revert
session.abort
- After
session.create, validate returned session.directory when present.
- After
session.get, require returned session.directory to match the requested T3 cwd.
- If a resumed OpenCode session belongs to a different directory, discard that resume cursor and create a fresh OpenCode session in the requested cwd.
- Fail closed on directory mismatch rather than silently running in the wrong checkout.
Related Runtime State Bug
When sending a message to an in-progress OpenCode session, T3 can show Runtime error and remain stuck in Working....
Observed OpenCode events include MessageAbortedError / Aborted, while T3 provider runtime state remains running with an active turn id.
T3 should treat OpenCode MessageAbortedError as an aborted turn, clear activeTurnId, and mark the provider session ready instead of leaving the UI stuck.
Related Issue
This is related to, but distinct from, the worktree projection race reported in:
#3653
Summary
The OpenCode provider can run in the original project checkout even when T3 Code believes the provider cwd is a selected worktree.
This appears to affect OpenCode session directory handling, and may affect both newly-created sessions and resumed sessions.
Impact
High severity: T3 shows a worktree-isolated thread, but OpenCode can edit the main checkout.
Environment
v0.0.28/ current main1.17.13@opencode-ai/sdk@1.15.13opencode-ai@1.17.13@opencode-ai/sdk@1.17.13Evidence
T3 provider runtime state showed the intended worktree cwd:
but OpenCode's own session metadata showed the same T3 session bound to the main checkout:
Example diagnostic query on T3 state:
OpenCode session metadata can then be compared via the OpenCode server session API, e.g.:
The OpenCode session id from T3 appears under the main checkout instead of the T3 worktree.
Expected
For a worktree-isolated T3 thread:
Actual
T3 thinks the provider is in the worktree:
but OpenCode is actually associated with the original checkout:
Suspected Causes
1. OpenCode SDK / server version skew
T3 is using
@opencode-ai/sdk@1.15.13while current OpenCode is1.17.13.Updating T3 to
@opencode-ai/sdk@1.17.13seems appropriate.2. T3 relies on client-level
x-opencode-directoryT3 creates the SDK client with a
directoryoption, which setsx-opencode-directory.Current SDK behavior rewrites
x-opencode-directoryinto?directory=only forGET/HEADrequests. Important provider operations arePOSTcalls, including:session.createsession.promptAsyncSo relying only on client-level
directory/x-opencode-directoryis not sufficient. T3 should passdirectoryexplicitly to the session API calls.3. Resume cursor directory mismatch
T3 resumes OpenCode sessions by id without verifying that the returned OpenCode session
directorymatches the requested T3 cwd.If the resume cursor points to an OpenCode session in the main checkout, T3 can continue it while believing it is operating in the selected worktree.
Proposed Fix
@opencode-ai/sdkto match current OpenCode, e.g.1.17.13.directoryexplicitly on OpenCode session calls:session.createsession.getsession.promptAsyncsession.messagessession.revertsession.abortsession.create, validate returnedsession.directorywhen present.session.get, require returnedsession.directoryto match the requested T3 cwd.Related Runtime State Bug
When sending a message to an in-progress OpenCode session, T3 can show
Runtime errorand remain stuck inWorking....Observed OpenCode events include
MessageAbortedError/Aborted, while T3 provider runtime state remainsrunningwith an active turn id.T3 should treat OpenCode
MessageAbortedErroras an aborted turn, clearactiveTurnId, and mark the provider session ready instead of leaving the UI stuck.Related Issue
This is related to, but distinct from, the worktree projection race reported in:
#3653