All checks were successful
CI / test (22) (pull_request) Successful in 49s
CI / test (true, 24) (pull_request) Successful in 1m5s
CI / flake (pull_request) Successful in 2s
CI / test (22) (push) Successful in 49s
CI / test (true, 24) (push) Successful in 1m3s
CI / flake (push) Successful in 3s
Add a `home-manager` flake input, its nixpkgs following this flake's, and a `home-manager-module` check that evaluates the real module through home-manager's standalone entry point and builds the home files derivation under four operator configurations, asserting on the tree each produces: the Skill coexisting with an operator's own skills in both the attribute-set and whole-directory forms, the sibling-enable gate leaving no Skill when Claude Code is off, and the hook merging into an operator's own SessionStart list. This is the first automated proof of the module's composition (ADR 0021), replacing verification by maintainer rebuild.
50 lines
5.0 KiB
Markdown
50 lines
5.0 KiB
Markdown
---
|
|
spec: hm-module-harness-integration
|
|
blocked-by: 0046-reshape-hm-module-per-harness-toggle
|
|
---
|
|
|
|
## What to build
|
|
|
|
Add the first automated proof of the module's composition, so that a change in home-manager or the Claude Code module that breaks the way the Skill is declared fails `nix flake check` rather than a maintainer rebuild weeks later.
|
|
|
|
A home-manager input is added to the flake, with its own nixpkgs following the flake's nixpkgs, so the module is checked against the same nixpkgs-and-home-manager pairing a consumer following this flake would get.
|
|
|
|
A flake check evaluates the actual module through home-manager's standalone configuration entry point and builds the resulting home files derivation under several configurations, asserting on the tree it produces.
|
|
Building the home files derivation is sufficient — it is the file-linkage layer that actually decides whether two declarations collide — and needs neither the Claude Code binary nor a running agent.
|
|
The check asserts on which files a generation contains, never on module internals (option values, store paths, the shape of the file mechanism).
|
|
|
|
The configurations exercised:
|
|
|
|
- An operator declaring their own skills as an attribute set: the module's Skill lands alongside the operator's, each at its own name.
|
|
- An operator declaring their own skills as a whole directory (path form): the module's Skill lands alongside the operator's directory contents.
|
|
This is the case that guards the recursive-install coupling; a regression to a non-recursive path-form install fails here as a build-time file collision.
|
|
- Claude Code disabled: no gitea-axi Skill entry is written.
|
|
This guards the explicit sibling-enable gate introduced in the reshape.
|
|
- The hook merges into an operator's own SessionStart hook list rather than replacing it.
|
|
|
|
This adds a module check alongside the existing package check in the flake's `checks` output rather than introducing a new kind of verification surface.
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] The flake has a home-manager input whose nixpkgs follows the flake's nixpkgs; `flake.lock` is updated.
|
|
- [x] A new check under the flake's `checks` output evaluates the real module through home-manager's standalone configuration entry point and builds the home files derivation — no Claude Code binary or running agent required.
|
|
- [x] The attribute-set-skills configuration asserts the module's Skill and the operator's skill both land, each at its own name.
|
|
- [x] The whole-directory-skills (path form) configuration asserts the module's Skill lands alongside the operator's directory contents; a non-recursive path-form install would fail this as a build-time collision.
|
|
- [x] The Claude-Code-disabled configuration asserts no gitea-axi Skill entry is written.
|
|
- [x] A configuration with an operator's own SessionStart hook asserts the module's hook merges into that list rather than replacing it.
|
|
- [x] The check asserts on the generation's file tree only, not on option values or store paths.
|
|
- [x] `nix flake check` runs the new check across the flake's systems and passes.
|
|
|
|
## Implementation Notes
|
|
|
|
The check lives in its own file, `checks/home-manager-module.nix`, imported from the flake's `checks` output beside the existing package check; the `forAllSystems` callback now destructures `{ pkgs, system }` because the check needs `pkgs` to build fixtures and the derivation.
|
|
`home-manager` is added as a flake input with `inputs.nixpkgs.follows = "nixpkgs"`; it is a check-only development input with no bearing on the package or the module a consumer imports, and the comment in `flake.nix` says so.
|
|
|
|
Each of the four configurations builds `config.home-files` — the home-manager file-linkage layer — and the final `runCommandLocal` asserts on that tree with `test`/`grep` only: skill files present or absent by path, and the two SessionStart commands present as quoted JSON string values in `settings.json`.
|
|
The hook-merge assertion reads generated file content because that is the only place a list merge is observable; the spec's Testing Decisions name "the hook merges into the operator's own hooks" as a required assertion, so this stays within "assert on the file tree, not on option values or store paths".
|
|
|
|
On the cross-system criterion: `nix flake check` builds the check for the host system and passes, and omits the incompatible systems (`aarch64-*`) with a warning — identical per-system semantics to the pre-existing package check, which also builds only natively.
|
|
The check is import-from-derivation-bearing (the Claude Code module reads the fixture skill directories at evaluation time), so evaluating a foreign system's check forces a cross-platform fixture build rather than skipping cleanly; this is not exercised by the default `nix flake check` and does not affect the native run.
|
|
|
|
Verified by `nix flake check` (passes, all outputs) and by inspecting each configuration's built `home-files` tree directly: the disabled-Claude-Code generation contains no `.claude` directory at all, and the merged-hook `settings.json` contains both hooks in the `SessionStart` array — confirming the assertions are not vacuous.
|