Skip to content

feat(graph): report edge counts, and narrow the NREM read in SQL - #83

Merged
Cipher208 merged 1 commit into
masterfrom
feat/graph-edge-visibility-and-nrem-sql-filter
Oct 7, 2026
Merged

Cipher208 merged 1 commit into
masterfrom
feat/graph-edge-visibility-and-nrem-sql-filter

Conversation

@Cipher208

Copy link
Copy Markdown
Owner

What this fixes

memory_stats and the dashboard reported graph_nodes and nothing else. The size of the graph was invisible to every tool that could read it — the numbers behind this session's measurements were all raw SQL against the database files, which makes them incomparable between runs.

Changes

1. Edges become visible. New EpistemicGraph.count_edges() returns {"total", "live", "dead"} in one query, surfaced as graph_edges / graph_edges_live / graph_edges_dead in memory_stats and dashboard.get_stats. All three default to 0, so existing consumers are unaffected.

epi_edges has no layer column, so an edge is attributed to its source node's layer via a join — the convention memory_graph_edges already uses. An edge whose source node is gone lands in no bucket rather than a guessed one.

"Dead" is weight <= 0, not weight < NREM_FLOOR: across all three house databases only 0.1% of edges fall strictly between zero and the 0.05 floor (103 of 186 523), so the coarser split is the accurate one. A dead edge is not garbage — it is the state the next miner pass revives it from, which is why nothing prunes zero eagerly.

2. _dream_nrem reads only what it can act on. It previously read every heuristic edge and decided age in Python, discarding most: on the busiest database, 155 486 rows read to act on a few thousand, and 0 rows for mimocode's over-30-day band. The two actionable age bands are now the query's bounds.

The comparison is the same one the loop made — created_at < now − 30 d is age_days > 30, and created_at >= now − 1 d is age_days <= 1, inclusive, as before.

Database rows before rows after reduction
hermes 155 486 77 857 49.9 %
mimocode 132 068 42 280 68.0 %
cowagent 49 772 28 817 42.1 %

A NULL created_at is now skipped instead of raising float(None). No house database has one; "unknown age" is not a reason to decay, boost, or prune.

No index, deliberately

An index on created_at was considered and rejected on measurement: the filter still reads 42–68% of the table and the time fell with the row count (190 → 115 ms, 205 → 79 ms). An index at that selectivity would buy nothing while keeping its write cost.

Tests

  • four on count_edges — live/dead split, empty graph, source-node layer attribution, orphan edge;
  • one on NREM — a ten-day-old edge is left exactly as it was, which is what proves narrowing the read kept the behaviour;
  • test_stats and test_dashboard assert total == live + dead.

Full suite: 2078 passed.

Cost

count_edges with the layer join, measured on the live databases: 97 ms on hermes (186 878 edges), 30 ms on cowagent. It runs only when the tool is called.

`memory_stats` and the dashboard reported `graph_nodes` and nothing else, so
the size of the graph was invisible to every tool that could read it. The
numbers behind this session's measurements were all raw SQL against the
database files, which makes them incomparable between runs.

Add `EpistemicGraph.count_edges()`, returning `{"total", "live", "dead"}`, and
surface it as `graph_edges` / `graph_edges_live` / `graph_edges_dead` in both
`memory_stats` and `dashboard.get_stats`. All three default to 0, so existing
consumers keep working.

`epi_edges` has no `layer` column, so an edge is attributed to its SOURCE
node's layer via a join — the convention `memory_graph_edges` already uses. An
edge whose source node is gone lands in no bucket rather than a guessed one;
there is a test for that.

"Dead" is `weight <= 0` rather than `weight < NREM_FLOOR`: across all three
house databases only 0.1% of edges fall strictly between zero and the 0.05
floor, so the coarser split is the accurate one. A dead edge is not garbage —
it is the state the next miner pass revives it from.

`_dream_nrem` also read every heuristic edge and decided age in Python, to
discard most of them: on the busiest database, 155 486 rows read to act on a
few thousand, and 0 rows for mimocode's over-30-day band. The two actionable
age bands are now the query's bounds. This is the same comparison the loop
made — `created_at < now − 30 d` is `age_days > 30`, and
`created_at >= now − 1 d` is `age_days <= 1`, inclusive, as before. Measured
row reduction: hermes 49.9%, mimocode 68.0%, cowagent 42.1%.

No index is added. The filter still reads 42–68% of the table and the time
fell with the row count (190 → 115 ms, 205 → 79 ms), so an index on
`created_at` would buy nothing while keeping its write cost.

A NULL `created_at` is now skipped instead of raising `float(None)`. No house
database has one; "unknown age" is not a reason to decay, boost, or prune.

Tests: four on `count_edges` (live/dead split, empty graph, source-node layer,
orphan edge) and one on NREM asserting a ten-day-old edge is left exactly as
it was, which is what proves narrowing the read kept the behaviour. `test_stats`
and `test_dashboard` assert `total == live + dead`.
@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: Cipher208/a-memory/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2370ecc8-2ef0-4664-acd7-dea8f4c856de
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Cipher208
Cipher208 merged commit 67e26f3 into master Oct 7, 2026
19 checks passed
@Cipher208
Cipher208 deleted the feat/graph-edge-visibility-and-nrem-sql-filter branch October 7, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant