Files
Nomarchy/agent/BACKLOG.md
Bernardo Magri eb8f9fad88
Some checks failed
Check / eval (push) Failing after 57s
feat(nixos): enable Magic SysRq keys for safe crash recovery
2026-07-08 22:06:04 +01:00

25 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

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 ≥ 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.

  • [stability] Interim build-tier gate for automated lock bumps (surfaced by the 2026-07-08 stability survey, iteration #71). Today .gitea/workflows/bump.yml pushes a lock bump to main on the eval tier only (nix flake check --no-build), and Nomarchy ships no binary cache — so users pull main and 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 broken home-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 build homeConfigurations.nomarchy.activationPackage and nixosConfigurations.nomarchy.config.system.build.toplevel before the push (V1 tier — catches build-time breakage eval misses). Cost/open question: the docker act_runner compiles 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.udisks2 is 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. Adding udiskie (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 brightnessctl and the OSD keys, but external monitors connected to a dock are stuck at blinding brightness unless the user reaches for physical buttons. Integrating ddcutil (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 — requires hardware.i2c.enable = true, adding ddcutil to 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). Without services.pcscd.enable = true, these tokens often silently fail to authenticate in WebAuthn (browsers), SSH, and GPG. Enabling this (and hardware.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. Adding easyeffects with a noise cancellation plugin (like rnnoise) 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. Enabling services.smartd.enable = true with desktop notifications (via -M exec custom 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. Adding xwaylandvideobridge invisibly 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-roller or xarchiver): Thunar is configured with thunar-archive-plugin in the system module, but the underlying archive extraction GUI backend is missing. Consequently, right-clicking a .zip or .tar.gz file 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-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.