@ruso-0/nreki 7.1.2 → 7.3.0

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 (87) hide show
  1. package/CHANGELOG.md +805 -774
  2. package/README.md +308 -442
  3. package/dist/ast-sandbox.d.ts.map +1 -1
  4. package/dist/ast-sandbox.js +17 -1
  5. package/dist/ast-sandbox.js.map +1 -1
  6. package/dist/audit.d.ts.map +1 -1
  7. package/dist/audit.js +10 -4
  8. package/dist/audit.js.map +1 -1
  9. package/dist/chronos-memory.d.ts.map +1 -1
  10. package/dist/chronos-memory.js +10 -2
  11. package/dist/chronos-memory.js.map +1 -1
  12. package/dist/compressor.d.ts.map +1 -1
  13. package/dist/compressor.js +13 -1
  14. package/dist/compressor.js.map +1 -1
  15. package/dist/database.d.ts +12 -1
  16. package/dist/database.d.ts.map +1 -1
  17. package/dist/database.js +81 -29
  18. package/dist/database.js.map +1 -1
  19. package/dist/embedder.d.ts.map +1 -1
  20. package/dist/embedder.js +7 -2
  21. package/dist/embedder.js.map +1 -1
  22. package/dist/handlers/code.d.ts.map +1 -1
  23. package/dist/handlers/code.js +198 -243
  24. package/dist/handlers/code.js.map +1 -1
  25. package/dist/handlers/guard.d.ts.map +1 -1
  26. package/dist/handlers/guard.js +10 -1
  27. package/dist/handlers/guard.js.map +1 -1
  28. package/dist/hologram/shadow-generator.d.ts.map +1 -1
  29. package/dist/hologram/shadow-generator.js +20 -1
  30. package/dist/hologram/shadow-generator.js.map +1 -1
  31. package/dist/kernel/backends/lsp-sidecar-base.d.ts +49 -5
  32. package/dist/kernel/backends/lsp-sidecar-base.d.ts.map +1 -1
  33. package/dist/kernel/backends/lsp-sidecar-base.js +209 -69
  34. package/dist/kernel/backends/lsp-sidecar-base.js.map +1 -1
  35. package/dist/kernel/backends/ts-compiler-wrapper.d.ts +1 -1
  36. package/dist/kernel/backends/ts-compiler-wrapper.d.ts.map +1 -1
  37. package/dist/kernel/backends/ts-compiler-wrapper.js +7 -4
  38. package/dist/kernel/backends/ts-compiler-wrapper.js.map +1 -1
  39. package/dist/kernel/backends/ts-corsa-sidecar.d.ts +26 -0
  40. package/dist/kernel/backends/ts-corsa-sidecar.d.ts.map +1 -0
  41. package/dist/kernel/backends/ts-corsa-sidecar.js +30 -0
  42. package/dist/kernel/backends/ts-corsa-sidecar.js.map +1 -0
  43. package/dist/kernel/nreki-kernel.d.ts +37 -0
  44. package/dist/kernel/nreki-kernel.d.ts.map +1 -1
  45. package/dist/kernel/nreki-kernel.js +669 -290
  46. package/dist/kernel/nreki-kernel.js.map +1 -1
  47. package/dist/kernel/spectral-topology.d.ts.map +1 -1
  48. package/dist/kernel/spectral-topology.js +32 -16
  49. package/dist/kernel/spectral-topology.js.map +1 -1
  50. package/dist/middleware/circuit-breaker.d.ts.map +1 -1
  51. package/dist/middleware/circuit-breaker.js +18 -2
  52. package/dist/middleware/circuit-breaker.js.map +1 -1
  53. package/dist/middleware/file-lock.d.ts.map +1 -1
  54. package/dist/middleware/file-lock.js +8 -3
  55. package/dist/middleware/file-lock.js.map +1 -1
  56. package/dist/monitor.d.ts.map +1 -1
  57. package/dist/monitor.js +1 -0
  58. package/dist/monitor.js.map +1 -1
  59. package/dist/parser.d.ts.map +1 -1
  60. package/dist/parser.js +19 -2
  61. package/dist/parser.js.map +1 -1
  62. package/dist/pin-memory.d.ts +2 -2
  63. package/dist/pin-memory.d.ts.map +1 -1
  64. package/dist/pin-memory.js.map +1 -1
  65. package/dist/repo-map.d.ts.map +1 -1
  66. package/dist/repo-map.js +26 -0
  67. package/dist/repo-map.js.map +1 -1
  68. package/dist/router.d.ts.map +1 -1
  69. package/dist/router.js +58 -18
  70. package/dist/router.js.map +1 -1
  71. package/dist/undo.js +1 -1
  72. package/dist/undo.js.map +1 -1
  73. package/dist/utils/imports.d.ts.map +1 -1
  74. package/dist/utils/imports.js +8 -4
  75. package/dist/utils/imports.js.map +1 -1
  76. package/dist/utils/latency-tracker.d.ts +22 -0
  77. package/dist/utils/latency-tracker.d.ts.map +1 -0
  78. package/dist/utils/latency-tracker.js +49 -0
  79. package/dist/utils/latency-tracker.js.map +1 -0
  80. package/dist/utils/logger.d.ts +5 -2
  81. package/dist/utils/logger.d.ts.map +1 -1
  82. package/dist/utils/logger.js +29 -6
  83. package/dist/utils/logger.js.map +1 -1
  84. package/dist/utils/path-jail.d.ts.map +1 -1
  85. package/dist/utils/path-jail.js +3 -0
  86. package/dist/utils/path-jail.js.map +1 -1
  87. package/package.json +96 -79
@@ -43,6 +43,157 @@ async function ensureHologramReady(kernel, nrekiMode) {
43
43
  kernel.setShadows(scanResult.prunable, scanResult.unprunable, scanResult.ambientFiles);
44
44
  }
45
45
  }
46
+ /** Shared kernel boot logic for edit handlers. */
47
+ async function ensureKernelBooted(deps) {
48
+ if (!deps.kernel || deps.nrekiMode === "syntax")
49
+ return false;
50
+ if (!deps.kernel.isBooted()) {
51
+ logger.info(`Booting kernel (${deps.nrekiMode} mode). First edit will be slower.`);
52
+ try {
53
+ await ensureHologramReady(deps.kernel, deps.nrekiMode ?? "");
54
+ deps.kernel.boot(process.cwd(), deps.nrekiMode);
55
+ }
56
+ catch (err) {
57
+ logger.error(`Kernel boot failed: ${err.message}. Falling back to Layer 1.`);
58
+ }
59
+ }
60
+ return deps.kernel.isBooted();
61
+ }
62
+ // ─── H-07: Shared kernel verification + TTRD + Chronos logic ────────
63
+ //
64
+ // Extracted from handleEdit and handleBatchEdit to eliminate ~150 lines
65
+ // of duplicated post-intercept logic.
66
+ /**
67
+ * Shared kernel verification pipeline (Layer 2).
68
+ *
69
+ * Given a kernel intercept result, performs:
70
+ * 1. node_modules error filtering
71
+ * 2. Chronos error recording
72
+ * 3. Backup + commit/rollback
73
+ * 4. TTRD regression tracking + debt payment
74
+ * 5. Chronos friction sync
75
+ *
76
+ * @param kernelResult - result from interceptAtomicBatch
77
+ * @param committedFiles - paths that were committed (for backup + Chronos)
78
+ * @param deps - router dependencies (kernel, chronos)
79
+ * @param rejectionHeader - header text for the rejection response
80
+ */
81
+ async function processKernelResult(kernelResult, committedFiles, deps, rejectionHeader) {
82
+ const kernel = deps.kernel;
83
+ let ttrdFeedback = "";
84
+ if (!kernelResult.safe) {
85
+ const agentErrors = kernelResult.structured?.filter(e => !e.file.match(/[/\\]node_modules[/\\]/)) || [];
86
+ if (agentErrors.length === 0) {
87
+ // All errors are in node_modules — safe to commit
88
+ logger.warn(`${kernelResult.structured?.length} error(s) in node_modules ignored.`);
89
+ for (const fp of committedFiles) {
90
+ try {
91
+ saveBackup(process.cwd(), fp);
92
+ }
93
+ catch { }
94
+ }
95
+ await kernel.commitToDisk();
96
+ return { committed: true, ttrdFeedback };
97
+ }
98
+ // Real errors in agent code — record + rollback
99
+ if (deps.chronos) {
100
+ const errorByFile = new Map();
101
+ for (const e of agentErrors) {
102
+ if (!errorByFile.has(e.file))
103
+ errorByFile.set(e.file, e.message);
104
+ }
105
+ for (const [fragileFile, firstMsg] of errorByFile.entries()) {
106
+ deps.chronos.recordSemanticError(fragileFile, firstMsg);
107
+ }
108
+ }
109
+ await kernel.rollbackAll();
110
+ const structuredInfo = "\n\nSemantic errors:\n" +
111
+ agentErrors.map(e => ` \u2192 ${path.relative(process.cwd(), e.file)} (${e.line},${e.column}): ${e.code} - ${e.message}`).join("\n");
112
+ return {
113
+ committed: false,
114
+ ttrdFeedback,
115
+ response: {
116
+ content: [{
117
+ type: "text",
118
+ text: `${rejectionHeader}` +
119
+ `Layer 1 (syntax) passed, but Layer 2 (cross-file semantics) detected errors.\n` +
120
+ `\ud83d\udee1\ufe0f **DISK UNTOUCHED: Caught in RAM. No files modified.**${structuredInfo}\n\n` +
121
+ `Fix the type errors and retry. If you changed a function signature, ` +
122
+ `use \`nreki_code action:"batch_edit"\` to update all callers in one atomic transaction.\n\n` +
123
+ `[NREKI: validated in ${kernelResult.latencyMs}ms]`,
124
+ }],
125
+ isError: true,
126
+ },
127
+ };
128
+ }
129
+ // ─── Safe: backup → commit → TTRD ───
130
+ for (const fp of committedFiles) {
131
+ try {
132
+ saveBackup(process.cwd(), fp);
133
+ }
134
+ catch { }
135
+ }
136
+ if (kernelResult.healedFiles) {
137
+ for (const hf of kernelResult.healedFiles) {
138
+ try {
139
+ saveBackup(process.cwd(), path.resolve(process.cwd(), hf));
140
+ }
141
+ catch { }
142
+ if (deps.chronos)
143
+ deps.chronos.recordHeal(hf);
144
+ }
145
+ }
146
+ await kernel.commitToDisk();
147
+ if (deps.chronos && kernelResult.postContracts) {
148
+ // Regression tracking
149
+ if (kernelResult.regressions && kernelResult.regressions.length > 0) {
150
+ const byFile = new Map();
151
+ for (const r of kernelResult.regressions) {
152
+ const arr = byFile.get(r.filePath) || [];
153
+ arr.push(r);
154
+ byFile.set(r.filePath, arr);
155
+ }
156
+ const penaltyList = [];
157
+ for (const [fPath, regs] of byFile.entries()) {
158
+ deps.chronos.recordRegressions(path.resolve(process.cwd(), fPath), regs);
159
+ for (const r of regs) {
160
+ penaltyList.push(` - \`${r.symbol}\` in \`${path.basename(fPath)}\`: \`${r.oldType}\` -> \`${r.newType}\``);
161
+ }
162
+ }
163
+ ttrdFeedback += `\n\n**TYPE REGRESSION DETECTED**\n` +
164
+ `The edit compiled successfully, but weakened type safety:\n` +
165
+ `${penaltyList.join("\n")}\n` +
166
+ `This technical debt has been logged. Restore strict typing instead of using any/unknown.`;
167
+ }
168
+ // Debt payment tracking
169
+ const allPaid = [];
170
+ for (const fp of committedFiles) {
171
+ const posixPath = kernel.resolvePosixPath(fp);
172
+ const fileContracts = kernelResult.postContracts.get(posixPath);
173
+ const paid = deps.chronos.assessDebtPayments(fp, fileContracts);
174
+ if (paid.length > 0) {
175
+ allPaid.push(`\`${path.basename(fp)}\`: ${paid.join(", ")}`);
176
+ }
177
+ const hasRegressionHere = kernelResult.regressions?.some(r => r.filePath === posixPath);
178
+ if (!hasRegressionHere) {
179
+ deps.chronos.recordSuccess(fp);
180
+ }
181
+ }
182
+ if (allPaid.length > 0) {
183
+ ttrdFeedback += `\n\n**TYPE DEBT PAID**\n` +
184
+ `Strict typing restored for: ${allPaid.map(s => s).join(", ")}.\n` +
185
+ `Friction score reduced.`;
186
+ }
187
+ deps.chronos.syncTechDebt(kernel.getInitialErrorCount(), kernel.getCurrentErrorCount());
188
+ }
189
+ else if (deps.chronos) {
190
+ for (const fp of committedFiles) {
191
+ deps.chronos.recordSuccess(fp);
192
+ }
193
+ deps.chronos.syncTechDebt(kernel.getInitialErrorCount(), kernel.getCurrentErrorCount());
194
+ }
195
+ return { committed: true, ttrdFeedback };
196
+ }
46
197
  // ─── Read ───────────────────────────────────────────────────────────
47
198
  export async function handleRead(params, deps) {
48
199
  const { engine } = deps;
@@ -307,20 +458,7 @@ export async function handleEdit(params, deps) {
307
458
  const mode = (typeof params.mode === "string" && ["replace", "insert_before", "insert_after"].includes(params.mode))
308
459
  ? params.mode
309
460
  : "replace";
310
- let useKernel = false;
311
- if (deps.kernel && deps.nrekiMode !== "syntax") {
312
- if (!deps.kernel.isBooted()) {
313
- logger.info(`Booting kernel (${deps.nrekiMode} mode). First edit will be slower.`);
314
- try {
315
- await ensureHologramReady(deps.kernel, deps.nrekiMode ?? "");
316
- deps.kernel.boot(process.cwd(), deps.nrekiMode);
317
- }
318
- catch (err) {
319
- logger.error(`Kernel boot failed: ${err.message}. Falling back to Layer 1.`);
320
- }
321
- }
322
- useKernel = deps.kernel.isBooted();
323
- }
461
+ const useKernel = await ensureKernelBooted(deps);
324
462
  const result = await semanticEdit(resolvedPath, symbol, new_code, parser, sandbox, mode, useKernel);
325
463
  if (!result.success || (useKernel && !result.newContent)) {
326
464
  return {
@@ -375,115 +513,34 @@ export async function handleEdit(params, deps) {
375
513
  kernelResult = await deps.kernel.interceptAtomicBatch([
376
514
  { targetFile: resolvedPath, proposedContent: result.newContent },
377
515
  ], dependentsToInject);
378
- if (!kernelResult.safe) {
379
- const agentErrors = kernelResult.structured?.filter(e => !e.file.match(/[/\\]node_modules[/\\]/)) || [];
380
- if (agentErrors.length === 0) {
381
- logger.warn(`${kernelResult.structured?.length} error(s) in node_modules ignored.`);
382
- try {
383
- saveBackup(process.cwd(), resolvedPath);
384
- }
385
- catch { }
386
- await deps.kernel.commitToDisk();
387
- }
388
- else {
389
- if (deps.chronos) {
390
- const errorByFile = new Map();
391
- for (const e of agentErrors) {
392
- if (!errorByFile.has(e.file))
393
- errorByFile.set(e.file, e.message);
394
- }
395
- for (const [fragileFile, firstMsg] of errorByFile.entries()) {
396
- deps.chronos.recordSemanticError(fragileFile, firstMsg);
397
- }
398
- }
399
- await deps.kernel.rollbackAll();
400
- const structuredInfo = "\n\nSemantic errors:\n" +
401
- agentErrors.map(e => ` → ${path.relative(process.cwd(), e.file)} (${e.line},${e.column}): ${e.code} - ${e.message}`).join("\n");
402
- return {
403
- content: [{
404
- type: "text",
405
- text: `## Semantic Edit: BLOCKED BY NREKI (Layer 2)\n\n` +
406
- `**Symbol:** ${symbol}\n` +
407
- `**File:** ${file}\n\n` +
408
- `Layer 1 (syntax) passed, but Layer 2 (cross-file semantics) detected errors.\n` +
409
- `🛡️ **DISK UNTOUCHED: Caught in RAM. File not modified.**${structuredInfo}\n\n` +
410
- `Fix the type errors and retry. If you changed a function signature, ` +
411
- `use \`nreki_code action:"batch_edit"\` to update all callers in one atomic transaction.\n\n` +
412
- `[NREKI: validated in ${kernelResult.latencyMs}ms]`,
413
- }],
414
- isError: true,
415
- };
416
- }
417
- }
418
- else {
419
- try {
420
- saveBackup(process.cwd(), resolvedPath);
421
- }
422
- catch { }
423
- if (kernelResult.healedFiles) {
424
- for (const hf of kernelResult.healedFiles) {
425
- try {
426
- saveBackup(process.cwd(), path.resolve(process.cwd(), hf));
427
- }
428
- catch { }
429
- if (deps.chronos)
430
- deps.chronos.recordHeal(hf);
431
- }
432
- }
433
- await deps.kernel.commitToDisk();
434
- if (deps.chronos && kernelResult.postContracts) {
435
- if (kernelResult.regressions && kernelResult.regressions.length > 0) {
436
- const byFile = new Map();
437
- for (const r of kernelResult.regressions) {
438
- const arr = byFile.get(r.filePath) || [];
439
- arr.push(r);
440
- byFile.set(r.filePath, arr);
441
- }
442
- const penaltyList = [];
443
- for (const [fPath, regs] of byFile.entries()) {
444
- deps.chronos.recordRegressions(path.resolve(process.cwd(), fPath), regs);
445
- for (const r of regs) {
446
- penaltyList.push(` - \`${r.symbol}\` in \`${path.basename(fPath)}\`: \`${r.oldType}\` -> \`${r.newType}\``);
447
- }
448
- }
449
- ttrdFeedback += `\n\n**TYPE REGRESSION DETECTED**\n` +
450
- `The edit compiled successfully, but weakened the type safety of the project:\n` +
451
- `${penaltyList.join("\n")}\n` +
452
- `This technical debt has been logged. Restore strict typing instead of using any/unknown.`;
453
- }
454
- const posixResolved = deps.kernel.resolvePosixPath(resolvedPath);
455
- const fileContracts = kernelResult.postContracts.get(posixResolved);
456
- const paidDebts = deps.chronos.assessDebtPayments(resolvedPath, fileContracts);
457
- if (paidDebts.length > 0) {
458
- ttrdFeedback += `\n\n**TYPE DEBT PAID**\n` +
459
- `Strict typing restored for: ${paidDebts.map(s => `\`${s}\``).join(", ")}.\n` +
460
- `Friction score reduced.`;
461
- }
462
- const hasRegressionHere = kernelResult.regressions?.some(r => r.filePath === posixResolved);
463
- if (!hasRegressionHere) {
464
- deps.chronos.recordSuccess(resolvedPath);
465
- }
466
- deps.chronos.syncTechDebt(deps.kernel.getInitialErrorCount(), deps.kernel.getCurrentErrorCount());
467
- }
468
- else if (deps.chronos) {
469
- deps.chronos.recordSuccess(resolvedPath);
470
- deps.chronos.syncTechDebt(deps.kernel.getInitialErrorCount(), deps.kernel.getCurrentErrorCount());
471
- }
472
- }
516
+ const verifyResult = await processKernelResult(kernelResult, [resolvedPath], deps, `## Semantic Edit: BLOCKED BY NREKI (Layer 2)\n\n` +
517
+ `**Symbol:** ${symbol}\n` +
518
+ `**File:** ${file}\n\n`);
519
+ if (verifyResult.response)
520
+ return verifyResult.response;
521
+ ttrdFeedback = verifyResult.ttrdFeedback;
473
522
  }
474
523
  catch (kernelError) {
475
524
  logger.error(`Kernel error during edit verification: ${kernelError}`);
476
- try {
477
- saveBackup(process.cwd(), resolvedPath);
478
- }
479
- catch { }
480
- fs.writeFileSync(resolvedPath, result.newContent, "utf-8");
481
525
  try {
482
526
  await deps.kernel.rollbackAll();
483
527
  }
484
528
  catch (e) {
485
529
  logger.error(`Rollback after kernel crash also failed: ${e}`);
486
530
  }
531
+ return {
532
+ content: [{
533
+ type: "text",
534
+ text: `## Edit: KERNEL ERROR\n\n` +
535
+ `The edit passed AST validation (Layer 1) but the kernel crashed during ` +
536
+ `cross-file semantic verification (Layer 2).\n\n` +
537
+ `**Error:** ${kernelError}\n\n` +
538
+ `The file was NOT modified. The kernel has been reset.\n` +
539
+ `Retry the edit, or apply manually if you trust the change.\n\n` +
540
+ `[NREKI saved ~0 tokens]`,
541
+ }],
542
+ isError: true,
543
+ };
487
544
  }
488
545
  }
489
546
  // ─── End NREKI Layer 2 ───────────────────────────────────────
@@ -553,12 +610,15 @@ export async function handleBatchEdit(params, deps) {
553
610
  };
554
611
  }
555
612
  }
613
+ const uniquePathSet = new Set();
556
614
  const uniquePaths = [];
557
615
  for (const e of edits) {
558
616
  try {
559
617
  const resolved = safePath(process.cwd(), e.path);
560
- if (!uniquePaths.includes(resolved))
618
+ if (!uniquePathSet.has(resolved)) {
619
+ uniquePathSet.add(resolved);
561
620
  uniquePaths.push(resolved);
621
+ }
562
622
  }
563
623
  catch (err) {
564
624
  return {
@@ -612,20 +672,7 @@ export async function handleBatchEdit(params, deps) {
612
672
  ? e.mode
613
673
  : "replace",
614
674
  }));
615
- let useKernel = false;
616
- if (deps.kernel && deps.nrekiMode !== "syntax") {
617
- if (!deps.kernel.isBooted()) {
618
- logger.info(`Booting kernel (${deps.nrekiMode} mode). First edit will be slower.`);
619
- try {
620
- await ensureHologramReady(deps.kernel, deps.nrekiMode ?? "");
621
- deps.kernel.boot(process.cwd(), deps.nrekiMode);
622
- }
623
- catch (err) {
624
- logger.error(`Kernel boot failed: ${err.message}. Falling back to Layer 1.`);
625
- }
626
- }
627
- useKernel = deps.kernel.isBooted();
628
- }
675
+ const useKernel = await ensureKernelBooted(deps);
629
676
  const result = await batchSemanticEdit(batchOps, parser, sandbox, process.cwd(), useKernel);
630
677
  if (!result.success || (useKernel && !result.vfs)) {
631
678
  return {
@@ -681,130 +728,38 @@ export async function handleBatchEdit(params, deps) {
681
728
  }
682
729
  if (kernelEdits.length > 0) {
683
730
  const kernelResult = await deps.kernel.interceptAtomicBatch(kernelEdits, batchDependents);
684
- if (!kernelResult.safe) {
685
- const agentErrors = kernelResult.structured?.filter(e => !e.file.match(/[/\\]node_modules[/\\]/)) || [];
686
- if (agentErrors.length === 0) {
687
- logger.warn(`${kernelResult.structured?.length} error(s) in node_modules ignored.`);
688
- for (const filePath of result.vfs.keys()) {
689
- try {
690
- saveBackup(process.cwd(), filePath);
691
- }
692
- catch { }
693
- }
694
- await deps.kernel.commitToDisk();
695
- }
696
- else {
697
- if (deps.chronos) {
698
- const errorByFile = new Map();
699
- for (const e of agentErrors) {
700
- if (!errorByFile.has(e.file))
701
- errorByFile.set(e.file, e.message);
702
- }
703
- for (const [fragileFile, firstMsg] of errorByFile.entries()) {
704
- deps.chronos.recordSemanticError(fragileFile, firstMsg);
705
- }
706
- }
707
- await deps.kernel.rollbackAll();
708
- const structuredInfo = "\n\nSemantic errors:\n" +
709
- agentErrors.map(e => ` → ${path.relative(process.cwd(), e.file)} (${e.line},${e.column}): ${e.code} - ${e.message}`).join("\n");
710
- return {
711
- content: [{
712
- type: "text",
713
- text: `## Batch Edit: BLOCKED BY NREKI (Layer 2)\n\n` +
714
- `**Edits attempted:** ${result.editCount}\n` +
715
- `**Files involved:** ${result.fileCount}\n\n` +
716
- `Layer 1 (syntax) passed for all files, but Layer 2 (cross-file semantics) detected errors.\n` +
717
- `🛡️ **DISK UNTOUCHED: Transaction aborted in RAM. No files were modified.**${structuredInfo}\n\n` +
718
- `If you changed a function signature, include ALL callers in the same batch.\n\n` +
719
- `[NREKI: RAM validated in ${kernelResult.latencyMs}ms]`,
720
- }],
721
- isError: true,
722
- };
723
- }
724
- }
725
- else {
726
- for (const filePath of result.vfs.keys()) {
727
- try {
728
- saveBackup(process.cwd(), filePath);
729
- }
730
- catch { }
731
- }
732
- if (kernelResult.healedFiles) {
733
- for (const hf of kernelResult.healedFiles) {
734
- try {
735
- saveBackup(process.cwd(), path.resolve(process.cwd(), hf));
736
- }
737
- catch { }
738
- if (deps.chronos)
739
- deps.chronos.recordHeal(hf);
740
- }
741
- }
742
- await deps.kernel.commitToDisk();
743
- if (deps.chronos && kernelResult.postContracts) {
744
- if (kernelResult.regressions && kernelResult.regressions.length > 0) {
745
- const byFile = new Map();
746
- for (const r of kernelResult.regressions) {
747
- const arr = byFile.get(r.filePath) || [];
748
- arr.push(r);
749
- byFile.set(r.filePath, arr);
750
- }
751
- const penaltyList = [];
752
- for (const [fPath, regs] of byFile.entries()) {
753
- deps.chronos.recordRegressions(path.resolve(process.cwd(), fPath), regs);
754
- for (const r of regs) {
755
- penaltyList.push(` - \`${r.symbol}\` in \`${path.basename(fPath)}\`: \`${r.oldType}\` -> \`${r.newType}\``);
756
- }
757
- }
758
- batchTtrdFeedback += `\n\n**TYPE REGRESSION DETECTED**\n` +
759
- `The batch compiled successfully, but weakened type safety:\n` +
760
- `${penaltyList.join("\n")}\n` +
761
- `This technical debt has been logged. Restore strict typing.`;
762
- }
763
- const allPaid = [];
764
- for (const filePath of result.vfs.keys()) {
765
- const posixPath = deps.kernel.resolvePosixPath(filePath);
766
- const fileContracts = kernelResult.postContracts.get(posixPath);
767
- const paid = deps.chronos.assessDebtPayments(filePath, fileContracts);
768
- if (paid.length > 0) {
769
- allPaid.push(`\`${path.basename(filePath)}\`: ${paid.join(", ")}`);
770
- }
771
- const hasRegressionHere = kernelResult.regressions?.some(r => r.filePath === posixPath);
772
- if (!hasRegressionHere) {
773
- deps.chronos.recordSuccess(filePath);
774
- }
775
- }
776
- if (allPaid.length > 0) {
777
- batchTtrdFeedback += `\n\n**TYPE DEBT PAID**\n` +
778
- `Strict typing restored:\n` +
779
- `${allPaid.join("\n")}\n` +
780
- `Friction score reduced.`;
781
- }
782
- deps.chronos.syncTechDebt(deps.kernel.getInitialErrorCount(), deps.kernel.getCurrentErrorCount());
783
- }
784
- else if (deps.chronos) {
785
- for (const filePath of result.vfs.keys()) {
786
- deps.chronos.recordSuccess(filePath);
787
- }
788
- deps.chronos.syncTechDebt(deps.kernel.getInitialErrorCount(), deps.kernel.getCurrentErrorCount());
789
- }
790
- }
731
+ const committedFiles = Array.from(result.vfs.keys());
732
+ const verifyResult = await processKernelResult(kernelResult, committedFiles, deps, `## Batch Edit: BLOCKED BY NREKI (Layer 2)\n\n` +
733
+ `**Edits attempted:** ${result.editCount}\n` +
734
+ `**Files involved:** ${result.fileCount}\n\n`);
735
+ if (verifyResult.response)
736
+ return verifyResult.response;
737
+ batchTtrdFeedback = verifyResult.ttrdFeedback;
791
738
  }
792
739
  }
793
740
  catch (kernelError) {
794
741
  logger.error(`Kernel error during batch verification: ${kernelError}`);
795
- for (const [filePath, content] of result.vfs.entries()) {
796
- try {
797
- saveBackup(process.cwd(), filePath);
798
- }
799
- catch { }
800
- fs.writeFileSync(filePath, content, "utf-8");
801
- }
802
742
  try {
803
743
  await deps.kernel.rollbackAll();
804
744
  }
805
745
  catch (e) {
806
746
  logger.error(`Rollback after kernel crash also failed: ${e}`);
807
747
  }
748
+ for (const rp of acquiredLocks)
749
+ releaseFileLock(rp);
750
+ return {
751
+ content: [{
752
+ type: "text",
753
+ text: `## Batch Edit: KERNEL ERROR\n\n` +
754
+ `All ${result.editCount} edits passed AST validation (Layer 1) but the kernel ` +
755
+ `crashed during cross-file semantic verification (Layer 2).\n\n` +
756
+ `**Error:** ${kernelError}\n\n` +
757
+ `No files were modified. The kernel has been reset.\n` +
758
+ `Retry the batch edit, or apply edits individually.\n\n` +
759
+ `[NREKI saved ~0 tokens]`,
760
+ }],
761
+ isError: true,
762
+ };
808
763
  }
809
764
  }
810
765
  // ─── End NREKI Layer 2 ───────────────────────────────────────