diff --git a/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts b/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts index 6544fafdacbff..f20399e4af80b 100644 --- a/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts +++ b/apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts @@ -637,6 +637,23 @@ it.effect( yield* sql`INSERT INTO orchestration_v2_projection_context_handoffs (context_handoff_id, thread_id, target_run_id, to_provider_thread_id, strategy, status, updated_at, payload_json) VALUES ('obsolete-handoff', ${threadId}, 'old-run', 'old-provider-thread', 'full_thread_summary', 'ready', ${DateTime.formatIso(now)}, '{"obsolete":true}')`; + for (const status of [ + "resolved_native", + "resolved_portable", + "failed", + "consumed", + "superseded", + ]) { + yield* sql`INSERT INTO orchestration_v2_projection_context_transfers + (context_transfer_id, source_thread_id, target_thread_id, target_run_id, + type, status, updated_at, payload_json) + VALUES (${`obsolete-transfer:${status}`}, 'old-source', ${threadId}, NULL, + 'merge_back', ${status}, ${DateTime.formatIso(now)}, '{"obsolete":true}')`; + } + assert.equal( + (yield* Effect.exit(projections.getThreadRecords(threadId, ["contextTransfers"])))._tag, + "Failure", + ); yield* orchestrator.dispatch({ type: "message.dispatch", commandId: CommandId.make("dispatch-with-old-history"), @@ -652,6 +669,9 @@ it.effect( turnItemTypes: ["user_message"], }); assert.isAbove(fresh.turnItems.at(-1)!.ordinal, 900); + // Deletion reads transfer history separately to find side chats. + yield* sql`DELETE FROM orchestration_v2_projection_context_transfers + WHERE target_thread_id = ${threadId}`; yield* orchestrator.dispatch({ type: "thread.archive", commandId: CommandId.make("archive-with-old-history"), diff --git a/apps/server/src/orchestration-v2/Orchestrator.ts b/apps/server/src/orchestration-v2/Orchestrator.ts index bcec0a13cf20a..8931e2866856a 100644 --- a/apps/server/src/orchestration-v2/Orchestrator.ts +++ b/apps/server/src/orchestration-v2/Orchestrator.ts @@ -1297,6 +1297,8 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio // Commands here read only undelivered or active tasks; a dispatcher thread keeps // thousands of delivered ones, each carrying its full prompt. omitSettledSubagents: true, + // Command decisions only consume pending forks and merge-backs. + contextTransferStatuses: ["pending"], }, ) .pipe(