holosphere 2.0.0-alpha1 → 2.0.0-alpha4

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 (154) hide show
  1. package/dist/2019-D2OG2idw.js +6680 -0
  2. package/dist/2019-D2OG2idw.js.map +1 -0
  3. package/dist/2019-EION3wKo.cjs +8 -0
  4. package/dist/2019-EION3wKo.cjs.map +1 -0
  5. package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
  6. package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
  7. package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
  8. package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
  9. package/dist/browser-BSniCNqO.js +3058 -0
  10. package/dist/browser-BSniCNqO.js.map +1 -0
  11. package/dist/browser-Cq59Ij19.cjs +2 -0
  12. package/dist/browser-Cq59Ij19.cjs.map +1 -0
  13. package/dist/cjs/holosphere.cjs +2 -0
  14. package/dist/cjs/holosphere.cjs.map +1 -0
  15. package/dist/esm/holosphere.js +53 -0
  16. package/dist/esm/holosphere.js.map +1 -0
  17. package/dist/index-BB_vVJgv.cjs +5 -0
  18. package/dist/index-BB_vVJgv.cjs.map +1 -0
  19. package/dist/index-CBitK71M.cjs +12 -0
  20. package/dist/index-CBitK71M.cjs.map +1 -0
  21. package/dist/index-CV0eOogK.js +37423 -0
  22. package/dist/index-CV0eOogK.js.map +1 -0
  23. package/dist/index-Cz-PLCUR.js +15104 -0
  24. package/dist/index-Cz-PLCUR.js.map +1 -0
  25. package/dist/indexeddb-storage-CRsZyB2f.cjs +2 -0
  26. package/dist/indexeddb-storage-CRsZyB2f.cjs.map +1 -0
  27. package/dist/indexeddb-storage-DZaGlY_a.js +132 -0
  28. package/dist/indexeddb-storage-DZaGlY_a.js.map +1 -0
  29. package/dist/memory-storage-BkUi6sZG.js +51 -0
  30. package/dist/memory-storage-BkUi6sZG.js.map +1 -0
  31. package/dist/memory-storage-C0DuUsdY.cjs +2 -0
  32. package/dist/memory-storage-C0DuUsdY.cjs.map +1 -0
  33. package/dist/secp256k1-0kPdAVkK.cjs +12 -0
  34. package/dist/secp256k1-0kPdAVkK.cjs.map +1 -0
  35. package/dist/secp256k1-DN4FVXcv.js +1890 -0
  36. package/dist/secp256k1-DN4FVXcv.js.map +1 -0
  37. package/docs/CONTRACTS.md +797 -0
  38. package/docs/FOSDEM_PROPOSAL.md +388 -0
  39. package/docs/LOCALFIRST.md +266 -0
  40. package/docs/contracts/api-interface.md +793 -0
  41. package/docs/data-model.md +476 -0
  42. package/docs/gun-async-usage.md +338 -0
  43. package/docs/plan.md +349 -0
  44. package/docs/quickstart.md +674 -0
  45. package/docs/research.md +362 -0
  46. package/docs/spec.md +244 -0
  47. package/docs/storage-backends.md +326 -0
  48. package/docs/tasks.md +947 -0
  49. package/examples/demo.html +47 -0
  50. package/package.json +10 -5
  51. package/src/contracts/abis/Appreciative.json +1280 -0
  52. package/src/contracts/abis/AppreciativeFactory.json +101 -0
  53. package/src/contracts/abis/Bundle.json +1435 -0
  54. package/src/contracts/abis/BundleFactory.json +106 -0
  55. package/src/contracts/abis/Holon.json +881 -0
  56. package/src/contracts/abis/Holons.json +330 -0
  57. package/src/contracts/abis/Managed.json +1262 -0
  58. package/src/contracts/abis/ManagedFactory.json +149 -0
  59. package/src/contracts/abis/Membrane.json +261 -0
  60. package/src/contracts/abis/Splitter.json +1624 -0
  61. package/src/contracts/abis/SplitterFactory.json +220 -0
  62. package/src/contracts/abis/TestToken.json +321 -0
  63. package/src/contracts/abis/Zoned.json +1461 -0
  64. package/src/contracts/abis/ZonedFactory.json +154 -0
  65. package/src/contracts/chain-manager.js +375 -0
  66. package/src/contracts/deployer.js +443 -0
  67. package/src/contracts/event-listener.js +507 -0
  68. package/src/contracts/holon-contracts.js +344 -0
  69. package/src/contracts/index.js +83 -0
  70. package/src/contracts/networks.js +224 -0
  71. package/src/contracts/operations.js +670 -0
  72. package/src/contracts/queries.js +589 -0
  73. package/src/core/holosphere.js +453 -1
  74. package/src/crypto/nostr-utils.js +263 -0
  75. package/src/federation/handshake.js +455 -0
  76. package/src/federation/hologram.js +1 -1
  77. package/src/hierarchical/upcast.js +6 -5
  78. package/src/index.js +463 -1939
  79. package/src/lib/ai-methods.js +308 -0
  80. package/src/lib/contract-methods.js +293 -0
  81. package/src/lib/errors.js +23 -0
  82. package/src/lib/federation-methods.js +238 -0
  83. package/src/lib/index.js +26 -0
  84. package/src/spatial/h3-operations.js +2 -2
  85. package/src/storage/backends/gundb-backend.js +377 -46
  86. package/src/storage/global-tables.js +28 -1
  87. package/src/storage/gun-auth.js +303 -0
  88. package/src/storage/gun-federation.js +776 -0
  89. package/src/storage/gun-references.js +198 -0
  90. package/src/storage/gun-schema.js +291 -0
  91. package/src/storage/gun-wrapper.js +347 -31
  92. package/src/storage/indexeddb-storage.js +49 -11
  93. package/src/storage/memory-storage.js +5 -0
  94. package/src/storage/nostr-async.js +45 -23
  95. package/src/storage/nostr-client.js +11 -5
  96. package/src/storage/persistent-storage.js +6 -1
  97. package/src/storage/unified-storage.js +119 -0
  98. package/src/subscriptions/manager.js +1 -1
  99. package/types/index.d.ts +133 -0
  100. package/tests/unit/ai/aggregation.test.js +0 -295
  101. package/tests/unit/ai/breakdown.test.js +0 -446
  102. package/tests/unit/ai/classifier.test.js +0 -294
  103. package/tests/unit/ai/council.test.js +0 -262
  104. package/tests/unit/ai/embeddings.test.js +0 -384
  105. package/tests/unit/ai/federation-ai.test.js +0 -344
  106. package/tests/unit/ai/h3-ai.test.js +0 -458
  107. package/tests/unit/ai/index.test.js +0 -304
  108. package/tests/unit/ai/json-ops.test.js +0 -307
  109. package/tests/unit/ai/llm-service.test.js +0 -390
  110. package/tests/unit/ai/nl-query.test.js +0 -383
  111. package/tests/unit/ai/relationships.test.js +0 -311
  112. package/tests/unit/ai/schema-extractor.test.js +0 -384
  113. package/tests/unit/ai/spatial.test.js +0 -279
  114. package/tests/unit/ai/tts.test.js +0 -279
  115. package/tests/unit/content.test.js +0 -332
  116. package/tests/unit/contract/core.test.js +0 -88
  117. package/tests/unit/contract/crypto.test.js +0 -198
  118. package/tests/unit/contract/data.test.js +0 -223
  119. package/tests/unit/contract/federation.test.js +0 -181
  120. package/tests/unit/contract/hierarchical.test.js +0 -113
  121. package/tests/unit/contract/schema.test.js +0 -114
  122. package/tests/unit/contract/social.test.js +0 -217
  123. package/tests/unit/contract/spatial.test.js +0 -110
  124. package/tests/unit/contract/subscriptions.test.js +0 -128
  125. package/tests/unit/contract/utils.test.js +0 -159
  126. package/tests/unit/core.test.js +0 -152
  127. package/tests/unit/crypto.test.js +0 -328
  128. package/tests/unit/federation.test.js +0 -234
  129. package/tests/unit/gun-async.test.js +0 -252
  130. package/tests/unit/hierarchical.test.js +0 -399
  131. package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
  132. package/tests/unit/integration/scenario-02-federation.test.js +0 -76
  133. package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
  134. package/tests/unit/integration/scenario-04-validation.test.js +0 -129
  135. package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
  136. package/tests/unit/integration/scenario-06-social.test.js +0 -135
  137. package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
  138. package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
  139. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
  140. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
  141. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
  142. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
  143. package/tests/unit/performance/benchmark.test.js +0 -85
  144. package/tests/unit/schema.test.js +0 -213
  145. package/tests/unit/spatial.test.js +0 -158
  146. package/tests/unit/storage.test.js +0 -195
  147. package/tests/unit/subscriptions.test.js +0 -328
  148. package/tests/unit/test-data-permanence-debug.js +0 -197
  149. package/tests/unit/test-data-permanence.js +0 -340
  150. package/tests/unit/test-key-persistence-fixed.js +0 -148
  151. package/tests/unit/test-key-persistence.js +0 -172
  152. package/tests/unit/test-relay-permanence.js +0 -376
  153. package/tests/unit/test-second-node.js +0 -95
  154. package/tests/unit/test-simple-write.js +0 -89
@@ -1,446 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
2
- import { TaskBreakdown } from '../../../src/ai/breakdown.js';
3
-
4
- describe('Unit: TaskBreakdown', () => {
5
- let breakdown;
6
- let mockLLM;
7
- let mockHolosphere;
8
-
9
- beforeEach(() => {
10
- vi.clearAllMocks();
11
-
12
- mockLLM = {
13
- getJSON: vi.fn()
14
- };
15
-
16
- mockHolosphere = {
17
- getAll: vi.fn().mockResolvedValue([]),
18
- getSchema: vi.fn().mockResolvedValue(null),
19
- write: vi.fn().mockResolvedValue({ success: true }),
20
- update: vi.fn().mockResolvedValue({ success: true })
21
- };
22
-
23
- breakdown = new TaskBreakdown(mockLLM, mockHolosphere);
24
- });
25
-
26
- describe('Constructor', () => {
27
- it('should initialize with LLM service', () => {
28
- const b = new TaskBreakdown(mockLLM);
29
- expect(b.llm).toBe(mockLLM);
30
- expect(b.holosphere).toBeNull();
31
- });
32
-
33
- it('should accept optional HoloSphere instance', () => {
34
- expect(breakdown.holosphere).toBe(mockHolosphere);
35
- });
36
- });
37
-
38
- describe('setHoloSphere', () => {
39
- it('should set HoloSphere instance', () => {
40
- const b = new TaskBreakdown(mockLLM);
41
- b.setHoloSphere(mockHolosphere);
42
- expect(b.holosphere).toBe(mockHolosphere);
43
- });
44
- });
45
-
46
- describe('breakdown', () => {
47
- it('should break down item into subtasks', async () => {
48
- mockLLM.getJSON.mockResolvedValue([
49
- { id: 'task1-1', title: 'Subtask 1', description: 'First step', parent: 'task1', dependencies: [], status: 'pending' },
50
- { id: 'task1-2', title: 'Subtask 2', description: 'Second step', parent: 'task1', dependencies: ['task1-1'], status: 'pending' }
51
- ]);
52
-
53
- const item = { id: 'task1', title: 'Main Task', description: 'Build something' };
54
- const result = await breakdown.breakdown(item, 'holon1', 'tasks');
55
-
56
- expect(result.original).toBe(item);
57
- expect(result.holonId).toBe('holon1');
58
- expect(result.lensName).toBe('tasks');
59
- expect(result.breakdown).toBeDefined();
60
- expect(result.totalSubtasks).toBeGreaterThan(0);
61
- });
62
-
63
- it('should throw error if item has no id', async () => {
64
- await expect(breakdown.breakdown({ title: 'No ID' }, 'holon', 'lens'))
65
- .rejects.toThrow('Item must have an id property');
66
-
67
- await expect(breakdown.breakdown(null, 'holon', 'lens'))
68
- .rejects.toThrow('Item must have an id property');
69
- });
70
-
71
- it('should respect depth option', async () => {
72
- mockLLM.getJSON.mockResolvedValue([
73
- { id: 'task1-1', title: 'Subtask', status: 'pending' }
74
- ]);
75
-
76
- const result = await breakdown.breakdown(
77
- { id: 'task1' },
78
- 'holon',
79
- 'lens',
80
- { depth: 1 }
81
- );
82
-
83
- expect(result.maxDepth).toBe(1);
84
- });
85
-
86
- it('should store subtasks when storeResults is true', async () => {
87
- mockLLM.getJSON.mockResolvedValue([
88
- { id: 'task1-1', title: 'Subtask', status: 'pending' }
89
- ]);
90
-
91
- await breakdown.breakdown(
92
- { id: 'task1' },
93
- 'holon',
94
- 'lens',
95
- { storeResults: true, depth: 1 }
96
- );
97
-
98
- expect(mockHolosphere.write).toHaveBeenCalled();
99
- });
100
-
101
- it('should use context from other items', async () => {
102
- mockHolosphere.getAll.mockResolvedValue([
103
- { id: 'other1', title: 'Other Task' },
104
- { id: 'other2', title: 'Another Task' }
105
- ]);
106
-
107
- mockLLM.getJSON.mockResolvedValue([]);
108
-
109
- await breakdown.breakdown(
110
- { id: 'task1' },
111
- 'holon',
112
- 'lens',
113
- { useContext: true, depth: 1 }
114
- );
115
-
116
- const call = mockLLM.getJSON.mock.calls[0];
117
- expect(call[0]).toContain('context');
118
- });
119
-
120
- it('should normalize stepsPerLevel number to object', async () => {
121
- mockLLM.getJSON.mockResolvedValue([]);
122
-
123
- await breakdown.breakdown(
124
- { id: 'task1' },
125
- 'holon',
126
- 'lens',
127
- { stepsPerLevel: 5, depth: 1 }
128
- );
129
-
130
- const call = mockLLM.getJSON.mock.calls[0];
131
- expect(call[0]).toContain('5');
132
- });
133
- });
134
-
135
- describe('_generateSubtasks', () => {
136
- it('should generate subtasks with proper structure', async () => {
137
- mockLLM.getJSON.mockResolvedValue([
138
- { id: 'p-1', title: 'Step 1', description: 'Do step 1', parent: 'parent', dependencies: [], status: 'pending' },
139
- { id: 'p-2', title: 'Step 2', description: 'Do step 2', parent: 'parent', dependencies: ['p-1'], status: 'pending' }
140
- ]);
141
-
142
- const parent = { id: 'parent', title: 'Parent Task' };
143
- const result = await breakdown._generateSubtasks(
144
- parent,
145
- [],
146
- null,
147
- { min: 2, max: 4 },
148
- 'dependencies',
149
- 'parent'
150
- );
151
-
152
- expect(result).toHaveLength(2);
153
- expect(result[0].parent).toBe('parent');
154
- expect(result[0]._meta.generatedFrom).toBe('parent');
155
- });
156
-
157
- it('should handle LLM errors gracefully', async () => {
158
- mockLLM.getJSON.mockRejectedValue(new Error('API Error'));
159
-
160
- const result = await breakdown._generateSubtasks(
161
- { id: 'parent' },
162
- [],
163
- null,
164
- { min: 3, max: 5 },
165
- 'dependencies',
166
- 'parent'
167
- );
168
-
169
- expect(result).toEqual([]);
170
- });
171
-
172
- it('should ensure all subtasks have required fields', async () => {
173
- mockLLM.getJSON.mockResolvedValue([
174
- { title: 'Missing ID and status' }
175
- ]);
176
-
177
- const result = await breakdown._generateSubtasks(
178
- { id: 'parent' },
179
- [],
180
- null,
181
- { min: 1, max: 1 },
182
- 'dependencies',
183
- 'parent'
184
- );
185
-
186
- expect(result[0].id).toBeDefined();
187
- expect(result[0].parent).toBe('parent');
188
- expect(result[0].status).toBe('pending');
189
- });
190
- });
191
-
192
- describe('_countSubtasks', () => {
193
- it('should count all subtasks in tree', () => {
194
- const tree = {
195
- item: { id: 'root' },
196
- children: [
197
- {
198
- item: { id: 'c1' },
199
- children: [
200
- { item: { id: 'c1-1' }, children: [] },
201
- { item: { id: 'c1-2' }, children: [] }
202
- ]
203
- },
204
- {
205
- item: { id: 'c2' },
206
- children: []
207
- }
208
- ]
209
- };
210
-
211
- expect(breakdown._countSubtasks(tree)).toBe(4);
212
- });
213
-
214
- it('should return 0 for node without children', () => {
215
- const tree = { item: { id: 'leaf' }, children: [] };
216
- expect(breakdown._countSubtasks(tree)).toBe(0);
217
- });
218
- });
219
-
220
- describe('flatten', () => {
221
- it('should flatten breakdown tree into list', () => {
222
- const breakdownResult = {
223
- breakdown: {
224
- item: { id: 'root' },
225
- children: [
226
- { item: { id: 'c1' }, children: [] },
227
- { item: { id: 'c2' }, children: [] }
228
- ]
229
- }
230
- };
231
-
232
- const flat = breakdown.flatten(breakdownResult);
233
-
234
- expect(flat).toHaveLength(3);
235
- expect(flat.map(i => i.id)).toContain('root');
236
- expect(flat.map(i => i.id)).toContain('c1');
237
- expect(flat.map(i => i.id)).toContain('c2');
238
- });
239
-
240
- it('should handle empty breakdown', () => {
241
- const flat = breakdown.flatten({});
242
- expect(flat).toEqual([]);
243
- });
244
- });
245
-
246
- describe('getDependencyOrder', () => {
247
- it('should order items by depth and dependencies', () => {
248
- const breakdownResult = {
249
- breakdown: {
250
- item: { id: 'root', _meta: { depth: 0 } },
251
- children: [
252
- { item: { id: 'c1', _meta: { depth: 1 }, dependencies: [] }, children: [] },
253
- { item: { id: 'c2', _meta: { depth: 1 }, dependencies: ['c1'] }, children: [] }
254
- ]
255
- }
256
- };
257
-
258
- const ordered = breakdown.getDependencyOrder(breakdownResult);
259
-
260
- const c1Index = ordered.findIndex(i => i.id === 'c1');
261
- const c2Index = ordered.findIndex(i => i.id === 'c2');
262
- expect(c1Index).toBeLessThan(c2Index);
263
- });
264
- });
265
-
266
- describe('suggestStrategy', () => {
267
- it('should suggest breakdown strategy', async () => {
268
- mockLLM.getJSON.mockResolvedValue({
269
- complexity: 'complex',
270
- recommendedDepth: 3,
271
- recommendedStepsPerLevel: { min: 3, max: 5 },
272
- focusAreas: ['Planning', 'Implementation'],
273
- potentialChallenges: ['Resource constraints'],
274
- estimatedTotalSubtasks: 15,
275
- reasoning: 'Complex project needs thorough breakdown'
276
- });
277
-
278
- const item = { id: 'project1', title: 'Big Project', description: 'Complex undertaking' };
279
- const result = await breakdown.suggestStrategy(item);
280
-
281
- expect(result.complexity).toBe('complex');
282
- expect(result.recommendedDepth).toBe(3);
283
- expect(result.focusAreas).toContain('Planning');
284
- });
285
- });
286
-
287
- describe('rebalance', () => {
288
- it('should rebalance breakdown tree', async () => {
289
- mockLLM.getJSON.mockResolvedValue({
290
- tasks: [
291
- { id: 'new1', title: 'Merged Task' },
292
- { id: 'new2', title: 'Split Task A' },
293
- { id: 'new3', title: 'Split Task B' }
294
- ],
295
- changes: [
296
- { type: 'merge', description: 'Merged granular tasks' },
297
- { type: 'split', description: 'Split large task' }
298
- ],
299
- summary: 'Rebalanced for 3-5 items per level'
300
- });
301
-
302
- const breakdownResult = {
303
- breakdown: {
304
- item: { id: 'root' },
305
- children: [
306
- { item: { id: 'c1' }, children: [] }
307
- ]
308
- }
309
- };
310
-
311
- const result = await breakdown.rebalance(breakdownResult);
312
-
313
- expect(result.tasks).toHaveLength(3);
314
- expect(result.changes).toHaveLength(2);
315
- });
316
- });
317
-
318
- describe('getProgress', () => {
319
- it('should calculate progress on breakdown tree', async () => {
320
- mockHolosphere.getAll.mockResolvedValue([
321
- { id: 'root', title: 'Main Task', status: 'in_progress' },
322
- { id: 'c1', title: 'Subtask 1', parent: 'root', status: 'completed' },
323
- { id: 'c2', title: 'Subtask 2', parent: 'root', status: 'pending' },
324
- { id: 'c3', title: 'Subtask 3', parent: 'root', status: 'in_progress', dependencies: ['c1'] }
325
- ]);
326
-
327
- const result = await breakdown.getProgress('holon', 'lens', 'root');
328
-
329
- expect(result.itemId).toBe('root');
330
- expect(result.total).toBe(3);
331
- expect(result.completed).toBe(1);
332
- expect(result.inProgress).toBe(1);
333
- expect(result.pending).toBe(1);
334
- expect(result.percentComplete).toBe(33);
335
- });
336
-
337
- it('should throw error if HoloSphere not available', async () => {
338
- const b = new TaskBreakdown(mockLLM);
339
-
340
- await expect(b.getProgress('holon', 'lens', 'id'))
341
- .rejects.toThrow('HoloSphere instance required');
342
- });
343
-
344
- it('should throw error if item not found', async () => {
345
- mockHolosphere.getAll.mockResolvedValue([]);
346
-
347
- await expect(breakdown.getProgress('holon', 'lens', 'nonexistent'))
348
- .rejects.toThrow('Item nonexistent not found');
349
- });
350
-
351
- it('should find blockers', async () => {
352
- mockHolosphere.getAll.mockResolvedValue([
353
- { id: 'root', title: 'Main', status: 'pending' },
354
- { id: 'c1', title: 'Blocker', parent: 'root', status: 'pending' },
355
- { id: 'c2', title: 'Blocked', parent: 'root', status: 'pending', dependencies: ['c1'] }
356
- ]);
357
-
358
- const result = await breakdown.getProgress('holon', 'lens', 'root');
359
-
360
- expect(result.blockers).toHaveLength(1);
361
- expect(result.blockers[0].id).toBe('c2');
362
- });
363
-
364
- it('should find next tasks ready to work on', async () => {
365
- mockHolosphere.getAll.mockResolvedValue([
366
- { id: 'root', title: 'Main', status: 'pending' },
367
- { id: 'c1', title: 'Ready', parent: 'root', status: 'pending', dependencies: [] },
368
- { id: 'c2', title: 'Also Ready', parent: 'root', status: 'pending' }
369
- ]);
370
-
371
- const result = await breakdown.getProgress('holon', 'lens', 'root');
372
-
373
- expect(result.nextUp.length).toBeGreaterThan(0);
374
- expect(result.nextUp.map(t => t.id)).toContain('c1');
375
- });
376
- });
377
-
378
- describe('_findDescendants', () => {
379
- it('should find all descendants', () => {
380
- const items = [
381
- { id: 'c1', parent: 'root' },
382
- { id: 'c2', parent: 'root' },
383
- { id: 'c1-1', parent: 'c1' },
384
- { id: 'other', parent: 'different' }
385
- ];
386
-
387
- const descendants = breakdown._findDescendants('root', items);
388
-
389
- expect(descendants).toHaveLength(3);
390
- expect(descendants.map(d => d.id)).toContain('c1');
391
- expect(descendants.map(d => d.id)).toContain('c1-1');
392
- });
393
- });
394
-
395
- describe('_findBlockers', () => {
396
- it('should find blocked tasks', () => {
397
- const items = [
398
- { id: 'c1', status: 'pending' },
399
- { id: 'c2', status: 'pending', dependencies: ['c1'] }
400
- ];
401
-
402
- const blockers = breakdown._findBlockers(items);
403
-
404
- expect(blockers).toHaveLength(1);
405
- expect(blockers[0].id).toBe('c2');
406
- });
407
-
408
- it('should not include tasks with completed dependencies', () => {
409
- const items = [
410
- { id: 'c1', status: 'completed' },
411
- { id: 'c2', status: 'pending', dependencies: ['c1'] }
412
- ];
413
-
414
- const blockers = breakdown._findBlockers(items);
415
-
416
- expect(blockers).toHaveLength(0);
417
- });
418
- });
419
-
420
- describe('_findNextTasks', () => {
421
- it('should find tasks ready to start', () => {
422
- const items = [
423
- { id: 'c1', status: 'pending' },
424
- { id: 'c2', status: 'pending', dependencies: ['c1'] }
425
- ];
426
-
427
- const next = breakdown._findNextTasks(items);
428
-
429
- expect(next).toHaveLength(1);
430
- expect(next[0].id).toBe('c1');
431
- });
432
-
433
- it('should exclude completed and in-progress tasks', () => {
434
- const items = [
435
- { id: 'c1', status: 'completed' },
436
- { id: 'c2', status: 'in_progress' },
437
- { id: 'c3', status: 'pending' }
438
- ];
439
-
440
- const next = breakdown._findNextTasks(items);
441
-
442
- expect(next.map(t => t.id)).not.toContain('c1');
443
- expect(next.map(t => t.id)).not.toContain('c2');
444
- });
445
- });
446
- });