Skip to content
Source
Sign inSign up

overview.md

133 lines8.8 KB

1# Architecture
2​
3T3 Code keeps execution in the environment that owns the workspace. Web, desktop, and mobile
4clients control it over authenticated RPC. A remote client must never substitute its own filesystem,
5provider credentials, or machine state for the environment's. The desktop app bundles a server,
6but its renderer follows the same boundary.
7​
8## Ownership boundaries
9​
10Provider processes, terminals, Git, and project files belong to the server. Shared connection and
11domain state belongs in `packages/client-runtime`; clients supply platform services and UI.
12Keeping that logic shared prevents reconnect and multi-environment behavior from diverging between
13web and mobile. See [connection runtime](./connection-runtime.md) and
14[remote environments](./remote.md).
15​
16The [RPC contract](../../packages/contracts/src/rpc.ts) is the boundary between independently
17versioned clients and servers. Subscriptions send the state a client needs, so a client viewing one
18thread does not pay for every thread's history. Authentication of a socket does not authorize every
19method on it. See [environment auth](./environment-auth.md).
20​
21### Pull request linking compatibility
22​
23Every protocol-2 environment advertises `threadPullRequests`. Once an environment's server config
24has loaded, clients use persisted `pullRequests[]`, multi-link commands, stack UI, and reverse thread
25lookup. Before that, they hide linking actions.
26​
27Environments keep advertising `threadPullRequestLinking`, accepting legacy metadata commands, and
28emitting the derived `linkedPullRequest` field, because older clients still read them. That
29hostless field includes only links in the thread project's own repository. Retain the legacy wire
30fields, projection column, and replay support.
31​
32Provider-specific behavior belongs behind an adapter. Orchestration works with normalized commands
33and events, so adding a provider should not require branches throughout the domain or clients.
34See [provider constraints](./providers.md).
35​
36## Settings ownership
37​
38Client preferences stay in the current client; environment defaults and project overrides stay
39on their owning server. The web and desktop settings target is URL state, resolved against current
40connections and project membership. An unavailable target must not fall back to another environment.
41**All environments** is an explicit bulk edit of connected, loaded servers, not a durable global
42default or a promise to synchronize offline or future environments. Project-group targets similarly
43select known environment-local checkouts; the group itself does not store inherited defaults.
44​
45## Durable intent and side effects
46​
47Orchestration state is decided as events, and projections are the source of truth once they apply
48them. The [v2 orchestrator](../../apps/server/src/orchestration-v2/Orchestrator.ts) serializes commands and
49decides events without performing provider or filesystem work.
50[EventSink](../../apps/server/src/orchestration-v2/EventSink.ts) commits events, persisted projections,
51the accepted command receipt, and outbox effects in one database transaction. Subscribers receive
52events after that commit. This keeps command retries idempotent and prevents a persisted projection
53from getting ahead of the event log.
54​
55The [effect worker](../../apps/server/src/orchestration-v2/EffectWorker.ts) performs side effects
56after intent has been recorded, then feeds results back into orchestration. A command acknowledgement
57therefore means the intent committed, not that the provider, checkpoint, or other follow-up work
58finished. Keep external I/O out of command decisions and the database transaction. Effects tied to
59a lost provider process cannot simply replay; recovery retires them before admitting new work.
60​
61The event log only keeps what live readers need: about a day of history for reconnect replay and
62command-id retries.
63[ProjectionMaintenance](../../apps/server/src/orchestration-v2/ProjectionMaintenance.ts) deletes
64older events the projections cover, so projections cannot be rebuilt from the log. It keeps each
65stream's newest event (high-water marks, stream versions, snapshot sequences), `thread.created`
66(the legacy importer's marker), every `running` run update (Codex usage history reads the first
67of each attempt), and prompt anchors that retained subagent rows reference. A client whose cursor
68is below both its thread's newest event and the recorded pruned-through sequence missed pruned
69events, so it resumes from a snapshot; an idle thread's cursor resumes without one. Resumes
70recheck that sequence after reading the replay, since a pass can prune during the read. V2 command
71receipts stay for idempotency: a retry older than the retained history is still deduplicated,
72but its replay comes back without the pruned events. Receipts of imported legacy commands are
73removed once their threads are imported.
74​
75Retained events must remain decodable on replay. Changing a schema affects old environments at
76startup as well as live RPC traffic. Compatibility work must account for stored history, not just
77what the newest client sends.
78​
79## Turn completion and checkpoints
80​
81A provider turn ending and its follow-up work settling are separate milestones. Orchestration
82records provider turn and run state independently from
83[run finalization](../../apps/server/src/orchestration-v2/RunFinalizationService.ts). A late
84checkpoint or diff must not extend the recorded provider duration or keep the client showing
85provider work as active. PR discovery after completion also checks that the checkout still matches
86the thread's non-default branch and that a newer run is not active.
87​
88[Checkpoints](../../apps/server/src/checkpointing/CheckpointStore.ts) use hidden Git refs to
89capture workspace state without adding commits to the user's branch. A revert must coordinate
90workspace state with the provider conversation. A provider that cannot roll back its conversation
91must reject that operation before changing the filesystem.
92​
93Thread settlement is server-owned. The
94[settlement service](../../apps/server/src/orchestration-v2/ThreadSettlementService.ts) evaluates PR
95and inactivity settings without a connected client. Merge notifications invalidate cached PR state
96and trigger a check. A merge outside T3, such as an agent running `gh pr merge`, sends no
97notification, so the [PR sync reactor](../../apps/server/src/orchestration-v2/PullRequestSyncReactor.ts)
98re-reads a thread's open links when a run that ran a merge or close command ends. Each forge's
99definition names its CLI's commands in `changeRequestCloseCommand`. The guarded
100`thread.auto-settle` command rejects newer activity, explicit settlement overrides, and live or
101blocked work. It records the activity timestamp for stable
102sorting and detaches idle provider sessions. Clients render the persisted result; they do not
103derive settlement from their own clocks or PR caches.
104​
105## Waiting for asynchronous work
106​
107Tests use [drainable workers](../../packages/shared/src/DrainableWorker.ts) to wait until both the
108queue and its current item have finished. An empty queue alone does not prove the worker is idle.
109​
110V2 tests also drain the effect worker or await a specific persisted event or receipt. Test signals
111are separate from the durable command receipts that make dispatch idempotent. Production behavior
112must use persisted state and events, not test instrumentation or assumptions about elapsed time.
113​
114The Electron shell acquires `DesktopPreReadyPlatform.layer` synchronously before asynchronous
115services. On Linux this sets the desktop-entry identity and global-shortcut portal flags before
116Chromium initializes its portal connection. Setting the identity later in `DesktopAppIdentity`
117is too late: Chromium caches the first registration, including failures. The identity must match
118the installed entry managed by `DesktopLinuxUrlHandler`. Pre-ready setup also refreshes that entry's
119`Exec` path before portal registration: AppImage updates can remove the previous executable, which
120makes the old entry invalid even though its filename is correct. The later URL handler avoids
121rewriting an identical entry while the portal may be reading it. On Wayland, Electron's synchronous
122shortcut-registration result only confirms submission; it does not confirm desktop consent or
123an active binding.
124​
125Native modules never load in the Electron main process on the startup path, and the two the
126snapshot feature keeps are isolated: `@crowecawcaw/xa11y` runs only in forked Node-mode children
127(`SnapShotAccessibilityWorker`, `RegionSnapShotWorker`) and a worker thread, and `ffi-rs` loads
128lazily inside `WindowsForeground.ts` for a handful of Win32 calls. macOS window lookup shells out
129to `osascript` instead of a native addon. A crash or stall in any of these must not take the app
130down, so new native capability goes in a child with a deadline, not an `import` in main.
131​
132See the [glossary](./glossary.md) for shared terms and the
133[development runbook](../operations/development.md) for setup and checks.