Skip to main content

Overview

The solidify() function is the final validation and commit phase of the evolution cycle. It verifies constraints, runs validation commands, checks blast radius, and either commits changes or rolls them back. Location: src/gep/solidify.js:1001

Function Signature

function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true } = {})

Parameters

intent
string
Evolution intent: repair, optimize, or innovate
summary
string
Human-readable summary of changes
dryRun
boolean
default:false
If true, validate but do not commit changes
rollbackOnFailure
boolean
default:true
If true, automatically rollback failed changes

Return Value

ok
boolean
Whether solidification succeeded
status
string
success or failed
event
object
The EvolutionEvent object created (if successful)
capsule
object
The Capsule object created on success
gene
object
The Gene object used
failure_reason
string
Detailed failure reason if status is failed

Validation Steps

1. Git Repository Check

Location: src/gep/solidify.js:1004
if (!isGitRepo(repoRoot)) {
  return {
    ok: false,
    status: 'failed',
    failure_reason: 'not_a_git_repository',
  };
}

2. Blast Radius Computation

Location: src/gep/solidify.js:206
function computeBlastRadius({ repoRoot, baselineUntracked }) {
  const changedFiles = gitListChangedFiles({ repoRoot });
  const countedFiles = changedFiles.filter(f => isConstraintCountedPath(f, policy));
  
  // Calculate lines changed (unstaged + staged + untracked)
  const churn = stagedUnstagedChurn + untrackedLines;
  
  return {
    files: countedFiles.length,
    lines: churn,
    changed_files: countedFiles,
    ignored_files: ignoredFiles,
    all_changed_files: changedFiles,
  };
}

3. Constraint Validation

Location: src/gep/solidify.js:261
function checkConstraints({ gene, blast, blastRadiusEstimate, repoRoot }) {
  const violations = [];
  const warnings = [];
  
  // Hard cap breach (system-level)
  if (blast.files > BLAST_RADIUS_HARD_CAP_FILES || blast.lines > BLAST_RADIUS_HARD_CAP_LINES) {
    violations.push(`HARD CAP BREACH: ${blast.files} files / ${blast.lines} lines`);
  }
  
  // Gene-level constraint
  if (blast.files > gene.constraints.max_files) {
    violations.push(`max_files exceeded: ${blast.files} > ${gene.constraints.max_files}`);
  }
  
  // Forbidden paths
  for (const f of blast.all_changed_files) {
    if (isForbiddenPath(f, gene.constraints.forbidden_paths)) {
      violations.push(`forbidden_path touched: ${f}`);
    }
  }
  
  return { ok: violations.length === 0, violations, warnings };
}

4. Critical Path Protection

Location: src/gep/solidify.js:492
const CRITICAL_PROTECTED_PREFIXES = [
  'skills/feishu-evolver-wrapper/',
  'skills/feishu-common/',
  'skills/evolver/',
  // ...
];

const CRITICAL_PROTECTED_FILES = [
  'MEMORY.md',
  'SOUL.md',
  'IDENTITY.md',
  'openclaw.json',
  '.env',
  'package.json',
];

function isCriticalProtectedPath(relPath) {
  for (const prefix of CRITICAL_PROTECTED_PREFIXES) {
    if (relPath.startsWith(prefix)) return true;
  }
  for (const f of CRITICAL_PROTECTED_FILES) {
    if (relPath === f) return true;
  }
  return false;
}

5. Validation Commands

Location: src/gep/solidify.js:583
function runValidations(gene, opts = {}) {
  const validation = Array.isArray(gene.validation) ? gene.validation : [];
  const results = [];
  
  for (const cmd of validation) {
    if (!isValidationCommandAllowed(cmd)) {
      results.push({ cmd, ok: false, err: 'BLOCKED: command rejected by safety check' });
      return { ok: false, results };
    }
    
    const r = tryRunCmd(cmd, { cwd: repoRoot, timeoutMs: 180000 });
    results.push({ cmd, ok: r.ok, out: r.out, err: r.err });
    if (!r.ok) return { ok: false, results };
  }
  
  return { ok: true, results };
}

6. Canary Check

Location: src/gep/solidify.js:607
function runCanaryCheck(opts) {
  const canaryScript = path.join(repoRoot, 'src', 'canary.js');
  if (!fs.existsSync(canaryScript)) {
    return { ok: true, skipped: true, reason: 'canary.js not found' };
  }
  
  const r = tryRunCmd(`node "${canaryScript}"`, { cwd: repoRoot, timeoutMs: 30000 });
  return {
    ok: r.ok,
    skipped: false,
    out: r.out.slice(0, 500),
    err: r.err.slice(0, 500),
  };
}

Blast Radius Classification

Location: src/gep/solidify.js:400
const BLAST_RADIUS_HARD_CAP_FILES = 60;
const BLAST_RADIUS_HARD_CAP_LINES = 20000;
const BLAST_WARN_RATIO = 0.8;   // >80% of limit: warning
const BLAST_CRITICAL_RATIO = 2.0; // >200% of limit: critical overrun

function classifyBlastSeverity({ blast, maxFiles }) {
  if (blast.files > BLAST_RADIUS_HARD_CAP_FILES) {
    return { severity: 'hard_cap_breach', message: 'HARD CAP BREACH' };
  }
  if (blast.files > maxFiles * BLAST_CRITICAL_RATIO) {
    return { severity: 'critical_overrun', message: 'CRITICAL OVERRUN' };
  }
  if (blast.files > maxFiles) {
    return { severity: 'exceeded', message: 'max_files exceeded' };
  }
  if (blast.files > maxFiles * BLAST_WARN_RATIO) {
    return { severity: 'approaching_limit', message: 'approaching limit' };
  }
  return { severity: 'within_limit', message: 'within limit' };
}

Rollback Mechanisms

Location: src/gep/solidify.js:663

Tracked Files Rollback

function rollbackTracked(repoRoot) {
  const mode = process.env.EVOLVER_ROLLBACK_MODE || 'hard';
  
  if (mode === 'stash') {
    const stashRef = 'evolver-rollback-' + Date.now();
    execSync('git stash push -m "' + stashRef + '" --include-untracked');
    console.log('[Rollback] Changes stashed with ref: ' + stashRef);
    return;
  }
  
  // Default: hard reset
  tryRunCmd('git restore --staged --worktree .');
  tryRunCmd('git reset --hard');
}

Untracked Files Rollback

Location: src/gep/solidify.js:696
function rollbackNewUntrackedFiles({ repoRoot, baselineUntracked }) {
  const baseline = new Set(baselineUntracked);
  const current = gitListUntrackedFiles(repoRoot);
  const toDelete = current.filter(f => !baseline.has(f));
  
  const deleted = [];
  for (const rel of toDelete) {
    if (isCriticalProtectedPath(rel)) continue; // Never delete critical files
    
    const abs = path.join(repoRoot, rel);
    if (fs.existsSync(abs) && fs.statSync(abs).isFile()) {
      fs.unlinkSync(abs);
      deleted.push(rel);
    }
  }
  
  // Clean up empty directories
  // ...
  
  return { deleted, skipped, removedDirs };
}

Event Structure

Location: src/gep/solidify.js:1163
const event = {
  type: 'EvolutionEvent',
  schema_version: SCHEMA_VERSION,
  id: buildEventId(ts),
  parent: parentEventId || null,
  intent: derivedIntent,
  signals,
  genes_used: [geneId],
  mutation_id: mutation.id,
  personality_state,
  blast_radius: { files: blast.files, lines: blast.lines },
  outcome: { status: outcomeStatus, score },
  capsule_id: capsuleId,
  source_type: 'generated',
  env_fingerprint: envFp,
  validation_report_id: validationReport.id,
  meta: {
    at: ts,
    signal_key: signalKey,
    selector,
    blast_radius_estimate,
    mutation,
    personality,
    gene: { id: geneId, created: ensured.created, reason: ensured.reason },
    constraints_ok: constraintCheck.ok,
    constraint_violations,
    validation_ok: validation.ok,
    canary_ok: canary.ok,
    protocol_ok: protocolViolations.length === 0,
  },
};

Example Usage

const result = solidify({
  intent: 'repair',
  summary: 'Fix error handling in selector',
  dryRun: false,
  rollbackOnFailure: true,
});

if (result.ok) {
  console.log('Evolution succeeded:', result.event.id);
  console.log('Capsule created:', result.capsule.id);
} else {
  console.error('Evolution failed:', result.failure_reason);
}

Build docs developers (and LLMs) love