Files
Nomarchy/agent/JOURNAL.md
Bernardo Magri bec826baf0
All checks were successful
Check / eval (push) Successful in 3m10s
fix(docking): the undock keyword is inert, not raced; escalate to reload
Round 7 FAILED: 5-6 consecutive unplugs on the dev box, the panel never came
back. Round 6's retry could not have worked, because round 6's diagnosis was
wrong — and so was the "phantom re-add" theory I brought to this session.

With ZERO enabled outputs — panel disabled by the dock, external gone —
Hyprland 0.55.4 accepts `hyprctl keyword monitor eDP-1,preferred,auto,1`,
prints `ok`, exits 0, and never flushes it: the rule waits for a DRM event
that is not coming. It is inert, not raced. Retrying it 25x/poll for 6 polls
bought 30s of black screen and nothing else.

What made this survive two rounds is worth more than the fix: every
`transition=undock result=ok` in the round-6 journal was Bernardo plugging
the cable back in because the screen was black. His hotplug flushed the
queued rule and the next poll took the credit. Success was indistinguishable
from the user working around the failure, so the logs confirmed whichever
story we brought to them. Ten minutes of probing on hardware killed both.

Probed live 2026-07-14 (dev box, cable out): keyword inert across 4s and 5s
in two runs; `dispatch forcerendererreload` inert too; only `hyprctl reload`
escapes — 99ms and 289ms. So: issue the keyword (enough whenever another
output is still enabled, e.g. the menu's Dock mode), and escalate to reload
only when `monitors` proves it inert. After a reload, re-assert the rule so a
config that parks the panel off cannot undo the undock, and restore per-device
keyboard layouts — a reload re-reads the config and drops the runtime
`device[<name>]:kb_layout` an external board depends on. The menu's `enable`
now proves itself against `monitors` too, instead of cheering for an exit code.

Verified V3 (partial): the fixed transition driven through a real unplug —
panel on, workspaces 1-3 home, 1.8s, `keyword=inert escalate=reload` ->
`enable=via-reload` -> `result=ok`. V2: nix flake check --no-build; docking-ux
+ dock-audio; monitor-fallback.nix; shellcheck clean on display-transition.

NOT proven, and queued as HARDWARE-QUEUE round 8: the watcher-driven path
(exec-once + baked store path = relogin required, the round-5 trap) and
repetition. The keyboard-restore path is untested — the dev box's keyboard
hangs off the dock's hub, so it leaves with the cable; round 8 adds a check
with a keyboard in the laptop. The VM harness still cannot reach any of this:
QEMU aborts Hyprland if the last active output is deleted while the DRM output
is disabled, which is the same zero-output degeneracy from the other side.

New BACKLOG #114 (PROPOSED): tuigreet ignores per-device keyboard layouts —
a VT has one keymap, so the greeter cannot honour `device[]:kb_layout`.
Bernardo hit it logging out while docked; not a regression, a design gap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:28:06 +01:00

55 KiB
Raw Blame History

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. When this file grows past ~25 entries, rotate the tail to JOURNAL-ARCHIVE.md (history, never edited).

Template:

## 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-14 — docking round 7 FAILED; the keyword was inert, not raced (this commit)

  • Task: HARDWARE-QUEUE docking round 7 result (Bernardo, AMD dev box) — 56 consecutive unplugs, the panel never came back. Round 6's fix did nothing because round 6's diagnosis was wrong.
  • Did: Probed it live instead of re-reading the journal. With ZERO enabled outputs, hyprctl keyword monitor eDP-1,preferred,auto,1 is inert — accepted, ok, exit 0, and never flushed until a DRM event arrives. Not a race: inert across 4s and 5s in two runs. dispatch forcerendererreload is inert too; only hyprctl reload escapes (99ms / 289ms, cable out). So the transition issues the keyword, and escalates to reload only when monitors proves it inert — then re-asserts the rule and calls nomarchy-keyboard-layout restore per device, since a reload drops runtime device[…]:kb_layout keywords. Also made the menu's enable prove itself.
  • The lesson, worth more than the fix: round 6 built its race theory on enable-timeout ×2 + result=ok ×1, and I extended it with a "phantom re-add" theory from the added= DP-2 lines. Both were the same artifact: every result=ok was Bernardo replugging the cable because the screen was black — his hotplug flushed the queued rule and the next poll claimed the win. A watcher whose success is indistinguishable from the user working around it will keep confirming whatever story you bring. The probe cost 10 minutes and killed two rounds of theory.
  • Verified: V3 (partial) — the fixed nomarchy-display-transition undock eDP-1 driven through a real unplug on the dev box: panel on, workspaces 13 home, 1.8s, keyword=inert escalate=reloadenable=via-reloadresult=ok. Plus V2: nix flake check --no-build all pass; checks.docking-ux + checks.dock-audio pass; shellcheck clean on display-transition (profile-watch's lone SC2125 is pre-existing).
  • Pending: HARDWARE-QUEUE round 8 — the watcher-driven path needs a relogin (exec-once, baked store path) and repetition; the keyboard-restore path is entirely untested (dev box's keyboard hangs off the dock hub, so it leaves with the cable). New BACKLOG #114 (PROPOSED): tuigreet ignores per-device layouts.
  • Next suggestion: #114, or the dock direction's unverified panel-off (it disables the panel trusting hyprctl's exit code — same class, latent).

2026-07-14 — docking round 6: undock left the laptop panel black (superseded by round 7 above)

  • Task: HARDWARE-QUEUE docking round 6 result (Bernardo, AMD dev box). Plug side all passed (auto-dock, audio to the monitor, wallpaper on the external); unplugging left the panel black until re-docked.
  • Did: Bernardo's journal was decisive — 3 undocks, 2× result=enable-timeout + 1× result=ok, so an intermittent race. Two fixes in modules/home/hyprland.nix: (1) the undock hyprctl keyword monitor eDP-1,preferred,auto,1 exits 0 but is dropped when it lands mid-teardown of the departing external — now re-issued every poll for 5s, with monitors listing the panel as the only proof; (2) the watcher marked the departure handled via "$TRANSITION" undock … || true + awaiting_lid_open=1 and known_outputs had already moved, so nothing ever retried — one lost keyword = a dark panel. The undock is now a queued invariant driven on the 1s tick (6 attempts, then result=gave-up).
  • Verified: V2 — nix flake check --no-build all checks passed; nix build --impure -f tools/monitor-fallback.nix exit 0 in 57s (reaches the final lid-open transition checkpoint); shellcheck clean on both generated scripts (the lone SC2125 is pre-existing + intentional). Honest limit: the harness CANNOT reach this race — QEMU's headless backend aborts Hyprland if the last active output is deleted while the DRM output is disabled, so it must undock before removing DP-1 and never collides the two. The fix itself is V3 pending; comment added at tools/monitor-fallback.nix so a reader doesn't mistake a pass for coverage.
  • Pending: V3 → HARDWARE-QUEUE round 7 (unplug ≥5×; old failure was only ~2 in 3). Rounds 46 opened 5 counts: 4 are now confirmed fixed on hardware (wallpaper, auto-dock, monitor audio, keyboard prompting once — Bernardo), the undock is this commit, and headphone jack-follow is still untested — now its own entry, to fold into the round 7 session.
  • Next suggestion: wait on round 7 before touching the dock path again.

2026-07-13 — #99 live-seed git-init; theme-switch failure filed as #113 (this commit)

  • Task: BACKLOG #99 — false "theme-state.json NOT git-tracked" doctor/ Waybar warning in the live session; resolve the held question from Fable.
  • Did: hosts/live.nix nomarchy-seed-flake now git init+add+commits the seeded ~/.nomarchy (like nomarchy-install.sh:654), so the live checkout is a committed repo. Also committed #94's live-posture comment in flake.nix (separate commit).
  • Verified: V2 — focused serial-log VM run (/tmp/nomarchy-v2-theme-99/): doctor prints ✔ theme-state.json is git-tracked + ✔ flake checkout is clean, GIT_REPO_OK/TRACKED_OK markers. Held question RESOLVED: the same run's offline gruvbox switch failed with a 1176-drv source rebuild, but a local drv-equivalence test proved the failing bvl30ggn…home-manager-generation.drv is BYTE-IDENTICAL path-seeded vs git-seeded → the git-seed is exonerated; the failure is a pre-existing ISO-pinning gap (unpinned non-default-theme generation), now filed as #113.
  • Pending: #113 (offline theme-switch contract); #94 launch check; #97 V2 click; #96 V3 + #101 round 2 (HARDWARE-QUEUE).
  • Next suggestion: #113 — decide pin-all-themes vs scope the offline promise.

2026-07-13 — #96+#97 menu gates explain instead of vanish/raw-fail (this commit)

  • Task: BACKLOG #96 (battery-limit row silently absent on threshold-less firmware) and #97 (raw "Connection to BlueZ failed").
  • Did: rofi.nix — Battery limit row now gates on has_system_battery (like Power profile); the leaf explains a missing charge_control_end_threshold as a firmware capability. Bluetooth leaf checks bluetooth.service before exec'ing blueman and explains the service-down vs no-adapter (/sys/class/bluetooth empty) cases with rfkill/firmware hints.
  • Verified: V1 — built nomarchy-menu, bash -n green, both new paths present in the generated script; V0 flake check green. VM can't fake a threshold-less battery or a BT adapter, so the behavioral checks are queued: #96 → HARDWARE-QUEUE (Acer + T14s row check); #97 → the no-adapter message is exercisable in the next QEMU run (planned with #94's test-install pass; VMs have no BT adapter).
  • Pending: #97 V2 click-check in the next VM run; #96 V3 on the Acer.
  • Next suggestion: #94 test-install run (chromium launch + BT click).

2026-07-13 — #107 direct System/Tools menu keybinds (this commit)

  • Task: BACKLOG #107 — conflict-free direct bindings via keybinds.nix.
  • Did: SUPER+CTRL+Inomarchy-menu system (Super+I settings muscle memory), SUPER+CTRL+Tnomarchy-menu tools in keybinds.nix (single source: Hyprland + cheatsheet); root-menu Tools/System rows now carry menuHint shortcut hints.
  • Verified: V2 — eval: 41 binds, zero duplicate mods+key; both binds in the generated Hyprland config; built nomarchy-menu bash -n green with cheatsheet rows + hints rendered. In-session on the live ISO: VIEWED /tmp/nomarchy-v2-swap-93/40-system-menu.png + 41-tools-menu.png — both submenus open directly from the binds. V0 flake check green.
  • Pending: nothing.
  • Next suggestion: #99 pending the focused doctor/theme-switch VM run.

2026-07-13 — #93 installer swap prompt labeled + explained (this commit)

  • Task: BACKLOG #93 — the swap field was a bare prefilled gum input (no visible section, label, or unit; the placeholder never shows once a value is prefilled).
  • Did: nomarchy-install.sh: visible "Swap & hibernation" section, two info lines (default = RAM ⇒ hibernation-ready; 0 = no swap), a labeled --header on the input, and unambiguous info + Review lines ("N GiB swapfile (enables hibernation)" / "none (hibernation disabled)").
  • Verified: V2 — full interactive KVM UI drive on the built ISO; VIEWED /tmp/nomarchy-v2-swap-93/10-swap-prompt.png (section, both info lines, labeled prefilled input) and …/30-doctor.png (Review box: "Swap: 6 GiB swapfile (enables hibernation)"); wipe confirm aborted by mismatch, nothing touched. bash -n + V0 flake check green.
  • Pending: side-finding: QEMU exposed /dev/fd0 as the FIRST picker row — recorded as live repro evidence on #112.
  • Next suggestion: #99/#107 commits from the same evidence run.

2026-07-13 — charge-limit start-limit-hit on spaced AC storms (this commit)

  • Task: Live V3 failure of #101 (TuringMachine doctor badge, 18:16): a spaced dock/AC event storm → 5 successful starts in 10s → start-limit-hit, unit marked failed though every run succeeded.
  • Did: unitConfig.StartLimitIntervalSec = 0 on nomarchy-battery-charge-limit (idempotent sub-second write — rate limiting only manufactures false failures); extended checks.battery-charge-limit with the exemption assert + an 8× sequential start loop and a clean-state check.
  • Verified: V2 — the check passes under KVM (exit 0: exemption asserted, 8 spaced starts, not failed, no start-limit-hit; original coalescing phase still green). V0 flake check green.
  • Pending: V3 round 2 re-queued in HARDWARE-QUEUE (#101 entry updated).
  • Next suggestion: land the in-flight #93/#99/#107 evidence run.

2026-07-13 — #102 Nextcloud Desktop opt-in in the template (this commit)

  • Task: BACKLOG #102 — document upstream HM services.nextcloud-client as a downstream-template opt-in; no Nomarchy wrapper.
  • Did: Commented services.nextcloud-client block in templates/downstream/home.nix (enable + startInBackground), and pointed the bare # nextcloud-client package line at it (service = app + session lifecycle; re-comment removes both).
  • Verified: V1 — pinned HM 26.05 module confirmed (enable/package/startInBackground); eval test on an uncommented template copy via lib.mkFlake: unit ExecStart nextcloud --background, WantedBy/PartOf graphical-session.target. nix flake check --no-build green (includes template-sot + downstream-template-home).
  • Pending: nothing — runtime lifecycle is the upstream module's contract.
  • Next suggestion: #93/#99/#107 land next (shared live-VM evidence run).

2026-07-13 — User-promoted NEXT queue curation (#102#112; this commit)

  • Task: Audit Bernardo's live-ISO, application, menu, and device follow-ups against shipped behavior and curate the executable queue.
  • Did: Promoted scoped work as NEXT #102#112, with [big] split gates for System-menu IA and Control Center retirement. Reused NOW #96 for the Acer threshold finding; #106/#108 start from shipped navigation/bind coverage.
  • Verified: V0 — read-only source/option/menu audit and git diff --check.
  • Pending: #105 and #110 must be split before implementation; no runtime behavior changed.
  • Next suggestion: #93 remains the NOW head; #102 is the first NEXT item.

2026-07-13 — #92 catalog-only installer keyboard selection (this commit)

  • Task: Make installer layout/variant choices understandable without allowing arbitrary XKB values to break generated Nix.
  • Did: Added a pinned human-readable XKB catalog, a one-step US/default path, strict searchable layout/variant pickers, shared exact validation for interactive and unattended input, and a resolved review summary.
  • Verified: V2 — full flake eval; installer safety/keyboard/package builds; shell/Python/diff checks; real validator accepts valid layout-specific rows and rejects fabricated/prefix/wrong-layout values before installer setup. Exact-current-ISO KVM UI review captured catalog, variant, and review screens; strict unmatched input was rejected and the blank disk remained untouched.
  • Pending: V3 real-hardware install remains; V2 artifacts are under /tmp/nomarchy-v2-keyboard-92-current/.
  • Next suggestion: #93 swap-size prompt and explanation.

2026-07-13 — #91 installer no-variant normalization (this commit)

  • Task: Stop the keyboard picker's display-only (none) value from breaking generated xkb-console-keymap.drv and the whole installation.
  • Did: Normalize at the shared input boundary and defensively in the real template patcher. Added a us + sentinel fixture, actual console-keymap build, and made the offline installer VM carry the literal sentinel.
  • Verified: V2 — full flake eval; focused installer-keyboard/safety/ template builds; shell/Python/Nix syntax and diff checks; KVM tools/test-install.sh installed offline with LUKS + swap, powered off after ~400s, unlocked, and booted the themed desktop. Inspected install progress, LUKS, and first-boot captures under /tmp/nomarchy-vm/.
  • Pending: #92 now explicitly forbids free-form XKB values. Expanded the existing per-device keyboard hardware check for the connected-at-login menu.
  • Next suggestion: #92 installer keyboard selection clarity/safety.

2026-07-13 — #101 charge-limit AC-event burst recovery (this commit)

  • Task: Fix nomarchy-battery-charge-limit.service failing when USB-C dock power events arrive while its settling write is still active.
  • Did: The oneshot now returns inactive after success; immediate and delayed udev paths use coalescing start jobs instead of interrupting restart jobs. Strengthened the focused VM with a 12-event Mains burst during an extended active run and explicit failed/start-limit assertions.
  • Verified: V0 nix flake check --no-build, module parse, and git diff --check; V2 nix build .#checks.x86_64-linux.battery-charge-limit --no-link -L under KVM — the 12-event active-run burst finished inactive/success, changed InvocationID, and produced no failed/start-limit journal result.
  • Pending: V3 post-rebuild dock-power burst check in HARDWARE-QUEUE.
  • Next suggestion: run V3 #101 alongside docking round 4, then #91.

2026-07-13 — #100 dock transition recovery (this commit)

  • Task: Fix the real T14s dangling workspace, closed-lid cable-removal suspend/apparent crash, and BenQ audio miss after 7ebfab4.
  • Did: Added one atomic dock/internal-first undock helper; the IPC watcher owns a verified low-level lid inhibitor through physical lid-open, with validated stale-process cleanup. monitoradded now debounces a settled PipeWire/WirePlumber reprobe then picks an available dock sink; generic audio events never override a manual choice. Named clamshell profiles share the safe transition. Replaced the fallback VM tool with a bounded hotplug workspace/output/inhibitor test and strengthened generated-artifact guards.
  • Verified: V2 — nix flake check --no-build; targeted docking-UX, option-docs, and template-SoT builds; and nix build --impure -f tools/monitor-fallback.nix --no-link -L all green. The KVM guest proved atomic workspace handoff, internal-first restoration, inhibitor retention with the simulated lid closed, and release on lid-open. The harness restores the DRM output before deleting QEMU's last synthetic headless output because that backend-specific zero-output state aborts Hyprland before a usable removal lifecycle is delivered.
  • Pending: V3 exact closed-lid BenQ round 4 in HARDWARE-QUEUE. No real-hardware or real monitor-audio claim.
  • Next suggestion: run V3 round 4, then take installer #91.

2026-07-13 — Live ISO hardware findings queued (#91#99)

  • Task: Transcribe Bernardo's Acer Aspire M5-481T install/session notes and installer failure photo into the executable queue.
  • Did: Added nine separate NOW items: literal (none) XKB install failure, keyboard/swap installer clarity, missing browser, Ghostty launch, charge-threshold discoverability, BlueZ failure, Boreal button contrast, and the false-looking untracked-theme warning.
  • Verified: V0 — exact backlog IDs/content reviewed; Nix failure's first causal error preserved separately from cascading dependency failures; git diff --check green.
  • Pending: implementation and item-specific V1V3 verification.
  • Next suggestion: #91 — fix the installation-blocking keyboard variant.

2026-07-13 — Docking hardware-QA corrections (interactive feedback)

  • Task: Bernardo's real clamshell QA: menu on hidden eDP, no keyboard layout route, audio stayed on speakers, laptop-off lost apps, abrupt undock never restored eDP.
  • Did: Rofi → focused output; replaced eDP disable with safe Dock mode (move workspaces/focus, keep fallback); display watcher now unconditional, reconnecting and race-tolerant. Keyboard watcher runs by default with all XKB layouts + System Keyboard. Audio also reacts to card/port changes; monitor-added re-applies the current awww wallpaper to the new output. Corrected the fallback harness assumption; added checks.docking-ux and synced README/template/ROADMAP/hardware queue.
  • Verified: V1 — nix flake check --no-build; checks.docking-ux built the real HM generation and passed syntax/invariant/layout-catalog assertions; option docs 90/90, template SoT 4/4; Nix parse + Python compile + diff check. V2 unavailable (no readable KVM).
  • Pending: hardware retest in HARDWARE-QUEUE (docking correction round 3).
  • Next suggestion: hardware retest this exact dock sequence.

2026-07-12 — #89 slice 3: Documented Signal tray icon limitation (no commit)

  • Task: BACKLOG #89 slice 3 — Signal's tray icon is full-colour blue and doesn't match the monochrome bar.
  • Did: Researched Signal's Linux tray icon behavior. Found it is a hardcoded, dynamically generated pixmap (Signal_status_icon_1) driven by its Electron wrapper with no built-in toggle for a monochrome icon. Added an entry to docs/ROADMAP.md's known issues noting that certain Electron apps cannot be natively themed. Closed out #89 from the backlog without introducing brittle app.asar repacking hacks.
  • Verified: N/A (Documentation only).
  • Pending: #89 is fully closed.
  • Next suggestion: Pick the next item from the BACKLOG.

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 → " 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 1632px; 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 iterations 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: V2nix 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: V2nix 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: V2nix 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: V2nix 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 <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: V2nix 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 frontmatterd 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 2429 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 PASSnix 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:0000: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).