neuro: unify consolidation planning, fix threshold drift
The daemon's compute_graph_health had a duplicated copy of the consolidation planning thresholds that had drifted from the canonical version (α<2.0 → +7 replay in daemon vs +10 in neuro). Split consolidation_plan into _inner(store, detect_interference) so the daemon can call consolidation_plan_quick (skips O(n²) interference) while using the same threshold logic.
This commit is contained in:
parent
945865f594
commit
8ba58ce9cd
3 changed files with 36 additions and 64 deletions
|
|
@ -13,7 +13,7 @@ pub use scoring::{
|
|||
consolidation_priority,
|
||||
replay_queue, replay_queue_with_graph,
|
||||
detect_interference,
|
||||
consolidation_plan, format_plan,
|
||||
consolidation_plan, consolidation_plan_quick, format_plan,
|
||||
daily_check,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -203,18 +203,28 @@ impl ConsolidationPlan {
|
|||
/// This is the control loop: metrics → error signal → agent allocation.
|
||||
/// Target values are based on healthy small-world networks.
|
||||
pub fn consolidation_plan(store: &Store) -> ConsolidationPlan {
|
||||
consolidation_plan_inner(store, true)
|
||||
}
|
||||
|
||||
/// Cheap version: skip O(n²) interference detection (for daemon status).
|
||||
pub fn consolidation_plan_quick(store: &Store) -> ConsolidationPlan {
|
||||
consolidation_plan_inner(store, false)
|
||||
}
|
||||
|
||||
fn consolidation_plan_inner(store: &Store, detect_interf: bool) -> ConsolidationPlan {
|
||||
let graph = store.build_graph();
|
||||
let alpha = graph.degree_power_law_exponent();
|
||||
let gini = graph.degree_gini();
|
||||
let avg_cc = graph.avg_clustering_coefficient();
|
||||
let interference_pairs = detect_interference(store, &graph, 0.5);
|
||||
let interference_count = interference_pairs.len();
|
||||
let interference_count = if detect_interf {
|
||||
detect_interference(store, &graph, 0.5).len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Count episodic vs semantic nodes
|
||||
let episodic_count = store.nodes.iter()
|
||||
.filter(|(_, n)| matches!(n.node_type, crate::store::NodeType::EpisodicSession))
|
||||
.count();
|
||||
let _semantic_count = store.nodes.len() - episodic_count;
|
||||
let episodic_ratio = if store.nodes.is_empty() { 0.0 }
|
||||
else { episodic_count as f32 / store.nodes.len() as f32 };
|
||||
|
||||
|
|
@ -223,7 +233,7 @@ pub fn consolidation_plan(store: &Store) -> ConsolidationPlan {
|
|||
linker_count: 0,
|
||||
separator_count: 0,
|
||||
transfer_count: 0,
|
||||
run_health: true, // always run health first
|
||||
run_health: true,
|
||||
rationale: Vec::new(),
|
||||
};
|
||||
|
||||
|
|
@ -232,7 +242,7 @@ pub fn consolidation_plan(store: &Store) -> ConsolidationPlan {
|
|||
plan.replay_count += 10;
|
||||
plan.linker_count += 5;
|
||||
plan.rationale.push(format!(
|
||||
"α={:.2} (target ≥2.5): extreme hub dominance → 10 replay + 5 linker for lateral links",
|
||||
"α={:.2} (target ≥2.5): extreme hub dominance → 10 replay + 5 linker",
|
||||
alpha));
|
||||
} else if alpha < 2.5 {
|
||||
plan.replay_count += 5;
|
||||
|
|
@ -250,7 +260,7 @@ pub fn consolidation_plan(store: &Store) -> ConsolidationPlan {
|
|||
if gini > 0.5 {
|
||||
plan.replay_count += 3;
|
||||
plan.rationale.push(format!(
|
||||
"Gini={:.3} (target ≤0.4): high inequality → +3 replay (lateral focus)",
|
||||
"Gini={:.3} (target ≤0.4): high inequality → +3 replay",
|
||||
gini));
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +299,7 @@ pub fn consolidation_plan(store: &Store) -> ConsolidationPlan {
|
|||
if episodic_ratio > 0.6 {
|
||||
plan.transfer_count += 10;
|
||||
plan.rationale.push(format!(
|
||||
"Episodic ratio: {:.0}% ({}/{}) → 10 transfer (knowledge extraction needed)",
|
||||
"Episodic ratio: {:.0}% ({}/{}) → 10 transfer",
|
||||
episodic_ratio * 100.0, episodic_count, store.nodes.len()));
|
||||
} else if episodic_ratio > 0.4 {
|
||||
plan.transfer_count += 5;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue