Skip to content

Commit 9fedd30

Browse files
committed
fix(memory): address Copilot review feedback on reflection-engine
- Use nullish coalescing (??) instead of || for durationMs, lesson, context fields to correctly preserve falsy values like 0 - Add null check for p.tags in injectContext() to prevent TypeError when patterns lack tags - Add _recomputePatterns() method that rebuilds patterns from remaining reflections after pruning - Call _recomputePatterns() in prune() and after maxReflections eviction to keep patterns consistent with remaining data - Copy reflection-engine.js to .aiox-core/core/memory/ for rebranded package path compatibility (keeps .aios-core version too)
1 parent 82875e4 commit 9fedd30

2 files changed

Lines changed: 588 additions & 4 deletions

File tree

.aios-core/core/memory/reflection-engine.js

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,9 @@ class ReflectionEngine extends EventEmitter {
171171
strategy: reflection.strategy,
172172
description: reflection.description || '',
173173
tags: reflection.tags || [],
174-
durationMs: reflection.durationMs || null,
175-
lesson: reflection.lesson || null,
176-
context: reflection.context || null,
174+
durationMs: reflection.durationMs ?? null,
175+
lesson: reflection.lesson ?? null,
176+
context: reflection.context ?? null,
177177
createdAt: new Date().toISOString(),
178178
};
179179

@@ -183,6 +183,7 @@ class ReflectionEngine extends EventEmitter {
183183
if (this.reflections.length > this.config.maxReflections) {
184184
const removed = this.reflections.shift();
185185
this.emit(Events.REFLECTIONS_PRUNED, { count: 1, reason: 'max_limit', removed: [removed.id] });
186+
this._recomputePatterns();
186187
}
187188

188189
// Check for new patterns
@@ -289,7 +290,7 @@ class ReflectionEngine extends EventEmitter {
289290
injectContext(context) {
290291
const recommendations = this.getRecommendations(context);
291292
const relevantPatterns = this.patterns.filter(
292-
(p) => p.taskType === context.taskType || (context.tags && context.tags.some((t) => p.tags.includes(t))),
293+
(p) => p.taskType === context.taskType || (p.tags && context.tags && context.tags.some((t) => p.tags.includes(t))),
293294
);
294295

295296
return {
@@ -377,6 +378,7 @@ class ReflectionEngine extends EventEmitter {
377378

378379
const pruned = before - this.reflections.length;
379380
if (pruned > 0) {
381+
this._recomputePatterns();
380382
this.emit(Events.REFLECTIONS_PRUNED, { count: pruned, reason: 'retention_window', removed });
381383
}
382384
return pruned;
@@ -471,6 +473,50 @@ class ReflectionEngine extends EventEmitter {
471473
}
472474
}
473475

476+
477+
/**
478+
* Recompute all patterns from the current set of reflections.
479+
* Called after pruning to ensure patterns stay consistent with remaining data.
480+
* @private
481+
*/
482+
_recomputePatterns() {
483+
// Group reflections by taskType + strategy
484+
const groups = new Map();
485+
for (const r of this.reflections) {
486+
const key = `${r.taskType}::${r.strategy}`;
487+
if (!groups.has(key)) {
488+
groups.set(key, []);
489+
}
490+
groups.get(key).push(r);
491+
}
492+
493+
const newPatterns = [];
494+
for (const [, group] of groups) {
495+
if (group.length < this.config.minReflectionsForPattern) continue;
496+
497+
const successes = group.filter((r) => r.outcome === Outcome.SUCCESS).length;
498+
const successRate = successes / group.length;
499+
500+
const tagSet = new Set();
501+
for (const r of group) {
502+
if (r.tags) r.tags.forEach((t) => tagSet.add(t));
503+
}
504+
505+
newPatterns.push({
506+
taskType: group[0].taskType,
507+
strategy: group[0].strategy,
508+
sampleSize: group.length,
509+
successRate,
510+
tags: Array.from(tagSet),
511+
confidence: Math.min(1.0, group.length / 10),
512+
verdict: successRate >= 0.7 ? 'recommended' : successRate <= 0.3 ? 'avoid' : 'neutral',
513+
updatedAt: new Date().toISOString(),
514+
});
515+
}
516+
517+
this.patterns = newPatterns;
518+
}
519+
474520
/**
475521
* Generate a unique ID
476522
* @private

0 commit comments

Comments
 (0)