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:
parent
b08176f089
commit
2ad4b43118
4 changed files with 32 additions and 35 deletions
|
|
@ -65,14 +65,14 @@ pub struct Coordinator {
|
||||||
/// Store path of the `claude-code` build every agent runs, written
|
/// Store path of the `claude-code` build every agent runs, written
|
||||||
/// into each per-agent flake as `hyperhive.claudeCodePath`. Set by
|
/// into each per-agent flake as `hyperhive.claudeCodePath`. Set by
|
||||||
/// the NixOS module option `services.hyperhive.c0re.claudeCodePackage`
|
/// the NixOS module option `services.hyperhive.c0re.claudeCodePackage`
|
||||||
/// (which resolves the package and hands us its path). Empty string =
|
/// (which resolves the package and hands us its path). `None` = every
|
||||||
/// every agent keeps the `claude-code` from its own nixpkgs.
|
/// agent keeps the `claude-code` from its own nixpkgs.
|
||||||
///
|
///
|
||||||
/// A path rather than a flake input because containers share the
|
/// A path rather than a flake input because containers share the
|
||||||
/// host's `/nix/store`: the binary is already reachable inside them,
|
/// host's `/nix/store`: the binary is already reachable inside them,
|
||||||
/// closure and all. The host module is what keeps it from being
|
/// closure and all. The host module is what keeps it from being
|
||||||
/// garbage-collected — see that option.
|
/// 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
|
/// 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
|
/// each per-agent flake so the agent's web UI can build the right
|
||||||
/// rebuild-button URL pointing back at the dashboard.
|
/// 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.
|
/// so a doc edit only re-locks this input, not the whole source.
|
||||||
pub hyperhive_docs_flake: String,
|
pub hyperhive_docs_flake: String,
|
||||||
pub nixpkgs_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
|
/// agent keeps the one out of its own nixpkgs". Travels into the
|
||||||
/// container as `hyperhive.claudeCodePath` — a plain string, kept
|
/// container as `hyperhive.claudeCodePath` — a plain string, kept
|
||||||
/// alive host-side by the module that resolved it.
|
/// 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 dashboard_port: u16,
|
||||||
pub operator_pronouns: String,
|
pub operator_pronouns: String,
|
||||||
pub context_window_tokens: std::collections::HashMap<String, u64>,
|
pub context_window_tokens: std::collections::HashMap<String, u64>,
|
||||||
|
|
@ -251,7 +251,7 @@ impl Default for HiveEnv {
|
||||||
hyperhive_flake: "/etc/hyperhive".to_string(),
|
hyperhive_flake: "/etc/hyperhive".to_string(),
|
||||||
hyperhive_docs_flake: String::new(),
|
hyperhive_docs_flake: String::new(),
|
||||||
nixpkgs_flake: String::new(),
|
nixpkgs_flake: String::new(),
|
||||||
claude_code_path: String::new(),
|
claude_code_path: None,
|
||||||
dashboard_port: 7000,
|
dashboard_port: 7000,
|
||||||
operator_pronouns: "she/her".to_string(),
|
operator_pronouns: "she/her".to_string(),
|
||||||
context_window_tokens: std::collections::HashMap::from([
|
context_window_tokens: std::collections::HashMap::from([
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ pub async fn sync_agents(hive: &HiveEnv, agents: &[AgentSpec]) -> Result<()> {
|
||||||
&hive.hyperhive_flake,
|
&hive.hyperhive_flake,
|
||||||
&hive.hyperhive_docs_flake,
|
&hive.hyperhive_docs_flake,
|
||||||
&hive.nixpkgs_flake,
|
&hive.nixpkgs_flake,
|
||||||
&hive.claude_code_path,
|
hive.claude_code_path.as_deref(),
|
||||||
hive.dashboard_port,
|
hive.dashboard_port,
|
||||||
&hive.operator_pronouns,
|
&hive.operator_pronouns,
|
||||||
&hive.context_window_tokens,
|
&hive.context_window_tokens,
|
||||||
|
|
@ -651,7 +651,7 @@ fn render_flake(
|
||||||
hyperhive_flake: &str,
|
hyperhive_flake: &str,
|
||||||
docs_flake: &str,
|
docs_flake: &str,
|
||||||
nixpkgs_flake: &str,
|
nixpkgs_flake: &str,
|
||||||
claude_code_path: &str,
|
claude_code_path: Option<&str>,
|
||||||
dashboard_port: u16,
|
dashboard_port: u16,
|
||||||
operator_pronouns: &str,
|
operator_pronouns: &str,
|
||||||
context_window_tokens: &std::collections::HashMap<String, u64>,
|
context_window_tokens: &std::collections::HashMap<String, u64>,
|
||||||
|
|
@ -940,7 +940,7 @@ fn render_flake_with_lookup<F>(
|
||||||
hyperhive_flake: &str,
|
hyperhive_flake: &str,
|
||||||
docs_flake: &str,
|
docs_flake: &str,
|
||||||
nixpkgs_flake: &str,
|
nixpkgs_flake: &str,
|
||||||
claude_code_path: &str,
|
claude_code_path: Option<&str>,
|
||||||
dashboard_port: u16,
|
dashboard_port: u16,
|
||||||
operator_pronouns: &str,
|
operator_pronouns: &str,
|
||||||
context_window_tokens: &std::collections::HashMap<String, u64>,
|
context_window_tokens: &std::collections::HashMap<String, u64>,
|
||||||
|
|
@ -1063,12 +1063,9 @@ where
|
||||||
// which pure eval rejects. The agent module puts its `bin/` on the
|
// which pure eval rejects. The agent module puts its `bin/` on the
|
||||||
// harness PATH; the host module holds the gc root, since a path
|
// harness PATH; the host module holds the gc root, since a path
|
||||||
// spelled out here is text and references nothing.
|
// spelled out here is text and references nothing.
|
||||||
// Empty = no override; agents keep their own nixpkgs' `claude-code`.
|
// `None` = no override; agents keep their own nixpkgs' `claude-code`.
|
||||||
if !claude_code_path.is_empty() {
|
if let Some(path) = claude_code_path {
|
||||||
let _ = writeln!(
|
let _ = writeln!(out, " hyperhive.claudeCodePath = \"{path}\";");
|
||||||
out,
|
|
||||||
" hyperhive.claudeCodePath = \"{claude_code_path}\";"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// CA trust: embed every hive-trusted CA so each agent validates them at
|
// CA trust: embed every hive-trusted CA so each agent validates them at
|
||||||
// build time. The list is the hive's own self-signed CA (when active)
|
// build time. The list is the hive's own self-signed CA (when active)
|
||||||
|
|
@ -1580,7 +1577,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1625,7 +1622,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"",
|
"",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1649,7 +1646,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"/nix/store/cccc-claude-code-2.1.220",
|
Some("/nix/store/cccc-claude-code-2.1.220"),
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1668,7 +1665,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn render_flake_omits_claude_path_when_unset() {
|
fn render_flake_omits_claude_path_when_unset() {
|
||||||
// Empty = no host-level pin: the option is left undefined so the
|
// `None` = no host-level pin: the option is left undefined so the
|
||||||
// agent module keeps its own nixpkgs' `claude-code` (and keeps it
|
// agent module keeps its own nixpkgs' `claude-code` (and keeps it
|
||||||
// in `environment.systemPackages`, which is what makes the
|
// in `environment.systemPackages`, which is what makes the
|
||||||
// unpinned case self-contained).
|
// unpinned case self-contained).
|
||||||
|
|
@ -1676,7 +1673,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1697,7 +1694,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"",
|
"",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1728,7 +1725,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1762,7 +1759,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1798,7 +1795,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1837,7 +1834,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1880,7 +1877,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -1964,7 +1961,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -2050,7 +2047,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
@ -2093,7 +2090,7 @@ mod tests {
|
||||||
"github:example/hyperhive",
|
"github:example/hyperhive",
|
||||||
"path:/nix/store/bbbb-hyperhive-docs-source",
|
"path:/nix/store/bbbb-hyperhive-docs-source",
|
||||||
"path:/nix/store/aaaa-nixpkgs-source",
|
"path:/nix/store/aaaa-nixpkgs-source",
|
||||||
"",
|
None,
|
||||||
8000,
|
8000,
|
||||||
"she/her",
|
"she/her",
|
||||||
&std::collections::HashMap::new(),
|
&std::collections::HashMap::new(),
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,11 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
options.hyperhive.claudeCodePath = lib.mkOption {
|
options.hyperhive.claudeCodePath = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
default = "";
|
default = null;
|
||||||
example = "/nix/store/…-claude-code-2.1.220";
|
example = "/nix/store/…-claude-code-2.1.220";
|
||||||
description = ''
|
description = ''
|
||||||
Store path of the `claude-code` this agent runs, or `""` (the
|
Store path of the `claude-code` this agent runs, or `null` (the
|
||||||
default) to use the `claude-code` from the container's own
|
default) to use the `claude-code` from the container's own
|
||||||
nixpkgs.
|
nixpkgs.
|
||||||
|
|
||||||
|
|
@ -212,7 +212,7 @@
|
||||||
]
|
]
|
||||||
++ [
|
++ [
|
||||||
(
|
(
|
||||||
if config.hyperhive.claudeCodePath == "" then
|
if config.hyperhive.claudeCodePath == null then
|
||||||
pkgs.claude-code
|
pkgs.claude-code
|
||||||
else
|
else
|
||||||
# Host-pinned claude: a symlink farm around a path the
|
# Host-pinned claude: a symlink farm around a path the
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ let
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Store path of the `claude-code` every agent runs, or "" for "each
|
# Store path of the `claude-code` every agent runs, or null for "each
|
||||||
# agent keeps the one out of its own nixpkgs". meta.rs writes it into
|
# agent keeps the one out of its own nixpkgs". meta.rs writes it into
|
||||||
# each agent's generated flake as a plain string literal, and the agent
|
# each agent's generated flake as a plain string literal, and the agent
|
||||||
# module puts its `bin/` on the harness PATH.
|
# module puts its `bin/` on the harness PATH.
|
||||||
|
|
@ -73,7 +73,7 @@ let
|
||||||
# do not hand meta.rs the path by a route that drops it. The failure
|
# do not hand meta.rs the path by a route that drops it. The failure
|
||||||
# mode is a garbage-collected `claude` and a hive that can't take a
|
# mode is a garbage-collected `claude` and a hive that can't take a
|
||||||
# turn, weeks after the commit that caused it.
|
# turn, weeks after the commit that caused it.
|
||||||
claudeCodePath = if cfg.claudeCodePackage == null then "" else "${cfg.claudeCodePackage}";
|
claudeCodePath = if cfg.claudeCodePackage == null then null else "${cfg.claudeCodePackage}";
|
||||||
|
|
||||||
# The `hive-c0re serve` config JSON. Keys are snake_case to match the
|
# The `hive-c0re serve` config JSON. Keys are snake_case to match the
|
||||||
# `ServeConfig` serde shape the daemon deserialises (the
|
# `ServeConfig` serde shape the daemon deserialises (the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue