From d8e64742f42775b2fc2a204b21c2dfdde25614b1 Mon Sep 17 00:00:00 2001 From: damocles Date: Wed, 20 May 2026 20:29:39 +0200 Subject: [PATCH] fix question answer sender and self-cancel feedback loop --- hive-c0re/src/coordinator.rs | 10 +++++++++- hive-c0re/src/dashboard.rs | 3 ++- hive-c0re/src/questions.rs | 30 ++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index 3a85bf2..16af4f5 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -532,6 +532,14 @@ impl Coordinator { /// events back to the agent that called `ask`, `QuestionAsked` /// events to the target of a peer question, etc. pub fn notify_agent(&self, agent: &str, event: &hive_sh4re::HelperEvent) { + self.notify_agent_from(hive_sh4re::SYSTEM_SENDER, agent, event); + } + + /// Same as `notify_agent` but with an explicit sender. Use this + /// when the event originates from a known agent or the operator + /// (e.g. `QuestionAnswered` — the answerer should be the `from`, + /// not `system`) so the recipient's terminal shows the right name. + pub fn notify_agent_from(&self, from: &str, agent: &str, event: &hive_sh4re::HelperEvent) { let body = match serde_json::to_string(event) { Ok(s) => s, Err(e) => { @@ -540,7 +548,7 @@ impl Coordinator { } }; if let Err(e) = self.broker.send(&hive_sh4re::Message { - from: hive_sh4re::SYSTEM_SENDER.to_owned(), + from: from.to_owned(), to: agent.to_owned(), body, in_reply_to: None, diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index 0f085da..599251c 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -865,7 +865,8 @@ async fn post_cancel_question( true, target.as_deref(), ); - state.coord.notify_agent( + state.coord.notify_agent_from( + hive_sh4re::OPERATOR_RECIPIENT, &asker, &hive_sh4re::HelperEvent::QuestionAnswered { id, diff --git a/hive-c0re/src/questions.rs b/hive-c0re/src/questions.rs index 5ff82a9..c2ddcb6 100644 --- a/hive-c0re/src/questions.rs +++ b/hive-c0re/src/questions.rs @@ -112,7 +112,10 @@ pub fn handle_answer( .answer(id, answer, answerer) .map_err(|e| format!("{e:#}"))?; tracing::info!(%id, %answerer, %asker, "question answered"); - coord.notify_agent( + // Use answerer as the broker `from` so the asker's terminal shows + // the real name (agent or "operator") instead of "system". + coord.notify_agent_from( + answerer, &asker, &hive_sh4re::HelperEvent::QuestionAnswered { id, @@ -149,15 +152,22 @@ pub fn handle_cancel_loose_end( .map_err(|e| format!("{e:#}"))?; let sentinel = format!("[cancelled by {canceller}]"); tracing::info!(%id, %canceller, %asker, "question cancelled"); - coord.notify_agent( - &asker, - &hive_sh4re::HelperEvent::QuestionAnswered { - id, - question, - answer: sentinel.clone(), - answerer: canceller.to_owned(), - }, - ); + // Only notify the asker if they didn't cancel it themselves. + // Self-cancels are already known to the canceller — sending + // a QuestionAnswered back would cause the harness to process + // its own cancel as an incoming answer. + if asker != canceller { + coord.notify_agent_from( + canceller, + &asker, + &hive_sh4re::HelperEvent::QuestionAnswered { + id, + question, + answer: sentinel.clone(), + answerer: canceller.to_owned(), + }, + ); + } coord.emit_question_resolved(id, &sentinel, canceller, true, target.as_deref()); Ok(()) }