Files
Nomarchy/agent/BACKLOG.md
Bernardo Magri 8a5714f330
Some checks failed
Check / eval (push) Has been cancelled
feat(desktop): overhaul multi-monitor docking UX
2026-07-08 22:29:44 +01:00

21 KiB
Raw Blame History

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

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 ≥ 3 now 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 earthy bad stays 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.nix boots 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 of alpha(@text, 0.5) tints; the CSS comment now documents the new invariant.
    • compositor backdrop (shipped iteration #35): Hyprland misc:background_color now 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 ~/.nomarchy seeded 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 (214 lines, from the f211ef0 rewrite 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 model overlay is 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.02.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 than ansi[15] — decide the light-theme ANSI convention (terminals conventionally keep black dark) before the greeter/tty theming ships to them. tokyo-night/summer-night ansi[7]-on-ansi[0] ≈ 2.83.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-wallpapers input so a state write stops re-copying 86 MB. Follow-on: pre-built theme variants if switches are still slow after.
  • Installer round 2 (ROADMAP § Installer): multi-disk BTRFS RAID, impermanence, BIOS/legacy boot.
  • Boot-from-snapshot: a systemd-boot equivalent of grub-btrfs.
  • Night-light geo mode: lat/long auto sunset/sunrise (means wlsunset).
  • Per-theme icon overrides / more icon packs (ROADMAP § Icon themes).
  • MIPI/IPU software-ISP camera support (no-UVC machines).
  • VPN exit-node richer display (country/city) (optional).
  • NixOS release bump → v2 [human]: deliberate, hand-edited, never automated; the previous attempt was discarded (2026-06-22) over a Hyprland OOM blocker — see MEMORY.md before retrying (NOW#3 should also soften that blocker class).

PROPOSED (agent suggestions — await human triage)

Agents: append here with a one-paragraph pitch (what/why/cost). Do not implement. Bernardo moves accepted items into a tier.

  • Battery charge-limit — make the toggle instant [blocked:hw] (follow-up to the preset toggle shipped 2026-07-07, iteration #55). The control-center now has a proper toggle (Off / 80% / 90% / 60% / Custom…) writing settings.power.batteryChargeLimit — but that's a baked NixOS option, so it only lands on the next sys-rebuild. To make it instant (no rebuild): add a udev rule making /sys/class/power_supply/BAT*/charge_control_end_threshold group-writable (MODE/GROUP) so the menu can echo N > it live alongside persisting state (the sysfs oneshot in power.nix still owns boot + AC-replug re-apply), and/or surface a rofi-menu quick row or a Waybar action. [blocked:hw]: the sysfs write + no-rebuild effect can only be confirmed on a laptop exposing the threshold. Decide first whether the small privilege grant (a local user can set the charge cap) is acceptable.

  • Portal/Flatpak camera picker still lists the internal IR sensor (ROADMAP § Webcam follow-up). The shipped IR-hide is a WirePlumber v4l2 rule, but Flatpak/portal apps consume cameras via the libcamera path, where both sensors remain visible — a Flatpak Zoom user can still pick the black IR "camera". Options, roughly ascending cost: (a) do nothing — document it (portal camera support is still rare in practice); (b) a WirePlumber libcamera monitor rule disabling GREY-only nodes — needs verifying that libcamera monitor rules can match early enough (the v4l2 investigation found only device.api binds pre-rule, which is why surgical scoping failed before — same wall likely applies); (c) a libcamera configuration/udev quirk hiding the IR sensor at the libcamera layer itself. Cost: (b)/(c) need a T14s-style RGB+IR machine to verify → pairs with a hardware-queue session. Recommend (a) now, (b) investigated when the T14s is next available.

  • [security] System-wide AppArmor confinement: Enabling security.apparmor.enable = true tightens the security posture of the workstation. It confines services and applications to their minimum necessary privileges, shielding the system against zero-days in desktop apps (like PDF viewers or media players). The cost is low (a single NixOS toggle that pulls in the default profile set), and it provides a critical layer of defense-in-depth for a daily-driver workstation.

  • [usefulness] Wayland-native color picker (hyprpicker): Designers and developers constantly need to sample colors from the screen. Adding hyprpicker and binding it to a keyboard shortcut (e.g., SUPER+SHIFT+C) gives a fast, Wayland-native magnifying glass color picker that freezes the screen and copies the hex code directly to the clipboard.

  • [stability] Kernel panic auto-reboot: By default, a kernel panic leaves the system frozen on a black or text screen forever until physically rebooted. Setting boot.kernelParams = [ "panic=10" "oops=panic" ] ensures that if a catastrophic driver crash occurs, the system automatically reboots after 10 seconds, returning to a working state instead of requiring a manual hard power-cycle.

Decisions [human]

Open calls only Bernardo can make; agents add options/evidence but never decide.

  • Formatter adoption: repo deliberately has none; nixfmt-rfc-style would flatten the aligned hand-formatting of ~33 files. Adopt or declare never?

  • Docs site vs Markdown-in-repo (from the docs-review item).

  • zram swap: faster under pressure and pairs with NOW#3, but it interacts with the hibernation-swapfile story (resume device/priority ordering) — adopt, adopt-with-hibernation-guard, or skip?

  • Default browser: the template comments Firefox out. The shipped mime defaults (item 8, done) point text/html/http(s) at firefox.desktop as inert entries — they activate the moment Firefox is installed and are skipped otherwise, so the remaining call is only: ship a browser active in the suite, or stay browserless-by-default?

  • Default power backend — PPD vs TLP: (raised by Bernardo 2026-07-08: would TLP be more power-efficient, and should it be the default?) TLP does get more idle battery life, but only from device-level knobs PPD deliberately omits — PCIe ASPM, disk/NVMe link PM, USB autosuspend, runtime PM — which are the same knobs behind NVMe/USB flakiness that pillar 1 (rock-stable, never fight your machine) guards against. Evidence (2026-07-08): no credible hard-watt benchmarks exist — TLP's own maintainer declines to quote any ("measure on your hardware") and says PPD's power-saver gives similar savings under load; TLP's edge is idle-only (linrunner.de/tlp/faq/ppd.html — PPD covers a subset of TLP, "no settings to reduce consumption when the CPU is idle"). Our sibling distro Omarchy ran this exact experiment — a "replace Power Profiles with TLP for battery" guide — and the author reverted to PPD ("more headaches and weird issues"); Omarchy shipped PPD auto-switching instead (basecamp/omarchy#3907). PPD 3.4.0 now does AC/battery auto-switching, closing part of TLP's UX gap. Cost of TLP-default: TLP has no live D-Bus profile API, so the powermgmt menu + Waybar profile indicator + low-battery auto power-saver (all powerprofilesctl) would need a rework — it's not a one-line backend swap. Agent rec (evidence, not a decision): keep PPD default; chase battery via targeted low-risk tweaks (PCIe ASPM policy, battery-side EPP, the charge start threshold, PPD auto-switching) and keep TLP the documented one-line opt-in it already is (nomarchy.system.power.backend = "tlp"). Options: (i) status quo + targeted tweaks [rec]; (ii) TLP default (reworks the profile UX); (iii) nothing.