Overview
The memory graph is an append-only, event-sourced causal memory system that records signals, hypotheses, attempts, and outcomes to provide learning and adaptation over time.
Location: src/gep/memoryGraph.js
Core Functions
recordSignalSnapshot()
Record current signals as a first-class node.
Location: src/gep/memoryGraph.js:314
function recordSignalSnapshot ({ signals , observations })
Current signals extracted from logs
System observations (health, scan time, error count, etc.)
Created MemoryGraphEvent with kind=signal
Event Structure:
{
type : 'MemoryGraphEvent' ,
kind : 'signal' ,
id : 'mge_1678901234567_abc123' ,
ts : '2026-03-09T12:34:56.789Z' ,
signal : {
key : 'log_error|errsig_norm:abc123' ,
signals : [ 'log_error' , 'errsig:TypeError: undefined' ],
error_signature : 'typeerror undefined <path> <n>' ,
},
observed : {
agent : 'main' ,
system_health : 'Uptime: 12.3h | Node: v18.0.0 | ...' ,
recent_error_count : 5 ,
scan_ms : 1234 ,
}
}
recordHypothesis()
Record hypothesis before execution.
Location: src/gep/memoryGraph.js:341
function recordHypothesis ({
signals ,
mutation ,
personality_state ,
selectedGene ,
selector ,
driftEnabled ,
selectedBy ,
capsulesUsed ,
observations ,
})
Personality state with rigor and risk_tolerance
Selector decision metadata
Whether drift mode is enabled
Selection source (e.g., selector, memory_graph+selector)
Array of capsule IDs used
Event Structure:
{
type : 'MemoryGraphEvent' ,
kind : 'hypothesis' ,
id : 'mge_1678901234567_def456' ,
ts : '2026-03-09T12:34:56.789Z' ,
signal : { key : '...' , signals : [ ... ], error_signature : '...' },
hypothesis : {
id : 'hyp_1678901234567_abc123' ,
text : 'Given signal_key=... selecting gene=gene_gep_repair is expected to reduce errors' ,
predicted_outcome : { status : null , score : null },
},
mutation : { id : 'mut_...' , category : 'repair' , risk_level : 'low' , ... },
personality : { key : 'rigor0.8_risk0.3' , state : { rigor : 0.8 , risk_tolerance : 0.3 } },
gene : { id : 'gene_gep_repair' , category : 'repair' },
action : { drift : false , selected_by : 'selector' , selector : { ... } },
capsules : { used : [ 'capsule_123' ] },
}
recordAttempt()
Record action attempt (before outcome is known).
Location: src/gep/memoryGraph.js:407
function recordAttempt ({
signals ,
mutation ,
personality_state ,
selectedGene ,
selector ,
driftEnabled ,
selectedBy ,
hypothesisId ,
capsulesUsed ,
observations ,
})
Parameters are similar to recordHypothesis() but include hypothesisId to link back.
Side Effect: Writes mutable state to memory_graph_state.json for later outcome recording.
recordOutcomeFromState()
Record outcome by closing the last action.
Location: src/gep/memoryGraph.js:633
function recordOutcomeFromState ({ signals , observations })
Current signals after action
Current system observations
Created MemoryGraphEvent with kind=outcome
Outcome Inference:
Location: src/gep/memoryGraph.js:493
function inferOutcomeFromSignals ({ prevHadError , currentHasError }) {
if ( prevHadError && ! currentHasError ) {
return { status: 'success' , score: 0.85 , note: 'error_cleared' };
}
if ( prevHadError && currentHasError ) {
return { status: 'failed' , score: 0.2 , note: 'error_persisted' };
}
if ( ! prevHadError && currentHasError ) {
return { status: 'failed' , score: 0.15 , note: 'new_error_appeared' };
}
return { status: 'success' , score: 0.6 , note: 'stable_no_error' };
}
Enhanced Outcome Inference:
Location: src/gep/memoryGraph.js:535
function inferOutcomeEnhanced ({ prevHadError , currentHasError , baselineObserved , currentObserved }) {
// Try to parse EvolutionEvent from evidence
const observed = tryParseLastEvolutionEventOutcome ( combinedEvidence );
if ( observed ) return observed ;
// Fallback to signal-based inference
const base = inferOutcomeFromSignals ({ prevHadError , currentHasError });
let score = base . score ;
// Adjust based on error count delta
const prevErrCount = baselineObserved . recent_error_count ;
const curErrCount = currentObserved . recent_error_count ;
if ( prevErrCount != null && curErrCount != null ) {
const delta = prevErrCount - curErrCount ;
score += Math . max ( - 0.12 , Math . min ( 0.12 , delta / 50 ));
}
// Adjust based on scan time improvement
const prevScan = baselineObserved . scan_ms ;
const curScan = currentObserved . scan_ms ;
if ( prevScan != null && curScan != null && prevScan > 0 ) {
const ratio = ( prevScan - curScan ) / prevScan ;
score += Math . max ( - 0.06 , Math . min ( 0.06 , ratio ));
}
return { status: base . status , score: clamp01 ( score ), note: ` ${ base . note } |heuristic_delta` };
}
getAdvice()
Get memory graph recommendations for gene selection.
Location: src/gep/memoryGraph.js:225
function getAdvice ({ signals , genes , driftEnabled })
Whether drift mode is enabled
Computed signal key for current signals
Recommended gene ID (highest expected success)
Set of gene IDs to avoid (low-efficiency paths)
Human-readable explanation of advice
Edge Aggregation:
Location: src/gep/memoryGraph.js:163
function aggregateEdges ( events ) {
const map = new Map ();
for ( const ev of events ) {
if ( ev . type !== 'MemoryGraphEvent' || ev . kind !== 'outcome' ) continue ;
const signalKey = ev . signal . key ;
const geneId = ev . gene . id ;
const k = ` ${ signalKey } :: ${ geneId } ` ;
const cur = map . get ( k ) || { signalKey , geneId , success: 0 , fail: 0 };
const status = ev . outcome . status ;
if ( status === 'success' ) cur . success += 1 ;
else if ( status === 'failed' ) cur . fail += 1 ;
map . set ( k , cur );
}
return map ;
}
Expected Success Calculation:
Location: src/gep/memoryGraph.js:214
function edgeExpectedSuccess ( edge , opts ) {
const succ = Number ( edge . success ) || 0 ;
const fail = Number ( edge . fail ) || 0 ;
const total = succ + fail ;
// Laplace smoothing: avoid 0/1 extremes
const p = ( succ + 1 ) / ( total + 2 );
// Time-based decay
const halfLifeDays = opts . half_life_days || 30 ;
const w = decayWeight ( edge . last_ts , halfLifeDays );
return { p , w , total , value: p * w };
}
Decay Weight:
Location: src/gep/memoryGraph.js:152
function decayWeight ( updatedAtIso , halfLifeDays ) {
const ageDays = ( Date . now () - Date . parse ( updatedAtIso )) / ( 1000 * 60 * 60 * 24 );
if ( ageDays <= 0 ) return 1 ;
// Exponential half-life decay: weight = 0.5^(age/hl)
return Math . pow ( 0.5 , ageDays / halfLifeDays );
}
Signal Key Computation
Location: src/gep/memoryGraph.js:62
function computeSignalKey ( signals ) {
const list = normalizeSignalsForMatching ( signals );
const uniq = Array . from ( new Set ( list . filter ( Boolean ))). sort ();
return uniq . join ( '|' ) || '(none)' ;
}
function normalizeSignalsForMatching ( signals ) {
const out = [];
for ( const s of signals ) {
const str = String ( s || '' ). trim ();
if ( ! str ) continue ;
// Normalize error signatures
if ( str . startsWith ( 'errsig:' )) {
const norm = normalizeErrorSignature ( str . slice ( 'errsig:' . length ));
if ( norm ) out . push ( `errsig_norm: ${ stableHash ( norm ) } ` );
continue ;
}
out . push ( str );
}
return out ;
}
Error Signature Normalization:
Location: src/gep/memoryGraph.js:27
function normalizeErrorSignature ( text ) {
return String ( text || '' )
. toLowerCase ()
. replace ( / [ a-z ] : \\ [ ^ \n\r\t ] + / gi , '<path>' ) // Windows paths
. replace ( / \/ [ ^ \n\r\t ] + / g , '<path>' ) // Unix paths
. replace ( / \b 0x [ 0-9a-f ] + \b / gi , '<hex>' ) // Hex values
. replace ( / \b \d + \b / g , '<n>' ) // Numbers
. replace ( / \s + / g , ' ' )
. slice ( 0 , 220 );
}
Memory Advice Scoring
Location: src/gep/memoryGraph.js:254
for ( const ck of candidateKeys ) {
for ( const g of genes ) {
const k = ` ${ ck . key } :: ${ g . id } ` ;
const edge = edges . get ( k );
const cur = byGene . get ( g . id ) || { geneId: g . id , best: 0 , attempts: 0 , prior: 0 };
if ( edge ) {
const ex = edgeExpectedSuccess ( edge , { half_life_days: 30 });
const weighted = ex . value * ck . sim ; // Weighted by signal similarity
if ( weighted > cur . best ) cur . best = weighted ;
cur . attempts = Math . max ( cur . attempts , ex . total );
}
// Gene->Outcome prior (independent of signal)
const gEdge = geneOutcomes . get ( String ( g . id ));
if ( gEdge ) {
const gx = edgeExpectedSuccess ( gEdge , { half_life_days: 45 });
cur . prior = Math . max ( cur . prior , gx . value );
}
byGene . set ( g . id , cur );
}
}
// Combined score: edge score + prior stabilizer
for ( const [ geneId , info ] of byGene . entries ()) {
const combined = info . best > 0 ? info . best + info . prior * 0.12 : info . prior * 0.4 ;
scoredGeneIds . push ({ geneId , score: combined , attempts: info . attempts });
// Low-efficiency path suppression
if ( ! driftEnabled && info . attempts >= 2 && info . best < 0.18 ) {
bannedGeneIds . add ( geneId );
}
}
Example Usage
Record Signal Snapshot
Get Memory Advice
Record Full Cycle
const { recordSignalSnapshot } = require ( './gep/memoryGraph' );
const event = recordSignalSnapshot ({
signals: [ 'log_error' , 'errsig:TypeError: undefined' ],
observations: {
agent: 'main' ,
system_health: 'Uptime: 12.3h' ,
recent_error_count: 5 ,
},
});
console . log ( 'Signal recorded:' , event . id );