refactor(#2693): null, not "", for the unpinned claude-code

mara on PR #2769: "make the default null instead of special casing """.

`claude_code_path` was a `String` whose empty value meant "no host-level
pin". That is a sentinel doing an `Option`'s job — the same shape argus
and mara already rejected on #2755's weights, and the same
empty-field cruft mara called out on #2756.

So it is `Option<String>` end to end:

- host module: `claudeCodePath` evaluates to `null` when
  `claudeCodePackage` is unset, so `serve.json` carries JSON `null`
  rather than `""`.
- `Coordinator` + `HiveEnv`: `Option<String>`, defaulting to `None`.
- `render_flake`/`render_flake_with_lookup`: `Option<&str>`, and the
  emission is an `if let Some(path)` instead of an `is_empty()` guard.
- agent module: `hyperhive.claudeCodePath` is `nullOr str`, default
  `null`.

Behaviour is unchanged in both directions; only the way "unset" is
spelled moves. The `builtins.hasContext` assertion still guards the
pinned case (short-circuited by the null check, so an unpinned hive
never evaluates it).

16/16 `meta::` tests, clippy clean, `nix fmt` no-op, `nix build .#docs`
green.
This commit is contained in:
atlas 2026-07-27 13:21:11 +02:00 committed by mara
commit 2ad4b43118
4 changed files with 32 additions and 35 deletions

View file

@ -65,14 +65,14 @@ pub struct Coordinator {
/// Store path of the `claude-code` build every agent runs, written
/// into each per-agent flake as `hyperhive.claudeCodePath`. Set by
/// the NixOS module option `services.hyperhive.c0re.claudeCodePackage`
/// (which resolves the package and hands us its path). Empty string =
/// every agent keeps the `claude-code` from its own nixpkgs.
/// (which resolves the package and hands us its path). `None` = every
/// agent keeps the `claude-code` from its own nixpkgs.
///
/// A path rather than a flake input because containers share the
/// host's `/nix/store`: the binary is already reachable inside them,
/// closure and all. The host module is what keeps it from being
/// garbage-collected — see that option.
pub claude_code_path: String,
pub claude_code_path: Option<String>,
/// TCP port the host's hive-c0re dashboard listens on. Inlined into
/// each per-agent flake so the agent's web UI can build the right
/// rebuild-button URL pointing back at the dashboard.
@ -224,11 +224,11 @@ pub struct HiveEnv {
/// so a doc edit only re-locks this input, not the whole source.
pub hyperhive_docs_flake: String,
pub nixpkgs_flake: String,
/// Store path of the `claude-code` agents run, or empty for "each
/// Store path of the `claude-code` agents run, or `None` for "each
/// agent keeps the one out of its own nixpkgs". Travels into the
/// container as `hyperhive.claudeCodePath` — a plain string, kept
/// alive host-side by the module that resolved it.
pub claude_code_path: String,
pub claude_code_path: Option<String>,
pub dashboard_port: u16,
pub operator_pronouns: String,
pub context_window_tokens: std::collections::HashMap<String, u64>,
@ -251,7 +251,7 @@ impl Default for HiveEnv {
hyperhive_flake: "/etc/hyperhive".to_string(),
hyperhive_docs_flake: String::new(),
nixpkgs_flake: String::new(),
claude_code_path: String::new(),
claude_code_path: None,
dashboard_port: 7000,
operator_pronouns: "she/her".to_string(),
context_window_tokens: std::collections::HashMap::from([