@evomap/evolver 1.31.0 → 1.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -671,6 +671,108 @@ function buildFailureReason(constraintCheck, validation, protocolViolations, can
671
671
  return reasons.join('; ').slice(0, 2000) || 'unknown';
672
672
  }
673
673
 
674
+ function buildSoftFailureLearningSignals(opts) {
675
+ const { expandSignals } = require('./learningSignals');
676
+ var signals = opts && Array.isArray(opts.signals) ? opts.signals : [];
677
+ var failureReason = opts && opts.failureReason ? String(opts.failureReason) : '';
678
+ var violations = opts && Array.isArray(opts.violations) ? opts.violations : [];
679
+ var validationResults = opts && Array.isArray(opts.validationResults) ? opts.validationResults : [];
680
+ var validationText = validationResults
681
+ .filter(function (r) { return r && r.ok === false; })
682
+ .map(function (r) { return [r.cmd, r.stderr, r.stdout].filter(Boolean).join(' '); })
683
+ .join(' ');
684
+ return expandSignals(signals.concat(violations), failureReason + ' ' + validationText)
685
+ .filter(function (tag) {
686
+ return tag.indexOf('problem:') === 0 || tag.indexOf('risk:') === 0 || tag.indexOf('area:') === 0 || tag.indexOf('action:') === 0;
687
+ });
688
+ }
689
+
690
+ function classifyFailureMode(opts) {
691
+ var constraintViolations = opts && Array.isArray(opts.constraintViolations) ? opts.constraintViolations : [];
692
+ var protocolViolations = opts && Array.isArray(opts.protocolViolations) ? opts.protocolViolations : [];
693
+ var validation = opts && opts.validation ? opts.validation : null;
694
+ var canary = opts && opts.canary ? opts.canary : null;
695
+
696
+ if (constraintViolations.some(function (v) {
697
+ var s = String(v || '');
698
+ return /HARD CAP BREACH|CRITICAL_FILE_|critical_path_modified|forbidden_path touched|ethics:/i.test(s);
699
+ })) {
700
+ return { mode: 'hard', reasonClass: 'constraint_destructive', retryable: false };
701
+ }
702
+
703
+ if (protocolViolations.length > 0) {
704
+ return { mode: 'hard', reasonClass: 'protocol', retryable: false };
705
+ }
706
+
707
+ if (canary && !canary.ok && !canary.skipped) {
708
+ return { mode: 'hard', reasonClass: 'canary', retryable: false };
709
+ }
710
+
711
+ if (constraintViolations.length > 0) {
712
+ return { mode: 'hard', reasonClass: 'constraint', retryable: false };
713
+ }
714
+
715
+ if (validation && validation.ok === false) {
716
+ return { mode: 'soft', reasonClass: 'validation', retryable: true };
717
+ }
718
+
719
+ return { mode: 'soft', reasonClass: 'unknown', retryable: true };
720
+ }
721
+
722
+ function adaptGeneFromLearning(opts) {
723
+ var gene = opts && opts.gene && opts.gene.type === 'Gene' ? opts.gene : null;
724
+ if (!gene) return gene;
725
+
726
+ var outcomeStatus = String(opts && opts.outcomeStatus || '').toLowerCase();
727
+ var learningSignals = Array.isArray(opts && opts.learningSignals) ? opts.learningSignals : [];
728
+ var failureMode = opts && opts.failureMode && typeof opts.failureMode === 'object'
729
+ ? opts.failureMode
730
+ : { mode: 'soft', reasonClass: 'unknown', retryable: true };
731
+
732
+ if (!Array.isArray(gene.learning_history)) gene.learning_history = [];
733
+ if (!Array.isArray(gene.signals_match)) gene.signals_match = [];
734
+
735
+ var seenSignal = new Set(gene.signals_match.map(function (s) { return String(s); }));
736
+ if (outcomeStatus === 'success') {
737
+ for (var i = 0; i < learningSignals.length; i++) {
738
+ var sig = String(learningSignals[i] || '');
739
+ if (!sig || seenSignal.has(sig)) continue;
740
+ if (sig.indexOf('problem:') === 0 || sig.indexOf('area:') === 0) {
741
+ gene.signals_match.push(sig);
742
+ seenSignal.add(sig);
743
+ }
744
+ }
745
+ }
746
+
747
+ gene.learning_history.push({
748
+ at: nowIso(),
749
+ outcome: outcomeStatus || 'unknown',
750
+ mode: failureMode.mode || 'soft',
751
+ reason_class: failureMode.reasonClass || 'unknown',
752
+ retryable: !!failureMode.retryable,
753
+ learning_signals: learningSignals.slice(0, 12),
754
+ });
755
+ if (gene.learning_history.length > 20) {
756
+ gene.learning_history = gene.learning_history.slice(gene.learning_history.length - 20);
757
+ }
758
+
759
+ if (outcomeStatus === 'failed') {
760
+ if (!Array.isArray(gene.anti_patterns)) gene.anti_patterns = [];
761
+ var anti = {
762
+ at: nowIso(),
763
+ mode: failureMode.mode || 'soft',
764
+ reason_class: failureMode.reasonClass || 'unknown',
765
+ learning_signals: learningSignals.slice(0, 8),
766
+ };
767
+ gene.anti_patterns.push(anti);
768
+ if (gene.anti_patterns.length > 12) {
769
+ gene.anti_patterns = gene.anti_patterns.slice(gene.anti_patterns.length - 12);
770
+ }
771
+ }
772
+
773
+ return gene;
774
+ }
775
+
674
776
  function rollbackTracked(repoRoot) {
675
777
  const mode = String(process.env.EVOLVER_ROLLBACK_MODE || 'hard').toLowerCase();
676
778
 
@@ -1157,6 +1259,23 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
1157
1259
  const ts = nowIso();
1158
1260
  const outcomeStatus = success ? 'success' : 'failed';
1159
1261
  const score = clamp01(success ? 0.85 : 0.2);
1262
+ const failureReason = !success ? buildFailureReason(constraintCheck, validation, protocolViolations, canary) : '';
1263
+ const failureMode = !success
1264
+ ? classifyFailureMode({
1265
+ constraintViolations: constraintCheck.violations,
1266
+ protocolViolations: protocolViolations,
1267
+ validation: validation,
1268
+ canary: canary,
1269
+ })
1270
+ : { mode: 'none', reasonClass: null, retryable: false };
1271
+ const softFailureLearningSignals = !success
1272
+ ? buildSoftFailureLearningSignals({
1273
+ signals,
1274
+ failureReason,
1275
+ violations: constraintCheck.violations,
1276
+ validationResults: validation.results,
1277
+ })
1278
+ : [];
1160
1279
 
1161
1280
  const selectedCapsuleId =
1162
1281
  lastRun && typeof lastRun.selected_capsule_id === 'string' && lastRun.selected_capsule_id.trim()
@@ -1224,6 +1343,12 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
1224
1343
  protocol_ok: protocolViolations.length === 0,
1225
1344
  protocol_violations: protocolViolations,
1226
1345
  memory_graph: memoryGraphPath(),
1346
+ soft_failure: success ? null : {
1347
+ learning_signals: softFailureLearningSignals,
1348
+ retryable: !!failureMode.retryable,
1349
+ class: failureMode.reasonClass,
1350
+ mode: failureMode.mode,
1351
+ },
1227
1352
  },
1228
1353
  };
1229
1354
  // Build desensitized execution trace for cross-agent experience sharing
@@ -1303,7 +1428,8 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
1303
1428
  ? 'Failed: ' + geneUsed.id + ' on signals [' + (signals.slice(0, 3).join(', ') || 'none') + ']'
1304
1429
  : 'Failed evolution on signals [' + (signals.slice(0, 3).join(', ') || 'none') + ']',
1305
1430
  diff_snapshot: diffSnapshot,
1306
- failure_reason: buildFailureReason(constraintCheck, validation, protocolViolations, canary),
1431
+ failure_reason: failureReason,
1432
+ learning_signals: softFailureLearningSignals,
1307
1433
  constraint_violations: constraintCheck.violations || [],
1308
1434
  env_fingerprint: envFp,
1309
1435
  blast_radius: { files: blast.files, lines: blast.lines },
@@ -1331,6 +1457,12 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
1331
1457
  // Apply epigenetic marks to the gene based on outcome and environment
1332
1458
  if (!dryRun && geneUsed && geneUsed.type === 'Gene') {
1333
1459
  try {
1460
+ adaptGeneFromLearning({
1461
+ gene: geneUsed,
1462
+ outcomeStatus: outcomeStatus,
1463
+ learningSignals: success ? signals : softFailureLearningSignals,
1464
+ failureMode: failureMode,
1465
+ });
1334
1466
  applyEpigeneticMarks(geneUsed, envFp, outcomeStatus);
1335
1467
  upsertGene(geneUsed);
1336
1468
  } catch (e) {
@@ -1562,7 +1694,7 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
1562
1694
  // which we already do above. The Hub-side solicitLesson() handles the rest.
1563
1695
  // For failures without a published event (no auto-publish), we still log locally.
1564
1696
  if (!dryRun && !success && event && event.outcome) {
1565
- var failureContent = buildFailureReason(constraintCheck, validation, protocolViolations, canary);
1697
+ var failureContent = failureReason;
1566
1698
  event.failure_reason = failureContent;
1567
1699
  event.summary = geneUsed
1568
1700
  ? 'Failed: ' + geneUsed.id + ' on signals [' + (signals.slice(0, 3).join(', ') || 'none') + '] - ' + failureContent.slice(0, 200)
@@ -1708,6 +1840,9 @@ module.exports = {
1708
1840
  classifyBlastSeverity,
1709
1841
  analyzeBlastRadiusBreakdown,
1710
1842
  compareBlastEstimate,
1843
+ classifyFailureMode,
1844
+ adaptGeneFromLearning,
1845
+ buildSoftFailureLearningSignals,
1711
1846
  runCanaryCheck,
1712
1847
  applyEpigeneticMarks,
1713
1848
  getEpigeneticBoost,