Skip to main content

Overview

Signals are actionable patterns extracted from logs, transcripts, and memory that trigger evolution. They serve as the bridge between raw runtime observations and structured evolution decisions.

Signal Types

Signals fall into three broad categories:

Defensive

Errors, missing resources, stability issues

Opportunity

Feature requests, capability gaps, performance bottlenecks

Meta

Stagnation, repair loops, drift detection

Extraction Logic

The extractSignals() function scans multiple data sources to build a comprehensive signal list:
// From src/gep/signals.js:137
function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, userSnippet, recentEvents }) {
  var signals = [];
  var corpus = [
    String(recentSessionTranscript || ''),
    String(todayLog || ''),
    String(memorySnippet || ''),
    String(userSnippet || ''),
  ].join('\n');
  var lower = corpus.toLowerCase();
  
  // Analyze recent evolution history for de-duplication
  var history = analyzeRecentHistory(recentEvents || []);
  
  // Extract signals...
}

Defensive Signals

These signals indicate errors or missing resources that need immediate repair.

Error Detection

// From src/gep/signals.js:154
var errorHit = /\[error\]|error:|exception:|iserror":true|"status":\s*"error"|"status":\s*"failed"|错误\s*[::]|异常\s*[::]|报错\s*[::]|失败\s*[::]/.test(lower);
if (errorHit) signals.push('log_error');
The regex prioritizes structured error markers to avoid false positives on “fail”/“failed” in normal text.

Error Signature

For more reproducible error tracking, the system extracts an error signature:
// From src/gep/signals.js:158
try {
  var lines = corpus.split('\n').map(l => String(l || '').trim()).filter(Boolean);
  
  var errLine = lines.find(l => 
    /\b(typeerror|referenceerror|syntaxerror)\b\s*:|error\s*:|exception\s*:|\[error|错误\s*[::]|异常\s*[::]|报错\s*[::]|失败\s*[::]/i.test(l)
  ) || null;
  
  if (errLine) {
    var clipped = errLine.replace(/\s+/g, ' ').slice(0, 260);
    signals.push('errsig:' + clipped);
  }
} catch (e) {}

Recurring Error Detection

When the same error appears 3+ times, it’s flagged as recurring:
// From src/gep/signals.js:188
try {
  var errorCounts = {};
  var errPatterns = corpus.match(/(?:LLM error|"error"|"status":\s*"error")[^}]{0,200}/gi) || [];
  
  for (var ep = 0; ep < errPatterns.length; ep++) {
    var key = errPatterns[ep].replace(/\s+/g, ' ').slice(0, 100);
    errorCounts[key] = (errorCounts[key] || 0) + 1;
  }
  
  var recurringErrors = Object.entries(errorCounts).filter(e => e[1] >= 3);
  if (recurringErrors.length > 0) {
    signals.push('recurring_error');
    var topErr = recurringErrors.sort((a, b) => b[1] - a[1])[0];
    signals.push('recurring_errsig(' + topErr[1] + 'x):' + topErr[0].slice(0, 150));
  }
} catch (e) {}

Missing Resources

// From src/gep/signals.js:174
if (lower.includes('memory.md missing')) signals.push('memory_missing');
if (lower.includes('user.md missing')) signals.push('user_missing');
if (lower.includes('key missing')) signals.push('integration_key_missing');
if (lower.includes('no session logs found')) signals.push('session_logs_missing');

Opportunity Signals

These signals indicate chances to innovate rather than just fix errors.

Feature Requests

Supports 4 languages (EN, ZH-CN, ZH-TW, JA) with snippet extraction:
// From src/gep/signals.js:212
var featureRequestSnippet = '';
var featEn = corpus.match(/\b(add|implement|create|build|make|develop|write|design)\b[^.?!\n]{3,120}\b(feature|function|module|capability|tool|support|endpoint|command|option|mode)\b/i);
if (featEn) featureRequestSnippet = featEn[0].replace(/\s+/g, ' ').trim().slice(0, 200);

// Chinese detection
if (!featureRequestSnippet && /加个|实现一下|做个|想要\s*一个|需要\s*一个/.test(corpus)) {
  var featZh = corpus.match(/.{0,100}(加个|实现一下|做个|想要\s*一个|需要\s*一个).{0,100}/);
  if (featZh) featureRequestSnippet = featZh[0].replace(/\s+/g, ' ').trim().slice(0, 200);
}

if (featureRequestSnippet) {
  signals.push('user_feature_request:' + featureRequestSnippet);
}
  • English: “add”, “implement”, “create”, “build”, “make”, “I want”, “I need”
  • Chinese (Simplified): 加个, 实现一下, 做个, 想要一个, 需要一个
  • Chinese (Traditional): 加個, 實現一下, 做個, 想要一個
  • Japanese: 追加, 実装, 作って, 機能を, が欲しい

Improvement Suggestions

// From src/gep/signals.js:246
if (!errorHit) {  // Only detect improvements when there's no error
  var impEn = corpus.match(/.{0,80}\b(should be|could be better|improve|enhance|upgrade|refactor|clean up|simplify|streamline)\b.{0,80}/i);
  if (impEn) improvementSnippet = impEn[0].replace(/\s+/g, ' ').trim().slice(0, 200);
  
  if (improvementSnippet) {
    signals.push('user_improvement_suggestion:' + improvementSnippet);
  }
}

Performance Bottlenecks

// From src/gep/signals.js:271
if (/\b(slow|timeout|timed?\s*out|latency|bottleneck|took too long|performance issue|high cpu|high memory|oom|out of memory)\b/i.test(lower)) {
  signals.push('perf_bottleneck');
}

Capability Gaps

// From src/gep/signals.js:276
if (/\b(not supported|cannot|doesn'?t support|no way to|missing feature|unsupported|not available|not implemented|no support for)\b/i.test(lower)) {
  if (!signals.includes('memory_missing') && !signals.includes('user_missing')) {
    signals.push('capability_gap');
  }
}

Signal De-duplication

To prevent repair loops, the system analyzes recent evolution history and suppresses over-processed signals.

History Analysis

// From src/gep/signals.js:28
function analyzeRecentHistory(recentEvents) {
  var recent = recentEvents.slice(-10);
  var tail = recent.slice(-8);
  
  // Count signal frequency in last 8 events
  var signalFreq = {};
  for (var j = 0; j < tail.length; j++) {
    var sigs = Array.isArray(tail[j].signals) ? tail[j].signals : [];
    for (var k = 0; k < sigs.length; k++) {
      var s = String(sigs[k]);
      var key = s.startsWith('errsig:') ? 'errsig'
        : s.startsWith('recurring_errsig') ? 'recurring_errsig'
        : s.startsWith('user_feature_request:') ? 'user_feature_request'
        : s;
      signalFreq[key] = (signalFreq[key] || 0) + 1;
    }
  }
  
  // Suppress signals that appeared in 3+ of the last 8 events
  var suppressedSignals = new Set();
  for (var [sig, count] of Object.entries(signalFreq)) {
    if (count >= 3) suppressedSignals.add(sig);
  }
  
  return { suppressedSignals, recentIntents, consecutiveRepairCount, ... };
}

Suppression Logic

// From src/gep/signals.js:322
if (history.suppressedSignals.size > 0) {
  var beforeDedup = signals.length;
  signals = signals.filter(s => {
    var key = s.startsWith('errsig:') ? 'errsig'
      : s.startsWith('recurring_errsig') ? 'recurring_errsig'
      : s.startsWith('user_feature_request:') ? 'user_feature_request'
      : s;
    return !history.suppressedSignals.has(key);
  });
  
  if (beforeDedup > 0 && signals.length === 0) {
    // All signals suppressed = stable but stuck in loop
    signals.push('evolution_stagnation_detected');
    signals.push('stable_success_plateau');
  }
}
If all signals are suppressed, the system injects evolution_stagnation_detected to force a strategy change.

Repair Loop Detection

When 3+ consecutive repairs fail, the system forces innovation intent:
// From src/gep/signals.js:342
if (history.consecutiveRepairCount >= 3) {
  // Remove repair-only signals
  signals = signals.filter(s => 
    s !== 'log_error' && !s.startsWith('errsig:') && !s.startsWith('recurring_errsig')
  );
  
  if (signals.length === 0) {
    signals.push('repair_loop_detected');
    signals.push('stable_success_plateau');
  }
  
  signals.push('force_innovation_after_repair_loop');
}

Empty Cycle Detection

When 50%+ of last 8 cycles produced zero code changes, the evolver is spinning idle:
// From src/gep/signals.js:356
if (history.emptyCycleCount >= 4) {
  signals = signals.filter(s => 
    s !== 'log_error' && !s.startsWith('errsig:')
  );
  if (!signals.includes('empty_cycle_loop_detected')) 
    signals.push('empty_cycle_loop_detected');
  if (!signals.includes('stable_success_plateau')) 
    signals.push('stable_success_plateau');
}

Saturation & Graceful Degradation

When consecutive empty cycles pile up, the system switches to steady-state mode:
// From src/gep/signals.js:370
if (history.consecutiveEmptyCycles >= 5) {
  if (!signals.includes('force_steady_state')) 
    signals.push('force_steady_state');
  if (!signals.includes('evolution_saturation')) 
    signals.push('evolution_saturation');
} else if (history.consecutiveEmptyCycles >= 3) {
  if (!signals.includes('evolution_saturation')) 
    signals.push('evolution_saturation');
}
This addresses the Echo-MingXuan failure: Cycle #55 hit “no committable changes” and load spiked to 1.30 because there was no degradation strategy.

Failure Streak Awareness

After 5+ consecutive failures, the system bans the dominant gene to force strategy change:
// From src/gep/signals.js:382
if (history.consecutiveFailureCount >= 3) {
  signals.push('consecutive_failure_streak_' + history.consecutiveFailureCount);
  
  if (history.consecutiveFailureCount >= 5) {
    signals.push('failure_loop_detected');
    
    var topGene = null;
    var topGeneCount = 0;
    for (var [gene, count] of Object.entries(history.geneFreq)) {
      if (count > topGeneCount) {
        topGeneCount = count;
        topGene = gene;
      }
    }
    if (topGene) signals.push('ban_gene:' + topGene);
  }
}

Tool Usage Analytics

High tool usage can indicate inefficient automation or manual loops:
// From src/gep/signals.js:284
var toolUsage = {};
var toolMatches = corpus.match(/\[TOOL:\s*([\w-]+)\]/g) || [];

for (var i = 0; i < toolMatches.length; i++) {
  var toolName = toolMatches[i].match(/\[TOOL:\s*([\w-]+)\]/)[1];
  toolUsage[toolName] = (toolUsage[toolName] || 0) + 1;
}

Object.keys(toolUsage).forEach(tool => {
  if (toolUsage[tool] >= 10) {
    signals.push('high_tool_usage:' + tool);
  }
  if (tool === 'exec' && toolUsage[tool] >= 5) {
    signals.push('repeated_tool_usage:exec');
  }
});

Signal Prioritization

Cosmetic signals are removed when actionable signals exist:
// From src/gep/signals.js:312
var actionable = signals.filter(s => 
  s !== 'user_missing' && 
  s !== 'memory_missing' && 
  s !== 'session_logs_missing'
);

if (actionable.length > 0) {
  signals = actionable;
}

Default Fallback

// From src/gep/signals.js:410
if (signals.length === 0) {
  signals.push('stable_success_plateau');
}

return Array.from(new Set(signals));

Example Signal Flow

Next Steps

Evolution Cycle

See how signals trigger the evolution cycle

Mutations

Learn how signals map to mutation categories

Build docs developers (and LLMs) love