feat(nix): agentPackages attrset + extraPackages + agent tools baseline (ADR-0033) #76
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!76
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/74"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #74
Replaces the module's single claude-shaped
claudePackageknob with a provider-keyed agent-CLI seam, and gives every session a fixed tools baseline. Design per the issue's agent brief (grilling session 2026-07-09), pinned in ADR-0033.What changed
services.lab.agentPackages—attrsOf (nullOr package), keyed by lab provider ID (claude-code,codex— the registry/DB/API strings). Every non-null value'sbin/lands on the unit PATH. Defaults are injected per-key atmkOptionDefaultpriority, soagentPackages."claude-code" = nulldrops the (unfree) Claude CLI while the codex default survives. The option description documents the unfree default and thenullopt-out.services.lab.extraPackages—listOf package, default[], appended to the unit PATH; purely additive.gawk gnutar gzip xz zstd unzip curl jq file patch procps ripgrep+ the host'sconfig.nix.package(sessions cannix developa project flake for per-project toolchains). No language toolchains by design.claudePackageis now a deprecated alias: non-null populatesagentPackages."claude-code"and warns; set together with an explicitagentPackages."claude-code"it fails eval with an assertion naming both options (explicitness read from per-attribute definition priorities vialib.modules.mergeAttrDefinitionsWithPrio, values never forced).nixos-moduleflake check grows variants for all of the above. The new variants are eval-only — they assert on package names offconfig.systemd.services.lab.path, so the check's build closure never pulls in claude-code/codex; the text-level dummy keeps every existing unit-invariant grep and proves PATH serialization withpkgs.hellostand-ins.allowUnfreePredicateone-liner.Verification
nix build .#checks.x86_64-linux.nixos-modulegreen (all six eval asserts + unit-text greps);checks.aarch64-linux.nixos-moduledrvPath evals clean.nix derivation show -rover the check: zeroclaude-code/codexreferences in the build closure.claudePackageset alone the conflict-variant assert correctly fails the check, proving the assertion keys on the double definition, not on the alias alone.d407951) verified to shipclaude-code-2.1.201,codex-0.142.3, andlib.modules.mergeAttrDefinitionsWithPrio.Out of scope (per issue)
Codex
AgentProviderimplementation (#2, staysready-for-human); downstream fleet-config migration; one-time deploy check that the nix daemon socket is reachable from a live session.🤖 Generated with Claude Code
https://claude.ai/code/session_014BKNq5c2thNWaDoMGCepWL
The NixOS module's agent-CLI surface generalizes from the single claude-shaped claudePackage knob to services.lab.agentPackages, an attrsOf (nullOr package) keyed by lab provider ID — the same strings the provider registry, the DB provider column, and the API use (claude-code, codex). The defaults ({ "claude-code" = pkgs.claude-code; codex = pkgs.codex; }) are injected per-key at mkOptionDefault priority, so agentPackages."claude-code" = null drops the (unfree) Claude CLI while the codex default survives — per-key merge, never whole-attrset replacement. services.lab.extraPackages (listOf package, default []) appends host-specific tools, purely additive. The unit PATH — inherited by every spawned session through the tmux server — gains a fixed tools baseline, deliberately not an option: gawk, gnutar, gzip, xz, zstd, unzip, curl, jq, file, patch, procps, ripgrep, and the host's config.nix.package, so sessions can run a project flake's devshell (nix develop / nix shell) for per-project toolchains. Previously ps/rg reached sessions only as an accident of the nixpkgs claude-code wrapper prefixing its own PATH — a second provider's sessions would have lacked them. Deliberately no language toolchains: those come from each project's flake. claudePackage stays as a deprecated alias: non-null populates agentPackages."claude-code" and warns; combined with an explicit agentPackages."claude-code" definition it fails eval with an assertion naming both options. Explicitness is read off the module system's per-attribute definition priorities (mergeAttrDefinitionsWithPrio), values never forced — the unfree default cannot leak into a host that nulled it. The nixos-module flake check grows eval-only variants: defaults land both CLIs plus the baseline on the unit PATH; the per-key null opt-out; extraPackages additivity; the alias warning; the both-set assertion message naming both options; the option-description unfree note. The variants assert on package names off config.systemd.services.lab.path — never realizing unit text carrying the real agent CLIs — so the check's build closure stays free of claude-code/codex (verified: zero matches in the recursive derivation closure). The text-level dummy keeps every existing unit-invariant grep and proves PATH serialization end-to-end with cheap stand-ins. Out of scope, unchanged: the Codex AgentProvider implementation (#2), the downstream fleet-config migration, and the one-time deploy check that the nix daemon is reachable from a live session (ADR-0033). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014BKNq5c2thNWaDoMGCepWL