25 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
29. Laptop docking / external-monitor UX [human] (needs hardware)
(Raised by Bernardo, 2026-07-07.) The shipped display application is weak and unintuitive, and Hyprland's model for workspaces across multiple monitors is unclear to users. Make docking a laptop to external monitors first-class:
- Unplug → restore the panel: when the external monitor is removed and the laptop's own panel is off (was the only-active output while docked), turn the internal panel back on immediately — never leave the user staring at a dead lid.
- Plug → extend by default: a newly connected external monitor should extend the laptop panel (not mirror), out of the box.
- HDMI audio: an easy way to route audio to the monitor when a display with audio (HDMI/DP) is connected — a menu toggle / quick action, not manual pavucontrol digging.
- Workspaces + multi-monitor: document (cheatsheet + menu) and,
where sensible, sane defaults for how Hyprland binds workspaces to
outputs so multi-monitor behavior is predictable.
Likely a
hyprland/kanshi-style profile layer plus a menu surface; split into slices before starting.[blocked:hw]for the real dock/undock + HDMI-audio verification (see HARDWARE-QUEUE.md).
28. Theme UI review — make every surface stunning [big]
(Raised by Bernardo, 2026-07-05 — placed at the head of NEXT; reorder freely.) A thorough visual pass over the whole themed UI — Waybar (the generated bar AND the summer whole-swaps), rofi menus, swaync, hyprlock, the cheatsheet, ghostty/btop, GTK/Qt, boot splash, greeter — with the bar set at stunning, not merely consistent (pillar 4). Grounded in design theory, not taste alone:
- Color: palette-role relationships that demonstrably work together — harmony models (analogous/complementary accents), perceptual lightness ordering of base→surface→overlay→text (check in OKLCH, not raw hex), accent/warn/bad distinguishable for color-blind users; extend tools/check-theme-contrast.py to encode the adopted rules (it currently guards legibility pairs only).
- Typography & rhythm: a consistent type scale across bar/menus/ OSD, spacing on the ui.gapsIn/gapsOut grid, aligned paddings (the bar's per-module padding vs rofi rows vs swaync toasts).
- Hierarchy: what reads first — bar module ordering/weights, menu
title vs rows, toast title vs body; icon weights consistent with the
adjacent text.
Bernardo has offered image-expert models for designed graphics
(wallpapers, logos, glyphs, preview/branding art): an agent slice that
needs artwork must STOP and ask ("say the word") instead of shipping
programmer-art.
Slices: (a) ✓ the audit (2026-07-05, iteration #31 — findings below);
(b) palette-level fixes + the extended contrast/harmony checker —
P1s ✓ shipped 2026-07-05 (iteration #32): summer-day + flexoki-light
subtext/surface/overlay/muted retuned (flexoki to its canonical
upstream tones), miasma + kanagawa surfaces raised (kanagawa to
sumiInk4);
subtext/base ≥ 3now GATED in check-theme-contrast.py (all 21 pass). P2s ✓ shipped 2026-07-05 (iteration #33): 18 hex retunes across 9 themes (OKLCH hue-preserving minimal raises; canonical where upstream defines one — gruvbox bg4, nord #616e88, latte overlay1, flexoki 600-series statuses for light mode) and the four floors GATED: muted ≥ 2, text/surface ≥ 4.5, accentAlt ≥ 3, warn ≥ 2.5 (21×7 pairings green; audit contrast findings 20 → 1 — miasma's earthybadstays as identity, good/bad floors stay audit-only so no exemption list was needed). Remaining (b): nothing — further palette work graduates from slices (c)/(d) findings; (c) per-surface polish slices — capture harness ✓ shipped 2026-07-05 (iteration #34):tools/theme-shot.nixboots the themed desktop headlessly at 1080p and screenshots desktop + open root menu (proven on tokyo-night); per-surface punch items from the first full-res review:- ✓ rofi keybind hints (shipped iteration #37): dimmed pango hints on root/Tools/System rows, generated from keybinds.nix via the cheatsheet's prettyKeys (single source, can't drift) — capture-verified on tokyo-night.
- Icon weight mix
[human]: full-color Papirus icons in the menus vs all-monochrome bar glyphs — pick: symbolic/monochrome menu icons for one system, or color as the menu's identity. A/B captured on lumon (iteration #46, side-by-side: https://claude.ai/code/artifact/1ccd10fe-4b6f-454a-888d-e7fdf2362b03): B (symbolic) matches the bar's weight but Papirus symbolic coverage has holes (preferences-desktop-theme-symbolic missing — Look & Feel lost its icon) and rofi tints symbolics a fixed dim gray, not the theme text color — going B needs an icon audit + recolor slice. A costs nothing. Awaiting the call; A stays until then. - ✓ Bar right-cluster rhythm (shipped iteration #36): group margins before pulseaudio/powerprofile/battery — heads that self-hide degrade to the uniform spacing.
- ✓ Semantic dim (shipped iteration #36): workspaces + dnd +
muted-volume now use
@muted(floor-guaranteed by 28b) instead ofalpha(@text, 0.5)tints; the CSS comment now documents the new invariant. - ✓ compositor backdrop (shipped iteration #35): Hyprland
misc:background_colornow the theme's base — no more black flash behind a missing/slow wallpaper (glaring on light themes in the summer-day capture). - summer-day whole-swap capture reviewed (iteration #35): the authored slate menu + island-pill bar survive the 28b retunes and read as one coherent identity — keep. ✓ placeholder-dim nit fixed (iteration #38: both summer rasi, bg0 at 60% alpha).
- latte capture reviewed (iteration #38): the generated menu is fully coherent on light themes (zebra rows, retuned surface, blue selected, dimmed hints) — the audit's "dark terminal on latte" preview concern was a stale preview, not a defect. Backdrop probe: misc:background_color IS applied (hyprctl getoption = 0xFFEFF1F5); captures show dark because awww's empty-state layer paints over it in the wallpaper-less VM — on hardware the fix covers its intended window (the pre-awww startup gap).
- ✓ Harness gap fixed (iteration #44): guest
~/.nomarchyseeded via tmpfiles BEFORE greetd starts the session, so the session's own exec-once wallpaper paint runs (a post-boot testScript seed was too late for exec-once, and a manual re-run fails without WAYLAND_DISPLAY — awww's client socket is display-named). Wallpaper-complete summer-night capture reviewed: lakeside night scene + cream inverted bar + menu-over-scene all cohere; the placeholder dim (#2d353b99) renders. Note: summer-night's cream bar/menu on dark backdrop is the authored inverted design (fg as surface), not a rendering bug. - ✓ Identity-theme capture + stub purge (iteration #45): the lumon
capture exposed that catppuccin/lumon/nord/retro-82 shipped stub
waybar.css files (2–14 lines, from the
f211ef0rewrite import) — and a theme waybar.css whole-swaps the ENTIRE generated stylesheet, so those four ran an unstyled default bar. Stubs deleted; re-capture confirms lumon falls back to the generated styled bar (workspace pill, padded right cluster). Their full ~110-line rofi.rasi designs kept. Real whole-swaps remain only summer-day/night (+ nord/retro-82 lost nothing: the stubs had no design). (d) the whole-swap themes brought to the same bar (module parity verified textually 2026-07-05: whole-swaps carry every functional module; omitting cpu/memory is identity). Visual work is V3-bound by nature — screenshots are the review artifact, hardware confirms.
Audit findings (slice a — tools/audit-theme-design.py over all 21
palettes + visual pass over previews; identity-aware curation):
P1 · invisible-secondary-text palettes✓ fixed iteration #32 (summer-day subtext #6e828a, surface #efe7d0; flexoki-light to canonical Flexoki: surface #E6E4D9, overlay #B7B5AC, subtext #6F6E69, muted #878580). Gate added.P1 · inverted bg stacks✓ fixed iteration #32 (miasma surface #2e2e2e; kanagawa surface #2a2a37 = upstream sumiInk4). Note learned: in this palette modeloverlayis a MID-TONE (borders/dim, catppuccin semantics), not a third bg tier — the dark-theme overlay==muted pattern is intentional.- P2 · muted floor: muted-on-base < 2.0 on gruvbox (1.27), everforest (1.55), lumon (1.68), nord (1.69), white (1.82), latte (1.91), retro-82 (1.93) — dimmed workspaces/inactive items barely render (visible on the lumon preview). Adopt ≥ 2.0 and retune.
- P2 · warn barely reads: warn-on-base ≈ 2.0–2.3 on rose-pine, summer-day, latte, flexoki-light — this is the bar's 25%-battery color. Floor 2.5 (or see the color-only rule below).
- P2 · text-on-surface < 4.5: ristretto 3.88, latte 4.39 (menus and chips draw text on surface).
- P3 · accentAlt-on-base < 3: latte 2.34, summer-day 2.83.
Design rule (systemic): status is never color-only.✓ swept iteration #69 (2026-07-08). good/warn collapse under protanopia and good/bad under deuteranopia is inherent to red/green/yellow, not fixable per-palette — but the sweep found every Waybar status indicator already carries non-color redundancy: updates shows a count ( N) and self-hides; vpn/doctor/nightlight/recording self-gate (presence is the state); battery changes glyph by level + bolt-on-charge; idle_inhibitor (/) and pulseaudio.muted () are glyph-distinct. The one real gap: swaync inhibited- states reused the normal bells (/), so "notifications suppressed by an inhibitor" was distinguished from normal by color alone — fixed by routing every suppressed state (DND or inhibited) to the bell-off glyph + @muted, in the generated bar and all four whole-swaps (parity). V3-pending: confirm the muted bell-off renders while an app holds a notification inhibitor (HARDWARE-QUEUE).- Identity exemptions, made explicit: vantablack + white (deliberately hueless statuses), lumon (all-blue), hackerman (matrix green statuses), matte-black/retro-82 (off-family hues) are identity, not defects — slice (b)'s checker gets a per-theme exemption list instead of silently lowered floors.
- ANSI / greeter notes: latte, rose-pine and white ship
ansi[0]LIGHTER thanansi[15]— decide the light-theme ANSI convention (terminals conventionally keep black dark) before the greeter/tty theming ships to them. tokyo-night/summer-nightansi[7]-on-ansi[0]≈ 2.8–3.0 = the greeter's muted text; fine as muted, watch on hardware. - Visual pass (480×270 previews — too small for type/spacing; the deep per-surface pass needs 1080p captures): the generated bar's right cluster runs ~9 modules with uniform spacing — no grouping rhythm (consider micro-gaps between tray/net · stats · toggles · battery/notify groups); latte's preview shows a DARK terminal on the light desktop — verify ghostty consumes the latte palette (or the preview is stale); summer-day's island-pill whole-swap bar and lumon's globe identity are strong — preserve through any polish.
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.
-
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. -
[stability] Interim build-tier gate for automated lock bumps (surfaced by the 2026-07-08 stability survey, iteration #71). Today
.gitea/workflows/bump.ymlpushes a lock bump tomainon the eval tier only (nix flake check --no-build), and Nomarchy ships no binary cache — so users pullmainand compile whatever landed. A bump that evaluates but fails to build (a dropped patch, an IFD/Stylix break, an upstream package that no longer compiles) reaches users as a brokenhome-update/sys-rebuild. Item 20 (VM suite in CI) is the real fix but is[human]-blocked on a KVM runner. Cheap interim that needs no KVM: have the bump gate additionally buildhomeConfigurations.nomarchy.activationPackageandnixosConfigurations.nomarchy.config.system.build.toplevelbefore the push (V1 tier — catches build-time breakage eval misses). Cost/open question: the dockeract_runnercompiles Nomarchy's own derivations from source (cache.nixos.org still serves upstream), so a full toplevel build could be slow/heavy — measure once; if too heavy, gate on the HM activation package alone (the surface most bumps touch). Does not replace item 20; it raises the floor until item 20 lands. -
[usefulness] Automount removable media (udiskie): Right now
services.udisks2is enabled at the system level, but there is no user-space automounter. Inserting a USB drive requires opening a file manager to mount it manually, and there are no notifications when a drive is safe to remove. Addingudiskie(enabled via Home Manager) provides a lightweight daemon for desktop notifications and zero-click automounting for USB drives, closing a standard workstation expectation gap. Cost: cheap (one package + home-manager toggle). -
[usefulness] External monitor brightness control (ddcutil): Laptops can dim their internal screens via
brightnessctland the OSD keys, but external monitors connected to a dock are stuck at blinding brightness unless the user reaches for physical buttons. Integratingddcutil(which controls external monitors via DDC/CI over HDMI/DP) into a Waybar slider or a new rofi menu row would give Nomarchy a true "docked workstation" polish. Cost: moderate — requireshardware.i2c.enable = true, addingddcutilto udev/groups, and building a UI path to control it. -
[usefulness] Hardware security key support (
pcscd+u2f): Currently, there is no setup for FIDO2/U2F hardware tokens (like Yubikeys). Withoutservices.pcscd.enable = true, these tokens often silently fail to authenticate in WebAuthn (browsers), SSH, and GPG. Enabling this (andhardware.u2f.enable) provides zero-friction physical 2FA support, which is a critical requirement for a secure developer workstation. -
[usefulness] Microphone noise cancellation (
easyeffects): PipeWire is robust, but it lacks out-of-the-box noise reduction for microphones. Addingeasyeffectswith a noise cancellation plugin (likernnoise) provides system-wide background noise filtering for voice calls (Zoom, Teams, Discord). This is a massive quality-of-life boost for remote workers that elevates the audio stack from "functioning" to "premium". -
[stability] Drive health monitoring (
smartd): While BTRFS auto-scrub prevents silent bitrot, it doesn't warn about impending physical hardware failure. Enablingservices.smartd.enable = truewith desktop notifications (via-M execcustom scripts or local mail) provides early warnings when an NVMe/SSD starts throwing SMART errors, giving the user a critical window to back up data before the drive dies. -
[usefulness] Screen Sharing for Legacy X11 Apps (
xwaylandvideobridge): Native Wayland screen sharing works via PipeWire and the desktop portal, but apps running via XWayland (like older Electron apps, Discord, or Slack) cannot capture the Wayland desktop directly. Addingxwaylandvideobridgeinvisibly routes Wayland window/screen streams into a virtual X11 window that these legacy apps can read, instantly fixing the "black screen" issue when sharing screens. -
[usefulness] File Manager Archive Backend (
file-rollerorxarchiver): Thunar is configured withthunar-archive-pluginin the system module, but the underlying archive extraction GUI backend is missing. Consequently, right-clicking a.zipor.tar.gzfile and clicking "Extract Here" will fail silently. Adding an archive manager fulfills a fundamental workstation requirement and fixes the broken context menu.
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.