Part II · Boundaries and vocabularyThe ownership boundary
Chapter 16source checked

Control surface, not agent brain

For each environment, T3 Code authoritatively coordinates repository and provider execution without replacing a provider's model loop, native context, or authentication.

What this chapter resolves
  • Draw the ownership boundary between client, T3 server, provider adapter, and provider CLI.
  • Explain why multi-harness support is an adapter problem rather than a shared agent implementation.
  • Identify the server services that make T3 Code more than a terminal wrapper.

The product’s cleanest architectural sentence is: each T3 environment is a server-authoritative control plane around replaceable coding-agent runtimes.

Clients are thin with respect to trusted workspace execution—not small programs. They express intent and render projected state, while Electron and mobile still own substantial native behavior. For the selected environment, the server owns repository-facing orchestration. A provider runtime owns its model/tool loop. The adapter and ProviderService divide live protocol behavior from persisted session binding and recovery state.

docs/internals/overview.md:5–28 ↗verbatim · markdown · f47d2d70
provider credentials, or machine state for the environment's. The desktop app bundles a server,
but its renderer follows the same boundary.
 
## Ownership boundaries
 
Provider processes, terminals, Git, and project files belong to the server. Shared connection and
domain state belongs in `packages/client-runtime`; clients supply platform services and UI.
Keeping that logic shared prevents reconnect and multi-environment behavior from diverging between
web and mobile. See [connection runtime](./connection-runtime.md) and
[remote environments](./remote.md).
 
The [RPC contract](../../packages/contracts/src/rpc.ts) is the boundary between independently
versioned clients and servers. Subscriptions send the state a client needs, so a client viewing one
thread does not pay for every thread's history. Authentication of a socket does not authorize every
method on it. See [environment auth](./environment-auth.md).
 
### Pull request linking compatibility
 
Web, desktop, mobile, and environments upgrade independently. Negotiate linking through the
environment descriptor, never through a client version or an assumed coordinated release:
 
| Environment capability                | Client behavior                                                                                                   |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `threadPullRequests: true`            | Use persisted `pullRequests[]`, multi-link commands, stack UI, and reverse thread lookup.                         |
Read this as: This boundary explains both remote access and multi-provider support. A remote client changes the transport to the same server; a new harness changes the adapter below it.

Six ownership layers

Ownership of durable, runtime, provider-native, client-local, and workspace state
OwnerOwnsDoes not own
T3 domaindurable project/thread history, lifecycle intent, messages, activities, proposed plans, checkpoint metadata, query projectionslive process objects, provider-native memory, every transient UI signal
ProviderService + session directoryinstance routing and persisted binding, cwd, model/runtime payload, continuation cursor, statusnative cursor meaning or provider reasoning
Provider adapterlive process/session object, native protocol, cursor interpretation, approval/input mapping, canonical event translationcross-provider product policy or durable projections
Provider runtimemodel/tool loop, native conversation/context, compaction, provider account authenticationT3’s multi-environment UI, checkpoint model, or product lifecycle
Client shellnavigation, presentation, caches, device settings; mobile drafts and durable outbox; Electron OS integrationsauthoritative environment thread history or remote workspace execution
Selected environmentworkspace files, provider processes, Git, PTYs, server SQLite/files/settings/secretsother environments supervised by the same client

A normalized, deliberately bounded seam

apps/server/src/provider/Services/ProviderAdapter.ts:67–94 ↗verbatim · typescript · cbef9b58
export interface ProviderAdapterShape<TError> {
  /**
   * Provider kind implemented by this adapter.
   */
  readonly provider: ProviderDriverKind;
  readonly capabilities: ProviderAdapterCapabilities;
 
  /**
   * Start a provider-backed session.
   */
  readonly startSession: (
    input: ProviderSessionStartInput,
  ) => Effect.Effect<ProviderSession, TError>;
 
  /**
   * Send a turn to an active provider session.
   */
  readonly sendTurn: (
    input: ProviderSendTurnInput,
  ) => Effect.Effect<ProviderTurnStartResult, TError>;
 
  /** Omitted when this adapter does not support manual context compaction. */
  readonly compaction?: ProviderCompaction<TError>;
 
  /**
   * Interrupt an active turn.
   */
  readonly interruptTurn: (threadId: ThreadId, turnId?: TurnId) => Effect.Effect<void, TError>;
Read this as: The common lifecycle declares provider identity and capabilities, then starts sessions, sends turns, optionally compacts, and interrupts. Provider-specific protocols stay outside orchestration.
apps/server/src/provider/Services/ProviderAdapter.ts:149–157 ↗verbatim · typescript · 4a95ebce
  /**
   * Stop all sessions owned by this adapter.
   */
  readonly stopAll: () => Effect.Effect<void, TError>;
 
  /**
   * Canonical runtime event stream emitted by this adapter.
   */
  readonly streamEvents: Stream.Stream<ProviderRuntimeEvent>;
Read this as: Every implementation emits the same ProviderRuntimeEvent stream. This is the upward-facing seam that keeps the projector and clients provider-agnostic.

The contract is not a promise of feature equality. Its lifecycle, interaction, history, rollback, feedback, and event-stream surface is deliberately bounded, but not tiny. At this revision, the separate common capability object only describes session model switching; other semantic differences are expressed by adapter behavior, errors, and provider presentation. Uniform orchestration is achieved by normalizing at the boundary, not by pretending the providers are identical.

Why the server is the real product kernel

apps/server/src/server.ts:353–416 ↗verbatim · typescript · 4e3a4403
  Layer.provideMerge(VcsDriverRegistryLayerLive),
);
 
const VcsLayerLive = Layer.empty.pipe(
  Layer.provideMerge(VcsProjectConfig.layer),
  Layer.provideMerge(VcsDriverRegistryLayerLive),
  Layer.provideMerge(VcsProvisioningService.layer.pipe(Layer.provide(VcsDriverRegistryLayerLive))),
  Layer.provideMerge(GitWorkflowLayerLive),
  Layer.provideMerge(ReviewLayerLive),
  Layer.provideMerge(SourceControlRepositoryServiceLayerLive),
  Layer.provideMerge(
    VcsStatusBroadcaster.layer.pipe(
      Layer.provide(GitWorkflowLayerLive),
      Layer.provide(
        VcsStatusBroadcaster.autoPullPolicyLayer.pipe(Layer.provide(ServerSettingsLayerLive)),
      ),
    ),
  ),
);
 
const CheckpointingLayerLive = Layer.empty.pipe(
  Layer.provideMerge(CheckpointDiffQuery.layer),
  Layer.provideMerge(CheckpointStore.layer.pipe(Layer.provide(VcsDriverRegistryLayerLive))),
);
 
const PortScannerLayerLive = PortScanner.layer.pipe(Layer.provide(ProcessRunner.layer));
 
const TerminalLayerLive = TerminalManager.layer.pipe(
  Layer.provide(PtyAdapterLive),
  Layer.provide(PortScannerLayerLive),
  Layer.provide(NativeTelemetryLayerLive),
);
 
const PreviewLayerLive = Layer.empty.pipe(
  Layer.provideMerge(PreviewManager.layer),
  Layer.provideMerge(PortScannerLayerLive),
);
 
const DeviceLayerLive = DeviceService.layer.pipe(
  Layer.provide(ServerSettingsLayerLive),
  Layer.provide(ProcessRunner.layer),
  Layer.provide(NetService.layer),
);
 
const WorkspaceEntriesLayerLive = WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer));
 
const WorkspaceFileSystemLayerLive = WorkspaceFileSystem.layer.pipe(
  Layer.provide(WorkspacePaths.layer),
  Layer.provide(WorkspaceEntriesLayerLive),
);
 
const WorkspaceLayerLive = Layer.mergeAll(
  WorkspacePaths.layer,
  WorkspaceEntriesLayerLive,
  WorkspaceFileSystemLayerLive,
);
 
const ProjectFaviconResolverLayerLive = ProjectFaviconResolver.layer.pipe(
  Layer.provide(WorkspacePaths.layer),
  Layer.provide(T3ProjectFileLoader.layer),
);
 
const ServerEnvironmentLayerLive = ServerEnvironment.layer.pipe(
  Layer.provide(ServerSecretStore.layer),
Read this as: This is actual layer composition rather than an import list: authorization, managed remote endpoints, provider runtime, event ingestion, command reactions, and the server dependency graph are wired into one owned runtime.
Decision

Keep workspace authority server-side

A phone can supervise the same environment as desktop because remote repository, provider, Git, and PTY operations remain behind that environment's server.

Decision

Keep provider complexity at the edge

The orchestration core speaks canonical commands and events; Codex/Claude/ACP/OpenCode quirks remain inside adapters.

Trade-off

The server becomes a large trust boundary

Remote authentication and per-method scopes matter because one socket fronts shells, files, Git, and autonomous coding agents.

Trade-off

Capabilities can diverge

A common product model still needs explicit answers for provider-only features, resume semantics, plan events, usage, and approvals.

Source trail

T3
Source-locked editionRead against 859304b78 · 10 Sept 2026
Book search

Find a concept, module, or source path

Type two or more characters.