Commit Graph

2 Commits

Author SHA1 Message Date
Dragan Spiridonov ae7c8b7105 fix(learning): surface consecutive_failures so the 'failures' deprecation branch fires (#510 side-finding)
Pre-existing bug surfaced by the qe-devils-advocate audit and confirmed:
checkDeprecation() reads (pattern as PatternWithDeprecation).consecutive_failures
to trigger the 'failures' deprecation reason, but rowToPattern() built the
QEPattern WITHOUT that column (and without deprecated_at), so getPattern()
always surfaced undefined->0 and the 'failures' branch NEVER fired — a pattern
that kept failing was never deprecated for failures.

Fix (surgical): rowToPattern now surfaces consecutive_failures and deprecated_at
(added to the PatternRow interface; returns a typed PatternWithDeprecation). No
thresholds or deprecation logic changed.

Verified: new tests assert a pattern with consecutive_failures==3 (== default
threshold), not stale, confidence above min, deprecates with reason 'failures';
below threshold does not. Regression-proven (reverting rowToPattern fails the
new test). 12 EWC tests pass; typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:04:57 +00:00
Dragan Spiridonov ef4738dec4 feat(learning): EWC++ importance-weighted forgetting protection (#510 item 6)
Confidence decay (applyConfidenceDecay) and stale/age deprecation
(checkDeprecation) were uniform — a proven high-success pattern was forgotten
at the same rate as noise, so an influx of new low-value patterns could crowd
out historically valuable ones (catastrophic forgetting).

Inspired by Elastic Weight Consolidation, high-IMPORTANCE patterns now resist
forgetting, weighted by an importance proxy = clamped success_rate gated on
usage_count >= EWC_MIN_USAGE_FOR_PROTECTION (recency already implied: only
unused patterns decay/go stale). Honest framing (as in ruflo's EWC adapter):
this is a heuristic, NOT true Fisher information — no model gradients exist here.

  - Decay: per-row factor shielded by EWC_DECAY_PROTECTION*importance. A
    fully-proven pattern decays up to 10x slower; importance-0 patterns decay
    EXACTLY as before (no regression — locked in by test).
  - Stale: effective window = staleDays * (1 + importance*EWC_STALE_PROTECTION_BONUS),
    so a proven pattern unused 30d isn't deprecated like noise.

Protection covers ONLY stale/age/decay forgetting. A pattern that drops below
minActiveConfidence (or starts failing) is STILL deprecated — we blend
retention, we don't permanently shield dead weight. All three EWC_* knobs are
exported constants; set to 0 to restore exact prior behavior (reversible).

PRODUCTION IMPACT: affects the dream/consolidation lifecycle. Low/zero-importance
patterns behave identically; high-success patterns persist longer. Verified with
10 tests against an in-memory DB matching the production qe_patterns schema
(decay deltas, stale window, no-regression, dead-weight still removed). 46 total
lifecycle tests pass.

Side-finding (pre-existing, NOT this change): checkDeprecation reads
consecutive_failures via a cast, but rowToPattern doesn't surface that column,
so the 'failures' deprecation branch never fires through getPattern. Logged for
a dedicated fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:15:19 +00:00