mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-10 11:23:32 +08:00
feat: add ecc2 delegate blocker hints
This commit is contained in:
@@ -192,6 +192,7 @@ struct AggregateUsage {
|
|||||||
struct DelegatedChildSummary {
|
struct DelegatedChildSummary {
|
||||||
session_id: String,
|
session_id: String,
|
||||||
state: SessionState,
|
state: SessionState,
|
||||||
|
worktree_health: Option<worktree::WorktreeHealth>,
|
||||||
approval_backlog: usize,
|
approval_backlog: usize,
|
||||||
handoff_backlog: usize,
|
handoff_backlog: usize,
|
||||||
tokens_used: u64,
|
tokens_used: u64,
|
||||||
@@ -2514,6 +2515,10 @@ impl Dashboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
route_candidates.push(DelegatedChildSummary {
|
route_candidates.push(DelegatedChildSummary {
|
||||||
|
worktree_health: self
|
||||||
|
.worktree_health_by_session
|
||||||
|
.get(&child_id)
|
||||||
|
.copied(),
|
||||||
approval_backlog,
|
approval_backlog,
|
||||||
handoff_backlog,
|
handoff_backlog,
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
@@ -2534,6 +2539,10 @@ impl Dashboard {
|
|||||||
.map(|line| truncate_for_dashboard(&line.text, 48)),
|
.map(|line| truncate_for_dashboard(&line.text, 48)),
|
||||||
});
|
});
|
||||||
delegated.push(DelegatedChildSummary {
|
delegated.push(DelegatedChildSummary {
|
||||||
|
worktree_health: self
|
||||||
|
.worktree_health_by_session
|
||||||
|
.get(&session.id)
|
||||||
|
.copied(),
|
||||||
approval_backlog,
|
approval_backlog,
|
||||||
handoff_backlog,
|
handoff_backlog,
|
||||||
state,
|
state,
|
||||||
@@ -2567,6 +2576,14 @@ impl Dashboard {
|
|||||||
self.selected_team_summary = if team.total > 0 { Some(team) } else { None };
|
self.selected_team_summary = if team.total > 0 { Some(team) } else { None };
|
||||||
self.selected_route_preview =
|
self.selected_route_preview =
|
||||||
self.build_route_preview(team.total, &route_candidates);
|
self.build_route_preview(team.total, &route_candidates);
|
||||||
|
delegated.sort_by_key(|delegate| {
|
||||||
|
(
|
||||||
|
delegate_attention_priority(delegate),
|
||||||
|
std::cmp::Reverse(delegate.approval_backlog),
|
||||||
|
std::cmp::Reverse(delegate.handoff_backlog),
|
||||||
|
delegate.session_id.clone(),
|
||||||
|
)
|
||||||
|
});
|
||||||
delegated.truncate(3);
|
delegated.truncate(3);
|
||||||
delegated
|
delegated
|
||||||
}
|
}
|
||||||
@@ -3019,16 +3036,26 @@ impl Dashboard {
|
|||||||
lines.push("Delegates".to_string());
|
lines.push("Delegates".to_string());
|
||||||
for child in &self.selected_child_sessions {
|
for child in &self.selected_child_sessions {
|
||||||
let mut child_line = format!(
|
let mut child_line = format!(
|
||||||
"- {} [{}] | approvals {} | backlog {} | progress {} tok / {} files / {} | task {}",
|
"- {} [{}] | next {}",
|
||||||
format_session_id(&child.session_id),
|
format_session_id(&child.session_id),
|
||||||
session_state_label(&child.state),
|
session_state_label(&child.state),
|
||||||
|
delegate_next_action(child)
|
||||||
|
);
|
||||||
|
if let Some(worktree_health) = child.worktree_health {
|
||||||
|
child_line.push_str(&format!(
|
||||||
|
" | worktree {}",
|
||||||
|
delegate_worktree_health_label(worktree_health)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
child_line.push_str(&format!(
|
||||||
|
" | approvals {} | backlog {} | progress {} tok / {} files / {} | task {}",
|
||||||
child.approval_backlog,
|
child.approval_backlog,
|
||||||
child.handoff_backlog,
|
child.handoff_backlog,
|
||||||
format_token_count(child.tokens_used),
|
format_token_count(child.tokens_used),
|
||||||
child.files_changed,
|
child.files_changed,
|
||||||
format_duration(child.duration_secs),
|
format_duration(child.duration_secs),
|
||||||
child.task_preview
|
child.task_preview
|
||||||
);
|
));
|
||||||
if let Some(branch) = child.branch.as_ref() {
|
if let Some(branch) = child.branch.as_ref() {
|
||||||
child_line.push_str(&format!(" | branch {branch}"));
|
child_line.push_str(&format!(" | branch {branch}"));
|
||||||
}
|
}
|
||||||
@@ -4194,6 +4221,65 @@ fn assignment_action_label(action: manager::AssignmentAction) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn delegate_worktree_health_label(health: worktree::WorktreeHealth) -> &'static str {
|
||||||
|
match health {
|
||||||
|
worktree::WorktreeHealth::Clear => "clear",
|
||||||
|
worktree::WorktreeHealth::InProgress => "in progress",
|
||||||
|
worktree::WorktreeHealth::Conflicted => "conflicted",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delegate_next_action(delegate: &DelegatedChildSummary) -> &'static str {
|
||||||
|
if delegate.worktree_health == Some(worktree::WorktreeHealth::Conflicted) {
|
||||||
|
return "resolve conflict";
|
||||||
|
}
|
||||||
|
if delegate.approval_backlog > 0 {
|
||||||
|
return "review approvals";
|
||||||
|
}
|
||||||
|
if delegate.handoff_backlog > 0 && delegate.state == SessionState::Idle {
|
||||||
|
return "process handoff";
|
||||||
|
}
|
||||||
|
if delegate.handoff_backlog > 0 {
|
||||||
|
return "drain backlog";
|
||||||
|
}
|
||||||
|
if delegate.worktree_health == Some(worktree::WorktreeHealth::InProgress) {
|
||||||
|
return "finish worktree changes";
|
||||||
|
}
|
||||||
|
match delegate.state {
|
||||||
|
SessionState::Pending => "wait for startup",
|
||||||
|
SessionState::Running => "let it run",
|
||||||
|
SessionState::Idle => "assign next task",
|
||||||
|
SessionState::Failed => "inspect failure",
|
||||||
|
SessionState::Stopped => "resume or reassign",
|
||||||
|
SessionState::Completed => "merge or cleanup",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delegate_attention_priority(delegate: &DelegatedChildSummary) -> u8 {
|
||||||
|
if delegate.worktree_health == Some(worktree::WorktreeHealth::Conflicted) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if delegate.approval_backlog > 0 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if matches!(delegate.state, SessionState::Failed | SessionState::Stopped) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if delegate.handoff_backlog > 0 {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if delegate.worktree_health == Some(worktree::WorktreeHealth::InProgress) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
match delegate.state {
|
||||||
|
SessionState::Pending => 5,
|
||||||
|
SessionState::Running => 6,
|
||||||
|
SessionState::Idle => 7,
|
||||||
|
SessionState::Completed => 8,
|
||||||
|
SessionState::Failed | SessionState::Stopped => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn session_branch(session: &Session) -> String {
|
fn session_branch(session: &Session) -> String {
|
||||||
session
|
session
|
||||||
.worktree
|
.worktree
|
||||||
@@ -4524,6 +4610,7 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
dashboard.selected_child_sessions = vec![DelegatedChildSummary {
|
dashboard.selected_child_sessions = vec![DelegatedChildSummary {
|
||||||
session_id: "delegate-12345678".to_string(),
|
session_id: "delegate-12345678".to_string(),
|
||||||
state: SessionState::Running,
|
state: SessionState::Running,
|
||||||
|
worktree_health: Some(worktree::WorktreeHealth::Conflicted),
|
||||||
approval_backlog: 1,
|
approval_backlog: 1,
|
||||||
handoff_backlog: 2,
|
handoff_backlog: 2,
|
||||||
tokens_used: 1_280,
|
tokens_used: 1_280,
|
||||||
@@ -4537,7 +4624,7 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
let text = dashboard.selected_session_metrics_text();
|
let text = dashboard.selected_session_metrics_text();
|
||||||
assert!(
|
assert!(
|
||||||
text.contains(
|
text.contains(
|
||||||
"- delegate [Running] | approvals 1 | backlog 2 | progress 1,280 tok / 3 files / 00:00:12 | task Implement rust tui delegate board | branch ecc/delegate-12345678"
|
"- delegate [Running] | next resolve conflict | worktree conflicted | approvals 1 | backlog 2 | progress 1,280 tok / 3 files / 00:00:12 | task Implement rust tui delegate board | branch ecc/delegate-12345678"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert!(text.contains(" last output Investigating pane selection behavior"));
|
assert!(text.contains(" last output Investigating pane selection behavior"));
|
||||||
@@ -5089,10 +5176,18 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
dashboard
|
dashboard
|
||||||
.approval_queue_counts
|
.approval_queue_counts
|
||||||
.insert("worker-12345678".into(), 2);
|
.insert("worker-12345678".into(), 2);
|
||||||
|
dashboard.worktree_health_by_session.insert(
|
||||||
|
"worker-12345678".into(),
|
||||||
|
worktree::WorktreeHealth::InProgress,
|
||||||
|
);
|
||||||
|
|
||||||
dashboard.sync_selected_lineage();
|
dashboard.sync_selected_lineage();
|
||||||
|
|
||||||
assert_eq!(dashboard.selected_child_sessions.len(), 1);
|
assert_eq!(dashboard.selected_child_sessions.len(), 1);
|
||||||
|
assert_eq!(
|
||||||
|
dashboard.selected_child_sessions[0].worktree_health,
|
||||||
|
Some(worktree::WorktreeHealth::InProgress)
|
||||||
|
);
|
||||||
assert_eq!(dashboard.selected_child_sessions[0].approval_backlog, 2);
|
assert_eq!(dashboard.selected_child_sessions[0].approval_backlog, 2);
|
||||||
assert_eq!(dashboard.selected_child_sessions[0].tokens_used, 128);
|
assert_eq!(dashboard.selected_child_sessions[0].tokens_used, 128);
|
||||||
assert_eq!(dashboard.selected_child_sessions[0].files_changed, 2);
|
assert_eq!(dashboard.selected_child_sessions[0].files_changed, 2);
|
||||||
@@ -5113,6 +5208,73 @@ diff --git a/src/next.rs b/src/next.rs
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sync_selected_lineage_prioritizes_conflicted_delegate_rows() {
|
||||||
|
let lead = sample_session(
|
||||||
|
"lead-12345678",
|
||||||
|
"planner",
|
||||||
|
SessionState::Running,
|
||||||
|
Some("ecc/lead"),
|
||||||
|
512,
|
||||||
|
42,
|
||||||
|
);
|
||||||
|
let conflicted = sample_session(
|
||||||
|
"worker-conflict",
|
||||||
|
"planner",
|
||||||
|
SessionState::Running,
|
||||||
|
Some("ecc/conflict"),
|
||||||
|
128,
|
||||||
|
12,
|
||||||
|
);
|
||||||
|
let idle = sample_session(
|
||||||
|
"worker-idle",
|
||||||
|
"planner",
|
||||||
|
SessionState::Idle,
|
||||||
|
Some("ecc/idle"),
|
||||||
|
64,
|
||||||
|
6,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut dashboard = test_dashboard(vec![lead.clone(), conflicted.clone(), idle.clone()], 0);
|
||||||
|
dashboard.db.insert_session(&lead).unwrap();
|
||||||
|
dashboard.db.insert_session(&conflicted).unwrap();
|
||||||
|
dashboard.db.insert_session(&idle).unwrap();
|
||||||
|
dashboard
|
||||||
|
.db
|
||||||
|
.send_message(
|
||||||
|
"lead-12345678",
|
||||||
|
"worker-conflict",
|
||||||
|
"{\"task\":\"Handle conflict\",\"context\":\"Delegated from lead\"}",
|
||||||
|
"task_handoff",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
dashboard
|
||||||
|
.db
|
||||||
|
.send_message(
|
||||||
|
"lead-12345678",
|
||||||
|
"worker-idle",
|
||||||
|
"{\"task\":\"Idle follow-up\",\"context\":\"Delegated from lead\"}",
|
||||||
|
"task_handoff",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
dashboard.worktree_health_by_session.insert(
|
||||||
|
"worker-conflict".into(),
|
||||||
|
worktree::WorktreeHealth::Conflicted,
|
||||||
|
);
|
||||||
|
|
||||||
|
dashboard.sync_selected_lineage();
|
||||||
|
|
||||||
|
assert_eq!(dashboard.selected_child_sessions.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
dashboard.selected_child_sessions[0].session_id,
|
||||||
|
"worker-conflict"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dashboard.selected_child_sessions[0].worktree_health,
|
||||||
|
Some(worktree::WorktreeHealth::Conflicted)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aggregate_cost_summary_mentions_total_cost() {
|
fn aggregate_cost_summary_mentions_total_cost() {
|
||||||
let db = StateStore::open(Path::new(":memory:")).unwrap();
|
let db = StateStore::open(Path::new(":memory:")).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user