Skip to content

feat(threads): add workspace field to UpdateThreadRequest#2640

Closed
gaord wants to merge 3 commits into
Hmbown:mainfrom
gaord:feat/thread-workspace-update
Closed

feat(threads): add workspace field to UpdateThreadRequest#2640
gaord wants to merge 3 commits into
Hmbown:mainfrom
gaord:feat/thread-workspace-update

Conversation

@gaord
Copy link
Copy Markdown
Contributor

@gaord gaord commented Jun 3, 2026

Summary

Add workspace field to UpdateThreadRequest, allowing the thread's workspace to be updated via PATCH /v1/threads/{id}.

Motivation

When a thread created in workspace A is loaded and continued in workspace B, the thread remains bound to workspace A. This causes the conversation to stall or output to the wrong workspace because the engine processes messages in the context of workspace A.

This is particularly problematic for GUI clients (VSCode extension) where users may open different workspaces and want to resume conversations across them.

Changes

  • Add workspace: Option<String> field to UpdateThreadRequest in runtime_threads.rs
  • Add workspace update logic in update_thread() method
  • Include workspace in the validation check for at-least-one-field requirement

API Contract

PATCH /v1/threads/{id}
Content-Type: application/json

Request:
{
  "workspace": "/path/to/new/workspace"  // optional, along with other fields
}

Testing

  • Manually tested with GUI client: loading a thread from workspace A into workspace B now correctly updates the thread's workspace
  • The engine processes subsequent messages in the correct workspace context
  • No regression in existing thread update functionality

Greptile Summary

Adds workspace: Option<PathBuf> to UpdateThreadRequest so clients can reassign a thread's working directory via PATCH /v1/threads/{id}. The persistence side of the change is correct: the empty-string guard, the idempotency check (thread.workspace != workspace), and the store/event update are all in order.

  • UpdateThreadRequest gains a workspace field with an empty-path guard consistent with the existing model/mode guards.
  • The update block correctly short-circuits when the new value equals the current one, persists the change to the store, and fires a thread.updated event with a changes payload.

Confidence Score: 4/5

Safe to merge for persistence correctness; the workspace reassignment won't take effect mid-session if an engine is already cached for the thread.

The store-layer changes are correct — validation, idempotency check, and event emission all look right. However, ensure_engine_loaded (line 1950) returns the cached engine unchanged whenever one already exists in active.engines. If the client had already sent a turn before calling PATCH, subsequent turns will still run under the old workspace until the LRU evicts the engine, which silently defeats the PR's stated motivation for mid-session workspace switching.

crates/tui/src/runtime_threads.rs — specifically the interaction between update_thread and the engine cache in active.engines.

Important Files Changed

Filename Overview
crates/tui/src/runtime_threads.rs Adds workspace field to UpdateThreadRequest with correct empty-string validation and change tracking; persistence is sound but a previously-flagged engine-cache eviction issue remains unresolved.

Sequence Diagram

sequenceDiagram
    participant Client as GUI Client (VSCode)
    participant API as PATCH /v1/threads/{id}
    participant RTM as RuntimeThreadManager
    participant Store as ThreadStore
    participant Event as EventBus

    Client->>API: "PATCH /v1/threads/{id} {workspace: /path/to/workspaceB}"
    API->>RTM: update_thread(id, req)
    RTM->>RTM: Validate at least one field present
    RTM->>RTM: Validate workspace not empty
    RTM->>Store: get_thread(id)
    Store-->>RTM: "ThreadRecord (workspace = workspaceA)"
    RTM->>RTM: "thread.workspace != workspace?"
    alt workspace changed
        RTM->>RTM: "changes[workspace] = workspaceB"
        RTM->>RTM: "thread.workspace = workspaceB"
        RTM->>Store: save_thread(thread)
        RTM->>Event: "emit thread.updated {changes}"
    else same workspace
        RTM->>RTM: skip (no changes)
    end
    RTM-->>API: ThreadRecord
    API-->>Client: 200 OK + updated thread
Loading

Reviews (2): Last reviewed commit: "fix(threads): remove unnecessary engine ..." | Re-trigger Greptile

Allow updating a thread's workspace via the PATCH /v1/threads/{id}
endpoint. This fixes the cross-workspace thread loading issue where
a thread created in workspace A would remain bound to A even when
loaded and continued in workspace B, causing the conversation to
stall or output to the wrong workspace.

The GUI client can now update the thread's workspace to the current
workspace when loading a thread from a different workspace, ensuring
the engine processes messages in the correct context.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

Thanks @gaord for taking the time to contribute.

This repository is currently observing a maintainer-managed contribution gate in dry-run mode, so this pull request is staying open. When enforcement is enabled, pull requests from contributors who are not listed in .github/APPROVED_CONTRIBUTORS will be closed automatically.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant PR access by commenting /lgtm on a pull request.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a workspace field to UpdateThreadRequest and integrates it into the thread update logic. The feedback suggests changing the type of the workspace field from Option<String> to Option<PathBuf> to maintain consistency with CreateThreadRequest and eliminate redundant .into() conversions.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/tui/src/runtime_threads.rs Outdated
pub mode: Option<String>,
pub title: Option<String>,
pub system_prompt: Option<String>,
pub workspace: Option<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with CreateThreadRequest::workspace (which uses PathBuf) and to avoid unnecessary .into() conversions, UpdateThreadRequest::workspace should be defined as Option<PathBuf> instead of Option<String>. This also improves type safety.

    pub workspace: Option<PathBuf>,

Comment on lines +1171 to +1176
if let Some(workspace) = req.workspace
&& thread.workspace != workspace
{
thread.workspace = workspace.clone().into();
changes.insert("workspace".to_string(), json!(workspace));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since UpdateThreadRequest::workspace is updated to Option<PathBuf>, we can directly assign it to thread.workspace without calling .into(), avoiding unnecessary type conversion.

        if let Some(workspace) = req.workspace
            && thread.workspace != workspace
        {
            thread.workspace = workspace.clone();
            changes.insert("workspace".to_string(), json!(workspace));
        }

Comment thread crates/tui/src/runtime_threads.rs
Comment on lines +1171 to +1176
if let Some(workspace) = req.workspace
&& thread.workspace != workspace
{
thread.workspace = workspace.clone().into();
changes.insert("workspace".to_string(), json!(workspace));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Loaded engine is not evicted when workspace changes

update_thread persists the new workspace to disk but does not remove the thread's entry from active.engines. ensure_engine_loaded (line 1944) short-circuits and returns the cached engine unchanged whenever one is already present. If the engine was loaded before the PATCH call — e.g., the user had already resumed or sent a turn in workspace A — the next turn will still run in workspace A regardless of the update. The PR's core motivation (switching workspaces mid-session) will silently have no effect until the engine is evicted by the LRU. The cached engine entry for this thread should be removed inside update_thread when the workspace field changes.

Fix in Codex Fix in Claude Code Fix in Cursor

Comment thread crates/tui/src/runtime_threads.rs
1. Change UpdateThreadRequest.workspace from Option<String> to
   Option<PathBuf> for consistency with CreateThreadRequest and
   ThreadRecord, eliminating unnecessary .into() conversions.

2. Add empty-string validation for workspace, matching the existing
   guards on model and mode fields.

3. Evict the cached engine when workspace changes. Without this,
   ensure_engine_loaded would return the stale engine configured for
   the old workspace, silently ignoring the update — directly
   undermining the PR's stated purpose.

4. Remove unnecessary .clone() by reversing the insertion/assignment
   order so workspace is moved directly into thread.workspace.
@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented Jun 3, 2026

Thanks @gaord for the thread workspace-field work. The v0.8.52 release repair is done now, so this is queued for normal v0.8.53 review rather than being handled during the publish firefight. I am tracking that sweep in #2645; sorry the release cleanup made review timing noisy.

In the GUI scenario, the TUI process for workspace B never has a cached
engine for a thread created in workspace A, so evicting is a no-op.
The real fix is simply updating the thread record's workspace field —
ensure_engine_loaded will create a new engine with the correct workspace
on the next turn.
@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented Jun 5, 2026

Thanks @gaord — this UpdateThreadRequest.workspace API addition was harvested into the public v0.9.0 integration branch (codex/v0.9.0-stewardship) as 66c88ddfa.

The landed version accepts workspace updates through PATCH /v1/threads/{id}, rejects empty paths, rejects workspace changes during active turns, emits the workspace change, and evicts idle cached engines so the next turn starts in the updated workspace.

Verification recorded for the harvest: cargo fmt --all -- --check, git diff --check, cargo test -p codewhale-tui --bin codewhale-tui --locked update_thread_workspace -- --nocapture, cargo clippy -p codewhale-tui --locked -- -D warnings, and co-author trailer audit. The commit includes Harvested from PR #2640 by @gaord and your GitHub-mappable co-author trailer.

Closing this PR as harvested into the integration branch.

@Hmbown Hmbown closed this Jun 5, 2026
Hmbown added a commit that referenced this pull request Jun 5, 2026
Update the execution map after closing harvested or superseded PRs #2476, #2498, #2502, #2513, #2530, #2576, #2581, #2636, #2639, #2640, #2708, and #2730, and refresh the live PR count.
@gaord
Copy link
Copy Markdown
Contributor Author

gaord commented Jun 6, 2026

@Hmbown could you backport this bit into 0.8.54 as vscode GUI 0.1.2 is expecting this for release. pls let me know if there is any concern.

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.

2 participants