Tree and clusters¶
The memory dashboard renders the same set of entries two ways. The tree shows structural lineage — who is whose parent, which phase a decision belongs to. The cluster view shows semantic proximity — which entries are about the same thing, regardless of where they sit in the hierarchy. Neither replaces the other; they answer different questions about one body of knowledge. (The animation of one morphing into the other lives on the Memory hub.)

The Cluster view — the same entries regrouped by meaning into auto-labeled topic clusters, with a granularity slider that sets how many clusters form.
- Backend:
memory/dashboard/backend/routes.py(/graph,/cluster) - Graph source:
memory/dashboard/backend/graph_loader.py - Frontend:
TreeView.tsxandClusterView.tsx
Why both views exist¶
A research tree is authored: an agent records a decision under a phase, under a subproject. That hierarchy encodes provenance and intent — the chain of reasoning that led here. It is precise but rigid: an entry lives where it was filed, and a theme that recurs across three different phases is scattered across three different branches.
Semantic clustering is emergent: it ignores where entries were filed and groups them by what their text means. It surfaces the cross-cutting themes the hierarchy hides, and the surprising adjacencies — two entries that turned out to be about the same problem even though nobody filed them together.
Why not pick one?
Lineage and meaning are orthogonal. You cannot recover "this decision
belongs to Phase 2" from an embedding, and you cannot recover "these five
entries are all about cycle detection" from parent_id. Each projection
discards exactly what the other preserves, so the dashboard keeps both and
lets you switch.
How it works¶
Both views read one shared KGLite graph rebuilt from the .memory/ markdown
files by graph_loader.py.
The same nodes then flow down two independent projections:
/graph for structural lineage (the tree), /cluster for semantic proximity (the clusters).Structural lineage — the tree¶
The /graph route walks each entry's parent_id to build primary
has_child edges, then adds secondary parents from explicit contains
edges so an entry can belong to more than one branch — a multi-parent DAG rather
than a strict tree (Multi-parent DAG: contains edges, ELK layered layout, cycle-safe add_parent API). Each entry carries a parent_ids array;
secondary edges are deduplicated and resolve_parent collapses links through
hidden nodes so the visible graph stays connected.
The frontend lays this out with ELK's layered algorithm (direction
RIGHT, falling back to mrtree if layout fails) — chosen over a pure tree
layout precisely because the topology is a DAG. Primary edges render solid;
secondary parent edges render dashed, and multi-parent nodes get a DAG badge.
The layout is deterministic: the same entries always produce the same shape.

Structural lineage: solid has_child edges trace the authored hierarchy; the dashed edge marks a multi-parent entry that belongs to two branches.
Semantic proximity — the clusters¶
The /cluster route pulls each entry's search_text embedding (model2vec
static vectors, from the graph or the .memory/ sidecar), projects the
high-dimensional vectors to 2D with t-SNE, and normalizes the result into a
fixed viewport. It then runs agglomerative clustering whose target cluster
count is mapped monotonically from the granularity slider — "Few" yields 2
clusters, "Many" up to a ceiling of 30 (Cluster view: semantic embedding layout with auto-detected clusters). Clusters are
auto-labeled from their members' most common content tags (structural tags like
phase/decision are skipped), falling back to a member title.
Position here means meaning, not lineage. Parent/child edges are still drawn,
but faintly; related cross-references become first-class. Entries with no
embedding land in cluster -1 ("other") near their parent, and federated repos'
embeddings are merged in so cross-repo entries cluster together too.

The same node set re-projected: entries pull together by meaning into auto-labeled clusters, regardless of which branch they were filed under.
What's available¶
Tree view (/graph) |
Cluster view (/cluster) |
|
|---|---|---|
| Question it answers | Where does this sit? What's the lineage? | What else is about this? |
| Position encodes | parent → child hierarchy | semantic similarity |
| Layout | ELK layered (deterministic) |
t-SNE + agglomerative (computed) |
| Edges shown | has_child (solid/dashed), invalidated_by, related |
related (primary), parent/child (faint) |
| Best for | provenance, navigation, active-phase tracking, multi-parent reuse | thematic discovery, spotting duplication, cross-cutting structure |
Reach for the tree when you are navigating a workstream, tracing why a decision was made, or checking which phase is still open. Reach for the cluster view when you want the shape of the whole knowledge base — to find entries related to a topic that were filed in different branches, or to notice that two efforts converged on the same idea.
Design notes¶
The decisions behind this, drawn as a slice of the memory tree.
- D Multi-parent DAG: contains edges, ELK layered layout, cycle-safe add_parent APIactive
Implemented multi-parent DAG support for the memory tree dashboard. - D Cluster view: semantic embedding layout with auto-detected clustersactive
- R Angelo
- P agent-memoryactive
- P Phase 6: Web Visualizer Dashboardactive
- P Dashboard Redesign: From Viewer to Research Workbenchactive
- P Phase 6: Web Visualizer Dashboardactive
- P agent-memoryactive
Related¶
Semantically related entries from the memory graph.