1# Thread Lineage And Context Transfer
2
3Forking, provider handoff, merge-back, and subagents are separate product features, but they should share one orchestration model. The common primitive is not "fork" and not "summary". It is:
4
5```text
6thread relationship
7 + source point
8 + optional context transfer resolution
9```
10
11The app should preserve lineage cheaply and resolve expensive provider/context work lazily when a run actually needs it.
12
13## Product Use Cases
14
15### Fork To Explore
16
17A user forks an existing app thread to explore a tangent without polluting the source thread's context.
18
19```text
20source AppThread
21 -> user forks from stable source point
22 -> new idle AppThread
23 -> no provider chosen yet
24 -> first message chooses provider/model
25```
26
27If the first run on the fork uses the same provider as the source and the provider supports native fork, use the provider-native fork path. If it uses a different provider, materialize portable context at first dispatch.
28
29### Continue Same Thread With A Different Agent
30
31A user continues the same app thread with another provider. This is not a new app thread, but it is still a context transfer.
32
33```text
34AppThread runs 1-5 with Codex
35user sends run 6 with Claude
36 -> resolve context transfer from Codex-backed source state to Claude
37 -> start Claude provider thread with handoff context + user message
38```
39
40The app thread remains the canonical conversation. Provider-native threads are backing handles with explicit coverage.
41
42### Merge A Fork Back Into Its Source
43
44A user explores deeply in a fork, then brings the result back to the source thread.
45
46```text
47source thread at source point S
48fork explores runs F1-Fn
49user sends next message in source thread with "bring this back"
50 -> build delta from S to fork latest stable point
51 -> inject delta context with the source-thread user message
52```
53
54This is not a full source-thread provider switch. It is a targeted context transfer from the fork back to the original thread.
55
56### Subagents
57
58Subagents are the same family of operation with a different creator and lifecycle contract.
59
60Native subagents:
61
62- the provider spawns the child through its native capability;
63- the app observes native child refs/events;
64- the child is modeled as a child execution node and, when useful, as a related app subthread.
65
66Cross-provider T3 subagents:
67
68- the parent agent calls an app-owned tool;
69- the app creates a child app thread/run with a relationship to the parent;
70- the child result is transferred back to the parent as a subagent result context transfer.
71
72The shared model should make user forks and agent-created subthreads feel like the same graph shape with different `createdBy` and lifecycle policy.
73
74## Core Concepts
75
76### Thread Relationship
77
78Thread relationship describes why a target thread or run exists relative to another thread.
79
80```ts
81type ThreadRelationshipKind =
82 "fork" | "provider_handoff" | "merge_back" | "subagent_spawn" | "subagent_result";
83```
84
85Relationships are product-level and provider-neutral. A fork is not the same thing as a provider-native fork. A provider-native fork is one possible resolution strategy for a fork relationship.
86
87### Source Point
88
89Source point describes the exact state being transferred.
90
91```ts
92type ContextSourcePoint = {
93 threadId: ThreadId;
94 runId?: RunId;
95 checkpointId?: CheckpointId;
96 turnItemId?: TurnItemId;
97 providerThreadRef?: NativeThreadRef;
98 providerTurnRef?: NativeTurnRef;
99};
100```
101
102Stable source points should prefer completed runs/checkpoints. Active runs are harder because provider chunks may still arrive and checkpoints may not be final.
103
104### Context Transfer
105
106Context transfer is the durable operational record that connects source and target. It is cheap to create and may stay unresolved until first use.
107
108```ts
109type ContextTransfer = {
110 id: ContextTransferId;
111 type: ThreadRelationshipKind;
112 sourceThreadId: ThreadId;
113 targetThreadId: ThreadId;
114 sourcePoint: ContextSourcePoint;
115 basePoint?: ContextSourcePoint;
116 sourceProvider?: ProviderKind;
117 targetProvider?: ProviderKind;
118 status:
119 "pending" | "resolved_native" | "resolved_portable" | "failed" | "consumed" | "superseded";
120 resolution?: ContextTransferResolution;
121 createdBy: "user" | "agent" | "system";
122 createdAt: string;
123 consumedAt?: string;
124};
125```
126
127`basePoint` is used for delta transfers, especially merge-back. For a fork merge-back, `basePoint` is the original fork source point and `sourcePoint` is the fork's latest stable point.
128
129### Context Handoff
130
131Context handoff is the expensive materialized context artifact. It is created lazily only when native transfer is unavailable or insufficient.
132
133```ts
134type ContextHandoff = {
135 id: ContextHandoffId;
136 transferId: ContextTransferId;
137 kind: "portable_context" | "delta_context" | "checkpoint_context";
138 payload: PortableContextPayload;
139 createdAt: string;
140};
141```
142
143Context handoffs should be auditable app artifacts, not hidden prompt concatenation.
144
145### Resolution Strategy
146
147Resolution decides how the target provider/thread receives the source context.
148
149```ts
150type ContextTransferResolution =
151 | {
152 strategy: "native_fork";
153 providerThreadRef: NativeThreadRef;
154 }
155 | {
156 strategy: "portable_context";
157 contextHandoffId: ContextHandoffId;
158 }
159 | {
160 strategy: "delta_context";
161 contextHandoffId: ContextHandoffId;
162 }
163 | {
164 strategy: "checkpoint_context";
165 contextHandoffId: ContextHandoffId;
166 };
167```
168
169Same-provider forks should prefer `native_fork` when the source native refs are strong and the adapter supports it. Cross-provider handoff and merge-back usually require a context handoff.
170
171## Lazy Resolution
172
173Fork creation should be cheap:
174
175```text
176thread.fork
177 -> create target AppThread
178 -> record thread relationship / ContextTransfer(status=pending)
179 -> do not create provider session
180 -> do not create provider thread
181 -> do not build portable context
182```
183
184First dispatch on a pending fork resolves the transfer:
185
186```text
187first run on fork chooses provider P
188 -> find pending fork ContextTransfer
189 -> if source provider == P and native fork is supported:
190 resolve native fork
191 else:
192 materialize portable context
193 -> create/resume provider thread
194 -> send handoff/native context + user message
195```
196
197This avoids forcing users to choose an agent at fork time and avoids generating summaries that may never be used.
198
199## Runtime Entry Point
200
201Run startup should have one provider-neutral hook:
202
203```ts
204resolveStartContext({
205 threadId,
206 provider,
207 message,
208}): StartContextResolution
209```
210
211This hook checks pending context transfers targeting the thread/run and chooses a strategy:
212
2131. No transfer needed: target provider already has current coverage.
2142. Native transfer: same provider and adapter supports native fork/resume.
2153. Portable transfer: build a context handoff and inject it into the run.
2164. Delta transfer: build the changes between `basePoint` and `sourcePoint`.
2175. Unsupported: fail explicitly before provider work starts.
218
219Provider adapters own native details. The orchestrator owns the relationship, source point, durable transfer record, and command receipts.
220
221For Codex, native `thread/fork` accepts an inclusive `lastTurnId` boundary. When the app source point is a completed provider turn with a native turn reference, the Codex adapter passes that native id so the provider creates the fork at the requested point directly. This is the only viable path on paginated Codex threads, which reject `thread/rollback`. If no native turn reference is available, the adapter falls back to forking the latest native state and rolling back the fork by the number of later terminal provider turns — and reports an explicit failure when the forked thread uses paginated history, since that fallback cannot be honored there. The orchestrator still passes a provider-neutral source point and source provider-turn history; it does not encode Codex boundary or rollback policy.
222
223The same paginated-history constraint applies to direct checkpoint rollback: `thread/rollback` only works on legacy-history Codex threads. The V2 adapter probes `historyMode` before rolling back and fails explicitly on paginated threads rather than sending a request Codex will reject. The paginated equivalent (page `thread/turns/list` to find the first removed turn, then `thread/revert` with `beforeTurnId`) is not implemented yet.
224
225## Data Ownership
226
227`AppThread` should store lightweight browsing lineage:
228
229```ts
230type AppThreadLineage = {
231 parentThreadId: ThreadId | null;
232 relationshipToParent: "fork" | "subagent" | null;
233 rootThreadId: ThreadId;
234};
235```
236
237Operational transfer details should live in `ContextTransfer`, not directly on `AppThread`, because a thread can participate in many transfers:
238
239- created by fork;
240- later switched to another provider;
241- later merged back to its source;
242- later spawned subagents.
243
244## Checkpoint And Active-Run Policy
245
246The first implementation should prefer stable source points:
247
248- completed run checkpoint;
249- explicit checkpoint;
250- idle provider thread with known coverage.
251
252Forking or handoff from an active run should either:
253
254- use the latest completed checkpoint, or
255- be rejected until active-run semantics are designed.
256
257Do not silently fork from partially streamed provider state. That weakens correlation and makes replay/recovery difficult.
258
259## Current Implementation Boundary
260
261The first backend slice implements lazy same-provider fork resolution:
262
263- `thread.fork` creates a target app thread with fork lineage and a pending `ContextTransfer`.
264- `thread.fork` does not create a provider session, provider thread, provider turn, or portable context artifact.
265- The first dispatch on the fork resolves the pending transfer. For Codex-to-Codex forks with strong native refs and `thread/fork` support, the orchestrator uses the provider-native fork path and records `resolved_native` then `consumed`.
266- Cross-provider or otherwise non-native fork transfers materialize a portable `ContextHandoff`
267 lazily on the target thread's first dispatch.
268- Active source runs are rejected for explicit run/checkpoint forks; `latest_stable` resolves to the latest completed checkpointed run.
269
270This keeps the runtime aligned with the architecture while making portable context explicit and
271auditable through `ContextTransfer` and `ContextHandoff` records.
272
273## Relationship To Provider Switching
274
275Provider switching is a context transfer where `sourceThreadId === targetThreadId` and the target provider differs from the current active provider.
276
277Returning to a prior provider is also a context transfer, usually a delta from the provider's last covered run range to the current app-thread point.
278
279The [Provider Switching And Context Handoff](./provider-switching-and-context.md) document describes strategy selection for that specific feature. This document defines the broader model shared by provider switching, forks, merge-back, and subagents.