hyperhive/nix/module-eval/agent-plugins.nix
müde dc418a5223 nix: split module-eval into per-subsystem checks
The single module-eval derivation forced ~62 full nixosSystem
fixtures live at once to compute its cases list: 10.6GB peak RSS /
5m25s to evaluate, by far the dominant cost in nix flake check.
Splits it into 21 independent checks.module-eval-* derivations
(1-7 fixtures each) sharing builders/helpers via module-eval/lib.nix,
so no single derivation needs more than a handful of fixtures live
at once. A few cases spanning two clusters carry a small duplicated
fixture rather than threading shared state through lib.nix.
2026-09-20 04:25:54 +02:00

89 lines
3.2 KiB
Nix

# `checks.module-eval-agent-plugins` — see ./lib.nix for the shared
# rationale (why this suite exists, naming convention, "evaluates
# not executes").
{
pkgs,
lib,
self,
nixosSystem,
}:
let
inherit
(import ./lib.nix {
inherit
pkgs
lib
self
nixosSystem
;
})
agent
runGroup
;
# `claudePlugins`'s additive-merge shape: a plain per-agent
# definition must ADD to the module's own base-set definition rather than
# replacing it, while `lib.mkForce` must still replace the whole list
# outright — the two arms below plus the unset default (read directly off
# `bare`-shaped `agent { }`, no fixture of its own needed) are the three
# cases that shape has to hold.
agentPluginsDefault = agent { };
agentPluginsAdded = agent { claudePlugins = [ "foo@bar" ]; };
agentPluginsForced = agent { claudePlugins = lib.mkForce [ "foo@bar" ]; };
# The de-dup arm: an agent that names a base-set entry explicitly must not
# get it installed twice.
agentPluginsDuplicate = agent { claudePlugins = [ "base@hyperhive" ]; };
agentPlugins = machine: machine.services.hyperhive.agent.claudePlugins;
cases = [
{
# Case 1 of the additive-merge shape: an agent that declares
# nothing gets exactly the module's base set, no more and no less.
name = "an agent with no claudePlugins definition gets exactly the base set";
ok =
agentPlugins agentPluginsDefault == [
"skill-creator@claude-plugins-official"
"base@hyperhive"
];
}
{
# Case 2: a plain per-agent definition ADDS to the base set (list-typed
# options at equal priority concatenate) rather than replacing it —
# the property the operator ruling asked for instead of `mkDefault`.
# Sorted before comparing: concatenation order between two same-
# priority definitions is a module-system implementation detail this
# case isn't about — membership and count are.
name = "an agent's own claudePlugins definition adds to the base set";
ok =
builtins.sort builtins.lessThan (agentPlugins agentPluginsAdded) == [
"base@hyperhive"
"foo@bar"
"skill-creator@claude-plugins-official"
];
}
{
# Case 3: `lib.mkForce` is still the escape hatch — an operator who
# wants the base set gone, not extended, can still say so outright.
name = "an agent's mkForce claudePlugins replaces the base set outright";
ok = agentPlugins agentPluginsForced == [ "foo@bar" ];
}
{
# De-dup arm: an agent that names a base-set entry itself must not get
# it installed twice — `lib.unique` at the JSON-render site, not the
# option's merge (the merge is a plain concatenation on purpose, so
# the un-deduped list stays readable for `agentPlugins` above).
name = "an agent repeating a base-set plugin does not get it installed twice";
ok =
builtins.sort builtins.lessThan (
builtins.fromJSON agentPluginsDuplicate.environment.etc."hyperhive/claude-plugins.json".text
) == [
"base@hyperhive"
"skill-creator@claude-plugins-official"
];
}
];
in
runGroup "agent-plugins" cases