harness: surface hive + swarm display names to agents (#701)

This commit is contained in:
damocles 2026-05-31 11:35:47 +02:00 committed by Mara
commit c41bf1b562
5 changed files with 239 additions and 16 deletions

View file

@ -1,4 +1,5 @@
//! Agent identity helpers — short label + hive-qualified long name.
//! Agent identity helpers — short label + hive-qualified long name +
//! human display names for the hive and swarm.
//!
//! `HIVE_LABEL` is the short, hive-local agent name (e.g. `iris`, `damocles`).
//! `HYPERHIVE_HIVE_DOMAIN` is the hive's canonical DNS domain (e.g.
@ -13,11 +14,19 @@
//! `from` / `to` qualification + dashboard rendering of cross-hive
//! identities are subsequent follow-ups inside #589.
//!
//! The hive name itself IS the operator's DNS domain — `hyperhive.hiveName`
//! was deliberately dropped in #589 spec discussion (mara #6577 / iris
//! #6582) so there's one source of truth. Matrix MXIDs already use the
//! same convention (`@iris:darkest.space`), so federation lookups Just Work
//! without a separate slug.
//! `HYPERHIVE_HIVE_NAME` + `HYPERHIVE_SWARM_NAME` are human-readable
//! display names for the local hive (`pr1ma`) and the wider swarm
//! (`constellat1on`) — added in #701 after mara's
//! `internal-requests#9` ("we want to persist this name somewhere in
//! the hive"). They're **distinct** from the DNS domain above: the
//! domain may carry the hive name as its leftmost label by
//! convention, but the convention isn't machine-readable, and
//! federated hives at different DNS domains can share a swarm name.
//! Both reverse the earlier #589 spec decision (mara #6577 / iris
//! #6582 dropped `hyperhive.hiveName` in favour of "the domain IS the
//! name") — turns out humans want both: the address (`@darkest.space`)
//! AND the prose name (`pr1ma`). Matrix MXIDs still use the
//! domain-based convention untouched.
use std::env;
@ -40,6 +49,30 @@ pub fn hive_domain() -> Option<String> {
.filter(|s| !s.is_empty())
}
/// Human display name of this hive (e.g. `pr1ma`). Distinct from
/// [`hive_domain`] — the domain is the machine-readable DNS address;
/// this is the prose label humans use in conversation. Returns None
/// when the host-side `services.hyperhive.hiveName` option is unset,
/// in which case callers fall back to the domain or the short label
/// at their discretion (#701).
#[must_use]
pub fn hive_name() -> Option<String> {
env::var("HYPERHIVE_HIVE_NAME")
.ok()
.filter(|s| !s.is_empty())
}
/// Human display name of the wider swarm this hive belongs to (e.g.
/// `constellat1on`). Federated hives at different DNS domains can
/// share a swarm name. Returns None when the host-side
/// `services.hyperhive.swarmName` option is unset (#701).
#[must_use]
pub fn swarm_name() -> Option<String> {
env::var("HYPERHIVE_SWARM_NAME")
.ok()
.filter(|s| !s.is_empty())
}
/// Hive-qualified agent identity. When the hive domain is configured, returns
/// `${label}@${domain}` (e.g. `iris@darkest.space`); when not, returns just
/// the short label so callers can render a single string regardless of
@ -82,9 +115,24 @@ mod tests {
/// to recover so a single test failure doesn't cascade through the
/// whole module.
fn with_env<F: FnOnce()>(label: Option<&str>, domain: Option<&str>, f: F) {
with_full_env(label, domain, None, None, f);
}
/// Extended form of [`with_env`] covering the #701 display-name env
/// vars (hive name + swarm name) alongside label + domain. Same
/// SAFETY contract — serialised on `ENV_LOCK`, restore in scope.
fn with_full_env<F: FnOnce()>(
label: Option<&str>,
domain: Option<&str>,
hive_name: Option<&str>,
swarm_name: Option<&str>,
f: F,
) {
let _guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let prev_label = env::var("HIVE_LABEL").ok();
let prev_domain = env::var("HYPERHIVE_HIVE_DOMAIN").ok();
let prev_hive_name = env::var("HYPERHIVE_HIVE_NAME").ok();
let prev_swarm_name = env::var("HYPERHIVE_SWARM_NAME").ok();
// SAFETY: serialised by ENV_LOCK above; restore in the same scope.
unsafe {
match label {
@ -95,6 +143,14 @@ mod tests {
Some(v) => env::set_var("HYPERHIVE_HIVE_DOMAIN", v),
None => env::remove_var("HYPERHIVE_HIVE_DOMAIN"),
}
match hive_name {
Some(v) => env::set_var("HYPERHIVE_HIVE_NAME", v),
None => env::remove_var("HYPERHIVE_HIVE_NAME"),
}
match swarm_name {
Some(v) => env::set_var("HYPERHIVE_SWARM_NAME", v),
None => env::remove_var("HYPERHIVE_SWARM_NAME"),
}
}
f();
unsafe {
@ -106,6 +162,14 @@ mod tests {
Some(v) => env::set_var("HYPERHIVE_HIVE_DOMAIN", v),
None => env::remove_var("HYPERHIVE_HIVE_DOMAIN"),
}
match prev_hive_name {
Some(v) => env::set_var("HYPERHIVE_HIVE_NAME", v),
None => env::remove_var("HYPERHIVE_HIVE_NAME"),
}
match prev_swarm_name {
Some(v) => env::set_var("HYPERHIVE_SWARM_NAME", v),
None => env::remove_var("HYPERHIVE_SWARM_NAME"),
}
}
}
@ -157,4 +221,58 @@ mod tests {
assert_eq!(label(), "");
});
}
#[test]
fn hive_name_returns_some_when_env_set() {
with_full_env(Some("iris"), None, Some("pr1ma"), None, || {
assert_eq!(hive_name().as_deref(), Some("pr1ma"));
});
}
#[test]
fn hive_name_returns_none_when_env_unset_or_empty() {
with_full_env(Some("iris"), None, None, None, || {
assert!(hive_name().is_none());
});
with_full_env(Some("iris"), None, Some(""), None, || {
assert!(hive_name().is_none(), "empty string treated as unset");
});
}
#[test]
fn swarm_name_returns_some_when_env_set() {
with_full_env(Some("iris"), None, None, Some("constellat1on"), || {
assert_eq!(swarm_name().as_deref(), Some("constellat1on"));
});
}
#[test]
fn swarm_name_returns_none_when_env_unset_or_empty() {
with_full_env(Some("iris"), None, None, None, || {
assert!(swarm_name().is_none());
});
with_full_env(Some("iris"), None, None, Some(""), || {
assert!(swarm_name().is_none(), "empty string treated as unset");
});
}
#[test]
fn name_accessors_independent_from_domain() {
// hive_name + swarm_name surface without HYPERHIVE_HIVE_DOMAIN
// being set — the names are display labels, not derived from
// the DNS domain (#701, mara on internal-requests#9).
with_full_env(
Some("iris"),
None,
Some("pr1ma"),
Some("constellat1on"),
|| {
assert!(hive_domain().is_none());
assert_eq!(hive_name().as_deref(), Some("pr1ma"));
assert_eq!(swarm_name().as_deref(), Some("constellat1on"));
// qualified_label still degrades to short label without domain.
assert_eq!(qualified_label(), "iris");
},
);
}
}