Files
Nomarchy/agent/JOURNAL.md
Bernardo Magri cd5d3b51e2
All checks were successful
Check / eval (push) Successful in 3m0s
fix(rofi): Display menu Back returns to System on single-monitor
The mode picker's Back did `exec "$0" display`. On a single-monitor
machine with no display profiles the output chooser is skipped, so
re-running `display` dropped straight back into the same mode picker — an
apparent no-op. Track modeBack (display when the chooser was shown, else
system) and dispatch Back to it, so single-monitor Back leaves Display.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 21:45:11 +01:00

1094 lines
65 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Journal — append-only iteration log
One entry per iteration, **newest first**. This is the loop's short-term
memory: the next session reads the last 35 entries to orient. Keep an
entry under ~15 lines; durable lessons go to MEMORY.md, not here.
Template:
```markdown
## YYYY-MM-DD — <task title> (<commit sha, or "no commit">)
- **Task:** which BACKLOG item (or QA sweep / bootstrap / escalation)
- **Did:** what changed, in 24 lines
- **Verified:** tier reached (V0/V1/V2) + the actual commands/checks run
- **Pending:** V3 queued? follow-ups filed? anything [stuck]?
- **Next suggestion:** what the following iteration should probably take
```
---
## 2026-07-07 — Display menu "Back" doesn't return (iteration #57, item 40)
- **Task:** BACKLOG item 40 — in System Display the Back row didn't
return on a single-monitor machine.
- **Root cause:** the mode picker's Back did `exec "$0" display`. On a
single-monitor laptop with no display profiles, the output chooser is
skipped, so re-running `display` drops straight back into the *same*
mode picker — an apparent no-op. (Multi-monitor works: `display`
re-shows the chooser, a genuine previous level.)
- **Fix:** `modules/home/rofi.nix` display handler now tracks `modeBack`
`display` when the chooser was shown, else `system`. Mode-picker Back
does `exec "$0" "$modeBack"`, so single-monitor Back leaves Display for
System.
- **Verified:** V0 `nix flake check --no-build` green. Logic is
deterministic; the single- vs multi-monitor branch is the whole fix.
## 2026-07-07 — Menu "Back" double arrows (iteration #56, item 34)
- **Task:** BACKLOG item 34 — every submenu's `↩ Back` row drew two
arrows: the `↩` glyph in the label AND a themed `go-previous` icon.
- **Fix:** `back()` in `modules/home/rofi.nix` was `row "$BACK"
go-previous` (label + icon). Changed to `printf '%s\n' "$BACK"` — now
identical to what the plain pick-lists already append, so every Back row
shows a single `` glyph. Kept `BACK="↩ Back"` as the universal
label/match, so no case-branch comparison changed (no risk of a Back
that fails to return — the item-40 failure mode). Collision-exact match
preserved by the glyph.
- **Verified:** V0 `nix flake check --no-build` green (evaluates
homeConfigurations). Single-arrow result is deterministic from the code;
visual confirmation on hardware is nice-to-have, not load-bearing.
## 2026-07-07 — Battery charge-limit quick toggle (iteration #55)
- **Task:** BACKLOG PROPOSED (promoted by Bernardo) — a toggle for the
battery charge threshold.
- **Did:** Replaced the control-center "Battery Limit" raw number-prompt
with a preset toggle picker (Off / 80% recommended / 90% / 60% /
Custom…) writing `settings.power.batteryChargeLimit`. Kept it in the
control-center — that's where the baked, rebuild-valued System Toggles
live (rofi `nomarchy-menu` is the *instant* surface; the CC holds
config values); it's reachable from the menu via System Control
Center. Base machinery (sysfs oneshot + AC-replug udev in power.nix)
untouched.
- **Verified:** V0 (`bash -n`; `nix flake check --no-build`, green) + V1
(nomarchy-control-center package builds → the writeShellApplication
shellcheck gate passes).
- **Pending:** instant-effect (no rebuild) refiled in PROPOSED as
`[blocked:hw]` — needs a udev-writable sysfs node, confirmable only on
a laptop with the charge-threshold control. Light session spot-check
queued in HARDWARE-QUEUE.
- **Next suggestion:** item 29 is `[human]`/`[blocked:hw]`; remaining is
item 28 slices or a QA sweep.
## 2026-07-07 — Waybar overlaps fullscreen video (iteration #54)
- **Task:** BACKLOG item 30 — bar drawn on top of a fullscreen YouTube video.
- **Did:** `layer: top` → `bottom` in the generated `waybar.nix` and the
four whole-swap jsoncs (boreal, summer-night, summer-day,
executive-slate — parity rule). On Hyprland the top layer renders above
even real-fullscreen surfaces; bottom lets the fullscreen window cover
the bar while the exclusive zone still reserves its space in normal
tiling. Rationale comment in waybar.nix; data jsoncs left comment-free
(they carry none). Also shipped this session (pre-loop, user-driven):
battery discharging-icon gap widened (f848e73).
- **Verified:** V0 (`nix flake check --no-build`, green) + V1
(`homeConfigurations.nomarchy.activationPackage` builds; the built
`.config/waybar/config` shows `"layer": "bottom"`).
- **Pending:** V3 queued in HARDWARE-QUEUE — fullscreen video covers the
bar, bar returns on exit, floating-overlap trade-off. Can't be
eyeballed headlessly here.
- **Next suggestion:** item 29 is `[human]`/`[blocked:hw]`; remaining
work is item 28 (theme UI) slices or a QA sweep.
## 2026-07-06 — menu parity for downstream-flake options (iteration #51, slice 1)
- **Task:** PROPOSED item — ensure `nomarchy.*` options have a menu toggle where appropriate.
- **Did:** Audited options and implemented the first slice of missing toggles in the TUI Control Center (`Updates`, `Battery Limit`, `Bluetooth`, `Printing`). Wired `modules/home/options.nix` and `modules/nixos/options.nix` to use `config.nomarchy.settings.*` as defaults.
- **Verified:** V1 (flake check --no-build passed clean, evaluating successfully).
- **Pending:** user visual review and testing.
- **Next suggestion:** triage other PROPOSED items.
## 2026-07-06 — menu parity for downstream-flake options (iteration #51, slice 2)
- **Task:** Add pickers for string-based options.
- **Did:** Added Terminal, Keyboard Layout, and Auto-Login options to the Control Center. Wired them to the Nix options via `config.nomarchy.settings.*`.
- **Verified:** V1 (flake check --no-build).
## 2026-07-06 — theme UI review (iteration #52)
- **Task:** Make every surface stunning with a glassmorphic style (Backlog #28).
- **Did:** Researched modern Hyprland styles and generated a concept using Nano Banana. Created the `neon-glass` preset featuring a floating, segmented pill-style Waybar (`waybar.css`) and a glassmorphic Rofi launcher (`rofi.rasi`). Generated a matching 4K wallpaper.
- **Verified:** Contrast checker script passed (22 themes x 7 pairings green). Flake check passed cleanly.
## 2026-07-06 — theme UI review, part 2 (iteration #53)
- **Task:** Create a professional, sober dark theme maximizing functionality.
- **Did:** Generated a minimalist geometric concept wallpaper. Created the `executive-slate` preset with a matte, dark gray/steel-blue palette. Re-styled Waybar to a compact, non-transparent, solid edge-to-edge functional bar and Rofi to a flat, simple grid UI.
- **Verified:** Contrast checker script passed (23 themes x 7 pairings green). Flake check passed cleanly.
## 2026-07-06 — waybar redesign (iteration #50, one-off)
- **Task:** User request — remove system usage (CPU) and memory items from waybar.
- **Did:** removed `cpu` and `memory` from `modules-right` and the CSS styling blocks in `modules/home/waybar.nix`.
- **Verified:** V1 (flake check --no-build passed clean).
- **Pending:** user visual review.
- **Next suggestion:** triage PROPOSED.
## 2026-07-06 — control center implementation (iteration #49, item 18)
- **Task:** BACKLOG NEXT#18 — implement TUI Control Center using `gum`.
- **Did:** created `nomarchy-control-center.sh` and packaged it in `pkgs/nomarchy-control-center`. Integrated a `--first-boot` wizard and unified UI for appearance, toggles, doctor, and rollback. Added the package to `flake.nix` and `modules/nixos/default.nix`. Added "Control Center" to the System menu in `rofi.nix`.
- **Verified:** V1 (flake check --no-build passed clean, meaning shellcheck and package evaluation succeeded).
- **Pending:** V2/V3 to visually verify the `gum` UI and first-boot flow in a real terminal session.
- **Next suggestion:** watch lock-bump CI (item 14) or triage PROPOSED.
## 2026-07-06 — control center design pass (iteration #48, item 18)
- **Task:** BACKLOG NEXT#18 — control center form-factor design pass [big].
- **Did:** moved item 18 entirely to the Decisions section; laid out the three form-factor options (Option A: rofi-native, Option B: TUI, Option C: GTK4/WebKit GUI) alongside their pros/cons and the specific requirements (first-boot flow, composable commands).
- **Verified:** V0 (docs-only); `nix flake check --no-build` passed pre-commit.
- **Pending:** the `[human]` decision on the form-factor.
- **Next suggestion:** #14 (watch lock-bump CI) or #20 KVM runner [human]. If none actionable, triage PROPOSED or do a QA sweep.
## 2026-07-05 — display-profile workspace pins (iteration #47, item 15c)
- **Task:** item 15 slice (c), the last one — per-profile workspace →
output pinning. Item 15 deleted (a/b/c all shipped; V3s queued).
- **Did:** a profile is now `{ monitors; workspaces = { "1" = "DP-3"; }; }`
(bare list still accepted — either-type + normalization in
hyprland.nix; coercedTo refuses list-of-submodule sources). Pins bake
as Hyprland `workspace` rules and apply instantly on profile switch
(`hyprctl keyword workspace` + `dispatch moveworkspacetomonitor`).
monitor-rules.nix gained pure `workspaceRule`; template + README
updated.
- **Verified:** V1 flake check green (incl. the extended
checks.display-profiles assert) + scratch-downstream eval: baked
settings render both rule sets, the generated applier carries the
keyword+dispatch pairs (bash -n OK), match/list still correct with
mixed profile shapes.
- **Pending:** V3 queued (workspace-pins entry in HARDWARE-QUEUE, rides
the slice-a dock test). Stale-pin note documented in the applier.
- **Next suggestion:** backlog is now human/event-gated (28 taste
calls, 14 Monday, 18 Decision, 20 KVM) — idle the loop or triage
PROPOSED.
## 2026-07-05 — icon-weight A/B for the human call (iteration #46, item 28)
- **Task:** item 28's last `[human]` blocker — make the color-vs-
symbolic menu-icon decision cheap.
- **Did:** captured the lumon root menu with `-symbolic` icon names
(temporary uncommitted rofi.nix edit, reverted after) and published
a side-by-side artifact (link in the BACKLOG bullet). Findings:
symbolic matches the bar's weight, but Papirus symbolic coverage
has holes and rofi tints symbolics fixed gray — B needs an
audit+recolor slice; A (color) is the free default.
- **Verified:** V2 for the experiment itself (both variants VM-
captured); no repo code changed — tree is clean apart from agent/.
- **Pending:** Bernardo's call on the artifact. Item 28 is now fully
agent-side done: all remaining bullets are `[human]`/hardware.
- **Next suggestion:** general loop — item 15 slice (c) (per-profile
workspaces) is the topmost actionable code item; 14 fires Monday.
## 2026-07-05 — purge stub waybar.css whole-swaps (iteration #45, item 28c/d)
- **Task:** item 28 — lumon vibe capture exposed four themes shipping
an unstyled default bar.
- **Did:** deleted the stub waybar.css in catppuccin/lumon/nord/
retro-82 (214 lines of color defines from the f211ef0 rewrite
import; a theme waybar.css replaces the WHOLE generated stylesheet,
so the stubs nuked all bar styling). The four themes now get the
generated bar in their own palette; their full rofi.rasi designs
kept. Whole-swap bars are now only summer-day/night, both authored.
- **Verified:** V2 — lumon re-capture shows the generated styling
(workspace pill, padded right cluster, no edge clipping); V1 flake
check green. catppuccin/nord/retro-82 share the mechanism, not
individually captured.
- **Pending:** HARDWARE-QUEUE eyeballs; the `[human]` icon-weight call.
- **Next suggestion:** item 28 is agent-side done pending Bernardo's
taste calls — park it; take the next actionable BACKLOG item.
## 2026-07-05 — capture harness paints wallpapers (iteration #44, item 28c)
- **Task:** item 28c harness gap — captures rendered a bare base-color
backdrop because the guest had no `~/.nomarchy` runtime state.
- **Did:** tools/theme-shot.nix now seeds theme-state.json via tmpfiles
at boot (before greetd), so the session's exec-once
`nomarchy-theme-sync wallpaper` paints naturally; dropped the
testScript seed+manual-repaint (too late for exec-once; manual runs
also lack WAYLAND_DISPLAY, which awww's client needs to find its
socket).
- **Verified:** V2 — summer-night capture shows wallpaper + cream
inverted bar + menu + placeholder dim, all coherent. V1 flake check
green.
- **Pending:** identity-theme vibe capture (lumon/retro-82); the
`[human]` icon-weight call; HARDWARE-QUEUE eyeballs.
- **Next suggestion:** item 28c identity capture, or park 28 for
Bernardo's taste calls.
## 2026-07-05 — clock zone tooltip (iteration #43, LATER item)
- **Task:** LATER auto-timezone Waybar tooltip. Bullet deleted.
- **Did:** the clock tooltip's first line is now the zone —
`{:%Z (UTC%z)}` above the calendar — in the generated bar AND both
summer whole-swaps (parity). Under auto-timezone the existing
tz-watch SIGUSR2 reload keeps it current, so it shows the detected
zone live. (%Z abbreviation, not the IANA name — strftime has no
code for that; good enough for an "(optional)" nicety.)
- **Verified:** V0; V1 — rendered generated config carries the new
tooltip-format; both summer jsonc parse (jq) with %Z present.
Hover render → V3 queued.
- **Pending:** V3. LATER now holds only deferred/big/hw/human items.
- **Next suggestion:** a QA sweep, or stop-and-wait — the queue is
effectively drained for unattended work; meaty LATER items
(wallpapers split is *deferred by decision*, installer round 2
[big]) deserve Bernardo's go-ahead or a fresh session.
## 2026-07-05 — doctor bar tripwire (iteration #42, LATER item)
- **Task:** LATER doctor Waybar warning. Bullet deleted.
- **Did:** nomarchy-doctor-status (named bin, PATH): silent exit 0
while the doctor is healthy or absent (self-gate), else JSON with
the first ✖ lines jq-escaped into the tooltip, in @bad; click =
nomarchy-menu doctor (the sheet). custom/doctor added to the
generated bar (after battery, interval 300 — the check is real
work, this is a tripwire not a monitor) + CSS, AND both summer
whole-swaps (module list, jsonc block, CSS membership + @red rule)
per the parity rule.
- **Verified:** V0; V1 — template-home builds, status bash -n, config
+ CSS each carry the module, both summer jsonc still parse (jq)
with the block; FUNCTIONAL — self-gate silent, and a shimmed
failing doctor produces valid JSON (jq-verified: class bad, ✖
lines in tooltip). Live render → V3 queued.
- **Pending:** V3. LATER remaining: wallpapers-artifact split
(deferred by decision), installer round 2 [big], boot-from-
snapshot, night-light geo, icon overrides, MIPI camera [hw],
auto-timezone tooltip, VPN richer display, release bump [human].
- **Next suggestion:** auto-timezone Waybar tooltip (small) — or a
QA sweep; the meatier LATER items deserve a fresh session.
## 2026-07-05 — OCR screenshot-to-text (iteration #41, LATER item)
- **Task:** LATER OCR (NEXT is fully human/event-gated, so LATER is
in play per LOOP.md). Bullet deleted.
- **Did:** Capture "OCR region → clipboard" (self-gated on
tesseract): slurp (Esc = silent cancel, the recording pattern) →
grim → tesseract stdin/stdout → wl-copy, whitespace-only results
toast instead of clobbering the clipboard; success toasts the word
count. tesseract ships eng-only via rofi.nix home.packages
(override documented — unscoped it drags every language's
traineddata, real cost on a compile-from-source distro).
- **Verified:** V0; V1 — template-home builds, menu bash -n, gated
row + case present; FUNCTIONAL — the profile's own tesseract read a
rendered test image back verbatim ("Nomarchy OCR smoke test 42"),
so the OCR half of the pipe is proven; grim/slurp half needs a
session → V3 queued.
- **Pending:** V3.
- **Next suggestion:** LATER: doctor Waybar warning (self-gating bar
indicator fed by nomarchy-doctor) or auto-timezone tooltip; both
small.
## 2026-07-05 — Look & Feel menu category (iteration #40, item 19)
- **Task:** BACKLOG NEXT#19 — its ≥3-entries gate is met (Theme, Next
wallpaper, Night light). Item deleted.
- **Did:** root's Theme row → "Look &amp; Feel" (pango-escaped —
-markup-rows menus treat & as an entity; matched on *Look*); new
lookfeel mode: Theme (SUPER+T hint), Next wallpaper (SUPER+SHIFT+T
hint via the new generalized hintForAction), Night light row moved
from System (System thins by one). Root stays six. menuHint is now
sugar over hintForAction.
- **Verified:** V0; V1 — menu bash -n, lookfeel mode + escaped label
+ night light relocated (4 occurrences, all in lookfeel/nightlight
paths); VISUAL — tokyo-night capture: root renders "Look & Feel"
correctly with six rows and intact hints. Submenu flow → V3
queued.
- **Pending:** V3. NEXT now: 28 (Bernardo-gated) · 14 (Monday watch)
· 18 [big, Decision-gated] · 20 [human] — the LATER tier or QA
sweeps are next for the loop.
- **Next suggestion:** LATER's OCR screenshot-to-text (two-row
Capture addition, self-contained) — or a QA sweep.
## 2026-07-05 — launch-or-focus binds (iteration #39, item 17)
- **Task:** BACKLOG NEXT#17 (the UI review is Bernardo-gated; back to
the queue head). Item deleted.
- **Did:** nomarchy.launchOrFocus (listOf submodule: key/mods/class/
command/desc; opt-in, template example) → hyprland.nix generates
binds through a shared nomarchy-focus-or-launch script (jq
case-insensitive class match → focuswindow "(?i)" regex, else
launch; uninstalled command → toast, so binds survive suite edits);
rofi.nix renders the same entries into the SUPER+? cheatsheet via
prettyKeys (single source). README row.
- **Verified:** V0; V1 — scratch downstream with 2 entries: both
binds rendered (default command = lowercased class; custom
mods/command/desc honored), tool bash -n + usage exit 64,
cheatsheet rows present. Focus behaviour needs a session → V3
queued.
- **Pending:** V3. Queue after this: #18 control center [big, needs
the form-factor Decision], #19 Look&Feel menu (gated on ≥3
entries), #20 [human], LATER tier.
- **Next suggestion:** a QA sweep (#18/#19/#20 are blocked-ish) — or
item 19's precondition check (does Look & Feel earn 3 entries
yet?).
## 2026-07-05 — placeholder dim + latte review + backdrop probe (iteration #38, item 28c)
- **Task:** item 28 slice (c) — the summer-rasi quickie + the light-
theme capture.
- **Did:** both summer rasi: placeholder-color @bg0 → bg0 at 60%
alpha (#…99), so prompt and placeholder no longer read as one
phrase. Captured catppuccin-latte at 1080p: the generated menu is
COHERENT on light themes (zebra rows on the 28b-retuned surface,
hints dimmed) — closes the audit's "dark terminal on latte"
concern as a stale preview. Backdrop suspicion probed with a
dedicated VM (hyprctl getoption): misc:background_color IS set
(0xFFEFF1F5) — awww's empty-state layer occludes it in the
wallpaper-less VM; on hardware the fix covers the startup gap as
intended.
- **Verified:** rasi edits grep-verified (rasi parses at rofi
runtime; visual rides the next summer capture / V3); latte menu
capture reviewed; probe output above.
- **Pending:** item 28 remaining: icon-weight `[human]`, hardware
eyeballs (queued), identity-theme capture (optional), slice (d)
needs nothing (parity verified).
- **Next suggestion:** item 28 is now Bernardo-gated (taste calls +
V3); the loop should return to the general BACKLOG head (item 14
watch / item 17 launch-or-focus).
## 2026-07-05 — rofi keybind hints (iteration #37, item 28c)
- **Task:** item 28 slice (c) — the hint column, the audit's biggest
remaining UX item.
- **Did:** rofi.nix: menuHint builds a dimmed pango span
(size small, alpha 55%) from keybinds.nix via the cheatsheet's
prettyKeys — single source, hints can't drift from the live binds;
applied to root (Apps/Theme/Power/Keybindings; Apps special-cased,
it's rofi drun not a menu module), Tools (all eight leaves), System
(Network/Bluetooth/DND); those three menus now pass -markup-rows.
Contains-glob matching and the exact "↩ Back" match are unaffected
(labels keep their prefix; Back stays markup-free).
- **Verified:** V0; V1 rendered menu bash -n + hint spans present +
3× -markup-rows; VISUAL — tokyo-night capture: hints render dimmed
and subordinate on normal AND selected (accent) rows; Tools/System
rows correctly bare.
- **Pending:** slice-c remainder: summer-rasi placeholder dim,
latte/identity captures, icon-weight `[human]`.
- **Next suggestion:** the placeholder-dim quickie + a latte capture
in one tick; then item 28 pauses for Bernardo's icon-weight call
and the hardware eyeballs.
## 2026-07-05 — bar group rhythm + @muted adoption (iteration #36, item 28c)
- **Task:** item 28 slice (c) — the two queued bar polish items.
- **Did:** waybar.nix generated CSS: margin-left 14px on the three
right-cluster group heads (pulseaudio/powerprofile/battery — media
· toggles · status; hidden heads degrade to uniform spacing);
workspaces buttons, dnd bell, and muted volume moved from
alpha(@text,.5) tints to the semantic @muted role — safe since 28b
floor-gates muted/base ≥ 2.0 (the old comment warning against
@muted documented the pre-28b world; rewritten to state the new
invariant). Whole-swap bars untouched (authored identity).
- **Verified:** V0; V1 rendered CSS has 4 @muted uses + the margin
rule; VISUAL — tokyo-night re-capture: themed backdrop confirmed
(navy base, not black), group gap visible before the volume group,
workspace pill legible. Cross-theme minima are guaranteed by the
gated checker rather than per-theme eyeballs.
- **Pending:** slice-c remainder: rofi hint column, summer-rasi
placeholder dim, icon-weight `[human]`, latte/identity captures.
- **Next suggestion:** rofi keybind-hint column (the biggest
remaining UX win), or the two-line placeholder nit as a quickie.
## 2026-07-05 — summer-day capture review + themed backdrop (iteration #35, item 28c)
- **Task:** item 28 slice (c) — whole-swap capture review + the first
cheap polish fix.
- **Did:** captured summer-day at 1080p (harness, VM): the authored
slate menu + island-pill bar hold up with the 28b retunes —
initially misread as polarity soup, but the rasi whole-swap
deliberately mirrors the bar's slate strip: identity, keep (nit
filed: undimmed entry placeholder). The surviving cross-theme fix:
Hyprland `misc:background_color` = theme base (was compositor
black — a glitch-looking flash behind missing/slow wallpaper,
glaring on light themes). Punch list updated.
- **Verified:** V0; V1 — template-home rebuilt, rendered hyprland.conf
carries `background_color=rgb(1a1b26)` (theme base). Backdrop
visible confirmation rides the next capture run / V3.
- **Pending:** captures for latte + one identity theme; polish items
queued: bar group margins, @muted adoption, rofi hint column, icon
weight `[human]`.
- **Next suggestion:** bar polish slice (group margins + @muted) —
code-level, verifiable by build + re-capture.
## 2026-07-05 — 1080p capture harness + first slice-c review (iteration #34, item 28c)
- **Task:** item 28 slice (c) prep — full-res captures for the
per-surface polish pass.
- **Did:** tools/theme-shot.nix (maintainer tool, THEME env-driven):
full themed desktop headless at 1920x1080 via the memory recipe +
two fixes over the spike — home-manager.useGlobalPkgs (HM submodule
otherwise misses the overlay → 'nomarchy-theme-sync missing') and a
menu.png shot (hyprctl dispatch exec nomarchy-menu). Proven on
tokyo-night; both shots read. Findings → item 28 punch list: rofi
root width vs empty space (→ keybind-hint column idea), icon-weight
mix (color Papirus vs mono bar) `[human]`, bar group margins
confirmed, alpha-tint dim vs @muted role. Whole-swap module parity
verified textually (no drift; cpu/memory omission is identity).
- **Verified:** harness runs end-to-end (exit 0, screenshots read);
eval from tools/ with relative flake path OK. Wallpaper still
doesn't paint (theme-sync needs ~/.nomarchy in the guest — fix
noted in the header for the next run).
- **Pending:** capture summer-day/night (whole-swaps) + latte;
then the polish slices; icon-weight call is Bernardo's.
- **Next suggestion:** run the retuned-theme captures + start the
cheap polish items (group margins, @muted adoption).
## 2026-07-05 — P2 floors retuned + gated (iteration #33, item 28b done)
- **Task:** item 28 slice (b) remainder — the P2 contrast floors.
- **Did:** 18 hex retunes across 9 themes: muted raised on lumon/
white/retro-82/everforest (OKLCH hue-preserving minimal solve to
≥2.05) + gruvbox→bg4 #7c6f64, nord→#616e88 (community standard),
latte→overlay1 #8c8fa1; text-on-surface via surface nudges
(ristretto darker, latte lighter); accentAlt darkened (latte,
summer-day); warn darkened on the light themes (latte, summer-day,
rose-pine-dawn) and flexoki-light's four statuses moved to the
canonical 600 series. check-theme-contrast.py gates all four
adopted floors (muted ≥2, text/surface ≥4.5, accentAlt ≥3, warn
≥2.5 — warn is the battery tint); good/bad stay audit-only
(identity), so no exemption mechanism was needed. Slice (b) done.
- **Verified:** V0 + checks.theme-contrast GREEN (21×7); re-audit:
contrast findings 20→1 (miasma bad = identity); flake check green.
Rendering is V3 → queued (P2 eyeball entry).
- **Pending:** slices (c) per-surface polish (needs 1080p captures)
and (d) whole-swap bar parity; ansi[] conventions decision still
open (audit notes).
- **Next suggestion:** slice (c) prep — 1080p captures via the VM
recipe, or hardware captures if Bernardo prefers eyeballing first.
## 2026-07-05 — P1 palette retunes + gated subtext floor (iteration #32, item 28b)
- **Task:** item 28 slice (b), P1s from the #31 audit.
- **Did:** summer-day: surface #5c6a72(=text!)→#efe7d0 raised cream,
subtext #fdf6e3(=base!)→#6e828a slate; flexoki-light to canonical
Flexoki tones: surface #E6E4D9(ui), overlay #B7B5AC, subtext
#6F6E69(tx-2), muted #878580(base-500); miasma surface #000→#2e2e2e;
kanagawa surface #090618→#2a2a37 (upstream sumiInk4 — old value was
sumiInk0, darker than bg). check-theme-contrast.py gains the adopted
`subtext/base ≥ 3` pairing (the item-25/27 trap, now impossible).
Model note recorded: `overlay` is a mid-tone role, not a bg tier.
- **Verified:** V0 + checks.theme-contrast GREEN (21×3 pairings);
re-audit: all four P1 findings gone (kanagawa now CVD-only findings);
V1 — scratch downstream with summer-day ACTIVE builds, new surface
hex present in rendered ghostty config. Looks are V3 → queued
(four-theme eyeball).
- **Pending:** 28b remainder: P2 floors (muted ≥2, warn ≥2.5,
text-on-surface ≥4.5, accentAlt ≥3) + exemption-list mechanism.
- **Next suggestion:** 28b P2s (7 muted retunes + 4 warn + 2
text-on-surface + 2 accentAlt, then gate with exemptions).
## 2026-07-05 — theme design audit (iteration #31, item 28 slice a)
- **Task:** BACKLOG NEXT#28 slice (a) — the design-theory audit
(human-requested; Bernardo asked to run it now).
- **Did:** tools/audit-theme-design.py (new, report-only): OKLCH
lightness architecture, extended WCAG pairs, status-hue families,
accent harmony, Machado protan/deutan simulation, ANSI slot
semantics — over all 21 palettes; plus a visual pass over 4
representative preview.png captures. Findings curated
identity-aware into a P1P3 punch list appended to item 28: 2
invisible-subtext palettes (summer-day, flexoki-light), 2 inverted
bg stacks (miasma, kanagawa), 7 muted-floor misses, warn-barely-
reads ×4, a systemic "status is never color-only" rule (CVD
collapse is inherent, glyph redundancy is the fix), explicit
identity exemptions, light-theme ANSI convention decision, bar
right-cluster grouping rhythm, latte dark-terminal verify.
- **Verified:** V0 (py_compile; report tool, not wired into checks).
Analysis is palette-math + thumbnail previews; per-surface
spacing/type audit needs 1080p captures (slice c prep).
- **Pending:** slice (b) — palette retunes + checker extension w/
exemption list; needs Bernardo's eye on the P1 retunes (visual
taste calls). Artwork needs none so far — no image-model ask yet.
- **Next suggestion:** slice (b) P1s (summer-day/flexoki subtext+
surface, miasma/kanagawa stacks) — small hex edits, big payoff.
## 2026-07-05 — greeter theming from the JSON (iteration #30, item 16)
- **Task:** BACKLOG NEXT#16 — tuigreet themed from theme-state.json at
system rebuild (the Plymouth model). Item deleted.
- **Did:** new modules/nixos/greeter.nix owns the greetd block (moved
out of default.nix, one concern one file) + the theming: (1)
console.colors = the theme's ansi[] (mkDefault; lands as
vt.default_* kernel params — also themes raw ttys + LUKS prompt);
(2) tuigreet --theme on NAMED slots (its parser is ratatui
Color::from_str, verified in source; names hit the standard indexes
the retinted VT palette now serves). Sparse state w/o ansi → skips
cleanly.
- **Verified:** V0; V1 — template-system toplevel: baked greetd.toml
carries the full --theme spec, kernel-params carry all three
vt.default_{red,grn,blu}. A checks.greeter VM test was attempted and
DROPPED: tuigreet dies under runNixOSTest even bare (harness limit,
not our flag — bisected; nixpkgs tests use agreety) → MEMORY.md.
- **Pending:** V3 queued (rendering + theme-follows-rebuild).
- **Next suggestion:** #17 launch-or-focus, or the new #28 UI-review
audit slice once Bernardo confirms its placement.
## 2026-07-05 — display profiles auto-switch (iteration #29, item 15b)
- **Task:** BACKLOG NEXT#15 slice b — opt-in hotplug auto-switch.
- **Did:** nomarchy-display-profile gains `match <outputs>` (exact set
→ else unambiguous largest subset → ties/none = exit 1);
nomarchy-display-profile-watch polls `hyprctl monitors all` every 3s
(exec-once, the keyboard-watcher pattern — session units race
Hyprland IPC), reacts only to CHANGES, reads settings.displayProfileAuto
LIVE each change (instant menu toggle, no rebuild) and applies via
the tool, so auto-picks persist concrete profiles that rebuilds
bake. Chose a separate boolean over an "auto" pseudo-profile for
exactly that bake semantics. Menu: Auto-switch (on/off) row in
Profiles. Template + README notes.
- **Verified:** V0; V1 — scratch 4-profile downstream builds; both
scripts bash -n; match matrix exercised: exact ×3, unambiguous
subset, tie → none, no-fit → none; watcher exec-once present; menu
renders with the row. Real hotplug needs a session+dock → V3 (rides
the slice-a queue entry).
- **Pending:** V3. Slice c (workspace binding) remains, tagged
optional.
- **Next suggestion:** #16 greeter theming — item 15's remaining
slice c is optional; a fresh item keeps variety.
## 2026-07-05 — display profiles slice a (iteration #28, item 15a)
- **Task:** BACKLOG NEXT#15 slice a, per the #27 design: option +
overlay + applier + menu + template example.
- **Did:** nomarchy.displayProfiles (attrsOf (listOf monitorType));
monitor logic extracted to pure modules/home/monitor-rules.nix
(rule renderer + 3-layer resolve: profile replaces whole-by-name →
resolution picks field-level with the disable-guard → wildcard),
hyprland.nix consumes it and gains nomarchy-display-profile
(list/active/apply/base; apply = baked hyprctl rules + state write
--no-switch, base = clear + hyprctl reload) on PATH only when
profiles exist; menu: Display gains a gated "Profiles " row + new
display-profile mode (●/○ active mark, Base layout row); template
example + README row. checks.display-profiles unit-tests resolve
(incl. junk-state degradation) — the first HM-eval fixture attempt
hit "path not valid" (eval store won't realise writeText/toFile
under flake check), hence the pure-function extraction.
- **Verified:** V0 (check's asserts force at eval); V1 — scratch
downstream through lib.mkFlake builds: tool bash -n + correct baked
rules (disable, vrr, floats), hyprland.conf shows the exact overlay
(wildcard + kept base + disabled panel + profile output), menu bash
-n, list/usage smoke. Live switching needs a session+dock → V3.
- **Pending:** V3 queued. Slices b (hotplug auto-switch) and c
(workspace binding) remain in the item.
- **Next suggestion:** #16 greeter theming, or 15b if momentum on
displays is preferred.
## 2026-07-05 — display-profiles design pass (iteration #27, item 15, no code)
- **Task:** BACKLOG NEXT#15 — the entry itself demands "design first,
write the plan into this entry before coding". Backlog-only iteration.
- **Did:** wrote the plan into item 15: `nomarchy.displayProfiles =
attrsOf (listOf monitorType)` (reuse the existing submodule),
`settings.displayProfile` state via the night-light pattern (instant
hyprctl apply + bake on rebuild), whole-entry-by-name overlay with a
disable-guard against stale resolution picks, self-gated menu row,
three slices (option+overlay+menu / opt-in hotplug auto-switch /
per-profile workspace binding), kanshi stays a non-goal.
- **Verified:** V0 (docs-only; flake untouched).
- **Pending:** nothing — 15a is now directly codeable.
- **Next suggestion:** 15a (option + overlay + menu picker), or #16
greeter theming if a fresh session prefers a self-contained item.
## 2026-07-05 — scheduled lock-bump workflow (iteration #26, item 14 b+c)
- **Task:** BACKLOG NEXT#14 `[big]` — split: (a) checks-on-push shipped
earlier; this ships (b) the weekly bump + (c) the fast-lane as
workflow_dispatch on the same job.
- **Did:** .gitea/workflows/bump.yml — Mon 05:17 UTC cron + dispatch:
nix flake update (pinned branches by construction) → gate `nix flake
check --no-build` (py_compile/bash -n skipped: they don't read the
lock) → commit --only flake.lock with the update's change list as
body → push main (triggers check.yml as second net). Red gate/push
race = failed run, nothing lands. Container recipe from check.yml.
- **Verified:** V0 (yq parse + bash -n each run block); logic simulated
end-to-end in a scratch clone with a REAL nix flake update + bare
remote: changed/unchanged branches, bystander file stays uncommitted,
push lands; today's bumped lock evals green (Monday's baseline).
Gitea 1.25.4 confirmed (schedule supported). The real cron pickup +
token push are NOT confirmable from here → item 14 slimmed to
"confirm first run".
- **Pending:** watch the first scheduled/dispatched bump run.
- **Next suggestion:** #16 greeter theming (next codeable item; #15
needs its design pass written first).
## 2026-07-05 — color picker (iteration #25, item 13 final slice → item done)
- **Task:** BACKLOG NEXT#13 last slice — hyprpicker → clipboard from
the menu + a direct bind. Item 13 complete; deleted from BACKLOG.
- **Did:** hyprpicker into systemPackages (next to grim/slurp); rofi.nix
`colorpicker` mode (hyprpicker → wl-copy → hex-in-toast, Esc = silent
cancel) + self-gated Tools "Color picker" row + usage line;
keybinds.nix SUPER+CTRL+P row (feeds bind + cheatsheet).
- **Verified:** V0; V1 — template-home AND template-system build;
rendered nomarchy-menu bash -n OK with mode + gated row; cheatsheet
txt has the SUPER+CTRL+P row; hyprland conf has the bind; system
toplevel ships sw/bin/hyprpicker. Picking needs a compositor → V3.
- **Pending:** V3 queued (loupe → click → wl-paste round-trip).
- **Next suggestion:** #15 (display profiles) needs a design pass
written into the entry first; #16 (greeter theming) is the next
directly codeable item — or #14's bump workflow (eval-tier gate).
## 2026-07-05 — low-battery notifications (iteration #24, item 13 slice)
- **Task:** BACKLOG NEXT#13 second slice — the bar colors the battery
at 25/10% but nothing *notified*.
- **Did:** pkgs/nomarchy-battery-notify (shellcheck-gated watcher:
polls the same sysfs the bar reads — type=Battery, scope≠Device —
one toast per downward crossing: normal@25, critical@10; re-armed by
charging; self-gates on battery presence → desktop no-op) + overlay.
HM unit in modules/home/battery-notify.nix behind
nomarchy.batteryNotify.enable (default on), graphical-session-bound,
libnotify on the unit PATH (notify-send resolved from PATH so the
check can shim it). README row. Chose the tiny watcher over
poweralertd: its UPower thresholds (20/5) wouldn't match the bar,
and it toasts every plug/unplug.
- **Verified:** V0; V1 — template-home builds, unit present; V2 GREEN —
new checks.battery-notify (test_power fake battery): self-gate exits
0 without a battery, silence at 80%, exactly one low toast crossing
25, one critical-urgency toast crossing 10, charging re-arms → a
second drain toasts again.
- **Pending:** V3 queued (real toast through swaync while draining).
- **Next suggestion:** item 13 final slice — hyprpicker color picker
(Tools entry + SUPER+CTRL bind).
## 2026-07-04 — caffeine/idle-inhibit toggle (iteration #23, item 13 slice)
- **Task:** BACKLOG NEXT#13 first slice — idle_inhibitor in the
generated bar (summer-night had it; reverse parity gap).
- **Did:** waybar.nix: idle_inhibitor module (󰾪/󰅶, tooltips, placed
after the ⏺), CSS membership + `.activated { color: @warn; }`.
summer-day: module + CSS mirrored from summer-night (same glyphs,
no tint — glyph change is the state signal there). keybinds.nix
extra row → cheatsheet mentions the bar click.
- **Verified:** V0; V1 — template-home built; rendered config has the
module second in modules-right with correct glyphs; CSS rules
present; cheatsheet row renders. Inhibit behaviour needs a session
→ V3 queued (hold past the lock timeout).
- **Pending:** V3. Item 13 remaining slices: low-battery
notifications, color picker.
- **Next suggestion:** item 13 next slice (low-battery notifications,
gate on power.laptop) — or #14 lock-bump CI if variety is better.
## 2026-07-04 — screen recording in Capture (iteration #22, item 12)
- **Task:** BACKLOG NEXT#12 — record region/screen from the menu, stop
from the bar.
- **Did:** modules/home/recording.nix: `nomarchy-record`
start/stop/active/status — wl-screenrec first, wf-recorder fallback
if it dies at startup (no-VAAPI case), SIGINT-finalized stop,
runtime pidfile, signal-8 bar pokes. Capture menu: 4 record rows that
self-swap to "■ Stop recording" while one runs. Waybar: self-gating
custom/recording (⏺ REC, @bad red), click = stop — the one stop
surface. Parity: both summer whole-swap bars got the module + CSS
(@red). DEVIATIONS: saves to ~/Videos/Recordings (an .mp4 in
Pictures/Screenshots felt wrong — same timestamp naming though);
audio = two extra explicit rows, not a toggle state.
- **Verified:** V0; V1 — template-home builds; rendered bar config has
the module (first in modules-right) + CSS rule; summer jsonc still
parse; nomarchy-record dry-run: status empty+exit 0 idle, active
exit 1, usage exit 2; both recorders in home-path. Real capture
needs a session → V3 (headless GL recipe can't confirm an encoder).
- **Pending:** V3 recording exercise (incl. VAAPI-vs-fallback check).
- **Next suggestion:** NEXT#13 niceties, one slice: idle-inhibit
(caffeine) toggle — reverse-parity gap already noted in the item.
## 2026-07-04 — state-file validation & friendly errors (iteration #21, item 11)
- **Task:** BACKLOG NEXT#11 — hand-edited state must fail with field +
problem + fix, never a Nix stack.
- **Did:** (a) tool: validate_state() (appearance schema = hard errors;
settings.*/unknown keys = warnings), `validate` subcommand,
validate-BEFORE-write in write_state (invalid set/apply never touches
disk), JSON syntax errors now say line/column/fix. (b) theme.nix:
same schema at eval time on `parsed` (post-defaults, so sparse files
stay fine); throws a multi-line message naming each field, the got-
value, and the three fix paths. Doctor's JSON fix-text now points at
`validate`.
- **Verified:** V0; V1 — new checks.theme-sync-validate GREEN: good
template passes, trailing comma → line/col message, bad hex/type →
field named, unknown key → warning only, invalid `set` refused with
the file byte-identical (cmp). Negative eval of a broken scratch
downstream through mkFlake shows the friendly throw (both fields);
good-path template-home still builds.
- **Pending:** nothing — no hardware dependency.
- **Next suggestion:** NEXT#12 (screen recording) or #13 niceties
slice; #12 first (it reshapes the Capture submenu the OCR LATER item
waits on).
## 2026-07-04 — nomarchy-doctor (iteration #20, item 10)
- **Task:** BACKLOG NEXT#10 — one-shot read-only health check.
- **Did:** pkgs/nomarchy-doctor (writeShellApplication → shellcheck-
gated; a package so the VM check runs on a minimal node): failed
system+user units (user bus self-skips), disk space on real
filesystems only (fstype allowlist skips tmpfs/9p → VM-safe; device
dedupe), state-file parses + git-tracked, flake dirty/behind
(warn-level), generation age via the profile SYMLINK mtime (store
paths are epoch-1), snapper timer when enabled. Every ✖ prints its
fix; exit 1 on any ✖. Wired: overlay, systemPackages, System
Doctor menu row (terminal), README §5.
- **Verified:** V0; V1 package build (shellcheck) + real-hardware
smoke: correctly flagged a genuinely failed user unit on the dev box;
V2 GREEN — VM test: induced failed unit → exit 1 + names it + prints
fix; reset-failed → healthy exit 0. Menu rebuild bash -n ok.
- **Pending:** V3 menu-row check queued. Waybar-warning follow-up →
LATER.
- **Next suggestion:** NEXT#11 (state-file validation) — doctor's
"parses" check is its little sibling; 11 adds validate-before-write
+ eval-time schema messages.
## 2026-07-04 — System Rollback menu (iteration #19, item 9b → item 9 done)
- **Task:** BACKLOG NEXT#9 half (b) — undo one menu away.
- **Did:** new `rollback` case in nomarchy-menu (System submenu row):
lists the last 10 `home-manager generations` (newest marked current),
picking one activates it in a terminal — HM's supported rollback,
reversible, so no typed-yes. System-level undo LINKS OUT by design:
Snapshots row → existing flow; "boot an older generation (how)" →
instruction notification pointing at docs/RECOVERY.md §3 (no
destructive reimplementation in rofi). RECOVERY §1 cross-references
the menu path. Item 9 complete → deleted.
- **Verified:** V0; V1 — template-home built, generated nomarchy-menu
bash -n clean, row-render + pick-parse logic run against a fixture
`generations` output (id/path extraction correct). V2 skipped
deliberately: the only novel logic is the parsing (fixture-covered);
the activate step is HM's own mechanism. V3 queued.
- **Pending:** V3 rollback-menu exercise on hardware.
- **Next suggestion:** NEXT#10 `nomarchy-doctor` (V2-able in a VM).
## 2026-07-04 — sys-update/-rebuild what-changed diff (iteration #18, item 9a)
- **Task:** BACKLOG NEXT#9 half (a) — a human diff after system rebuilds.
- **Did:** both sys-update and sys-rebuild (twins stay twins) capture
/run/current-system before, and after the switch print
`nvd diff before after` (store-path-pinned ${"$"}{pkgs.nvd}, `|| true`
so it can't fail a succeeded rebuild; identical-path fast-path says
"no changes"). README §3 shortcut block notes it.
- **Verified:** V0; V1 — template-system toplevel rebuilt, generated
scripts extracted + bash -n, nvd smoke-run against the real closure
(correct output format). Runtime behaviour is a trivial shell path —
no VM needed.
- **Pending:** half (b) — the System → Rollback menu flow (desktop
generations picker; system → boot menu + snapshots pointer).
- **Next suggestion:** item 9b — it completes the item; V2 the
generation-activate path.
## 2026-07-04 — viewers + mime defaults (iteration #17, item 8)
- **Task:** BACKLOG NEXT#8 — PDF/image viewers + xdg-mime defaults.
- **Did:** modules/home/viewers.nix (`nomarchy.viewers.enable`, default
on): programs.zathura (Stylix themes it — target added to stylix.nix's
explicit list, autoEnable=false bit us first try) + imv.
modules/home/mime.nix (`nomarchy.mime.enable`): mkDefault
xdg.mimeApps for pdf→zathura, image/*→imv, av→mpv, text→code,
dir→thunar, html/http(s)→firefox as INERT entries (browser Decision
untouched — absent .desktop entries are skipped by GIO). README rows
added (option-docs enforces them now). DEVIATION from the item text:
viewers are a module toggle, not template packages — zathura's
theming needs its HM module, which is exactly the "real config"
boundary of the toggle discipline.
- **Verified:** V0; V1 — template-home generation builds; asserted
rendered mimeapps.list (all associations), themed zathurarc
(Tokyo Night rgb), imv/zathura in home-path. V3 smoke queued.
- **Pending:** V3 open-a-file smoke; browser Decision now smaller
(inert entries ship; only "active in suite?" remains).
- **Next suggestion:** NEXT#9 (update & rollback UX) — or #10 doctor.
## 2026-07-04 — docs slice (c): install story — item 6 COMPLETE (iteration #16)
- **Task:** BACKLOG NOW#6 slice (c), the last one — install/first-run
narrative end to end.
- **Did:** read README §2 (ISO/installer) → §3 (downstream) → template
README as one story; checked the facts en route: mkFlake's arg
surface == the §3 table (src/username/hardwareProfile/system, lib.nix),
disko really makes @snapshots + the hibernation swapfile, .#default /
.#me names consistent. One seam fixed: §3 showed `nix flake init -t`
but never handed off to the template README's six first-run steps —
connective paragraph added (+ "the installer does all of this").
Item 6 done → deleted from BACKLOG (NOW now empty); ROADMAP § Full
docs review flipped to ✓ with the slice summary.
- **Verified:** V0 (docs-only) — flake check clean pre-commit.
- **Pending:** docs-site-vs-Markdown stays a Decision.
- **Next suggestion:** NEXT#8 (viewers + mime defaults) is the new queue
head; note its "Default browser" Decision dependency — the mime module
half can proceed with mkDefault degradation regardless.
## 2026-07-04 — docs slice (d): recovery runbook (iteration #15, item 6d)
- **Task:** BACKLOG NOW#6 slice (d) — write the undo story.
- **Did:** docs/RECOVERY.md, symptom-ordered: (1) bad theme/HM change →
generations / apply-known-good / git-revert ~/.nomarchy; (2) desktop
won't start → Ctrl+Alt+F2, greetd + user journals, VM-GL and
preactivate-log pointers; (3) system change → systemd-boot older
generation (kept: 10) + make-it-stick via sys-rebuild; (4) files →
btrfs-assistant / sudo nomarchy-snapshots (undochange, root rollback,
typed-yes); (5) ISO + nixos-enter. Every command checked against the
tree (greetd cfg, configurationLimit, snapshots script actions,
helper names). Linked from README tree + §5.
- **Verified:** V0 (docs-only) — flake check clean pre-commit.
- **Pending:** slice (c) install-story read-through — the last of item 6.
- **Next suggestion:** finish item 6 with slice (c).
## 2026-07-04 — docs slice (b): drift pass (iteration #14, item 6b)
- **Task:** BACKLOG NOW#6 slice (b) — OVERRIDES.md / TESTING.md /
templates/downstream/README.md vs the live tree.
- **Did:** spot-checked every factual claim: mkDefault sites (hyprland
behaviour knobs, ghostty paddings, waybar mainBar/style), bind-list
normal priority, `set ui.gapsOut` CLI key, awww(-daemon) naming,
21 presets, tokyo-night's 4 wallpapers, summer-night's light @fg bar,
live `,highres` mkForce, .gitea/workflows/check.yml, QEMU gl flags,
template `.#me` + ~/.nomarchy/NOMARCHY_PATH. All accurate. Fixed the
two real gaps: OVERRIDES §2 predated `nomarchy.monitors` (now routes
there + quick-ref row); TESTING §1 lacked the shell-syntax check §1b
claims CI runs (added `bash -n`).
- **Verified:** V0 (docs-only; flake untouched — check run pre-commit).
- **Pending:** slices (c) install story, (d) recovery runbook.
- **Next suggestion:** item 6 slice (c) or (d) — (d) is self-contained
and ships user value (the runbook).
## 2026-07-04 — docs slice (a): option-surface reconcile (iteration #13, item 6a)
- **Task:** BACKLOG NOW#6, first slice — README tables vs live options.
- **Did:** split item 6 into slices (ad) in BACKLOG. Reconciled: 9
undocumented options (autoTimezone.enable!, camera.hideIrSensor +
irMatch!, intel.guc, amd.pstate/vaapi, package, system.stateFile,
restic.paths) — rows/inline mentions added, no stale rows found.
Made it permanent: `checks.option-docs` — flake.nix eval-walks option
names from the 4 option files (config halves stay lazy; dummy args)
→ tools/check-option-docs.py enforces row-or-`.leaf`-mention + flags
stale rows.
- **Verified:** V0; V1 — check builds green (78 options); negative test:
same script vs the pre-fix README reports exactly the 9 gaps.
- **Pending:** slices (b) doc drift pass, (c) install story, (d)
recovery runbook.
- **Next suggestion:** item 6 slice (b) — or NEXT#8 (viewers+mime) for
variety; both fine.
## 2026-07-04 — Back audit (iteration #12, item 24)
- **Task:** BACKLOG NOW#24 — every list menu ends in ↩ Back.
- **Did:** audited every `rofi -dmenu` call site in the generated
nomarchy-menu + nomarchy-vpn. Already compliant: power, power-profile,
theme, clipboard, files, audio, display (both stages), capture,
tools, system, VPN main/import/exit-node/tailscale. Fixed: keybinds
cheatsheet (Enter was a silent exit) now ends in ↩ Back → root
picker. Documented the exceptions in rofi.nix: external modi
(calc/emoji/networkmanager_dmenu/rofi-pulse-select) can't take an
injected row (Esc = back); free-text prompts (web/ask) have no list.
- **Verified:** V0; V1 — built nomarchy-menu, bash -n OK; awk audit
over the generated script: only rows lacking back are the root
picker (by design) and the free-text prompt. V3 spot-check queued.
- **Pending:** V3 spot-check. NOW now holds only item 6 (docs, [big]).
- **Next suggestion:** item 6 first slice, or NEXT#8 (viewers+mime).
## 2026-07-04 — network menu blank rows (iteration #11, item 22)
- **Task:** BACKLOG NOW#22 — nameless entries in the network picker.
- **Did:** read the pinned networkmanager_dmenu 2.6.2 source: with
`compact = False` (our config!) it inserts literal empty rows as
section separators (eth/wifi/vpn) — the systematic blank entries.
Flipped config.ini to `compact = True` (also unpadded row format,
better in rofi's proportional font). Hidden-SSID APs additionally
render ONE nameless sec+bars row (dedup keeps one; not config-
filterable) — documented, V3-gated, source-patch follow-up if seen.
- **Verified:** V0; V1 — rendered config.ini shows compact = True.
Behavior needs Wi-Fi hardware → V3 queued.
- **Pending:** V3 on the Latitude (no separators; hidden-AP residual?).
- **Next suggestion:** item 24 (Back audit) — grep-the-generated-script.
## 2026-07-04 — waybar/rofi palette-safe pairings (iteration #10, item 27)
- **Task:** BACKLOG NOW#27 — generated waybar/rofi CSS invisible on
on-surface palettes (flexoki-light runs it; summers whole-swap).
- **Did:** waybar.nix generated CSS: module row + #window →
alpha(@text, 0.85); inactive workspaces / dnd / pulseaudio.muted →
alpha(@text, 0.5) (gruvbox's muted/base was 1.27). rofi.nix: @dim →
text+80, @surface → text+1a (#RRGGBBAA fg tints). Contrast check
docstring/pairings now cover all three generated surfaces. Status
glyphs (good/warn/bad on base, worst 2.05) left as-is — palette
design, human call. Generated bar only; whole-swaps untouched.
- **Verified:** V0; V1 — checks.theme-contrast green (21 themes),
downstream-template-home builds, rendered waybar style has zero
@subtext/@muted/@surface, rendered custom.rasi shows fg-derived
tints. NOT visually verified — V3 queued (flexoki-light eyeball).
- **Pending:** V3 flexoki-light + dark-theme regression spot-check.
- **Next suggestion:** item 22 (network menu rows) — V1-actionable.
## 2026-07-04 — swaync contrast fix + theme-contrast check (iteration #9, item 25)
- **Task:** BACKLOG NOW#25 — swaync text invisible on summer-day.
- **Did:** swaync.nix now uses only palette-safe pairings: body/summary
= @text on @base (worst ratio 5.18 across all 21 themes; subtext/base
was 1.00 on summer-day + flexoki-light), hover rows + widget buttons
= alpha(@text, 0.1) tints (contrast by construction); dropped the
subtext/surface defines. NEW permanent check `checks.theme-contrast`
(tools/check-theme-contrast.py) asserts the hex-on-hex text pairings
across every theme JSON. Audit found the same mispairing in generated
waybar/rofi CSS, live on flexoki-light (no whole-swap) → NOW item 27.
- **Verified:** V0; V1 — checks.theme-contrast builds green (21 themes),
rendered services.swaync.style contains zero @subtext/@surface.
NOT V2/V3-verified visually — queued (Bernardo is at the Latitude).
- **Pending:** V3 notify-send readability check on summer-day; item 27.
- **Next suggestion:** item 27 (same bug class, waybar/rofi surfaces).
## 2026-07-04 — SUPER+? bind fix (iteration #8, item 26)
- **Task:** BACKLOG NOW#26 — cheatsheet bind never fires on hardware.
- **Did:** keybinds.nix: `$mod, question` → `$mod SHIFT, question`
(? is Shift+/, Hyprland matches the exact modmask); rofi.nix
prettyKeys collapses "SHIFT + ?" → "?" so the row stays SUPER + ?.
Gotcha added to MEMORY.md (shifted keysyms need SHIFT in mods).
- **Verified:** V0 (flake check --no-build); V1 both surfaces — eval'd
hyprland settings.bind renders `$mod SHIFT, question, exec,
nomarchy-menu keybinds`, and built nomarchy-menu's cheatsheet file
renders `SUPER + ?`. No other shifted keysyms in the bind list.
- **Pending:** V3 queued (SUPER+? on the Latitude after home-update +
relogin).
- **Next suggestion:** item 25 (swaync contrast on summer-day).
## 2026-07-04 — Latitude hardware sweep #2 (Bernardo; results booked, no code)
- **Task:** V3 sweep on the Latitude after pulling ≥18b8545 + reboot.
- **PASS:** waybar supervisor (theme switches clean; `pkill -x waybar`
respawns instantly); snapshots — themed polkit prompt → GUI as root,
fzf fallback works (restore/rollback gate not exercised → residual
queued); `sys-rebuild`; night-light enable/disable; auto-commit
(theme commits + the toggle self-commit); hibernate single-unlock +
suspend-still-locks (1b0eeea CONFIRMED).
- **PASS (late confirm):** auto-timezone — `timedatectl` shows
Europe/London.
- **Bug:** SUPER+? never fires — `$mod, question` misses because a real
? carries SHIFT in the modmask → BACKLOG 26 (bind-only: the menu
itself opens fine from a terminal). SUPER+SHIFT+K no-op was the
comma-layout gate working (none configured) — retest queued.
- **Next suggestion:** item 25 (swaync contrast) or 26 (cheatsheet bind).
## 2026-07-04 — Waybar resilience + sys-rebuild (iteration #7, items 21+23)
- **Task:** the two top Latitude findings.
- **Did:** (21) `nomarchy-waybar` supervisor (waybar.nix, exec-once'd
from hyprland.nix): ANY waybar exit respawns, crash-loop guard (5
fast exits → critical notify + stop), TERM trap for a real stop.
theme-sync now prefers a clean `pkill -x waybar` when the supervisor
is running (restart with fresh config+style — avoids waybar's
in-place SIGUSR2 reload entirely, the suspected double-reload race
with reload_style_on_change during symlink flips); SIGUSR2 stays as
the unsupervised fallback; reload_style_on_change kept for manual
home-update restyles. (23) `sys-rebuild` — snapshot-first rebuild
against the current lock, no `nix flake update`; README §3/§5 + motd.
- **Verified:** V0; V1 (HM renders `exec-once=nomarchy-waybar`,
supervisor bash -n; `sys-rebuild` in systemPackages). V2: headless
software-GL desktop VM (recipe from memory — gotchas hit: node needs
the overlay, users.users.<u>, useGlobalPkgs) — SIGKILL→new pid, clean
kill→respawn, SIGUSR2→alive. Result recorded below once green.
- **Pending:** V3 on the Latitude: theme switch survives / bar returns.
- **Next suggestion:** item 22 (network menu rows) or 24 (Back audit).
## 2026-07-04 — Keyboard cycle bind + parity (iteration #6) + Latitude triage
- **Task:** BACKLOG NOW#5; mid-iteration Bernardo delivered Latitude
5410 hardware-QA findings (the machine got a real session!).
- **Did:** `multiLayoutBinds` in keybinds.nix (SUPER+SHIFT+K →
`switchxkblayout current next`), gated in hyprland.nix AND the rofi
cheatsheet on the same comma-in-layout condition; `hyprland/language`
+ `#language` CSS added to both summer whole-swaps (shows even
single-layout there — static JSON can't gate; deliberate). Reverse
parity gap found (summer-night has idle_inhibitor, generated bar
doesn't) → folded into item 13. **Triage:** four new NOW items from
hardware: #21 Waybar crash on theme switch + no respawn (top),
#22 nameless network-menu entries, #23 `sys-rebuild` (no-update
rebuild), #24 Back-everywhere audit.
- **Verified:** V0 (JSON + flake check) + V1 both directions
(single-layout build: no bind; `us,de` via extendModules: bind + the
cheatsheet row render; summer JSONs parse).
- **Pending:** V3 cycle-bind check queued; items 21/22/24 carry V3
re-checks on the Latitude.
- **Next suggestion:** #21 (waybar crash — stability pillar, user-hit).
## 2026-07-04 — Webcam follow-ups (loop iteration #5, quick item)
- **Task:** BACKLOG NOW#7 (Bernardo asked for "a quick item").
- **Did:** template `home.packages` gains a commented "Webcam tuning"
pair (cameractrls GUI, v4l-utils CLI — bare packages, so template not
toggle, per the option-surface rule); the portal/Flatpak libcamera IR
gap is written up as a PROPOSED item with three options (recommend:
document-only now, libcamera-rule investigation when the T14s is
available). CI runs #60/#61 confirmed green (earlyoom + snapshot fix).
- **Verified:** V0 (flake check — template evals through mkFlake;
changes are comments + agent docs).
- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity) —
the last code item in NOW besides the docs pass.
## 2026-07-04 — Snapshot GUI un-broken + polkit agent (loop iteration #4)
- **Task:** BACKLOG NOW#4 — btrfs-assistant segfault.
- **Did:** re-diagnosed instead of overriding. gdb: crash is in
libbtrfsutil's *unprivileged* subvolume iteration (btrfs-progs 6.17.1;
upstream fix `886571653` post-6.17.1; symbol versions verified — not
ABI drift). VM A/B: root works (exit 0), user crashes (139). The
launcher runs it as root → GUI fine *if* pkexec can prompt — and the
distro shipped **no polkit agent** (all pkexec silently failed; the
actual root cause of "GUI is dead"). Shipped: hyprpolkitagent
(exec-once, Stylix-themed Qt), menu re-pointed to the launcher with
the fzf flow as fallback, `checks.snapshot-gui` canary (root path +
offscreen event loop on real btrfs). No btrfs-progs patch — root-side
flows don't hit the bug; the fix arrives with a lock bump.
- **Verified:** V0 + V2 (snapshot-gui check, run for real) + V1 (HM
generation renders the agent exec-once; menu bash -n).
- **Pending:** V3 queued — first on-hardware themed polkit prompt.
- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity).
## 2026-07-04 — CI is green (run #58) — item 20 main half closed
- **Task:** monitor follow-up, no code. Run #58 (the .gitea path fix)
**succeeded** — first green CI: the container recipe works on the
live runner, eval tier confirmed end to end.
- **Did:** BACKLOG item 20 reduced to its stretch (KVM runner → enable
vm-checks), moved to NEXT. Run #59 (earlyoom push) monitored.
- **Next suggestion:** NOW#4 (btrfs-assistant override attempt).
## 2026-07-04 — Memory-pressure protection shipped (loop iteration #3)
- **Task:** BACKLOG NOW#3 — default-on userspace OOM killer.
- **Did:** `modules/nixos/oom.nix`: earlyoom (mkDefault on, desktop
notifications, session-plumbing `--avoid` list, no `--prefer`) +
`systemd.oomd.enable = false`. Choice rationale: Hyprland session is
one cgroup scope → oomd's cgroup-level kill would nuke the desktop;
earlyoom is process-level. Found en route: nixpkgs default-enables
oomd *inert* (enable=true, all slice toggles false). README "beyond
the surface" note added.
- **Verified:** V0 (flake check) + **V2** — new `checks.oom-protection`
runNixOSTest, run for real: hog (chunked allocator, 686 MB peak in a
1 GB VM) SIGTERM'd by earlyoom in 0.1 s, bystander unit survived,
oomd asserted inactive. 25 s test, cheap enough for the suite.
- **Pending:** nothing hardware-specific (behavior fully VM-provable).
- **Next suggestion:** NOW#4 (btrfs-assistant override attempt). Also:
CI run #58 (the .gitea fix) was *running* at commit time — check its
outcome next tick; this push triggers #59.
## 2026-07-04 — CI fix: wrong workflow dir (correction to iteration #2)
- **Task:** Bernardo reported no run despite an active runner + shared
his compose file: the server is **Gitea** (gitea/act_runner), not
Forgejo — I'd shipped the workflow to `.forgejo/workflows/`, which
Gitea never reads. The legacy repo's `.gitea/workflows/` path was the
clue I under-weighted.
- **Did:** `git mv` → `.gitea/workflows/check.yml`; corrected the
Forgejo references in the workflow header, TESTING.md §1b, BACKLOG
item 20 (now: watch the first run; runner was never the problem);
MEMORY.md gains the Gitea-not-Forgejo line.
- **Verified:** the push of this commit is itself the test — the run
must appear; monitoring.
- **Pending:** first green run (item 20); runner-container memory
headroom is the watch-out.
## 2026-07-04 — CI checks-on-push shipped (loop iteration #2)
- **Task:** BACKLOG NOW#2 — Forgejo Actions workflow.
- **Did:** `.forgejo/workflows/check.yml` (push to main/v1 + dispatch):
flake check --no-build, theme-sync py_compile, bash -n over tracked
.sh. Scoped to the **eval tier** deliberately — API probing +
archaeology of the legacy `.gitea/workflows/check.yml` (57 runs on
this instance) showed the runner is an act_runner docker container
(no KVM/systemd), so the VM suite can't run there; a commented
`vm-checks` job documents the KVM-runner upgrade path. Legacy's
hard-won container gotchas (nixbld setup, sandbox=false for Stylix
IFD, Nix 2.31.5 pin) carried over verbatim. TESTING.md §1b added.
- **Verified:** V0 locally (same commands the workflow runs, minus the
container Nix install) + yq YAML parse. The real green run needs the
runner to be alive — **not confirmable from here** (runners API is
401 unauthenticated); watching the run after push.
- **Pending:** new `[human]` item 20 — confirm/re-register the runner;
stretch: a KVM runner unlocks the vm-checks job.
- **Next suggestion:** NOW#3 (memory-pressure protection).
## 2026-07-04 — Opt-in auto-commit shipped (loop iteration #1)
- **Task:** BACKLOG NOW#1 — auto-commit of state mutations (in-flake
state Phase 4). First autonomous /loop iteration.
- **Did:** `settings.autoCommit` live-read by nomarchy-theme-sync;
pathspec-limited commit of theme-state.json on apply/set (fires if flag
on before OR after the write → the off-toggle commits too); identity
fallback; same-value no-op; `bg` excluded. Menu: System Auto-commit
(instant, self-gated on `.git`). Rider: `get` prints booleans as JSON
`true`/`false`, fixing the stuck "Auto timezone (on/off)" menu label.
BACKLOG numbering switched to stable IDs (no renumbering).
- **Verified:** V0 (py_compile, flake check) + V1 (HM generation builds;
generated menu `bash -n`) + a 7-assertion sandbox git-repo round-trip
(enable/set/same-value/disable/post-disable/apply/identity-fallback).
- **Pending:** V3 queued in HARDWARE-QUEUE (on-machine toggle + apply).
- **Next suggestion:** NOW#2 (CI checks-on-push) — but its runner half
may need Bernardo to register a Forgejo runner; deliver the workflow
regardless.
## 2026-07-04 — Backlog revision pass (human-requested)
- **Task:** Bernardo asked for a philosophy-aligned revision of BACKLOG.md
("I'm sure I'm forgetting important items").
- **Did:** verified guessed gaps against the tree first (GC/optimise/boot
limit + all 21 previews already ship — dropped those ideas). Added the
confirmed gaps: CI-on-push promoted to NOW#2 (agents push to main now),
memory-pressure protection NOW#3 (nothing mitigates OOM; the release
bump died to one), viewers+mime defaults #8, update/rollback UX #9,
nomarchy-doctor #10, state-file validation #11, screen recording #12,
niceties batch #13 (idle-inhibit, low-battery notify, hyprpicker), OCR
in LATER; recovery runbook folded into the docs item; zram + default
browser added to Decisions.
- **Verified:** V0 (docs-only).
- **Pending:** Bernardo to sanity-check the new NOW ordering, esp. CI at
#2 and the two new Decisions.
- **Next suggestion:** unchanged — NOW#1 (opt-in auto-commit).
## 2026-07-04 — Bootstrap the loop infrastructure (this commit)
- **Task:** create `agent/` (human-requested): LOOP/GOALS/BACKLOG/
CONVENTIONS/MEMORY/HARDWARE-QUEUE/JOURNAL, root CLAUDE.md, README +
ROADMAP pointers.
- **Did:** reworked the forward half of docs/ROADMAP.md into the tiered
BACKLOG (5 NOW / 6 NEXT / LATER / Decisions); collected every pending
on-hardware check into HARDWARE-QUEUE.md; seeded MEMORY.md from the
ROADMAP's decision records.
- **Verified:** V0 (docs-only change; `nix flake check --no-build` run to
confirm the tree still evaluates).
- **Pending:** Bernardo to sanity-read GOALS.md pillars + BACKLOG tiering
— reorder freely, the order *is* the instruction.
- **Next suggestion:** BACKLOG NOW#1 (opt-in auto-commit — Phase 4 of
in-flake state, already the agreed next step).