Files
Nomarchy/agent/BACKLOG.md
Bernardo Magri e01303851d
All checks were successful
Check / eval (push) Successful in 3m6s
test(checks): pure-contract guards batch (#49)
Four new no-VM checks.* + CI py_compile expansion:
- hardware-db-modules: installer DB module names ∈ pinned
  nixos-hardware.nixosModules (a lock bump can rename a module and break
  profiled installs on just the matching DMI, invisible to any VM).
- installer-compose-lock: offline lock-composer contract on fixtures.
- installer-disko: pure Nix assert — swapSize "0"/"0G" → no @swap, "2G"
  → sized, withLuks wraps root (permanently guards the #46 install fix).
- windowrule-syntax: builds the generated hyprland.conf and fails on the
  pre-0.53 grammar / windowrulev2 keyword (guards ed7fd93).
CI py_compile now covers all tracked *.py; docs/TESTING.md §1 synced.

Implemented by a worktree agent; diff reviewed. Verified V0 (flake check)
+ V1 (each checks.x86_64-linux.* built on main, exit 0); agent's negative
tests confirm each guard fails on a regression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 19:10:45 +01:00

708 lines
36 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
**This is the only executable work list for agents.** Product themes and
v1.0 intent live in [`docs/VISION.md`](../docs/VISION.md); design history
in [`docs/ROADMAP.md`](../docs/ROADMAP.md); map in
[`docs/README.md`](../docs/README.md) and [`agent/README.md`](README.md).
**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 (and/or a note in VISION)
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 (include
`VISION § …` or `ROADMAP § …` when relevant); only Bernardo moves items
*out* of PROPOSED into the tiers.
---
## NOW
*(empty — NEXT's top item is the queue head)*
## NEXT
### 43. System Firmware — fwupd menu (V2 render + optional extensions)
Core shipped 2026-07-09: self-gated **System ▸ Firmware** rofi row
(`nomarchy-menu firmware`) — refresh → get-updates → y/N confirm →
`fwupdmgr update` in a terminal; never silent-flash. Verified V0+V1
(flake check, HM build, generated `bash -n`). **Remaining:**
1. **V2** — render the System submenu in a session (VM/live ISO) and
confirm the Firmware row shows when fwupd is present and the flow
launches. Was blocked on VM contention (a parallel install test held
KVM); do it on the next VM pass.
2. **V3** — real LVFS flash + reboot note on hardware → HARDWARE-QUEUE
(entry added).
3. Optional (own slices): first-boot/MOTD one-liner when fwupd is active;
Waybar/updates-panel integration. (The doctor "updates available"
check is item #44.) Spec: docs/HARDWARE.md §4.
### 44. Doctor hardware section — V2 checks.doctor extension
Six read-only hardware checks shipped 2026-07-09 (NetworkManager,
PipeWire sink, VA-API/GL smoke, fprintd, fwupd + get-updates warn,
charge-threshold), all self-gating; V1 (doctor package builds:
bash -n + shellcheck) + a real-hardware read-only smoke on the dev box.
**Remaining V2:** extend the `checks.doctor` nixosTest with fakes to
cover the new rows headlessly; pin the fwupd `get-updates` exit-code
semantics (0 = updates) in that fake. Spec: docs/HARDWARE.md §10.
### 46. V2 verify: install swap=0 + unattended LUKS fail-closed (`208b8d4`)
Pure `nix-instantiate` on disko subvols passed V0; **full install path
not exercised**. VM agent with KVM:
1. Unattended install with `NOMARCHY_SWAP_GB=0` + LUKS passphrase —
assert no `/swap` (or empty policy) and no `@swap` surprise; system
boots.
2. Unattended **without** passphrase and **without** `NOMARCHY_NO_LUKS=1`
— must **fail** before wipe (fail-closed).
3. Optional: `NOMARCHY_NO_LUKS=1` + swap=2 still installs. Use
`tools/test-install.sh` variants / env flags; journal results; delete
item when green (or file NOW bugs on failure).
### 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).
### 50. Small code one-liners batch — V0/V1 (promoted 2026-07-09)
- Bluetooth menu row self-gate on `command -v blueman-manager` (`rofi.nix`) —
no-op when `nomarchy.system.bluetooth.enable = false`, like Printers.
- Control Center rollback list `/tmp/nomarchy-gens``mktemp` (predictable
world-writable path → multi-user race).
- `usbutils` on `nomarchy-install` PATH so the fingerprint `lsusb` probe works
(sysfs is the current fallback).
- Export overlay tools (doctor/control-center/battery-notify) as flake
`packages` (`nix build .#nomarchy-doctor`) — three lines in flake.nix.
## 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.*
### v1.0 track — see `docs/VISION.md`
Product themes and the release bar live in **VISION**. Shipped 2026-07-09:
#42 boreal default (V2-verified) · #43 Firmware menu (V1, V2 pending) ·
#44 doctor hardware (V1 + hw smoke, V2 pending). Remaining candidate
promotions: fingerprint menu · first-boot tips · human rebuild errors ·
neon-glass quarantine.
### 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)
- **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.
- **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.
### Docs / option-surface drift (codebase exam 2026-07-09)
_(i2c option-docs → #48; README/docs drift pack, keybind nmtui label,
nomarchy-menu usage string, secondary docs, always-on pieces → #47.)_
- **Template / seed state drift (residual after SoT work)**
(1) `templates/downstream/theme-state.json` lacks `border` and
`settings` keys present in repo root state (eval merges defaults);
(2) no commented `nomarchy.system.snapper.enable` or
`hardware.i2c.*` in template `system.nix` despite opt-in convention.
Main home/system sync is **Installer consumes template as SoT**
below — do that first, then close these residual gaps on the
template itself. Cost: small template polish.
- **Installer ↔ template SoT — follow-ups** (shipped 2026-07-09: copy +
`patch-template.py`). Remaining: (1) optional CI that the install
share ships the same template files; (2) V2 `test-install.sh` after
ISO rebuild (HM pre-activate now pulls starter packages — larger
closure); (3) residual theme-state / i2c comment polish on template
(see residual seed drift item).
### Theme polish / completeness (codebase exam 2026-07-09)
- **Missing theme picker previews + hand btop for identity themes**
`executive-slate` and `neon-glass` lack `preview.png` (picker
plain-name rows). All three still ship generated btop only (no hand
`btop.theme`). `boreal` now has a `preview.png` (2026-07-09) but it's a
**headless theme-shot capture** — a clean bare desktop+bar, not the
floating-terminal (fastfetch+btop) composition the other 21 use; a
real-hardware recapture to match the grid would be nicer (VM is the
documented fallback). CSS/jsonc for boreal/executive-slate are solid.
Capture the other two 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)
_(py_compile expand, installer pure contracts, hardware-db ∈ nixos-hardware,
windowrule dead-syntax guard → #49.)_
- **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` → mktemp → #50.)_
### 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 overlay tools as flake packages → #50.)_
- **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) → #47.)_
### Hardware product (investigation 2026-07-09)
*Full write-up: [`docs/HARDWARE.md`](../docs/HARDWARE.md). Install-time
autodetect is strong; day-2 lifecycle (firmware, biometrics, re-detect,
NVIDIA depth) is still mostly CLI. Items below are product work on top
of that doc — design notes live there (§4§10).*
- **`nomarchy-detect-hw` CLI (post-install re-probe)**
The installer already has pure `nomarchy_detect_hw` stdout protocol
(MODULE / NOMARCHY / DETAIL). Ship it as a package on PATH that
prints suggested `hardwareProfile = [ … ]` and `system.nix` snippets
without reinstalling — closes the template/migration gap and “I
swapped the WiFi card.” Does not rewrite the flake unless a later
`--apply` is explicitly designed. Spec: docs/HARDWARE.md §8. Cost:
extract script + package + man/README pointer; V0V1.
- **Fingerprint menu: enroll / list (+ optional PAM toggle)**
Installer enables fprintd on known USB VIDs but enroll is CLI-only
(`fprintd-enroll`) and PAM is a commented rebuild. Self-gated System
row when `fprintd` is present: enroll, list, delete; optional “use
for login” that writes `settings`/system intent + rebuild note
(Control Center Bluetooth pattern). Spec: docs/HARDWARE.md §5.
Cost: menu module; V3 enroll on real reader (`[blocked:hw]` for
full path).
- **Installer: NVIDIA commented system.nix guidance**
Detection already adds `common-gpu-nvidia`; there is no
`nomarchy.hardware.nvidia.*` and hybrid/PRIME is wiki territory.
When NVIDIA is detected, emit a **commented** block in generated
`system.nix` (same style as ROCm/NPU) pointing at PRIME /
powerManagement / open-module and docs/HARDWARE.md §6 — not a full
second stack. Cost: install script strings; V0 + doc.
_(hardware-db ∈ nixos-hardware → #49; usbutils on install PATH → #50;
i2c README table → #48.)_
- **Post-install hardware hints (MOTD / first session)**
When relevant services exist, surface one line each: firmware
(`fwupdmgr get-updates`), fingerprint (`fprintd-enroll`), optional
“run nomarchy-doctor”. Avoid nagging — once or until dismissed.
Spec: HARDWARE.md §4/§5. Cost: small greeter/MOTD or notify; V2/V3.
- **NVIDIA first-class options (larger, optional)**
Only if a maintainer commits to hybrid testing: thin
`nomarchy.hardware.nvidia.*` wrapping common PRIME/power patterns.
Prefer the commented-guidance item first; promote only with
hardware-queue coverage. Cost: high; `[big]` + `[blocked:hw]`.
### Installer process (audit 2026-07-09)
*Full-path review of live ISO → `nomarchy-install` → disko → config
generation → offline/online `nixos-install` → HM pre-activate → first
boot. Philosophy applied: **opinionated, stability-first** — prefer safer
defaults and fewer install questions over option sprawl. Day-2 firmware /
fingerprint / re-detect stay in Hardware product above, not in the
installer questionnaire.*
#### Already strong (do not “fix” with more choices)
- Single whole-disk GPT, UEFI + systemd-boot only; multi-disk RAID /
dual-boot / BIOS deferred (ROADMAP LATER).
- LUKS2 **default** + greeter auto-login (one gate); type-to-confirm wipe;
live medium excluded from disk list; careful pre-wipe.
- BTRFS subvols + snapper on by default; hibernation swap default = RAM;
hardware autodetection with **safe defaults on, heavy bits commented**.
- Offline-capable install (compose-lock, ISO store pin, daemon
substituter); HM pre-activate so first boot is themed.
- No Secure Boot theater; no install-time app-suite *wizard* / custom
partitioner / pbkdf knobs. (Starter apps come from the **template**
`home.packages` once the installer consumes the template as SoT —
see above; not a separate install questionnaire.)
- Live: no idle suspend (avoids mid-install sleep); WiFi via normal
desktop NM path.
#### Bugs (stability contracts — fix, dont optionalize)
- **Installer swap=0 + unattended LUKS fail-closed** — ✓ shipped
(workflow test run 2026-07-09): pass bare `"0"`; disko accepts
`"0"`/`"0G"`; unattended needs passphrase or `NOMARCHY_NO_LUKS=1`.
- **Offline mode must not silently fall back to network lock**
When the network probe fails (or install is offline),
`compose-lock.py` failure currently **warns** and runs `nix flake
lock` (needs network). That can turn a promised offline install into a
hung/partial one. **Fix:** if offline path is active and compose-lock
fails → **fail the install** with a clear message (dirty ISO / missing
narHash). Network `flake lock` only when the probe said online.
Cost: small branch in `nomarchy-install.sh`; V2 offline install test.
#### Safety / clarity (one line each — no new install menus)
- **Review panel: warn when target already has Windows / BitLocker /
NTFS / crypto_LUKS**
Typed disk-name confirm is solid, but multi-OS machines still risk
“wrong disk.” At selection or review, if `lsblk -f`/`blkid` shows
BitLocker, NTFS, Microsoft, or existing LUKS on the **chosen** disk,
add one explicit line: data will be destroyed. Still single-disk,
whole-disk only — **no** dual-boot path. Cost: few lines of probe +
review string; V0 + V2 optional.
- **Review panel: offline vs online source line**
Review always implies “no network needed” even when the online path
(skip full store copy, use substituters) is active. Make the Source
line conditional on the same `cache.nixos.org` probe so expectations
match. Cost: tiny; V0.
- **User password minimum length = LUKS (8 chars)**
LUKS requires ≥8; account password only requires non-empty. Align to
8 for one consistent rule — not a strength meter. Cost: one check;
V0.
#### Live ISO discoverability (one durable cue)
- **Live: one always-visible “Install Nomarchy” action**
Install is only in: 3s notify-send toast, getty helpLine, and
tribal knowledge of `nomarchy-install`. No desktop entry / menu row.
**One** durable surface on the live host only: e.g.
`xdg.desktopEntries` (Terminal=true) **or** a single Tools/System
menu item that opens a terminal into `nomarchy-install`. Prefer that
over more install-time questions or a second installer UI. Also fix
stale `hosts/live.nix` header (“No installer yet” — already in docs
drift pack). Cost: live.nix only; V1 ISO rebuild + V2 smoke.
#### Generated config / post-install polish
- **HM pre-activate failure → one durable recovery hint on target**
Installer already prints the `home-manager switch` one-liner and logs
to `/var/log/nomarchy-hm-preactivate.log`. If pre-activate fails, also
drop a small flag file or MOTD snippet on the target so first login
still shows the fix after the live session is gone. Avoid permanent
first-boot checklists (fwupd/fprintd belong in menu/doctor). Cost:
small install-script write; V2.
- **MIGRATION.md: installer snapshot layout vs `@home-snapshots`**
Installer creates disko `@snapshots` → `/.snapshots` and a first-boot
oneshot for nested `/home/.snapshots`. MIGRATION still speaks of
top-level `@home-snapshots` (TuringMachine vocabulary). One paragraph
clarifying installer layout vs migration machines prevents wrong
expectations. Cost: docs only.
#### Test / pure contracts (after P0 bugs)
- **Installer pure checks + expanded unattended matrix** — overlaps
existing *Tooling* items (disko `swapSize` contract, compose-lock
py_compile, hardware-db ∈ nixos-hardware). After swap/LUKS fixes:
document or script unattended no-swap and explicit no-LUKS paths in
`test-install.sh` (env flags only — no interactive options). Do **not**
grow the interactive installer questionnaire.
#### Explicit non-goals for the installer (defer)
Do **not** add install-time: dual-boot / preserve partitions, multi-disk
RAID, Secure Boot/lanzaboote, recovery-key *wizard* (optional silent
recovery print is the only recovery-key idea worth considering later),
custom partitioner, online/offline user toggle, app suite selection,
pbkdf/TRIM knobs, or a second installer frontend. Stability and the
single golden path win.
## 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.