Add docs/HARDWARE.md (layers, install path, firmware/fingerprint/NVIDIA deep dives, unsupported machines, contributing a DMI line). Link from README and MIGRATION; queue firmware UI, doctor checks, detect-hw CLI, and related PROPOSED items in BACKLOG.
33 KiB
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 § "); 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-wallpapersinput 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…) writingsettings.power.batteryChargeLimit— but that's a baked NixOS option, so it only lands on the nextsys-rebuild. To make it instant (no rebuild): add a udev rule making/sys/class/power_supply/BAT*/charge_control_end_thresholdgroup-writable (MODE/GROUP) so the menu canecho 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.apibinds 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.shalways passes--argstr swapSize "${SWAP_GB}G". When the user chooses 0 (no swap), disko gets"0G", butdisko-config.nixonly treats the exact string"0"as no-swap — so it still creates@swapwith size0G. 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.nixnomarchy-doctor-statusprints"text":""on failure. Waybar custom modules with emptytextself-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: badwhen unhealthy; exit 0 / no output when healthy. Cost: one-line script fix; V2 with existingchecks.doctorif extended, else V1. -
neon-glass theme ships a broken Waybar whole-swap
themes/neon-glass/waybar.cssuses@text/@base/@accent/ etc. with no@define-colorblock. 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 missingwaybar.jsonc,preview.png,btop.theme; CSS covers only a subset of modules. Fix: either finish (self-contained colors + layout parity + preview) or removewaybar.cssuntil 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.shset_statealways 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. Ifnomarchy.system.bluetooth.enable = false, blueman is not installed → silent/broken launch. Printers / colorpicker / audio gate correctly. Fix: gate oncommand -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.jsoncusesworkspace e+1/e-1(next empty workspace); summer-night, executive-slate, boreal user+1/r-1(relative). Likely leftover; behavior differs under scroll. Fix: align tor±1. Cost: two lines. -
Battery charge-limit / powerprofile only match
BAT*sysfs namespower.nixoneshot and menu/waybar globs use/sys/class/power_supply/BAT*.nomarchy-battery-notifycorrectly treats any system battery (type=Battery, notscope=Device). Machines withCMB0(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_PASSPHRASEinstalls unencrypted with only a log line — no fail-closed.test-install.shalways sets a passphrase, so the regression path is untested. Options: require the env var when unattended, or an explicitNOMARCHY_NO_LUKS=1. Cost: small policy change in install script + test matrix note.
Docs / option-surface drift (codebase exam 2026-07-09)
-
option-docswould fail: undocumentednomarchy.hardware.i2c.*Live optionsnomarchy.hardware.i2c.enableand.ddcciinmodules/nixos/hardware.nix; distro default turns ddcci on (modules/nixos/default.nixmkDefault true). No README table rows, no template comments. Simulatedchecks.option-docsfinds 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 sayssudo tailscale upbut module grants--operator=/ template says no sudo; (5)hosts/live.nixheader 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.nixdesc = "Network (nmtui)"but the module runsnetworkmanager_dmenu. Cheatsheet inherits the wrong label. Cost: one string. -
nomarchy-menuusage string incomplete Usage omits implemented modules (doctor,controlcenter,rollback, …). Misleading for direct invocation / scripting. Cost: one string inrofi.nix. -
Template / seed state drift (1)
templates/downstream/theme-state.jsonlacksborderandsettingskeys present in repo root state (eval merges defaults — not a break, but new installs ship a thinner shape); (2) no commentednomarchy.system.snapper.enableorhardware.i2c.*insystem.nixdespite opt-in convention; (3) installer-generatedhome.nixis minimal (keyboard only) whileflake inittemplate 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.mdunlinked 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, andneon-glasslackpreview.png(picker falls back to plain-name rows) andbtop.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/powermenuand a left logo → main menu; the generatedwaybar.nixbar 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/.recordingpolish 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.cssdefines every@colorit 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) butthemes/rose-pine/btop.thememixes Dawnmain_bg/main_fgwith Main accents:hi_fg=#e0def4on#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.themeandthemes/summer-night/btop.themesetinactive_fgto#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
overlayandmutedare near-white#fdfdfdwhile base is#0d0d0d. Generated consumers useoverlayas selected_bg; hand btop hasselected_bg=#fdfdfd+selected_fg=#ffffff(white-on-white) andinactive_fg=#fdfdfd(inactive rows scream). Status greys are intentional monochrome identity. Fix (keep void identity):surface #1a1a1a,overlay #2a2a2a,muted #666666; btopselected_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 #459451is green (same family asgood #549e6a) → battery warning reads as "ok".subtext #F6F5DDis brighter thantext #C1C497(secondary louder than primary). Fix: swap text↔subtext; setwarn #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) andansi[7]/[15]are darkish → terminal "black"/"white" slots misbehave; design-audit flags inverted slots.mantle #cbcdd0is import-darken of light base (should be Latte mantle#e6e9ef);accentAlt #d260b5is not Latte pink/mauve. Fix:mantle #e6e9ef; accentAlt #ea76cb (pink); ansi[0] #5c5f77; ansi[7] #acb0be; ansi[8] #6c6f85; ansi[15] #bcc0ccwarn keep
#d6860aif 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.themeuses Mochamain_bgbut 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-vpnin pill selectors; (4) day scrolle±1already 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+ optionalbtop.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), raisesurface #f0f0f0, splitsubtext #404040while 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
subtextone 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 #ff5a7awhile leaving ANSI matrix. Raisesurface #1a1c2e(flat base=surface). Cost: product call + small JSON. -
matte-black — identity:
good=#FFC107amber,warn=#b91c1cred (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/#a65d4ewithout 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" andbtop.themeis 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 #e5e9f0is brighter thantext #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/titleuse warm#cfc9c2instead 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 #00172edarker thanbase #05182e(inverted raise);good #028391teal (reads info not ok). Fix:surface #0a2844; optionalgood #3f8f8aor phosphor green. Cost: small JSON; keep rofi. -
ristretto — Monokai Pro Ristretto, ship-quality. Only
subtext=text; btopmain_bg1-step drift (#2c2421vs#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.pymapssurface←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_compileonly covers theme-sync.gitea/workflows/check.ymlcompiles onlypkgs/nomarchy-theme-sync/nomarchy-theme-sync.py. Missescompose-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, diskoswapSize/withLuksarg contract, and hardware-db module names vsnixos-hardware.nixosModuleshave nochecks.*. 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
lsusbbut package PATH haspciutilsnotusbutils(sysfs fallback usually OK); (2)chown -R 1000:100on the flake dir assumes first-user UID/GID; (3) flakepackagesexports only theme-sync + install, not doctor / control-center / battery-notify (runtime via overlay is fine — discoverability only). Cost: small each. -
Control Center
/tmp/nomarchy-gensfor rollback list Predictable world-writable path for generation listing — multi-user race / trash. Prefermktemp. Cost: tiny.
Distro improvement ideas (codebase exam 2026-07-09)
-
Fail-closed / friendlier theme-state errors in
mkFlakeMissingtheme-state.jsonfails later atreadFile; invalid JSON fails atfromJSONbefore 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 useor); 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-doctoretc. for maintainers and CI convenience without going through modules. Cost: three lines in flake.nix. -
Unattended-install test matrix
test-install.shalways 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.
Hardware product (investigation 2026-07-09)
Full write-up: 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).
-
System › Firmware (fwupd product surface)
services.fwupd.enableis already default-on and never auto-flashes, but users only learn aboutfwupdmgrfrom a README sentence. Add a self-gated menu entry (System submenu) and/or Control Center row that: refreshes metadata, lists updatable devices, confirms, runsfwupdmgr update, then prints a reboot note when needed. Never silent-flash (pillar 1). Optional: first-boot / MOTD one-liner when the daemon is active. Spec: docs/HARDWARE.md §4. Cost: menu script + optional MOTD; V2 menu, V3 on real LVFS hardware. -
Doctor hardware section (read-only) Extend
nomarchy-doctorwith the same pass/fail + one-fix-command contract for: NetworkManager connectivity, default PipeWire sink, optional GPU smoke (vainfo/glxinfoif present), fprintd unit when fingerprint was enabled, fwupd active (+ warn ifget-updatesnon-empty), laptop charge-threshold sysfs when a limit is set. Spec: docs/HARDWARE.md §10. Cost: doctor.sh sections; V1 in existingchecks.doctorwhere fakes exist, rest V2/V3. -
nomarchy-detect-hwCLI (post-install re-probe) The installer already has purenomarchy_detect_hwstdout protocol (MODULE / NOMARCHY / DETAIL). Ship it as a package on PATH that prints suggestedhardwareProfile = [ … ]andsystem.nixsnippets without reinstalling — closes the template/migration gap and “I swapped the Wi‑Fi card.” Does not rewrite the flake unless a later--applyis 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 whenfprintdis present: enroll, list, delete; optional “use for login” that writessettings/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 nonomarchy.hardware.nvidia.*and hybrid/PRIME is wiki territory. When NVIDIA is detected, emit a commented block in generatedsystem.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. -
CI: hardware-db module names ∈ nixos-hardware Every
HARDWARE_DBthird field must exist in the pinnednixos-hardware.nixosModulesor installs break only on matching DMI. Add a purechecks.*(or option-docs-style runCommand). Pairs with existing “installer pure contracts” proposal. Cost: small check; V0. -
Installer:
usbutilson PATH for fingerprint probe Fingerprint path preferslsusbbut package wrap haspciutilsonly; sysfs VID walk is the fallback. Addusbutilstonomarchy-installPATH for reliable detection. Cost: one package in wrap; V0. -
Document + README table:
nomarchy.hardware.i2c.*i2c.ddcciis mkDefault true (external monitor brightness) but missing from README option tables →checks.option-docsred (also under docs-drift pack). Document in README + HARDWARE.md already mentions ddcci; close option-docs. Cost: docs; V0 with option-docs. -
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].
Decisions [human]
Open calls only Bernardo can make; agents add options/evidence but never decide.
-
Formatter adoption: repo deliberately has none;
nixfmt-rfc-stylewould 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) atfirefox.desktopas 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
powermgmtmenu + Waybar profile indicator + low-battery auto power-saver (allpowerprofilesctl) 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.