@unrdf/kgc-runtime 26.4.2

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 (70) hide show
  1. package/IMPLEMENTATION_SUMMARY.json +150 -0
  2. package/PLUGIN_SYSTEM_SUMMARY.json +149 -0
  3. package/README.md +98 -0
  4. package/TRANSACTION_IMPLEMENTATION.json +119 -0
  5. package/capability-map.md +93 -0
  6. package/docs/api-stability.md +269 -0
  7. package/docs/extensions/plugin-development.md +382 -0
  8. package/package.json +40 -0
  9. package/plugins/registry.json +35 -0
  10. package/src/admission-gate.mjs +414 -0
  11. package/src/api-version.mjs +373 -0
  12. package/src/atomic-admission.mjs +310 -0
  13. package/src/bounds.mjs +289 -0
  14. package/src/bulkhead-manager.mjs +280 -0
  15. package/src/capsule.mjs +524 -0
  16. package/src/crdt.mjs +361 -0
  17. package/src/enhanced-bounds.mjs +614 -0
  18. package/src/executor.mjs +73 -0
  19. package/src/freeze-restore.mjs +521 -0
  20. package/src/index.mjs +62 -0
  21. package/src/materialized-views.mjs +371 -0
  22. package/src/merge.mjs +472 -0
  23. package/src/plugin-isolation.mjs +392 -0
  24. package/src/plugin-manager.mjs +441 -0
  25. package/src/projections-api.mjs +336 -0
  26. package/src/projections-cli.mjs +238 -0
  27. package/src/projections-docs.mjs +300 -0
  28. package/src/projections-ide.mjs +278 -0
  29. package/src/receipt.mjs +340 -0
  30. package/src/rollback.mjs +258 -0
  31. package/src/saga-orchestrator.mjs +355 -0
  32. package/src/schemas.mjs +1330 -0
  33. package/src/storage-optimization.mjs +359 -0
  34. package/src/tool-registry.mjs +272 -0
  35. package/src/transaction.mjs +466 -0
  36. package/src/validators.mjs +485 -0
  37. package/src/work-item.mjs +449 -0
  38. package/templates/plugin-template/README.md +58 -0
  39. package/templates/plugin-template/index.mjs +162 -0
  40. package/templates/plugin-template/plugin.json +19 -0
  41. package/test/admission-gate.test.mjs +583 -0
  42. package/test/api-version.test.mjs +74 -0
  43. package/test/atomic-admission.test.mjs +155 -0
  44. package/test/bounds.test.mjs +341 -0
  45. package/test/bulkhead-manager.test.mjs +236 -0
  46. package/test/capsule.test.mjs +625 -0
  47. package/test/crdt.test.mjs +215 -0
  48. package/test/enhanced-bounds.test.mjs +487 -0
  49. package/test/freeze-restore.test.mjs +472 -0
  50. package/test/materialized-views.test.mjs +243 -0
  51. package/test/merge.test.mjs +665 -0
  52. package/test/plugin-isolation.test.mjs +109 -0
  53. package/test/plugin-manager.test.mjs +208 -0
  54. package/test/projections-api.test.mjs +293 -0
  55. package/test/projections-cli.test.mjs +204 -0
  56. package/test/projections-docs.test.mjs +173 -0
  57. package/test/projections-ide.test.mjs +230 -0
  58. package/test/receipt.test.mjs +295 -0
  59. package/test/rollback.test.mjs +132 -0
  60. package/test/saga-orchestrator.test.mjs +279 -0
  61. package/test/schemas.test.mjs +716 -0
  62. package/test/storage-optimization.test.mjs +503 -0
  63. package/test/tool-registry.test.mjs +341 -0
  64. package/test/transaction.test.mjs +189 -0
  65. package/test/validators.test.mjs +463 -0
  66. package/test/work-item.test.mjs +548 -0
  67. package/test/work-item.test.mjs.bak +548 -0
  68. package/var/kgc/test-atomic-log.json +519 -0
  69. package/var/kgc/test-cascading-log.json +145 -0
  70. package/vitest.config.mjs +18 -0
@@ -0,0 +1,716 @@
1
+ /**
2
+ * @file Comprehensive validation tests for KGC Runtime schemas
3
+ * @description Tests all schemas with valid/invalid inputs, edge cases, and defaults
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import {
8
+ ReceiptSchema,
9
+ RunCapsuleSchema,
10
+ ToolTraceEntrySchema,
11
+ BoundsSchema,
12
+ WorkItemSchema,
13
+ ProjectionManifestSchema,
14
+ KGCMarkdownSchema,
15
+ validateReceipt,
16
+ validateRunCapsule,
17
+ validateToolTraceEntry,
18
+ validateBounds,
19
+ validateWorkItem,
20
+ validateProjectionManifest,
21
+ validateKGCMarkdown,
22
+ } from '../src/schemas.mjs';
23
+
24
+ // =============================================================================
25
+ // Receipt Schema Tests
26
+ // =============================================================================
27
+
28
+ describe('ReceiptSchema', () => {
29
+ it('should validate a complete receipt', () => {
30
+ const receipt = {
31
+ version: '1.0.0',
32
+ id: '550e8400-e29b-41d4-a716-446655440000',
33
+ timestamp: 1703001600000,
34
+ runId: 'run-2024-001',
35
+ actor: 'agent:orchestrator',
36
+ action: 'execute',
37
+ payload: { workflowId: 'wf-001', input: { x: 42 } },
38
+ result: {
39
+ success: true,
40
+ output: { y: 84 },
41
+ duration: 2314,
42
+ },
43
+ contentHash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
44
+ previousHash: 'a1b2c3d4e5f67890123456789012345678901234567890123456789012345678',
45
+ signature: {
46
+ algorithm: 'ed25519',
47
+ publicKey: '0x1234567890abcdef',
48
+ value: '0xabcdef1234567890',
49
+ },
50
+ };
51
+
52
+ const result = validateReceipt(receipt);
53
+ expect(result.success).toBe(true);
54
+ expect(result.data).toBeDefined();
55
+ expect(result.data.version).toBe('1.0.0');
56
+ });
57
+
58
+ it('should validate minimal receipt', () => {
59
+ const receipt = {
60
+ id: '550e8400-e29b-41d4-a716-446655440000',
61
+ timestamp: 1703001600000,
62
+ runId: 'run-001',
63
+ actor: 'agent:test',
64
+ action: 'execute',
65
+ payload: {},
66
+ };
67
+
68
+ const result = validateReceipt(receipt);
69
+ expect(result.success).toBe(true);
70
+ expect(result.data.version).toBe('1.0.0'); // Default
71
+ });
72
+
73
+ it('should reject invalid actor format', () => {
74
+ const receipt = {
75
+ id: '550e8400-e29b-41d4-a716-446655440000',
76
+ timestamp: 1703001600000,
77
+ runId: 'run-001',
78
+ actor: 'invalid-actor', // Missing type prefix
79
+ action: 'execute',
80
+ payload: {},
81
+ };
82
+
83
+ const result = validateReceipt(receipt);
84
+ expect(result.success).toBe(false);
85
+ expect(result.errors.length).toBeGreaterThan(0);
86
+ });
87
+
88
+ it('should reject invalid UUID', () => {
89
+ const receipt = {
90
+ id: 'not-a-uuid',
91
+ timestamp: 1703001600000,
92
+ runId: 'run-001',
93
+ actor: 'agent:test',
94
+ action: 'execute',
95
+ payload: {},
96
+ };
97
+
98
+ const result = validateReceipt(receipt);
99
+ expect(result.success).toBe(false);
100
+ });
101
+
102
+ it('should reject invalid SHA-256 hash', () => {
103
+ const receipt = {
104
+ id: '550e8400-e29b-41d4-a716-446655440000',
105
+ timestamp: 1703001600000,
106
+ runId: 'run-001',
107
+ actor: 'agent:test',
108
+ action: 'execute',
109
+ payload: {},
110
+ contentHash: 'invalid-hash', // Not 64 hex chars
111
+ };
112
+
113
+ const result = validateReceipt(receipt);
114
+ expect(result.success).toBe(false);
115
+ });
116
+ });
117
+
118
+ // =============================================================================
119
+ // ToolTraceEntry Schema Tests
120
+ // =============================================================================
121
+
122
+ describe('ToolTraceEntrySchema', () => {
123
+ it('should validate a complete tool trace entry', () => {
124
+ const entry = {
125
+ id: '123e4567-e89b-12d3-a456-426614174000',
126
+ timestamp: 1703001600000,
127
+ toolName: 'Bash',
128
+ input: { command: 'npm test', timeout: 5000 },
129
+ output: { stdout: '✅ All tests passed', stderr: '', exitCode: 0 },
130
+ duration: 2314,
131
+ status: 'success',
132
+ parentId: null,
133
+ dependencies: [],
134
+ resources: {
135
+ cpuTime: 1200,
136
+ memoryPeak: 45678901,
137
+ ioBytes: 1024,
138
+ },
139
+ };
140
+
141
+ const result = validateToolTraceEntry(entry);
142
+ expect(result.success).toBe(true);
143
+ expect(result.data.toolName).toBe('Bash');
144
+ expect(result.data.status).toBe('success');
145
+ });
146
+
147
+ it('should validate minimal trace entry', () => {
148
+ const entry = {
149
+ id: '123e4567-e89b-12d3-a456-426614174000',
150
+ timestamp: 1703001600000,
151
+ toolName: 'Read',
152
+ input: { file_path: '/home/user/file.txt' },
153
+ duration: 10,
154
+ status: 'success',
155
+ };
156
+
157
+ const result = validateToolTraceEntry(entry);
158
+ expect(result.success).toBe(true);
159
+ });
160
+
161
+ it('should validate error status with error details', () => {
162
+ const entry = {
163
+ id: '123e4567-e89b-12d3-a456-426614174000',
164
+ timestamp: 1703001600000,
165
+ toolName: 'Bash',
166
+ input: { command: 'invalid-command' },
167
+ duration: 50,
168
+ status: 'error',
169
+ error: {
170
+ message: 'Command not found',
171
+ code: 'ENOENT',
172
+ },
173
+ };
174
+
175
+ const result = validateToolTraceEntry(entry);
176
+ expect(result.success).toBe(true);
177
+ expect(result.data.error).toBeDefined();
178
+ });
179
+ });
180
+
181
+ // =============================================================================
182
+ // RunCapsule Schema Tests
183
+ // =============================================================================
184
+
185
+ describe('RunCapsuleSchema', () => {
186
+ it('should validate a complete run capsule', () => {
187
+ const capsule = {
188
+ id: 'run-2024-12-26-001',
189
+ version: '1.0.0',
190
+ startTime: 1703001600000,
191
+ endTime: 1703001620000,
192
+ status: 'completed',
193
+ input: {
194
+ task: 'Implement feature X',
195
+ parameters: { timeout: 5000 },
196
+ context: { workingDir: '/home/user/project' },
197
+ artifacts: [
198
+ {
199
+ type: 'file',
200
+ path: '/home/user/spec.md',
201
+ hash: 'abc1234567890def1234567890abc1234567890def1234567890abc123456789',
202
+ },
203
+ ],
204
+ },
205
+ output: {
206
+ success: true,
207
+ results: { filesChanged: 5, testsAdded: 12 },
208
+ artifacts: [
209
+ {
210
+ type: 'file',
211
+ path: '/home/user/project/src/feature.mjs',
212
+ hash: 'def1234567890abc1234567890def1234567890abc1234567890def123456789',
213
+ size: 4567,
214
+ },
215
+ ],
216
+ },
217
+ trace: [
218
+ {
219
+ id: '123e4567-e89b-12d3-a456-426614174000',
220
+ timestamp: 1703001605000,
221
+ toolName: 'Bash',
222
+ input: { command: 'npm test' },
223
+ duration: 2000,
224
+ status: 'success',
225
+ },
226
+ ],
227
+ bounds: {
228
+ maxFiles: 100,
229
+ maxBytes: 10485760,
230
+ maxOps: 1000,
231
+ maxRuntime: 300000,
232
+ },
233
+ actor: 'agent:backend-dev',
234
+ };
235
+
236
+ const result = validateRunCapsule(capsule);
237
+ expect(result.success).toBe(true);
238
+ expect(result.data.status).toBe('completed');
239
+ });
240
+
241
+ it('should validate minimal run capsule', () => {
242
+ const capsule = {
243
+ id: 'run-001',
244
+ startTime: 1703001600000,
245
+ status: 'pending',
246
+ input: {
247
+ task: 'Simple task',
248
+ },
249
+ actor: 'agent:test',
250
+ };
251
+
252
+ const result = validateRunCapsule(capsule);
253
+ expect(result.success).toBe(true);
254
+ expect(result.data.version).toBe('1.0.0'); // Default
255
+ });
256
+
257
+ it('should validate failed run with error', () => {
258
+ const capsule = {
259
+ id: 'run-002',
260
+ startTime: 1703001600000,
261
+ endTime: 1703001605000,
262
+ status: 'failed',
263
+ input: { task: 'Failing task' },
264
+ output: {
265
+ success: false,
266
+ error: {
267
+ message: 'Test failed',
268
+ code: 'TEST_FAILURE',
269
+ recoverable: true,
270
+ },
271
+ },
272
+ actor: 'agent:test',
273
+ };
274
+
275
+ const result = validateRunCapsule(capsule);
276
+ expect(result.success).toBe(true);
277
+ expect(result.data.output.success).toBe(false);
278
+ });
279
+ });
280
+
281
+ // =============================================================================
282
+ // Bounds Schema Tests
283
+ // =============================================================================
284
+
285
+ describe('BoundsSchema', () => {
286
+ it('should validate complete bounds with usage', () => {
287
+ const bounds = {
288
+ maxFiles: 100,
289
+ maxBytes: 10485760,
290
+ maxOps: 1000,
291
+ maxRuntime: 300000,
292
+ maxGraphRewrites: 50,
293
+ enforcementPolicy: 'strict',
294
+ warnings: {
295
+ filesThreshold: 0.8,
296
+ bytesThreshold: 0.9,
297
+ opsThreshold: 0.75,
298
+ runtimeThreshold: 0.9,
299
+ graphRewritesThreshold: 0.8,
300
+ },
301
+ currentUsage: {
302
+ files: 45,
303
+ bytes: 5242880,
304
+ ops: 500,
305
+ runtime: 120000,
306
+ graphRewrites: 20,
307
+ },
308
+ };
309
+
310
+ const result = validateBounds(bounds);
311
+ expect(result.success).toBe(true);
312
+ expect(result.data.enforcementPolicy).toBe('strict');
313
+ });
314
+
315
+ it('should apply defaults', () => {
316
+ const bounds = {};
317
+
318
+ const result = validateBounds(bounds);
319
+ expect(result.success).toBe(true);
320
+ expect(result.data.maxFiles).toBe(100);
321
+ expect(result.data.maxBytes).toBe(10 * 1024 * 1024);
322
+ expect(result.data.maxOps).toBe(1000);
323
+ expect(result.data.maxRuntime).toBe(300000);
324
+ expect(result.data.maxGraphRewrites).toBe(50);
325
+ expect(result.data.enforcementPolicy).toBe('strict');
326
+ });
327
+
328
+ it('should reject invalid enforcement policy', () => {
329
+ const bounds = {
330
+ enforcementPolicy: 'invalid',
331
+ };
332
+
333
+ const result = validateBounds(bounds);
334
+ expect(result.success).toBe(false);
335
+ });
336
+
337
+ it('should reject out-of-range values', () => {
338
+ const bounds = {
339
+ maxFiles: 100000, // Exceeds max of 10000
340
+ };
341
+
342
+ const result = validateBounds(bounds);
343
+ expect(result.success).toBe(false);
344
+ });
345
+ });
346
+
347
+ // =============================================================================
348
+ // WorkItem Schema Tests
349
+ // =============================================================================
350
+
351
+ describe('WorkItemSchema', () => {
352
+ it('should validate complete work item', () => {
353
+ const workItem = {
354
+ id: '123e4567-e89b-12d3-a456-426614174000',
355
+ type: 'file_operation',
356
+ state: 'running',
357
+ priority: 75,
358
+ createdAt: 1703001600000,
359
+ startedAt: 1703001605000,
360
+ payload: {
361
+ operation: 'write',
362
+ path: '/home/user/file.txt',
363
+ content: 'Hello World',
364
+ },
365
+ dependencies: ['223e4567-e89b-12d3-a456-426614174000'],
366
+ retries: {
367
+ max: 3,
368
+ current: 0,
369
+ backoff: 'exponential',
370
+ delay: 1000,
371
+ },
372
+ timeout: 30000,
373
+ assignedTo: 'agent:worker-01',
374
+ progress: 0.5,
375
+ };
376
+
377
+ const result = validateWorkItem(workItem);
378
+ expect(result.success).toBe(true);
379
+ expect(result.data.state).toBe('running');
380
+ expect(result.data.priority).toBe(75);
381
+ });
382
+
383
+ it('should validate minimal work item', () => {
384
+ const workItem = {
385
+ id: '123e4567-e89b-12d3-a456-426614174000',
386
+ type: 'task',
387
+ state: 'queued',
388
+ createdAt: 1703001600000,
389
+ payload: {},
390
+ };
391
+
392
+ const result = validateWorkItem(workItem);
393
+ expect(result.success).toBe(true);
394
+ expect(result.data.priority).toBe(50); // Default
395
+ expect(result.data.timeout).toBe(30000); // Default
396
+ });
397
+
398
+ it('should validate failed work item with error', () => {
399
+ const workItem = {
400
+ id: '123e4567-e89b-12d3-a456-426614174000',
401
+ type: 'computation',
402
+ state: 'failed',
403
+ createdAt: 1703001600000,
404
+ startedAt: 1703001605000,
405
+ completedAt: 1703001610000,
406
+ payload: { x: 42 },
407
+ error: {
408
+ message: 'Division by zero',
409
+ code: 'MATH_ERROR',
410
+ retryable: false,
411
+ },
412
+ };
413
+
414
+ const result = validateWorkItem(workItem);
415
+ expect(result.success).toBe(true);
416
+ expect(result.data.state).toBe('failed');
417
+ });
418
+
419
+ it('should reject invalid priority range', () => {
420
+ const workItem = {
421
+ id: '123e4567-e89b-12d3-a456-426614174000',
422
+ type: 'task',
423
+ state: 'queued',
424
+ priority: 150, // Exceeds max of 100
425
+ createdAt: 1703001600000,
426
+ payload: {},
427
+ };
428
+
429
+ const result = validateWorkItem(workItem);
430
+ expect(result.success).toBe(false);
431
+ });
432
+ });
433
+
434
+ // =============================================================================
435
+ // ProjectionManifest Schema Tests
436
+ // =============================================================================
437
+
438
+ describe('ProjectionManifestSchema', () => {
439
+ it('should validate complete projection manifest', () => {
440
+ const manifest = {
441
+ version: '1.0.0',
442
+ surfaces: {
443
+ cli: {
444
+ commands: [
445
+ {
446
+ name: 'run',
447
+ description: 'Execute a workflow',
448
+ aliases: ['r'],
449
+ options: [
450
+ {
451
+ name: 'file',
452
+ type: 'string',
453
+ required: true,
454
+ description: 'Workflow file path',
455
+ },
456
+ ],
457
+ examples: ['kgc run workflow.yml'],
458
+ },
459
+ ],
460
+ },
461
+ docs: {
462
+ generator: 'typedoc',
463
+ outputDir: './docs',
464
+ includes: ['**/*.mjs'],
465
+ theme: 'default',
466
+ },
467
+ ide: {
468
+ lsp: {
469
+ enabled: true,
470
+ port: 9000,
471
+ features: ['completion', 'hover', 'goto'],
472
+ },
473
+ snippets: [
474
+ {
475
+ prefix: 'run',
476
+ body: 'RunCapsuleSchema.parse({ ... })',
477
+ description: 'Create run capsule',
478
+ },
479
+ ],
480
+ },
481
+ },
482
+ };
483
+
484
+ const result = validateProjectionManifest(manifest);
485
+ expect(result.success).toBe(true);
486
+ expect(result.data.surfaces.cli).toBeDefined();
487
+ });
488
+
489
+ it('should validate minimal manifest', () => {
490
+ const manifest = {
491
+ surfaces: {},
492
+ };
493
+
494
+ const result = validateProjectionManifest(manifest);
495
+ expect(result.success).toBe(true);
496
+ expect(result.data.version).toBe('1.0.0'); // Default
497
+ });
498
+
499
+ it('should validate API surface', () => {
500
+ const manifest = {
501
+ surfaces: {
502
+ api: {
503
+ type: 'rest',
504
+ baseUrl: 'https://api.example.com',
505
+ endpoints: [
506
+ {
507
+ path: '/runs',
508
+ method: 'POST',
509
+ auth: 'bearer',
510
+ },
511
+ ],
512
+ versioning: {
513
+ strategy: 'url',
514
+ current: '1.0.0',
515
+ },
516
+ },
517
+ },
518
+ };
519
+
520
+ const result = validateProjectionManifest(manifest);
521
+ expect(result.success).toBe(true);
522
+ });
523
+ });
524
+
525
+ // =============================================================================
526
+ // KGCMarkdown Schema Tests
527
+ // =============================================================================
528
+
529
+ describe('KGCMarkdownSchema', () => {
530
+ it('should validate complete markdown document', () => {
531
+ const doc = {
532
+ type: 'document',
533
+ frontMatter: {
534
+ title: 'KGC Example',
535
+ version: '1.0.0',
536
+ author: 'Test Author',
537
+ date: new Date('2024-12-26'),
538
+ ontology: ['http://schema.org/'],
539
+ tags: ['example', 'test'],
540
+ },
541
+ children: [
542
+ {
543
+ type: 'heading',
544
+ level: 1,
545
+ content: 'Introduction',
546
+ id: 'intro',
547
+ metadata: {},
548
+ },
549
+ {
550
+ type: 'paragraph',
551
+ content: 'This is a paragraph.',
552
+ metadata: {},
553
+ },
554
+ {
555
+ type: 'fenced-block',
556
+ language: 'javascript',
557
+ attributes: { executable: true },
558
+ content: 'console.log("Hello");',
559
+ executable: true,
560
+ metadata: {},
561
+ },
562
+ ],
563
+ metadata: {},
564
+ };
565
+
566
+ const result = validateKGCMarkdown(doc);
567
+ expect(result.success).toBe(true);
568
+ expect(result.data.children.length).toBe(3);
569
+ });
570
+
571
+ it('should validate minimal document', () => {
572
+ const doc = {
573
+ type: 'document',
574
+ children: [
575
+ {
576
+ type: 'paragraph',
577
+ content: 'Simple paragraph',
578
+ metadata: {},
579
+ },
580
+ ],
581
+ };
582
+
583
+ const result = validateKGCMarkdown(doc);
584
+ expect(result.success).toBe(true);
585
+ });
586
+
587
+ it('should validate list node', () => {
588
+ const doc = {
589
+ type: 'document',
590
+ children: [
591
+ {
592
+ type: 'list',
593
+ ordered: false,
594
+ items: ['Item 1', 'Item 2', 'Item 3'],
595
+ metadata: {},
596
+ },
597
+ ],
598
+ };
599
+
600
+ const result = validateKGCMarkdown(doc);
601
+ expect(result.success).toBe(true);
602
+ });
603
+
604
+ it('should validate table node', () => {
605
+ const doc = {
606
+ type: 'document',
607
+ children: [
608
+ {
609
+ type: 'table',
610
+ headers: ['Column 1', 'Column 2'],
611
+ rows: [
612
+ ['A', 'B'],
613
+ ['C', 'D'],
614
+ ],
615
+ alignment: ['left', 'right'],
616
+ metadata: {},
617
+ },
618
+ ],
619
+ };
620
+
621
+ const result = validateKGCMarkdown(doc);
622
+ expect(result.success).toBe(true);
623
+ });
624
+
625
+ it('should reject invalid heading level', () => {
626
+ const doc = {
627
+ type: 'document',
628
+ children: [
629
+ {
630
+ type: 'heading',
631
+ level: 7, // Invalid, max is 6
632
+ content: 'Invalid heading',
633
+ metadata: {},
634
+ },
635
+ ],
636
+ };
637
+
638
+ const result = validateKGCMarkdown(doc);
639
+ expect(result.success).toBe(false);
640
+ });
641
+ });
642
+
643
+ // =============================================================================
644
+ // Integration Tests - Schema Composition
645
+ // =============================================================================
646
+
647
+ describe('Schema Integration', () => {
648
+ it('should compose RunCapsule with ToolTraceEntry and Receipt', () => {
649
+ const trace1 = {
650
+ id: '123e4567-e89b-12d3-a456-426614174000',
651
+ timestamp: 1703001600000,
652
+ toolName: 'Bash',
653
+ input: { command: 'npm test' },
654
+ duration: 2000,
655
+ status: 'success',
656
+ };
657
+
658
+ const trace2 = {
659
+ id: '223e4567-e89b-12d3-a456-426614174000',
660
+ timestamp: 1703001602000,
661
+ toolName: 'Write',
662
+ input: { file_path: '/home/user/file.txt', content: 'test' },
663
+ duration: 50,
664
+ status: 'success',
665
+ };
666
+
667
+ const receipt = {
668
+ id: '550e8400-e29b-41d4-a716-446655440000',
669
+ timestamp: 1703001610000,
670
+ runId: 'run-001',
671
+ actor: 'agent:test',
672
+ action: 'execute',
673
+ payload: {},
674
+ result: { success: true },
675
+ };
676
+
677
+ const capsule = {
678
+ id: 'run-001',
679
+ startTime: 1703001600000,
680
+ endTime: 1703001610000,
681
+ status: 'completed',
682
+ input: { task: 'Test task' },
683
+ output: { success: true, results: {} },
684
+ trace: [trace1, trace2],
685
+ actor: 'agent:test',
686
+ receipt,
687
+ };
688
+
689
+ const result = validateRunCapsule(capsule);
690
+ expect(result.success).toBe(true);
691
+ expect(result.data.trace.length).toBe(2);
692
+ expect(result.data.receipt).toBeDefined();
693
+ });
694
+
695
+ it('should compose WorkItem with Bounds', () => {
696
+ const bounds = {
697
+ maxFiles: 50,
698
+ maxBytes: 5242880,
699
+ maxOps: 500,
700
+ maxRuntime: 60000,
701
+ maxGraphRewrites: 25,
702
+ };
703
+
704
+ const workItem = {
705
+ id: '123e4567-e89b-12d3-a456-426614174000',
706
+ type: 'bounded_task',
707
+ state: 'running',
708
+ createdAt: 1703001600000,
709
+ payload: { bounds },
710
+ };
711
+
712
+ const result = validateWorkItem(workItem);
713
+ expect(result.success).toBe(true);
714
+ expect(result.data.payload.bounds.maxFiles).toBe(50);
715
+ });
716
+ });