# Queued native child continuation output is dropped after root completion · Issue \#19228 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/issues/19228)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: backlog

Priority: 0

## Description

[Discussion and activity](https://git.cafe/pingdotgg/t3code/issues/19228)

---

Native child continuation events can be dropped after a root turn finishes, even on current `main` with synchronous SQLite. This is the separate defect discussed by the maintainer in [#19211](https://github.com/pingdotgg/t3code/pull/19211#issuecomment-6159841685).

Confirmed on `main` commit `11fd54fcbbfb39f824bb4c908d428c20d801267c`, Node 24.21.0. Only the test file changes; no SQLite worker or new provider-event fields are involved. Server typecheck passes.

A focused `RunExecutionService` test queues these normalized events while the adapter's live pending-work probe already returns false:

1. A child is running when the root terminal arrives.
2. The child's subagent completion arrives, emptying the ingestion tracking sets.
3. That child's native continuation starts, sends `GRANDCHILD_OK`, and completes.

`shouldStopProviderEventIngestion` consults the live probe at step 2 and closes before consuming step 3. Both retained-root and lost-root ownership cases discard all continuation events. Expected observations are `running`, `GRANDCHILD_OK`, `completed`; actual observations are `[]`. Both tests fail by assertion immediately, without sleeps, polling, timeout changes, or database delays.

Apply the focused patch below to the pinned `main` revision, then run:

```sh
vp test run apps/server/src/orchestration-v2/RunExecutionService.test.ts --testNamePattern='retains queued child output'
```

This is a service-level reproduction with the ingestion persistence dependency mocked, distinct from a physical-client reproduction. The full-worker investigation also reproduced the missing response in the real OpenCode HTTP replay with SQLite persistence. It does not establish the historical phone incident's exact cause.

<details>
<summary>Focused regression patch for current main</summary>

```diff
diff --git a/apps/server/src/orchestration-v2/RunExecutionService.test.ts b/apps/server/src/orchestration-v2/RunExecutionService.test.ts
index d877922a6..9a0e40d63 100644
--- a/apps/server/src/orchestration-v2/RunExecutionService.test.ts
+++ b/apps/server/src/orchestration-v2/RunExecutionService.test.ts
@@ -12,9 +12,11 @@ import {
   type OrchestrationV2DomainEvent,
   type OrchestrationV2ExecutionNode,
   type OrchestrationV2ProviderThread,
+  type OrchestrationV2ProviderTurn,
   type OrchestrationV2Run,
   type OrchestrationV2RunAttempt,
   type OrchestrationV2Subagent,
+  type OrchestrationV2StoredEvent,
   type OrchestrationV2TurnItem,
   ProviderDriverKind,
   ProviderInstanceId,
@@ -1687,6 +1689,92 @@ it.effect("ends a nested native subagent and its thread when the root run is int
   }),
 );
 
+it.effect.each([false, true])(
+  "retains queued child output when the adapter is already idle and root ownership lost=%s",
+  (rootOwnershipLost) =>
+    Effect.gen(function* () {
+      const childEvents = yield* Ref.make<ReadonlyArray<string>>([]);
+      yield* runBackgroundItemScenario(
+        "ordered-background-continuation",
+        (ids) => {
+          const childProviderThreadId = ProviderThreadId.make(
+            "provider-thread:ordered-background-continuation:child",
+          );
+          const snapshot = (populated: boolean, child = false) => ({
+            type: "provider_thread.updated" as const,
+            driver,
+            providerThread: {
+              id: child ? childProviderThreadId : ids.providerThreadId,
+              driver,
+              providerInstanceId: ProviderInstanceId.make("codex"),
+              providerSessionId: null,
+              appThreadId: child ? ids.childThreadId : ids.threadId,
+              ownerNodeId: null,
+              nativeThreadRef: null,
+              nativeConversationHeadRef: null,
+              status: "idle" as const,
+              firstRunOrdinal: 1,
+              lastRunOrdinal: 1,
+              handoffIds: [],
+              forkedFrom: null,
+              pendingBackgroundTasks: populated
+                ? [{ taskId: "child", kind: "subagent" as const, childThreadId: ids.childThreadId }]
+                : [],
+              createdAt: DateTime.makeUnsafe(0),
+              updatedAt: DateTime.makeUnsafe(0),
+            } satisfies OrchestrationV2ProviderThread,
+          });
+          const childTurn = (status: "running" | "completed") => ({
+            type: "provider_turn.updated" as const,
+            driver,
+            threadId: ids.childThreadId,
+            providerTurn: {
+              id: ProviderTurnId.make("provider-turn:ordered-background-continuation:child"),
+              providerThreadId: ProviderThreadId.make(
+                "provider-thread:ordered-background-continuation:child",
+              ),
+              runAttemptId: null,
+              ordinal: 2,
+              status,
+            } as OrchestrationV2ProviderTurn,
+          });
+          return [
+            childThreadCreatedEvent(ids),
+            snapshot(true),
+            snapshot(true, true),
+            subagentEvent(ids, "running"),
+            rootTerminalEvent(ids, "completed"),
+            subagentEvent(ids, "completed"),
+            snapshot(false),
+            childTurn("running"),
+            {
+              type: "message.updated",
+              driver,
+              message: { threadId: ids.childThreadId, runId: null, text: "GRANDCHILD_OK" },
+            } as ProviderAdapter.ProviderAdapterV2Event,
+            childTurn("completed"),
+            snapshot(false, true),
+            snapshot(false),
+          ];
+        },
+        {
+          keepEventStreamOpen: true,
+          hasPendingBackgroundWorkForThread: () => Effect.succeed(false),
+          rejectRootProviderThreadUpdates: rootOwnershipLost,
+          onIngest: (event) =>
+            Ref.update(childEvents, (events) =>
+              event.type === "provider_turn.updated"
+                ? [...events, event.providerTurn.status]
+                : event.type === "message.updated"
+                  ? [...events, event.message.text]
+                  : events,
+            ),
+        },
+      );
+      assert.deepEqual(yield* Ref.get(childEvents), ["running", "GRANDCHILD_OK", "completed"]);
+    }),
+);
+
 it.effect("keeps ingesting a late background command item completion after root terminal", () =>
   Effect.gen(function* () {
     const observed = yield* runBackgroundItemScenario("bg-command", (ids) => [
@@ -4331,6 +4419,11 @@ function runBackgroundItemScenario(
       ReadonlyArray<{ readonly id: TurnItemId; readonly runId: RunId }>
     >;
     readonly onSubscribe?: Effect.Effect<void>;
+    readonly onIngest?: (event: ProviderAdapter.ProviderAdapterV2Event) => Effect.Effect<void>;
+    readonly hasPendingBackgroundWorkForThread?: (
+      thread: OrchestrationV2ProviderThread,
+    ) => Effect.Effect<boolean>;
+    readonly rejectRootProviderThreadUpdates?: boolean;
   },
 ) {
   return Effect.gen(function* () {
@@ -4363,6 +4456,7 @@ function runBackgroundItemScenario(
             ingestNormalized: (input) =>
               Effect.gen(function* () {
                 const event = input.event;
+                yield* options?.onIngest?.(event) ?? Effect.void;
                 if (event.type === "turn_item.updated") {
                   yield* Ref.update(observed, (current) => [
                     ...current,
@@ -4375,6 +4469,18 @@ function runBackgroundItemScenario(
                     `subagent:${event.subagent.status}`,
                   ]);
                 }
+                if (event.type === "provider_thread.updated") {
+                  if (
+                    options?.rejectRootProviderThreadUpdates === true &&
+                    input.writeIfProviderThreadOwner !== undefined
+                  )
+                    return [];
+                  return [
+                    {
+                      event: { type: "provider-thread.updated", payload: event.providerThread },
+                    } as OrchestrationV2StoredEvent,
+                  ];
+                }
                 return [];
               }),
           }),
@@ -4391,6 +4497,11 @@ function runBackgroundItemScenario(
         providerSessionId: ProviderSessionId.make(`session:${key}`),
         session: {
           events: Stream.empty,
+          ...(options?.hasPendingBackgroundWorkForThread === undefined
+            ? {}
+            : {
+                hasPendingBackgroundWorkForThread: options.hasPendingBackgroundWorkForThread,
+              }),
           subscribeEvents: Effect.gen(function* () {
             yield* options?.onSubscribe ?? Effect.void;
             const events = Stream.fromIterable(makeEvents(ids));
```
</details>

Written by GPT-6.1 Sol in Codex.

