All checks were successful
Check / eval (push) Successful in 4m12s
On LUKS machines the s2h auto-hibernate path resumes to a hyprlock screen despite the LUKS passphrase already gating resume — a second password. Root cause: s2h's hibernate phase runs inside systemd-suspend-then-hibernate.service and never activates hibernate.target, so nomarchy-unlock-before-encrypted-hibernate (wantedBy hibernate.target) never fires; and phase-1 already started hyprlock, which a live Wayland session-lock cannot safely drop. Direct systemctl hibernate is unaffected. Shares the s2h switch with #155; safe failure mode (never leaves unlocked). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
468 lines
28 KiB
Markdown
468 lines
28 KiB
Markdown
# Backlog — the prioritized task queue
|
||
|
||
**This is the only executable work list for agents.** Product themes and
|
||
v1.0 intent live in [`docs/VISION.md`](../docs/VISION.md); design history
|
||
in [`docs/ROADMAP.md`](../docs/ROADMAP.md); map in
|
||
[`docs/README.md`](../docs/README.md) and [`agent/README.md`](README.md).
|
||
|
||
**Rules:**
|
||
- Agents take the topmost actionable item (see LOOP.md). Finished items
|
||
are **deleted** here — the journal + git log are the record; durable
|
||
design notes get a ✓-entry in docs/ROADMAP.md (and/or a note in VISION)
|
||
if worth keeping.
|
||
- Item numbers are **stable IDs** — never renumbered or reused. A gap in
|
||
the sequence means shipped (or dropped) work; new items take the next
|
||
free number regardless of tier.
|
||
- Tags: `[blocked:hw]` needs real hardware (see HARDWARE-QUEUE.md) ·
|
||
`[human]` needs Bernardo · `[stuck]` two failed attempts, needs help ·
|
||
`[big]` must be split before starting.
|
||
- Agents may append to **PROPOSED** and **Decisions** freely (include
|
||
`VISION § …` or `ROADMAP § …` when relevant); only Bernardo moves items
|
||
*out* of PROPOSED into the tiers.
|
||
|
||
---
|
||
|
||
## NOW
|
||
|
||
### Live ISO / install hardware findings — Acer Aspire M5-481T + Dell XPS 9350
|
||
|
||
Bernardo, real installs 2026-07-13–14 (photos of the install end screens and
|
||
post-boot sessions). Preserve separation: the installer bake failure and the
|
||
flake pin are different root causes even when they show up on the same machine.
|
||
(Terminal / Ghostty-on-Acer → shipped as Kitty-only, #95.)
|
||
|
||
## NEXT
|
||
|
||
### 151. `[human]` v1 launch plan — devise with Bernardo (includes the GitHub move)
|
||
|
||
Bernardo, 2026-07-17: before tagging v1, sit down and devise the launch plan.
|
||
Known ingredients, so that session starts concrete:
|
||
- **The repo moves to GitHub.** Everything baking the Gitea URL follows: the
|
||
downstream template's `inputs.nomarchy.url`, the `nomarchy-wallpapers`
|
||
input URL (baked 2026-07-17 — sweep it in the same move), README/docs
|
||
links, and CI (eval CI runs on Gitea act_runner — decide GitHub Actions
|
||
vs keeping Gitea CI on a mirror).
|
||
- The move is the natural moment for the **history-rewrite decision**, and
|
||
it is now measured (2026-07-17): the 107 MiB pack is 94.2 MB wallpaper
|
||
blobs + 9.9 MB preview generations; all 848 commits of *text* pack to
|
||
~15–20 MiB. So the history is not long-heavy, it is image-heavy.
|
||
Recommendation on the table: **filter, don't reset** —
|
||
`git filter-repo` stripping only `themes/*/backgrounds/` keeps every
|
||
commit's diffs/blame/`log -S` (load-bearing: it diagnosed #147) at a
|
||
~15–20 MiB clone. Cost: all SHAs change, and ROADMAP/journal cite them —
|
||
mitigate by freezing the Gitea repo as a read-only archive (old SHAs
|
||
resolve there; stash filter-repo's commit-map in it) + one ROADMAP note.
|
||
Argue against a full mirror (ships 94 MB of deleted images to every
|
||
clone forever) and against fresh-start (burns the archaeology to save
|
||
~15 MiB).
|
||
- The existing v1 bar (VISION § v1.0): HARDWARE-QUEUE burn-down, install P0
|
||
re-verify before the `v1` fast-forward, visual ritual. LICENSE shipped
|
||
(MIT, 2026-07-17).
|
||
- Adjacent calls that may fold in: cachix/public binary cache (#120's
|
||
prerequisite), the netinstall decision itself, release notes/announce.
|
||
|
||
`v1` stays human-only throughout — agents prepare evidence and checklists,
|
||
never the tag or branch.
|
||
|
||
## LATER
|
||
|
||
- **Pre-built theme variants** (ROADMAP § Faster switches follow-on): if
|
||
`home-manager switch` itself is still the theme-switch bottleneck now
|
||
that the wallpapers split shipped (2026-07-17), pre-build each theme's
|
||
generation so a switch just activates a cached one. Measure first.
|
||
- **Installer round 2** (ROADMAP § Installer): multi-disk BTRFS RAID,
|
||
impermanence, BIOS/legacy boot.
|
||
- **Boot-from-snapshot**: a systemd-boot equivalent of grub-btrfs.
|
||
- **MIPI/IPU software-ISP camera** support (no-UVC machines).
|
||
- **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.
|
||
|
||
## FUTURE (decided deferred — not the agent queue head)
|
||
|
||
Work we **intend** someday but explicitly **not** NEXT. Agents do not
|
||
pick these unless Bernardo promotes one into NEXT/NOW.
|
||
|
||
### 20. KVM runner → VM suite in CI `[human]`
|
||
**Status (2026-07-10):** keep **eval-only** CI on the current Gitea
|
||
stack (act_runner in docker-compose on the 4c/4 GB IONOS VPS). Nested
|
||
KVM + RAM headroom on that host are a poor fit next to Gitea; full
|
||
`checks.*` VMs stay local / promotion-time until a **separate**
|
||
KVM-capable machine exists.
|
||
|
||
**When ready:** register a second runner (host-mode nix + `/dev/kvm`,
|
||
label `nix-kvm` — not the existing docker eval runner), then uncomment
|
||
the `vm-checks` job in `.gitea/workflows/check.yml` (`runs-on: nix-kvm`,
|
||
`nix flake check` + toplevel/HM builds). Do not enable the job until
|
||
that label is online (Gitea queues forever otherwise).
|
||
|
||
### Formatter — adopt later `[human]`
|
||
**Intent:** add a Nix formatter (likely `nixfmt-rfc-style`) in a dedicated
|
||
pass: reformat the tree once, document in CONVENTIONS, optional CI
|
||
check. **Not** the queue head — no drive-by reformats until that pass.
|
||
|
||
## 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.*
|
||
|
||
*Open work only. Shipped exam/A–C items (#47–#63, #14, #52 theme
|
||
high-ROI, etc.) live in the journal + ROADMAP — not here.*
|
||
|
||
### Product / day-2
|
||
|
||
### 153. Desktop widgets (clock / system stats) as an opt-in per-theme surface
|
||
|
||
Pitch (filed while shipping the three exclusive theme pairs, 2026-07-18):
|
||
themed desktop widgets — a clock, calendar, or system-stats panel living on
|
||
the wallpaper, conky-style — would deepen the identity themes (imagine
|
||
kiln's copper stats panel or executive-ivory's letterhead clock). No such
|
||
surface exists today, so it is a NEW theming surface: per CONVENTIONS it
|
||
must consume the palette from the state JSON (no side pipeline) and ship
|
||
as an opt-in `nomarchy.*` toggle with a commented template example. Engine
|
||
choice is the real decision — conky is X11-era; a Wayland layer-shell
|
||
widget system (eww or a small QML/GTK layer-shell tool) fits the stack
|
||
better. Cost: moderate (engine spike + palette bridge + 2–3 widget
|
||
layouts; start with the exclusive pairs only, not all 28 themes). Value:
|
||
v1+ polish, not a v1 blocker.
|
||
|
||
### 155. Hibernate can crash on AMD APUs (amdgpu/TTM NULL deref) — and `oops=panic` makes it catastrophic
|
||
|
||
Filed 2026-07-24 from a root-caused investigation on the dev box (TuringMachine,
|
||
T14s Gen 4, AMD APU). Hibernate crashes **intermittently on entry**: a kernel
|
||
NULL-pointer deref in amdgpu/TTM's hibernation GPU-eviction path
|
||
(`ttm_resource_manager_next` ← `ttm_bo_swapout` ← `ttm_device_prepare_hibernation`
|
||
← `amdgpu_pmops_freeze`), captured in pstore for the 2026-07-23 22:49 crash
|
||
(`Kernel panic - not syncing: Fatal exception`). It is **intermittent** (07-19..07-23:
|
||
three clean `hibernation exit` successes vs two entry crashes, on both kernel 7.1.3
|
||
and 7.1.4) because it depends on which GPU buffers are resident at freeze time — so
|
||
**not** a misconfig (offset/`resume=`/swapfile are correct), **not** hardware, **not**
|
||
the fingerprint reader (see #156; absent from the trace). Confirmed a **known upstream
|
||
bug class**: uninitialized TTM resource manager on APUs with no dedicated VRAM
|
||
(`ttm_device_prepare_hibernation()` is a young 2025 AMD API; NULL-check fixes are
|
||
landing on amd-gfx/dri-devel, but for the *sysfs* `_usage` sibling, not yet
|
||
confirmed for this hibernation `_next` site → reportable). **Kernel-source audit
|
||
(2026-07-24, vanilla 7.1.4 from the store):** 7.1.4 already carries the mid-2025
|
||
"move swapped objects off the LRU" rework (`ttm_resource_is_swapped`, the
|
||
`unevictable` list) — so that known bug is *not* the cause — but it **lacks the later
|
||
NULL-guard hardening**: `ttm_resource_manager_next()` has no `!man` guard (its sibling
|
||
`_first()` does), and the swapout walk `__ttm_bo_lru_cursor_next()` deliberately drops
|
||
+ re-acquires `lru_lock` (its own comment warns the resource "may have been freed and
|
||
allocated again" in that window) before calling the unguarded `_next` → a TOCTOU
|
||
NULL/UAF deref. So the fix is **not** in 7.1.4 — and there is **no in-pin kernel
|
||
escape**: the dev box is already on `latestKernel` (7.1.4 = newest in the 26.05 pin;
|
||
default = 6.18.39), and a 2026-07-24 source check shows the **default 6.18.39 carries the
|
||
same unguarded path** (`ttm_device_prepare_hibernation`, unguarded
|
||
`ttm_resource_manager_next`, the same drop/re-lock race) — so downgrading off
|
||
`latestKernel` does **not** help. Only a future *patched* release (lock advance) or a
|
||
local kernel patch fixes it kernel-side.
|
||
|
||
**Two separable problems.** (1) *The kernel bug* — not Nomarchy-fixable; track
|
||
upstream, report with the pstore trace + DMI (**filed upstream by email to amd-gfx,
|
||
2026-07-24**), and re-test only when a *patched* release lands via a lock advance (both
|
||
shipped kernels are affected — see above). (2) *The blast radius, which IS ours* — Nomarchy's
|
||
`panic=10 oops=panic` boot default turns a **recoverable** suspend-time driver oops
|
||
(kernel would abort hibernation and thaw back to a live session) into a **guaranteed
|
||
reboot + total session loss**. That penalty is inherited by every machine's
|
||
suspend/hibernate, not just this APU. Nomarchy-actionable slice: make the sleep path
|
||
**fail-soft** — scope/relax `oops=panic` so a device-suspend oops aborts the sleep and
|
||
keeps the session, while `panic=10` still auto-reboots genuine panics; weigh against
|
||
the deliberate stability posture of `oops=panic` (don't remove it blindly). Fits
|
||
**VISION § Theme C** (hibernate/sleep confidence); consider a doctor caveat for APU
|
||
hibernate. Immediate per-host mitigation (separate from the distro fix): disabling
|
||
`suspend-then-hibernate` on affected APU hosts stops the *auto-triggered* crash.
|
||
Cost: fail-soft is a `boot.kernelParams` scoping decision (care needed); upstream
|
||
report is cheap.
|
||
|
||
**Scope (who's affected).** Not machine-specific and not hardware failure — a kernel
|
||
software bug, so any host running the affected code is exposed. The class is **AMD GPU
|
||
(esp. APUs / no discrete VRAM) + hibernation (S4) + a kernel in this new-TTM-eviction
|
||
window**; Intel/Nvidia users and users who never hibernate are unaffected. **Both**
|
||
shipped kernels carry it (default 6.18.39 and latest 7.1.4), so it is *not* limited to
|
||
`latestKernel` opt-ins. Nomarchy's own defaults steer the exposed class straight into
|
||
it: the installer sets up a hibernate swapfile + **suspend-then-hibernate** on laptops
|
||
(auto-hibernate after 1h on battery) and `oops=panic` turns each hit into a reboot — and
|
||
that intersects the **VISION north-star user** on the AMD side (modern AMD laptop = APU).
|
||
So this is broad product exposure, not a one-off. Caveats: the exact affected
|
||
kernel-version *range* is not mapped, and the APU-vs-discrete split is from the upstream
|
||
discussion, not reproduced on other hardware.
|
||
|
||
### 156. Synaptics fingerprint reader (`06cb:00f9`) resets every ~5 min
|
||
|
||
Filed 2026-07-24, noticed while diagnosing #155 (unrelated to those crashes — nowhere
|
||
in the trace). On the dev box the fingerprint reader (USB `1-5`, Synaptics
|
||
`06cb:00f9`) logs `usb 1-5: reset full-speed USB device number 3 using xhci_hcd`
|
||
every ~5 minutes for hours, and `fprintd.service` is started→`Deactivated
|
||
successfully` on the same 5-min cadence — i.e. *something polls fprintd every 5 min*
|
||
and the device needs a reset each time. Cosmetic-ish, but it is needless
|
||
wakeups/power draw and a flaky device that won't quiesce is a latent suspend-hang
|
||
risk. Look into: what polls fprintd on that interval (hyprlock fingerprint backend
|
||
from `nomarchy.idle.fingerprint`? a geoclue/other watcher?), and whether the reset is
|
||
avoidable (autosuspend quirk, or gating the poll). Cost: investigation only; low.
|
||
|
||
### 157. Redundant hyprlock on suspend-then-hibernate resume (encrypted disks)
|
||
|
||
Filed 2026-07-24 (dev box, reported by Bernardo + journal-confirmed). On a LUKS
|
||
machine, resuming from the **suspend-then-hibernate** path lands on a **hyprlock** screen
|
||
even though the LUKS passphrase already gated the resume — a second password. The design
|
||
intent (idle.nix + `nomarchy-{lock,unlock}-before-*` in `modules/nixos/default.nix`) is
|
||
exactly "no hyprlock on encrypted hibernate — LUKS is the gate"; it holds for a **direct**
|
||
`systemctl hibernate` but **not** for s2h.
|
||
|
||
**Root cause (two layers).** (1) s2h's hibernate phase runs *inside*
|
||
`systemd-suspend-then-hibernate.service` and **never activates `hibernate.target`** —
|
||
journal-confirmed: at the phase-2 transition (`Performing sleep operation 'hibernate'`
|
||
from the same `systemd-sleep` PID) there is no target, no "Starting System Hibernate",
|
||
no unit/`system-sleep` hook at all. So `nomarchy-unlock-before-encrypted-hibernate`
|
||
(`wantedBy = hibernate.target`) never fires for s2h, whereas direct hibernate *does*
|
||
reach the target and unlock. (2) The deeper problem: phase-1 (the RAM suspend) *must*
|
||
lock (`nomarchy-lock-before-sleep` is `wantedBy` `suspend-then-hibernate.target`), so
|
||
**hyprlock is already up** before phase-2 — and a live Wayland session-lock **cannot be
|
||
safely dropped** (idle.nix: killing the locker trips hyprlock's "go to a tty" failsafe).
|
||
The whole unlock-before-hibernate design only works because for direct hibernate hyprlock
|
||
is *never up*; s2h violates that premise, and there is no systemd event between the two
|
||
phases to un-ring the bell.
|
||
|
||
**Scope.** Encrypted disk + the s2h auto-hibernate path only (the default idle path on
|
||
battery). Direct `systemctl hibernate`/Power ▸ Hibernate is fine. Not a security hole —
|
||
the redundant lock is the *safe* failure (never leaves you unlocked).
|
||
|
||
**Options (all have a cost).** (a) Accept it — safe default, one extra password.
|
||
(b) Skip locking on s2h for encrypted — removes the redundant lock but leaves the ≤1h
|
||
RAM-suspend window **unlocked** (real exposure; advise against). (c) A **safe external
|
||
hyprlock-dismiss** — the only clean fix, gated on whether current hyprlock exposes an
|
||
IPC/socket to unlock without the tty-failsafe (needs investigating; may not exist).
|
||
|
||
**Intersection:** shares the s2h switch with **#155** — flipping
|
||
`nomarchy.system.power.suspendThenHibernate = false` on a box makes *both* the redundant
|
||
lock and the #155 crash exposure disappear (cost: no auto-hibernate-on-idle). Positioning:
|
||
correctness gap in a shipped v1 feature (**VISION § Theme C**, hibernate/sleep confidence).
|
||
Files: `modules/nixos/default.nix` (~257–286), `modules/home/idle.nix` (hypridle: no
|
||
`unlock_cmd`; the design comment).
|
||
|
||
### 146. `[watch]` hypridle hangs instead of exiting when its compositor vanishes
|
||
|
||
**Re-measured 2026-07-20 (dev box; user journal is persistent back to 2026-05-01,
|
||
so this is the full window, not a fragment).** The linger fix landed between 17:25
|
||
and 20:13 on 2026-07-16: the last zombie-shaped disconnect is the known one
|
||
(17:25:39, PID 1698 — logout→greeter with **no** following `Stopped hypridle`), and
|
||
**every** `Disconnected from pollfd id 1` since is a clean teardown bracketed by both
|
||
a logind `Session N logged out` and a systemd `Stopped hypridle` within ≤11s
|
||
(07-16 20:13:04, 07-17 17:04:38, 07-18 13:35:25, 07-19 16:46:49). **Genuine zombies
|
||
post-fix: 0.** The live daemon is healthy too — PID 1707: `active`, `NRestarts=0`,
|
||
`ExecMainPID`==pid, `ss` Recv-Q 0 on its D-Bus socket (reading, not the 48 KB-unread
|
||
wedge), `WCHAN futex_do_wait`, logged activity <10 min before inspection. **Why this
|
||
stays open a little longer, not closed today:** all four post-fix disconnects were
|
||
*deliberate* logouts — no Hyprland *crash* (the zombie's actual trigger; the nearby
|
||
coredumps were xdg-desktop-portal/swww/swayosd during teardown, not the compositor)
|
||
has occurred since the fix, so the crash path is confirmed by argument (a crash ends
|
||
the logind session too → no-linger manager teardown reaps hypridle) but not yet by an
|
||
observed crash, and ~4 days is under the "few weeks" bar. **Close condition:** if
|
||
genuine zombies stay 0 by ~2026-08-10 (≈3 weeks post-fix), close unbuilt; one wedged
|
||
process (`ps` alive + `ss` Recv-Q high + `NRestarts=0` while `active`) reopens it as
|
||
real work. Everything below is prior context, unchanged.
|
||
|
||
**Rewritten 2026-07-16, hours after filing: the premise was disproven and almost
|
||
nothing survives.** As filed it said hypridle "dies silently ~2×/week — 19 times
|
||
in 11 days", with Jul 14 as the clean example ("`CRITICAL` at 13:08:17, then
|
||
eleven hours of silence"). Every number there was an artifact of the
|
||
measurement. **18 of the 19** `Disconnected from pollfd id 1` lines are
|
||
hypridle's noisy goodbye while systemd *deliberately stops it* — each bracketed
|
||
by `Stopped hypridle` / `Started hypridle` with a healthy replacement seconds
|
||
later. The "eleven hours of silence" came from grepping **one PID**: systemd
|
||
logged `Stopped hypridle` 13:08:17, `Started hypridle` 13:09:00, and PID 1678
|
||
ran fine all evening where the filter could not see it. Do not re-derive the old
|
||
rate; it counts normal shutdown noise.
|
||
|
||
**What is real, and it is small.** Exactly one genuine zombie has been observed
|
||
(2026-07-16 17:25:39), verified by process inspection rather than logs — `ps`
|
||
(alive 9h36m), `ss` (48 KB unread on its D-Bus socket, i.e. not reading),
|
||
`NRestarts=0` while `systemctl --user is-active` said `active (running)`. That
|
||
is upstream hyprwm/hypridle#171 ("Possible deadlock in pollThr when compositor
|
||
exits?", open since 2025-09, in neither 0.1.7 nor main) and it is a true bug:
|
||
hypridle hangs rather than exits, so `Restart=always` never fires.
|
||
|
||
**A second real one joined it with #127's close (2026-07-17):** an
|
||
inhibit-lock underflow (`BUG THIS: inhibit locks < 0: -1`, hypridle's own
|
||
words, Jul 15 10:36:29) left the fired DPMS listener's `on-resume` permanently
|
||
dead on 0.1.7 — the incident's actual mechanism (ROADMAP § "The idle brick's
|
||
CAUSE found"). Reported upstream with the full trace 2026-07-17:
|
||
https://github.com/hyprwm/hypridle/issues/208
|
||
(#74/#104/#128 are the same class, closed before 0.1.7 evidently without
|
||
killing it). Its impact path here is closed by the compositor-side wake fix
|
||
(a2151f4); same watch posture — take the upstream fix when a release carries
|
||
it.
|
||
|
||
**But its impact path is closed, which is why this is `[watch]` and not work.**
|
||
The zombie only *persists* if systemd never stops the unit — which needed the
|
||
user manager to survive logout, i.e. the stray pre-migration linger removed in
|
||
#147. Without linger, logout tears the manager down and takes any zombie with
|
||
it; the Jul 14 trace above shows the healthy path (`Stopped` → `Started`) doing
|
||
exactly that. A Hyprland death ends the logind session anyway (2026-07-16:
|
||
`Session 2 logged out` → greeter), so there is no known route left to a
|
||
surviving zombie.
|
||
|
||
**Do before building anything:** re-measure. Count only disconnects with **no**
|
||
`Stopped/Started hypridle` around them and **no** logind session event, and
|
||
confirm by `ps`/`ss` that the process is really wedged — not by grepping a PID.
|
||
If that count is zero over a few weeks post-linger, **close this unbuilt**. If it
|
||
is not, the upstream issue has sat ten months on vaxerski's "that will cause a
|
||
segfault no?" and a reproducer would be worth more than a local guard.
|
||
|
||
### 149. `[watch]` Waybar's exec-once workaround may be defending against nothing post-linger
|
||
|
||
Spun out of #147 (shipped 2026-07-17) so its loose thread survives the entry:
|
||
`modules/home/hyprland.nix` runs Waybar from `exec-once` rather than a systemd
|
||
unit because the unit "raced Hyprland's IPC on a **warm** relogin ... never
|
||
retried, so the bar vanished" — and a warm relogin is exactly what linger
|
||
created. With the stray linger removed (2026-07-16) that workaround may be
|
||
defending against nothing. Do not rip it out on this note alone: it works, and
|
||
the claim needs a real relogin without linger to test (V3 — the same relogin
|
||
that verifies #148's watcher fix would answer it).
|
||
|
||
**2026-07-18 evidence: the warm relogin is real post-linger.** On the dev
|
||
box the user manager survived a Hyprland logout (the tty1 greetd session
|
||
keeps it alive — no linger involved) and the teardown broke:
|
||
`graphical-session.target`'s stop transaction was rejected as destructive
|
||
(easyeffects had a queued start job), the target stayed active,
|
||
cliphist/swaync/portals/swayosd crash-looped against the dead Wayland
|
||
socket into `start-limit-hit`, and the relogin's plain `start` was a no-op
|
||
— doctor red for the whole session. Session bring-up now stops stale
|
||
targets + `reset-failed` before starting (`hyprland.nix`
|
||
systemd.extraCommands); relogin V3 check queued in HARDWARE-QUEUE. The
|
||
exec-once posture this entry watches is therefore still earning its keep.
|
||
|
||
### 120. A netinstall ISO, next to the fat offline one
|
||
|
||
**Deferred to PROPOSED 2026-07-16 (Bernardo): not now.** Nothing below is
|
||
stale — the measurements stand and the decision it needs is unchanged. It sits
|
||
here because it is the only genuinely large item left, and because the gotcha
|
||
at the bottom (no binary cache for our own derivations) probably makes a
|
||
cachix the real first step, not the ISO.
|
||
|
||
Bernardo 2026-07-14, after seeing the measured size: **keep the current ISO
|
||
exactly as it is** — the guaranteed offline install is the feature it buys —
|
||
and ship a **much lighter netinstall variant alongside it**. Two products, one
|
||
distro: "works on a plane" and "8 GiB is absurd to download" are both true, and
|
||
a second target settles them without compromising either.
|
||
|
||
**Measured facts (2026-07-14), so this starts from numbers, not vibes.**
|
||
*(These stand as measured: #121 would have cut ~0.67 GiB of duplicate chromium
|
||
from them, but it was **reverted** — decided against, ROADMAP § one chromium,
|
||
not two. If a netinstall ships, revisit it: the duplicate is worth ~195 MiB of
|
||
**download**, which is this item's whole currency, even though it is worth
|
||
almost nothing on disk or on the ISO.)*
|
||
|
||
> **Read this before using the numbers below.** They are **closure arithmetic**,
|
||
> and #121 proved the hard way that closure size is neither disk size nor image
|
||
> size: removing a 687 MiB path shrank the ISO by **8 KiB**, because
|
||
> **mksquashfs dedupes duplicate files** and **`auto-optimise-store` hardlinks**
|
||
> them on disk. So a change that looks like it sheds gigabytes of closure can
|
||
> shed nothing off the actual image. **Measure the artifact — build the ISO and
|
||
> `stat` it.** The corollary cuts the other way and is the good news for this
|
||
> item: what dedupe cannot help is the **wire**, so a netinstall's download is
|
||
> the one figure closure/NAR size predicts honestly (`nix path-info --store
|
||
> https://cache.nixos.org --json` gives the real `downloadSize`).
|
||
- Current ISO **8.078 GiB** compressed; **18.03 GiB** of store uncompressed
|
||
(`zstd -19`, 2.23:1 — compression is already near-max, not the lever).
|
||
- The offline pin (`system.extraDependencies`, 60 roots: a representative
|
||
installed system + the template HM closure + all flake inputs) is **4.02 GiB
|
||
uncompressed of that — only ~22%**. Dropping it entirely still leaves a
|
||
**~13.3 GiB** desktop → roughly **6 GiB** compressed at the same ratio.
|
||
**So "no pin" alone is NOT the lighter ISO** — this is the trap to avoid.
|
||
- The desktop's own top weights: libreoffice 1457 MiB, initrd 1369,
|
||
linux-firmware 770, chromium 1391 (two builds — #121, left in), llvm-lib 540,
|
||
bibata-cursors 322, mesa 264, mbrola-voices 259, nerd-fonts ~420 combined.
|
||
Note what that list implies: no single lever gets a desktop ISO under ~4 GB —
|
||
which is the case for (b) below.
|
||
|
||
**So the real decision is what a netinstall ISO IS**, and it should be settled
|
||
first (`[human]`): (a) the full try-before-install desktop minus the pin
|
||
(~6.3 GiB — barely lighter, probably not worth a second target); (b) a **TUI
|
||
installer only, no desktop** (~1 GiB, the actual "netinstall" in the Debian
|
||
sense) which drops "try before install" from that medium — the fat ISO still
|
||
offers it; (c) a middle desktop (no libreoffice/chromium — but note #103 just
|
||
put those there deliberately, and a *demo* desktop that can't browse is the
|
||
bug #103 fixed).
|
||
|
||
**The gotcha that decides feasibility:** without the pin, a netinstall target
|
||
fetches from `cache.nixos.org` for stock nixpkgs paths — but **Nomarchy's own
|
||
derivations are in no binary cache**, so they would build *from source on the
|
||
user's machine* during install. That is the same failure `tools/vm/gap-analysis.py`
|
||
exists to diagnose (and #113 is a live instance of). So this item probably
|
||
depends on a public binary cache (cachix) for the flake's own outputs, or it
|
||
trades an 8 GiB download for a 40-minute install. Establish that before
|
||
building the target.
|
||
|
||
Pass = a second, documented ISO target that is *substantially* smaller (state
|
||
the measured number, both ISOs built from one tree), installs successfully with
|
||
a network in a QEMU run, says clearly at boot that it needs one, and leaves the
|
||
offline ISO's behaviour untouched (`checks.*` for the offline path stay green).
|
||
- **NVIDIA first-class options** — **deferred past v1** (Bernardo
|
||
2026-07-10). Keep #59 commented install guidance; no
|
||
`nomarchy.hardware.nvidia.*` until a hybrid maintainer + queue.
|
||
|
||
_(#80–#83 + #85–#88 shipped 2026-07-11. Theme A day-2 + neon-glass finish
|
||
shipped — VISION ✓. Dock/hibernate V3 → HARDWARE-QUEUE. Parallel
|
||
fingerprint-or-password shipped 2026-07-12 (Bernardo promoted it live;
|
||
`fingerprint.parallel`, pam-fprint-grosshack) — reader V3 →
|
||
HARDWARE-QUEUE.)_
|
||
|
||
### v1.0 pointer
|
||
|
||
See **VISION**. Open PROPOSED: NVIDIA deferred past v1; IR portal (b)/(c)
|
||
need T14s (HARDWARE-QUEUE § T14s). Standing calls: browser = Chromium;
|
||
power = PPD. (Post-install hardware hints shipped 2026-07-17.)
|
||
|
||
## Decisions `[human]`
|
||
|
||
Open calls only Bernardo can make; agents add options/evidence but never
|
||
decide. **Resolved** entries stay for history; agents treat them as closed.
|
||
|
||
### Resolved (2026-07-17)
|
||
|
||
- **#143 rofi refilter highlight** — the 07-16 "wait for upstream" ruling
|
||
was priced against carrying a patch; the maintainer's reply on
|
||
rofi#2317 pointed at the `inputchange {}` config block instead. Adopted
|
||
same day as pure config (`kb-row-first` on query edits, generated
|
||
config.rasi) — no patch, no source build, watch closed
|
||
(ROADMAP § Rofi highlight).
|
||
- **#120 netinstall** — stays deferred in PROPOSED (re-affirmed). When it
|
||
is promoted, a public binary cache (cachix) is the likely first step.
|
||
- **#134 unstable packages** — build it: Nomarchy carries the
|
||
`nixpkgs-unstable` input, `unstable.*` via the overlay, home-scope only.
|
||
Shipped the same day (ROADMAP § `unstable.<pkg>` in the downstream).
|
||
- **#114 greeter layouts** — document only: a VT has one keymap by design,
|
||
so tuigreet cannot honour per-device layouts. Shipped as the README
|
||
"Greeter keyboard layout" note + a RECOVERY.md pointer; entry deleted.
|
||
|
||
### Resolved (2026-07-10)
|
||
|
||
- **Docs site vs Markdown-in-repo** — **markdown in-repo for now**
|
||
(`docs/`, README). A rendered docs site is FUTURE if wanted.
|
||
- **Default browser** — **ship Chromium** in
|
||
`templates/downstream/home.nix`; mime → `chromium-browser.desktop`.
|
||
Opt out: delete the line / override mime.
|
||
- **Default power backend** — **keep PPD** (`nomarchy.system.power.backend`
|
||
default). TLP remains the one-line opt-in. Rationale: stability + live
|
||
profile API for menu/Waybar; Omarchy’s TLP experiment reverted.
|
||
|
||
### Resolved (2026-07-10, more)
|
||
|
||
- **Formatter adoption** — **yes, but not now.** Tracked as FUTURE
|
||
(below). Nix-source style only (`nixfmt-rfc-style` or similar); one
|
||
bulk reformat + CI/check when promoted. Until then: hand-aligned
|
||
style per CONVENTIONS.
|
||
|
||
- **Hibernation** — **want by default** (product intent). Needs a
|
||
disk-backed swap (file or partition) sized for resume; not zram alone.
|
||
**Shipped as #76**; V3 power-cycle PASSED on TuringMachine 2026-07-12
|
||
(ROADMAP § Hibernation + zram by default).
|
||
|
||
### Resolved (2026-07-10, #76 design)
|
||
|
||
- **Swap sizing** — **exactly RAM** (installer default, unchanged). Hibernate
|
||
image ≤ RAM; zram takes day-to-day paging. **`swapSize=0`** stays no-swap.
|
||
- **Migration** — **docs runbook** (`docs/MIGRATION.md`), not a tool.
|
||
- **No-swap Hibernate** — keep the menu row; **notify on failure**.
|