-
Notifications
You must be signed in to change notification settings - Fork 0
petgraph_algorithm_utilization_spec
Status: Planning
Date: 2026-03-07
Scope: All algorithmic uses of petgraph in graphshell — existing replacements and new capabilities.
Graphshell's backing store is a petgraph::stable_graph::StableGraph<Node, EdgePayload, Directed>.
Petgraph ships a rich algorithm library (petgraph::algo, petgraph::visit) that we are not yet
using. Instead the codebase contains three hand-rolled BFS implementations and several features that
are algorithmic in nature but not yet built. This spec enumerates:
- Direct replacements — hand-rolled code that should be deleted and replaced with petgraph calls.
- New capabilities — features described in roadmap docs that petgraph algorithms directly unlock.
- Performance implications — which sites are hot-path and require memoization.
The dependency is already declared in Cargo.toml:
petgraph = "0.8.3" # features: serde-1No new dependencies are required for anything in this spec.
File: shell/desktop/ui/toolbar/toolbar_omnibar.rs:443
What it does: Full undirected BFS from a context node, returns HashMap<NodeKey, usize> mapping
every reachable node to its hop count from the context.
Call sites:
-
connected_nodes_matches_for_query— called once per omnibar render with non-empty query text. -
omnibar_match_signifier— called once per visible match entry per render frame, meaning if 10 matches are displayed, this BFS runs 10 times with the same context node.
Current code:
fn connected_hop_distances_for_context(
graph_app: &GraphBrowserApp,
context: NodeKey,
) -> HashMap<NodeKey, usize> {
let mut distances = HashMap::new();
let mut queue = VecDeque::new();
distances.insert(context, 0);
queue.push_back(context);
while let Some(current) = queue.pop_front() {
let Some(current_hop) = distances.get(¤t).copied() else { continue; };
for neighbor in graph_app.domain_graph().out_neighbors(current)
.chain(graph_app.domain_graph().in_neighbors(current))
{
if distances.contains_key(&neighbor) { continue; }
distances.insert(neighbor, current_hop + 1);
queue.push_back(neighbor);
}
}
distances
}Petgraph replacement: petgraph::algo::dijkstra with unit edge weights over an undirected view
of the inner StableGraph. Dijkstra with unit weights is BFS, so the result is identical.
use petgraph::algo::dijkstra;
use petgraph::visit::EdgeRef;
fn connected_hop_distances_for_context(
graph: &Graph,
context: NodeKey,
) -> HashMap<NodeKey, usize> {
dijkstra(
&petgraph::visit::AsUndirected(&graph.inner),
context,
None, // no early-exit target
|_| 1_usize, // unit weight
)
}petgraph::visit::AsUndirected wraps any directed graph and presents it as undirected to the
algorithm, matching the current behavior of chaining out_neighbors and in_neighbors.
Memoization requirement (CRITICAL): This function is called O(matches) times per render frame.
The result must be cached and invalidated only when the primary selection changes or the graph
structure changes. The cache should live on GraphWorkspace (or be passed down from the omnibar
parent render call). Signature after memoization:
/// Cached in GraphWorkspace; recomputed when primary selection or graph structure changes.
pub hop_distance_cache: Option<(NodeKey, HashMap<NodeKey, usize>)>,Invalidation sites: apply_graph_delta_and_sync (graph structure change) and wherever primary
selection is committed.
File: shell/desktop/ui/gui_frame.rs:314
What it does: Bounded BFS from a source node up to depth 2, collecting (NodeKey, depth) pairs.
Used for the "Open Connected" layout command.
Current code: 25-line VecDeque+HashSet BFS with a depth cap of 2.
Petgraph replacement: petgraph::visit::Bfs with a manual depth tracker, or simply two rounds of
neighbor expansion (depth 1 = direct neighbors, depth 2 = neighbors of neighbors minus already
visited). The Bfs struct from petgraph::visit maintains the visited set internally:
use petgraph::visit::{Bfs, Walker};
// Depth-1 case (PendingConnectedOpenScope::Neighbors):
// just out_neighbors + in_neighbors of source — no BFS needed, keep as-is.
// Depth-2 case (PendingConnectedOpenScope::Connected):
let mut bfs = Bfs::new(&petgraph::visit::AsUndirected(&graph.inner), source);
let mut out = Vec::new();
bfs.next(&petgraph::visit::AsUndirected(&graph.inner)); // consume source itself
let mut depth1 = HashSet::new();
// gather depth-1
while let Some(n) = bfs.next(&petgraph::visit::AsUndirected(&graph.inner)) {
depth1.insert(n);
out.push((n, 1_u8));
// BFS will naturally expand these next; stop at depth 2 via visited check
}Because Bfs doesn't expose per-node depth natively, the cleanest approach for the depth-2 cap is
to run two explicit neighbor expansions rather than a full BFS walk. This avoids needing a wrapper
and is only two loops. The depth cap (2) is small and fixed, so full Bfs is over-engineering here;
explicit two-round expansion is cleaner and should be preferred.
Verdict: Replace the VecDeque BFS loop with two explicit neighbor expansion rounds. Use
petgraph::visit::AsUndirected to avoid duplicating the out_neighbors + in_neighbors pattern.
File: shell/desktop/ui/gui_frame.rs:237
What it does: Depth-1 undirected neighbor expansion for a set of seed nodes. Returns all seeds
plus their immediate neighbors.
Current code: Straightforward set union over out_neighbors + in_neighbors.
Petgraph replacement: No BFS needed. Use petgraph::visit::AsUndirected to make the neighbor
query uniform, but the logic is already correct. This is a style cleanup, not a correctness fix.
The only real gain is replacing the explicit out_neighbors(...).chain(in_neighbors(...)) pair with
graph.inner.neighbors_undirected(seed) once the inner graph is accessible.
Verdict: Minor cleanup — expose Graph::neighbors_undirected(key) accessor on the domain graph
and call it here.
Function: petgraph::algo::connected_components(&graph) — returns the number of weakly
connected components in a directed graph. petgraph::algo::kosaraju_scc(&graph) — returns the
strongly connected components as Vec<Vec<NodeKey>> (each SCC is a group of nodes that are
mutually reachable in the directed sense).
Graphshell applications:
When a user opens a new workspace/frame, a sensible default set of panes is "all nodes in the connected component containing the currently active node." Implemented with:
// Find which component the active node belongs to
let components = petgraph::algo::kosaraju_scc(&graph.inner);
let active_component = components.iter()
.find(|comp| comp.contains(&active_node))
.cloned()
.unwrap_or_default();The resulting Vec<NodeKey> is the natural candidate set for "Open Connected" at maximum depth.
Nodes with no edges (degree 0) are trivially orphaned. Nodes in components of size 1 are graph orphans. This is useful for:
- Surfacing a "these nodes are disconnected" callout in the canvas overlay.
- Dimming or visually distinguishing isolated nodes from the primary component.
- Driving an "orphan cleanup" suggestion in the UX (link or remove).
Implementation site: Add Graph::orphan_node_keys() -> Vec<NodeKey> that returns nodes whose
total degree (in + out) is 0. For richer isolation detection, use connected_components on an
undirected view to find components of size 1.
connected_components count is a meaningful graph health metric — a highly fragmented graph (many
small components) suggests the user's browsing is siloed. Surface this count in the diagnostics
panel or session stats overlay.
Function: condensation(graph, make_acyclic) — collapses each SCC to a single node, producing
a DAG of "super-nodes."
Graphshell application: Browsing Loop Detection
HTTP browsing sessions often contain loops — the user navigated A→B→C→A. These loops show up as SCCs in the traversal-derived edge subgraph. The condensation DAG reveals the high-level flow of a browsing session stripped of loops.
Uses:
-
Visual: Render condensed super-nodes at a zoom-out level (Navigator overview swatch / atlas-oriented projection) as cluster representatives.
See
multi_view_pane_spec.md §5.3Aand../navigator/NAVIGATOR.md §11.7for the Navigator-owned orientation semantics. -
History: In the history timeline subsystem (
subsystem_history/history_timeline_and_temporal_navigation_spec.md), condensation provides a natural "canonical path" through a session by following the condensation DAG topologically. -
Cleanup suggestion: SCCs with only
TraversalDerivededges and noHyperlinkedges are pure browsing loops; the UI can offer to collapse them to a single node.
Implementation note: Condensation is O(V+E) via Kosaraju; for graphshell's expected graph sizes (hundreds to low thousands of nodes) this runs in microseconds and can be computed on demand.
Function: toposort(&graph, None) — returns nodes in topological order if the graph is a DAG,
or Err(Cycle { node_id }) if it contains a cycle.
Graphshell applications:
The WAL replay (services/persistence) must add nodes before edges. Within a batch of AddNode
deltas, ordering matters only when edges create dependencies. toposort provides the correct replay
order for a batch of nodes+edges: add nodes in topological order so that AddEdge calls always find
both endpoints present.
Currently the WAL is ordered by insertion time, which is correct for sequential sessions but may
not be for reconstructed or merged snapshots. toposort on the pending replay batch provides a
safe, correct ordering independent of time.
In viewer/2026-03-02_filesystem_ingest_graph_mapping_plan.md, files are mapped to nodes and
directory containment creates edges. Directory hierarchies are DAGs. toposort gives the correct
node creation order for ingest: create leaf files before directories, or vice versa, depending on
edge direction convention.
Graphshell application: Primary ↔ Secondary Selection Path
When the user has two nodes selected (primary + secondary), the shortest undirected path between them is a natural navigation primitive:
- Visual: Highlight the path edges in the canvas.
- Command: "Open path" — open all nodes along the shortest path as panes.
- Omnibar: Show path length as a signifier in search results ("3 hops away").
Implementation:
use petgraph::algo::astar;
fn shortest_path(graph: &Graph, from: NodeKey, to: NodeKey) -> Option<Vec<NodeKey>> {
let undirected = petgraph::visit::AsUndirected(&graph.inner);
astar(
&undirected,
from,
|n| n == to,
|_| 1_usize, // unit edge cost
|_| 0_usize, // zero heuristic = Dijkstra
).map(|(_, path)| path)
}Memoization: Cache the result keyed on (primary, secondary). Invalidate on selection change
or graph structural change.
Function: has_path_connecting(&graph, from, to, None) — boolean reachability test.
Graphshell applications:
In gui_frame.rs, connected_targets_for_open currently computes all candidates via BFS and then
checks membership. has_path_connecting is a cleaner predicate when only the boolean is needed:
// Is 'candidate' reachable from 'source' in at most 2 hops?
// (has_path_connecting doesn't support depth limits, so explicit 2-hop expansion is still
// better for the capped case. Use has_path_connecting only for uncapped reachability.)Better use: When checking whether to dimm or gray out a node on the canvas (i.e., "is this node
connected to the selection at all?"), has_path_connecting is O(V+E) in the worst case but exits
early on success. For large sparse graphs this is faster than building the full distance map.
Before adding a UserGrouped edge between two nodes that are already connected, check if a path
already exists. If a path exists and the new edge would create a duplicate semantic connection, the
UI can warn or merge.
Function: Returns an iterator of Element::Node / Element::Edge forming the MST (Kruskal's
algorithm).
Graphshell application: Physics Cold-Start Warm Seed
When a workspace loads and nodes have no committed positions (first load, imported graph, or a graph where all positions are zero/uniform), the FR physics engine starts from a degenerate state: all nodes at center, maximum repulsion, maximum layout work before convergence. The result is visually jarring — nodes scatter randomly and settle slowly.
An MST warm seed produces a topologically grounded initial arrangement before the first physics tick:
- Extract a
f32edge weight per edge:w = 1.0 / (1.0 + total_navigations as f32), making heavily-traversed edges "short" in the MST so frequently co-visited nodes start close together. - Compute the MST with
min_spanning_tree(Kruskal's, O(E log E)). - Lay out the MST tree using a simple radial or BFS-level layout (root = highest-degree node, children placed at equal angular intervals per depth level).
- Write resulting positions to
node.committed_positionfor all nodes in the MST. Orphan nodes (not in MST because they have no edges) are placed on an outer ring around the MST bounding box.
Guard: Only apply when all nodes have committed_position == Point2D::zero() or equivalent
sentinel. If any node has a non-zero committed position (i.e., persisted from a previous session),
skip the seed entirely — the user's spatial memory matters more than a clean initial layout.
Scope: This is a pre-physics pass in the workspace initialization path, not a Layout<S> impl
or an ExtraForce. It runs once, writes committed_position, then physics takes over normally.
Implementation note: min_spanning_tree operates on NodeWeight/EdgeWeight pairs via the
IntoEdgeReferences + EdgeWeight: PartialOrd + Copy constraint. Extract edge weights into a
temporary HashMap<EdgeKey, ordered_float::NotNan<f32>> (or use a wrapper) before passing to the
algorithm, then map the resulting Element::Edge keys back to node positions.
The problem with single-center gravity on fragmented graphs:
The current CenterGravity extra in the FR physics pipeline applies a single gravity well at
canvas center. When the graph has multiple weakly connected components — a common state when the
user has several unrelated browsing sessions loaded — all components are pulled toward the same
center. The result is that unrelated clusters collide and overlap, and the user cannot visually
distinguish separate research threads.
The single-center model is only correct when the graph is a single connected component.
Per-component gravity loci:
Replace the single CenterGravity extra with a ComponentGravityLoci ExtraForce that:
- Computes weakly connected components each time the graph structure changes (not every frame —
cached, invalidated by
apply_graph_delta_and_sync). - Assigns each component a stable locus position. Initial locus positions are spread on a grid or ring to ensure components start spatially separated.
- Applies per-node attraction toward the component's locus rather than canvas center.
- As a component's locus converges toward its natural stable position, the locus itself can be updated to track the component's actual centroid (lerped, not snapped, to avoid oscillation).
// graph/forces/component_gravity.rs
pub struct ComponentGravityLoci;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ComponentGravityParams {
/// Map from component representative (lowest NodeKey in component) to locus position.
pub loci: HashMap<NodeKey, egui::Pos2>,
/// Map from NodeKey to its component representative.
pub membership: HashMap<NodeKey, NodeKey>,
/// Gravity strength per component (same value for all; could be made per-component).
pub strength: f32,
}
impl ExtraForce for ComponentGravityLoci {
type Params = ComponentGravityParams;
fn apply<...>(params: &Self::Params, g, indices, disp, area, k) {
for (i, idx) in indices.iter().enumerate() {
let rep = params.membership.get(idx).copied().unwrap_or(*idx);
let locus = params.loci.get(&rep).copied().unwrap_or(egui::Pos2::ZERO);
let pos = g.node(*idx).location();
let delta = locus - pos;
disp[i] += delta.to_vec2() * params.strength * k;
}
}
}Recomputation site — Graph::weakly_connected_components() is O(V+E) via Kosaraju on an
undirected view. Called once after each apply_graph_delta_and_sync that changes graph structure.
The resulting ComponentGravityParams is written into GraphPhysicsState before the next frame.
Locus placement for new components:
When a new component appears (node added with no edges, or an isolated cluster is loaded), its
initial locus must be placed so it does not overlap existing components. A simple strategy:
place new loci on an outward spiral from the canvas origin, stepping by 2 * max_radius_of_existing_components.
Relationship to existing CenterGravity:
CenterGravity should be disabled (Extra flag false) when ComponentGravityLoci is enabled.
Running both simultaneously would pull single-component graphs correctly but double-attract
multi-component graphs toward center, undoing the separation benefit.
The PhysicsProfile gains a flag per_component_gravity: bool. When true, CenterGravity is
disabled and ComponentGravityLoci is enabled in apply_to_state(). The default for new profiles
should be per_component_gravity: true — it degrades gracefully to single-center behavior when the
graph has exactly one component (the locus is then just canvas center).
Priority: P2 — this is a correctness fix for a real layout defect, not a polish feature. Any workspace with multiple unconnected browsing sessions currently has overlapping clusters.
The physics extensibility plan (2026-02-24_physics_engine_extensibility_plan.md) identifies
LayoutHierarchical (egui_graphs Sugiyama-style) and a future TimelineLayout as ActiveLayout
enum variants. Petgraph provides the preprocessing those layouts need.
Before switching a graph pane to LayoutHierarchical, the layout engine must verify the graph is
a DAG (or extract a DAG subgraph). petgraph::algo::toposort returns Err(Cycle { node_id }) if
the graph contains a cycle. The canonical pre-switch check:
match petgraph::algo::toposort(&graph.inner, None) {
Ok(order) => {
// Safe to apply hierarchical layout.
// `order` is the topological node sequence — pass to LayoutHierarchical as the layer hint.
}
Err(cycle) => {
// Graph has cycles. Options:
// 1. Run condensation first, layout the condensation DAG, expand SCCs in place.
// 2. Fall back to force-directed.
// 3. Offer user "collapse loops" action first.
}
}For graphs with traversal-derived edges (browsing history), cycles are common (A→B→A). The correct
response is condensation: collapse each SCC to a super-node, apply LayoutHierarchical to the
condensation DAG, then expand super-nodes back into their constituent nodes arranged locally via FR.
This gives a global timeline structure with local loop clusters.
A TimelineLayout (planned in the ActiveLayout enum) maps nodes to horizontal positions by
their logical time-ordering and vertical positions by depth in the traversal DAG. Prerequisites:
-
toposorton theTraversalDerivededge subgraph gives the temporal order of nodes. - Condensation collapses browsing loops into single timeline events.
- Dominator tree (
petgraph::algo::dominators::simple_fast) identifies which nodes are navigation entry points — these become the "chapter markers" in the timeline view.
None of these require a new crate dependency; all are in petgraph.
Function: dominators::simple_fast(&graph, root) — computes the dominator tree rooted at a
given node for a directed graph.
Graphshell application: Traversal Funnel Analysis
In traversal-derived edges (history edges), the dominator tree identifies which nodes are "entry points" — every path from the root (session start) passes through a dominator. This identifies:
- Navigation funnels: A highly dominating node is a hub that the user always passes through.
- Dead ends: Nodes dominated only by themselves with no successors.
This maps directly to the history timeline spec
(subsystem_history/history_timeline_and_temporal_navigation_spec.md), which calls for "session
flow analysis." The dominator tree is the formal tool for this.
Implementation note: The dominators module requires a directed graph and a start node.
Restrict to TraversalDerived edge subgraph for pure browsing analysis.
Graphshell application: Pattern-Based Workspace Templates
Two workspace graphs are structurally equivalent if their node/edge topology matches. This enables:
- Detecting when a user's current browsing pattern matches a saved workspace template.
- Suggesting "you seem to be in a research pattern — apply Research template?"
This is a future/advanced feature, not near-term. Mentioned for completeness; isomorphism checking is NP-hard in general but polynomial for bounded-degree graphs, which browsing graphs typically are.
| Priority | Item | Section | File | Effort | Payoff |
|---|---|---|---|---|---|
| P0 | Memoize connected_hop_distances_for_context cache |
§1.1 |
graph_app.rs + omnibar |
Medium | Eliminates O(matches) BFS per frame |
| P0 | Replace connected_hop_distances_for_context with dijkstra
|
§1.1 | toolbar_omnibar.rs |
Small | Code clarity + correctness |
| P1 | Replace connected_candidates_with_depth with 2-round expansion + AsUndirected
|
§1.2 | gui_frame.rs |
Small | Code clarity |
| P1 | Add Graph::neighbors_undirected(key) accessor |
§1.3 | model/graph/mod.rs |
Tiny | Eliminates out+in duplication |
| P2 |
ComponentGravityLoci ExtraForce + disable CenterGravity for multi-component graphs |
§2.7 |
graph/forces/, graph_app.rs
|
Medium | Fixes cluster overlap — correctness fix |
| P2 |
Graph::weakly_connected_components() + component cache in GraphWorkspace
|
§2.7 | model/graph/mod.rs |
Small | Prerequisite for §2.7 ExtraForce |
| P2 |
Graph::orphan_node_keys() + canvas callout |
§2.1 |
model/graph/mod.rs, render |
Small | Useful UX signal |
| P2 | Shortest path between primary/secondary selection | §2.4 |
graph_app.rs, canvas render |
Medium | Navigation primitive |
| P2 |
has_path_connecting for canvas dimming predicate |
§2.5 | render layer | Small | Replaces full distance map |
| P3 | MST warm seed for cold-start layout | §2.6 | workspace init path | Medium | Better first-load layout experience |
| P3 | SCC-based "Open Connected" scope | §2.1 | gui_frame.rs |
Medium | Better default scope |
| P3 | Condensation for atlas view cluster representatives | §2.2 | atlas render | Medium | Roadmap prerequisite |
| P3 | Topological sort for WAL replay batch ordering | §2.3 | services/persistence |
Small | Correctness for merged snapshots |
| P4 | Hierarchical layout with toposort preprocessing | §2.8 | physics engine, ActiveLayout
|
Large | Level 3 layout for DAG topologies |
| P4 | Dominator tree for history funnel analysis | §2.10 | history subsystem | Large | Roadmap: history timeline |
| P5 | Condensation DAG for history canonical path + timeline layout | §2.2, §2.8 | history subsystem | Large | Roadmap: history timeline |
impl Graph {
/// Undirected neighbors of `key` (both in-edges and out-edges).
pub fn neighbors_undirected(&self, key: NodeKey) -> impl Iterator<Item = NodeKey> + '_ {
self.inner.neighbors_undirected(key)
}
/// Hop distances from `source` to all reachable nodes (undirected BFS, unit weights).
pub fn hop_distances_from(&self, source: NodeKey) -> HashMap<NodeKey, usize> {
petgraph::algo::dijkstra(
&petgraph::visit::AsUndirected(&self.inner),
source,
None,
|_| 1_usize,
)
}
/// Nodes with no edges (in-degree + out-degree == 0).
pub fn orphan_node_keys(&self) -> Vec<NodeKey> {
self.inner.node_indices()
.filter(|&n| self.inner.edges(n).next().is_none()
&& self.inner.edges_directed(n, Direction::Incoming).next().is_none())
.collect()
}
/// Shortest undirected path between two nodes (unit weights), if one exists.
pub fn shortest_path(&self, from: NodeKey, to: NodeKey) -> Option<Vec<NodeKey>> {
petgraph::algo::astar(
&petgraph::visit::AsUndirected(&self.inner),
from,
|n| n == to,
|_| 1_usize,
|_| 0_usize,
).map(|(_, path)| path)
}
/// Whether `to` is reachable from `from` in the undirected graph.
pub fn is_reachable(&self, from: NodeKey, to: NodeKey) -> bool {
petgraph::algo::has_path_connecting(
&petgraph::visit::AsUndirected(&self.inner),
from,
to,
None,
)
}
/// Weakly connected components. Returns each component as a Vec of NodeKeys.
/// Uses `kosaraju_scc` on an undirected view: treating all edges as bidirectional
/// makes every SCC a weakly-connected component of the original directed graph.
/// (Alternatively, `petgraph::algo::connected_components` counts components without
/// returning membership; use this function when membership is needed.)
pub fn weakly_connected_components(&self) -> Vec<Vec<NodeKey>> {
petgraph::algo::kosaraju_scc(&petgraph::visit::AsUndirected(&self.inner))
}
/// Strongly connected components (directed graph, Kosaraju's algorithm).
pub fn strongly_connected_components(&self) -> Vec<Vec<NodeKey>> {
petgraph::algo::kosaraju_scc(&self.inner)
}
}/// Cached hop-distance map from the primary selection node.
/// Keyed on NodeKey; None means stale (recompute before next omnibar render).
pub hop_distance_cache: Option<(NodeKey, HashMap<NodeKey, usize>)>,Invalidation:
- Set to
Noneinapply_graph_delta_and_sync(any structural change). - Set to
Nonewhenworkspace.selected_nodes.primary()changes.
Access pattern in omnibar:
fn get_or_compute_hop_distances<'a>(
workspace: &'a mut GraphWorkspace,
graph: &Graph,
) -> Option<&'a HashMap<NodeKey, usize>> {
let primary = workspace.selected_nodes.primary()?;
let cache = workspace.hop_distance_cache.get_or_insert_with(|| {
(primary, graph.hop_distances_from(primary))
});
if cache.0 != primary {
*cache = (primary, graph.hop_distances_from(primary));
}
Some(&cache.1)
}This ensures BFS runs at most once per selection+graph-state combination, regardless of how many matches the omnibar renders.
/// Cached weakly-connected component membership.
/// Recomputed after each apply_graph_delta_and_sync that touches structure.
/// Key: NodeKey → component representative (lowest NodeKey in component).
pub component_membership_cache: Option<HashMap<NodeKey, NodeKey>>,
/// Stable locus positions per component representative.
/// Updated when new components appear; existing loci are preserved across recomputations
/// so in-progress physics convergence is not disrupted.
pub component_loci: HashMap<NodeKey, egui::Pos2>,Recomputation:
fn recompute_component_membership(graph: &Graph) -> HashMap<NodeKey, NodeKey> {
let components = petgraph::algo::kosaraju_scc(
&petgraph::visit::AsUndirected(&graph.inner)
);
let mut membership = HashMap::new();
for component in &components {
// Representative = lowest index in component for stability across recomputes.
let rep = *component.iter().min().expect("component is non-empty");
for &node in component {
membership.insert(node, rep);
}
}
membership
}Locus placement for new components (called from apply_graph_delta_and_sync after
recompute_component_membership):
fn assign_new_component_loci(
old_membership: &HashMap<NodeKey, NodeKey>,
new_membership: &HashMap<NodeKey, NodeKey>,
loci: &mut HashMap<NodeKey, egui::Pos2>,
) {
let old_reps: HashSet<NodeKey> = old_membership.values().copied().collect();
let new_reps: HashSet<NodeKey> = new_membership.values().copied().collect();
let added_reps = new_reps.difference(&old_reps);
let existing_count = loci.len();
for (i, &rep) in added_reps.enumerate() {
// Spiral outward: radius grows with component count, angle evenly spaced.
let idx = existing_count + i;
let angle = idx as f32 * std::f32::consts::TAU / 6.0; // 6 per ring
let radius = 300.0 * (1 + idx / 6) as f32;
loci.entry(rep).or_insert_with(|| egui::Pos2::new(
radius * angle.cos(),
radius * angle.sin(),
));
}
// Remove loci for components that have merged or disappeared.
loci.retain(|rep, _| new_reps.contains(rep));
}apply_to_state integration in PhysicsProfile:
if self.per_component_gravity {
// Disable single-center gravity; enable per-component loci.
state.extras.0.enabled = false; // CenterGravity off
state.extras.N.enabled = true; // ComponentGravityLoci on
state.extras.N.params.loci = workspace.component_loci.clone();
state.extras.N.params.membership = workspace.component_membership_cache
.clone()
.unwrap_or_default();
state.extras.N.params.strength = self.gravity_strength;
} else {
state.extras.0.enabled = true; // CenterGravity on
state.extras.0.params.c = self.gravity_strength;
state.extras.N.enabled = false; // ComponentGravityLoci off
}All algorithms needed are in the existing petgraph dependency:
use petgraph::algo::{
astar, condensation, dijkstra, has_path_connecting, kosaraju_scc,
min_spanning_tree, toposort,
};
use petgraph::algo::dominators;
use petgraph::data::Element; // for min_spanning_tree Element::Node / Element::Edge
use petgraph::visit::{AsUndirected, Bfs, Walker};
use petgraph::Direction;dominators::simple_fast(&graph, root) is the entry point for dominator tree computation (§2.10).
-
petgraph::algo::is_isomorphic_matching— subgraph matching. Mentioned in §2.11 as a future capability; not planned for any current roadmap lane. -
petgraph::algo::bellman_ford— negative-weight shortest paths. Graphshell edge weights are non-negative traversal counts; Dijkstra/A* suffice. -
petgraph::algo::floyd_warshall— all-pairs shortest paths. Graph size makes this impractical to cache; compute on demand with single-source Dijkstra instead.
This section defines the first implementation slices as small, reviewable PRs.
Goal: Remove hot-path repeated BFS in omnibar and replace with petgraph shortest-path primitive.
Files expected:
model/graph/mod.rsgraph_app.rsshell/desktop/ui/toolbar/toolbar_omnibar.rs
Changes:
- Add
Graph::hop_distances_from(source)usingpetgraph::algo::dijkstra+AsUndirected. - Add
hop_distance_cache: Option<(NodeKey, HashMap<NodeKey, usize>)>toGraphWorkspace. - Add helper in omnibar path to fetch cached distances or recompute once.
- Invalidate cache when graph structure changes (
apply_graph_delta_and_sync). - Invalidate cache when primary selection changes.
Acceptance criteria:
- No direct
VecDequeBFS remains inconnected_hop_distances_for_context. - Omnibar signifier and query filtering share one hop map per frame/selection.
- Behavior parity: displayed hop counts remain unchanged for same graph state.
Validation:
- Targeted tests for omnibar match/signifier behavior (existing and new).
-
cargo check -q.
Goal: Replace hand-rolled depth-capped BFS loop in gui_frame with two-round expansion.
Files expected:
model/graph/mod.rsshell/desktop/ui/gui_frame.rs
Changes:
- Add
Graph::neighbors_undirected(key)accessor. - Replace
connected_candidates_with_depthqueue loop with explicit depth-1 + depth-2 expansions. - Preserve current ordering and cap behavior (
MAX_CONNECTED_OPEN_NODESlogic unchanged).
Acceptance criteria:
- No queue-based BFS loop remains in that function.
- Output set/depth semantics remain equivalent for both
NeighborsandConnectedmodes.
Validation:
- Existing
gui_frame/connected-open tests pass. - Add regression test for depth-2 expansion dedupe and depth annotation.
-
cargo check -q.
Goal: Land reusable graph API surface for upcoming P2/P3 features.
Files expected:
model/graph/mod.rs
Changes:
- Add
orphan_node_keys(). - Add
shortest_path(from, to)(A* with unit weights). - Add
is_reachable(from, to). - Add
weakly_connected_components()andstrongly_connected_components()helpers.
Acceptance criteria:
- Accessors compile and return stable deterministic outputs.
- Unit tests cover empty graph, disconnected graph, and cyclic graph cases.
Validation:
- New graph unit tests.
-
cargo test -q model::graph::tests -- --nocapture.
Goal: Introduce state/cache plumbing for per-component gravity without switching physics behavior yet.
Files expected:
graph_app.rs- physics profile/state files where extras are configured
Changes:
- Add
component_membership_cacheandcomponent_locito workspace state. - Recompute membership only on structural graph updates.
- Add locus assignment utility for new/merged components.
- Gate new behavior behind profile flag, default OFF in this PR.
Acceptance criteria:
- No behavior change when flag is disabled.
- Cache updates are deterministic and do not churn each frame.
Validation:
- Unit tests for membership/loci recomputation and merge/remove handling.
-
cargo check -q.
Goal: Activate per-component gravity and disable single-center gravity when enabled.
Files expected:
-
graph/forces/component_gravity.rs(new) - physics profile/state integration points
Changes:
- Implement
ComponentGravityLociExtraForce. - Wire profile toggle: when enabled, disable
CenterGravityand enableComponentGravityLoci. - Seed reasonable defaults for new profiles.
Acceptance criteria:
- Multi-component graphs remain visually separated under physics.
- Single-component behavior remains stable.
Validation:
- Focused physics tests and/or deterministic snapshot tests where available.
- Manual smoke in multi-component workspace.
- Keep each PR independently shippable.
- Preserve terminology from
design_docs/TERMINOLOGY.mdin code/docs. - Avoid bundling unrelated changes into algorithm replacement PRs.
This spec is the authoritative reference for petgraph algorithm utilization in graphshell. Update it when new use sites are identified or implementations are completed.
- Home
-
design_docs
-
archive_docs
- checkpoint_2026-01-29
- checkpoint_2026-02-01
-
checkpoint_2026-02-09
- [Claude ANALYSIS 2.9.26](design_docs/archive_docs/checkpoint_2026-02-09/Claude ANALYSIS 2.9.26)
- [Gemini Graphshell Analysis 2.9.26](design_docs/archive_docs/checkpoint_2026-02-09/Gemini Graphshell Analysis 2.9.26)
- GRAPHSHELL_CHANGELOG
- checkpoint_2026-02-10
- checkpoint_2026-02-11
- checkpoint_2026-02-12
- checkpoint_2026-02-14_no_legacy_cleanup
-
checkpoint_2026-02-16
- 2026-02-11_camera_zoom_plan
- 2026-02-11_center_camera_plan
- 2026-02-11_graph_persistence_plan
- 2026-02-11_phase1_refinement_plan
- 2026-02-11_search_filtering_plan
- 2026-02-11_thumbnails_favicons_plan
- 2026-02-12_architecture_reconciliation
- 2026-02-12_egui_tiles_implementation_guide
- 2026-02-12_egui_tiles_implementation_plan
- 2026-02-12_persistence_ux_plan
- 2026-02-12_physics_selection_plan
- 2026-02-12_servoshell_inheritance_analysis
- 2026-02-13_committed_vs_planned_architecture
- 2026-02-13_graph_tile_architecture_research
- 2026-02-13_graph_tile_unification_plan
- 2026-02-14_graph_tile_parity_research
- 2026-02-14_servo_architecture_constraints
- 2026-02-15_navigation_control_plane_plan
- NAVIGATION_NEXT_STEPS_OPTIONS
- SERVO_INTEGRATION_BRIEF
- checkpoint_2026-02-17
- checkpoint_2026-02-19
- checkpoint_2026-02-20
-
checkpoint_2026-02-21
- .claude
- readmes
- checkpoint_2026-02-22
- checkpoint_2026-02-23
-
checkpoint_2026-02-24
- 2026-02-11_performance_optimization_plan
- 2026-02-16_architecture_and_navigation_plan
- 2026-02-18_edge_operations_and_cmd_palette_plan
- 2026-02-19_graph_ux_polish_plan
- 2026-02-19_layout_advanced_plan
- 2026-02-20_cross_platform_sync_and_extension_plan
- 2026-02-24_input_surface_polish_plan
- 2026-02-24_step5_5_workspace_access_control
- 2026-02-24_sync_logic_validation_plan
- 2026-02-24_workspace_routing_polish_plan
- GRAPHSHELL_P2P_COLLABORATION
-
checkpoint_2026-02-25
-
stubs
- 2026-02-21_control_panel_async_scaling
- 2026-02-25_planning_register_backlog_and_copilot_guides
- 2026-02-25_subsystem_accessibility
- 2026-02-25_subsystem_diagnostics
- 2026-02-25_subsystem_history_temporal_integrity
- 2026-02-25_subsystem_persistence_integrity
- 2026-02-25_subsystem_security_access_control
- IMPLEMENTATION_ROADMAP.pointer_stub
- 2026-02-24_immediate_priorities
- 2026-02-24_spatial_accessibility_plan
- 2026-02-25_accessibility_contracts_diagnostics_and_validation_strategy
- 2026-02-25_backlog_ticket_stubs
- 2026-02-25_copilot_implementation_guides
- 2026-02-25_planning_register_lane_sequence_receipt
- IMPLEMENTATION_ROADMAP
-
stubs
- checkpoint_2026-02-26
-
checkpoint_2026-02-27
-
graphshell_docs
- technical_architecture
- testing
- 2026-02-27_planning_register_historical_tail_archive_receipt
-
graphshell_docs
-
checkpoint_2026-03-01
- 2026-02-27_ux_baseline_done_definition
- 2026-02-28_current_milestone_ux_contract_checklist
- 2026-02-28_ux_issue_domain_map
- 2026-03-01_embedder_debt_host_ui_boundary_ownership_receipt
- 2026-03-01_issue_171_compositor_chaos_mode_receipt
- 2026-03-01_issue_175_content_open_routing_receipt
- 2026-03-01_issue_180_bridge_diagnostics_wishlist_receipt
- 2026-03-01_issue_180_bridge_probe_instrumentation_receipt
- 2026-03-01_issue_180_bridge_spike_export_harness_receipt
- 2026-03-01_issue_180_bridge_spike_run_01_receipt
- 2026-03-01_issue_180_bridge_spike_run_02_receipt
- 2026-03-01_issue_180_bridge_spike_run_03_receipt
- 2026-03-01_issue_180_bridge_spike_sample_schema_receipt
- 2026-03-01_issue_180_gl_to_wgpu_bridge_baseline_receipt
- 2026-03-01_issue_180_scissor_box_restoration_fix_receipt
- checkpoint_2026-03-05
- checkpoint_2026-03-07
-
checkpoint_2026-03-10
-
graphshell_docs
- implementation_strategy
- 2026-03-10_planning_register_audit_cleanup_receipt
-
graphshell_docs
-
checkpoint_2026-03-18
-
graphshell_docs
- implementation_strategy
- 2026-03-18_event_log_fact_store_query_architecture
- 2026-03-18_fact_query_type_sketch
- node_navigation_history_spec
-
graphshell_docs
-
checkpoint_2026-03-19
-
graphshell_docs
- implementation_strategy
-
graphshell_docs
- checkpoint_2026-03-21
-
checkpoint_2026-03-22
-
graphshell_docs
- implementation_strategy
-
graphshell_docs
-
checkpoint_2026-03-27
-
graphshell_docs
- technical_architecture
-
graphshell_docs
-
checkpoint_2026-03-28
-
graphshell_docs
- implementation_strategy
-
graphshell_docs
-
checkpoint_2026-04-01
-
graphshell_docs
-
implementation_strategy
-
graph
- 2026-02-20_node_badge_and_tagging_plan
- 2026-02-22_multi_graph_pane_plan
- 2026-02-23_graph_interaction_consistency_plan
- 2026-02-23_udc_semantic_tagging_plan
- 2026-02-25_progressive_lens_and_physics_binding_plan
- 2026-03-05_hybrid_graph_view_overview_atlas_plan
- 2026-03-31_node_badge_and_tagging_follow_on_plan
-
graph
-
implementation_strategy
-
graphshell_docs
-
checkpoint_2026-04-02
-
graphshell_docs
- implementation_strategy
-
graphshell_docs
-
checkpoint_2026-04-03
-
graphshell_docs
- implementation_strategy
-
graphshell_docs
-
checkpoint_2026-04-06
-
graphshell_docs
-
implementation_strategy
- subsystem_ux_semantics
-
implementation_strategy
-
graphshell_docs
-
checkpoint_2026-04-07
-
graphshell_docs
-
implementation_strategy
- subsystem_ux_semantics
-
implementation_strategy
-
graphshell_docs
-
checkpoint_2026-04-09
-
wr-wgpu-notes
- 2026-03-01_webrender_wgpu_renderer_implementation_plan
- 2026-04-03_p6_progress_report
- 2026-04-03_p8_progress_report
- 2026-04-04_p10_progress_report
- 2026-04-04_p9_progress_report
- 2026-04-05_p11_progress_report
- 2026-04-05_p12_progress_report
- 2026-04-07_p13_progress_report
- 2026-04-07_p14_progress_report
- 2026-04-08_live_full_reftest_confirmation
- draw_context_plan
- PROGRESS
- project_wgpu_backend
- servo_wgpu_integration
- shader_translation_journal
- texture_cache_cleanup_plan
- typed_pipeline_metadata_plan
- wasm-portability-checklist
- wgpu-backend-minimal-plan
- wr_wgpu_debug_plan
- wr_wgpu_diagnostics_archive
-
wr-wgpu-notes
-
graphshell_docs
- design
-
implementation_strategy
- aspect_command
- aspect_control
- aspect_distillery
- aspect_input
- aspect_projection
-
aspect_render
- 2026-03-03_servo_wgpu_upgrade_audit_report
- 2026-03-08_render_mod_decomposition_plan
- 2026-03-12_compositor_expansion_plan
- 2026-03-27_egui_retained_state_efficiency_and_physics_worker_evaluation_plan
- 2026-03-27_physics_spike_metrics
- ASPECT_RENDER
- frame_assembly_and_compositor_spec
- gl_to_wgpu_plan
- render_backend_contract_spec
- running_app_state_boundary_spec
-
graph
- 2026-02-24_physics_engine_extensibility_plan
- 2026-02-25_doi_fisheye_plan
- 2026-02-27_roadmap_lane_19_readiness_plan
- 2026-02-27_viewdimension_acceptance_contract
- 2026-03-05_hybrid_graph_view_overview_atlas_plan
- 2026-03-11_graph_enrichment_plan
- 2026-03-14_canvas_behavior_contract
- 2026-03-14_edge_operability_matrix
- 2026-03-14_edge_visual_encoding_spec
- 2026-03-14_graph_relation_families
- 2026-03-21_edge_family_and_provenance_expansion_plan
- 2026-03-21_edge_payload_type_sketch
- 2026-04-01_swatch_spec_extraction_plan
- 2026-04-02_parry2d_scene_enrichment_plan
- 2026-04-02_scene_mode_ux_plan
- 2026-04-03_damping_profile_follow_on_plan
- 2026-04-03_edge_routing_follow_on_plan
- 2026-04-03_layout_backend_state_ownership_plan
- 2026-04-03_layout_transition_and_history_plan
- 2026-04-03_layout_variant_follow_on_plan
- 2026-04-03_node_glyph_spec
- 2026-04-03_physics_preferences_surface_plan
- 2026-04-03_physics_region_plan
- 2026-04-03_semantic_clustering_follow_on_plan
- 2026-04-03_twod_twopointfive_isometric_plan
- 2026-04-03_wasm_layout_runtime_plan
- 2026-04-10_vello_scene_canvas_rapier_scene_mode_architecture_plan
- 2026-04-11_graph_canvas_crate_plan
- agent_derived_edges_spec
- facet_pane_routing_spec
- faceted_filter_surface_spec
- force_layout_and_barnes_hut_spec
- frame_graph_representation_spec
- GRAPH
- graph_backlog_pack
- graph_node_edge_interaction_spec
- layout_algorithm_portfolio_spec
- layout_behaviors_and_physics_spec
- multi_view_pane_spec
- node_badge_and_tagging_spec
- petgraph_algorithm_utilization_spec
- semantic_tagging_and_knowledge_spec
- view_dimension_spec
- navigator
- shell
- social
- subsystem_accessibility
- subsystem_diagnostics
- subsystem_focus
-
subsystem_history
- 2026-03-08_unified_history_architecture_plan
- 2026-03-18_mixed_timeline_contract
- 2026-04-02_bookmarks_import_plan
- 2026-04-02_browser_history_import_plan
- 2026-04-09_owner_scoped_temporal_branching_follow_on
- 2026-04-11_browser_import_normalized_carrier_sketch
- edge_traversal_spec
- history_timeline_and_temporal_navigation_spec
- node_audit_log_spec
- SUBSYSTEM_HISTORY
- subsystem_mods
- subsystem_security
- subsystem_storage
-
subsystem_ux_semantics
- 2026-02-28_ux_contract_register
- 2026-03-01_automated_ux_testing_research
- 2026-03-01_ux_execution_control_plane
- 2026-03-04_model_boundary_control_matrix
- 2026-03-04_per_control_audit_grid
- 2026-03-08_unified_ux_semantics_architecture_plan
- 2026-03-13_chrome_scope_split_plan
- 2026-04-05_command_surface_observability_and_at_plan
- SUBSYSTEM_UX_SEMANTICS
- ux_event_dispatch_spec
- ux_scenario_and_harness_spec
- ux_tree_and_probe_spec
-
system
-
register
- 2026-03-08_registry_development_plan
- 2026-03-08_sector_b_input_dispatch_plan
- 2026-03-08_sector_c_identity_verse_plan
- 2026-03-08_sector_e_workbench_surface_plan
- 2026-03-08_sector_g_mod_agent_plan
- 2026-03-08_sector_h_signal_infrastructure_plan
- action_registry_contract_spec
- action_registry_spec
- agent_registry_spec
- canvas_registry_spec
- diagnostics_registry_spec
- identity_registry_spec
- index_registry_spec
- input_registry_spec
- knowledge_registry_spec
- layout_domain_registry_spec
- layout_registry_spec
- lens_compositor_spec
- mod_registry_spec
- physics_profile_registry_spec
- presentation_domain_registry_spec
- protocol_registry_spec
- SYSTEM_REGISTER
- theme_registry_spec
- viewer_registry_spec
- viewer_surface_registry_spec
- workbench_surface_registry_spec
- workflow_registry_spec
- 2026-02-21_lifecycle_intent_model
- 2026-03-03_graphshell_address_scheme_implementation_plan
- 2026-03-05_cp4_p2p_sync_plan
- 2026-03-05_network_architecture
- 2026-03-06_foundational_reset_implementation_plan
- 2026-03-06_reducer_only_mutation_enforcement_plan
- 2026-03-08_graph_app_decomposition_plan
- 2026-03-12_architectural_inconsistency_register
- 2026-03-12_workspace_decomposition_and_renaming_plan
- 2026-03-17_multi_identity_binding_rules
- 2026-03-17_runtime_task_budget
- control_panel_spec
- coop_session_spec
- register_layer_spec
- registry_runtime_spec
- signal_bus_spec
- system_architecture_spec
- VERSIONING_POLICY
-
register
-
viewer
- 2026-02-24_universal_content_model_plan
- 2026-02-25_interactive_html_export_plan
- 2026-02-26_composited_viewer_pass_contract
- 2026-02-26_visual_tombstones_plan
- 2026-03-02_filesystem_ingest_graph_mapping_plan
- 2026-03-02_unified_source_directory_mapping_plan
- 2026-03-05_node_viewport_preview_minimal_slice_plan
- 2026-03-08_servo_text_editor_architecture_plan
- 2026-03-28_wry_composited_texture_feasibility_spike
- 2026-04-03_clipping_viewer_follow_on_plan
- clipping_and_dom_extraction_spec
- node_lifecycle_and_runtime_reconcile_spec
- node_viewport_preview_spec
- universal_content_model_spec
- VIEWER
- viewer_presentation_and_fallback_spec
- visual_tombstones_spec
- webview_backpressure_spec
- webview_lifecycle_and_crash_recovery_spec
- wry_integration_spec
-
workbench
- 2026-03-03_pane_opening_mode_and_simplification_suppressed_plan
- 2026-03-26_frame_layout_hint_spec
- 2026-04-10_graph_tree_implementation_plan
- 2026-04-11_egui_tiles_retirement_strategy
- 2026-04-11_graph_tree_egui_tiles_decoupling_follow_on_plan
- frame_persistence_format_spec
- graph_first_frame_semantics_spec
- graphlet_projection_binding_spec
- navigator_backlog_pack
- navigator_graph_isomorphism_spec
- pane_chrome_and_promotion_spec
- pane_presentation_and_locking_spec
- tile_view_ops_spec
- WORKBENCH
- workbench_backlog_pack
- workbench_frame_tile_interaction_spec
- workbench_layout_policy_spec
- workbench_profile_and_workflow_composition_spec
- 2026-02-28_ux_contract_register
- 2026-03-01_complete_feature_inventory
- 2026-03-01_ux_migration_design_spec
- 2026-03-01_ux_migration_feature_spec_coverage_matrix
- 2026-03-01_ux_migration_lifecycle_audit_register
- 2026-03-01_webrender_readiness_gate_feature_guardrails
- 2026-03-02_scaffold_registry
- 2026-03-03_pre_wgpu_feature_validation_gate_checklist
- 2026-03-03_pre_wgpu_plot
- 2026-03-03_spec_conflict_resolution_register
- 2026-03-11_boa_scripting_engine_plan
- core-interaction-model-plan
- domain_interaction_acceptance_matrix
- plan-featureFirstNowRefactorFriendlyLater.prompt
- PLANNING_REGISTER
-
research
- 2026-02-18_graph_ux_research_report
- 2026-02-20_edge_traversal_model_research
- 2026-02-24_diagnostics_research
- 2026-02-24_interaction_and_semantic_design_schemes
- 2026-02-24_spatial_accessibility_research
- 2026-02-24_visual_tombstones_research
- 2026-02-26_architecture_terminology_alignment_gap_analysis
- 2026-02-26_original_vision_to_current_architecture_mapping
- 2026-02-27_all_docs_context_bootstrap
- 2026-02-27_egui_stack_assessment
- 2026-02-27_egui_wgpu_custom_canvas_migration_requirements
- 2026-02-27_freenet_takeaways_for_graphshell
- 2026-02-27_viewer_state_matrix
- 2026-02-28_graphshell_ux_research_agenda
- 2026-02-28_knowledge_capture_research_agenda
- 2026-02-28_navigation_semantics_mental_models_research
- 2026-02-28_performance_visual_scaling_research_agenda
- 2026-02-28_viewer_backend_research_agenda
- 2026-03-01_servo_script_engine_alternatives
- 2026-03-01_webrender_wgpu_renderer_research
- 2026-03-02_ux_integration_research
- 2026-03-04_standards_alignment_report
- 2026-03-24_tool_comparison_product_lessons
- 2026-03-27_ambient_graph_visual_effects
- 2026-03-29_graph_interaction_brainstorm
- 2026-03-30_middlenet_vision_synthesis
- 2026-04-02_intelligence_capability_tiers_and_blockers
- 2026-04-02_intelligence_taxonomy
- 2026-04-02_scene_mode_ux_sketch
- 2026-04-03_servo_slint_wgpu_hal_interop_research
- 2026-04-09_smolweb_browser_capability_gaps
- 2026-04-09_smolweb_discovery_and_aggregation_signal_model
- 2026-04-09_smolweb_graph_enrichment_and_accessibility_note
- 2026-04-10_servo_wpt_mountain_and_wr_wgpu_leverage
- 2026-04-10_ui_framework_alternatives_and_graph_tree_discovery
- 2026-04-11_linked_data_over_middlenet_relevance_note
- 2026-04-11_tabfs_tablab_graphshell_relevance_note
- scene_customization
- STANDALONE_EXTRACTION
-
technical_architecture
- 2026-02-18_universal_node_content_model
- 2026-02-27_presentation_provider_and_ai_orchestration
- 2026-03-01_dependency_inventory
- 2026-03-08_graphshell_core_extraction_plan
- 2026-03-12_specification_coverage_register
- 2026-03-29_middlenet_engine_spec
- 2026-03-29_portable_web_core_host_envelopes
- 2026-03-29_workspace_restructuring_plan
- 2026-03-30_protocol_modularity_and_host_capability_model
- 2026-04-09_browser_envelope_coop_and_degradation_policy
- 2026-04-09_graph_object_classification_model
- 2026-04-09_graphshell_verse_uri_scheme
- 2026-04-09_identity_convergence_and_person_node_model
- 2026-04-11_core_intent_inventory
- ARCHITECTURAL_OVERVIEW
- BUILD
- codebase_guide
- domain_interaction_scenarios
- graph_canvas_spec
- graph_tree_spec
- graphlet_model
- GRAPHSHELL_AS_BROWSER
- node_object_query_model
- QUICKSTART
- TERM_ARCHITECTURE_DESC
- unified_view_model
- testing
- matrix_docs
-
nostr_docs
- implementation_strategy
- technical_architecture
-
verse_docs
-
implementation_strategy
- 2026-02-26_intelligence_memory_architecture_stm_ltm_engrams_plan
- 2026-03-09_agent_wal_and_distillery_architecture_plan
- 2026-03-28_decentralized_storage_bank_spec
- 2026-04-06_sync_badge_verse_controls_plan
- community_governance_spec
- engram_spec
- flora_submission_checkpoint_spec
- lineage_dag_spec
- proof_of_access_ledger_spec
- self_hosted_model_spec
- self_hosted_verse_node_spec
- verseblob_content_addressing_spec
-
research
- 2026-02-22_aspirational_protocols_and_tools
- 2026-02-23_modern_yacy_gap_analysis
- 2026-02-23_storage_economy_and_indices
- 2026-02-24_local_intelligence_research
- 2026-02-24_wasm_mod_abi_research
- 2026-02-27_freenet_takeaways_for_verse
- 2026-02-28_verse_concrete_research_agenda
- 2026-03-28_libp2p_nostr_synergy_for_verse
- SEARCH_FINDINGS_SUMMARY
- VERSE
- technical_architecture
-
implementation_strategy
-
verso_docs
-
implementation_strategy
- 2026-02-22_verse_implementation_strategy
- 2026-02-23_verse_tier1_sync_plan
- 2026-02-25_verse_presence_plan
- 2026-03-08_simple_document_engine_target_spec
- 2026-03-27_session_capsule_ledger_plan
- 2026-03-28_cable_coop_minichat_spec
- 2026-03-28_gemini_capsule_server_plan
- coop_session_spec
- PHASE5_STEP5.1_COMPLETE
- PHASE5_STEP5.2_COMPLETE
- PHASE5_STEP5.3_COMPLETE
- research
- technical_architecture
-
implementation_strategy
- 2026-03-14_documentation_compression_plan
- AI_USAGE
- DOC_POLICY
- DOC_README
- OPERATOR_GUIDE
- PROJECT_DESCRIPTION
- TERMINOLOGY
-
archive_docs