@neurcode-ai/cli 0.9.60 → 0.9.62

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 (71) hide show
  1. package/LICENSE +201 -0
  2. package/dist/commands/control-plane.d.ts +3 -0
  3. package/dist/commands/control-plane.d.ts.map +1 -0
  4. package/dist/commands/control-plane.js +163 -0
  5. package/dist/commands/control-plane.js.map +1 -0
  6. package/dist/commands/export.d.ts +7 -0
  7. package/dist/commands/export.d.ts.map +1 -0
  8. package/dist/commands/export.js +72 -0
  9. package/dist/commands/export.js.map +1 -0
  10. package/dist/commands/fix.d.ts +1 -0
  11. package/dist/commands/fix.d.ts.map +1 -1
  12. package/dist/commands/fix.js +3 -0
  13. package/dist/commands/fix.js.map +1 -1
  14. package/dist/commands/generate.d.ts +1 -0
  15. package/dist/commands/generate.d.ts.map +1 -1
  16. package/dist/commands/generate.js +63 -48
  17. package/dist/commands/generate.js.map +1 -1
  18. package/dist/commands/patch-apply.d.ts.map +1 -1
  19. package/dist/commands/patch-apply.js +1 -0
  20. package/dist/commands/patch-apply.js.map +1 -1
  21. package/dist/commands/replay.d.ts +3 -0
  22. package/dist/commands/replay.d.ts.map +1 -0
  23. package/dist/commands/replay.js +267 -0
  24. package/dist/commands/replay.js.map +1 -0
  25. package/dist/commands/verify.d.ts +7 -1
  26. package/dist/commands/verify.d.ts.map +1 -1
  27. package/dist/commands/verify.js +281 -149
  28. package/dist/commands/verify.js.map +1 -1
  29. package/dist/commands/workspace.d.ts +3 -0
  30. package/dist/commands/workspace.d.ts.map +1 -0
  31. package/dist/commands/workspace.js +407 -0
  32. package/dist/commands/workspace.js.map +1 -0
  33. package/dist/daemon/server.d.ts +0 -17
  34. package/dist/daemon/server.d.ts.map +1 -1
  35. package/dist/daemon/server.js +1043 -58
  36. package/dist/daemon/server.js.map +1 -1
  37. package/dist/index.js +296 -12
  38. package/dist/index.js.map +1 -1
  39. package/dist/utils/cli-json.d.ts +1 -0
  40. package/dist/utils/cli-json.d.ts.map +1 -1
  41. package/dist/utils/cli-json.js +1 -0
  42. package/dist/utils/cli-json.js.map +1 -1
  43. package/dist/utils/control-plane.d.ts +171 -0
  44. package/dist/utils/control-plane.d.ts.map +1 -0
  45. package/dist/utils/control-plane.js +684 -0
  46. package/dist/utils/control-plane.js.map +1 -0
  47. package/dist/utils/execution-bus.d.ts +205 -0
  48. package/dist/utils/execution-bus.d.ts.map +1 -0
  49. package/dist/utils/execution-bus.js +1346 -0
  50. package/dist/utils/execution-bus.js.map +1 -0
  51. package/dist/utils/gitignore.d.ts +2 -2
  52. package/dist/utils/gitignore.d.ts.map +1 -1
  53. package/dist/utils/gitignore.js +27 -14
  54. package/dist/utils/gitignore.js.map +1 -1
  55. package/dist/utils/replay-runtime.d.ts +295 -0
  56. package/dist/utils/replay-runtime.d.ts.map +1 -0
  57. package/dist/utils/replay-runtime.js +1080 -0
  58. package/dist/utils/replay-runtime.js.map +1 -0
  59. package/dist/utils/runtime-events.d.ts +44 -0
  60. package/dist/utils/runtime-events.d.ts.map +1 -0
  61. package/dist/utils/runtime-events.js +213 -0
  62. package/dist/utils/runtime-events.js.map +1 -0
  63. package/dist/utils/verification-evidence.d.ts +22 -0
  64. package/dist/utils/verification-evidence.d.ts.map +1 -0
  65. package/dist/utils/verification-evidence.js +233 -0
  66. package/dist/utils/verification-evidence.js.map +1 -0
  67. package/dist/utils/workspace-runtime.d.ts +267 -0
  68. package/dist/utils/workspace-runtime.d.ts.map +1 -0
  69. package/dist/utils/workspace-runtime.js +1415 -0
  70. package/dist/utils/workspace-runtime.js.map +1 -0
  71. package/package.json +7 -8
@@ -0,0 +1,1346 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runExecution = runExecution;
4
+ exports.recordSyntheticExecution = recordSyntheticExecution;
5
+ exports.queryExecutions = queryExecutions;
6
+ exports.listExecutions = listExecutions;
7
+ exports.buildExecutionTimeline = buildExecutionTimeline;
8
+ exports.buildExecutionDiffInspection = buildExecutionDiffInspection;
9
+ exports.getExecutionById = getExecutionById;
10
+ const fs_1 = require("fs");
11
+ const crypto_1 = require("crypto");
12
+ const path_1 = require("path");
13
+ const cli_json_1 = require("./cli-json");
14
+ const runtime_events_1 = require("./runtime-events");
15
+ const DEFAULT_EXECUTIONS_DIR = '.neurcode/executions';
16
+ const RECORD_SCHEMA_VERSION = 'neurcode.execution.v1';
17
+ const RECORD_PREFIX = 'execution-';
18
+ const RECORD_SUFFIX = '.json';
19
+ const DEFAULT_RETENTION = 250;
20
+ const DEFAULT_LOCK_TIMEOUT_MS = 90_000;
21
+ const LOCK_STALE_GRACE_MS = 5_000;
22
+ const DEFAULT_DEDUPE_WINDOW_MS = 0;
23
+ const MAX_DEDUPE_WINDOW_MS = 60_000;
24
+ const EXECUTION_CHILD_ENV = { NEURCODE_EXECUTION_CHILD: '1' };
25
+ function nowIso() {
26
+ return new Date().toISOString();
27
+ }
28
+ function parseJsonRecord(raw) {
29
+ try {
30
+ const parsed = JSON.parse(raw);
31
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
32
+ return null;
33
+ return parsed;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ function asObject(value) {
40
+ if (!value || typeof value !== 'object' || Array.isArray(value))
41
+ return null;
42
+ return value;
43
+ }
44
+ function asString(value) {
45
+ return typeof value === 'string' ? value : null;
46
+ }
47
+ function asNumber(value) {
48
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
49
+ }
50
+ function readRuntimeControlPlaneDefaults(cwd) {
51
+ const defaultValues = {
52
+ duplicateSuppression: false,
53
+ dedupeWindowMs: DEFAULT_DEDUPE_WINDOW_MS,
54
+ executionRetention: DEFAULT_RETENTION,
55
+ lockTimeoutMs: DEFAULT_LOCK_TIMEOUT_MS,
56
+ };
57
+ const configPath = (0, path_1.resolve)(cwd, '.neurcode/control-plane/runtime.json');
58
+ if (!(0, fs_1.existsSync)(configPath)) {
59
+ return defaultValues;
60
+ }
61
+ try {
62
+ const parsed = parseJsonRecord((0, fs_1.readFileSync)(configPath, 'utf-8'));
63
+ if (!parsed)
64
+ return defaultValues;
65
+ const execution = asObject(parsed.execution);
66
+ const retention = asObject(parsed.retention);
67
+ const duplicateSuppression = typeof execution?.duplicateSuppression === 'boolean'
68
+ ? execution.duplicateSuppression
69
+ : defaultValues.duplicateSuppression;
70
+ const dedupeWindowMs = asNumber(execution?.dedupeWindowMs);
71
+ const executionRetention = asNumber(retention?.executionRecords);
72
+ const lockTimeoutMs = asNumber(execution?.lockTimeoutMs);
73
+ return {
74
+ duplicateSuppression,
75
+ dedupeWindowMs: dedupeWindowMs === null
76
+ ? defaultValues.dedupeWindowMs
77
+ : Math.min(MAX_DEDUPE_WINDOW_MS, Math.max(0, Math.floor(dedupeWindowMs))),
78
+ executionRetention: executionRetention === null
79
+ ? defaultValues.executionRetention
80
+ : Math.max(1, Math.floor(executionRetention)),
81
+ lockTimeoutMs: lockTimeoutMs === null
82
+ ? defaultValues.lockTimeoutMs
83
+ : Math.max(10_000, Math.min(300_000, Math.floor(lockTimeoutMs))),
84
+ };
85
+ }
86
+ catch {
87
+ return defaultValues;
88
+ }
89
+ }
90
+ function parseRetentionLimit(input, cwd) {
91
+ if (typeof input === 'number' && Number.isFinite(input) && input >= 1) {
92
+ return Math.floor(input);
93
+ }
94
+ const runtimeDefaults = readRuntimeControlPlaneDefaults(cwd);
95
+ if (Number.isFinite(runtimeDefaults.executionRetention) && runtimeDefaults.executionRetention >= 1) {
96
+ return Math.floor(runtimeDefaults.executionRetention);
97
+ }
98
+ const env = process.env.NEURCODE_EXECUTION_RETENTION;
99
+ if (!env)
100
+ return DEFAULT_RETENTION;
101
+ const parsed = Number.parseInt(env, 10);
102
+ if (!Number.isFinite(parsed) || parsed < 1)
103
+ return DEFAULT_RETENTION;
104
+ return Math.floor(parsed);
105
+ }
106
+ function parseDedupeWindowMs(input, cwd) {
107
+ if (typeof input === 'number' && Number.isFinite(input) && input >= 0) {
108
+ return Math.min(MAX_DEDUPE_WINDOW_MS, Math.floor(input));
109
+ }
110
+ const runtimeDefaults = readRuntimeControlPlaneDefaults(cwd);
111
+ if (runtimeDefaults.duplicateSuppression) {
112
+ return Math.min(MAX_DEDUPE_WINDOW_MS, Math.max(0, Math.floor(runtimeDefaults.dedupeWindowMs)));
113
+ }
114
+ const env = process.env.NEURCODE_EXECUTION_DEDUPE_WINDOW_MS;
115
+ if (!env)
116
+ return DEFAULT_DEDUPE_WINDOW_MS;
117
+ const parsed = Number.parseInt(env, 10);
118
+ if (!Number.isFinite(parsed) || parsed < 0)
119
+ return DEFAULT_DEDUPE_WINDOW_MS;
120
+ return Math.min(MAX_DEDUPE_WINDOW_MS, Math.floor(parsed));
121
+ }
122
+ function normalizeSource(input) {
123
+ if (!input)
124
+ return 'unknown';
125
+ const normalized = input.trim().toLowerCase();
126
+ if (normalized === 'cli'
127
+ || normalized === 'daemon'
128
+ || normalized === 'dashboard'
129
+ || normalized === 'vscode'
130
+ || normalized === 'ci'
131
+ || normalized === 'mcp'
132
+ || normalized === 'cursor'
133
+ || normalized === 'api') {
134
+ return normalized;
135
+ }
136
+ return 'unknown';
137
+ }
138
+ function resolveExecutionPaths(cwd) {
139
+ const rootDir = (0, path_1.resolve)(cwd, DEFAULT_EXECUTIONS_DIR);
140
+ const recordsDir = (0, path_1.join)(rootDir, 'records');
141
+ const eventsFile = (0, path_1.join)(rootDir, 'events.jsonl');
142
+ const lockFile = (0, path_1.join)(rootDir, '.lock');
143
+ (0, fs_1.mkdirSync)(recordsDir, { recursive: true });
144
+ return { rootDir, recordsDir, eventsFile, lockFile };
145
+ }
146
+ function executionIdFromNow() {
147
+ const stamp = nowIso().replace(/[:.]/g, '-');
148
+ const random = Math.random().toString(16).slice(2, 8);
149
+ return `exec-${stamp}-${random}`;
150
+ }
151
+ function appendEventLine(paths, event) {
152
+ const line = `${JSON.stringify(event)}\n`;
153
+ try {
154
+ (0, fs_1.writeFileSync)(paths.eventsFile, line, { encoding: 'utf-8', flag: 'a' });
155
+ }
156
+ catch {
157
+ // Best-effort only; record file remains source of truth.
158
+ }
159
+ }
160
+ function buildCounts(payload) {
161
+ if (!payload) {
162
+ return { blocking: 0, advisory: 0 };
163
+ }
164
+ const blockingFromField = asNumber(payload.blockingCount);
165
+ const advisoryFromField = asNumber(payload.advisoryCount);
166
+ if (blockingFromField !== null || advisoryFromField !== null) {
167
+ return {
168
+ blocking: blockingFromField ?? 0,
169
+ advisory: advisoryFromField ?? 0,
170
+ };
171
+ }
172
+ const violations = Array.isArray(payload.violations) ? payload.violations : [];
173
+ let blocking = 0;
174
+ for (const violation of violations) {
175
+ const record = asObject(violation);
176
+ const severity = (asString(record?.severity) || '').toLowerCase();
177
+ if (severity === 'critical' || severity === 'high' || severity === 'block') {
178
+ blocking += 1;
179
+ }
180
+ }
181
+ return {
182
+ blocking,
183
+ advisory: Math.max(0, violations.length - blocking),
184
+ };
185
+ }
186
+ function severityFromCounts(counts) {
187
+ if (!counts)
188
+ return 'medium';
189
+ if (counts.blocking > 0)
190
+ return 'high';
191
+ if (counts.advisory > 0)
192
+ return 'medium';
193
+ return 'low';
194
+ }
195
+ function toPayloadObject(value) {
196
+ if (!value || typeof value !== 'object' || Array.isArray(value))
197
+ return null;
198
+ return value;
199
+ }
200
+ function toVerificationSnapshot(payload) {
201
+ if (!payload)
202
+ return null;
203
+ return {
204
+ verdict: asString(payload.verdict) || 'UNKNOWN',
205
+ grade: asString(payload.grade) || undefined,
206
+ score: asNumber(payload.score),
207
+ summary: payload.summary ?? null,
208
+ counts: buildCounts(payload),
209
+ };
210
+ }
211
+ function toVerificationDiff(before, after) {
212
+ const beforeCounts = before?.counts ?? null;
213
+ const afterCounts = after?.counts ?? null;
214
+ if (!beforeCounts && !afterCounts) {
215
+ return {
216
+ before: null,
217
+ after: null,
218
+ blockingDelta: null,
219
+ advisoryDelta: null,
220
+ trend: 'baseline',
221
+ };
222
+ }
223
+ if (!beforeCounts) {
224
+ return {
225
+ before: null,
226
+ after: afterCounts,
227
+ blockingDelta: null,
228
+ advisoryDelta: null,
229
+ trend: 'baseline',
230
+ };
231
+ }
232
+ if (!afterCounts) {
233
+ return {
234
+ before: beforeCounts,
235
+ after: null,
236
+ blockingDelta: null,
237
+ advisoryDelta: null,
238
+ trend: 'unchanged',
239
+ };
240
+ }
241
+ const blockingDelta = afterCounts.blocking - beforeCounts.blocking;
242
+ const advisoryDelta = afterCounts.advisory - beforeCounts.advisory;
243
+ let trend = 'unchanged';
244
+ if (blockingDelta < 0 || (blockingDelta === 0 && advisoryDelta < 0)) {
245
+ trend = 'improved';
246
+ }
247
+ else if (blockingDelta > 0 || (blockingDelta === 0 && advisoryDelta > 0)) {
248
+ trend = 'regressed';
249
+ }
250
+ return {
251
+ before: beforeCounts,
252
+ after: afterCounts,
253
+ blockingDelta,
254
+ advisoryDelta,
255
+ trend,
256
+ };
257
+ }
258
+ function buildNarrative(actionType, succeeded, verification, target) {
259
+ if (!succeeded) {
260
+ return {
261
+ status: 'failure',
262
+ summary: `${actionType} execution failed before deterministic remediation completed.`,
263
+ why: 'The command exited non-zero or produced no valid payload for downstream verification.',
264
+ riskLevel: 'high',
265
+ recommendedAction: 'Review command output, resolve local errors, and re-run execution.',
266
+ expectedImprovement: 'A successful rerun will re-enter verify/evidence/narrative stages and restore traceability.',
267
+ };
268
+ }
269
+ const diff = verification.diff;
270
+ if (diff.trend === 'improved') {
271
+ return {
272
+ status: 'success',
273
+ summary: target
274
+ ? `Execution improved governance posture for ${target}.`
275
+ : 'Execution improved governance posture.',
276
+ why: `Blocking delta ${diff.blockingDelta ?? 0}, advisory delta ${diff.advisoryDelta ?? 0}.`,
277
+ riskLevel: 'low',
278
+ recommendedAction: 'Run follow-up verify in CI to confirm stability across ephemeral runners.',
279
+ expectedImprovement: 'Sustaining the same patch/fix pattern should reduce recurrent blocking drift.',
280
+ };
281
+ }
282
+ if (diff.trend === 'regressed') {
283
+ return {
284
+ status: 'warning',
285
+ summary: 'Execution completed but verification regressed.',
286
+ why: `Blocking delta ${diff.blockingDelta ?? 0}, advisory delta ${diff.advisoryDelta ?? 0}.`,
287
+ riskLevel: 'high',
288
+ recommendedAction: 'Open Fix Center or run neurcode fix --apply-safe before merging.',
289
+ expectedImprovement: 'Applying deterministic remediation should reduce blocking findings on the next run.',
290
+ };
291
+ }
292
+ return {
293
+ status: 'success',
294
+ summary: 'Execution completed with stable verification posture.',
295
+ why: 'No net blocking/advisory change was detected in the deterministic reverify stage.',
296
+ riskLevel: 'medium',
297
+ recommendedAction: 'Review advisory findings and maintain verify cadence.',
298
+ expectedImprovement: 'Targeted follow-up patches can move the system from stable to improving.',
299
+ };
300
+ }
301
+ function listEvidenceFiles(dir) {
302
+ if (!(0, fs_1.existsSync)(dir))
303
+ return [];
304
+ return (0, fs_1.readdirSync)(dir)
305
+ .filter((name) => name.startsWith('verification-') && name.endsWith('.json'))
306
+ .sort();
307
+ }
308
+ function resolveEvidenceDirectory(cwd, configured) {
309
+ if (configured && configured.trim().length > 0) {
310
+ return (0, path_1.resolve)(cwd, configured.trim());
311
+ }
312
+ return (0, path_1.resolve)(cwd, '.neurcode/evidence');
313
+ }
314
+ function collectNewEvidenceRefs(before, afterEntries, evidenceDir) {
315
+ const created = [];
316
+ for (const name of afterEntries) {
317
+ if (before.has(name))
318
+ continue;
319
+ created.push((0, path_1.join)(evidenceDir, name));
320
+ }
321
+ return created.sort();
322
+ }
323
+ function resolveCiMode(request, source) {
324
+ if (typeof request.ciMode === 'boolean')
325
+ return request.ciMode;
326
+ return source === 'ci' || process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';
327
+ }
328
+ function ensureFlag(args, flag) {
329
+ if (args.includes(flag))
330
+ return [...args];
331
+ return [...args, flag];
332
+ }
333
+ function ensureCiFlag(args, ciMode) {
334
+ return ciMode ? ensureFlag(args, '--ci') : [...args];
335
+ }
336
+ function buildDefaultVerifyArgs(ciMode, withEvidence) {
337
+ let args = ['verify'];
338
+ if (withEvidence) {
339
+ args = ensureFlag(args, '--evidence');
340
+ }
341
+ return ensureCiFlag(args, ciMode);
342
+ }
343
+ function resolvePrimaryCommand(request, ciMode) {
344
+ if (Array.isArray(request.primaryArgs) && request.primaryArgs.length > 0) {
345
+ const provided = [...request.primaryArgs];
346
+ if (provided[0] === 'verify') {
347
+ return ensureCiFlag(provided, ciMode);
348
+ }
349
+ if (provided[0] === 'fix') {
350
+ return ensureCiFlag(provided, ciMode);
351
+ }
352
+ return provided;
353
+ }
354
+ switch (request.type) {
355
+ case 'verify':
356
+ return buildDefaultVerifyArgs(ciMode, true);
357
+ case 'reverify':
358
+ return buildDefaultVerifyArgs(ciMode, true);
359
+ case 'fix':
360
+ return ensureCiFlag(['fix'], ciMode);
361
+ case 'apply-safe':
362
+ return ensureCiFlag(['fix', '--apply-safe'], ciMode);
363
+ case 'patch':
364
+ return ['patch', '--file', request.target || '', '--json'];
365
+ case 'policy-sync':
366
+ return ['policy', 'list'];
367
+ case 'intent-update':
368
+ return ['start', request.intentText || 'Update intent'];
369
+ default:
370
+ return buildDefaultVerifyArgs(ciMode, true);
371
+ }
372
+ }
373
+ function resolveBaselineVerifyCommand(request, ciMode) {
374
+ if (Array.isArray(request.baselineVerifyArgs) && request.baselineVerifyArgs.length > 0) {
375
+ return ensureCiFlag([...request.baselineVerifyArgs], ciMode);
376
+ }
377
+ return buildDefaultVerifyArgs(ciMode, false);
378
+ }
379
+ function resolveReverifyCommand(request, ciMode) {
380
+ if (Array.isArray(request.reverifyArgs) && request.reverifyArgs.length > 0) {
381
+ return ensureCiFlag(ensureFlag([...request.reverifyArgs], '--evidence'), ciMode);
382
+ }
383
+ return buildDefaultVerifyArgs(ciMode, true);
384
+ }
385
+ function isPathInsideCwd(cwd, targetPath) {
386
+ const resolvedTarget = (0, path_1.resolve)(cwd, targetPath);
387
+ const root = cwd.endsWith(path_1.sep) ? cwd : `${cwd}${path_1.sep}`;
388
+ return resolvedTarget === cwd || resolvedTarget.startsWith(root);
389
+ }
390
+ function validateRequest(request, cwd) {
391
+ if (request.type === 'patch') {
392
+ if (!request.target || request.target.trim().length === 0) {
393
+ return 'patch execution requires a target file path';
394
+ }
395
+ if (!isPathInsideCwd(cwd, request.target)) {
396
+ return 'patch target path is unsafe';
397
+ }
398
+ }
399
+ if (request.type === 'intent-update' && (!request.intentText || request.intentText.trim().length === 0)) {
400
+ return 'intent-update execution requires intentText';
401
+ }
402
+ return null;
403
+ }
404
+ function buildExecutionFingerprint(request, source, actor, cwd, ciMode) {
405
+ const fingerprintPayload = {
406
+ type: request.type,
407
+ source,
408
+ actor,
409
+ target: request.target || null,
410
+ intentText: request.intentText || null,
411
+ reverify: request.reverify !== false,
412
+ ciMode,
413
+ cwd,
414
+ primaryArgs: request.primaryArgs ?? null,
415
+ baselineVerifyArgs: request.baselineVerifyArgs ?? null,
416
+ reverifyArgs: request.reverifyArgs ?? null,
417
+ };
418
+ return (0, crypto_1.createHash)('sha256').update(JSON.stringify(fingerprintPayload)).digest('hex');
419
+ }
420
+ function writeRecord(paths, record) {
421
+ const filePath = (0, path_1.join)(paths.recordsDir, `${RECORD_PREFIX}${record.id}${RECORD_SUFFIX}`);
422
+ (0, fs_1.writeFileSync)(filePath, `${JSON.stringify(record, null, 2)}\n`, { encoding: 'utf-8', flag: 'wx' });
423
+ return filePath;
424
+ }
425
+ function pruneRecords(paths, keepLatest) {
426
+ const files = (0, fs_1.readdirSync)(paths.recordsDir)
427
+ .filter((name) => name.startsWith(RECORD_PREFIX) && name.endsWith(RECORD_SUFFIX))
428
+ .sort();
429
+ if (files.length <= keepLatest)
430
+ return;
431
+ const toDelete = files.slice(0, files.length - keepLatest);
432
+ for (const name of toDelete) {
433
+ (0, fs_1.rmSync)((0, path_1.join)(paths.recordsDir, name), { force: true });
434
+ }
435
+ }
436
+ function acquireLock(lockFile, timeoutMs) {
437
+ if ((0, fs_1.existsSync)(lockFile)) {
438
+ let ageMs = 0;
439
+ try {
440
+ const stamp = (0, fs_1.readFileSync)(lockFile, 'utf-8').trim();
441
+ const parsed = Date.parse(stamp);
442
+ ageMs = Number.isFinite(parsed) ? Date.now() - parsed : 0;
443
+ }
444
+ catch {
445
+ ageMs = 0;
446
+ }
447
+ if (ageMs > timeoutMs + LOCK_STALE_GRACE_MS) {
448
+ try {
449
+ (0, fs_1.unlinkSync)(lockFile);
450
+ }
451
+ catch {
452
+ // fall through and attempt lock creation anyway
453
+ }
454
+ }
455
+ }
456
+ try {
457
+ return (0, fs_1.openSync)(lockFile, 'wx');
458
+ }
459
+ catch (error) {
460
+ const message = error instanceof Error ? error.message : String(error);
461
+ throw new Error(`execution lock busy: ${message}`);
462
+ }
463
+ }
464
+ function releaseLock(fd, lockFile) {
465
+ try {
466
+ (0, fs_1.unlinkSync)(lockFile);
467
+ }
468
+ catch {
469
+ // ignore
470
+ }
471
+ try {
472
+ (0, fs_1.closeSync)(fd);
473
+ }
474
+ catch {
475
+ // ignore
476
+ }
477
+ }
478
+ function toEvent(stage, message, details) {
479
+ return {
480
+ timestamp: nowIso(),
481
+ stage,
482
+ message,
483
+ ...(details ? { details } : {}),
484
+ };
485
+ }
486
+ function readExecutionRecord(filePath) {
487
+ try {
488
+ const parsed = parseJsonRecord((0, fs_1.readFileSync)(filePath, 'utf-8'));
489
+ if (!parsed)
490
+ return null;
491
+ if (parsed.schemaVersion !== RECORD_SCHEMA_VERSION)
492
+ return null;
493
+ return parsed;
494
+ }
495
+ catch {
496
+ return null;
497
+ }
498
+ }
499
+ function toRecordPath(paths, executionId) {
500
+ return (0, path_1.join)(paths.recordsDir, `${RECORD_PREFIX}${executionId}${RECORD_SUFFIX}`);
501
+ }
502
+ function normalizeLimit(limit) {
503
+ if (typeof limit === 'number' && Number.isFinite(limit) && limit >= 1) {
504
+ return Math.floor(limit);
505
+ }
506
+ return 50;
507
+ }
508
+ function normalizeQueryLimit(limit) {
509
+ const normalized = normalizeLimit(limit);
510
+ return Math.min(200, normalized);
511
+ }
512
+ function normalizeOffset(offset) {
513
+ if (typeof offset === 'number' && Number.isFinite(offset) && offset >= 0) {
514
+ return Math.floor(offset);
515
+ }
516
+ return 0;
517
+ }
518
+ function parseQueryDate(value) {
519
+ if (!value || value.trim().length === 0)
520
+ return null;
521
+ const parsed = Date.parse(value.trim());
522
+ return Number.isFinite(parsed) ? parsed : null;
523
+ }
524
+ function normalizeSeverityFilter(value) {
525
+ if (!value)
526
+ return 'all';
527
+ const normalized = value.trim().toLowerCase();
528
+ if (normalized === 'all'
529
+ || normalized === 'blocking'
530
+ || normalized === 'advisory'
531
+ || normalized === 'high'
532
+ || normalized === 'medium'
533
+ || normalized === 'low') {
534
+ return normalized;
535
+ }
536
+ return 'all';
537
+ }
538
+ function getVerificationCountsForFilter(record) {
539
+ return record.verification.after?.counts ?? record.verification.before?.counts ?? null;
540
+ }
541
+ function inferRiskLevel(record) {
542
+ const explicit = record.narrative?.riskLevel;
543
+ if (explicit === 'low' || explicit === 'medium' || explicit === 'high') {
544
+ return explicit;
545
+ }
546
+ const counts = getVerificationCountsForFilter(record);
547
+ if (!counts)
548
+ return 'medium';
549
+ if (counts.blocking > 0)
550
+ return 'high';
551
+ if (counts.advisory > 0)
552
+ return 'medium';
553
+ return 'low';
554
+ }
555
+ function buildExecutionSearchText(record) {
556
+ const chunks = [
557
+ record.id,
558
+ record.type,
559
+ record.source,
560
+ record.actor,
561
+ record.status,
562
+ record.target || '',
563
+ record.result?.message || '',
564
+ record.result?.stderr || '',
565
+ record.result?.command.join(' ') || '',
566
+ record.narrative?.summary || '',
567
+ record.narrative?.why || '',
568
+ record.narrative?.recommendedAction || '',
569
+ record.evidence.references.join(' '),
570
+ ];
571
+ return chunks.join(' ').toLowerCase();
572
+ }
573
+ function extractHotspotSnapshot(payload) {
574
+ if (!payload)
575
+ return [];
576
+ const buckets = new Map();
577
+ const candidates = [
578
+ ...(Array.isArray(payload.violations) ? payload.violations : []),
579
+ ...(Array.isArray(payload.warnings) ? payload.warnings : []),
580
+ ];
581
+ for (const candidate of candidates) {
582
+ const row = toPayloadObject(candidate);
583
+ const file = (asString(row?.file) || '').trim();
584
+ if (!file)
585
+ continue;
586
+ buckets.set(file, (buckets.get(file) || 0) + 1);
587
+ }
588
+ return [...buckets.entries()]
589
+ .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))
590
+ .slice(0, 3)
591
+ .map(([file, count]) => ({ file, count }));
592
+ }
593
+ function matchesExecutionFilters(record, options, parsedFromMs, parsedToMs, normalizedSeverity) {
594
+ if (options.type && options.type !== 'all' && record.type !== options.type) {
595
+ return false;
596
+ }
597
+ if (options.source && options.source !== 'all' && record.source !== options.source) {
598
+ return false;
599
+ }
600
+ if (options.status && options.status !== 'all' && record.status !== options.status) {
601
+ return false;
602
+ }
603
+ if (options.actor && options.actor.trim().length > 0) {
604
+ const actorNeedle = options.actor.trim().toLowerCase();
605
+ if (!record.actor.toLowerCase().includes(actorNeedle)) {
606
+ return false;
607
+ }
608
+ }
609
+ const createdAtMs = Date.parse(record.createdAt);
610
+ if (parsedFromMs !== null && Number.isFinite(createdAtMs) && createdAtMs < parsedFromMs) {
611
+ return false;
612
+ }
613
+ if (parsedToMs !== null && Number.isFinite(createdAtMs) && createdAtMs > parsedToMs) {
614
+ return false;
615
+ }
616
+ if (normalizedSeverity !== 'all') {
617
+ const counts = getVerificationCountsForFilter(record) ?? { blocking: 0, advisory: 0 };
618
+ const risk = inferRiskLevel(record);
619
+ if (normalizedSeverity === 'blocking' && counts.blocking <= 0)
620
+ return false;
621
+ if (normalizedSeverity === 'advisory' && counts.advisory <= 0)
622
+ return false;
623
+ if (normalizedSeverity === 'high' && risk !== 'high')
624
+ return false;
625
+ if (normalizedSeverity === 'medium' && risk !== 'medium')
626
+ return false;
627
+ if (normalizedSeverity === 'low' && risk !== 'low')
628
+ return false;
629
+ }
630
+ if (options.q && options.q.trim().length > 0) {
631
+ const needle = options.q.trim().toLowerCase();
632
+ const haystack = buildExecutionSearchText(record);
633
+ if (!haystack.includes(needle)) {
634
+ return false;
635
+ }
636
+ }
637
+ return true;
638
+ }
639
+ function toCliInvocationFailure(message) {
640
+ return {
641
+ exitCode: 1,
642
+ stdout: '',
643
+ stderr: message,
644
+ payload: null,
645
+ command: [],
646
+ };
647
+ }
648
+ function isInvocationSuccessful(invocation) {
649
+ if (!invocation)
650
+ return false;
651
+ if (invocation.exitCode !== 0)
652
+ return false;
653
+ if (invocation.payload && typeof invocation.payload.success === 'boolean') {
654
+ return invocation.payload.success;
655
+ }
656
+ return true;
657
+ }
658
+ function findRecentDuplicateExecution(paths, fingerprint, dedupeWindowMs) {
659
+ if (dedupeWindowMs <= 0)
660
+ return null;
661
+ const files = (0, fs_1.readdirSync)(paths.recordsDir)
662
+ .filter((name) => name.startsWith(RECORD_PREFIX) && name.endsWith(RECORD_SUFFIX))
663
+ .sort()
664
+ .reverse()
665
+ .slice(0, 25);
666
+ const nowMs = Date.now();
667
+ for (const file of files) {
668
+ const record = readExecutionRecord((0, path_1.join)(paths.recordsDir, file));
669
+ if (!record)
670
+ continue;
671
+ if (record.fingerprint !== fingerprint)
672
+ continue;
673
+ const completedMs = Date.parse(record.completedAt || record.createdAt);
674
+ if (!Number.isFinite(completedMs))
675
+ continue;
676
+ if (nowMs - completedMs <= dedupeWindowMs) {
677
+ return record;
678
+ }
679
+ }
680
+ return null;
681
+ }
682
+ async function runExecution(request) {
683
+ const cwd = (0, path_1.resolve)(request.cwd || process.cwd());
684
+ const runtimeDefaults = readRuntimeControlPlaneDefaults(cwd);
685
+ const paths = resolveExecutionPaths(cwd);
686
+ const lockTimeout = typeof request.maxLockMs === 'number' && request.maxLockMs > 0
687
+ ? Math.floor(request.maxLockMs)
688
+ : runtimeDefaults.lockTimeoutMs;
689
+ const lockFd = acquireLock(paths.lockFile, lockTimeout);
690
+ (0, fs_1.writeFileSync)(paths.lockFile, nowIso(), { encoding: 'utf-8' });
691
+ const source = normalizeSource(request.source);
692
+ const actor = request.actor && request.actor.trim().length > 0
693
+ ? request.actor.trim()
694
+ : source === 'ci'
695
+ ? 'ci-runner'
696
+ : source === 'vscode'
697
+ ? 'vscode-user'
698
+ : source === 'dashboard'
699
+ ? 'dashboard-user'
700
+ : source === 'daemon'
701
+ ? 'daemon-bridge'
702
+ : 'local-user';
703
+ const ciMode = resolveCiMode(request, source);
704
+ const fingerprint = buildExecutionFingerprint(request, source, actor, cwd, ciMode);
705
+ const dedupeWindowMs = parseDedupeWindowMs(request.dedupeWindowMs, cwd);
706
+ const executionId = executionIdFromNow();
707
+ const createdAt = nowIso();
708
+ const events = [toEvent('queued', 'Execution requested')];
709
+ let primaryInvocation = null;
710
+ let verifyInvocation = null;
711
+ let finalStatus = 'failed';
712
+ let startedAt = null;
713
+ let completedAt = null;
714
+ const evidenceDir = resolveEvidenceDirectory(cwd, request.evidenceDir);
715
+ const evidenceBefore = new Set(listEvidenceFiles(evidenceDir));
716
+ const retentionLimit = parseRetentionLimit(request.evidenceRetentionLimit, cwd);
717
+ let verificationBeforePayload = null;
718
+ let verificationAfterPayload = null;
719
+ const emitRuntime = (type, severity, payload) => {
720
+ try {
721
+ (0, runtime_events_1.emitRuntimeEvent)(cwd, {
722
+ type,
723
+ executionId,
724
+ source,
725
+ actor,
726
+ severity,
727
+ payload: payload ?? {},
728
+ });
729
+ }
730
+ catch {
731
+ // Runtime event emission is best-effort and must not alter execution determinism.
732
+ }
733
+ };
734
+ try {
735
+ const dedupeEnabled = request.type !== 'verify' && request.type !== 'reverify';
736
+ const duplicate = dedupeEnabled
737
+ ? findRecentDuplicateExecution(paths, fingerprint, dedupeWindowMs)
738
+ : null;
739
+ if (duplicate) {
740
+ events.push(toEvent('validating', 'Duplicate execution suppressed', {
741
+ duplicateExecutionId: duplicate.id,
742
+ dedupeWindowMs,
743
+ }));
744
+ appendEventLine(paths, {
745
+ timestamp: nowIso(),
746
+ executionId: duplicate.id,
747
+ status: 'deduplicated',
748
+ source,
749
+ actor,
750
+ dedupeWindowMs,
751
+ fingerprint,
752
+ });
753
+ return {
754
+ execution: duplicate,
755
+ primaryPayload: duplicate.result?.payload ?? null,
756
+ verificationPayload: null,
757
+ };
758
+ }
759
+ events.push(toEvent('validating', 'Validating execution request'));
760
+ emitRuntime('execution.progress', 'low', {
761
+ stage: 'validating',
762
+ type: request.type,
763
+ target: request.target || null,
764
+ ciMode,
765
+ });
766
+ const validationError = validateRequest(request, cwd);
767
+ if (validationError) {
768
+ primaryInvocation = toCliInvocationFailure(validationError);
769
+ throw new Error(validationError);
770
+ }
771
+ if (request.type !== 'verify' && request.type !== 'reverify') {
772
+ const baselineInvocation = await (0, cli_json_1.runCliJson)(resolveBaselineVerifyCommand(request, ciMode), {
773
+ cwd,
774
+ env: EXECUTION_CHILD_ENV,
775
+ });
776
+ verificationBeforePayload = baselineInvocation.payload;
777
+ events.push(toEvent('validating', 'Captured baseline verification snapshot', {
778
+ exitCode: baselineInvocation.exitCode,
779
+ verdict: asString(baselineInvocation.payload?.verdict) || 'UNKNOWN',
780
+ }));
781
+ }
782
+ startedAt = nowIso();
783
+ events.push(toEvent('executing', 'Executing primary action'));
784
+ emitRuntime('execution.started', 'low', {
785
+ type: request.type,
786
+ target: request.target || null,
787
+ command: resolvePrimaryCommand(request, ciMode),
788
+ ciMode,
789
+ createdAt,
790
+ startedAt,
791
+ });
792
+ emitRuntime('execution.progress', 'low', {
793
+ stage: 'executing',
794
+ type: request.type,
795
+ target: request.target || null,
796
+ });
797
+ const primaryCommand = resolvePrimaryCommand(request, ciMode);
798
+ primaryInvocation = await (0, cli_json_1.runCliJson)(primaryCommand, { cwd, env: EXECUTION_CHILD_ENV });
799
+ if (!primaryInvocation.payload) {
800
+ throw new Error(primaryInvocation.stderr.trim() || `${request.type} produced no JSON payload`);
801
+ }
802
+ events.push(toEvent('executing', 'Primary action completed', {
803
+ exitCode: primaryInvocation.exitCode,
804
+ verdict: asString(primaryInvocation.payload.verdict) || null,
805
+ }));
806
+ const shouldReverify = request.reverify !== false;
807
+ if (request.type === 'verify' || request.type === 'reverify') {
808
+ verificationAfterPayload = primaryInvocation.payload;
809
+ }
810
+ else if (shouldReverify) {
811
+ events.push(toEvent('verifying', 'Running deterministic reverify'));
812
+ emitRuntime('execution.progress', 'low', {
813
+ stage: 'verifying',
814
+ type: request.type,
815
+ });
816
+ verifyInvocation = await (0, cli_json_1.runCliJson)(resolveReverifyCommand(request, ciMode), {
817
+ cwd,
818
+ env: EXECUTION_CHILD_ENV,
819
+ });
820
+ verificationAfterPayload = verifyInvocation.payload;
821
+ events.push(toEvent('verifying', 'Reverify completed', {
822
+ exitCode: verifyInvocation.exitCode,
823
+ verdict: asString(verifyInvocation.payload?.verdict) || 'UNKNOWN',
824
+ }));
825
+ }
826
+ events.push(toEvent('evidence', 'Collecting evidence artifact references'));
827
+ emitRuntime('execution.progress', 'low', {
828
+ stage: 'evidence',
829
+ type: request.type,
830
+ });
831
+ const evidenceAfter = listEvidenceFiles(evidenceDir);
832
+ const evidenceRefs = collectNewEvidenceRefs(evidenceBefore, evidenceAfter, evidenceDir);
833
+ const verification = {
834
+ before: toVerificationSnapshot(verificationBeforePayload),
835
+ after: toVerificationSnapshot(verificationAfterPayload),
836
+ diff: toVerificationDiff(toVerificationSnapshot(verificationBeforePayload), toVerificationSnapshot(verificationAfterPayload)),
837
+ };
838
+ const executionSucceeded = verifyInvocation
839
+ ? isInvocationSuccessful(verifyInvocation)
840
+ : isInvocationSuccessful(primaryInvocation);
841
+ events.push(toEvent('narrating', 'Synthesizing deterministic execution narrative'));
842
+ emitRuntime('execution.progress', 'low', {
843
+ stage: 'narrating',
844
+ type: request.type,
845
+ });
846
+ const narrative = buildNarrative(request.type, executionSucceeded, verification, request.target || null);
847
+ completedAt = nowIso();
848
+ finalStatus = 'completed';
849
+ const execution = {
850
+ schemaVersion: RECORD_SCHEMA_VERSION,
851
+ id: executionId,
852
+ fingerprint,
853
+ type: request.type,
854
+ actor,
855
+ source,
856
+ target: request.target || null,
857
+ status: finalStatus,
858
+ createdAt,
859
+ startedAt,
860
+ completedAt,
861
+ durationMs: startedAt ? Math.max(0, Date.parse(completedAt) - Date.parse(startedAt)) : null,
862
+ result: {
863
+ success: executionSucceeded,
864
+ exitCode: verifyInvocation?.exitCode ?? primaryInvocation.exitCode,
865
+ command: primaryInvocation.command,
866
+ message: asString(primaryInvocation.payload.message),
867
+ payload: primaryInvocation.payload,
868
+ stderr: primaryInvocation.stderr.trim() || null,
869
+ },
870
+ verification,
871
+ evidence: {
872
+ references: evidenceRefs,
873
+ generated: evidenceRefs.length > 0,
874
+ retentionLimit,
875
+ },
876
+ narrative,
877
+ runtime: {
878
+ cwd,
879
+ nodeVersion: process.version,
880
+ platform: process.platform,
881
+ arch: process.arch,
882
+ ciMode,
883
+ },
884
+ events,
885
+ };
886
+ writeRecord(paths, execution);
887
+ appendEventLine(paths, {
888
+ timestamp: completedAt,
889
+ executionId,
890
+ status: finalStatus,
891
+ type: request.type,
892
+ source,
893
+ actor,
894
+ trend: verification.diff.trend,
895
+ evidenceCount: evidenceRefs.length,
896
+ });
897
+ pruneRecords(paths, retentionLimit);
898
+ const verificationCounts = verification.after?.counts ?? verification.before?.counts ?? null;
899
+ emitRuntime('verification.completed', severityFromCounts(verificationCounts), {
900
+ verdict: verification.after?.verdict || verification.before?.verdict || 'UNKNOWN',
901
+ counts: verificationCounts,
902
+ trend: verification.diff.trend,
903
+ blockingDelta: verification.diff.blockingDelta,
904
+ advisoryDelta: verification.diff.advisoryDelta,
905
+ ciMode,
906
+ });
907
+ if (verification.diff.trend === 'regressed') {
908
+ emitRuntime('regression.detected', 'high', {
909
+ blockingDelta: verification.diff.blockingDelta,
910
+ advisoryDelta: verification.diff.advisoryDelta,
911
+ target: request.target || null,
912
+ });
913
+ }
914
+ const hotspotSnapshot = extractHotspotSnapshot(verificationAfterPayload ?? verificationBeforePayload);
915
+ if (hotspotSnapshot.length > 0) {
916
+ emitRuntime('hotspot.updated', verification.diff.trend === 'regressed' ? 'high' : 'medium', {
917
+ hotspots: hotspotSnapshot,
918
+ trend: verification.diff.trend,
919
+ });
920
+ }
921
+ if (request.type === 'patch' || request.type === 'apply-safe' || request.type === 'fix') {
922
+ const patchPayload = toPayloadObject(primaryInvocation.payload);
923
+ const patchSucceeded = patchPayload?.success === true
924
+ || (typeof asNumber(patchPayload?.applied) === 'number' && (asNumber(patchPayload?.applied) || 0) > 0);
925
+ if (patchSucceeded) {
926
+ emitRuntime('patch.applied', request.type === 'patch' ? 'low' : 'medium', {
927
+ type: request.type,
928
+ target: request.target || asString(patchPayload?.file) || null,
929
+ changed: patchPayload?.changed === true,
930
+ applied: asNumber(patchPayload?.applied) ?? null,
931
+ skipped: asNumber(patchPayload?.skipped) ?? null,
932
+ });
933
+ }
934
+ }
935
+ if (evidenceRefs.length > 0) {
936
+ emitRuntime('evidence.generated', 'low', {
937
+ count: evidenceRefs.length,
938
+ references: evidenceRefs,
939
+ });
940
+ }
941
+ if (narrative) {
942
+ emitRuntime('narrative.updated', narrative.riskLevel === 'high' ? 'high' : narrative.riskLevel === 'medium' ? 'medium' : 'low', {
943
+ summary: narrative.summary,
944
+ why: narrative.why,
945
+ recommendedAction: narrative.recommendedAction,
946
+ expectedImprovement: narrative.expectedImprovement,
947
+ });
948
+ }
949
+ emitRuntime(executionSucceeded ? 'execution.completed' : 'execution.failed', executionSucceeded ? 'low' : 'high', {
950
+ status: execution.status,
951
+ type: execution.type,
952
+ durationMs: execution.durationMs,
953
+ trend: execution.verification.diff.trend,
954
+ evidenceCount: execution.evidence.references.length,
955
+ exitCode: execution.result?.exitCode ?? 1,
956
+ });
957
+ return {
958
+ execution,
959
+ primaryPayload: primaryInvocation.payload,
960
+ verificationPayload: verificationAfterPayload,
961
+ };
962
+ }
963
+ catch (error) {
964
+ completedAt = nowIso();
965
+ const message = error instanceof Error ? error.message : String(error);
966
+ events.push(toEvent('failed', message));
967
+ const execution = {
968
+ schemaVersion: RECORD_SCHEMA_VERSION,
969
+ id: executionId,
970
+ fingerprint,
971
+ type: request.type,
972
+ actor,
973
+ source,
974
+ target: request.target || null,
975
+ status: 'failed',
976
+ createdAt,
977
+ startedAt,
978
+ completedAt,
979
+ durationMs: startedAt ? Math.max(0, Date.parse(completedAt) - Date.parse(startedAt)) : null,
980
+ result: {
981
+ success: false,
982
+ exitCode: verifyInvocation?.exitCode ?? primaryInvocation?.exitCode ?? 1,
983
+ command: primaryInvocation?.command || [],
984
+ message,
985
+ payload: primaryInvocation?.payload || null,
986
+ stderr: primaryInvocation?.stderr?.trim() || message,
987
+ },
988
+ verification: {
989
+ before: toVerificationSnapshot(verificationBeforePayload),
990
+ after: toVerificationSnapshot(verificationAfterPayload),
991
+ diff: toVerificationDiff(toVerificationSnapshot(verificationBeforePayload), toVerificationSnapshot(verificationAfterPayload)),
992
+ },
993
+ evidence: {
994
+ references: [],
995
+ generated: false,
996
+ retentionLimit,
997
+ },
998
+ narrative: buildNarrative(request.type, false, {
999
+ before: toVerificationSnapshot(verificationBeforePayload),
1000
+ after: toVerificationSnapshot(verificationAfterPayload),
1001
+ diff: toVerificationDiff(toVerificationSnapshot(verificationBeforePayload), toVerificationSnapshot(verificationAfterPayload)),
1002
+ }, request.target || null),
1003
+ runtime: {
1004
+ cwd,
1005
+ nodeVersion: process.version,
1006
+ platform: process.platform,
1007
+ arch: process.arch,
1008
+ ciMode,
1009
+ },
1010
+ events,
1011
+ };
1012
+ writeRecord(paths, execution);
1013
+ appendEventLine(paths, {
1014
+ timestamp: completedAt,
1015
+ executionId,
1016
+ status: 'failed',
1017
+ type: request.type,
1018
+ source,
1019
+ actor,
1020
+ message,
1021
+ });
1022
+ pruneRecords(paths, retentionLimit);
1023
+ emitRuntime('execution.failed', 'high', {
1024
+ type: request.type,
1025
+ stage: events[events.length - 1]?.stage || 'failed',
1026
+ message,
1027
+ exitCode: verifyInvocation?.exitCode ?? primaryInvocation?.exitCode ?? 1,
1028
+ });
1029
+ return {
1030
+ execution,
1031
+ primaryPayload: primaryInvocation?.payload || null,
1032
+ verificationPayload: verificationAfterPayload,
1033
+ };
1034
+ }
1035
+ finally {
1036
+ releaseLock(lockFd, paths.lockFile);
1037
+ }
1038
+ }
1039
+ function recordSyntheticExecution(input) {
1040
+ const cwd = (0, path_1.resolve)(input.cwd || process.cwd());
1041
+ const paths = resolveExecutionPaths(cwd);
1042
+ const source = normalizeSource(input.source);
1043
+ const actor = input.actor && input.actor.trim().length > 0
1044
+ ? input.actor.trim()
1045
+ : source === 'ci'
1046
+ ? 'ci-runner'
1047
+ : source === 'vscode'
1048
+ ? 'vscode-user'
1049
+ : source === 'dashboard'
1050
+ ? 'dashboard-user'
1051
+ : source === 'daemon'
1052
+ ? 'daemon-bridge'
1053
+ : 'local-user';
1054
+ const executionId = executionIdFromNow();
1055
+ const createdAt = nowIso();
1056
+ const startedAt = createdAt;
1057
+ const completedAt = createdAt;
1058
+ const status = input.status === 'failed' ? 'failed' : 'completed';
1059
+ const success = input.success !== false;
1060
+ const retentionLimit = parseRetentionLimit(input.evidenceRetentionLimit, cwd);
1061
+ const command = Array.isArray(input.command) && input.command.length > 0
1062
+ ? input.command
1063
+ : ['control-plane', 'apply'];
1064
+ const message = input.message ?? null;
1065
+ const payload = input.payload ?? null;
1066
+ const verificationSnapshot = input.verification
1067
+ ? {
1068
+ verdict: input.verification.verdict || (success ? 'PASS' : 'FAIL'),
1069
+ grade: input.verification.grade,
1070
+ score: input.verification.score ?? null,
1071
+ summary: input.verification.summary,
1072
+ counts: {
1073
+ blocking: Math.max(0, Math.floor(input.verification.counts?.blocking ?? 0)),
1074
+ advisory: Math.max(0, Math.floor(input.verification.counts?.advisory ?? 0)),
1075
+ },
1076
+ }
1077
+ : null;
1078
+ const verification = {
1079
+ before: null,
1080
+ after: verificationSnapshot,
1081
+ diff: {
1082
+ before: null,
1083
+ after: verificationSnapshot?.counts ?? null,
1084
+ blockingDelta: verificationSnapshot ? verificationSnapshot.counts.blocking : null,
1085
+ advisoryDelta: verificationSnapshot ? verificationSnapshot.counts.advisory : null,
1086
+ trend: 'baseline',
1087
+ },
1088
+ };
1089
+ const evidenceRefs = Array.isArray(input.evidenceReferences)
1090
+ ? input.evidenceReferences.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
1091
+ : [];
1092
+ const narrativeDefaults = {
1093
+ status: success ? 'success' : 'failure',
1094
+ summary: success ? 'Synthetic execution recorded' : 'Synthetic execution failed',
1095
+ why: message || (success ? 'Deterministic external event persisted' : 'Synthetic execution marked failed'),
1096
+ riskLevel: success ? 'low' : 'high',
1097
+ recommendedAction: success ? 'Run verify to confirm runtime posture.' : 'Review error and retry execution.',
1098
+ expectedImprovement: 'Maintains deterministic execution and governance history.',
1099
+ };
1100
+ const narrative = input.narrative
1101
+ ? {
1102
+ ...narrativeDefaults,
1103
+ ...input.narrative,
1104
+ }
1105
+ : narrativeDefaults;
1106
+ const events = [
1107
+ toEvent('queued', 'Synthetic execution requested'),
1108
+ toEvent('executing', 'Persisting synthetic execution event'),
1109
+ toEvent(status, message || (success ? 'Synthetic execution completed' : 'Synthetic execution failed'), input.eventDetails),
1110
+ ];
1111
+ const execution = {
1112
+ schemaVersion: RECORD_SCHEMA_VERSION,
1113
+ id: executionId,
1114
+ fingerprint: (0, crypto_1.createHash)('sha256')
1115
+ .update(JSON.stringify({
1116
+ type: input.type,
1117
+ source,
1118
+ actor,
1119
+ target: input.target || null,
1120
+ createdAt,
1121
+ payload,
1122
+ success,
1123
+ }))
1124
+ .digest('hex'),
1125
+ type: input.type,
1126
+ actor,
1127
+ source,
1128
+ target: input.target || null,
1129
+ status,
1130
+ createdAt,
1131
+ startedAt,
1132
+ completedAt,
1133
+ durationMs: 0,
1134
+ result: {
1135
+ success,
1136
+ exitCode: success ? 0 : 1,
1137
+ command,
1138
+ message,
1139
+ payload,
1140
+ stderr: input.stderr ?? null,
1141
+ },
1142
+ verification,
1143
+ evidence: {
1144
+ references: evidenceRefs,
1145
+ generated: evidenceRefs.length > 0,
1146
+ retentionLimit,
1147
+ },
1148
+ narrative,
1149
+ runtime: {
1150
+ cwd,
1151
+ nodeVersion: process.version,
1152
+ platform: process.platform,
1153
+ arch: process.arch,
1154
+ ciMode: input.ciMode === true,
1155
+ },
1156
+ events,
1157
+ };
1158
+ writeRecord(paths, execution);
1159
+ appendEventLine(paths, {
1160
+ timestamp: completedAt,
1161
+ executionId,
1162
+ status,
1163
+ type: input.type,
1164
+ source,
1165
+ actor,
1166
+ message: message || null,
1167
+ });
1168
+ pruneRecords(paths, retentionLimit);
1169
+ return execution;
1170
+ }
1171
+ function queryExecutions(cwd = process.cwd(), options = {}) {
1172
+ const paths = resolveExecutionPaths((0, path_1.resolve)(cwd));
1173
+ const limit = normalizeQueryLimit(options.limit);
1174
+ const offset = normalizeOffset(options.offset);
1175
+ const parsedFromMs = parseQueryDate(options.from);
1176
+ const parsedToMs = parseQueryDate(options.to);
1177
+ const normalizedSeverity = normalizeSeverityFilter(options.severity);
1178
+ const files = (0, fs_1.readdirSync)(paths.recordsDir)
1179
+ .filter((name) => name.startsWith(RECORD_PREFIX) && name.endsWith(RECORD_SUFFIX))
1180
+ .sort()
1181
+ .reverse();
1182
+ const items = [];
1183
+ let matched = 0;
1184
+ let scanned = 0;
1185
+ let hasMore = false;
1186
+ for (const file of files) {
1187
+ const parsed = readExecutionRecord((0, path_1.join)(paths.recordsDir, file));
1188
+ if (!parsed)
1189
+ continue;
1190
+ scanned += 1;
1191
+ if (!matchesExecutionFilters(parsed, options, parsedFromMs, parsedToMs, normalizedSeverity)) {
1192
+ continue;
1193
+ }
1194
+ if (matched < offset) {
1195
+ matched += 1;
1196
+ continue;
1197
+ }
1198
+ if (items.length >= limit) {
1199
+ hasMore = true;
1200
+ break;
1201
+ }
1202
+ items.push(parsed);
1203
+ matched += 1;
1204
+ }
1205
+ return {
1206
+ items,
1207
+ limit,
1208
+ offset,
1209
+ hasMore,
1210
+ nextOffset: offset + items.length,
1211
+ scanned,
1212
+ };
1213
+ }
1214
+ function listExecutions(cwd = process.cwd(), limit) {
1215
+ return queryExecutions(cwd, { limit }).items;
1216
+ }
1217
+ function eventTimestampMs(value) {
1218
+ const parsed = Date.parse(value);
1219
+ return Number.isFinite(parsed) ? parsed : null;
1220
+ }
1221
+ function buildExecutionTimeline(record) {
1222
+ const stages = [];
1223
+ const completedMs = eventTimestampMs(record.completedAt || '');
1224
+ const sortedEvents = [...record.events].sort((left, right) => {
1225
+ const leftMs = eventTimestampMs(left.timestamp) ?? 0;
1226
+ const rightMs = eventTimestampMs(right.timestamp) ?? 0;
1227
+ return leftMs - rightMs;
1228
+ });
1229
+ for (let idx = 0; idx < sortedEvents.length; idx += 1) {
1230
+ const current = sortedEvents[idx];
1231
+ const startMs = eventTimestampMs(current.timestamp);
1232
+ if (startMs === null)
1233
+ continue;
1234
+ const next = sortedEvents[idx + 1];
1235
+ const nextMs = next ? eventTimestampMs(next.timestamp) : null;
1236
+ const endMs = nextMs ?? completedMs ?? startMs;
1237
+ const boundedEndMs = endMs >= startMs ? endMs : startMs;
1238
+ stages.push({
1239
+ stage: current.stage,
1240
+ message: current.message,
1241
+ startedAt: new Date(startMs).toISOString(),
1242
+ endedAt: new Date(boundedEndMs).toISOString(),
1243
+ durationMs: Math.max(0, boundedEndMs - startMs),
1244
+ });
1245
+ }
1246
+ return {
1247
+ id: record.id,
1248
+ status: record.status,
1249
+ startedAt: record.startedAt,
1250
+ completedAt: record.completedAt,
1251
+ totalDurationMs: record.durationMs,
1252
+ stages,
1253
+ };
1254
+ }
1255
+ function extractFindings(payload) {
1256
+ if (!payload)
1257
+ return [];
1258
+ const entries = Array.isArray(payload.violations)
1259
+ ? payload.violations
1260
+ : Array.isArray(payload.warnings)
1261
+ ? payload.warnings
1262
+ : [];
1263
+ const findings = [];
1264
+ for (const entry of entries.slice(0, 50)) {
1265
+ const row = asObject(entry);
1266
+ findings.push({
1267
+ file: asString(row?.file),
1268
+ message: asString(row?.message) || asString(row?.issue) || asString(row?.rule) || 'Issue',
1269
+ severity: asString(row?.severity),
1270
+ rule: asString(row?.rule) || asString(row?.policy),
1271
+ });
1272
+ }
1273
+ return findings;
1274
+ }
1275
+ function extractDiffPreview(payload) {
1276
+ if (!payload)
1277
+ return null;
1278
+ const directPatch = asObject(payload.patch);
1279
+ const directDiff = asString(directPatch?.diff) || asString(payload.diff);
1280
+ if (directDiff)
1281
+ return directDiff;
1282
+ const suggestions = Array.isArray(payload.suggestions) ? payload.suggestions : [];
1283
+ for (const suggestion of suggestions) {
1284
+ const suggestionRecord = asObject(suggestion);
1285
+ const patchRecord = asObject(suggestionRecord?.patch);
1286
+ const suggestionDiff = asString(patchRecord?.diff);
1287
+ if (suggestionDiff)
1288
+ return suggestionDiff;
1289
+ }
1290
+ return null;
1291
+ }
1292
+ function buildExecutionDiffInspection(record) {
1293
+ const payload = record.result?.payload ?? null;
1294
+ const patchPayload = asObject(payload?.patch) ?? payload;
1295
+ const patchDiff = extractDiffPreview(payload);
1296
+ const before = record.verification.diff.before ?? null;
1297
+ const after = record.verification.diff.after ?? null;
1298
+ return {
1299
+ id: record.id,
1300
+ type: record.type,
1301
+ source: record.source,
1302
+ actor: record.actor,
1303
+ target: record.target,
1304
+ command: record.result?.command ?? [],
1305
+ predictedOutcome: {
1306
+ riskLevel: record.narrative?.riskLevel ?? null,
1307
+ expectedImprovement: record.narrative?.expectedImprovement ?? null,
1308
+ },
1309
+ actualOutcome: {
1310
+ success: record.result?.success === true,
1311
+ trend: record.verification.diff.trend,
1312
+ blockingDelta: record.verification.diff.blockingDelta,
1313
+ advisoryDelta: record.verification.diff.advisoryDelta,
1314
+ },
1315
+ beforeAfter: {
1316
+ before,
1317
+ after,
1318
+ },
1319
+ findings: extractFindings(payload),
1320
+ patch: {
1321
+ available: Boolean(patchDiff)
1322
+ || record.type === 'patch'
1323
+ || (payload ? Array.isArray(payload.suggestions) : false),
1324
+ file: asString(patchPayload?.file) || record.target || null,
1325
+ changed: typeof patchPayload?.changed === 'boolean'
1326
+ ? patchPayload.changed
1327
+ : null,
1328
+ confidence: asString(patchPayload?.patchConfidence) || null,
1329
+ patternKind: asString(patchPayload?.patternKind) || null,
1330
+ diffPreview: patchDiff,
1331
+ },
1332
+ };
1333
+ }
1334
+ function getExecutionById(executionId, cwd = process.cwd()) {
1335
+ const paths = resolveExecutionPaths((0, path_1.resolve)(cwd));
1336
+ if (!executionId || executionId.trim().length === 0)
1337
+ return null;
1338
+ const safeId = (0, path_1.basename)(executionId.trim());
1339
+ if (safeId !== executionId.trim())
1340
+ return null;
1341
+ const filePath = toRecordPath(paths, safeId);
1342
+ if (!(0, fs_1.existsSync)(filePath))
1343
+ return null;
1344
+ return readExecutionRecord(filePath);
1345
+ }
1346
+ //# sourceMappingURL=execution-bus.js.map