# 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 3–5 entries to orient. Keep an
entry under ~15 lines; durable lessons go to MEMORY.md, not here. When
this file grows past ~25 entries, rotate the tail to
[JOURNAL-ARCHIVE.md](JOURNAL-ARCHIVE.md) (history, never edited).

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 2–4 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-12 — CI OOM: memory-bounded eval for check.yml/bump.yml
- **Task:** Bernardo: check.yml always red since he capped the runner
  container at 2 GB (uncapped it OOM'd the whole 4 GB VPS); bump.yml
  status unknown.
- **Did:** Measured (GNU time, eval-cache off): one-process `nix flake
  check --no-build` peaks **6.0 GB** RSS — that's the whole story. New
  `tools/ci-eval.sh`: same coverage, ONE output per nix process (heap
  freed between outputs); both workflows now call it. bump.yml also got
  max-jobs=1/cores=2. Facts: bump.yml WORKS — landed 8fded63 on
  2026-07-06, on schedule; ROADMAP's "no CI today" was stale → fixed.
- **Verified:** V1+ — ci-eval.sh ran green locally end-to-end (3:22);
  per-output cold peaks measured: worst check 0.99 GB
  (hardware-toggles), nomarchy toplevel 0.78 GB, HM 0.60 GB, **except
  nomarchy-live 2.69 GB** (live ISO eval) — fits only with the
  container's swap allowance (docker default --memory-swap = 2×memory);
  if Bernardo set swap off, that one output still OOMs → server-side
  one-liner (--memory=2g --memory-swap=6g) or a skip-hatch decision.
- **Pending:** real proof is the next push's Actions run on the VPS —
  watch nomarchy-live's step; can't be verified from this machine.
- **Next suggestion:** confirm green run on the remote, then hardware
  V3 batch.

## 2026-07-12 — parallel fingerprint-or-password (interactive, promoted)
- **Task:** Bernardo promoted the PROPOSED "fingerprint or password in
  parallel" item live; wants it default-on for fingerprint-PAM users.
- **Did:** Packaged pam-fprint-grosshack v0.3.0 (pkgs/, source-reviewed
  first: all failure paths → PAM_AUTHINFO_UNAVAIL, typed password only
  ferried via AUTHTOK to pam_unix try_first_pass — can't lock out
  password login). New `nomarchy.hardware.fingerprint.parallel`
  (default true; false = stock sequential). Wiring swaps stock fprintd's
  rule modulePath (mkForce) so ordering is inherited. README/template
  rows; BACKLOG PROPOSED entry closed into ROADMAP ✓.
- **Verified:** V2 — checks.hardware-toggles extended to three nodes and
  green: parallel node asserts grosshack line before pam_unix in
  /etc/pam.d/sudo AND `sudo -S` with the right password succeeds /
  wrong password fails with NO reader present (the lockout invariant);
  seqpam node = stock pam_fprintd, no grosshack; nopam = neither.
  Package builds (meson needed libpam-wrapper + python3). flake check
  green.
- **Pending:** V3 on the AMD dev box (HARDWARE-QUEUE): the actual
  type-or-touch race, fprintd-stopped fallback, hyprlock/greeter
  behavior after a fingerprint win (pthread_cancel'd prompt quirk).
- **Next suggestion:** dock + fingerprint V3 batch on hardware, then
  #89 slice 3.

## 2026-07-12 — undock blackout rescue (interactive follow-up)
- **Task:** Bernardo: "if I turn the laptop screen off and undock, does
  the panel come back?" Answer had to be tested, not recalled.
- **Did:** Built tools/monitor-fallback.nix (softGL desktop VM, crib of
  theme-shot): headless "external" + menu's exact disable + output
  remove. Verdict: Hyprland 0.55.4 does NOT re-enable → shipped
  `rescue_blackout` in the display hotplug watcher (immediate + retried
  keyword re-enable of disabled outputs on zero-active, toast).
- **Verified:** V2 for the hazard (VM reproduced the dead state);
  rescue itself is V1+components — the VM crashes before rescue can act
  in 4/5 runs (aquamarine ABRT in dispatchIdle on the zero-output
  state; control with panel active survives 5/5 — discriminator run).
  Crash predates rescue (run with rescue provably inert also crashed).
  Also fixed: `grep -c .` exit-1-at-zero disabling the rescue's own
  trigger. flake check green.
- **Pending:** real-dock V3 (HARDWARE-QUEUE, "nothing important open"
  warning); if real DRM crashes too → rework row (mirror) or wait for
  Hyprland bump; consider reporting upstream with the coredump.
- **Next suggestion:** dock V3 results, then #89 slice 3.

## 2026-07-12 — dock audio auto-follow + docked menu rows (interactive)
- **Task:** Bernardo's dock test (= #87's V3): audio stayed on speakers,
  had to hand-tweak EasyEffects; wants docked Display/Audio menu options.
- **Did:** Root-caused live on Newton: WirePlumber's stored default
  (default-nodes) outranks the #87 priority rules (control test: new
  sink, no watcher → default pinned), and the running gen lacked the
  rules anyway; EE 8 follows the default fine (kcfg default true +
  observed links move). Shipped `modules/home/dock-audio.nix`
  (`nomarchy.dockAudio.enable`, on): pactl-subscribe watcher
  set-default-sinks new dock-class sinks (regexes imported from
  dock-audio-rules.nix) + toast + startup sweep. Menu: Display gains
  laptop-off/screen-on/move-workspace/swap rows (live-only keywords);
  Audio gains self-gated "Send output → <dock sink>" row.
- **Verified:** V2-equivalent live on Newton — watcher: fake dock-named
  null sink → default followed, unplug → fell back, EE links moved
  mid-playback (pw-link); menu: generated script bash -n, dock-row jq
  exercised against real pactl JSON, dispatchers probed on live
  Hyprland. flake check green; option-docs green. Beware: kill the
  subscribe pipe by PID — pkill -f self-matches the test shell.
- **Pending:** real-dock V3 ×2 in HARDWARE-QUEUE (watcher may miss
  sinks that pre-exist and only flip route availability — capture pactl
  subscribe + sink JSON if it fails again).
- **Next suggestion:** #89 slice 3 (Signal pixmap) or dock V3 feedback.

## 2026-07-12 — auto-commit sweep before pull/rebuild/home (interactive)
- **Task:** Bernardo's request (no BACKLOG item): with autoCommit on,
  hand edits to `~/.nomarchy` (his had 4) stayed forever-dirty —
  theme-sync's commit is pathspec-limited to theme-state.json by design.
- **Did:** New internal `nomarchy-autocommit` in nomarchy-lifecycle:
  live-reads `settings.autoCommit`, commits *everything* dirty with
  message `nomarchy: auto-commit before <label>` + file list in the
  body; called (`|| true`, never blocks) at the top of nomarchy-pull /
  -rebuild / -home. Exposed as `passthru.autocommit`; package exported.
  Docs/comments swept: README, RECOVERY, ROADMAP ✓ entry, rofi toggle
  text, control-center prompt, theme-sync docstring.
- **Verified:** V1+ — `nix flake check --no-build` green; new permanent
  `checks.lifecycle-autocommit` builds (real binary, sandbox repos:
  sweep/clean/off/non-repo); end-to-end on a *copy* of the real
  ~/.nomarchy via built nomarchy-rebuild with stubbed sudo/rebuild —
  the 4 real dirty files landed in one commit, tree clean. No VM boot:
  no session-visible surface. Real-machine confirmation lands after the
  next nomarchy-pull picks up this rev.
- **Pending:** nothing queued; Bernardo's actual ~/.nomarchy still has
  its 4 uncommitted files until his next pull/rebuild with the new rev.
- **Next suggestion:** back to BACKLOG (#89 slice 3, Signal pixmap).

## 2026-07-12 — #89 slice 2: EasyEffects tray icon themed monochrome (b98248f)
- **Task:** BACKLOG #89 slice 2 — EasyEffects SNI shows Papirus' full-
  colour blue equaliser, clashing with the monochrome tray.
- **Did:** Wrapped the resolved icon set in a `Nomarchy-icons` child
  theme (theme.nix) that Inherits it + ships a scalable, palette-`text`-
  coloured monochrome easyeffects glyph (the app's own -symbolic shape);
  made it the session icon theme. Single shared layer, so it covers all
  themes (all resolve to Papirus-*); colour tracks each theme via
  `checked.colors.text`. iconTheme feeds stylix+rofi, both fine via
  Inherits. Fixed a slip: `git add -A` swept a settings.local.json.tmp.*
  into the commit → untracked + gitignored (f9d5e2c).
- **Verified:** **V3 in Boreal** (Bernardo confirmed live). Mechanism
  pre-validated by GTK IconTheme lookup (override wins 16–32px; udiskie/
  nm/folder still Papirus). No theme uses a custom icon pack, so Boreal+
  Papirus covers the set. NB: cursor-warp doesn't refresh the tray —
  needs a waybar restart (new icon-theme cache).
- **Pending:** slice 3 — Signal (`:1.227`, generated pixmap, full-colour
  blue round icon); pixmap apps can't be fixed by name override.
- **Next suggestion:** #89 slice 3 (Signal) or close #89 if pixmap apps
  are accepted as documented-not-fixable.

## 2026-07-12 — tray menus truly fixed: counter-rules must live in waybar.css (8eb672b, 72e53e2)
- **Task:** Bernardo: icons still missing in the REAL nm-applet menu.
- **Did:** e8658fe's gtk.css cure worked in a plain GTK app but not in
  Waybar: Waybar adds its provider at GTK_STYLE_PROVIDER_PRIORITY_USER
  too (src/client.cpp) and wins the tie in practice (GTK 3.24) — proven
  by repro at priority 900 matching the live menu exactly. Only rules in
  the SAME stylesheet out-specify its `*`, so every waybar.css now
  carries a menu block after the reset: arrow 16px, check/radio 14px,
  separator 1px, `menu, menu *` font (labels are matched directly by
  `*`, inheritance never applies). D-Bus GetLayout first proved
  nm-applet exports per-network icon-data.
- **Verified:** **V3 on the real menu** — opened nm-applet's tray menu
  via wlrctl virtual-pointer clicks, grim captures: arrows, checkmarks,
  separators, Inter labels, Pica's icon, and every network's signal icon
  in the Available-networks submenu. Bar pills intact. NB: cursor-warp
  hover does not open GTK submenus (no motion events) — click the item.
- **Pending:** nothing for this item.
- **Next suggestion:** #89 slice 2 — the override-icon layer.

## 2026-07-12 — #90 correction: scoping regressed the bar; USER-priority cure (e8658fe)
- **Task:** Bernardo reports ac7e6bd broke Boreal's bar + icons still missing.
- **Did:** Two lessons. (1) `window#waybar *` carries id specificity, so
  the reset started *beating* the later class rules (pill radius/borders)
  that plain `*` correctly lost to — reverted all three stylesheets;
  arrows restored instead via stylix gtk.css (`menu menuitem arrow
  { min-width/height:16px }`) — USER priority (800) outranks waybar's
  APPLICATION (600) regardless of specificity, same mechanism as the
  existing check/radio rule. NB comments guard each `*` block.
  (2) The "missing icons" were also GTK3's `gtk-menu-images` default-off:
  set `gtk.gtk3.extraConfig.gtk-menu-images = 1`.
- **Verified:** **V3** — repro (no forced always-show-image): icon, check
  and arrow all render even with Boreal's reset injected screen-wide;
  bar screenshot shows pills restored. Flake check green.
- **Pending:** Bernardo eyeballs the real nm-applet menu (icons + arrow).
- **Next suggestion:** #89 slice 2 — the override-icon layer.

## 2026-07-12 — #76 closed: hibernate V3 PASSED on TuringMachine (this commit)
- **Task:** BACKLOG #76 `[human]` closure — Bernardo reports the V3 check.
- **Did:** Bernardo ran the real power-cycle on the T14s (LUKS + btrfs
  `@swap` swapfile): hibernate → power off → single LUKS unlock →
  session restored, "flawlessly". Deleted #76; HARDWARE-QUEUE resume
  entry pruned (outcome recorded here + ROADMAP ✓-entry updated);
  Decisions note marked shipped. The **no-swap Hibernate notify** check
  stays open in HARDWARE-QUEUE as the one leftover.
- **Verified:** V3 (human, real hardware). Docs-only commit otherwise.
- **Pending:** no-swap notify check (needs a swapless session or
  temporary `swapoff -a`).
- **Next suggestion:** #89 slice 2 — the override-icon layer.

## 2026-07-12 — #90 tray submenu arrows: root cause was waybar CSS (this commit)
- **Task:** BACKLOG #90 `[stuck]` (interactive, Bernardo driving on T14s).
- **Did:** Diagnosis was wrong — Papirus-Dark resolves `pan-end-symbolic`
  fine via its breeze-dark inheritance (breeze ships inside the nixpkgs
  papirus output). Real cause: Waybar's stylesheet applies process-wide,
  and the `* { min-height: 0; }` block collapsed the arrow node in the
  SNI tray menus Waybar hosts. Scoped the `*` block to `window#waybar`
  in boreal + executive-slate waybar.css and waybar.nix generatedStyle
  (tooltip rules restate the font — tooltips are separate toplevels);
  removed the go-next-symbolic remap workaround from stylix.nix.
- **Verified:** **V3** — live GTK3 repro app popping a dbusmenu-shaped
  menu, screenshotted via grim: arrow vanishes with old CSS injected
  screen-wide, renders with fixed CSS; property bisect pinned
  `min-height: 0` as the sole culprit. V1 flake check green.
- **Pending:** post-switch check that the real nm-applet menu shows the
  arrow (needs a human click on the tray icon).
- **Next suggestion:** #89 slice 2 — the override-icon layer.

## 2026-07-11 — interactive T14s session: migration follow-ups (50a5586…6967d89)
- **Task:** Escalation — Bernardo driving, live on the migrated T14s
  (first full session on real Nomarchy hardware; 7 commits).
- **Did:** autologin restored (migration missed the LUKS-machine line —
  fixed downstream + MIGRATION.md example); SUPER+0 → workspace 10;
  VPN tooltip names active VPNs (stuck-shield confusion was Tailscale);
  battery "Not charging" hold-band tooltip (+ 4 whole-swap themes);
  GTK submenu arrows (bundled SVG — Papirus lacks pan-end-symbolic and
  inherits uninstalled breeze); xdg.userDirs.setSessionVariables pinned
  (HM 26.05 flip); easyeffects gated on a live tray host (After=
  waybar.service was inert — waybar is supervisor-run, not a unit);
  #89 slice-1 SNI inventory recorded; #76 unblocked → `[human]`.
- **Verified:** **V3** throughout — live binds via `hyprctl binds`, GTK
  menu screenshot (arrow + check render), SNI watcher registration,
  deployed configs inspected; V1 flake check green on every commit.
- **Pending:** Bernardo: `nomarchy-rebuild` (autologin + new services),
  then the #76 hibernate power-cycle. #89 targets narrowed to
  EasyEffects (slice 2) + Signal (slice 3).
- **Next suggestion:** #89 slice 2 — the override-icon layer.

## 2026-07-11 — hotfix: Left=Back bound over rofi default → every menu dead
- **Task:** Escalation — V3 on the migrated T14s: all `nomarchy-menu`
  entry points (main, battery, power-profile, VPN) showed rofi's
  "Binding 'Left' is already bound" error dialog instead of a menu.
- **Did:** `rofi.nix` — release `kb-move-char-back` to `Control+b` before
  binding `-kb-custom-1 Left`, in both `rofi_menu` copies (menu + vpn).
  rofi hard-fails on duplicate bindings, and reports it in a GUI dialog,
  not stderr — stub tests and `bash -n` can never catch this class.
  Filed PROPOSED: EasyEffects 8 blue tray icon; parallel finger/password.
- **Verified:** **V3** — real rofi on the T14s: old flags reproduce the
  error dialog (screenshot), fixed flags render the menu; after HM
  switch, `nomarchy-menu` renders end-to-end. V0 flake check green.
- **Pending:** flatpak apps missing from drun = stale session env
  (relogin), not a code bug. EasyEffects icon + pam items in PROPOSED.
- **Next suggestion:** V3 sweep of the remaining 05bab55 session items.

## 2026-07-11 — post-migration fix batch (05bab55; entry backfilled)
- **Task:** Escalation — first real-hardware migration (TuringMachine →
  Nomarchy) surfaced a batch of annoyances; session crashed before
  journaling, entry written by the follow-up session.
- **Did:** Force fprintAuth off unless `fingerprint.pam`; first-boot toast
  waits for swaync + retries; night-light handles masked units; VPN menu
  `rofi_menu` no-op fixed; ← = Back in list menus; GTK menu contrast;
  mic-mute LED udev rule + sync; EasyEffects after tray; MIGRATION.md notes.
- **Verified:** V0 flake check; checks.first-boot; checks.hardware-toggles.
- **Pending:** V3 on the migrated machine. Follow-up found the downstream
  `~/.nomarchy` lock still pinned the *previous* commit — the user's rebuild
  excluded this batch (mic LED "regression" was just that). Also: HM
  `programs.chromium.extensions` from the old config wasn't carried over, so
  Chromium externally-uninstalled all extensions **and their data** (wallets
  included); recovered from the `pre-nomarchy-home` btrfs snapshot.
  MIGRATION.md browser note rewritten with the real mechanism.
- **Next suggestion:** V3 sweep of the 05bab55 items after a locked rebuild.

## 2026-07-11 — #88 neon-glass polish / finish
- **Task:** NEXT #88 — finish neon-glass (`VISION § D`), not quarantine.
- **Did:** Rewrote `themes/neon-glass/rofi.rasi` (was invalid `var()` CSS;
  now Boreal-style glass with `@colors`, cyan edge, soft selection).
  btop accent titles/selection; JSON ui rounding 14 + slight glass
  opacity; `preview.png` from theme-shot desktop. No waybar.css.
- **Verified:** **V2** — contrast 24×7 pass; theme-wholeswap green;
  theme-shot neon-glass desktop+menu viewed (cyan bar + glass menu);
  boreal theme-shot regression green (menu/bar intact).
- **Pending:** —
- **Next suggestion:** post-install hardware hints PROPOSED, or human
  HARDWARE-QUEUE.

## 2026-07-11 — Promote neon-glass → NEXT #88 (Bernardo)
- **Task:** human triage: promote neon-glass PROPOSED.
- **Did:** NEXT **#88** neon-glass polish/finish (`VISION § D`); path =
  **finish** (not quarantine). Notes: no broken waybar.css reintro without
  wholeswap parity; V2 theme-shot required. PROPOSED pitch removed;
  VISION pointers updated. **No implementation** this commit.
- **Verified:** V0 (backlog/docs only).
- **Pending:** —
- **Next suggestion:** implement #88 (theme-shot V2).

## 2026-07-11 — VISION hygiene + PROPOSED (queue empty)
- **Task:** QA/docs loop — NEXT still only #76 `[blocked:hw]`; no product
  item to implement without inventing work.
- **Did:** Marked Theme A/B/C shipped rows in `docs/VISION.md` (Firmware
  #43, Fingerprint #55, doctor stack, rebuild errors #56, Boreal default,
  hibernate confidence #77). Rewrote “Suggested agent slices” to open
  work only. PROPOSED: post-install hardware hints; neon-glass
  finish/quarantine `[human]`.
- **Verified:** **V0** — docs/backlog only (no module change). Prior
  iteration’s V2 suite still stands; no regression claimed here.
- **Pending:** human triage of PROPOSED; HARDWARE-QUEUE V3.
- **Next suggestion:** promote post-install hints → NEXT, or human
  HARDWARE-QUEUE / neon-glass call.

## 2026-07-11 — QA sweep (queue empty but #76)
- **Task:** QA sweep — NEXT only has #76 `[blocked:hw]`; no other
  actionable items.
- **Did:** option-docs (88 opts) + template-sot green; HM generation has
  What changed? / doctor float / first-boot unit; rotated JOURNAL.md
  41→18 entries (tail → JOURNAL-ARCHIVE). No product code change.
- **Verified:** **V2** — `nix flake check --no-build`; rebuilt green:
  doctor, first-boot, what-changed, clamshell-logind, dock-audio,
  windowrule-syntax, battery-notify, zram-swap, nightlight-geo.
- **Pending:** #76 V3 hibernate on Nomarchy hardware; #86/#87 V3 dock QA.
- **Next suggestion:** HARDWARE-QUEUE pass by human, or promote LATER
  wallpapers split / triage PROPOSED.

## 2026-07-11 — #87 dock audio sink priority
- **Task:** NEXT #87 — default audio follows dock plug/unplug.
- **Did:** WirePlumber `90-nomarchy-dock-audio` (HDMI/DP session prio
  1100, USB 1050) via `modules/nixos/dock-audio-rules.nix` + default.nix
  pipewire. `checks.dock-audio` pure rule asserts + conf-file VM. V3
  hotplug steps in HARDWARE-QUEUE. Closed #87.
- **Verified:** **V2** — `nix flake check --no-build`; `checks.dock-audio`
  green (pure rule asserts + conf installed under wireplumber.conf.d).
- **Pending:** V3 real HDMI/USB-dock hotplug (HARDWARE-QUEUE).
- **Next suggestion:** QA sweep or LATER wallpapers split if promoted.

## 2026-07-11 — #86 clamshell HandleLidSwitchDocked
- **Task:** NEXT #86 — lid closed + external monitor must not suspend.
- **Did:** Explicit `HandleLidSwitchDocked=ignore` in
  `modules/nixos/power.nix` (logind settings.Login). Undocked lid
  behaviour unchanged. idle.nix comment corrected. `checks.clamshell-logind`
  asserts conf + active logind. V3 steps in HARDWARE-QUEUE. Closed #86.
- **Verified:** **V2** — `nix flake check --no-build`;
  `checks.clamshell-logind` green (`HandleLidSwitchDocked=ignore` +
  logind active).
- **Pending:** V3 real lid+external on laptop (HARDWARE-QUEUE).
- **Next suggestion:** #87 dock audio sink follow.

## 2026-07-11 — #85 hardware hall of fame + split #84
- **Task:** NEXT #85 docs hall of fame; #84 was `[big]` → split.
- **Did:** HARDWARE.md §9 install-tested table (T14s Gen 4 AMD, Latitude
  5410/5310) + DMI PR invite; renumbered following sections. #84 → #86
  clamshell + #87 dock audio. VISION/ROADMAP; closed #85.
- **Verified:** V0 (docs only).
- **Pending:** —
- **Next suggestion:** #86 clamshell or #87 dock audio (both product).

## 2026-07-11 — #83 doctor first-boot pre-activate flag
- **Task:** NEXT #83 — doctor points at failed installer HM pre-activate.
- **Did:** doctor row: if `/var/log/nomarchy-hm-preactivate.log` is present
  and no Home Manager generation exists → ✖ +
  `home-manager switch --flake ~/.nomarchy -b bak`. Skip/ok otherwise.
  Extended `checks.doctor` (fail arm + recovered gen). Closed #83.
- **Verified:** **V2** — checks.doctor green (fail arm + recovered gen);
  local smoke with NOMARCHY_HM_PREACTIVATE_LOG / empty HOME.
- **Pending:** —
- **Next suggestion:** #85 docs hall of fame (cheap) or split #84 dock.

## 2026-07-11 — #82 generation readability
- **Task:** NEXT #82 — plain-language “what changed last rebuild”.
- **Did:** `pkgs/nomarchy-what-changed` (nvd → “N added, M removed, K
  updated”); wired into lifecycle post-`nomarchy-rebuild` / `nomarchy-home`
  (full nvd + notify toast); System › What changed? (toast + floating
  full report). `checks.what-changed` with NOMARCHY_NVD fixture. Closed #82.
- **Verified:** **V2** — checks.what-changed green; local smoke on real
  HM gens (“Desktop: 1 added, 1 removed”) + fixture counts.
- **Pending:** —
- **Next suggestion:** #83 doctor pre-activate fail flag or #85 docs.

## 2026-07-11 — #81 first-boot welcome toast
- **Task:** NEXT #81 — dismissible “you're set” card (`VISION § B`).
- **Did:** `pkgs/nomarchy-first-boot` + user oneshot
  (`modules/home/first-boot.nix`): notify once with SUPER+M/T/? + Network
  + Doctor pointers; marker `settings.firstBootShown` via theme-sync
  `--no-switch` (in-checkout state). Skip live hostname. Option
  `nomarchy.firstBootWelcome.enable` (default on). `checks.first-boot`.
  ROADMAP/VISION/README; closed #81.
- **Verified:** **V2** — `nix flake check --no-build`; `checks.first-boot`
  green (toast + marker + silent re-run + live skip); local shim smoke;
  downstream-template-home carries the user unit.
- **Pending:** V3 — real swaync rendering on first installed login
  (toast body is pure text; not queued unless it misbehaves).
- **Next suggestion:** #82 generation readability or #85 docs hall of fame.

## 2026-07-11 — #80 doctor battery health readout
- **Task:** NEXT #80 — battery health in `nomarchy-doctor` (`VISION § C`).
- **Did:** Doctor row: `cycle_count` + retained capacity
  (`charge_full`/`design` or `energy_*`) as `% of design`; system
  batteries only; soft warn &lt;70% design; skip when attrs absent.
  `NOMARCHY_POWER_SUPPLY_ROOT` for the VM fixture. Extended
  `checks.doctor` (fake BAT0 + Device-scope MOUSE0 ignore). HARDWARE §10,
  ROADMAP ✓, VISION ✓; closed #80.
- **Verified:** **V2** — `nix flake check --no-build`; `checks.doctor`
  green; local smoke on real BAT0 (342 cycles, 81% via energy_*) + fixture.
- **Pending:** Power-menu surface left optional/out of scope (doctor is
  the product surface for report-only health).
- **Next suggestion:** #81 first-boot welcome card (highest product ROI)
  or #85 docs-only hall of fame.

## 2026-07-11 — Triage: VISION-gap PROPOSED → NEXT #80–#85 (Bernardo)
- **Task:** Bernardo promoted five of the six fresh VISION-gap pitches
  (all but the wallpapers split, which stays LATER).
- **Did:** NEXT gains #80 battery health readout, #81 first-boot welcome
  card, #82 generation readability, #83 doctor pre-activate fail flag,
  #84 dock life polish `[big]`, #85 hardware hall of fame. PROPOSED
  placeholder left. **No implementation started** (explicit instruction).
- **Verified:** V0 (backlog only).
- **Pending:** —
- **Next suggestion:** #80 (smallest) or #81 (highest product ROI); #84
  must be split first.

## 2026-07-11 — Quick wins: exit-node locations + geo tooltip
- **Task:** BACKLOG LATER "VPN exit-node richer display" + a geo-mode
  tooltip nit from the previous iteration (human asked for quick wins).
- **Did:** Tailscale Exit node picker rows now "hostname — Country City",
  sliced by the header's fixed-width column offsets (COUNTRY/CITY are
  multi-word; never field-split), locationless nodes stay bare, pick strips
  back to hostname at the first space. Nightlight Waybar tooltip says
  "follows your location" in geo mode. Queue's VPN entry updated.
- **Verified:** V1 — awk proven against a fabricated 3-case fixture
  (Mullvad 1-word + multi-word city + bare node); flake check exit 0;
  downstream-template-home builds (writeShellScriptBin syntax-gates both
  scripts). Real tailscale output is V3 (existing VPN queue entry).
- **Pending:** V3 VPN menu live paths (extended, not new).
- **Next suggestion:** wallpapers artifact split (LATER, decided).

## 2026-07-11 — Night-light geo mode (lat/long → wlsunset)
- **Task:** BACKLOG LATER "Night-light geo mode" (human-picked).
- **Did:** `nomarchy.nightlight.latitude`/`.longitude` (both set = geo mode):
  `services.wlsunset` (location-computed sunrise/sunset, `.temperature` →
  night temp) replaces hyprsunset; the toggle script's `unit=` and the
  live-state ExecCondition follow via one `unit` let-binding. Template
  example lines, README row, ROADMAP ✓. New `checks.nightlight-geo`:
  pure-eval asserts on the rendered units (coords + temp in ExecStart,
  ExecCondition present, hyprsunset off). Declarative-only (no menu writer —
  coords are machine config like keyboard layout).
- **Verified:** V1 — `nix flake check --no-build` exit 0 (evals the new
  asserts + option-docs); built `downstream-template-home` (default
  hyprsunset path unregressed) + the check drv. Caught en route: HM renders
  `ExecStart` as a list (`toString` needed in eval asserts).
- **Pending:** V3 queued (HARDWARE-QUEUE § Any machine): visible warm shift
  at location night + toggle/persistence on the swapped unit.
- **Next suggestion:** wallpapers artifact split (LATER, decided).

## 2026-07-11 — Sync-sweep rule + grooming + zram user docs (human-requested)
- **Task:** Bernardo: groom stale items and codify the check so they don't
  recur; close the zram user-doc gap.
- **Did:** LOOP.md §5 gains step 4 **Sync sweep** (grep item/feature across
  agent/+docs/ on ship; prune recorded `[x]` queue entries) + an AGENTS.md
  rule line. Applied it: BACKLOG PROPOSED pruned (auto-theme pitch was
  stale — shipped as #79; shipped/promoted placeholders removed; v1.0
  pointer rewritten; FUTURE promoted-stub dropped). HARDWARE-QUEUE pruned
  29 checked `[x]` entries (363→~250 lines), dangling refs reworded,
  prune rule in header. README §3 now documents the zram default +
  hibernation out-of-the-box + MIGRATION pointer.
- **Verified:** V0 (docs/meta only) — grep sweep for dangling refs clean.
- **Pending:** night-light geo mode next (same session).
- **Next suggestion:** wallpapers artifact split (LATER, decided).

## 2026-07-11 — Vendor-neutral agent docs restructure (human-requested)
- **Task:** Bernardo: de-vendor the agent instructions (multi-model repo),
  clean stale files, better docs/agent/.claude structure.
- **Did:** New root `AGENTS.md` entry (CLAUDE.md → symlink). Skill bodies
  moved to `agent/`: VERIFICATION.md (enforcement, ex-nomarchy skill),
  DELEGATION.md (tiers light/standard/frontier + scout/runner contracts +
  fan-out; consolidates CLAUDE.md table + LOOP §economy + skill §6.5),
  THEME-DESIGN.md (ex-theme skill, now with frontmatter’d shim). `.claude/`
  is a thin adapter (shims route into `agent/`). Maps updated (agent/,
  docs/, README tree). Stale: `.gitignore` old_distro entry dropped; local
  result*/__pycache__ removed. JOURNAL rotated (29 kept, 120 →
  JOURNAL-ARCHIVE.md).
- **Verified:** V0 — `nix flake check --no-build` exit 0 with the symlink
  tracked; vendor-name grep clean outside DELEGATION's mapping table.
- **Pending:** nothing; other harnesses add their own adapter row when used.
- **Next suggestion:** #76/#79 remain [blocked:hw]; otherwise groom PROPOSED.

## 2026-07-11 — #76 checks.hibernate-swapfile (VM-verify the swapfile+offset)
- **Task:** Bernardo — push #76's swapfile-resume residual into a VM (like
  the auto-theme sunset trick).
- **Did:** Tried a full runtime-swapfile hibernate→resume test; it hit a
  bounded, definitive **`CanHibernate=no`** — systemd won't hibernate to a
  swap that isn't declared in the boot config, and a runtime-computed
  `resume_offset` can't reach the boot cmdline (the genuine chicken-and-egg).
  First cut also had an **unbounded `wait_for_shutdown`** that hung → killed
  it, bounded the wait in a thread (180s) + a `CanHibernate` pre-check, which
  is what surfaced the clean verdict. Pivoted `checks.hibernate-swapfile` to
  what a VM *can* prove deterministically and the partition test can't: the
  installer swapfile **build + offset** — NOCOW `mkswapfile`, file-type
  swapon, valid `map-swapfile -r` offset accepted by `/sys/power/resume_offset`,
  zram above it. (Device fix: `/dev/vdb` once `useNixStoreImage` was dropped.)
- **Verified:** **V2 PASS** (fresh build, exit 0). BACKLOG/ROADMAP updated;
  #76 residual narrowed to the literal encrypted power-cycle (dev box runs it).
- **Lesson:** the sunset-trick spirit applies but not always the full cycle —
  when systemd/kernel gates the behaviour (CanHibernate), verify the
  *construction* deterministically and leave only the physical cycle to HW.
  Always bound `wait_for_shutdown` (no timeout param → thread + join).
- **Next suggestion:** #76/#79 both `[blocked:hw]`; QA / PROPOSED otherwise.

## 2026-07-11 — Code-review fixes (parallel review of the session batch)
- **Task:** Bernardo asked for parallel work; spawned a fresh-eyes opus
  review of `2055842..HEAD` (static). It found the batch sound — 2 real LOW
  edge bugs, 1 false positive, 1 cosmetic.
- **Did:** (1) `cmd_auto` — inverted sunrise/sunset (sunrise > sunset) went
  perpetually day; replaced the `not between` shortcut with a real
  wrap-around `mins >= sunrise or mins < sunset`, so a cross-midnight day
  window switches both ways. (2) `rofi.nix` autotheme HH:MM guard tightened
  `[0-2][0-9]` → `[01][0-9]|2[0-3]` so 24–29 are rejected. Dismissed the
  `breeze` finding (papirus DOES ship breeze/breeze-dark — verified) and the
  cosmetic doctor-RAM one.
- **Verified:** py_compile; boundary truth-table (normal + wrap-around);
  HH:MM case table (23:00/19:59/20:00 accept, 24/25/30:00 reject); `auto
  --which` normal-case unchanged; menu bash -n; flake check green. Normal
  case unchanged so `checks.auto-theme` still holds.
- **Pending:** hibernate-swapfile VM test still running (uncommitted flake.nix).
- **Next suggestion:** land hibernate-swapfile once its VM test returns.

## 2026-07-10 — #79 V2: checks.auto-theme (simulated sunset) — was mis-framed V3
- **Task:** Bernardo challenged the "V3 real-session" framing — a simulated
  sunset IS VM-testable. He's right; I'd conflated "needs a real desktop"
  with "needs the generic home-manager switch."
- **Did:** Added `checks.auto-theme` runNixOSTest: installs nomarchy-theme-sync,
  points `NOMARCHY_DEFAULT_THEMES` at presets, stubs the switch via
  `NOMARCHY_REBUILD` (a marker), configures the pair+schedule, then **walks
  the VM clock** 19:55→20:05→20:06→next-day 07:30 asserting slug goes
  summer-day → night (1 rebuild) → night (idempotent, still 1) → day (2). The
  self-gating I relied on: presets via env, wallpaper/notify no-op headless,
  run_switch honors NOMARCHY_REBUILD + waybar nudge is check=False.
- **Verified:** **V2 PASS** — `nix build .#checks…auto-theme` fresh, exit 0.
  Dropped #79's `[blocked:hw]`; it's done (V2). Updated ROADMAP.
- **Lesson:** don't default "timer/session feature" to V3 — stub only the
  generic step (the real HM switch, covered elsewhere) and simulate the rest
  (clock) headlessly. Only the literal timer-fires-on-OnCalendar is on-HW,
  and its schedule is systemd-analyze-validated.
- **Next suggestion:** NEXT head #76 `[blocked:hw]`; else QA / PROPOSED.

## 2026-07-10 — #79 slice 3: Look & Feel › Auto theme menu (#79 complete)
- **Task:** BACKLOG #79 slice 3 — the menu; completes the feature.
- **Did:** `rofi.nix` — "Auto theme (on/off)" row in Look & Feel + an
  `autotheme)` submenu: toggle, pick day/night (from
  `nomarchy-theme-sync list`), set sunrise/sunset (HH:MM input, validated).
  Enable writes the flag + defaults the summer pair `--no-switch`, then
  `exec auto --force` — one rebuild installs the timer AND applies the right
  theme. Disable is instant `--no-switch` (`auto` self-gates; the lingering
  timer no-ops). Day/night/times are live `--no-switch`.
- **Verified:** **V1** — HM generation builds (`writeShellScriptBin` bash -n
  on nomarchy-menu passes) + bash -n on the built menu. **Logic test**
  (stubbed rofi/theme-sync/notify): selecting "Auto theme (off)" runs exactly
  `set day summer-day`, `set night summer-night`, `set enable true`
  (all `--no-switch`), then `auto --force` — the intended one-rebuild path.
  Disable path is static-clear (set false `--no-switch`, no `auto`); a
  stateful-stub dynamic test would loop (stub doesn't reflect the flip). V0
  flake check green. **#79 collapsed to `[blocked:hw]`** (all 3 slices done;
  only live-session switch remains, needs a Nomarchy machine).
- **Pending:** V3 real-session (timer switches day↔night across sunset).
- **Next suggestion:** NEXT head #76/#79 both `[blocked:hw]`; QA/PROPOSED.

## 2026-07-10 — #79 slice 2: auto-theme systemd timer
- **Task:** BACKLOG #79 slice 2 — the timer/hook.
- **Did:** `modules/home/autotheme.nix` (imported in home default.nix):
  `systemd.user.{service,timer}.nomarchy-auto-theme`, install gated on
  `config.nomarchy.settings.autoTheme.enable or false` (state-driven, like
  updates.nix gates on its enable). Service runs `nomarchy-theme-sync auto`
  with a PATH for `home-manager`; timer `OnStartupSec=1min` +
  `OnCalendar=*:0/15` + `Persistent`. Chose periodic over exact
  sunrise/sunset timers — robust to suspend/DST, and `auto` is idempotent so
  ticks are cheap (rebuild only at the transition).
- **Verified:** **V1** — real-config eval (state temp-enabled, restored):
  `settings.autoTheme.enable`→true, timer `OnCalendar`=`*:0/15`, service
  ExecStart=`… nomarchy-theme-sync auto`; disabled→no units (isolation eval
  condition=false). `systemd-analyze calendar '*:0/15'` valid. **V0** flake
  check green. (Gotcha: a new .nix file is invisible to the flake until
  `git add` — flake eval only sees tracked files.)
- **Pending:** live timer→`home-manager switch` on a real Nomarchy session
  (needs a Nomarchy machine, like #76). Slice 3 (menu) next.
- **Next suggestion:** #79 slice 3 — Look & Feel › Auto theme menu.

## 2026-07-10 — #79 slice 1: auto time-of-day theme CLI primitive
- **Task:** Split BACKLOG #79 (`VISION § D`, `[big]`) into 3 slices (CLI /
  timer / menu); took slice 1.
- **Did:** `nomarchy-theme-sync auto` — reads
  `settings.autoTheme.{enable,day,night,sunrise,sunset}`, computes day/night
  by the local clock, and applies the matching preset **only if different**
  (idempotent → the slice-2 timer won't rebuild needlessly). `--which`
  prints the decision without switching; `--force`/`--no-switch` too.
  Factored `cmd_apply`'s core into `apply_named` so `auto` reuses the exact
  same one engine (GOALS: no second pipeline).
- **Verified:** **V1** package builds; functional tests on the built bin
  against scratch state: wide-day→day slug, 00:00–00:01 window→night,
  disabled→no-op, missing day/night→die, `auto --no-switch` writes state,
  re-run→"already on" no-op, plain `apply` still writes (refactor intact).
  **V0** py_compile + flake check + `checks.theme-sync-validate` green.
- **Pending:** slices 2 (systemd sunrise/sunset timers + login apply, V2)
  and 3 (Look & Feel menu) remain under #79.
- **Next suggestion:** #79 slice 2 — the timer/hook (V2 runNixOSTest).

