Add docs/VISION.md (product themes, v1.0 bar, agent slice hints) and README maps under docs/ and agent/. Point ROADMAP, BACKLOG, LOOP, CLAUDE, HARDWARE, and the root README at the split: VISION = intent, BACKLOG = executable queue, ROADMAP = design history, .claude = harness only. Mark v1.0 track in BACKLOG PROPOSED for human triage.
40 KiB
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; design history
in docs/ROADMAP.md; map in
docs/README.md and agent/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 § …orROADMAP § …when relevant); 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.
v1.0 track — see docs/VISION.md
Product themes and the release bar live in VISION. Many slices
already have pitches below (Hardware product, Installer bugs, Default
theme → boreal). When adding new product work, reference
VISION § <theme> and prefer the north-star laptop user. Suggested
next promotions (human picks order): install P0 contracts · boreal
default · System › Firmware · doctor hardware · fingerprint menu ·
first-boot tips · human rebuild errors · neon-glass quarantine.
Pre-existing
-
Default theme → boreal (
VISION § B/ v1.0 bar) Ship Boreal as the distro default instead of Tokyo Night: roottheme-state.json,templates/downstream/theme-state.json(and any greeter/plymouth fallbacks that hardcode Tokyo Night when state is missing). Preset already exists underthemes/boreal/. Capturepreview.pngif still missing so the picker looks first-class. Cost: copy/apply palette into seed state files + grep for tokyo-night defaults; V0 contrast, V1/V2 theme apply, V3 visual on hardware. -
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 (residual after SoT work) (1)
templates/downstream/theme-state.jsonlacksborderandsettingskeys present in repo root state (eval merges defaults); (2) no commentednomarchy.system.snapper.enableorhardware.i2c.*in templatesystem.nixdespite 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) V2test-install.shafter 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). -
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].
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.packagesonce 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-open — already under Bugs / broken behavior above (
"0G"vs"0"; missingNOMARCHY_LUKS_PASSPHRASE→ cleartext). Same audit reconfirmed both as install-path P0. -
Offline mode must not silently fall back to network lock When the network probe fails (or install is offline),
compose-lock.pyfailure currently warns and runsnix 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). Networkflake lockonly when the probe said online. Cost: small branch innomarchy-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/blkidshows 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.orgprobe 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 intonomarchy-install. Prefer that over more install-time questions or a second installer UI. Also fix stalehosts/live.nixheader (“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 switchone-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-snapshotsInstaller creates disko@snapshots→/.snapshotsand 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
swapSizecontract, compose-lock py_compile, hardware-db ∈ nixos-hardware). After swap/LUKS fixes: document or script unattended no-swap and explicit no-LUKS paths intest-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-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.