Skip to content
Source
Sign inSign up

RestartBackgroundNote.test.ts

256 lines9.0 KB

1import { assert, it } from "@effect/vitest";
2import {
3 MessageId,
4 ProviderThreadId,
5 RunAttemptId,
6 RunId,
7 type OrchestrationV2Run,
8} from "@t3tools/contracts";
9import * as DateTime from "effect/DateTime";
10​
11import {
12 cancelledRosterTaskWork,
13 cancelledTurnItemWork,
14 isAwaitedWork,
15 isRestartNoteContinuation,
16 pendingRestartCancelledBackgroundWork,
17 restartContinuationNote,
18 mergeRestartCancelledBackgroundWork,
19} from "./RestartBackgroundNote.ts";
20​
21const claudeThread = ProviderThreadId.make("provider-thread:claude");
22const codexThread = ProviderThreadId.make("provider-thread:codex");
23const lost = [{ kind: "subagent" as const, label: "Background subagent test" }];
24​
25function run(
26 ordinal: number,
27 providerThreadId: ProviderThreadId,
28 extra: Partial<OrchestrationV2Run> = {},
29): OrchestrationV2Run {
30 return {
31 id: RunId.make(`run:${ordinal}`),
32 ordinal,
33 providerThreadId,
34 userMessageId: MessageId.make(`message:${ordinal}`),
35 activeAttemptId: RunAttemptId.make(`attempt:${ordinal}`),
36 status: "completed",
37 ...extra,
38 } as OrchestrationV2Run;
40​
41const turnFor = (source: OrchestrationV2Run) => ({
42 runAttemptId: source.activeAttemptId,
43 providerThreadId: source.providerThreadId!,
44 status: "completed" as const,
45});
46​
47it("keeps the note for the provider thread that lost the work across a provider switch", () => {
48 const root = run(1, claudeThread, { restartCancelledBackgroundWork: lost });
49 const onCodex = run(2, codexThread);
50 const backOnClaude = run(3, claudeThread);
51 const pending = (target: OrchestrationV2Run, runs: ReadonlyArray<OrchestrationV2Run>) =>
52 pendingRestartCancelledBackgroundWork({
53 runs,
54 providerTurns: runs.filter((source) => source.id !== target.id).map(turnFor),
55 compactionMessageIds: new Set(),
56 run: target,
57 attempts: [{ id: target.activeAttemptId!, runId: target.id }],
58 });
59​
60 assert.deepEqual(pending(onCodex, [root, onCodex]), []);
61 assert.deepEqual(pending(backOnClaude, [root, onCodex, backOnClaude]), lost);
62 const later = run(4, claudeThread);
63 assert.deepEqual(pending(later, [root, onCodex, backOnClaude, later]), []);
64});
65​
66it("delivers a resumed queued run's note even after a higher-ordinal run", () => {
67 const ranFirst = run(3, claudeThread, {
68 completedAt: DateTime.makeUnsafe("2026-10-03T10:00:00.000Z"),
69 });
70 const resumed = run(2, claudeThread, {
71 completedAt: DateTime.makeUnsafe("2026-10-03T10:05:00.000Z"),
72 restartCancelledBackgroundWork: lost,
73 });
74 const next = run(4, claudeThread);
75 const runs = [resumed, ranFirst, next];
76 assert.deepEqual(
77 pendingRestartCancelledBackgroundWork({
78 runs,
79 providerTurns: [resumed, ranFirst].map(turnFor),
80 compactionMessageIds: new Set(),
81 run: next,
82 attempts: [{ id: next.activeAttemptId!, runId: next.id }],
83 }),
84 lost,
85 );
86});
87​
88// regression: #19750 wake threads whose subagents' background work a restart cut
89it("names a lost subagent by the id the agent resumes it with", () => {
90 const ownShell = cancelledRosterTaskWork({
91 kind: "command",
92 taskId: "shell-1",
93 description: "sleep 150",
94 parentTaskId: "agent-1",
95 });
96 assert.deepEqual(ownShell, {
97 kind: "subagent",
98 label: "sleep 150 (subagent agent-1)",
99 id: "agent-1",
100 });
101 assert.isTrue(isAwaitedWork(ownShell));
102 const long = cancelledRosterTaskWork({
103 kind: "command",
104 taskId: "shell-1",
105 description: "d".repeat(400),
106 parentTaskId: "agent-1",
107 }).label;
108 assert.lengthOf(long, 160);
109 assert.isTrue(long.endsWith("(subagent agent-1)"));
110 const longId = cancelledRosterTaskWork({
111 kind: "command",
112 taskId: "shell-1",
113 parentTaskId: "a".repeat(400),
114 }).label;
115 assert.lengthOf(longId, 160);
116 assert.isTrue(
117 cancelledRosterTaskWork({
118 kind: "command",
119 taskId: "shell-1",
120 description: `${"d".repeat(158)}πŸš€ deploy`,
121 }).label.isWellFormed(),
122 );
123 assert.isFalse(isAwaitedWork(cancelledRosterTaskWork({ kind: "command", taskId: "shell-2" })));
124 assert.isTrue(
125 isAwaitedWork(cancelledRosterTaskWork({ kind: "command", taskId: "tests", awaited: true })),
126 );
127 assert.isFalse(isAwaitedWork(cancelledRosterTaskWork({ kind: "monitor", taskId: "watchdog" })));
128 assert.isTrue(
129 isAwaitedWork(cancelledRosterTaskWork({ kind: "monitor", taskId: "ci", awaited: true })),
130 );
131 const codexCommand = { id: "item-2", type: "command_execution", input: "sleep 180" };
132 assert.isFalse(isAwaitedWork(cancelledTurnItemWork(codexCommand as never)!));
133 assert.isTrue(
134 isAwaitedWork(cancelledTurnItemWork({ ...codexCommand, completionWake: "always" } as never)!),
135 );
136 assert.equal(
137 cancelledTurnItemWork({
138 id: "item-1",
139 type: "subagent",
140 origin: "provider_native",
141 title: "Review",
142 nativeItemRef: { driver: "claudeAgent", nativeId: "agent-1", strength: "strong" },
143 } as never)?.label,
144 "Review (id agent-1)",
145 );
146});
147​
148it("keeps separate cancelled tasks that share a kind and label", () => {
149 const first = { kind: "shell" as const, label: "sleep 20", id: "item-1" };
150 const second = { kind: "shell" as const, label: "sleep 20", id: "item-2" };
151 const merged = mergeRestartCancelledBackgroundWork([first], [second, first]);
152 assert.deepEqual(merged, [first, second]);
153 const legacy = { kind: "shell" as const, label: "sleep 20" };
154 assert.lengthOf(mergeRestartCancelledBackgroundWork([legacy], [legacy]), 1);
155});
156​
157it("does not repeat the note when a steer restarts the run on a new attempt", () => {
158 const root = run(1, claudeThread, { restartCancelledBackgroundWork: lost });
159 const steered = run(2, claudeThread, {
160 activeAttemptId: RunAttemptId.make("attempt:2b"),
161 });
162 const firstAttempt = RunAttemptId.make("attempt:2a");
163 const pending = (runAttemptIds: ReadonlyArray<RunAttemptId>, delivered: boolean) =>
164 pendingRestartCancelledBackgroundWork({
165 runs: [root, steered],
166 providerTurns: [
167 turnFor(root),
168 ...(delivered
169 ? [
170 {
171 runAttemptId: firstAttempt,
172 providerThreadId: claudeThread,
173 status: "completed" as const,
174 },
175 ]
176 : []),
177 ],
178 compactionMessageIds: new Set(),
179 run: steered,
180 attempts: runAttemptIds.map((id) => ({ id, runId: steered.id })),
181 });
182​
183 assert.deepEqual(pending([firstAttempt, RunAttemptId.make("attempt:2b")], true), []);
184 assert.deepEqual(pending([firstAttempt, RunAttemptId.make("attempt:2b")], false), lost);
185});
186​
187it("counts a prompted mid-turn or chained continuation as delivering the note", () => {
188 const cut = run(1, claudeThread, { status: "cancelled", restartCancelledBackgroundWork: lost });
189 const cutTurn = { ...turnFor(cut), status: "cancelled" as const };
190 const continuation = run(2, claudeThread, { restartContinuationOfRunId: cut.id });
191 assert.isTrue(isRestartNoteContinuation(continuation, [cut, continuation], [cutTurn], []));
192 assert.isFalse(
193 isRestartNoteContinuation(
194 continuation,
195 [{ ...cut, restartCancelledBackgroundWork: [] }, continuation],
196 [cutTurn],
197 [],
198 ),
199 );
200 const later = run(3, claudeThread);
201 const pending = (delivered: boolean) =>
202 pendingRestartCancelledBackgroundWork({
203 runs: [cut, continuation, later],
204 providerTurns: [cutTurn, ...(delivered ? [turnFor(continuation)] : [])],
205 compactionMessageIds: new Set(),
206 run: later,
207 attempts: [{ id: later.activeAttemptId!, runId: later.id }],
208 });
209 assert.deepEqual(pending(true), []);
210 assert.deepEqual(pending(false), lost);
211​
212 const unstarted = { ...continuation, status: "cancelled" as const };
213 const chained = run(3, claudeThread, { restartContinuationOfRunId: unstarted.id });
214 assert.deepEqual(restartContinuationNote(unstarted, [cut, unstarted], [cutTurn], []), {
215 work: lost,
216 settled: false,
217 });
218 assert.isTrue(isRestartNoteContinuation(chained, [cut, unstarted, chained], [cutTurn], []));
219 for (const status of ["running", "cancelled"] as const) {
220 const turns = [cutTurn, { ...turnFor(unstarted), status }];
221 assert.deepEqual(restartContinuationNote(unstarted, [cut, unstarted], turns, []), {
222 work: lost,
223 settled: false,
224 });
225 assert.deepEqual(
226 pendingRestartCancelledBackgroundWork({
227 runs: [cut, unstarted, later],
228 providerTurns: turns,
229 compactionMessageIds: new Set(),
230 run: later,
231 attempts: [{ id: later.activeAttemptId!, runId: later.id }],
232 }),
233 lost,
234 );
235 }
236​
237 const steered = { ...unstarted, activeAttemptId: RunAttemptId.make("attempt:replacement") };
238 const attempts = [{ id: unstarted.activeAttemptId!, runId: steered.id }];
239 const turns = [
240 cutTurn,
241 turnFor(unstarted),
242 { ...turnFor(steered), status: "cancelled" as const },
243 ];
244 assert.deepEqual(restartContinuationNote(steered, [cut, steered], turns, attempts).work, []);
245 assert.isFalse(isRestartNoteContinuation(chained, [cut, steered, chained], turns, attempts));
246 assert.deepEqual(
247 pendingRestartCancelledBackgroundWork({
248 runs: [cut, steered, later],
249 providerTurns: turns,
250 compactionMessageIds: new Set(),
251 run: later,
252 attempts,
253 }),
254 [],
255 );
256});