Skip to content

fix(t3team): run chat threads on their project's environment, not always the primary - #329

Draft
johnnyelwailer wants to merge 2 commits into
mainfrom
nexi/t3team-dispatch-thread-environment
Draft

johnnyelwailer wants to merge 2 commits into
mainfrom
nexi/t3team-dispatch-thread-environment

Conversation

@johnnyelwailer

Copy link
Copy Markdown
Owner

Problem

The t3team chat assumed there is only one server. Its hooks built every thread reference with usePrimaryEnvironmentId() (t3team-useThreadChatServerState.ts:33, t3team-useThreadChatComposerState.ts:36), and its dispatcher always sent commands to the primary (t3team-orchestrationDispatch.ts:20).

A thread whose project lives on another environment, such as a cloud session's VM, was created on the local server, which rejected it:
Orchestration command invariant failed (thread.create): Project '…' does not exist. The UI showed "This didn't start" or "Launch interrupted". Upstream's ChatView already routes per environment (threadEnvironment.create / startTurn); only the fork's t3team layer didn't.

Fix

  • The chat resolves a thread's environment from its project (EnvironmentProject.environmentId). It falls back to the primary only while the project isn't known yet (resolveThreadChatEnvironmentId, with a unit test).
  • BackendApi.dispatchCommand(command, { environmentId }): the dispatcher sends to that environment through the same createEnvironmentRpcCommand path, and every bootstrap and turn call site passes its thread's environment.
  • dispatchThreadBootstrapCreateWithRecovery moves next to its only callee, which keeps the kickoff module under the 200-line cap (179).

Verified

  • tsc --noEmit (apps/web) is clean.
  • Focused tests pass 17/17. The bootstrap tests now assert that each command is routed to the thread's environment.
  • t3team-additive-guard passed.
  • Live, partly. In an isolated Nexi Work shell running this branch, before the second commit the local server's trace still showed thread.create arriving there. That exposed the hard-coded environment in the hooks, which this PR also fixes.
  • Not yet live-verified end to end. The final build couldn't reconnect to the cloud VM. The client stayed at "Reconnecting" and made no relay /connect calls while the relay reported the environment online. That's a separate client reconnect bug.

Not covered

The t3team HTTP routes (/api/t3team/thread/*: tool context, recipe workflows, placements) still go only to the primary server. The architectural follow-up is for the t3team chat to call upstream's threadEnvironment.* commands directly and drop its own dispatch layer.

🤖 Generated with Claude Code

…ays the primary

The t3team chat hard-coded the primary environment: its server-state hooks
built every thread ref with usePrimaryEnvironmentId(), and its dispatcher
(runT3TeamOrchestrationDispatch) always sent commands to the primary. A
thread whose project lives on another environment — a cloud session's VM —
was therefore created on the local server, which rejected it with "Project
… does not exist for command 'thread.create'" ("This didn't start" /
"Launch interrupted" in the UI). Upstream's ChatView already routes per
environment via threadEnvironment.create/startTurn; only the fork layer
assumed one server.

- The chat resolves a thread's environment from its project
  (EnvironmentProject.environmentId), falling back to the primary only while
  the project is not known yet (resolveThreadChatEnvironmentId).
- BackendApi.dispatchCommand takes an optional target environment; the
  dispatcher uses it (same createEnvironmentRpcCommand path as before), and
  every bootstrap/turn call site passes its thread's environment.
- dispatchThreadBootstrapCreateWithRecovery moves next to
  dispatchThreadBootstrapCreate (its only callee) to keep the kickoff module
  under the 200-line cap.

Not covered: the t3team HTTP routes (/api/t3team/thread/*) still go to the
primary server only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Sep 28, 2026
…ommands

Replaces the fork's own single-server dispatch layer with upstream's typed
environment commands, so every t3team command names the environment that
owns its thread or project:

- BackendApi.dispatchCommand and t3team-orchestrationDispatch.ts are gone.
  Call sites run threadEnvironment.create / startTurn / updateMetadata and
  projectEnvironment.create / update through runT3TeamEnvironmentCommand,
  which has no default environment.
- Work-project creation and binding repair target the primary explicitly
  (primaryEnvironmentIdOrThrow): their workspace setup is primary-server HTTP.
- The composing heartbeat takes the thread ref the composer store already
  has (resolveComposerThreadRef), so it no longer pings the primary for a
  cloud session's thread.
- Draft feedback resolves the proposing thread's environment from upstream's
  thread shells (t3team-threadEnvironmentLookup) and reports "not connected"
  instead of silently sending to the primary.
- The widget prompt reuses sendT3TeamThreadTurn, the one threadId-addressable
  send path, instead of its own copy (drops the file back under the cap).

Tests now assert which upstream command ran on which environment. tsc clean;
20 affected test files / 205 tests pass (all 29 previously-passing cases in
the touched suites were re-checked on clean main first).

Not covered: the t3team HTTP routes (/api/t3team/thread/*) remain
primary-only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for 0782d14.

This comment will update automatically after the next completed run.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant