Skip to content

feat(terranix,system-manager): port to configurations evaluated per system - #96

Merged
clhodapp merged 4 commits into
mainfrom
feat/terranix-system-manager
Oct 18, 2026
Merged

clhodapp merged 4 commits into
mainfrom
feat/terranix-system-manager

Conversation

@clhodapp

@clhodapp clhodapp commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

terranix and system-manager were the last two integrations with the old shape, where mkConfiguration returned the evaluation directly, required configModule and took a pkgSets attribute set. They now work like the other integrations. This PR is based on #95, which is based on #94. Merge those first, then change the base of this PR to main. The repository does not delete merged branches, so GitHub does not change the base by itself.

The port

  • mkConfiguration returns a configuration. A parent declares it under caisson.<integration>.configurations.<name>, or mkTopConfiguration finalizes it at a top. It is evaluated per system, once for each system in force.
  • configModule is optional. When it is absent, the configuration registered under the declared name in configs/terranix or configs/systemManager is used.
  • Neither entry point takes pkgSets. The modules get the composition's package sets at the system as pkgSets and the composed library as lib. The inputs argument, which nothing read, is gone.

terranix:

  • It runs on the selected package set, so it takes defaultPkgs.
  • Its value and its json output are the config.tf.json file, with the evaluated configuration under passthru.config.
  • It is published as terranixConfigurations.<name>, the name terranix's flake-parts module uses.
  • terranix returns only the keys Terraform reads plus _meta, its documented out-of-band channel. A module of the integration copies config.caisson into _meta.caisson, and the integration reads it back from there. A terranix configuration therefore holds no configurations beneath it.

system-manager:

  • The package set and nixpkgs. A system-manager configuration runs on the package set caisson selects (defaultPkgs, else the set named default), as every other integration does. It also runs on the nixpkgs the composition declares, as a NixOS configuration does.
    • caisson calls system-manager's nix/lib.nix, the file its flake builds lib.makeSystemConfig from, with the composition's nixpkgs and the composed library. The NixOS integration calls nixos/lib/eval-config.nix the same way.
    • The selected set is passed as the pkgs special argument. The module system takes a special argument before an entry of _module.args, so the selected set is the pkgs of every module and the default of the read-only nixpkgs.pkgs.
    • userborn, the fork system-manager pins, is built from that source on the selected set.
  • What is refused. A configuration is refused if it sets nixpkgs.overlays or nixpkgs.config, or passes overlays in ecosystemArgs, because the selected set makes them ineffective. So is one whose nixpkgs.hostPlatform differs from the platform of that set.
  • Its value and its toplevel output are the system. It is published as systemConfigs.<name>, which system-manager switch --flake reads, under default when no name is given.
  • The compatibility bridge for nixpkgs' nix.nix is unchanged.

Both classes get caisson's core module (modules/terranix/core and modules/systemManager/core, symlinks to the generic core, as for the other classes).

The cost of two more classes

Each registered class declares caisson.modules.<class>.export.enabled and caisson.modules.<class>.exported in every evaluation, and caisson.exports.modules merged both for every class. The two new classes took caisson's overhead in the eval-weight check from 4.18 MB to 4.27 MB, against a ceiling of 4.28 MB. The first commit merges the options only for a class where a module defines them, and builds their types once. The overhead with this PR is 4.01 MB.

As part of that change, exported is now a function returning lazyAttrsOf raw. Before, each exported module was wrapped in a deferred module ({ imports = [ … ]; } with a location string). Now the modules are exported exactly as the function returns them, so a default selection and a defined one publish the same values.

Tests

  • The pinned-world tests for both integrations evaluate a top and a configuration published under a structural top. The terranix test checks the file name and the passthru configuration. The system-manager test checks the platform. Both check that the modules see the composed library.
  • ecosystemSrcValidationThrows and the twin test use the new shape.
  • The unit tests for ecosystem resolution read terranix through a top, over a library that declares one system. The test of the entry-point signatures covers both integrations.
  • The argument-errors check now finds no entry point that requires an argument, and checks that both refuse pkgSets.

Consumers

The only consumer is ch-system-manager, whose test flakes call the old shape. clhodapp/ch-system-manager#15 moves them to mkTopConfiguration and declares systems on mkLib. Merge it after this PR.

All 18 workspace consumers pass nix flake check --no-build with this branch in place of caisson, ch-system-manager on that branch. This time the run also points each consumer's tests/dependencies lock at this branch, so the checks partitions, which read that lock, evaluate against it too. My earlier consumer runs for #91 through #95 overrode only the root caisson input, so those partitions ran against the caisson they pin. This branch contains #94 and #95, so the run covers them as well.

🤖 Generated with Claude Code

@clhodapp clhodapp self-assigned this Oct 17, 2026
@clhodapp clhodapp changed the title feat(terranix,system-manager): port to configurations evaluated at a system feat(terranix,system-manager): port to configurations evaluated per system Oct 17, 2026
@clhodapp
clhodapp changed the base branch from feat/darwin-children to main October 17, 2026 20:04
@clhodapp
clhodapp added this pull request to stack #106 October 17, 2026 20:04
clhodapp and others added 2 commits October 17, 2026 22:39
…es them

Every evaluation declares `caisson.modules.<class>.export.enabled` and
`caisson.modules.<class>.exported` for every registered class, and
merged both for every class to compute `caisson.exports.modules`. A
class whose options no module defines now exports its default without
the merge, and the types of the options are built once.

`exported` is now a function to the modules as they are registered
(`lazyAttrsOf raw`), so a default and a defined selection publish the
same values; it no longer wraps each module in a deferred module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…system

A terranix or system-manager configuration is now a configuration like
the others: `mkConfiguration` returns a configuration a parent declares
under `caisson.<integration>.configurations.<name>` or
`mkTopConfiguration` finalizes, evaluated at every system in force. The
module is found by name in `configs/terranix` or `configs/systemManager`
when `configModule` is absent, and the entry points take no `pkgSets`.
The modules get the composed library as `lib` and the package sets of
the composition as `pkgSets`; the `inputs` argument is gone.

A terranix configuration runs on the selected package set
(`defaultPkgs`), its value and its `json` output are the
`config.tf.json` file, and it is published as
`terranixConfigurations.<name>`. caisson reads its own options back
through terranix's `_meta`, so a terranix configuration holds no
configurations beneath it.

A system-manager configuration keeps building its own package set,
with `nixpkgs.hostPlatform` defaulted to the system of the evaluation.
Its value and its `toplevel` output are the system, published as
`systemConfigs.<name>`, which `system-manager switch --flake` reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The port let `makeSystemConfig` build the package set from the nixpkgs
system-manager pins, so the integration took no `defaultPkgs` and
ignored the package configs and package overlays declared on `mkLib`.
A system-manager configuration now runs on the selected set, as every
other integration does, and on the nixpkgs the composition declares,
as a NixOS configuration does.

caisson calls system-manager's `nix/lib.nix`, the file its flake builds
`lib.makeSystemConfig` from, with the composition's nixpkgs and the
composed library, as the NixOS integration calls
`nixos/lib/eval-config.nix`. The selected set is passed as the `pkgs`
special argument. The module system takes a special argument before an
entry of `_module.args`, so it is the `pkgs` of every module and the
default of the read-only `nixpkgs.pkgs`, and the set system-manager
would import is never evaluated. `userborn`, the fork system-manager
pins, is built from that source on the selected set.

`mkConfiguration` takes `defaultPkgs`. A configuration that sets
`nixpkgs.overlays` or `nixpkgs.config`, or passes `overlays` in
`ecosystemArgs`, is refused, since the selected set makes them
ineffective, and so is one whose `nixpkgs.hostPlatform` differs from
the platform of that set. The pinned-world test checks that the system
runs on the declared set, through a marker only that set carries, and
that its userborn is built on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@clhodapp
clhodapp force-pushed the feat/terranix-system-manager branch from 4b3b230 to b8b9b1c Compare October 18, 2026 08:25
hjem is pronounced with a leading "y" sound, so it takes "a".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@clhodapp
clhodapp merged commit 825a72c into main Oct 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant