All checks were successful
Check / eval (push) Successful in 3m7s
Offline compose-lock failure now fail-closes (reusing the cache.nixos.org probe) instead of falling back to a network `nix flake lock`; a disk-review warn fires when the target already carries Windows/BitLocker/NTFS/LUKS (before the wipe); account password requires ≥8 chars like LUKS; on HM pre-activate failure the installer drops a NOMARCHY-DESKTOP-NOT-THEMED.txt recovery hint in the target home so first login still shows the fix. Worktree agent; diff reviewed + cherry-picked. V0 flake check + V1 nomarchy-install build. Password ≥8 is V0-complete; the offline/NTFS/ pre-activate scenarios are V2-pending (crafted install runs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
673 lines
34 KiB
Markdown
673 lines
34 KiB
Markdown
# 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.
|
||
|
||
### 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).
|
||
|
||
### 51. Waybar/menu behaviour bugs — V2 render pending
|
||
Fixes shipped V1 (2026-07-09): doctor indicator emits `` + `class:bad`
|
||
when unhealthy (was empty text → self-hidden); CC appearance toggles print
|
||
"(requires rebuild)"; summer-day scroll `e±1`→`r±1`. **Remaining V2:** a
|
||
theme-shot render with an induced doctor failure to see the glyph (fold into
|
||
#44's `checks.doctor` extension); CC message + scroll are behavioural (V1
|
||
+ self-evident).
|
||
|
||
### 52. Theme fidelity bugs — btop V2 render pending
|
||
Six per-theme defects fixed 2026-07-09 (rose-pine btop→Dawn; everforest +
|
||
summer-night btop `inactive_fg`; vantablack role inversion; osaka-jade
|
||
text/subtext + warn; catppuccin-latte mantle/accentAlt/ANSI; catppuccin
|
||
Mocha btop). JSON fixes **V0-verified** (`check-theme-contrast` 24×7 all
|
||
pass — accentAlt retuned to Latte mauve `#8839ef`, not the audit's pink
|
||
`#ea76cb` which fails the 3.0 floor). **Remaining V2:** a theme-shot render
|
||
of the btop-changed themes (rose-pine, everforest, summer-night, vantablack,
|
||
catppuccin) — btop.theme isn't contrast-checked.
|
||
|
||
### 53. Missing theme previews + neon-glass whole-swap (promoted 2026-07-09, V2)
|
||
- Capture `executive-slate` + `neon-glass` `preview.png` headlessly via the
|
||
theme-shot recipe proven on boreal (480×270); optional hand `btop.theme`.
|
||
- **First, for neon-glass:** its `waybar.css` uses `@text/@base/…` with no
|
||
`@define-color` block (and no `waybar.jsonc`), so applying it yields broken
|
||
bar colours. Decide: **finish** (self-contained colours + jsonc + preview)
|
||
or **quarantine** (remove `waybar.css` → falls back to the generated bar),
|
||
then capture the preview. V2 render.
|
||
|
||
### 54. Installer safety + polish — V2 scenarios pending
|
||
Four guards shipped 2026-07-09 (V0/V1: bash -n, flake check, nomarchy-install
|
||
builds): offline `compose-lock` failure now fail-closes instead of a network
|
||
`flake lock`; disk-review `warn` when the target shows Windows/BitLocker/NTFS/
|
||
LUKS; account password min 8 (mirrors LUKS); HM pre-activate failure drops a
|
||
`NOMARCHY-DESKTOP-NOT-THEMED.txt` hint on the target home. **Remaining V2**
|
||
(need crafted install scenarios): offline install actually failing closed; an
|
||
NTFS/BitLocker disk firing the warning; the pre-activate hint landing on a
|
||
booted target. (Password ≥8 is V0-complete.)
|
||
|
||
## 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, Control Center appearance no-ops, summer-day
|
||
scroll → #51; neon-glass broken waybar → #53.)_
|
||
|
||
- **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)
|
||
|
||
- **boreal preview recapture nicety** (executive-slate + neon-glass previews
|
||
→ **#53**). `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). Optional hand
|
||
`btop.theme` for the three identity themes still open. Cost: asset capture;
|
||
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
|
||
|
||
_→ promoted as NEXT **#52** (2026-07-09); the six bullets below are its
|
||
spec (exact hexes). The rest of the per-theme audit stays PROPOSED._
|
||
|
||
_Residual ANSI drift exposed by the #52 fixes (not yet touched — small
|
||
follow-up): **vantablack** `ansi[8]` (bright-black) still `#fdfdfd`, now
|
||
diverging from the new `muted #666666`; **osaka-jade** `ansi[3]`
|
||
(normal-yellow) still the old warn-green `#459451` while `warn`/`ansi[11]`
|
||
are now `#E5C736`. Decide per theme whether these are identity or bugs._
|
||
|
||
- **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; V1–V2.
|
||
|
||
- **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 Wi‑Fi 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; V0–V1.
|
||
|
||
- **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, don’t 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 fail-closed → #54.)_
|
||
|
||
#### Safety / clarity (one line each — no new install menus)
|
||
|
||
_(Review-panel destructive-data warning → #54.)_
|
||
|
||
- **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 = 8 → #54.)_
|
||
|
||
#### 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 → durable recovery hint → #54.)_
|
||
|
||
- **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.
|