docs(options): README tables reconciled + permanent option-docs check (item 6a)
All checks were successful
Check / eval (push) Successful in 2m59s

Slice (a) of the docs review: 9 undocumented nomarchy.* options got
README rows or inline .leaf mentions (autoTimezone, camera IR-hide,
intel.guc, amd.pstate/vaapi, package, system.stateFile, restic.paths).
The diff is now a flake check: option names are eval-walked from the
four option-declaring modules (config halves stay lazy) and compared
to the tables by tools/check-option-docs.py — undocumented options and
stale table rows both fail. Negative-tested against the pre-fix README
(reports exactly the 9 gaps). Item 6 split into slices a–d in BACKLOG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-07-04 19:31:20 +01:00
parent 9cd6f5e30c
commit 56f1cc3fa9
6 changed files with 145 additions and 16 deletions

View File

@@ -218,31 +218,35 @@ two tables below are split along exactly that line.
| `nomarchy.displays.enable` | `true` | nwg-displays interactive monitor arranger (helper for `nomarchy.monitors`) |
| `nomarchy.monitors` | `[]` | Declarative per-output layout → Hyprland `monitor` rules (applied on hotplug); `,preferred,auto,1` wildcard kept as fallback |
| `nomarchy.themesDir` | Nomarchy's `themes/` | Where per-theme app overrides are probed |
| `nomarchy.package` | overlay's `nomarchy-theme-sync` | The theme/state tool package, overridable if you fork it |
**`system.nix`** (NixOS — the machine):
| Option | Default | Purpose |
|---|---|---|
| `nomarchy.system.plymouth.enable` | `true` | Branded boot splash, background from the theme JSON (recolors on system rebuilds) |
| `nomarchy.system.stateFile` | `null` | theme-state.json for the system-side consumers (the Plymouth tint); `lib.mkFlake` wires it for you — set it only when composing the modules by hand |
| `nomarchy.system.fileManager.enable` | `true` | Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler) |
| `nomarchy.system.greeter.enable` | `true` | greetd/tuigreet |
| `nomarchy.system.greeter.autoLogin` | `null` | Auto-login this user into Hyprland (installer sets it on LUKS machines) |
| `nomarchy.system.audio.enable` | `true` | Pipewire stack |
| `nomarchy.system.bluetooth.enable` | `true` | Bluetooth + blueman |
| `nomarchy.system.autoTimezone.enable` | `false` | Automatic timezone (geoclue + automatic-timezoned) — the clock follows your location; toggle from System Auto timezone (a menu enable rebuilds: it has to unset the static `time.timeZone`) |
| `nomarchy.system.snapper.enable` | `false` | Hourly/daily BTRFS timeline snapshots + `nixos-rebuild-snap` (installer enables it; no-op unless root is BTRFS) |
| `nomarchy.system.power.enable` | `true` | Active power management (see below) |
| `nomarchy.system.power.backend` | `"ppd"` | `"ppd"` (power-profiles-daemon + menu/Waybar switcher) or `"tlp"` (deeper battery tuning, no switcher) — mutually exclusive |
| `nomarchy.system.power.laptop` | `false` | Marks a laptop, gating battery-only features; the installer sets it when a battery is present |
| `nomarchy.system.power.thermal.enable` | `false` | thermald (Intel-only); the installer enables it on a GenuineIntel CPU |
| `nomarchy.system.power.batteryChargeLimit` | `null` | Stop charging at this % (e.g. `80`) where the hardware supports it; needs `power.laptop` |
| `nomarchy.hardware.intel.enable` | `false` | Intel enablement above nixos-hardware (GuC/HuC via `i915.enable_guc=3`); the installer sets it on an Intel CPU/GPU |
| `nomarchy.hardware.intel.enable` | `false` | Intel enablement above nixos-hardware (GuC/HuC firmware via `i915.enable_guc=3``.guc` toggles just that; the installer unsets it on `xe`-driver GPUs); the installer sets it on an Intel CPU/GPU |
| `nomarchy.hardware.intel.computeRuntime` | `false` | Opt-in: Intel GPU compute — OpenCL/Level-Zero (`intel-compute-runtime`) + oneVPL (`vpl-gpu-rt`) |
| `nomarchy.hardware.amd.enable` | `false` | AMD enablement above nixos-hardware (amd-pstate EPP + radeonsi VA-API); installer-set on an AMD CPU/GPU |
| `nomarchy.hardware.amd.enable` | `false` | AMD enablement above nixos-hardware (amd-pstate EPP + radeonsi VA-API, each toggleable via `.pstate` / `.vaapi`); installer-set on an AMD CPU/GPU |
| `nomarchy.hardware.amd.rocm.enable` | `false` | Opt-in: ROCm HIP/OpenCL GPU compute (multi-GB); pair with `.gfxOverride` (e.g. `"11.0.0"`) for an unlisted iGPU |
| `nomarchy.hardware.fingerprint.enable` | `false` | fprintd for a detected fingerprint reader (installer-set); enroll with `fprintd-enroll` |
| `nomarchy.hardware.fingerprint.pam` | `false` | Opt-in: use the fingerprint for login + sudo (PAM) |
| `nomarchy.hardware.npu.enable` | `false` | Opt-in/experimental: load the on-die NPU driver (`amdxdna`/`intel_vpu`); userspace runtime is BYO |
| `nomarchy.hardware.latestKernel` | `false` | Opt-in: ship `linuxPackages_latest` instead of the default kernel — for very new hardware whose drivers landed recently |
| `nomarchy.hardware.camera.hideIrSensor` | `false` | Hide a dual-sensor webcam's IR node from PipeWire so apps only ever see the colour camera (the "second, dark Integrated Camera"); installer-set on a paired RGB+IR webcam. `/dev/video*` stays open, so Howdy-style face unlock still works; `.irMatch` overrides the IR-name regex |
| `nomarchy.services.tailscale.enable` | `false` | Opt-in: Tailscale mesh VPN (then `sudo tailscale up`) |
| `nomarchy.services.syncthing.enable` | `false` | Opt-in: Syncthing file sync as the login user (GUI at `127.0.0.1:8384`) |
| `nomarchy.services.podman.enable` | `false` | Opt-in: rootless Podman (`docker` aliased to it) |
@@ -259,7 +263,7 @@ two tables below are split along exactly that line.
| `nomarchy.services.ollama.enable` | `false` | Opt-in: Ollama local LLM runtime on `127.0.0.1:11434` (CPU; GPU via `services.ollama.acceleration`) |
| `nomarchy.services.printing.enable` | `false` | Opt-in: CUPS + Avahi/mDNS network printer discovery |
| `nomarchy.services.openrgb.enable` | `false` | Opt-in: OpenRGB daemon for RGB peripheral/motherboard lighting |
| `nomarchy.services.restic.enable` | `false` | Opt-in: scheduled daily restic backup (set `.repository` + `.passwordFile`; 7/4/6 retention; list/restore via the `restic-nomarchy` wrapper) |
| `nomarchy.services.restic.enable` | `false` | Opt-in: scheduled daily restic backup (set `.repository` + `.passwordFile`; backs up `.paths`, default `/home`; 7/4/6 retention; list/restore via the `restic-nomarchy` wrapper) |
Beyond the `nomarchy.*` surface, the system layer turns on the usual
desktop services with `lib.mkDefault` (override natively). Two worth

View File

@@ -22,19 +22,21 @@ next, in what order*.
## NOW
*Items 2127: real-hardware QA findings from the Latitude 5410
(Bernardo, 2026-07-04) — bugs found on metal outrank the queue.*
### 6. Full docs review & restructure
ROADMAP § Full docs review. The roadmap/backlog split is done (this
file); remaining: reconcile every README option table against the live
`nomarchy.*` surface; drift pass over docs/OVERRIDES.md, docs/TESTING.md,
templates/downstream/README.md; read the install/first-run story end to
end; add a short **recovery runbook** (desktop won't start → boot an
older generation / `nomarchy-snapshots` / greetd journal — the pieces
exist, the story isn't written). Site-vs-markdown is a Decision (below).
**Verify:** V0 + a mechanical option-surface diff (eval the options,
compare to the tables — consider making that a permanent check).
### 6. Full docs review & restructure — sliced, one slice per iteration
ROADMAP § Full docs review. Done: the roadmap/backlog split (this file);
slice (a) option-surface reconcile — README tables fixed (9 gaps) and
the diff made permanent as the `option-docs` flake check (eval-walked
surface vs tables, catches stale rows too). Remaining slices:
- **(b) drift pass** over docs/OVERRIDES.md, docs/TESTING.md,
templates/downstream/README.md — read each against the current tree,
fix what lies.
- **(c) install/first-run story** read end to end (README install §§ +
template README as one narrative; fix the seams).
- **(d) recovery runbook** — short doc: desktop won't start → boot an
older generation / `nomarchy-snapshots` / greetd journal. The pieces
exist, the story isn't written.
Site-vs-markdown is a Decision (below). **Verify:** V0 per slice; (b)/(c)
against the live tree, not memory.
## NEXT

View File

@@ -17,6 +17,23 @@ Template:
---
## 2026-07-04 — docs slice (a): option-surface reconcile (iteration #13, item 6a)
- **Task:** BACKLOG NOW#6, first slice — README tables vs live options.
- **Did:** split item 6 into slices (ad) in BACKLOG. Reconciled: 9
undocumented options (autoTimezone.enable!, camera.hideIrSensor +
irMatch!, intel.guc, amd.pstate/vaapi, package, system.stateFile,
restic.paths) — rows/inline mentions added, no stale rows found.
Made it permanent: `checks.option-docs` — flake.nix eval-walks option
names from the 4 option files (config halves stay lazy; dummy args)
→ tools/check-option-docs.py enforces row-or-`.leaf`-mention + flags
stale rows.
- **Verified:** V0; V1 — check builds green (78 options); negative test:
same script vs the pre-fix README reports exactly the 9 gaps.
- **Pending:** slices (b) doc drift pass, (c) install story, (d)
recovery runbook.
- **Next suggestion:** item 6 slice (b) — or NEXT#8 (viewers+mime) for
variety; both fine.
## 2026-07-04 — Back audit (iteration #12, item 24)
- **Task:** BACKLOG NOW#24 — every list menu ends in ↩ Back.
- **Did:** audited every `rofi -dmenu` call site in the generated

View File

@@ -849,6 +849,16 @@ how to override it. Items marked ✓ are shipped.
your theme / essentials" flow (ties into the branding work).
## Known issues & follow-ups
- ✓ **README option tables drift from the live surface** (docs review
slice (a), 2026-07-04): a fresh reconcile found 9 undocumented options
(autoTimezone, camera.hideIrSensor/irMatch, intel.guc, amd.pstate/
vaapi, package, system.stateFile, restic.paths) — rows/mentions added,
and the diff is now permanent: `checks.option-docs` walks the option
names out of the four option-declaring modules at eval time (their
lazy `config` halves never evaluate, so dummy args suffice) and
requires each a table row — or a backticked `.leaf` mention for
non-enable sub-knobs — plus flags table rows whose option no longer
exists (tools/check-option-docs.py).
- ✓ **swaync text invisible on summer-day** (Latitude QA, 2026-07-04):
the themed CSS paired @subtext on @base, but palette roles are not
uniform across themes — summer-day/flexoki-light use subtext as

View File

@@ -147,6 +147,42 @@
touch $out
'';
# The README option tables must track the live `nomarchy.*`
# surface. The surface is walked at eval time from the four
# option-declaring modules (their lazy `config` halves stay
# unevaluated), so the live side of the diff can't go stale;
# the script wants a table row per option (inline `.leaf`
# mentions ok for non-enable sub-knobs) and flags stale rows.
option-docs =
let
lib = nixpkgs.lib;
names = prefix: opts: lib.concatLists (lib.mapAttrsToList
(n: v:
if lib.isOption v
then lib.optional (!(v.readOnly or false)) "${prefix}${n}"
else names "${prefix}${n}." v)
opts);
surface = lib.concatLists [
(names "nomarchy."
((import ./modules/home/options.nix { inherit lib pkgs; }).options.nomarchy))
(names "nomarchy.system."
((import ./modules/nixos/options.nix { inherit lib; }).options.nomarchy.system))
(names "nomarchy.hardware."
((import ./modules/nixos/hardware.nix { inherit lib pkgs; config = { }; }).options.nomarchy.hardware))
(names "nomarchy.services."
((import ./modules/nixos/services.nix { inherit lib pkgs; config = { }; }).options.nomarchy.services))
];
in
pkgs.runCommand "nomarchy-option-docs"
{ nativeBuildInputs = [ pkgs.python3 ]; }
''
python3 ${./tools/check-option-docs.py} \
${pkgs.writeText "option-surface.txt"
(lib.concatStringsSep "\n" surface + "\n")} \
${./README.md}
touch $out
'';
# Config-level VM assertions for the nomarchy.hardware.* toggles —
# what they SET (kernel cmdline, fprintd, PAM). Real hardware
# behaviour (firmware/driver/device) needs bare metal and is out of

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Option-surface ↔ README drift check (flake check `option-docs`).
Usage: check-option-docs.py <option-surface.txt> <README.md>
<option-surface.txt> is one full option path per line (nomarchy.…),
generated at eval time in flake.nix by walking the four option-declaring
modules — so the "live surface" side can never go stale by itself.
Two directions:
1. Every live option must be documented in the README: either its full
path appears verbatim (a table row), or — for sub-knobs that don't end
in `.enable` — a backticked `.leaf` shorthand appears somewhere (the
convention for tuning knobs documented inline in their parent row,
e.g. nightlight's `.temperature`). `.enable` options always get their
own row: the shorthand `` `.enable` `` would match any of them.
2. Every `nomarchy.*` path in a README option-table row (lines starting
`| `nomarchy.`) must still exist — catches rows for renamed/removed
options.
"""
import re
import sys
surface_file, readme_file = sys.argv[1], sys.argv[2]
surface = [l.strip() for l in open(surface_file) if l.strip()]
readme = open(readme_file).read()
# Backticked `.leaf` shorthands present in the README (inline sub-knob docs).
shorthands = set(re.findall(r"`(\.[A-Za-z][A-Za-z0-9]*)`", readme))
failures = []
for opt in surface:
if opt in readme:
continue
leaf = "." + opt.rsplit(".", 1)[1]
if leaf != ".enable" and leaf in shorthands:
continue
failures.append(
f"undocumented option: {opt} — add a README table row"
+ ("" if leaf == ".enable" else f" or an inline `{leaf}` mention")
)
# Reverse: option paths claimed by table rows must exist.
for line in readme.splitlines():
m = re.match(r"\|\s*`(nomarchy\.[A-Za-z0-9.]+)`", line)
if m and m.group(1) not in surface:
failures.append(f"stale README table row: {m.group(1)} is not a live option")
if failures:
print(f"option-docs: {len(failures)} problem(s)")
for f in failures:
print(" " + f)
sys.exit(1)
print(f"option-docs: {len(surface)} options all documented, no stale rows")