You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Addresses the five pathologies identified by the agent+genius audit of
darval's performance report (issue #13). Phase order matches the telemetry
patch commit message in 69d81fb.
Phase A — Set-based SQL batching
---------------------------------
Per-row UPDATE/DELETE loops with per-row commits were the dominant cost
of consolidate on a 66K-memory store (Erlang analysis: ~64 ops/s ceiling
imposed by fsync amplification). Added batch methods:
pg_store.update_memories_heat_batch (UPDATE FROM UNNEST)
pg_store.update_entities_heat_batch
pg_store.archive_entities_batch (UPDATE WHERE id = ANY)
pg_store.update_relationships_weight_batch
pg_store.delete_relationships_batch
pg_store.insert_stage_transitions_batch (INSERT FROM UNNEST)
Each mirrors onto SqliteMemoryStore via executemany + single commit.
Callers converted:
decay.run_decay_cycle → heat updates in one statement
decay._decay_entities → entity heat in one statement
decay._modulate_domain → metabolic adjustments in one statement
plasticity._apply_updates → 33k LTP/LTD updates in one statement
pruning._prune_edges → 32k DELETEs in one statement
pruning._archive_orphans → orphan archival in one statement
cascade.run_cascade_advancement → transition INSERTs in one statement
Erlang predicted 100-500× speedup on the decay stage alone. On
darval's 66K store the decay stage should drop from ~1100 s to 3-10 s.
Phase B — Consolidation-scoped memory cache
--------------------------------------------
`store.get_all_memories_for_decay()` was called 6× per consolidate run
(decay, compression, memify, homeostatic, sleep, emergence). Load once
at handler entry, pass list to each stage:
run_decay_cycle(store, settings, memories=None)
run_compression_cycle(store, settings, embeddings, memories=None)
run_memify_cycle(store, memories=None)
run_homeostatic_cycle(store, memories=None)
run_deep_sleep(store, embeddings, memories=None)
(emergence closure uses the same list inline)
Each stage retains a fallback load when called standalone outside the
handler, preserving backward compatibility.
Phase C — Plasticity co-access starvation
------------------------------------------
Feinstein's audit: `get_hot_memories(limit=50)` sampled 0.5% of a
10k-entity store → 99.95% LTD was distribution collapse, not
plasticity. Fix:
- raised sample cap 50 → 2000 (_CO_ACCESS_SAMPLE_CAP)
- reuses the Phase B consolidation-scoped list when present
- precomputes lowercase entity name index once per run
- exception handler now surfaces the error (was logger.debug silent)
- returns co_access_pairs and memories_sampled for diagnostics
Phase D — CLS causal-edge signal gate
--------------------------------------
Feynman's audit: `_discover_causal_edges` hard-capped at 500 episodic
on a 25K store (2% sample), ran O(entities × 500) substring scans,
produced 0 edges by construction. Fix:
- episodic sample cap 500 → 2000 (_EPISODIC_SAMPLE_CAP)
- precomputes content_lowered once, then per-name scan is one pass
- early-exit gate: if fewer than _MIN_ENTITIES_FOR_PC entities
clear _PC_MIN_OBSERVATIONS mentions, skip the O(E²) PC pass
- restricts the PC vocabulary to qualifying entities only, so the
matrix is E_qualifying² not E_all²
- returns episodic_scanned for diagnostics
Phase E — Cascade heartbeat + payload trim
-------------------------------------------
Feinstein's audit: cascade wrote a heartbeat UPDATE on every scanned
memory (~2000) even when nothing advanced, each with its own commit;
also per-transition INSERT+commit for stage_transitions (503 fsyncs
on darval's run); also returned the full 503-transition list in the
MCP response duplicating queryable DB state. Fix:
- heartbeat UPDATE skipped when |Δhours| < _HEARTBEAT_SKIP_HOURS (1h)
— the previous value was pure noise and wasted fsync
- stage_transitions INSERTs batched into one statement
- response payload capped: transitions_preview (first 50) + count
- exception handler surfaces the error (was logger.debug silent)
- returns scanned / heartbeats_written / heartbeats_skipped
- SQLite schema gets stage_transitions table + stage_entered_at
migration so cascade no longer crashes silently on cowork mode
Reviewed by agents: code-reviewer (APPROVE), test-engineer (2188 pass),
genius:erlang (queuing analysis), genius:feinstein (differential),
genius:feynman (independent rederivation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 commit comments