All checks were successful
Check / eval (push) Successful in 3m30s
The "blank + hypridle deadlock = the brick" cause published this morning is retracted everywhere it was asserted: ROADMAP, hyprland.nix's comment, #146, HARDWARE-QUEUE and two commit messages (a2151f4,321ccf7). It rested on two pieces of evidence and both were artifacts of how I measured: - "19 disconnects in 11 days": 18 of 19 are hypridle's noisy goodbye while systemd DELIBERATELY stops it — each bracketed by `Stopped hypridle` / `Started hypridle`, healthy replacement seconds later. - "Jul 14: CRITICAL at 13:08:17, then eleven hours of silence": an artifact of grepping ONE PID. systemd logged `Stopped hypridle` 13:08:17 and `Started hypridle` 13:09:00; PID 1678 ran fine all evening, invisible to the filter. Decisive: at the incident (before the 12:51:34 mitigation commit) hypridle had NO disconnect at all and had been healthy since Jul 14 13:09. It should have fired on-resume normally. The deadlock theory never had a case. The FIX stands on its own evidence and is untouched: the options really were off, hypridle's on-resume really was the only caller of `dpms on`, and input now wakes the screen with hypridle deliberately stopped (4s docked / 6s clamshell). Removing a proven single point of failure never needed a story about why the daemon might fail — which is exactly why it survived the story being wrong. #127 is RESTORED to NOW (deleted this morning as "shipped"; only the fix shipped). Its cause is open and the lead is the dead Ctrl+Alt+F3 — neither DPMS-off nor a dead hypridle can break VT switching, so something was wedged below the compositor holding DRM master. Every theory so far has been about the wake path, i.e. a symptom of the screen already being black. #146 → [watch], near-dead: one genuine zombie ever (verified by ps/ss, not grep); upstream #171 is real but its impact path closed when #147's stray linger went. Re-measure properly before building anything; close unbuilt if zero. Also closes the #127 hardware-queue entry: Bernardo ran the real 10-min idle — 19:27:58 dpms off (genuine timeout, shipped config, and it BLANKED = the revert working) -> mouse -> 19:32:14 `done enabled=1 first=DP-2`, clamshell, the incident geometry, one healthy hypridle throughout. All 4 steps pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
483 lines
27 KiB
Markdown
483 lines
27 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.)
|
||
|
||
### 127. `[blocked:hw]` The idle brick's CAUSE — reopened; the fix shipped, the diagnosis did not
|
||
|
||
**Restored 2026-07-16, same day it was deleted.** It was deleted as shipped on
|
||
the strength of a diagnosis that was retracted hours later. The **fix** is real,
|
||
proven and stays (`misc:{key_press,mouse_move}_enables_dpms`, a2151f4): the wake
|
||
path had a genuine single point of failure — hypridle's `on-resume` was the only
|
||
caller of `dpms on` in the session — and it is gone. Verified end-to-end on the
|
||
incident hardware, including a real 10-min idle blanking and waking clamshell
|
||
(`done enabled=1 first=DP-2`, 19:32). **None of that explains the brick.**
|
||
|
||
**What the incident was:** docked clamshell, idle → black, external keyboard and
|
||
mouse woke nothing, unplugging the external did not light the panel, Caps Lock
|
||
LED still toggled (machine alive), and **Ctrl+Alt+F3 gave no usable TTY**.
|
||
|
||
**What has been ruled OUT (all on hardware, 2026-07-16):**
|
||
- *DPMS-off is not the trigger.* With hypridle driving the blank, input wakes it
|
||
in every geometry — undocked lid-open, clamshell-docked on the sole live
|
||
output, docked with both outputs live. The clamshell skip was reverted on this.
|
||
- *The hypridle deadlock is not the trigger.* This was the published cause and it
|
||
was wrong: at the incident (before 12:51:34 on Jul 15) hypridle had **no
|
||
disconnect at all** and had been healthy since Jul 14 13:09. The "19 in 11
|
||
days" rate that made the theory look strong was normal-teardown noise
|
||
(see #146). It should have fired `on-resume`.
|
||
- *The #100 undock latch* explains symptom 2 (unplugging not restoring the panel)
|
||
and is fixed (ff5017e) — but not the blackout itself.
|
||
|
||
**The lead is Ctrl+Alt+F3, and it always was.** Neither DPMS-off nor a dead
|
||
hypridle can break VT switching; a wedged compositor still holding DRM master
|
||
can, and it would explain the black screen *and* the dead TTY in one stroke.
|
||
Every theory so far has been about the wake path — i.e. about a *symptom of the
|
||
screen being black* — and none of them touch the TTY. Start there instead.
|
||
Bernardo does not recall whether the machine had been relogged-in between Jul 14
|
||
13:09 and the incident; that would say whether hypridle was ever a suspect.
|
||
|
||
**Note the fix makes this much harder to reproduce** (input now wakes the screen
|
||
compositor-side), which is a good trade but means a repro may need the options
|
||
temporarily off. It has not recurred since.
|
||
|
||
## NEXT
|
||
|
||
### 143. Rofi keeps the selected *row number* when the search changes, so the highlight lands on an unrelated entry
|
||
|
||
Bernardo 2026-07-16, and his repro is exact: open the app menu, type `ca`, move
|
||
the selection two items right (onto ChatGPT), press backspace once (query is
|
||
now `c`). The list re-expands — first entry is now Slack — but the highlight
|
||
stays on the **third row**, which is now GHex, an app that has nothing to do
|
||
with what he typed. Enter at that moment launches the wrong thing.
|
||
|
||
**Cause — upstream rofi (2.0.0, `source/widgets/listview.c`), not our config.**
|
||
A filter change ends in `listview_set_num_elements()`, which does not reset the
|
||
selection; it re-asserts the old one:
|
||
|
||
```c
|
||
lv->req_elements = rows;
|
||
listview_set_selected(lv, lv->selected); /* the *previous* index */
|
||
```
|
||
|
||
and `listview_set_selected()` only clamps it to the new bounds:
|
||
|
||
```c
|
||
lv->selected = MIN(selected, lv->req_elements - 1);
|
||
```
|
||
|
||
So what is preserved is the **row number**, not the entry — nothing checks that
|
||
row 2 still holds the thing that was selected, or that it matches the query at
|
||
all. Typing *more* hides this (the list shrinks, the clamp usually drags the
|
||
highlight along); **backspacing exposes it**, because the list grows back
|
||
underneath a stationary index. There is no option for it: `-dump-config` has
|
||
nothing about selection-on-refilter, and `auto-select` is a different feature
|
||
(it fires only when exactly one row survives).
|
||
|
||
**One correction to the report:** it is not the app menu. `listview` is the
|
||
widget behind **every** rofi surface we ship, so our text menus (Recovery,
|
||
System, Preferences…) do the same. It is merely far more visible in drun, where
|
||
the list is long and — because we set `sort = true` — reorders on every
|
||
keystroke, so a stationary index points somewhere new each time. In a short
|
||
text menu you would probably see it and correct without noticing why.
|
||
|
||
**Reported upstream 2026-07-16: https://github.com/davatorium/rofi/issues/2317**
|
||
(open, filed as `bemagri`, with the 2.0.0 line numbers and the observation that
|
||
`rofi_view_set_selected_line` already maps a stable id through `line_map` — so
|
||
the fix reuses an existing primitive rather than inventing one). No patch was
|
||
sent: the scoping call is the maintainer's, since keeping the index across a
|
||
*reload* looks deliberate (#1064 asked for exactly that, and got it in 1.7.4).
|
||
No duplicate found before filing.
|
||
|
||
**Decided 2026-07-16 (Bernardo): wait for upstream. Do not carry a patch.** The
|
||
overlay option was real but priced badly — our derivations are in no binary
|
||
cache, so a patched rofi means **every machine compiles rofi from source**, on
|
||
every lock bump, to fix a highlight. That is a permanent, distro-wide install
|
||
cost for an annoyance with a free workaround (glance before you press Enter).
|
||
**This item is now a watch, not work:** the only thing to do is check
|
||
rofi#2317, and take the fix when a release carries it. If it is still open at
|
||
the next lock bump, that is fine — nothing here rots meanwhile.
|
||
|
||
|
||
## 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.
|
||
- **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
|
||
|
||
### 148. Every `nomarchy-home` re-lights the laptop panel inside a shut lid
|
||
|
||
Bernardo, 2026-07-16, and he hit it twice in ten minutes: after `nomarchy-home`
|
||
the laptop panel "turns on again" while docked **lid closed**, and he had to
|
||
re-set docking by hand from the Display menu. Then an agent's `hyprctl reload`
|
||
silently undid his fix and re-lit it a second time — confirmed live:
|
||
`/proc/acpi/button/lid/*/state` = `closed` with `eDP-1` enabled and
|
||
`dpms_on=true`, i.e. a panel burning inside a shut clamshell with nobody
|
||
looking at it.
|
||
|
||
**Cause is a known hammer with no follow-through.** `hyprctl reload` re-applies
|
||
every monitor rule and re-enables every output — the ROADMAP already calls this
|
||
"the #142 hammer", and the undock transition *relies* on it. But
|
||
`nomarchy-display-watch` only reacts to `monitoradded`/`monitorremoved`; a
|
||
reload emits neither, so nothing re-asserts the dock profile and the panel just
|
||
stays on. Any HM activation reloads Hyprland, so **every rebuild while
|
||
docked-clamshell lights the panel** and leaves it lit until the user notices.
|
||
Journal for the window is empty — display-watch never saw a thing.
|
||
|
||
Cost: wasted power and panel life, and the live display state silently diverging
|
||
from the profile the user chose — the exact class of "you have to fix it by
|
||
hand" that the display work exists to remove. Fix is probably to re-assert the
|
||
dock/clamshell profile *after* a reload rather than only on hotplug: either
|
||
watch the lid switch as a trigger in its own right (logind already knows), or
|
||
have the transition/activation path re-run the dock decision once the reload
|
||
settles. Note the ordering trap — the reload is also the undock rescue, so
|
||
whatever re-asserts must not fight the rescue that deliberately re-enables eDP
|
||
when the lid is open.
|
||
|
||
|
||
### 146. `[watch]` hypridle hangs instead of exiting when its compositor vanishes
|
||
|
||
**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.
|
||
|
||
**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.
|
||
|
||
### 147. Nomarchy cannot see machine state it never set — and it bit the QA box for 6 months
|
||
|
||
**Rewritten 2026-07-16 after diagnosis; the original filing was wrong and its
|
||
wrongness is the point.** It claimed "a re-login leaves session units dead;
|
||
the repo never adopted the standard session wiring". The repo *has* the wiring
|
||
— HM's `wayland.windowManager.hyprland.systemd.enable` defaults on and is line
|
||
1 of the generated `hyprland.conf` — and it fired correctly. Do not go add it.
|
||
|
||
**What actually happened**, from systemd's own words at the 17:25:54 relogin:
|
||
|
||
```
|
||
Stopped target Hyprland compositor session.
|
||
Requested transaction contradicts existing jobs: Transaction for
|
||
graphical-session.target/stop is destructive (xdg-desktop-portal-gtk.service
|
||
has 'start' job queued, but 'stop' is included in transaction).
|
||
graphical-session.target: Failed to enqueue stop job, ignoring
|
||
Reached target Hyprland compositor session.
|
||
```
|
||
|
||
`hyprland-session.target` stopped; `StopWhenUnneeded=yes` correctly tried to
|
||
take `graphical-session.target` with it; systemd **refused the stop** because
|
||
portal-gtk had a `start` job queued from its own post-crash restart loop (3
|
||
restart/fail lines in that 13s gap). So graphical-session never went down, 8ms
|
||
later hyprland-session came back to find it active, and nothing `WantedBy` it
|
||
was re-pulled. cliphist had already burned 5 restarts into `start-limit-hit`;
|
||
nm-applet/udiskie have no restart policy and just stayed dead.
|
||
|
||
**But none of that can happen on a Nomarchy install.** The whole sequence needs
|
||
the user manager to survive logout, i.e. `Linger=yes` — and linger came from an
|
||
empty marker in `/var/lib/systemd/linger/bernardo` dated **2026-01-24**, which
|
||
**pre-dates Bernardo's migration to Nomarchy** (he was on a custom flake then).
|
||
Nothing here sets it: not modules, not hosts, not templates, and `git log -S`
|
||
says it was never shipped, ever. Removed 2026-07-16 (`loginctl disable-linger`,
|
||
no restart needed); nothing wanted it — `nomarchy-updates.timer` is
|
||
`Persistent=yes` and catches up on login.
|
||
|
||
**So the real item is the class, not the bug.** `/var/lib` is machine state; a
|
||
migration reconciles the flake and leaves everything else exactly as it was.
|
||
Nomarchy therefore ran for six months on its own primary V3 box with a setting
|
||
it never chose, never set, and could not see — on the machine whose whole job is
|
||
telling us how the distro behaves. That is a direct hit on the in-flake-state
|
||
promise (state outside the checkout) *and* on V3's trustworthiness: a QA box
|
||
that silently diverges reports on a distro nobody ships.
|
||
|
||
**Pitch:** a `nomarchy-doctor` check for drift of this class — linger is the
|
||
worked example (`loginctl show-user $USER -p Linger` vs. what the flake asks
|
||
for), and MIGRATION.md should say plainly that `/var/lib` survives and what to
|
||
sweep. Cost is small and it is the only part of this worth shipping. **Do not**
|
||
harden the session wiring against the portal-gtk race: a clean install cannot
|
||
reach it, and defending against it would be clever code for a state we do not
|
||
ship.
|
||
|
||
**Loose thread for whoever takes this:** `modules/home/hyprland.nix` runs Waybar
|
||
from `exec-once` rather than a 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 creates. That workaround may now 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.
|
||
|
||
### 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).
|
||
### 134. `unstable.<pkg>` in the downstream — a newer app without a second flake
|
||
|
||
Bernardo 2026-07-15, wanting a newer LM Studio (pinned 0.4.15-2 vs unstable
|
||
0.4.19-2) and disliking the only path that works today. There is **no seam**:
|
||
`mkFlake` takes `src`/`username`/`hardwareProfile`/`system` only, and
|
||
`homeConfigurations` is built from mkFlake's own `pkgs` (`inherit pkgs`), so a
|
||
`nixpkgs.overlays` in system.nix cannot reach a home package. The user is left
|
||
hand-pinning a `fetchTarball` rev+sha256 in home.nix (verified working) — a
|
||
second pin with no lock, which is exactly the Nix expertise the distro exists
|
||
to spare them. Sketch: `home.packages = [ unstable.lmstudio ];`, and maybe
|
||
`stable.` as an explicit synonym for today's bare `pkgs`.
|
||
|
||
The shape that keeps the promises: Nomarchy carries the `nixpkgs-unstable`
|
||
input **itself** and exposes it through `overlays.default` as an `unstable`
|
||
attrset, so downstream stays **one input**, stays locked, and `nomarchy-pull`
|
||
still moves it. Cost/questions to settle before building:
|
||
- Lock churn: every lock bump now moves two channels, and the full-checklist
|
||
rule (VERIFICATION §5) applies to both.
|
||
- Duplication: measured — pinned vs unstable lmstudio build closures share
|
||
only 690 of ~3.7–4k paths (separate stdenv bootstrap). Opt-in per package,
|
||
but a user who reaches for it pays a second glibc/stack in the closure.
|
||
- "Tested together upstream" weakens by construction: nothing validates
|
||
pinned-Nomarchy + unstable-app. Needs an explicit, documented "you own this
|
||
combination" line, not silence.
|
||
- Does `unstable` belong in the eval by default, or only when referenced?
|
||
(An unused input still locks + fetches.)
|
||
- Scope: home packages only, or system.nix too?
|
||
|
||
Alternative if that is too much surface: give `mkFlake` an `overlays ? []`
|
||
param (one-line seam, user still owns their own input/pin — cheaper for us,
|
||
still expert-only for them). Related: #133.
|
||
|
||
### 114. Greeter ignores per-device keyboard layouts
|
||
|
||
Found by Bernardo 2026-07-14: logging out while docked lands on tuigreet,
|
||
where his external keyboard (remembered as `us` via
|
||
`settings.keyboard.devices`) types the session layout `gb` — so the password
|
||
prompt fights him. Not a regression and not docking-related: per-device
|
||
layouts are applied with `hyprctl keyword device[<name>]:kb_layout`, which
|
||
only exists inside a running Hyprland session, while tuigreet draws on a
|
||
kernel VT whose single keymap comes from `console.useXkbConfig` ←
|
||
`services.xserver.xkb.layout`. A VT structurally cannot do per-device
|
||
layouts, so this is a design gap, not a bug to patch. Options, cheapest
|
||
first: (a) document it and stop there; (b) a greeter layout-cycle key
|
||
(tuigreet has no such feature — would need the keymap swapped under it);
|
||
(c) host tuigreet inside a small Wayland compositor (cage/labwc), which
|
||
*does* get per-device XKB and would let the greeter honour the same
|
||
in-flake state the session uses — real work, and it changes the greeter's
|
||
whole rendering path (`modules/nixos/greeter.nix` themes tuigreet through
|
||
the 16 ANSI console slots, so (c) is not a drop-in). Worth deciding whether
|
||
the greeter is meant to be layout-aware at all before costing it.
|
||
|
||
- **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.
|
||
|
||
- **Post-install hardware hints** (`VISION § B`) — After the general
|
||
“you're set” card (#81), optionally fire **one** additional
|
||
self-gated notify when the machine actually has the hardware:
|
||
(a) `fwupdmgr` on PATH → “System › Firmware to check LVFS updates”;
|
||
(b) `fprintd-list` on PATH → “System › Fingerprint to enroll”.
|
||
One-shot markers in `settings.*` (same in-checkout discipline as
|
||
`firstBootShown`); never a permanent MOTD nag. Cost: small — extend
|
||
`nomarchy-first-boot` or a sibling oneshot + `checks.first-boot`
|
||
fixture. Control-center / MOTD already mention these; the gap is the
|
||
silent first *graphical* session for people who never open those.
|
||
|
||
_(#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: post-install hardware hints; NVIDIA
|
||
deferred past v1; IR portal (b)/(c) need T14s (HARDWARE-QUEUE § T14s).
|
||
Standing calls: browser = Chromium; power = PPD.
|
||
|
||
## 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-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**.
|