dashboard: question_added / question_resolved mutation events + client derived state

This commit is contained in:
müde 2026-05-17 13:33:02 +02:00
parent 56d615b51f
commit 1879b2f485
6 changed files with 175 additions and 15 deletions

View file

@ -218,6 +218,64 @@ impl Coordinator {
});
}
/// Emit `QuestionAdded` after an operator-targeted question is
/// inserted. Peer-to-peer questions (those with a non-null
/// `target` agent) never fire this — they don't surface on the
/// dashboard at all. Caller is responsible for the
/// `target.is_none()` guard.
pub fn emit_question_added(
&self,
id: i64,
asker: &str,
question: &str,
options: &[String],
multi: bool,
deadline_at: Option<i64>,
) {
let asked_at = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.ok()
.and_then(|d| i64::try_from(d.as_secs()).ok())
.unwrap_or(0);
self.emit_dashboard_event(DashboardEvent::QuestionAdded {
seq: self.next_seq(),
id,
asker: asker.to_owned(),
question: question.to_owned(),
options: options.to_vec(),
multi,
asked_at,
deadline_at,
});
}
/// Emit `QuestionResolved` when an operator-targeted question
/// transitions to answered (operator answer, peer override,
/// cancel, or ttl watchdog). Caller filters on the original
/// question's `target.is_none()` — peer questions are dashboard-
/// invisible.
pub fn emit_question_resolved(
&self,
id: i64,
answer: &str,
answerer: &str,
cancelled: bool,
) {
let answered_at = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.ok()
.and_then(|d| i64::try_from(d.as_secs()).ok())
.unwrap_or(0);
self.emit_dashboard_event(DashboardEvent::QuestionResolved {
seq: self.next_seq(),
id,
answer: answer.to_owned(),
answerer: answerer.to_owned(),
answered_at,
cancelled,
});
}
pub fn register_agent(self: &Arc<Self>, name: &str) -> Result<PathBuf> {
// Idempotent: drop any existing listener so re-registration (e.g. on rebuild,
// or after a hive-c0re restart cleared /run/hyperhive) gets a fresh socket.