docs(agent): backlog codebase exam + per-theme design audit
All checks were successful
Check / eval (push) Successful in 3m16s

Record PROPOSED findings from a full tree exam (installer, modules,
docs drift, CI) and a 24-theme design pass (contrast green; btop
fidelity, identity semantics, whole-swap gaps). No code changes —
await human triage into NOW/NEXT.
This commit is contained in:
Bernardo Magri
2026-07-09 08:46:13 +01:00
parent 5273493c20
commit d09c978b9b

View File

@@ -101,7 +101,7 @@ actually float once seen (regex tolerance for the `.…-wrapped` form).
*Agents: append here with a one-paragraph pitch (what/why/cost). Do not
implement. Bernardo moves accepted items into a tier.*
### Pre-existing
- **Battery charge-limit — make the toggle instant** `[blocked:hw]`
(follow-up to the preset toggle shipped 2026-07-07, iteration #55).
@@ -134,11 +134,388 @@ implement. Bernardo moves accepted items into a tier.*
pairs with a hardware-queue session. Recommend (a) now, (b)
investigated when the T14s is next available.
### Bugs / broken behavior (codebase exam 2026-07-09)
- **Installer: swap "none" still creates a 0G swap subvolume**
`nomarchy-install.sh` always passes `--argstr swapSize "${SWAP_GB}G"`.
When the user chooses 0 (no swap), disko gets `"0G"`, but
`disko-config.nix` only treats the exact string `"0"` as no-swap —
so it still creates `@swap` with size `0G`. Resume wiring correctly
skips (`SWAP_GB != "0"`), so layout and config disagree. Fix: pass
bare `"0"` (or teach disko to accept `"0G"`). Cheap pure check /
unit test for the swapSize contract would lock this. Cost: small
installer + disko-config change; high install-path impact.
- **Waybar doctor indicator never shows an icon**
`modules/home/waybar.nix` `nomarchy-doctor-status` prints
`"text":""` on failure. Waybar custom modules with empty `text`
self-hide, so the tripwire never becomes visible (tooltip-only if
it rendered at all). Intent in comments is opposite: appear only
when the sheet has a ✖. Fix: non-empty glyph (e.g. `󰒡` / `!`) +
`class: bad` when unhealthy; exit 0 / no output when healthy.
Cost: one-line script fix; V2 with existing `checks.doctor` if
extended, else V1.
- **neon-glass theme ships a broken Waybar whole-swap**
`themes/neon-glass/waybar.css` uses `@text` / `@base` / `@accent` /
etc. with **no** `@define-color` block. Whole-swap CSS replaces the
entire stylesheet with no palette prepend (`waybar.nix`), so
applying neon-glass yields undefined / broken bar colors (same
class as the purged stub CSS, JOURNAL #45). Also missing
`waybar.jsonc`, `preview.png`, `btop.theme`; CSS covers only a
subset of modules. Fix: either finish (self-contained colors +
layout parity + preview) **or** remove `waybar.css` until ready so
apply falls back to the generated bar. Cost: medium polish or
small quarantine delete; V2/V3 visual.
- **Control Center appearance toggles are no-ops without a rebuild**
`nomarchy-control-center.sh` `set_state` always uses `--no-switch`.
Blur / gaps (and similar appearance keys) only take effect after a
home switch, but the UI never runs one and never warns (unlike
Updates/Bluetooth which print "requires rebuild"). User flips look
identical until a later rebuild. Fix: run switch for appearance
keys, or refuse / warn clearly. Cost: small UX fix in one script.
- **Bluetooth menu row not self-gated**
System submenu always offers Bluetooth and `exec blueman-manager`.
If `nomarchy.system.bluetooth.enable = false`, blueman is not
installed → silent/broken launch. Printers / colorpicker / audio
gate correctly. Fix: gate on `command -v blueman-manager` (or the
settings flag). Cost: tiny; V0 + manual.
- **summer-day workspace scroll dispatches differently from every other bar**
`themes/summer-day/waybar.jsonc` uses `workspace e+1` / `e-1` (next
**empty** workspace); summer-night, executive-slate, boreal use
`r+1` / `r-1` (relative). Likely leftover; behavior differs under
scroll. Fix: align to `r±1`. Cost: two lines.
- **Battery charge-limit / powerprofile only match `BAT*` sysfs names**
`power.nix` oneshot and menu/waybar globs use
`/sys/class/power_supply/BAT*`. `nomarchy-battery-notify` correctly
treats any system battery (`type=Battery`, not `scope=Device`).
Machines with `CMB0` (and similar) get notify but not charge-cap /
inconsistent gating. Fix: name-agnostic scan aligned with
battery-notify. Cost: small multi-file; `[blocked:hw]` to confirm
on non-BAT* hardware.
- **Unattended install silently disables LUKS if passphrase unset**
Interactive path defaults encrypt-on; unattended without
`NOMARCHY_LUKS_PASSPHRASE` installs **unencrypted** with only a log
line — no fail-closed. `test-install.sh` always sets a passphrase,
so the regression path is untested. Options: require the env var
when unattended, or an explicit `NOMARCHY_NO_LUKS=1`. Cost: small
policy change in install script + test matrix note.
### Docs / option-surface drift (codebase exam 2026-07-09)
- **`option-docs` would fail: undocumented `nomarchy.hardware.i2c.*`**
Live options `nomarchy.hardware.i2c.enable` and `.ddcci` in
`modules/nixos/hardware.nix`; distro default turns ddcci **on**
(`modules/nixos/default.nix` `mkDefault true`). No README table
rows, no template comments. Simulated `checks.option-docs` finds
exactly these two. Also: option default is false while module
default is true — document the effective default. Cost: docs +
optional template opt-in comments; would green option-docs.
- **README / docs factual drift pack** (single docs pass or split):
(1) theme count still says **21** — repo has **24** presets
(`README.md`, `docs/TESTING.md`, `templates/downstream/README.md`);
(2) waybar whole-swap list still names catppuccin/lumon/nord/
retro-82 — stubs purged; live CSS whole-swaps are summer-day,
summer-night, executive-slate, boreal, neon-glass; (3) idle table
still says "suspend **30**" — code is **15 min battery-only**
(`idle.nix`; ROADMAP already correct); (4) Tailscale row says
`sudo tailscale up` but module grants `--operator=` / template
says no sudo; (5) `hosts/live.nix` header still says "No installer
yet"; (6) CONVENTIONS parity list omits **boreal** (has full
waybar.jsonc). Cost: pure docs; high trust payoff.
- **Keybind cheatsheet: Network still labeled nmtui**
`keybinds.nix` `desc = "Network (nmtui)"` but the module runs
`networkmanager_dmenu`. Cheatsheet inherits the wrong label. Cost:
one string.
- **`nomarchy-menu` usage string incomplete**
Usage omits implemented modules (`doctor`, `controlcenter`,
`rollback`, …). Misleading for direct invocation / scripting.
Cost: one string in `rofi.nix`.
- **Template / seed state drift**
(1) `templates/downstream/theme-state.json` lacks `border` and
`settings` keys present in repo root state (eval merges defaults —
not a break, but new installs ship a thinner shape); (2) no
commented `nomarchy.system.snapper.enable` or `hardware.i2c.*` in
`system.nix` despite opt-in convention; (3) installer-generated
`home.nix` is minimal (keyboard only) while `flake init` template
ships a fat default package suite — product divergence worth
documenting or aligning. Cost: small template/docs.
- **Secondary docs polish**
README layout tree omits many real modules and pkgs
(doctor/control-center/battery-notify); `docs/MIGRATION.md`
unlinked from README nav and has a placeholder flake-init URL;
OVERRIDES.md example still pipes screenshots to **swappy** while
shipped annotator is **satty**. Cost: docs-only.
### Theme polish / completeness (codebase exam 2026-07-09)
- **Missing theme picker previews + hand btop for identity themes**
`boreal`, `executive-slate`, and `neon-glass` lack `preview.png`
(picker falls back to plain-name rows) and `btop.theme` (generated
btop only). CSS/jsonc for boreal/executive-slate are otherwise
solid. Capture previews after neon-glass is fixed/quarantined.
Cost: asset capture + optional hand btop; V3 visual.
- **Generated Waybar missing identity-bar affordances**
Whole-swap bars ship `custom/powermenu` and a left logo → main menu;
the generated `waybar.nix` bar has neither. Optional parity add for
default-theme users. Cost: small waybar.nix + CSS; parity rule
says whole-swaps must get the same modules if added.
- **summer-* CSS under-styles status module states**
summer-day/night include modules in selectors but largely lack
`.on` / `.available` / `.recording` polish that boreal,
executive-slate, and the generated style have. Functional OK;
visual hierarchy weak. Cost: CSS pass; V3.
- **Optional CI: whole-swap CSS self-containment**
Contrast check does not require that a `waybar.css` defines every
`@color` it references, nor that every preset has preview +
backgrounds. A cheap gate would have caught neon-glass. Cost:
small tool + flake check.
### Per-theme design audit (2026-07-09)
*Method: `checks.theme-contrast` (all 24×7 pairings pass) +
`tools/audit-theme-design.py` + per-theme agent review of JSON roles,
ANSI, assets, whole-swaps, and fidelity to canonical sources. Gated
WCAG floors are green; items below are fidelity, hierarchy, btop
drift, identity semantics, and polish. Identity themes (white,
vantablack, lumon, hackerman, matte-black, miasma, neon-glass) are
flagged separately from fidelity bugs.*
#### Broken / high-ROI fixes
- **rose-pine btop is Main-on-Dawn (near-invisible hi_fg)**
JSON is **Rosé Pine Dawn** (`mode: light`, base `#faf4ed`, text
`#575279`) but `themes/rose-pine/btop.theme` mixes Dawn
`main_bg`/`main_fg` with **Main** accents: `hi_fg=#e0def4` on
`#faf4ed` ≈ invisible, `selected_bg=#524f67`, love `#eb6f92`,
foam/iris Main hexes. **Fix:** rewrite btop entirely to Dawn roles
(foam `#56949f`, iris `#907aa9`, love `#b4637a`, gold
`#ea9d34`/`#d68a16`, pine `#286983`). Also rename display name to
"Rosé Pine Dawn" (or ship a dark Main preset as a separate slug).
Cost: one btop rewrite + optional rename; V1 + visual V2.
- **everforest + summer-night btop: inactive_fg == main_bg**
`themes/everforest/btop.theme` and `themes/summer-night/btop.theme`
set `inactive_fg` to `#2d353b` (= `main_bg`) → inactive/disabled
labels invisible. **Fix:** `inactive_fg` → grey1/muted range
(`#859289` / `#7a8478` / JSON muted). Cost: two lines; V1.
- **vantablack role inversion breaks selection / muted**
`overlay` and `muted` are near-white `#fdfdfd` while base is
`#0d0d0d`. Generated consumers use `overlay` as selected_bg;
hand btop has `selected_bg=#fdfdfd` + `selected_fg=#ffffff`
(white-on-white) and `inactive_fg=#fdfdfd` (inactive rows scream).
Status greys are intentional monochrome identity. **Fix (keep void
identity):** `surface #1a1a1a`, `overlay #2a2a2a`, `muted #666666`;
btop `selected_bg #333` / `selected_fg #fff` / `inactive_fg`
muted. Cost: JSON + btop; re-run contrast; V1V2.
- **osaka-jade: warn≈good and text/subtext inverted**
`warn #459451` is green (same family as `good #549e6a`) → battery
warning reads as "ok". `subtext #F6F5DD` is brighter than `text
#C1C497` (secondary louder than primary). **Fix:** swap text↔subtext;
set `warn #E5C736` (from bright yellow ANSI, already in palette).
Keep jade accent + blossom accentAlt. Cost: small JSON retune;
contrast re-check; V1.
- **catppuccin-latte: ANSI black/white axis inverted + mantle sludge**
Latte UI roles mostly match (base/text/accent/good/bad exact), but
`ansi[0]=#bcc0cc` (light) and `ansi[7]/[15]` are darkish → terminal
"black"/"white" slots misbehave; design-audit flags inverted slots.
`mantle #cbcdd0` is import-darken of light base (should be Latte
mantle `#e6e9ef`); `accentAlt #d260b5` is not Latte pink/mauve.
**Fix:**
```
mantle #e6e9ef; accentAlt #ea76cb (pink);
ansi[0] #5c5f77; ansi[7] #acb0be; ansi[8] #6c6f85; ansi[15] #bcc0cc
```
warn keep `#d6860a` if contrast requires, else `#df8e1d`. Cost:
JSON only; btop already Latte-correct. V0 contrast + V1.
- **catppuccin (Mocha) btop is Frappé-on-Mocha**
JSON is exact Mocha; `btop.theme` uses Mocha `main_bg` but Frappé
fgs/boxes (`main_fg #c6d0f5`, `hi_fg #8caaee`, mauve/green/maroon
Frappé hexes). **Fix:** replace with official Mocha btop hexes
(`#cdd6f4`, `#89b4fa`, …). Optional: rename to "Catppuccin Mocha".
Cost: btop rewrite; V1.
#### Whole-swap / identity themes
- **neon-glass** — already queued above (broken waybar.css). JSON
palette itself is fine (high-chroma cyber). Finish or quarantine.
- **summer-day / summer-night pair polish**
Structurally paired (inverted Everforest bar language) but:
(1) waybar CSS hexes drift from JSON (day warn/accentAlt; night
red/blue/yellow/purple vs JSON); bar follows legacy EF source,
Stylix/swaync follow JSON — dual sources of truth;
(2) night JSON collapses `subtext`=`muted`=`overlay` `#868d80`
(no secondary ladder for generated surfaces);
(3) day font 16px vs night 13px; day battery thresholds 30/15 vs
night 25/10; day missing `#custom-vpn` in pill selectors;
(4) day scroll `e±1` already queued. **Fix:** pick JSON as source
of truth and map waybar tokens to it (or document intentional EF
split); split night muted darker / subtext lighter; align font +
battery thresholds + VPN pill. Cost: CSS/JSON polish pass; V3.
- **boreal / executive-slate** — whole-swaps self-define colors and
match JSON; best-in-class. Only gaps: `preview.png` + optional
`btop.theme` (already queued).
- **white (monochrome light)** — identity-ok. Status good/warn/bad
are greys by design (audit hue/CVD noise expected). Optional:
widen L steps (`good #555`, `warn #777`, `bad #222`), raise
`surface #f0f0f0`, split `subtext #404040` while staying hueless.
Do not inject traffic-light hues. Cost: optional taste retune.
- **lumon (Severance blue mono-status)** — identity-ok. good/warn/bad
all blue family by design; CVD collapse expected — rely on
glyph/shape (item 28). Optional: dim `subtext` one step
(`#9bb0c0`); increase L separation only among blues. Cost:
optional; document as identity exemption in audit tooling.
- **hackerman** — matrix identity: warn cyan, bad green, ANSI reds
are greens. CVD collapse expected. **Keep identity** or optional
cyber-semantic UI only: `warn #e8d44f`, `bad #ff5a7a` while leaving
ANSI matrix. Raise `surface #1a1c2e` (flat base=surface). Cost:
product call + small JSON.
- **matte-black** — identity: `good=#FFC107` amber, `warn=#b91c1c`
red (inverted traffic-light). Matches Omarchy matte-black. Minimal
fix: `subtext #9a9a9d` (text==subtext today). Full semantic remap
only if product wants conventional battery colors on this theme.
Cost: one-line hierarchy or deliberate status retune.
- **miasma** — earthy `bad #685742` (brown, contrast ~2.3 audit-only)
is JOURNAL-exempt identity. Optional more readable brown-red
`#8b5a4a` / `#a65d4e` without leaving the forest. Cost: optional.
#### Fidelity / hierarchy polish (popular ports)
- **gruvbox is Material medium, not classic — name + btop split**
JSON hexes are Gruvbox **Material** (`text #d4be98`, material
green/yellow/red) while name says "Gruvbox" and `btop.theme` is
**classic** (`#ebdbb2`, `#d79921`, …). `surface`=`overlay`,
`text`=`subtext`. **Fix:** rename to "Gruvbox Material" **or**
retune JSON to classic; sync btop to the chosen lineage;
`overlay #504945`, `subtext #a89984`. Cost: naming + assets.
- **nord** — clean Polar Night. Only nit: `subtext #e5e9f0` is
**brighter** than `text #d8dee9` (hierarchy inverted). **Fix:**
subtext → nord4-dim or bump text to nord6 `#eceff4`. Cost: one hex.
- **tokyo-night** — Night (not Storm), JSON solid. btop
`main_fg`/`title` use warm `#cfc9c2` instead of Night
`#c0caf5`/`#a9b1d6`. Optional: `text #c0caf5`, `accentAlt
#bb9af7` (modern purple). Cost: btop + optional JSON.
- **kanagawa** — Wave, clean. No action required; optional rename
"Kanagawa Wave"; optional brighter good `springGreen #98bb6c`.
- **ethereal** — vibe solid; `surface`=`base`, `muted`=`overlay`.
**Fix:** `surface #1a2340` (or Omarchy color0 `#3C486D`); split
muted darker than overlay; optional richer good. Cost: small JSON.
- **retro-82** — strong synthwave + excellent rofi whole-swap.
`surface #00172e` darker than `base #05182e` (inverted raise);
`good #028391` teal (reads info not ok). **Fix:** `surface
#0a2844`; optional `good #3f8f8a` or phosphor green. Cost: small
JSON; keep rofi.
- **ristretto** — Monokai Pro Ristretto, ship-quality. Only
`subtext`=`text`; btop `main_bg` 1-step drift (`#2c2421` vs
`#2c2525`). **Fix:** `subtext #b5a9aa`; align btop bg. Cost: tiny.
- **flexoki-light** — best of the Flexoki/import set post item-28.
Optional: distinct ANSI bright-black `#575653`; recapture preview
if terminal chrome still looks dark-on-paper. Cost: polish only.
#### Systemic theme tooling
- **Import pipeline collapses hierarchy when ANSI 0==8**
`tools/import-palettes.py` maps `surface←color0`, `overlay←color8`,
`good/warn/bad←color2/3/1`, `mantle←darken(base)`. When color0==8
(gruvbox, everforest) surface=overlay; light themes get sludge
mantle. Long-term: allow explicit role overrides independent of
ANSI for identity themes; don't re-import without a hierarchy pass.
Cost: tool + convention doc.
- **audit-theme-design identity exemptions**
Document expected noise for white/vantablack/lumon/hackerman/
matte-black/miasma (hue-family + CVD) so future audits don't
"fix" identity into traffic lights. Optional: special-case in
the report script. Cost: docs or small script tweak.
### Tooling / CI / installer hardening (codebase exam 2026-07-09)
- **CI `py_compile` only covers theme-sync**
`.gitea/workflows/check.yml` compiles only
`pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py`. Misses
`compose-lock.py`, `tools/*.py`, `tools/vm/*.py` — syntax errors
can ship. Expand to all tracked `*.py` (or at least installer +
tools). Cost: one workflow line; TESTING.md §1 should match.
- **No automated check for installer pure contracts**
`compose-lock.py`, disko `swapSize`/`withLuks` arg contract, and
hardware-db module names vs `nixos-hardware.nixosModules` have no
`checks.*`. Lock bumps of nixos-hardware can rename modules and
break profiled installs only on matching hardware. Cost: medium
one-time check scaffolding; high long-term install safety.
- **Installer soft gaps**
(1) fingerprint path prefers `lsusb` but package PATH has
`pciutils` not `usbutils` (sysfs fallback usually OK); (2)
`chown -R 1000:100` on the flake dir assumes first-user UID/GID;
(3) flake `packages` exports only theme-sync + install, not
doctor / control-center / battery-notify (runtime via overlay is
fine — discoverability only). Cost: small each.
- **Control Center `/tmp/nomarchy-gens` for rollback list**
Predictable world-writable path for generation listing — multi-user
race / trash. Prefer `mktemp`. Cost: tiny.
### Distro improvement ideas (codebase exam 2026-07-09)
- **Fail-closed / friendlier theme-state errors in `mkFlake`**
Missing `theme-state.json` fails later at `readFile`; invalid JSON
fails at `fromJSON` before theme.nix's field-level validator. A
friendly "add theme-state.json" / schema pointer would help
downstream authors. Related: `settings.displayProfile*` not in
defaults block (consumers use `or`); unknown border role falls
through to raw string. Cost: medium lib/theme.nix UX.
- **Look & Feel submenu (ROADMAP optional)**
Night-light, wallpaper cycle, blur/gaps (once CC appearance is
fixed) grouped with Theme once the root stays six entries. Cost:
menu structure only; product call.
- **Export all overlay tools as flake packages**
`nix build .#nomarchy-doctor` etc. for maintainers and CI
convenience without going through modules. Cost: three lines in
flake.nix.
- **Unattended-install test matrix**
`test-install.sh` always LUKS+swap; add (or document) no-swap and
no-LUKS paths once those contracts are fixed, so swap/LUKS-class
bugs cannot recur. Cost: script flags + time on KVM host.
- **Document always-on home pieces**
easyeffects, udiskie, cliphist always on with no toggle (by
design) — optional README note so users know what ships and how to
override. Cost: docs only.
## Decisions `[human]`