feat: surface ecc2 graph context in metrics

This commit is contained in:
Affaan Mustafa
2026-04-10 04:35:34 -07:00
parent beaba1ca15
commit 4b1ff48219

View File

@@ -38,6 +38,7 @@ const PANE_RESIZE_STEP_PERCENT: u16 = 5;
const MAX_LOG_ENTRIES: u64 = 12; const MAX_LOG_ENTRIES: u64 = 12;
const MAX_DIFF_PREVIEW_LINES: usize = 6; const MAX_DIFF_PREVIEW_LINES: usize = 6;
const MAX_DIFF_PATCH_LINES: usize = 80; const MAX_DIFF_PATCH_LINES: usize = 80;
const MAX_METRICS_GRAPH_RELATIONS: usize = 6;
const MAX_FILE_ACTIVITY_PATCH_LINES: usize = 3; const MAX_FILE_ACTIVITY_PATCH_LINES: usize = 3;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -5135,6 +5136,60 @@ impl Dashboard {
lines lines
} }
fn session_graph_metrics_lines(&self, session_id: &str) -> Vec<String> {
let entity = self
.db
.list_context_entities(Some(session_id), Some("session"), 4)
.unwrap_or_default()
.into_iter()
.find(|entity| {
entity.session_id.as_deref() == Some(session_id) || entity.name == session_id
});
let Some(entity) = entity else {
return Vec::new();
};
let Ok(Some(detail)) = self
.db
.get_context_entity_detail(entity.id, MAX_METRICS_GRAPH_RELATIONS)
else {
return Vec::new();
};
if detail.outgoing.is_empty() && detail.incoming.is_empty() {
return Vec::new();
}
let mut lines = vec![
"Context graph".to_string(),
format!(
"- outgoing {} | incoming {}",
detail.outgoing.len(),
detail.incoming.len()
),
];
for relation in detail.outgoing.iter().take(4) {
lines.push(format!(
"- -> {} {}:{}",
relation.relation_type,
relation.to_entity_type,
truncate_for_dashboard(&relation.to_entity_name, 72)
));
}
for relation in detail.incoming.iter().take(2) {
lines.push(format!(
"- <- {} {}:{}",
relation.relation_type,
relation.from_entity_type,
truncate_for_dashboard(&relation.from_entity_name, 72)
));
}
lines
}
fn visible_git_status_lines(&self) -> Vec<Line<'static>> { fn visible_git_status_lines(&self) -> Vec<Line<'static>> {
self.selected_git_status_entries self.selected_git_status_entries
.iter() .iter()
@@ -6100,6 +6155,7 @@ impl Dashboard {
} }
} }
} }
lines.extend(self.session_graph_metrics_lines(&session.id));
let file_overlaps = self let file_overlaps = self
.db .db
.list_file_overlaps(&session.id, 3) .list_file_overlaps(&session.id, 3)
@@ -10157,6 +10213,48 @@ diff --git a/src/lib.rs b/src/lib.rs\n\
Ok(()) Ok(())
} }
#[test]
fn selected_session_metrics_text_includes_context_graph_relations() -> Result<()> {
let focus = sample_session(
"focus-12345678",
"planner",
SessionState::Running,
None,
1,
1,
);
let delegate = sample_session(
"delegate-87654321",
"coder",
SessionState::Idle,
None,
1,
1,
);
let dashboard = test_dashboard(vec![focus.clone(), delegate.clone()], 0);
dashboard.db.insert_session(&focus)?;
dashboard.db.insert_session(&delegate)?;
dashboard.db.insert_decision(
&focus.id,
"Use sqlite graph sync",
&[],
"Keeps shared memory queryable",
)?;
dashboard.db.send_message(
&focus.id,
&delegate.id,
"{\"task\":\"Review graph edge\",\"context\":\"coordination smoke\"}",
"task_handoff",
)?;
let text = dashboard.selected_session_metrics_text();
assert!(text.contains("Context graph"));
assert!(text.contains("outgoing 2 | incoming 0"));
assert!(text.contains("-> decided decision:Use sqlite graph sync"));
assert!(text.contains("-> delegates_to session:delegate-87654321"));
Ok(())
}
#[test] #[test]
fn worktree_diff_columns_split_removed_and_added_lines() { fn worktree_diff_columns_split_removed_and_added_lines() {
let patch = "\ let patch = "\