Skip to content

[Bug]: OpenCode provider can ignore T3 worktree cwd and run in main checkout #3656

Description

@patroza

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions