Watch
0
0
Fork
You've already forked hyperhive
0

deploy the closure the swarm declares, read no forge head, drop the update badge

This commit is contained in:
atlas 2026-11-01 23:56:40 +01:00 • committed by mara
commit 47c2acb640

View file

@ -42,12 +42,7 @@ pub struct ContainerView {
/// up is indistinguishable from one an operator stopped on purpose.
#[serde(default)]
pub failed: bool,
pub needs_update: bool,
pub needs_login: bool,
/// First 12 chars of the config commit the agent's last successful build
/// pinned (`paths::applied_config_marker`).
#[serde(skip_serializing_if = "Option::is_none")]
pub deployed_sha: Option<String>,
/// The Claude model the agent's harness is currently using, read from
/// `state/hyperhive-harness.json["active_model"]`. `None` when the
/// agent has never started a turn or the field is absent. Only
@ -97,9 +92,7 @@ impl From<ContainerView> for hive_sh4re::container::AgentStatusRow {
name: v.name,
running: v.running,
failed: v.failed,
needs_update: v.needs_update,
needs_login: v.needs_login,
deployed_sha: v.deployed_sha,
pending_reminders: 0,
paused: v.paused,
active_model: v.active_model,
@ -153,11 +146,6 @@ pub async fn build_all() -> anyhow::Result<Vec<ContainerView>> {
let Ok(logical) = hive_types::Ident::parse(logical) else {
continue;
};
let deployed_full =
std::fs::read_to_string(crate::paths::applied_config_marker(logical.as_str())).ok();
let needs_update =
crate::auto_update::agent_config_pending(logical.as_str(), deployed_full.as_deref());
let deployed_sha = deployed_full.as_deref().map(short_rev);
// One `systemctl` call for both facts: `unit_state` is the same
// shell-out `is_running` makes, minus `--quiet`. Asking twice would
// double the per-agent subprocess count on every SSE scan.
@ -211,9 +199,7 @@ pub async fn build_all() -> anyhow::Result<Vec<ContainerView>> {
failed,
container: c.clone(),
name: logical.into_string(),
needs_update,
needs_login,
deployed_sha,
active_model,
status_text,
status_set_at,
@ -378,27 +364,36 @@ pub fn hive_swarm_names() -> (Option<String>, Option<String>) {
(read("HYPERHIVE_HIVE_NAME"), read("HYPERHIVE_SWARM_NAME"))
}
/// First 12 characters of a rev. Counts chars, not bytes, so a byte slice at 12
/// cannot split a multi-byte char.
fn short_rev(rev: &str) -> String {
rev.chars().take(12).collect()
}
#[cfg(test)]
mod tests {
use super::short_rev;
/// The row says nothing of an update being pending or of a deployed commit:
/// the hive applies a changed declaration on its own, so there is no state
/// for an operator to act on.
#[test]
fn short_rev_truncates_and_keeps_short() {
assert_eq!(short_rev("0123456789abcdef"), "0123456789ab");
assert_eq!(short_rev("abc123"), "abc123");
}
#[test]
fn short_rev_survives_multibyte_at_boundary() {
// 'é' is 2 bytes and straddles byte index 12.
let rev = "0123456789aébcdef";
assert_eq!(short_rev(rev), "0123456789aé");
fn the_row_carries_no_update_state() {
use super::ContainerView;
let view = ContainerView {
name: "alice".to_owned(),
container: "h-alice".to_owned(),
port: 7000,
running: true,
failed: false,
needs_login: false,
active_model: None,
status_text: None,
status_set_at: None,
paused: false,
};
let value = serde_json::to_value(&view).unwrap();
assert_eq!(value["running"], true, "control: the lookup finds fields");
for gone in ["needs_update", "deployed_sha"] {
assert!(value.get(gone).is_none(), "{gone} is still serialized");
}
let row = serde_json::to_value(hive_sh4re::container::AgentStatusRow::from(view)).unwrap();
assert_eq!(row["running"], true);
for gone in ["needs_update", "deployed_sha"] {
assert!(row.get(gone).is_none(), "{gone} is still serialized");
}
}
/// The `From` impl both `handle_agent_status`'s poll path and
@ -417,9 +412,7 @@ mod tests {
port: 7000,
running: true,
failed: false,
needs_update: true,
needs_login: false,
deployed_sha: Some("abc123def456".to_owned()),
active_model: Some("claude-opus".to_owned()),
status_text: Some("shipping".to_owned()),
status_set_at: Some(Utc.timestamp_opt(1_700_000_000, 0).unwrap()),
@ -430,9 +423,7 @@ mod tests {
assert_eq!(row.name, "alice");
assert!(row.running);
assert!(!row.failed);
assert!(row.needs_update);
assert!(!row.needs_login);
assert_eq!(row.deployed_sha.as_deref(), Some("abc123def456"));
assert!(row.paused);
assert_eq!(row.active_model.as_deref(), Some("claude-opus"));
assert_eq!(row.status_text.as_deref(), Some("shipping"));