Files
Nomarchy/agent/BACKLOG.md
Bernardo Magri d09c978b9b
All checks were successful
Check / eval (push) Successful in 3m16s
docs(agent): backlog codebase exam + per-theme design audit
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.
2026-07-09 08:46:13 +01:00

566 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Backlog — the prioritized task queue
The forward-looking half of the old docs/ROADMAP.md, reworked as a queue
agents can execute. Detailed design notes and decision records stay in
`docs/ROADMAP.md` (referenced as "ROADMAP § <item>"); this file is *what's
next, in what order*.
**Rules:**
- Agents take the topmost actionable item (see LOOP.md). Finished items
are **deleted** here — the journal + git log are the record; durable
design notes get a ✓-entry in docs/ROADMAP.md if worth keeping.
- Item numbers are **stable IDs** — never renumbered or reused. A gap in
the sequence means shipped (or dropped) work; new items take the next
free number regardless of tier.
- Tags: `[blocked:hw]` needs real hardware (see HARDWARE-QUEUE.md) ·
`[human]` needs Bernardo · `[stuck]` two failed attempts, needs help ·
`[big]` must be split before starting.
- Agents may append to **PROPOSED** and **Decisions** freely; only
Bernardo moves items *out* of PROPOSED into the tiers.
---
## NOW
*(empty — NEXT's top item is the queue head)*
## NEXT
### 14. Automated lock bumps — confirm the first real run
Slices b+c shipped 2026-07-05 (`.gitea/workflows/bump.yml`): weekly
schedule (Mon 05:17 UTC) + `workflow_dispatch` as the security
fast-lane; `nix flake update` → eval gate → pathspec-limited lock
commit pushed to `main` on green (that push triggers check.yml as the
second net). Update/commit/push logic simulated locally end-to-end in
a scratch clone, incl. a real update; today's bumped lock evals green.
Remaining (not confirmable from a session): the first scheduled or
dispatched run must land — watch that the runner picks up the cron and
that the Actions token can push. Then delete this item. Item 20's KVM
runner later upgrades the gate from eval-only to the VM suite.
### 20. KVM runner → VM suite in CI `[human]`
The remaining stretch of the CI item — checks-on-push is live and
**green** (run #58; runner = gitea/act_runner docker, eval tier).
Register a second runner on a host with `/dev/kvm` + nix (host-mode
label `nix-kvm`), then an agent uncomments the workflow's `vm-checks`
job: the `checks.*` VM suite + real toplevel/HM builds on every push
that later upgrades item 14's bump gate from eval-only to the full suite.
### 33. rofi page-overflow hint (scrollbar)
(Raised by Bernardo, 2026-07-07 — item 3.) When a list/grid overflows
onto another page, nothing signals there's more below. Add a scrollbar
(rofi's `scrollbar` element) or a "more ↓" hint, themed to the palette,
on the launcher grid and the long text menus. Check rofi 2.0.0's
scrollbar support/theming.
_Scoped 2026-07-07:_ rofi only draws the scrollbar when the list actually
overflows (`lines < rows`), so enabling it self-hides on short menus —
the right primitive. But it's a **visual, ~10-file slice**: the generated
palette theme (`rofi.nix`, `scrollbar = false`) **plus every authored
`.rasi`** (boreal/nord/lumon/kanagawa/retro-82/neon-glass/executive-slate/
summer-day/summer-night) — all deliberately set `scrollbar { width:0 }`
today, each needs a per-theme handle width/colour. Payoff is V3-only
(rendering), so it wants a themed session, not a headless edit.
### 41. Floating-window audit — remaining (needs hardware class checks)
(Raised by Bernardo, 2026-07-07 — item 11.) Broaden the `windowrule`
float set beyond the conservative first cut.
_Conservative cut shipped 2026-07-07 (iteration #63, on Bernardo's "take a
conservative approach"):_ float + center the mixer (item 35), **blueman**
(manager/adapters) and **system-config-printer** — the only shipped,
unambiguous config dialogs whose classes are confident. **Remaining
candidates, deliberately deferred because their window class can't be
verified headlessly** (a guessed class = dead rule): the polkit auth
prompt (hyprpolkitagent — no discoverable class in the package), GTK
file-chooser portals (`xdg-desktop-portal-gtk`), and any pinentry dialog.
Next slice: on hardware, run `hyprctl clients` while each is open, read
the real `class`, then append rules. Also revisit whether blueman/s-c-p
actually float once seen (regex tolerance for the `.…-wrapped` form).
## LATER
- **Wallpapers artifact split** (ROADMAP § Faster switches — decided,
deferred): pinned `Nomarchy-wallpapers` input so a state write stops
re-copying 86 MB. Follow-on: pre-built theme variants if switches are
still slow after.
- **Installer round 2** (ROADMAP § Installer): multi-disk BTRFS RAID,
impermanence, BIOS/legacy boot.
- **Boot-from-snapshot**: a systemd-boot equivalent of grub-btrfs.
- **Night-light geo mode**: lat/long auto sunset/sunrise (means wlsunset).
- **Per-theme icon overrides** / more icon packs (ROADMAP § Icon themes).
- **MIPI/IPU software-ISP camera** support (no-UVC machines).
- **VPN exit-node richer display** (country/city) (optional).
- **NixOS release bump → v2** `[human]`: deliberate, hand-edited, never
automated; the previous attempt was discarded (2026-06-22) over a
Hyprland OOM blocker — see MEMORY.md before retrying (NOW#3 should
also soften that blocker class).
## PROPOSED (agent suggestions — await human triage)
*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).
The control-center now has a proper toggle (Off / 80% / 90% / 60% /
Custom…) writing `settings.power.batteryChargeLimit` — but that's a
baked NixOS option, so it only lands on the next `sys-rebuild`. To make
it instant (no rebuild): add a udev rule making
`/sys/class/power_supply/BAT*/charge_control_end_threshold`
group-writable (`MODE`/`GROUP`) so the menu can `echo N >` it live
alongside persisting state (the sysfs oneshot in power.nix still owns
boot + AC-replug re-apply), and/or surface a rofi-menu quick row or a
Waybar action. `[blocked:hw]`: the sysfs write + no-rebuild effect can
only be confirmed on a laptop exposing the threshold. Decide first
whether the small privilege grant (a local user can set the charge cap)
is acceptable.
- **Portal/Flatpak camera picker still lists the internal IR sensor**
(ROADMAP § Webcam follow-up). The shipped IR-hide is a *WirePlumber
v4l2* rule, but Flatpak/portal apps consume cameras via the
**libcamera** path, where both sensors remain visible — a Flatpak
Zoom user can still pick the black IR "camera". Options, roughly
ascending cost: (a) do nothing — document it (portal camera support
is still rare in practice); (b) a WirePlumber *libcamera* monitor
rule disabling GREY-only nodes — needs verifying that libcamera
monitor rules can match early enough (the v4l2 investigation found
only `device.api` binds pre-rule, which is why surgical scoping
failed before — same wall likely applies); (c) a libcamera
configuration/udev quirk hiding the IR sensor at the libcamera layer
itself. Cost: (b)/(c) need a T14s-style RGB+IR machine to verify →
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]`
Open calls only Bernardo can make; agents add options/evidence but never
decide.
- **Formatter adoption:** repo deliberately has none; `nixfmt-rfc-style`
would flatten the aligned hand-formatting of ~33 files. Adopt or
declare never?
- **Docs site vs Markdown-in-repo** (from the docs-review item).
- **zram swap:** faster under pressure and pairs with NOW#3, but it
interacts with the hibernation-swapfile story (resume device/priority
ordering) — adopt, adopt-with-hibernation-guard, or skip?
- **Default browser:** the template comments Firefox out. The shipped
mime defaults (item 8, done) point `text/html`/http(s) at
`firefox.desktop` as *inert* entries — they activate the moment
Firefox is installed and are skipped otherwise, so the remaining call
is only: ship a browser active in the suite, or stay
browserless-by-default?
- **Default power backend — PPD vs TLP:** (raised by Bernardo 2026-07-08:
would TLP be more power-efficient, and should it be the default?)
TLP does get more *idle* battery life, but only from device-level knobs
PPD deliberately omits — PCIe ASPM, disk/NVMe link PM, USB autosuspend,
runtime PM — which are the same knobs behind NVMe/USB flakiness that
pillar 1 (rock-stable, never fight your machine) guards against.
**Evidence (2026-07-08):** no credible hard-watt benchmarks exist — TLP's
own maintainer declines to quote any ("measure on your hardware") and says
PPD's power-saver gives *similar* savings under load; TLP's edge is
idle-only (linrunner.de/tlp/faq/ppd.html — PPD covers a *subset* of TLP,
"no settings to reduce consumption when the CPU is idle"). Our sibling
distro **Omarchy** ran this exact experiment — a "replace Power Profiles
with TLP for battery" guide — and the author **reverted to PPD** ("more
headaches and weird issues"); Omarchy shipped PPD auto-switching instead
(basecamp/omarchy#3907). PPD 3.4.0 now does AC/battery auto-switching,
closing part of TLP's UX gap. **Cost of TLP-default:** TLP has no live
D-Bus profile API, so the `powermgmt` menu + Waybar profile indicator +
low-battery auto power-saver (all `powerprofilesctl`) would need a rework —
it's not a one-line backend swap. **Agent rec (evidence, not a decision):
keep PPD default;** chase battery via targeted low-risk tweaks (PCIe ASPM
policy, battery-side EPP, the charge *start* threshold, PPD auto-switching)
and keep TLP the documented one-line opt-in it already is
(`nomarchy.system.power.backend = "tlp"`). Options: (i) status quo +
targeted tweaks [rec]; (ii) TLP default (reworks the profile UX);
(iii) nothing.