fix(#1164,#1171): harness writes status file, c0re just rescans

This commit is contained in:
damocles 2026-06-03 18:23:32 +02:00 committed by mara
commit 049ae47191
5 changed files with 75 additions and 26 deletions

View file

@ -102,6 +102,51 @@ impl From<hive_sh4re::Response> for SocketReply {
}
}
/// Write (or remove) the status file in the agent's own `state/` directory.
/// Called by both `AgentServer::set_status` and `ManagerServer::set_status`
/// before dispatching the wire `SetStatus` request (which only triggers a
/// dashboard rescan on the host side — file I/O moved here because the
/// harness runs as the agent user and has write access to `state/`, whereas
/// hive-c0re's `hive-core` user does not after the privsep migration).
///
/// Mirrors the validation in `hive-c0re::limits::check_status_text` so
/// the file is never written with text the server would later reject (which
/// would leave a stale invalid entry on disk).
fn write_status_file(text: &str) -> Result<(), String> {
let trimmed = text.trim();
if !trimmed.is_empty() {
if trimmed.contains('\n') || trimmed.contains('\r') {
return Err(
"set_status text must be a single line — write multi-line context to \
a file under your state/ dir and reference that path from the chip instead"
.to_owned(),
);
}
// 200 chars mirrors STATUS_MAX_CHARS in hive-c0re/src/limits.rs.
// Keep in sync if that constant changes.
const STATUS_MAX_CHARS: usize = 200;
let len = trimmed.chars().count();
if len > STATUS_MAX_CHARS {
return Err(format!(
"set_status text too long ({len} chars, max {STATUS_MAX_CHARS}); trim to a short summary"
));
}
}
let path = crate::paths::state_dir().join("hyperhive-status");
let result = if trimmed.is_empty() {
std::fs::remove_file(&path).or_else(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
Ok(())
} else {
Err(e)
}
})
} else {
std::fs::write(&path, format!("{trimmed}\n"))
};
result.map_err(|e| format!("set_status write failed: {e}"))
}
/// Format helper for "send-like" tools (anything that expects an `Ok`).
/// `tool` and `ok_msg` only appear in the result string; they don't change
/// behavior.
@ -763,6 +808,9 @@ impl AgentServer {
)]
async fn set_status(&self, Parameters(args): Parameters<SetStatusArgs>) -> String {
run_tool_envelope("set_status", args.text.clone(), async move {
if let Err(e) = write_status_file(&args.text) {
return e;
}
let (resp, retries) = self
.dispatch(hive_sh4re::AgentRequest::SetStatus { text: args.text })
.await;
@ -1821,6 +1869,9 @@ impl ManagerServer {
)]
async fn set_status(&self, Parameters(args): Parameters<SetStatusArgs>) -> String {
run_tool_envelope("set_status", args.text.clone(), async move {
if let Err(e) = write_status_file(&args.text) {
return e;
}
let (resp, retries) = self
.dispatch(hive_sh4re::ManagerRequest::SetStatus { text: args.text })
.await;