Files
Nomarchy/agent/BACKLOG.md
Bernardo Magri d09c978b9b
All checks were successful
Check / eval (push) Successful in 3m16s
docs(agent): backlog codebase exam + per-theme design audit
Record PROPOSED findings from a full tree exam (installer, modules,
docs drift, CI) and a 24-theme design pass (contrast green; btop
fidelity, identity semantics, whole-swap gaps). No code changes —
await human triage into NOW/NEXT.
2026-07-09 08:46:13 +01:00

29 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

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.

Pre-existing

  • Battery charge-limit — make the toggle instant [blocked:hw] (follow-up to the preset toggle shipped 2026-07-07, iteration #55). The control-center now has a proper toggle (Off / 80% / 90% / 60% / Custom…) 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.

Bugs / broken behavior (codebase exam 2026-07-09)

  • Installer: swap "none" still creates a 0G swap subvolume nomarchy-install.sh always passes --argstr swapSize "${SWAP_GB}G". When the user chooses 0 (no swap), disko gets "0G", but disko-config.nix only treats the exact string "0" as no-swap — so it still creates @swap with size 0G. 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.nix nomarchy-doctor-status prints "text":"" on failure. Waybar custom modules with empty text self-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: bad when unhealthy; exit 0 / no output when healthy. Cost: one-line script fix; V2 with existing checks.doctor if extended, else V1.

  • neon-glass theme ships a broken Waybar whole-swap themes/neon-glass/waybar.css uses @text / @base / @accent / etc. with no @define-color block. 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 missing waybar.jsonc, preview.png, btop.theme; CSS covers only a subset of modules. Fix: either finish (self-contained colors + layout parity + preview) or remove waybar.css until 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.sh set_state always 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. If nomarchy.system.bluetooth.enable = false, blueman is not installed → silent/broken launch. Printers / colorpicker / audio gate correctly. Fix: gate on command -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.jsonc uses workspace e+1 / e-1 (next empty workspace); summer-night, executive-slate, boreal use r+1 / r-1 (relative). Likely leftover; behavior differs under scroll. Fix: align to r±1. Cost: two lines.

  • Battery charge-limit / powerprofile only match BAT* sysfs names power.nix oneshot and menu/waybar globs use /sys/class/power_supply/BAT*. nomarchy-battery-notify correctly treats any system battery (type=Battery, not scope=Device). Machines with CMB0 (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_PASSPHRASE installs unencrypted with only a log line — no fail-closed. test-install.sh always sets a passphrase, so the regression path is untested. Options: require the env var when unattended, or an explicit NOMARCHY_NO_LUKS=1. Cost: small policy change in install script + test matrix note.

Docs / option-surface drift (codebase exam 2026-07-09)

  • option-docs would fail: undocumented nomarchy.hardware.i2c.* Live options nomarchy.hardware.i2c.enable and .ddcci in modules/nixos/hardware.nix; distro default turns ddcci on (modules/nixos/default.nix mkDefault true). No README table rows, no template comments. Simulated checks.option-docs finds 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 says sudo tailscale up but module grants --operator= / template says no sudo; (5) hosts/live.nix header 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.nix desc = "Network (nmtui)" but the module runs networkmanager_dmenu. Cheatsheet inherits the wrong label. Cost: one string.

  • nomarchy-menu usage string incomplete Usage omits implemented modules (doctor, controlcenter, rollback, …). Misleading for direct invocation / scripting. Cost: one string in rofi.nix.

  • Template / seed state drift (1) templates/downstream/theme-state.json lacks border and settings keys present in repo root state (eval merges defaults — not a break, but new installs ship a thinner shape); (2) no commented nomarchy.system.snapper.enable or hardware.i2c.* in system.nix despite opt-in convention; (3) installer-generated home.nix is minimal (keyboard only) while flake init template ships a fat default package suite — product divergence worth documenting or aligning. Cost: small template/docs.

  • Secondary docs polish README layout tree omits many real modules and pkgs (doctor/control-center/battery-notify); docs/MIGRATION.md unlinked 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, and neon-glass lack preview.png (picker falls back to plain-name rows) and btop.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/powermenu and a left logo → main menu; the generated waybar.nix bar 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 / .recording polish 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.css defines every @color it 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) but themes/rose-pine/btop.theme mixes Dawn main_bg/main_fg with Main accents: hi_fg=#e0def4 on #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.theme and themes/summer-night/btop.theme set inactive_fg to #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 overlay and muted are near-white #fdfdfd while base is #0d0d0d. Generated consumers use overlay as selected_bg; hand btop has selected_bg=#fdfdfd + selected_fg=#ffffff (white-on-white) and inactive_fg=#fdfdfd (inactive rows scream). Status greys are intentional monochrome identity. Fix (keep void identity): surface #1a1a1a, overlay #2a2a2a, muted #666666; btop selected_bg #333 / selected_fg #fff / inactive_fg ≈ muted. Cost: JSON + btop; re-run contrast; V1V2.

  • osaka-jade: warn≈good and text/subtext inverted warn #459451 is green (same family as good #549e6a) → battery warning reads as "ok". subtext #F6F5DD is brighter than text #C1C497 (secondary louder than primary). Fix: swap text↔subtext; set warn #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) and ansi[7]/[15] are darkish → terminal "black"/"white" slots misbehave; design-audit flags inverted slots. mantle #cbcdd0 is import-darken of light base (should be Latte mantle #e6e9ef); accentAlt #d260b5 is not Latte pink/mauve. Fix:

    mantle #e6e9ef; accentAlt #ea76cb (pink);
    ansi[0] #5c5f77; ansi[7] #acb0be; ansi[8] #6c6f85; ansi[15] #bcc0cc
    

    warn keep #d6860a if 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.theme uses Mocha main_bg but 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-vpn in pill selectors; (4) day scroll e±1 already 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 + optional btop.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), raise surface #f0f0f0, split subtext #404040 while 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 subtext one 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 #ff5a7a while leaving ANSI matrix. Raise surface #1a1c2e (flat base=surface). Cost: product call + small JSON.

  • matte-black — identity: good=#FFC107 amber, warn=#b91c1c red (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 / #a65d4e without leaving the forest. Cost: optional.

  • gruvbox is Material medium, not classic — name + btop split JSON hexes are Gruvbox Material (text #d4be98, material green/yellow/red) while name says "Gruvbox" and btop.theme is 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 #e5e9f0 is brighter than text #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/title use warm #cfc9c2 instead 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 #00172e darker than base #05182e (inverted raise); good #028391 teal (reads info not ok). Fix: surface #0a2844; optional good #3f8f8a or phosphor green. Cost: small JSON; keep rofi.

  • ristretto — Monokai Pro Ristretto, ship-quality. Only subtext=text; btop main_bg 1-step drift (#2c2421 vs #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.py maps surface←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_compile only covers theme-sync .gitea/workflows/check.yml compiles only pkgs/nomarchy-theme-sync/nomarchy-theme-sync.py. Misses compose-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, disko swapSize/withLuks arg contract, and hardware-db module names vs nixos-hardware.nixosModules have no checks.*. 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 lsusb but package PATH has pciutils not usbutils (sysfs fallback usually OK); (2) chown -R 1000:100 on the flake dir assumes first-user UID/GID; (3) flake packages exports only theme-sync + install, not doctor / control-center / battery-notify (runtime via overlay is fine — discoverability only). Cost: small each.

  • Control Center /tmp/nomarchy-gens for rollback list Predictable world-writable path for generation listing — multi-user race / trash. Prefer mktemp. Cost: tiny.

Distro improvement ideas (codebase exam 2026-07-09)

  • Fail-closed / friendlier theme-state errors in mkFlake Missing theme-state.json fails later at readFile; invalid JSON fails at fromJSON before 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 use or); 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-doctor etc. for maintainers and CI convenience without going through modules. Cost: three lines in flake.nix.

  • Unattended-install test matrix test-install.sh always 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.

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.