nix module: agentPackages attrset + extraPackages + agent tools baseline (replaces claudePackage) #74

Closed
opened 2026-07-09 21:14:56 +02:00 by dominik.polakovics · 2 comments

This was generated by AI during triage.

Context

The NixOS module puts agent CLIs and tools on the lab unit PATH (which every spawned session inherits via the tmux server). Today that surface is claude-shaped and bare:

  • The only agent-CLI knob is claudePackage (default null) — one provider, no way to add the Codex CLI (#2) without the raw systemd.services.lab.path escape hatch.
  • The session PATH is missing tools agents assume constantly: no awk, tar, curl, jq, file, unzip, patch — and ps/rg are present only as an accident of the nixpkgs claude-code wrapper prefixing its own PATH, so a second provider's sessions would not get them.
  • No nix on PATH, so agents cannot use a project flake's devshell for per-project toolchains and instead grovel /nix/store.

Both agent CLIs exist in nixpkgs (claude-code, unfree; codex, the OpenAI codex-cli). Design was resolved in a grilling session on 2026-07-09; the agent brief below is the contract.

Relation to #2

This issue only puts the codex binary on the sandbox PATH and generalizes the module seam. The Codex AgentProvider implementation stays #2 and remains ready-for-human.

> *This was generated by AI during triage.* ## Context The NixOS module puts agent CLIs and tools on the lab unit PATH (which every spawned session inherits via the tmux server). Today that surface is claude-shaped and bare: - The only agent-CLI knob is `claudePackage` (default `null`) — one provider, no way to add the Codex CLI (#2) without the raw `systemd.services.lab.path` escape hatch. - The session PATH is missing tools agents assume constantly: no `awk`, `tar`, `curl`, `jq`, `file`, `unzip`, `patch` — and `ps`/`rg` are present only as an accident of the nixpkgs claude-code wrapper prefixing its own PATH, so a second provider's sessions would not get them. - No `nix` on PATH, so agents cannot use a project flake's devshell for per-project toolchains and instead grovel `/nix/store`. Both agent CLIs exist in nixpkgs (`claude-code`, unfree; `codex`, the OpenAI codex-cli). Design was resolved in a grilling session on 2026-07-09; the agent brief below is the contract. ## Relation to #2 This issue only puts the `codex` binary on the sandbox PATH and generalizes the module seam. The Codex `AgentProvider` implementation stays #2 and remains `ready-for-human`.
Author
Owner

This was generated by AI during triage.

Agent Brief

Category: enhancement
Summary: Replace the claudePackage option with a provider-keyed agentPackages attrset (claude-code + codex defaults), add an additive extraPackages list, and give the unit PATH a fixed baseline of standard agent tools including nix.

Current behavior:
The services.lab NixOS module assembles the systemd unit PATH from the lab package, git, tmux, openssh, util-linux, plus an optional single claudePackage (default null). Sessions inherit this PATH. There is no way to declare a second agent CLI, no general tools baseline (awk, tar, curl, jq, file, unzip, patch are all absent), ps and rg reach sessions only via the nixpkgs claude-code wrapper (so non-claude sessions would lack them), and nix is not on PATH.

Desired behavior:

  • services.lab.agentPackagesattrsOf (nullOr package). Keys are lab provider IDs (the same strings the provider registry, DB provider column, and API use): default { "claude-code" = pkgs.claude-code; codex = pkgs.codex; }. Every non-null value's bin/ lands on the unit PATH. Setting one key to null disables that CLI without touching the other defaults (attrset per-key merge). The option description must note that claude-code is unfree in nixpkgs and that consumers who forbid unfree software can set agentPackages."claude-code" = null.
  • services.lab.extraPackageslistOf package, default [], appended to the unit PATH. Purely additive: setting it must never affect agentPackages or the baseline.
  • Fixed tools baseline joins the unit PATH unconditionally (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 use nix develop / nix shell for per-project toolchains). Deliberately no language toolchains — those come from each project's flake.
  • claudePackage becomes a deprecated alias: when set (non-null) it populates agentPackages."claude-code" and emits a deprecation warning. An assertion fails eval with a clear message when both claudePackage and an explicit agentPackages."claude-code" definition are set.

Key interfaces:

  • services.lab.agentPackages / services.lab.extraPackages option declarations as above
  • The systemd unit path assembly in the module — merges baseline + non-null agentPackages values + extraPackages
  • The flake's NixOS VM test, which currently stubs claudePackage with a stand-in package — extend it to cover the new options

Acceptance criteria:

  • With defaults, both claude and codex resolve on the unit PATH
  • agentPackages."claude-code" = null removes claude from PATH while the codex default survives (per-key merge, not replacement)
  • extraPackages = [ pkg ] appends without dropping any baseline tool or agent CLI
  • Baseline tools resolve from a session shell in the VM test: awk, tar, xz, zstd, unzip, curl, jq, file, patch, ps, rg, nix
  • Setting claudePackage alone still works, warns, and lands the package on PATH as agentPackages."claude-code"
  • Setting both claudePackage and agentPackages."claude-code" fails eval with an assertion naming both options
  • Option descriptions document the unfree claude-code default and the null opt-out
  • nix flake check passes with the updated VM test

Out of scope:

  • The Codex AgentProvider implementation (#2, ready-for-human)
  • The downstream fleet config migration (Cloonar/nixos rev bump + swapping claudePackage for the new options) and the rebuild/deploy
  • Live verification that the nix daemon socket is reachable from inside a real session (nix develop in a spawned session) — one-time manual check at deploy time
  • Language toolchains (node, go, python3, compilers) in the baseline — intentionally excluded in the design session; per-project flakes are the mechanism
> *This was generated by AI during triage.* ## Agent Brief **Category:** enhancement **Summary:** Replace the `claudePackage` option with a provider-keyed `agentPackages` attrset (claude-code + codex defaults), add an additive `extraPackages` list, and give the unit PATH a fixed baseline of standard agent tools including `nix`. **Current behavior:** The `services.lab` NixOS module assembles the systemd unit PATH from the lab package, git, tmux, openssh, util-linux, plus an optional single `claudePackage` (default `null`). Sessions inherit this PATH. There is no way to declare a second agent CLI, no general tools baseline (`awk`, `tar`, `curl`, `jq`, `file`, `unzip`, `patch` are all absent), `ps` and `rg` reach sessions only via the nixpkgs claude-code wrapper (so non-claude sessions would lack them), and `nix` is not on PATH. **Desired behavior:** - `services.lab.agentPackages` — `attrsOf (nullOr package)`. **Keys are lab provider IDs** (the same strings the provider registry, DB `provider` column, and API use): default `{ "claude-code" = pkgs.claude-code; codex = pkgs.codex; }`. Every non-null value's `bin/` lands on the unit PATH. Setting one key to `null` disables that CLI without touching the other defaults (attrset per-key merge). The option description must note that `claude-code` is **unfree** in nixpkgs and that consumers who forbid unfree software can set `agentPackages."claude-code" = null`. - `services.lab.extraPackages` — `listOf package`, default `[]`, appended to the unit PATH. Purely additive: setting it must never affect `agentPackages` or the baseline. - **Fixed tools baseline** joins the unit PATH unconditionally (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 use `nix develop` / `nix shell` for per-project toolchains). Deliberately no language toolchains — those come from each project's flake. - `claudePackage` becomes a **deprecated alias**: when set (non-null) it populates `agentPackages."claude-code"` and emits a deprecation warning. An **assertion** fails eval with a clear message when both `claudePackage` and an explicit `agentPackages."claude-code"` definition are set. **Key interfaces:** - `services.lab.agentPackages` / `services.lab.extraPackages` option declarations as above - The systemd unit `path` assembly in the module — merges baseline + non-null `agentPackages` values + `extraPackages` - The flake's NixOS VM test, which currently stubs `claudePackage` with a stand-in package — extend it to cover the new options **Acceptance criteria:** - [ ] With defaults, both `claude` and `codex` resolve on the unit PATH - [ ] `agentPackages."claude-code" = null` removes claude from PATH while the codex default survives (per-key merge, not replacement) - [ ] `extraPackages = [ pkg ]` appends without dropping any baseline tool or agent CLI - [ ] Baseline tools resolve from a session shell in the VM test: `awk`, `tar`, `xz`, `zstd`, `unzip`, `curl`, `jq`, `file`, `patch`, `ps`, `rg`, `nix` - [ ] Setting `claudePackage` alone still works, warns, and lands the package on PATH as `agentPackages."claude-code"` - [ ] Setting both `claudePackage` and `agentPackages."claude-code"` fails eval with an assertion naming both options - [ ] Option descriptions document the unfree claude-code default and the `null` opt-out - [ ] `nix flake check` passes with the updated VM test **Out of scope:** - The Codex `AgentProvider` implementation (#2, `ready-for-human`) - The downstream fleet config migration (Cloonar/nixos rev bump + swapping `claudePackage` for the new options) and the rebuild/deploy - Live verification that the nix daemon socket is reachable from inside a real session (`nix develop` in a spawned session) — one-time manual check at deploy time - Language toolchains (`node`, `go`, `python3`, compilers) in the baseline — intentionally excluded in the design session; per-project flakes are the mechanism
Author
Owner

This was generated by AI while landing a PR.

Landed PR #76feat(nix): agentPackages attrset + extraPackages + agent tools baseline (ADR-0033). Verdict PASS, merged into main.

Checked:

  • Conventional title ✓; AFK contract ✓ (afk/74, valid Closes #74); cleanly mergeable (based on current main, no conflicts).
  • Diff faithful to the agent brief: agentPackages per-key mkOptionDefault merge (null opt-out works), conflict assertion keys on real definition priority via mergeAttrDefinitionsWithPrio without forcing unfree values, claudePackage deprecated-alias // override + warning, fixed baseline + config.nix.package + additive extraPackages.
  • Checks well-designed: eval-only dummies assert on package names (no outPath forcing); text dummy overrides claude-code with hello via the alias and nulls codex, keeping both real CLIs out of the realized closure, then greps serialized unit PATH.
  • Docs (ADR-0033, ops.md) accurate and consistent.

Signal relied on: both CI gates green — ci / native (required) and the authoritative hermetic ci-nix / flake-check (Successful in 14m7s), which runs the nixos-module check this PR extends. Not re-run locally.

Noted, non-blocking: the runtime VM test (baseline tools resolving from a live session shell) is deliberately deferred to M8 and replaced by eval + unit-text assertions — documented in the PR body and ADR-0033.

> *This was generated by AI while landing a PR.* **Landed PR #76** — `feat(nix): agentPackages attrset + extraPackages + agent tools baseline (ADR-0033)`. Verdict **PASS**, merged into `main`. **Checked:** - Conventional title ✓; AFK contract ✓ (`afk/74`, valid `Closes #74`); cleanly mergeable (based on current `main`, no conflicts). - Diff faithful to the agent brief: `agentPackages` per-key `mkOptionDefault` merge (null opt-out works), conflict assertion keys on real definition priority via `mergeAttrDefinitionsWithPrio` without forcing unfree values, `claudePackage` deprecated-alias `//` override + warning, fixed baseline + `config.nix.package` + additive `extraPackages`. - Checks well-designed: eval-only dummies assert on package *names* (no outPath forcing); text dummy overrides claude-code with `hello` via the alias and nulls codex, keeping both real CLIs out of the realized closure, then greps serialized unit PATH. - Docs (ADR-0033, ops.md) accurate and consistent. **Signal relied on:** both CI gates green — `ci / native` (required) and the authoritative hermetic `ci-nix / flake-check` (Successful in 14m7s), which runs the `nixos-module` check this PR extends. Not re-run locally. **Noted, non-blocking:** the runtime VM test (baseline tools resolving from a live session shell) is deliberately deferred to M8 and replaced by eval + unit-text assertions — documented in the PR body and ADR-0033.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab#74
No description provided.