From 1ceabae892b1c11bb34b89aeddecaf26adcce352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Fri, 15 May 2026 00:26:42 +0200 Subject: [PATCH] Phase 7c: ApprovalResolved helper events into manager's inbox --- hive-ag3nt/src/bin/hive-m1nd.rs | 11 ++++- hive-c0re/src/actions.rs | 76 +++++++++++++++++++++++++++++---- hive-c0re/src/approvals.rs | 1 - 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/hive-ag3nt/src/bin/hive-m1nd.rs b/hive-ag3nt/src/bin/hive-m1nd.rs index 9b704dc..498178f 100644 --- a/hive-ag3nt/src/bin/hive-m1nd.rs +++ b/hive-ag3nt/src/bin/hive-m1nd.rs @@ -9,7 +9,7 @@ use std::time::Duration; use anyhow::{Result, bail}; use clap::{Parser, Subcommand}; use hive_ag3nt::{DEFAULT_SOCKET, DEFAULT_WEB_PORT, client, web_ui}; -use hive_sh4re::{ManagerRequest, ManagerResponse}; +use hive_sh4re::{HelperEvent, ManagerRequest, ManagerResponse, SYSTEM_SENDER}; #[derive(Parser)] #[command(name = "hive-m1nd", about = "hyperhive manager harness")] @@ -94,7 +94,14 @@ async fn serve(socket: &Path, interval: Duration) -> Result<()> { let recv: Result = client::request(socket, &ManagerRequest::Recv).await; match recv { Ok(ManagerResponse::Message { from, body }) => { - tracing::info!(%from, %body, "manager inbox"); + if from == SYSTEM_SENDER { + match serde_json::from_str::(&body) { + Ok(event) => tracing::info!(?event, "helper event"), + Err(_) => tracing::info!(%from, %body, "system message"), + } + } else { + tracing::info!(%from, %body, "manager inbox"); + } } Ok(ManagerResponse::Empty) => {} Ok(ManagerResponse::Ok) => { diff --git a/hive-c0re/src/actions.rs b/hive-c0re/src/actions.rs index aebc5d7..df758a3 100644 --- a/hive-c0re/src/actions.rs +++ b/hive-c0re/src/actions.rs @@ -4,6 +4,9 @@ //! shape they want (HTTP redirect vs JSON). use anyhow::Result; +use hive_sh4re::{ + ApprovalStatus, HelperEvent, MANAGER_AGENT, Message, SYSTEM_SENDER, +}; use crate::coordinator::Coordinator; use crate::lifecycle; @@ -11,7 +14,8 @@ use crate::lifecycle; /// Approve a pending request: read the agent.nix at the approval's commit from /// the proposed repo, copy into the applied repo, commit there, and rebuild /// the agent container. On failure marks the approval failed (with the error -/// note) and returns the error. +/// note) and returns the error. Either way, an `ApprovalResolved` helper event +/// is pushed into the manager's inbox. pub async fn approve(coord: &Coordinator, id: i64) -> Result<()> { let approval = coord.approvals.mark_approved(id)?; tracing::info!(%approval.id, %approval.agent, %approval.commit_ref, "approval: applying + rebuilding"); @@ -30,16 +34,70 @@ pub async fn approve(coord: &Coordinator, id: i64) -> Result<()> { .await } .await; - if let Err(e) = result { - let note = format!("{e:#}"); - let _ = coord.approvals.mark_failed(approval.id, ¬e); - return Err(e); + match result { + Ok(()) => { + notify_manager( + coord, + &HelperEvent::ApprovalResolved { + id: approval.id, + agent: approval.agent.clone(), + commit_ref: approval.commit_ref.clone(), + status: ApprovalStatus::Approved, + note: None, + }, + ); + Ok(()) + } + Err(e) => { + let note = format!("{e:#}"); + let _ = coord.approvals.mark_failed(approval.id, ¬e); + notify_manager( + coord, + &HelperEvent::ApprovalResolved { + id: approval.id, + agent: approval.agent.clone(), + commit_ref: approval.commit_ref.clone(), + status: ApprovalStatus::Failed, + note: Some(note), + }, + ); + Err(e) + } + } +} + +pub fn deny(coord: &Coordinator, id: i64) -> Result<()> { + let approval = coord.approvals.get(id)?; + coord.approvals.mark_denied(id)?; + tracing::info!(%id, "approval denied"); + if let Some(a) = approval { + notify_manager( + coord, + &HelperEvent::ApprovalResolved { + id: a.id, + agent: a.agent, + commit_ref: a.commit_ref, + status: ApprovalStatus::Denied, + note: None, + }, + ); } Ok(()) } -pub fn deny(coord: &Coordinator, id: i64) -> Result<()> { - coord.approvals.mark_denied(id)?; - tracing::info!(%id, "approval denied"); - Ok(()) +fn notify_manager(coord: &Coordinator, event: &HelperEvent) { + let body = match serde_json::to_string(event) { + Ok(s) => s, + Err(e) => { + tracing::warn!(error = ?e, "failed to encode helper event"); + return; + } + }; + if let Err(e) = coord.broker.send(&Message { + from: SYSTEM_SENDER.to_owned(), + to: MANAGER_AGENT.to_owned(), + body, + }) { + tracing::warn!(error = ?e, "failed to push helper event to manager"); + } } diff --git a/hive-c0re/src/approvals.rs b/hive-c0re/src/approvals.rs index 8c32c40..8f6e66a 100644 --- a/hive-c0re/src/approvals.rs +++ b/hive-c0re/src/approvals.rs @@ -66,7 +66,6 @@ impl Approvals { .map_err(Into::into) } - #[allow(dead_code)] // used by Phase 5b commit verification pub fn get(&self, id: i64) -> Result> { let conn = self.conn.lock().unwrap(); conn.query_row(