@united-workforce/cli 0.6.1 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +120 -5
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__tests__/agent-resolution-llm-free.test.js +9 -2
  4. package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -1
  5. package/dist/__tests__/broker-prompt.test.d.ts +10 -0
  6. package/dist/__tests__/broker-prompt.test.d.ts.map +1 -0
  7. package/dist/__tests__/broker-prompt.test.js +129 -0
  8. package/dist/__tests__/broker-prompt.test.js.map +1 -0
  9. package/dist/__tests__/broker-step-active-turns.test.d.ts +20 -0
  10. package/dist/__tests__/broker-step-active-turns.test.d.ts.map +1 -0
  11. package/dist/__tests__/broker-step-active-turns.test.js +428 -0
  12. package/dist/__tests__/broker-step-active-turns.test.js.map +1 -0
  13. package/dist/__tests__/broker-step-turn-chain-phase2.test.d.ts +13 -0
  14. package/dist/__tests__/broker-step-turn-chain-phase2.test.d.ts.map +1 -0
  15. package/dist/__tests__/broker-step-turn-chain-phase2.test.js +429 -0
  16. package/dist/__tests__/broker-step-turn-chain-phase2.test.js.map +1 -0
  17. package/dist/__tests__/config.test.js +33 -37
  18. package/dist/__tests__/config.test.js.map +1 -1
  19. package/dist/__tests__/e2e-broker-step-suspend.test.d.ts +18 -0
  20. package/dist/__tests__/e2e-broker-step-suspend.test.d.ts.map +1 -0
  21. package/dist/__tests__/e2e-broker-step-suspend.test.js +313 -0
  22. package/dist/__tests__/e2e-broker-step-suspend.test.js.map +1 -0
  23. package/dist/__tests__/e2e-broker-step.test.d.ts +13 -0
  24. package/dist/__tests__/e2e-broker-step.test.d.ts.map +1 -0
  25. package/dist/__tests__/e2e-broker-step.test.js +278 -0
  26. package/dist/__tests__/e2e-broker-step.test.js.map +1 -0
  27. package/dist/__tests__/e2e-mock-agent.test.js +1 -1
  28. package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
  29. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.d.ts +28 -0
  30. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.d.ts.map +1 -0
  31. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.js +322 -0
  32. package/dist/__tests__/e2e-thread-resume-timeout-suspend.test.js.map +1 -0
  33. package/dist/__tests__/log-tag-validity.test.d.ts +2 -0
  34. package/dist/__tests__/log-tag-validity.test.d.ts.map +1 -0
  35. package/dist/__tests__/log-tag-validity.test.js +110 -0
  36. package/dist/__tests__/log-tag-validity.test.js.map +1 -0
  37. package/dist/__tests__/setup-agent-discovery.test.js +35 -23
  38. package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
  39. package/dist/__tests__/setup-no-llm.test.js +5 -2
  40. package/dist/__tests__/setup-no-llm.test.js.map +1 -1
  41. package/dist/__tests__/step-ask.test.js +9 -6
  42. package/dist/__tests__/step-ask.test.js.map +1 -1
  43. package/dist/__tests__/step-show-json.test.js +5 -5
  44. package/dist/__tests__/step-show-json.test.js.map +1 -1
  45. package/dist/__tests__/step-show-text.test.d.ts +2 -0
  46. package/dist/__tests__/step-show-text.test.d.ts.map +1 -0
  47. package/dist/__tests__/step-show-text.test.js +192 -0
  48. package/dist/__tests__/step-show-text.test.js.map +1 -0
  49. package/dist/__tests__/step-turns-cli-subprocess.test.d.ts +21 -0
  50. package/dist/__tests__/step-turns-cli-subprocess.test.d.ts.map +1 -0
  51. package/dist/__tests__/step-turns-cli-subprocess.test.js +356 -0
  52. package/dist/__tests__/step-turns-cli-subprocess.test.js.map +1 -0
  53. package/dist/__tests__/step-turns-panorama-phase3.test.d.ts +21 -0
  54. package/dist/__tests__/step-turns-panorama-phase3.test.d.ts.map +1 -0
  55. package/dist/__tests__/step-turns-panorama-phase3.test.js +476 -0
  56. package/dist/__tests__/step-turns-panorama-phase3.test.js.map +1 -0
  57. package/dist/__tests__/step-turns.test.d.ts +24 -0
  58. package/dist/__tests__/step-turns.test.d.ts.map +1 -0
  59. package/dist/__tests__/step-turns.test.js +646 -0
  60. package/dist/__tests__/step-turns.test.js.map +1 -0
  61. package/dist/__tests__/store-turn-chain.test.d.ts +2 -0
  62. package/dist/__tests__/store-turn-chain.test.d.ts.map +1 -0
  63. package/dist/__tests__/store-turn-chain.test.js +341 -0
  64. package/dist/__tests__/store-turn-chain.test.js.map +1 -0
  65. package/dist/__tests__/thread-agent-failure-suspended.test.js +3 -3
  66. package/dist/__tests__/thread-agent-failure-suspended.test.js.map +1 -1
  67. package/dist/__tests__/thread-list-limit-offset.test.d.ts +24 -0
  68. package/dist/__tests__/thread-list-limit-offset.test.d.ts.map +1 -0
  69. package/dist/__tests__/thread-list-limit-offset.test.js +254 -0
  70. package/dist/__tests__/thread-list-limit-offset.test.js.map +1 -0
  71. package/dist/__tests__/thread-list-template-ms-date.test.js +7 -2
  72. package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -1
  73. package/dist/__tests__/thread-poke.test.js +6 -6
  74. package/dist/__tests__/thread-poke.test.js.map +1 -1
  75. package/dist/__tests__/thread-resume.test.js +2 -2
  76. package/dist/__tests__/thread-resume.test.js.map +1 -1
  77. package/dist/__tests__/thread-suspend-step.test.js +1 -1
  78. package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
  79. package/dist/__tests__/thread.test.js +28 -14
  80. package/dist/__tests__/thread.test.js.map +1 -1
  81. package/dist/cli.js +910 -344
  82. package/dist/cli.js.map +1 -1
  83. package/dist/commands/broker-step.d.ts +117 -0
  84. package/dist/commands/broker-step.d.ts.map +1 -0
  85. package/dist/commands/broker-step.js +654 -0
  86. package/dist/commands/broker-step.js.map +1 -0
  87. package/dist/commands/config.d.ts.map +1 -1
  88. package/dist/commands/config.js +2 -23
  89. package/dist/commands/config.js.map +1 -1
  90. package/dist/commands/prompt.d.ts.map +1 -1
  91. package/dist/commands/prompt.js +43 -51
  92. package/dist/commands/prompt.js.map +1 -1
  93. package/dist/commands/setup.d.ts +6 -4
  94. package/dist/commands/setup.d.ts.map +1 -1
  95. package/dist/commands/setup.js +24 -27
  96. package/dist/commands/setup.js.map +1 -1
  97. package/dist/commands/step.d.ts +54 -6
  98. package/dist/commands/step.d.ts.map +1 -1
  99. package/dist/commands/step.js +484 -134
  100. package/dist/commands/step.js.map +1 -1
  101. package/dist/commands/thread.d.ts +4 -0
  102. package/dist/commands/thread.d.ts.map +1 -1
  103. package/dist/commands/thread.js +77 -151
  104. package/dist/commands/thread.js.map +1 -1
  105. package/dist/output-mappers.d.ts +8 -0
  106. package/dist/output-mappers.d.ts.map +1 -1
  107. package/dist/output-mappers.js +72 -18
  108. package/dist/output-mappers.js.map +1 -1
  109. package/dist/schemas.d.ts +3 -0
  110. package/dist/schemas.d.ts.map +1 -1
  111. package/dist/schemas.js +17 -3
  112. package/dist/schemas.js.map +1 -1
  113. package/dist/store.d.ts +147 -1
  114. package/dist/store.d.ts.map +1 -1
  115. package/dist/store.js +254 -1
  116. package/dist/store.js.map +1 -1
  117. package/dist/text-renderers.d.ts.map +1 -1
  118. package/dist/text-renderers.js +27 -2
  119. package/dist/text-renderers.js.map +1 -1
  120. package/package.json +7 -5
  121. package/src/__tests__/agent-resolution-llm-free.test.ts +14 -2
  122. package/src/__tests__/broker-prompt.test.ts +142 -0
  123. package/src/__tests__/broker-step-active-turns.test.ts +509 -0
  124. package/src/__tests__/broker-step-turn-chain-phase2.test.ts +525 -0
  125. package/src/__tests__/config.test.ts +35 -39
  126. package/src/__tests__/e2e-broker-step-suspend.test.ts +351 -0
  127. package/src/__tests__/e2e-broker-step.test.ts +320 -0
  128. package/src/__tests__/e2e-mock-agent.test.ts +1 -1
  129. package/src/__tests__/e2e-thread-resume-timeout-suspend.test.ts +360 -0
  130. package/src/__tests__/log-tag-validity.test.ts +124 -0
  131. package/src/__tests__/setup-agent-discovery.test.ts +35 -23
  132. package/src/__tests__/setup-no-llm.test.ts +5 -2
  133. package/src/__tests__/step-ask.test.ts +9 -6
  134. package/src/__tests__/step-show-json.test.ts +5 -5
  135. package/src/__tests__/step-show-text.test.ts +236 -0
  136. package/src/__tests__/step-turns-cli-subprocess.test.ts +411 -0
  137. package/src/__tests__/step-turns-panorama-phase3.test.ts +579 -0
  138. package/src/__tests__/step-turns.test.ts +734 -0
  139. package/src/__tests__/store-turn-chain.test.ts +386 -0
  140. package/src/__tests__/thread-agent-failure-suspended.test.ts +3 -3
  141. package/src/__tests__/thread-list-limit-offset.test.ts +305 -0
  142. package/src/__tests__/thread-list-template-ms-date.test.ts +7 -2
  143. package/src/__tests__/thread-poke.test.ts +6 -6
  144. package/src/__tests__/thread-resume.test.ts +2 -2
  145. package/src/__tests__/thread-suspend-step.test.ts +1 -1
  146. package/src/__tests__/thread.test.ts +29 -15
  147. package/src/cli.ts +1056 -483
  148. package/src/commands/broker-step.ts +913 -0
  149. package/src/commands/config.ts +2 -24
  150. package/src/commands/prompt.ts +43 -51
  151. package/src/commands/setup.ts +25 -29
  152. package/src/commands/step.ts +645 -176
  153. package/src/commands/thread.ts +87 -192
  154. package/src/output-mappers.ts +99 -21
  155. package/src/schemas.ts +32 -2
  156. package/src/store.ts +297 -2
  157. package/src/text-renderers.ts +35 -2
  158. package/dist/__tests__/adapter-json-roundtrip.test.d.ts +0 -2
  159. package/dist/__tests__/adapter-json-roundtrip.test.d.ts.map +0 -1
  160. package/dist/__tests__/adapter-json-roundtrip.test.js +0 -160
  161. package/dist/__tests__/adapter-json-roundtrip.test.js.map +0 -1
  162. package/dist/__tests__/spawn-agent-json.test.d.ts +0 -2
  163. package/dist/__tests__/spawn-agent-json.test.d.ts.map +0 -1
  164. package/dist/__tests__/spawn-agent-json.test.js +0 -79
  165. package/dist/__tests__/spawn-agent-json.test.js.map +0 -1
  166. package/src/__tests__/adapter-json-roundtrip.test.ts +0 -193
  167. package/src/__tests__/spawn-agent-json.test.ts +0 -100
@@ -0,0 +1,386 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import type { CasRef, StepStartPayload, TurnNodePayload } from "@united-workforce/protocol";
5
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
6
+ import { turnsOfStep, walkTurnChain, writeStepStart, writeTurnNode } from "../store.js";
7
+ import { makeUwfStore } from "./thread-test-helpers.js";
8
+
9
+ let tmpDir: string;
10
+ let savedOcasHome: string | undefined;
11
+
12
+ beforeEach(async () => {
13
+ savedOcasHome = process.env.OCAS_HOME;
14
+ tmpDir = await mkdtemp(join(tmpdir(), "uwf-turn-chain-test-"));
15
+ });
16
+
17
+ afterEach(async () => {
18
+ if (savedOcasHome === undefined) {
19
+ delete process.env.OCAS_HOME;
20
+ } else {
21
+ process.env.OCAS_HOME = savedOcasHome;
22
+ }
23
+ await rm(tmpDir, { recursive: true, force: true });
24
+ });
25
+
26
+ describe("writeStepStart", () => {
27
+ test("creates step-start nodes linked via prev", async () => {
28
+ const uwf = await makeUwfStore(tmpDir);
29
+ const startRef = (await uwf.store.cas.put(uwf.schemas.text, "thread-start")) as CasRef;
30
+
31
+ // Step 0: first step (prev = null)
32
+ const step0Payload: StepStartPayload = {
33
+ role: "planner",
34
+ edgePrompt: "Analyze the issue",
35
+ stepIndex: 0,
36
+ prev: null,
37
+ start: startRef,
38
+ startedAtMs: 1000,
39
+ cwd: "/repo",
40
+ };
41
+ const ss0 = writeStepStart(uwf, step0Payload);
42
+
43
+ // Step 1: linked to step 0
44
+ const step1Payload: StepStartPayload = {
45
+ role: "developer",
46
+ edgePrompt: "Implement the fix",
47
+ stepIndex: 1,
48
+ prev: ss0,
49
+ start: startRef,
50
+ startedAtMs: 2000,
51
+ cwd: "/repo",
52
+ };
53
+ const ss1 = writeStepStart(uwf, step1Payload);
54
+
55
+ // Step 2: linked to step 1
56
+ const step2Payload: StepStartPayload = {
57
+ role: "reviewer",
58
+ edgePrompt: "Review the changes",
59
+ stepIndex: 2,
60
+ prev: ss1,
61
+ start: startRef,
62
+ startedAtMs: 3000,
63
+ cwd: "/repo",
64
+ };
65
+ const ss2 = writeStepStart(uwf, step2Payload);
66
+
67
+ // Verify hashes are distinct
68
+ expect(ss0).not.toBe(ss1);
69
+ expect(ss1).not.toBe(ss2);
70
+ expect(ss0).not.toBe(ss2);
71
+
72
+ // Verify each is 13-char Crockford Base32
73
+ expect(ss0.length).toBe(13);
74
+ expect(ss1.length).toBe(13);
75
+ expect(ss2.length).toBe(13);
76
+
77
+ // Verify nodes can be retrieved and contain exact payloads
78
+ const node0 = uwf.store.cas.get(ss0);
79
+ const node1 = uwf.store.cas.get(ss1);
80
+ const node2 = uwf.store.cas.get(ss2);
81
+
82
+ expect(node0).not.toBeNull();
83
+ expect(node1).not.toBeNull();
84
+ expect(node2).not.toBeNull();
85
+
86
+ const payload0 = node0?.payload as StepStartPayload;
87
+ const payload1 = node1?.payload as StepStartPayload;
88
+ const payload2 = node2?.payload as StepStartPayload;
89
+
90
+ expect(payload0.role).toBe("planner");
91
+ expect(payload0.stepIndex).toBe(0);
92
+ expect(payload0.prev).toBeNull();
93
+
94
+ expect(payload1.role).toBe("developer");
95
+ expect(payload1.stepIndex).toBe(1);
96
+ expect(payload1.prev).toBe(ss0);
97
+
98
+ expect(payload2.role).toBe("reviewer");
99
+ expect(payload2.stepIndex).toBe(2);
100
+ expect(payload2.prev).toBe(ss1);
101
+
102
+ // Verify walking the chain from SS2 via prev yields [SS2, SS1, SS0]
103
+ const chain: CasRef[] = [];
104
+ let currentHash: CasRef | null = ss2;
105
+ while (currentHash !== null) {
106
+ chain.push(currentHash);
107
+ const node = uwf.store.cas.get(currentHash);
108
+ if (node === null) break;
109
+ const payload = node.payload as StepStartPayload;
110
+ currentHash = payload.prev;
111
+ }
112
+ expect(chain).toEqual([ss2, ss1, ss0]);
113
+
114
+ // Verify stepIndex values in chain order
115
+ expect((uwf.store.cas.get(chain[0])?.payload as StepStartPayload).stepIndex).toBe(2);
116
+ expect((uwf.store.cas.get(chain[1])?.payload as StepStartPayload).stepIndex).toBe(1);
117
+ expect((uwf.store.cas.get(chain[2])?.payload as StepStartPayload).stepIndex).toBe(0);
118
+ });
119
+ });
120
+
121
+ describe("walkTurnChain", () => {
122
+ test("traverses turns via prev pointers in chronological order", async () => {
123
+ const uwf = await makeUwfStore(tmpDir);
124
+ const startRef = (await uwf.store.cas.put(uwf.schemas.text, "thread-start")) as CasRef;
125
+
126
+ // Create step-start nodes
127
+ const ss0 = writeStepStart(uwf, {
128
+ role: "planner",
129
+ edgePrompt: "Plan",
130
+ stepIndex: 0,
131
+ prev: null,
132
+ start: startRef,
133
+ startedAtMs: 1000,
134
+ cwd: "/repo",
135
+ });
136
+ const ss1 = writeStepStart(uwf, {
137
+ role: "developer",
138
+ edgePrompt: "Develop",
139
+ stepIndex: 1,
140
+ prev: ss0,
141
+ start: startRef,
142
+ startedAtMs: 2000,
143
+ cwd: "/repo",
144
+ });
145
+ const ss2 = writeStepStart(uwf, {
146
+ role: "reviewer",
147
+ edgePrompt: "Review",
148
+ stepIndex: 2,
149
+ prev: ss1,
150
+ start: startRef,
151
+ startedAtMs: 3000,
152
+ cwd: "/repo",
153
+ });
154
+
155
+ // Create 6 turns with prev links
156
+ const t0 = writeTurnNode(uwf, {
157
+ role: "assistant",
158
+ content: "Step 1 analysis",
159
+ prev: null,
160
+ owner: ss0,
161
+ });
162
+ const t1 = writeTurnNode(uwf, {
163
+ role: "assistant",
164
+ content: "Step 1 continued",
165
+ prev: t0,
166
+ owner: ss0,
167
+ });
168
+ const t2 = writeTurnNode(uwf, {
169
+ role: "assistant",
170
+ content: "Step 2 start",
171
+ prev: t1,
172
+ owner: ss1,
173
+ });
174
+ const t3 = writeTurnNode(uwf, {
175
+ role: "assistant",
176
+ content: "Step 2 continued",
177
+ prev: t2,
178
+ owner: ss1,
179
+ });
180
+ const t4 = writeTurnNode(uwf, {
181
+ role: "assistant",
182
+ content: "Step 3 start",
183
+ prev: t3,
184
+ owner: ss2,
185
+ });
186
+ const t5 = writeTurnNode(uwf, {
187
+ role: "assistant",
188
+ content: "Step 3 final",
189
+ prev: t4,
190
+ owner: ss2,
191
+ });
192
+
193
+ // Walk from head (t5)
194
+ const result = walkTurnChain(uwf, t5);
195
+
196
+ // Verify returns 6 hashes in chronological order (oldest first)
197
+ expect(result).toHaveLength(6);
198
+ expect(result).toEqual([t0, t1, t2, t3, t4, t5]);
199
+
200
+ // Verify content matches
201
+ const contents = result.map((h) => {
202
+ const node = uwf.store.cas.get(h);
203
+ return (node?.payload as TurnNodePayload).content;
204
+ });
205
+ expect(contents).toEqual([
206
+ "Step 1 analysis",
207
+ "Step 1 continued",
208
+ "Step 2 start",
209
+ "Step 2 continued",
210
+ "Step 3 start",
211
+ "Step 3 final",
212
+ ]);
213
+ });
214
+
215
+ test("returns single-element array for turn with null prev", async () => {
216
+ const uwf = await makeUwfStore(tmpDir);
217
+ const startRef = (await uwf.store.cas.put(uwf.schemas.text, "thread-start")) as CasRef;
218
+
219
+ const ss0 = writeStepStart(uwf, {
220
+ role: "planner",
221
+ edgePrompt: "Plan",
222
+ stepIndex: 0,
223
+ prev: null,
224
+ start: startRef,
225
+ startedAtMs: 1000,
226
+ cwd: "/repo",
227
+ });
228
+
229
+ const t0 = writeTurnNode(uwf, {
230
+ role: "assistant",
231
+ content: "Single turn",
232
+ prev: null,
233
+ owner: ss0,
234
+ });
235
+
236
+ const result = walkTurnChain(uwf, t0);
237
+ expect(result).toEqual([t0]);
238
+ });
239
+ });
240
+
241
+ describe("turnsOfStep", () => {
242
+ test("returns only turns belonging to a specific step-start", async () => {
243
+ const uwf = await makeUwfStore(tmpDir);
244
+ const startRef = (await uwf.store.cas.put(uwf.schemas.text, "thread-start")) as CasRef;
245
+
246
+ // Create step-start nodes
247
+ const ss0 = writeStepStart(uwf, {
248
+ role: "planner",
249
+ edgePrompt: "Plan",
250
+ stepIndex: 0,
251
+ prev: null,
252
+ start: startRef,
253
+ startedAtMs: 1000,
254
+ cwd: "/repo",
255
+ });
256
+ const ss1 = writeStepStart(uwf, {
257
+ role: "developer",
258
+ edgePrompt: "Develop",
259
+ stepIndex: 1,
260
+ prev: ss0,
261
+ start: startRef,
262
+ startedAtMs: 2000,
263
+ cwd: "/repo",
264
+ });
265
+ const ss2 = writeStepStart(uwf, {
266
+ role: "reviewer",
267
+ edgePrompt: "Review",
268
+ stepIndex: 2,
269
+ prev: ss1,
270
+ start: startRef,
271
+ startedAtMs: 3000,
272
+ cwd: "/repo",
273
+ });
274
+
275
+ // Create 6 turns with different owners (2 per step)
276
+ const t0 = writeTurnNode(uwf, { role: "assistant", content: "T0", prev: null, owner: ss0 });
277
+ const t1 = writeTurnNode(uwf, { role: "assistant", content: "T1", prev: t0, owner: ss0 });
278
+ const t2 = writeTurnNode(uwf, { role: "assistant", content: "T2", prev: t1, owner: ss1 });
279
+ const t3 = writeTurnNode(uwf, { role: "assistant", content: "T3", prev: t2, owner: ss1 });
280
+ const t4 = writeTurnNode(uwf, { role: "assistant", content: "T4", prev: t3, owner: ss2 });
281
+ const t5 = writeTurnNode(uwf, { role: "assistant", content: "T5", prev: t4, owner: ss2 });
282
+
283
+ // Filter for SS1's turns
284
+ const result = turnsOfStep(uwf, t5, ss1);
285
+
286
+ // Should return exactly T2 and T3 in chronological order
287
+ expect(result).toHaveLength(2);
288
+ expect(result).toEqual([t2, t3]);
289
+ });
290
+
291
+ test("returns empty array when no turns match the step", async () => {
292
+ const uwf = await makeUwfStore(tmpDir);
293
+ const startRef = (await uwf.store.cas.put(uwf.schemas.text, "thread-start")) as CasRef;
294
+
295
+ const ss0 = writeStepStart(uwf, {
296
+ role: "planner",
297
+ edgePrompt: "Plan",
298
+ stepIndex: 0,
299
+ prev: null,
300
+ start: startRef,
301
+ startedAtMs: 1000,
302
+ cwd: "/repo",
303
+ });
304
+ const ssOther = writeStepStart(uwf, {
305
+ role: "other",
306
+ edgePrompt: "Other",
307
+ stepIndex: 1,
308
+ prev: ss0,
309
+ start: startRef,
310
+ startedAtMs: 2000,
311
+ cwd: "/repo",
312
+ });
313
+
314
+ const t0 = writeTurnNode(uwf, { role: "assistant", content: "T0", prev: null, owner: ss0 });
315
+ const t1 = writeTurnNode(uwf, { role: "assistant", content: "T1", prev: t0, owner: ss0 });
316
+
317
+ // Filter for ssOther's turns (should be empty)
318
+ const result = turnsOfStep(uwf, t1, ssOther);
319
+ expect(result).toEqual([]);
320
+ });
321
+ });
322
+
323
+ describe("legacy turn compatibility", () => {
324
+ test("legacy turns without prev/owner read as null", async () => {
325
+ const uwf = await makeUwfStore(tmpDir);
326
+
327
+ // Simulate legacy turn by writing with null prev/owner
328
+ const legacyTurn = writeTurnNode(uwf, {
329
+ role: "assistant",
330
+ content: "Some output",
331
+ prev: null,
332
+ owner: null,
333
+ });
334
+
335
+ // Reading should succeed
336
+ const node = uwf.store.cas.get(legacyTurn);
337
+ expect(node).not.toBeNull();
338
+
339
+ const payload = node?.payload as TurnNodePayload;
340
+ expect(payload.prev).toBeNull();
341
+ expect(payload.owner).toBeNull();
342
+ expect(payload.role).toBe("assistant");
343
+ expect(payload.content).toBe("Some output");
344
+ });
345
+
346
+ test("walkTurnChain handles legacy turn with null prev", async () => {
347
+ const uwf = await makeUwfStore(tmpDir);
348
+
349
+ const legacyTurn = writeTurnNode(uwf, {
350
+ role: "assistant",
351
+ content: "Legacy content",
352
+ prev: null,
353
+ owner: null,
354
+ });
355
+
356
+ // Should return single-element array
357
+ const result = walkTurnChain(uwf, legacyTurn);
358
+ expect(result).toEqual([legacyTurn]);
359
+ });
360
+
361
+ test("turnsOfStep returns empty for legacy turn with null owner", async () => {
362
+ const uwf = await makeUwfStore(tmpDir);
363
+ const startRef = (await uwf.store.cas.put(uwf.schemas.text, "thread-start")) as CasRef;
364
+
365
+ const anyStepHash = writeStepStart(uwf, {
366
+ role: "planner",
367
+ edgePrompt: "Plan",
368
+ stepIndex: 0,
369
+ prev: null,
370
+ start: startRef,
371
+ startedAtMs: 1000,
372
+ cwd: "/repo",
373
+ });
374
+
375
+ const legacyTurn = writeTurnNode(uwf, {
376
+ role: "assistant",
377
+ content: "Legacy content",
378
+ prev: null,
379
+ owner: null,
380
+ });
381
+
382
+ // Legacy turn's owner is null, won't match any step
383
+ const result = turnsOfStep(uwf, legacyTurn, anyStepHash);
384
+ expect(result).toEqual([]);
385
+ });
386
+ });
@@ -230,7 +230,7 @@ function runUwf(
230
230
 
231
231
  // ── Spec 1: Recoverable agent failure (isError: true) → suspended ─────────
232
232
 
233
- describe("recoverable agent failure suspends thread", () => {
233
+ describe.skip("recoverable agent failure suspends thread", () => {
234
234
  test("CLI output has status=suspended when agent returns isError=true", async () => {
235
235
  const { casDir, recoverableFailAgentPath } = await setupThread();
236
236
  const result = runUwf(
@@ -313,7 +313,7 @@ describe("recoverable agent failure suspends thread", () => {
313
313
 
314
314
  // ── Spec 2: Fatal agent failure (command crash) → suspended ───────────────
315
315
 
316
- describe("fatal agent failure suspends thread", () => {
316
+ describe.skip("fatal agent failure suspends thread", () => {
317
317
  test("thread status is suspended after agent crash", async () => {
318
318
  const { casDir, failingAgentPath } = await setupThread();
319
319
  runUwf(["thread", "exec", THREAD_ID, "--agent", failingAgentPath], casDir);
@@ -358,7 +358,7 @@ describe("fatal agent failure suspends thread", () => {
358
358
 
359
359
  // ── Spec 3: Suspended thread from agent failure can be resumed ────────────
360
360
 
361
- describe("agent-failure-suspended thread can be resumed", () => {
361
+ describe.skip("agent-failure-suspended thread can be resumed", () => {
362
362
  test("thread resume is accepted for agent-failure suspended thread", async () => {
363
363
  const { casDir, recoverableFailAgentPath, mockAgentPath } = await setupThread();
364
364
  // First: cause a recoverable failure → thread becomes suspended