create-principles-disciple 1.138.2 → 1.139.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 (103) hide show
  1. package/console/dist/server/config/pd-config-store.js +9 -2
  2. package/core/dist/evolution-store.d.ts +4 -2
  3. package/core/dist/evolution-store.d.ts.map +1 -1
  4. package/core/dist/evolution-store.js +20 -2
  5. package/core/dist/evolution-store.js.map +1 -1
  6. package/core/dist/evolution-store.test.js +8 -4
  7. package/core/dist/evolution-store.test.js.map +1 -1
  8. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +19 -133
  9. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  10. package/core/dist/runtime-v2/__tests__/pain-signal-observability.test.js +5 -6
  11. package/core/dist/runtime-v2/__tests__/pain-signal-observability.test.js.map +1 -1
  12. package/core/dist/runtime-v2/config/__tests__/pd-config-reasoning.test.d.ts +2 -0
  13. package/core/dist/runtime-v2/config/__tests__/pd-config-reasoning.test.d.ts.map +1 -0
  14. package/core/dist/runtime-v2/config/__tests__/pd-config-reasoning.test.js +99 -0
  15. package/core/dist/runtime-v2/config/__tests__/pd-config-reasoning.test.js.map +1 -0
  16. package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts +2 -0
  17. package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts.map +1 -1
  18. package/core/dist/runtime-v2/config/pd-config-agent-binding.js +3 -0
  19. package/core/dist/runtime-v2/config/pd-config-agent-binding.js.map +1 -1
  20. package/core/dist/runtime-v2/config/pd-config-types.d.ts +8 -0
  21. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
  22. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
  23. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
  24. package/core/dist/runtime-v2/config/pd-config-validate.js +10 -0
  25. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
  26. package/core/dist/runtime-v2/evolution/evolution-types.d.ts +6 -304
  27. package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  28. package/core/dist/runtime-v2/evolution/evolution-types.js +6 -173
  29. package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  30. package/core/dist/runtime-v2/full-trace-contract.d.ts +1 -1
  31. package/core/dist/runtime-v2/full-trace-contract.js +1 -1
  32. package/core/dist/runtime-v2/index.d.ts +2 -12
  33. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  34. package/core/dist/runtime-v2/index.js +1 -9
  35. package/core/dist/runtime-v2/index.js.map +1 -1
  36. package/core/dist/runtime-v2/pain-signal-observability.d.ts.map +1 -1
  37. package/core/dist/runtime-v2/pain-signal-observability.js +69 -63
  38. package/core/dist/runtime-v2/pain-signal-observability.js.map +1 -1
  39. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +2 -0
  40. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  41. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +2 -0
  42. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  43. package/core/dist/runtime-v2/types/index.d.ts +0 -2
  44. package/core/dist/runtime-v2/types/index.d.ts.map +1 -1
  45. package/core/dist/runtime-v2/types/index.js +0 -1
  46. package/core/dist/runtime-v2/types/index.js.map +1 -1
  47. package/core/package.json +1 -1
  48. package/host-runtime/dist/internalization-consumer-cycle.js +3 -0
  49. package/host-runtime/package.json +1 -1
  50. package/package.json +2 -2
  51. package/pd-cli/dist/commands/runtime-init.js +1 -1
  52. package/pd-cli/dist/commands/runtime-init.js.map +1 -1
  53. package/pd-cli/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
  54. package/pd-cli/dist/services/runtime-adapter-resolver.js +2 -0
  55. package/pd-cli/dist/services/runtime-adapter-resolver.js.map +1 -1
  56. package/pd-cli/package.json +1 -1
  57. package/plugin/dist/bundle.js +483 -571
  58. package/plugin/dist/core/event-log.d.ts +1 -3
  59. package/plugin/dist/core/evolution-types.d.ts +9 -32
  60. package/plugin/dist/core/trajectory.d.ts +10 -4
  61. package/plugin/dist/governance-audit.js +204 -204
  62. package/plugin/dist/rulehost-evidence.js +251 -306
  63. package/plugin/dist/types/event-types.d.ts +1 -1
  64. package/plugin/openclaw.plugin.json +1 -1
  65. package/plugin/package.json +1 -1
  66. package/release-manager/package.json +2 -2
  67. package/core/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.d.ts +0 -2
  68. package/core/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.d.ts.map +0 -1
  69. package/core/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.js +0 -759
  70. package/core/dist/runtime-v2/__tests__/golden-trace-candidate-builder.test.js.map +0 -1
  71. package/core/dist/runtime-v2/__tests__/trace-refiner-agent.test.d.ts +0 -2
  72. package/core/dist/runtime-v2/__tests__/trace-refiner-agent.test.d.ts.map +0 -1
  73. package/core/dist/runtime-v2/__tests__/trace-refiner-agent.test.js +0 -726
  74. package/core/dist/runtime-v2/__tests__/trace-refiner-agent.test.js.map +0 -1
  75. package/core/dist/runtime-v2/__tests__/trace-refiner.test.d.ts +0 -2
  76. package/core/dist/runtime-v2/__tests__/trace-refiner.test.d.ts.map +0 -1
  77. package/core/dist/runtime-v2/__tests__/trace-refiner.test.js +0 -527
  78. package/core/dist/runtime-v2/__tests__/trace-refiner.test.js.map +0 -1
  79. package/core/dist/runtime-v2/evolution/index.d.ts +0 -2
  80. package/core/dist/runtime-v2/evolution/index.d.ts.map +0 -1
  81. package/core/dist/runtime-v2/evolution/index.js +0 -2
  82. package/core/dist/runtime-v2/evolution/index.js.map +0 -1
  83. package/core/dist/runtime-v2/golden-trace-candidate-builder.d.ts +0 -23
  84. package/core/dist/runtime-v2/golden-trace-candidate-builder.d.ts.map +0 -1
  85. package/core/dist/runtime-v2/golden-trace-candidate-builder.js +0 -193
  86. package/core/dist/runtime-v2/golden-trace-candidate-builder.js.map +0 -1
  87. package/core/dist/runtime-v2/golden-trace-replay-adapter.d.ts +0 -18
  88. package/core/dist/runtime-v2/golden-trace-replay-adapter.d.ts.map +0 -1
  89. package/core/dist/runtime-v2/golden-trace-replay-adapter.js +0 -18
  90. package/core/dist/runtime-v2/golden-trace-replay-adapter.js.map +0 -1
  91. package/core/dist/runtime-v2/trace-refiner-agent.d.ts +0 -71
  92. package/core/dist/runtime-v2/trace-refiner-agent.d.ts.map +0 -1
  93. package/core/dist/runtime-v2/trace-refiner-agent.js +0 -239
  94. package/core/dist/runtime-v2/trace-refiner-agent.js.map +0 -1
  95. package/core/dist/runtime-v2/trace-refiner.d.ts +0 -47
  96. package/core/dist/runtime-v2/trace-refiner.d.ts.map +0 -1
  97. package/core/dist/runtime-v2/trace-refiner.js +0 -261
  98. package/core/dist/runtime-v2/trace-refiner.js.map +0 -1
  99. package/core/dist/runtime-v2/types/queue-types.d.ts +0 -51
  100. package/core/dist/runtime-v2/types/queue-types.d.ts.map +0 -1
  101. package/core/dist/runtime-v2/types/queue-types.js +0 -44
  102. package/core/dist/runtime-v2/types/queue-types.js.map +0 -1
  103. package/plugin/dist/types/queue.d.ts +0 -2
@@ -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