query: rich QueryResult + toolkit cleanup

QueryResult carries a fields map (BTreeMap<String, Value>) so callers
don't re-resolve fields after queries run. Neighbors queries inject
edge context (strength, rel_type) at construction time.

New public API:
- run_query(): parse + execute + format in one call
- format_value(): format a Value for display
- execute_parsed(): internal, avoids double-parse in run_query

Removed: output_stages(), format_field()

Simplified commands:
- cmd_query, cmd_graph, cmd_link, cmd_list_keys all delegate to run_query
- cmd_experience_mine uses existing find_current_transcript()

Deduplication:
- now_epoch() 3 copies → 1 (capnp_store's public fn)
- hub_threshold → Graph::hub_threshold() method
- eval_node + eval_edge → single eval() with closure for field resolution
- compare() collapsed via Ordering (35 → 15 lines)

Modernization:
- 12 sites of partial_cmp().unwrap_or(Ordering::Equal) → total_cmp()
This commit is contained in:
ProofOfConcept 2026-03-03 12:07:04 -05:00
parent 64d2b441f0
commit fa7fe8c14b
7 changed files with 187 additions and 264 deletions

View file

@ -10,14 +10,7 @@ use crate::similarity;
use crate::spectral::{self, SpectralEmbedding, SpectralPosition};
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
fn now_epoch() -> f64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs_f64()
}
use crate::capnp_store::now_epoch;
const SECS_PER_DAY: f64 = 86400.0;
@ -137,7 +130,7 @@ pub fn replay_queue_with_graph(
})
.collect();
items.sort_by(|a, b| b.priority.partial_cmp(&a.priority).unwrap_or(std::cmp::Ordering::Equal));
items.sort_by(|a, b| b.priority.total_cmp(&a.priority));
items.truncate(count);
items
}
@ -228,7 +221,7 @@ fn format_topology_header(graph: &Graph) -> String {
let e = graph.edge_count();
// Identify saturated hubs — nodes with degree well above threshold
let threshold = hub_threshold(graph);
let threshold = graph.hub_threshold();
let mut hubs: Vec<_> = graph.nodes().iter()
.map(|k| (k.clone(), graph.degree(k)))
.filter(|(_, d)| *d >= threshold)
@ -262,22 +255,10 @@ fn format_topology_header(graph: &Graph) -> String {
n, e, graph.community_count(), sigma, alpha, gini, avg_cc, hub_list)
}
/// Compute the hub degree threshold (top 5% by degree)
fn hub_threshold(graph: &Graph) -> usize {
let mut degrees: Vec<usize> = graph.nodes().iter()
.map(|k| graph.degree(k))
.collect();
degrees.sort_unstable();
if degrees.len() >= 20 {
degrees[degrees.len() * 95 / 100]
} else {
usize::MAX
}
}
/// Format node data section for prompt templates
fn format_nodes_section(store: &Store, items: &[ReplayItem], graph: &Graph) -> String {
let hub_thresh = hub_threshold(graph);
let hub_thresh = graph.hub_threshold();
let mut out = String::new();
for item in items {
let node = match store.nodes.get(&item.key) {
@ -363,7 +344,7 @@ fn format_nodes_section(store: &Store, items: &[ReplayItem], graph: &Graph) -> S
})
.filter(|(_, sim)| *sim > 0.1)
.collect();
candidates.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
candidates.sort_by(|a, b| b.1.total_cmp(&a.1));
candidates.truncate(8);
if !candidates.is_empty() {
@ -568,7 +549,7 @@ pub fn agent_prompt(store: &Store, agent: &str, count: usize) -> Result<String,
.filter(|(k, _)| k.contains("journal") || k.contains("session"))
.map(|(k, n)| (k.clone(), n.timestamp))
.collect();
episodes.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
episodes.sort_by(|a, b| b.1.total_cmp(&a.1));
episodes.truncate(count);
let episode_keys: Vec<_> = episodes.iter().map(|(k, _)| k.clone()).collect();
@ -959,7 +940,7 @@ pub fn differentiate_hub_with_graph(store: &Store, hub_key: &str, graph: &Graph)
}
}
moves.sort_by(|a, b| b.similarity.partial_cmp(&a.similarity).unwrap_or(std::cmp::Ordering::Equal));
moves.sort_by(|a, b| b.similarity.total_cmp(&a.similarity));
Some(moves)
}
@ -1017,7 +998,7 @@ pub fn apply_differentiation(
/// Find all file-level hubs that have section children to split into.
pub fn find_differentiable_hubs(store: &Store) -> Vec<(String, usize, usize)> {
let graph = store.build_graph();
let threshold = hub_threshold(&graph);
let threshold = graph.hub_threshold();
let mut hubs = Vec::new();
for key in graph.nodes() {
@ -1093,7 +1074,7 @@ pub fn triangle_close(
}
}
pair_scores.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
pair_scores.sort_by(|a, b| b.2.total_cmp(&a.2));
let to_add = pair_scores.len().min(max_links_per_hub);
if to_add > 0 {
@ -1168,7 +1149,7 @@ pub fn link_orphans(
.filter(|(_, s)| *s >= sim_threshold)
.collect();
scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
scores.sort_by(|a, b| b.1.total_cmp(&a.1));
let to_link = scores.len().min(links_per_orphan);
if to_link == 0 { continue; }