@principles/core 1.281.0 → 1.281.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 (64) hide show
  1. package/dist/evolution-store.d.ts +4 -2
  2. package/dist/evolution-store.d.ts.map +1 -1
  3. package/dist/evolution-store.js +20 -2
  4. package/dist/evolution-store.js.map +1 -1
  5. package/dist/evolution-store.test.js +8 -4
  6. package/dist/evolution-store.test.js.map +1 -1
  7. package/dist/runtime-v2/__tests__/architecture-regression.test.js +19 -133
  8. package/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  9. package/dist/runtime-v2/__tests__/pain-signal-observability.test.js +5 -6
  10. package/dist/runtime-v2/__tests__/pain-signal-observability.test.js.map +1 -1
  11. package/dist/runtime-v2/evolution/evolution-types.d.ts +6 -304
  12. package/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  13. package/dist/runtime-v2/evolution/evolution-types.js +6 -173
  14. package/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  15. package/dist/runtime-v2/full-trace-contract.d.ts +1 -1
  16. package/dist/runtime-v2/full-trace-contract.js +1 -1
  17. package/dist/runtime-v2/index.d.ts +2 -12
  18. package/dist/runtime-v2/index.d.ts.map +1 -1
  19. package/dist/runtime-v2/index.js +1 -9
  20. package/dist/runtime-v2/index.js.map +1 -1
  21. package/dist/runtime-v2/pain-signal-observability.d.ts.map +1 -1
  22. package/dist/runtime-v2/pain-signal-observability.js +69 -63
  23. package/dist/runtime-v2/pain-signal-observability.js.map +1 -1
  24. package/dist/runtime-v2/types/index.d.ts +0 -2
  25. package/dist/runtime-v2/types/index.d.ts.map +1 -1
  26. package/dist/runtime-v2/types/index.js +0 -1
  27. package/dist/runtime-v2/types/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.d.ts +0 -2
  30. package/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.d.ts.map +0 -1
  31. package/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.js +0 -759
  32. package/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.js.map +0 -1
  33. package/dist/runtime-v2/__tests__/trace-refiner-agent.test.d.ts +0 -2
  34. package/dist/runtime-v2/__tests__/trace-refiner-agent.test.d.ts.map +0 -1
  35. package/dist/runtime-v2/__tests__/trace-refiner-agent.test.js +0 -726
  36. package/dist/runtime-v2/__tests__/trace-refiner-agent.test.js.map +0 -1
  37. package/dist/runtime-v2/__tests__/trace-refiner.test.d.ts +0 -2
  38. package/dist/runtime-v2/__tests__/trace-refiner.test.d.ts.map +0 -1
  39. package/dist/runtime-v2/__tests__/trace-refiner.test.js +0 -527
  40. package/dist/runtime-v2/__tests__/trace-refiner.test.js.map +0 -1
  41. package/dist/runtime-v2/evolution/index.d.ts +0 -2
  42. package/dist/runtime-v2/evolution/index.d.ts.map +0 -1
  43. package/dist/runtime-v2/evolution/index.js +0 -2
  44. package/dist/runtime-v2/evolution/index.js.map +0 -1
  45. package/dist/runtime-v2/golden-trace-candidate-builder.d.ts +0 -23
  46. package/dist/runtime-v2/golden-trace-candidate-builder.d.ts.map +0 -1
  47. package/dist/runtime-v2/golden-trace-candidate-builder.js +0 -193
  48. package/dist/runtime-v2/golden-trace-candidate-builder.js.map +0 -1
  49. package/dist/runtime-v2/golden-trace-replay-adapter.d.ts +0 -18
  50. package/dist/runtime-v2/golden-trace-replay-adapter.d.ts.map +0 -1
  51. package/dist/runtime-v2/golden-trace-replay-adapter.js +0 -18
  52. package/dist/runtime-v2/golden-trace-replay-adapter.js.map +0 -1
  53. package/dist/runtime-v2/trace-refiner-agent.d.ts +0 -71
  54. package/dist/runtime-v2/trace-refiner-agent.d.ts.map +0 -1
  55. package/dist/runtime-v2/trace-refiner-agent.js +0 -239
  56. package/dist/runtime-v2/trace-refiner-agent.js.map +0 -1
  57. package/dist/runtime-v2/trace-refiner.d.ts +0 -47
  58. package/dist/runtime-v2/trace-refiner.d.ts.map +0 -1
  59. package/dist/runtime-v2/trace-refiner.js +0 -261
  60. package/dist/runtime-v2/trace-refiner.js.map +0 -1
  61. package/dist/runtime-v2/types/queue-types.d.ts +0 -51
  62. package/dist/runtime-v2/types/queue-types.d.ts.map +0 -1
  63. package/dist/runtime-v2/types/queue-types.js +0 -44
  64. package/dist/runtime-v2/types/queue-types.js.map +0 -1
@@ -1,759 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { buildGoldenTraceCandidate } from '../golden-trace-candidate-builder.js';
3
- import { validateGoldenTrace } from '../golden-trace.js';
4
- import { refineFullTrace } from '../trace-refiner.js';
5
- import { replayGoldenTrace } from '../golden-trace-replay-validator.js';
6
- function makeFullTrace(overrides = {}) {
7
- return {
8
- sourceTaskId: 'task-001',
9
- sourcePainId: 'pain-001',
10
- sourceRunIds: ['run-001'],
11
- capturedAt: '2026-05-19T12:00:00.000Z',
12
- sourceRefs: [
13
- { kind: 'task', id: 'task-001' },
14
- { kind: 'run', id: 'run-001' },
15
- ],
16
- timeline: [],
17
- ambiguityNotes: [],
18
- sanitizationNotes: [],
19
- ...overrides,
20
- };
21
- }
22
- function makeRefinedTrace(overrides = {}) {
23
- return {
24
- sourceTaskId: 'task-001',
25
- sourcePainId: 'pain-001',
26
- sourceRunIds: ['run-001'],
27
- evidenceRefs: ['task:task-001', 'run:run-001'],
28
- keyEvents: [],
29
- failureSummary: null,
30
- toolUseSummary: [],
31
- userIntentSummary: null,
32
- ambiguityNotes: [],
33
- sanitizationNotes: [],
34
- refinementNotes: [],
35
- ...overrides,
36
- };
37
- }
38
- describe('buildGoldenTraceCandidate', () => {
39
- it('returns insufficient_evidence when no failure evidence exists', () => {
40
- const fullTrace = makeFullTrace({
41
- timeline: [
42
- {
43
- at: '2026-05-19T12:00:01.000Z',
44
- kind: 'tool_call',
45
- summary: 'Read (completed)',
46
- rawPreview: '{"file_path":"/src/main.ts"}',
47
- metadata: { toolName: 'Read', status: 'completed' },
48
- },
49
- ],
50
- });
51
- const refinedTrace = makeRefinedTrace({
52
- keyEvents: [
53
- {
54
- kind: 'tool_use',
55
- summary: 'Read (completed)',
56
- evidenceRefs: ['task:task-001'],
57
- severity: 'low',
58
- at: '2026-05-19T12:00:01.000Z',
59
- },
60
- ],
61
- });
62
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
63
- expect(result.decision).toBe('insufficient_evidence');
64
- if (result.decision === 'insufficient_evidence') {
65
- expect(result.reasons.length).toBeGreaterThan(0);
66
- expect(result.reasons.some((r) => r.includes('failure') || r.includes('negative'))).toBe(true);
67
- }
68
- });
69
- it('returns insufficient_evidence when no positive comparator exists', () => {
70
- const fullTrace = makeFullTrace({
71
- timeline: [
72
- {
73
- at: '2026-05-19T12:00:01.000Z',
74
- kind: 'tool_call',
75
- summary: 'Write (failed)',
76
- rawPreview: '{"file_path":"/etc/passwd","content":"root::0:0"}',
77
- metadata: { toolName: 'Write', status: 'failed' },
78
- },
79
- {
80
- at: '2026-05-19T12:00:02.000Z',
81
- kind: 'tool_result',
82
- summary: 'Error from Write',
83
- rawPreview: 'Permission denied',
84
- metadata: { toolName: 'Write', error: 'Permission denied' },
85
- },
86
- ],
87
- });
88
- const refinedTrace = makeRefinedTrace({
89
- keyEvents: [
90
- {
91
- kind: 'failure',
92
- summary: 'Write (failed)',
93
- evidenceRefs: ['task:task-001'],
94
- severity: 'high',
95
- at: '2026-05-19T12:00:01.000Z',
96
- },
97
- ],
98
- failureSummary: 'Write: Permission denied',
99
- });
100
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
101
- expect(result.decision).toBe('insufficient_evidence');
102
- if (result.decision === 'insufficient_evidence') {
103
- expect(result.reasons.some((r) => r.includes('positive') || r.includes('comparator'))).toBe(true);
104
- }
105
- });
106
- it('returns insufficient_evidence when tool params are missing', () => {
107
- const fullTrace = makeFullTrace({
108
- timeline: [
109
- {
110
- at: '2026-05-19T12:00:01.000Z',
111
- kind: 'tool_call',
112
- summary: 'Bash (failed)',
113
- metadata: { toolName: 'Bash', status: 'failed', error: 'timeout' },
114
- },
115
- {
116
- at: '2026-05-19T12:00:02.000Z',
117
- kind: 'tool_call',
118
- summary: 'Read (completed)',
119
- rawPreview: '{"file_path":"/src/main.ts"}',
120
- metadata: { toolName: 'Read', status: 'completed' },
121
- },
122
- ],
123
- });
124
- const refinedTrace = makeRefinedTrace({
125
- keyEvents: [
126
- {
127
- kind: 'failure',
128
- summary: 'Bash (failed)',
129
- evidenceRefs: ['task:task-001'],
130
- severity: 'high',
131
- at: '2026-05-19T12:00:01.000Z',
132
- },
133
- {
134
- kind: 'tool_use',
135
- summary: 'Read (completed)',
136
- evidenceRefs: ['task:task-001'],
137
- severity: 'low',
138
- at: '2026-05-19T12:00:02.000Z',
139
- },
140
- ],
141
- failureSummary: 'Bash: timeout',
142
- });
143
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
144
- expect(result.decision).toBe('insufficient_evidence');
145
- if (result.decision === 'insufficient_evidence') {
146
- expect(result.reasons.some((r) => r.includes('params') || r.includes('param'))).toBe(true);
147
- }
148
- });
149
- it('creates candidate from failing tool call with positive comparator', () => {
150
- const fullTrace = makeFullTrace({
151
- timeline: [
152
- {
153
- at: '2026-05-19T12:00:01.000Z',
154
- kind: 'tool_call',
155
- summary: 'Bash (failed)',
156
- rawPreview: '{"command":"rm -rf /"}',
157
- metadata: { toolName: 'Bash', status: 'failed', error: 'Permission denied' },
158
- },
159
- {
160
- at: '2026-05-19T12:00:02.000Z',
161
- kind: 'tool_result',
162
- summary: 'Error from Bash',
163
- rawPreview: 'Permission denied',
164
- metadata: { toolName: 'Bash', error: 'Permission denied' },
165
- },
166
- {
167
- at: '2026-05-19T12:00:03.000Z',
168
- kind: 'tool_call',
169
- summary: 'Read (completed)',
170
- rawPreview: '{"file_path":"/src/main.ts"}',
171
- metadata: { toolName: 'Read', status: 'completed' },
172
- },
173
- ],
174
- });
175
- const refinedTrace = makeRefinedTrace({
176
- keyEvents: [
177
- {
178
- kind: 'failure',
179
- summary: 'Bash (failed)',
180
- evidenceRefs: ['task:task-001', 'run:run-001'],
181
- severity: 'high',
182
- at: '2026-05-19T12:00:01.000Z',
183
- },
184
- {
185
- kind: 'tool_use',
186
- summary: 'Read (completed)',
187
- evidenceRefs: ['task:task-001', 'run:run-001'],
188
- severity: 'low',
189
- at: '2026-05-19T12:00:03.000Z',
190
- },
191
- ],
192
- failureSummary: 'Bash: Permission denied',
193
- });
194
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
195
- expect(result.decision).toBe('candidate_created');
196
- if (result.decision === 'candidate_created') {
197
- expect(result.goldenTrace).toBeDefined();
198
- expect(result.evidenceRefs.length).toBeGreaterThan(0);
199
- expect(result.builderNotes.length).toBeGreaterThanOrEqual(0);
200
- }
201
- });
202
- it('generated GoldenTrace passes validateGoldenTrace', () => {
203
- const fullTrace = makeFullTrace({
204
- timeline: [
205
- {
206
- at: '2026-05-19T12:00:01.000Z',
207
- kind: 'tool_call',
208
- summary: 'Bash (failed)',
209
- rawPreview: '{"command":"rm -rf /"}',
210
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous command' },
211
- },
212
- {
213
- at: '2026-05-19T12:00:02.000Z',
214
- kind: 'tool_result',
215
- summary: 'Error from Bash',
216
- metadata: { toolName: 'Bash', error: 'dangerous command' },
217
- },
218
- {
219
- at: '2026-05-19T12:00:03.000Z',
220
- kind: 'tool_call',
221
- summary: 'Read (completed)',
222
- rawPreview: '{"file_path":"/src/main.ts"}',
223
- metadata: { toolName: 'Read', status: 'completed' },
224
- },
225
- ],
226
- });
227
- const refinedTrace = makeRefinedTrace({
228
- keyEvents: [
229
- {
230
- kind: 'failure',
231
- summary: 'Bash (failed)',
232
- evidenceRefs: ['task:task-001'],
233
- severity: 'high',
234
- at: '2026-05-19T12:00:01.000Z',
235
- },
236
- {
237
- kind: 'tool_use',
238
- summary: 'Read (completed)',
239
- evidenceRefs: ['task:task-001'],
240
- severity: 'low',
241
- at: '2026-05-19T12:00:03.000Z',
242
- },
243
- ],
244
- failureSummary: 'Bash: dangerous command',
245
- });
246
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
247
- expect(result.decision).toBe('candidate_created');
248
- if (result.decision === 'candidate_created') {
249
- const validation = validateGoldenTrace(result.goldenTrace);
250
- expect(validation.valid).toBe(true);
251
- expect(validation.errors).toHaveLength(0);
252
- }
253
- });
254
- it('preserves source refs in every generated case', () => {
255
- const fullTrace = makeFullTrace({
256
- sourceTaskId: 'task-042',
257
- sourcePainId: 'pain-042',
258
- timeline: [
259
- {
260
- at: '2026-05-19T12:00:01.000Z',
261
- kind: 'tool_call',
262
- summary: 'Write (failed)',
263
- rawPreview: '{"file_path":"/etc/shadow","content":"root:x:0:0"}',
264
- metadata: { toolName: 'Write', status: 'failed', error: 'permission denied' },
265
- },
266
- {
267
- at: '2026-05-19T12:00:02.000Z',
268
- kind: 'tool_result',
269
- summary: 'Error from Write',
270
- metadata: { toolName: 'Write', error: 'permission denied' },
271
- },
272
- {
273
- at: '2026-05-19T12:00:03.000Z',
274
- kind: 'tool_call',
275
- summary: 'Read (completed)',
276
- rawPreview: '{"file_path":"/src/app.ts"}',
277
- metadata: { toolName: 'Read', status: 'completed' },
278
- },
279
- ],
280
- });
281
- const refinedTrace = makeRefinedTrace({
282
- sourceTaskId: 'task-042',
283
- sourcePainId: 'pain-042',
284
- keyEvents: [
285
- {
286
- kind: 'failure',
287
- summary: 'Write (failed)',
288
- evidenceRefs: ['task:task-042'],
289
- severity: 'high',
290
- at: '2026-05-19T12:00:01.000Z',
291
- },
292
- {
293
- kind: 'tool_use',
294
- summary: 'Read (completed)',
295
- evidenceRefs: ['task:task-042'],
296
- severity: 'low',
297
- at: '2026-05-19T12:00:03.000Z',
298
- },
299
- ],
300
- failureSummary: 'Write: permission denied',
301
- });
302
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
303
- expect(result.decision).toBe('candidate_created');
304
- if (result.decision === 'candidate_created') {
305
- for (const c of result.goldenTrace.cases) {
306
- expect(c.sourceRefs).toBeDefined();
307
- expect(c.sourceRefs?.painId).toBe('pain-042');
308
- }
309
- expect(result.goldenTrace.sourcePainId).toBe('pain-042');
310
- }
311
- });
312
- it('replay validator can consume generated candidate in deterministic mode', () => {
313
- const fullTrace = makeFullTrace({
314
- timeline: [
315
- {
316
- at: '2026-05-19T12:00:01.000Z',
317
- kind: 'tool_call',
318
- summary: 'Bash (failed)',
319
- rawPreview: '{"command":"rm -rf /"}',
320
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
321
- },
322
- {
323
- at: '2026-05-19T12:00:02.000Z',
324
- kind: 'tool_result',
325
- summary: 'Error from Bash',
326
- metadata: { toolName: 'Bash', error: 'dangerous' },
327
- },
328
- {
329
- at: '2026-05-19T12:00:03.000Z',
330
- kind: 'tool_call',
331
- summary: 'Read (completed)',
332
- rawPreview: '{"file_path":"/src/main.ts"}',
333
- metadata: { toolName: 'Read', status: 'completed' },
334
- },
335
- ],
336
- });
337
- const refinedTrace = makeRefinedTrace({
338
- keyEvents: [
339
- {
340
- kind: 'failure',
341
- summary: 'Bash (failed)',
342
- evidenceRefs: ['task:task-001'],
343
- severity: 'high',
344
- at: '2026-05-19T12:00:01.000Z',
345
- },
346
- {
347
- kind: 'tool_use',
348
- summary: 'Read (completed)',
349
- evidenceRefs: ['task:task-001'],
350
- severity: 'low',
351
- at: '2026-05-19T12:00:03.000Z',
352
- },
353
- ],
354
- failureSummary: 'Bash: dangerous',
355
- });
356
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
357
- expect(result.decision).toBe('candidate_created');
358
- if (result.decision === 'candidate_created') {
359
- const blockBashEvaluate = () => ({
360
- decision: 'block',
361
- matched: true,
362
- reason: 'dangerous command',
363
- confidence: 0.95,
364
- });
365
- const replayResult = replayGoldenTrace(blockBashEvaluate, result.goldenTrace.cases);
366
- expect(replayResult.totalCases).toBe(result.goldenTrace.cases.length);
367
- expect(typeof replayResult.passed).toBe('boolean');
368
- }
369
- });
370
- it('generated IDs are deterministic for same input', () => {
371
- const fullTrace = makeFullTrace({
372
- timeline: [
373
- {
374
- at: '2026-05-19T12:00:01.000Z',
375
- kind: 'tool_call',
376
- summary: 'Bash (failed)',
377
- rawPreview: '{"command":"rm -rf /"}',
378
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
379
- },
380
- {
381
- at: '2026-05-19T12:00:02.000Z',
382
- kind: 'tool_result',
383
- summary: 'Error from Bash',
384
- metadata: { toolName: 'Bash', error: 'dangerous' },
385
- },
386
- {
387
- at: '2026-05-19T12:00:03.000Z',
388
- kind: 'tool_call',
389
- summary: 'Read (completed)',
390
- rawPreview: '{"file_path":"/src/main.ts"}',
391
- metadata: { toolName: 'Read', status: 'completed' },
392
- },
393
- ],
394
- });
395
- const refinedTrace = makeRefinedTrace({
396
- keyEvents: [
397
- {
398
- kind: 'failure',
399
- summary: 'Bash (failed)',
400
- evidenceRefs: ['task:task-001'],
401
- severity: 'high',
402
- at: '2026-05-19T12:00:01.000Z',
403
- },
404
- {
405
- kind: 'tool_use',
406
- summary: 'Read (completed)',
407
- evidenceRefs: ['task:task-001'],
408
- severity: 'low',
409
- at: '2026-05-19T12:00:03.000Z',
410
- },
411
- ],
412
- failureSummary: 'Bash: dangerous',
413
- });
414
- const result1 = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
415
- const result2 = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
416
- expect(result1.decision).toBe('candidate_created');
417
- expect(result2.decision).toBe('candidate_created');
418
- if (result1.decision === 'candidate_created' && result2.decision === 'candidate_created') {
419
- expect(result1.goldenTrace.traceId).toBe(result2.goldenTrace.traceId);
420
- expect(result1.goldenTrace.cases.map((c) => c.caseId)).toEqual(result2.goldenTrace.cases.map((c) => c.caseId));
421
- }
422
- });
423
- it('ambiguous/refinement notes are preserved in builder notes', () => {
424
- const fullTrace = makeFullTrace({
425
- timeline: [
426
- {
427
- at: '2026-05-19T12:00:01.000Z',
428
- kind: 'tool_call',
429
- summary: 'Bash (failed)',
430
- rawPreview: '{"command":"rm -rf /"}',
431
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
432
- },
433
- {
434
- at: '2026-05-19T12:00:02.000Z',
435
- kind: 'tool_result',
436
- summary: 'Error from Bash',
437
- metadata: { toolName: 'Bash', error: 'dangerous' },
438
- },
439
- {
440
- at: '2026-05-19T12:00:03.000Z',
441
- kind: 'tool_call',
442
- summary: 'Read (completed)',
443
- rawPreview: '{"file_path":"/src/main.ts"}',
444
- metadata: { toolName: 'Read', status: 'completed' },
445
- },
446
- ],
447
- ambiguityNotes: ['source_trace_ambiguous: multiple runs found'],
448
- });
449
- const refinedTrace = makeRefinedTrace({
450
- keyEvents: [
451
- {
452
- kind: 'failure',
453
- summary: 'Bash (failed)',
454
- evidenceRefs: ['task:task-001'],
455
- severity: 'high',
456
- at: '2026-05-19T12:00:01.000Z',
457
- },
458
- {
459
- kind: 'tool_use',
460
- summary: 'Read (completed)',
461
- evidenceRefs: ['task:task-001'],
462
- severity: 'low',
463
- at: '2026-05-19T12:00:03.000Z',
464
- },
465
- ],
466
- failureSummary: 'Bash: dangerous',
467
- ambiguityNotes: ['source_trace_ambiguous: multiple runs found'],
468
- refinementNotes: ['sanitized_input_present'],
469
- });
470
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
471
- expect(result.decision).toBe('candidate_created');
472
- if (result.decision === 'candidate_created') {
473
- expect(result.builderNotes.some((n) => n.includes('ambiguous') || n.includes('refinement'))).toBe(true);
474
- }
475
- });
476
- it('negative case has block expectedDecision for failing tool call', () => {
477
- const fullTrace = makeFullTrace({
478
- timeline: [
479
- {
480
- at: '2026-05-19T12:00:01.000Z',
481
- kind: 'tool_call',
482
- summary: 'Bash (failed)',
483
- rawPreview: '{"command":"rm -rf /"}',
484
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
485
- },
486
- {
487
- at: '2026-05-19T12:00:02.000Z',
488
- kind: 'tool_result',
489
- summary: 'Error from Bash',
490
- metadata: { toolName: 'Bash', error: 'dangerous' },
491
- },
492
- {
493
- at: '2026-05-19T12:00:03.000Z',
494
- kind: 'tool_call',
495
- summary: 'Read (completed)',
496
- rawPreview: '{"file_path":"/src/main.ts"}',
497
- metadata: { toolName: 'Read', status: 'completed' },
498
- },
499
- ],
500
- });
501
- const refinedTrace = makeRefinedTrace({
502
- keyEvents: [
503
- {
504
- kind: 'failure',
505
- summary: 'Bash (failed)',
506
- evidenceRefs: ['task:task-001'],
507
- severity: 'high',
508
- at: '2026-05-19T12:00:01.000Z',
509
- },
510
- {
511
- kind: 'tool_use',
512
- summary: 'Read (completed)',
513
- evidenceRefs: ['task:task-001'],
514
- severity: 'low',
515
- at: '2026-05-19T12:00:03.000Z',
516
- },
517
- ],
518
- failureSummary: 'Bash: dangerous',
519
- });
520
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
521
- expect(result.decision).toBe('candidate_created');
522
- if (result.decision === 'candidate_created') {
523
- const negativeCases = result.goldenTrace.cases.filter((c) => c.kind === 'negative');
524
- expect(negativeCases.length).toBeGreaterThan(0);
525
- for (const nc of negativeCases) {
526
- expect(nc.expectedDecision).toBe('block');
527
- expect(nc.toolName).toBe('Bash');
528
- expect(nc.params).toBeDefined();
529
- expect(typeof nc.params).toBe('object');
530
- }
531
- }
532
- });
533
- it('positive case has allow expectedDecision for non-failing tool call', () => {
534
- const fullTrace = makeFullTrace({
535
- timeline: [
536
- {
537
- at: '2026-05-19T12:00:01.000Z',
538
- kind: 'tool_call',
539
- summary: 'Bash (failed)',
540
- rawPreview: '{"command":"rm -rf /"}',
541
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
542
- },
543
- {
544
- at: '2026-05-19T12:00:02.000Z',
545
- kind: 'tool_result',
546
- summary: 'Error from Bash',
547
- metadata: { toolName: 'Bash', error: 'dangerous' },
548
- },
549
- {
550
- at: '2026-05-19T12:00:03.000Z',
551
- kind: 'tool_call',
552
- summary: 'Read (completed)',
553
- rawPreview: '{"file_path":"/src/main.ts"}',
554
- metadata: { toolName: 'Read', status: 'completed' },
555
- },
556
- ],
557
- });
558
- const refinedTrace = makeRefinedTrace({
559
- keyEvents: [
560
- {
561
- kind: 'failure',
562
- summary: 'Bash (failed)',
563
- evidenceRefs: ['task:task-001'],
564
- severity: 'high',
565
- at: '2026-05-19T12:00:01.000Z',
566
- },
567
- {
568
- kind: 'tool_use',
569
- summary: 'Read (completed)',
570
- evidenceRefs: ['task:task-001'],
571
- severity: 'low',
572
- at: '2026-05-19T12:00:03.000Z',
573
- },
574
- ],
575
- failureSummary: 'Bash: dangerous',
576
- });
577
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
578
- expect(result.decision).toBe('candidate_created');
579
- if (result.decision === 'candidate_created') {
580
- const positiveCases = result.goldenTrace.cases.filter((c) => c.kind === 'positive');
581
- expect(positiveCases.length).toBeGreaterThan(0);
582
- for (const pc of positiveCases) {
583
- expect(pc.expectedDecision).toBe('allow');
584
- expect(pc.toolName).toBeDefined();
585
- expect(pc.params).toBeDefined();
586
- }
587
- }
588
- });
589
- it('works with real refineFullTrace output', () => {
590
- const fullTrace = makeFullTrace({
591
- timeline: [
592
- {
593
- at: '2026-05-19T12:00:01.000Z',
594
- kind: 'tool_call',
595
- summary: 'Bash (failed)',
596
- rawPreview: '{"command":"rm -rf /"}',
597
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous command blocked' },
598
- },
599
- {
600
- at: '2026-05-19T12:00:02.000Z',
601
- kind: 'tool_result',
602
- summary: 'Error from Bash',
603
- rawPreview: 'dangerous command blocked',
604
- metadata: { toolName: 'Bash', error: 'dangerous command blocked' },
605
- },
606
- {
607
- at: '2026-05-19T12:00:03.000Z',
608
- kind: 'tool_call',
609
- summary: 'Read (completed)',
610
- rawPreview: '{"file_path":"/src/main.ts"}',
611
- metadata: { toolName: 'Read', status: 'completed' },
612
- },
613
- ],
614
- });
615
- const refinedTrace = refineFullTrace(fullTrace);
616
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
617
- expect(result.decision).toBe('candidate_created');
618
- if (result.decision === 'candidate_created') {
619
- const validation = validateGoldenTrace(result.goldenTrace);
620
- expect(validation.valid).toBe(true);
621
- }
622
- });
623
- it('returns insufficient_evidence when timeline is empty', () => {
624
- const fullTrace = makeFullTrace({ timeline: [] });
625
- const refinedTrace = makeRefinedTrace({ keyEvents: [], refinementNotes: ['empty_timeline'] });
626
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
627
- expect(result.decision).toBe('insufficient_evidence');
628
- if (result.decision === 'insufficient_evidence') {
629
- expect(result.reasons.length).toBeGreaterThan(0);
630
- }
631
- });
632
- it('candidate_created includes missing_params in builderNotes when some tool calls lack params', () => {
633
- const fullTrace = makeFullTrace({
634
- timeline: [
635
- {
636
- at: '2026-05-19T12:00:01.000Z',
637
- kind: 'tool_call',
638
- summary: 'Bash (failed)',
639
- rawPreview: '{"command":"rm -rf /"}',
640
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
641
- },
642
- {
643
- at: '2026-05-19T12:00:02.000Z',
644
- kind: 'tool_result',
645
- summary: 'Error from Bash',
646
- metadata: { toolName: 'Bash', error: 'dangerous' },
647
- },
648
- {
649
- at: '2026-05-19T12:00:03.000Z',
650
- kind: 'tool_call',
651
- summary: 'Grep (failed)',
652
- metadata: { toolName: 'Grep', status: 'failed', error: 'not found' },
653
- },
654
- {
655
- at: '2026-05-19T12:00:04.000Z',
656
- kind: 'tool_call',
657
- summary: 'Read (completed)',
658
- rawPreview: '{"file_path":"/src/main.ts"}',
659
- metadata: { toolName: 'Read', status: 'completed' },
660
- },
661
- ],
662
- });
663
- const refinedTrace = makeRefinedTrace({
664
- keyEvents: [
665
- {
666
- kind: 'failure',
667
- summary: 'Bash (failed)',
668
- evidenceRefs: ['task:task-001'],
669
- severity: 'high',
670
- at: '2026-05-19T12:00:01.000Z',
671
- },
672
- {
673
- kind: 'tool_use',
674
- summary: 'Read (completed)',
675
- evidenceRefs: ['task:task-001'],
676
- severity: 'low',
677
- at: '2026-05-19T12:00:04.000Z',
678
- },
679
- ],
680
- failureSummary: 'Bash: dangerous',
681
- });
682
- const result = buildGoldenTraceCandidate({ fullTrace, refinedTrace });
683
- expect(result.decision).toBe('candidate_created');
684
- if (result.decision === 'candidate_created') {
685
- expect(result.builderNotes.some((n) => n.includes('missing_params'))).toBe(true);
686
- expect(result.builderNotes.some((n) => n.includes('Grep'))).toBe(true);
687
- }
688
- });
689
- it('uses provided createdAt when given', () => {
690
- const fullTrace = makeFullTrace({
691
- timeline: [
692
- {
693
- at: '2026-05-19T12:00:01.000Z',
694
- kind: 'tool_call',
695
- summary: 'Bash (failed)',
696
- rawPreview: '{"command":"rm -rf /"}',
697
- metadata: { toolName: 'Bash', status: 'failed', error: 'dangerous' },
698
- },
699
- {
700
- at: '2026-05-19T12:00:02.000Z',
701
- kind: 'tool_result',
702
- summary: 'Error from Bash',
703
- metadata: { toolName: 'Bash', error: 'dangerous' },
704
- },
705
- {
706
- at: '2026-05-19T12:00:03.000Z',
707
- kind: 'tool_call',
708
- summary: 'Read (completed)',
709
- rawPreview: '{"file_path":"/src/main.ts"}',
710
- metadata: { toolName: 'Read', status: 'completed' },
711
- },
712
- ],
713
- });
714
- const refinedTrace = makeRefinedTrace({
715
- keyEvents: [
716
- {
717
- kind: 'failure',
718
- summary: 'Bash (failed)',
719
- evidenceRefs: ['task:task-001'],
720
- severity: 'high',
721
- at: '2026-05-19T12:00:01.000Z',
722
- },
723
- {
724
- kind: 'tool_use',
725
- summary: 'Read (completed)',
726
- evidenceRefs: ['task:task-001'],
727
- severity: 'low',
728
- at: '2026-05-19T12:00:03.000Z',
729
- },
730
- ],
731
- failureSummary: 'Bash: dangerous',
732
- });
733
- const result = buildGoldenTraceCandidate({
734
- fullTrace,
735
- refinedTrace,
736
- createdAt: '2026-06-01T00:00:00.000Z',
737
- });
738
- expect(result.decision).toBe('candidate_created');
739
- if (result.decision === 'candidate_created') {
740
- expect(result.goldenTrace.createdAt).toBe('2026-06-01T00:00:00.000Z');
741
- }
742
- });
743
- });
744
- describe('buildGoldenTraceCandidate architecture guard', () => {
745
- it('builder module has no plugin import', async () => {
746
- const fs = await import('node:fs');
747
- const path = await import('node:path');
748
- const src = fs.readFileSync(path.resolve(__dirname, '../golden-trace-candidate-builder.ts'), 'utf-8');
749
- expect(src).not.toContain('openclaw-plugin');
750
- expect(src).not.toContain("from 'fs'");
751
- expect(src).not.toContain("from 'path'");
752
- expect(src).not.toContain("from 'node:fs'");
753
- expect(src).not.toContain("from 'node:path'");
754
- expect(src).not.toContain("from 'node:process'");
755
- expect(src).not.toContain("from 'net'");
756
- expect(src).not.toContain("from 'http'");
757
- });
758
- });
759
- //# sourceMappingURL=golden-trace-candidate-builder.test.js.map