test(state): #133 — pin the #107 legacy-name shim (and its fail-closed gate)
All checks were successful
Check / eval (push) Successful in 4m9s

mkFlake prefers state.json and still accepts the legacy theme-state.json,
because a machine only migrates on its next menu write. Nothing evaluated a
checkout carrying ONLY the legacy name, so the shim could rot with every
check green — breaking rebuilds for precisely the users who have not opened
the menu since #107, i.e. the ones least likely to see it coming.

checks.state-legacy-name evaluates two fixtures: legacy-only must resolve,
and no-state-at-all must still throw. The gate is half the shim's contract
(it turns "no state file" into one readable error instead of a readFile
stack from inside the module system) and was equally unpinned.

Cheap by construction: mkFlake wraps its return in `builtins.seq
_themeState`, so forcing the attrset forces the state read and nothing else
— no module system, no home.nix. Hence a one-file fixture and no measurable
cost (flake check still ~35s). That file is a SYMLINK to the shipped
template's state.json: a copy would drift and the check would quietly start
testing a fossil. The fixture also asserts it has no state.json, or it would
pass while testing nothing.

Both assertions proven by breaking lib.nix — deleting the fallback, then
defeating the gate — and watching each fail by name. Delete this check
together with the shim and the nomarchy-theme-sync alias.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 14:04:16 +01:00
parent 3cc644edd9
commit a6f86cf575
5 changed files with 67 additions and 12 deletions

View File

@@ -1511,6 +1511,51 @@
in
assert ok; pkgs.runCommand "nomarchy-rofi-text-width" { } "touch $out";
# The #107 rename shipped a compat shim: mkFlake prefers state.json
# and still accepts the legacy theme-state.json (lib.nix), because a
# machine only migrates on its next menu write. Nothing evaluated a
# checkout carrying ONLY the legacy name, so the shim could rot with
# every check green — and it is load-bearing for exactly the users who
# have not touched the menu since #107, i.e. the ones least likely to
# notice before their rebuild breaks.
#
# Cheap by construction: mkFlake wraps its return in
# `builtins.seq _themeState`, so forcing the attrset forces the state
# read and nothing else — no module system, no home.nix. Which is also
# why the fixture needs only that one file.
#
# Delete this check together with the shim and the
# `nomarchy-theme-sync` alias.
state-legacy-name =
let
inherit (nixpkgs) lib;
expect = cond: msg: lib.assertMsg cond "state-legacy-name: ${msg}";
legacy = ./checks/fixtures/legacy-state;
none = ./checks/fixtures/no-state;
evalsOk = src:
(builtins.tryEval
(builtins.isAttrs (self.lib.mkFlake { inherit src; username = "fixture"; })))
.success;
ok =
# Fixture drift: if someone adds a state.json here the check
# would still pass, while testing nothing at all.
expect (builtins.pathExists (legacy + "/theme-state.json"))
"fixture drift: checks/fixtures/legacy-state has no theme-state.json"
&& expect (! builtins.pathExists (legacy + "/state.json"))
("fixture drift: checks/fixtures/legacy-state also has a state.json, "
+ "so it no longer exercises the legacy-only path")
&& expect (evalsOk legacy)
("mkFlake cannot evaluate a checkout carrying only theme-state.json "
+ "the #107 compat shim is broken and every pre-#107 machine "
+ "fails to rebuild")
# The other half of the shim's contract: no state at all must
# still fail, and fail here rather than deep in a module.
&& expect (! evalsOk none)
("mkFlake accepted a checkout with no state file at all the "
+ "fail-closed gate in lib.nix is gone");
in
assert ok; pkgs.runCommand "nomarchy-state-legacy-name" { } "touch $out";
# Docking artifact guard: default/no-profile Home Manager output
# must still launch both hotplug watchers, put Rofi on the focused
# monitor, expose the manual keyboard picker, and never ship the