Commit Graph

200 Commits

Author SHA1 Message Date
38ae6775e4 docs(backlog): settle #107 on state.json
All checks were successful
Check / eval (push) Successful in 3m14s
Bernardo's call — the rename target is decided, so drop the alternative
rather than leave whoever picks the item re-opening it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:42:42 +01:00
e0da1ea40f fix(docking): don't lose the hotplug event; one prompt per keyboard; follow the jack
All checks were successful
Check / eval (push) Successful in 3m31s
Four failures from Bernardo's round-4 dock pass on the AMD dev box, plus a
headphone regression, tracked to three causes.

The display watcher ran socat with `-T 1`, which closes Hyprland's IPC
socket after one second of *inactivity*. A dock is normally plugged into an
idle desktop, so the socket was almost always mid-reconnect at that moment
and `monitoradded` was lost outright — and the wallpaper repaint, the
docking profile, and the dock-audio reprobe all hung off that single event.
The reconnect path recovered only the lid inhibitor, so the dock stayed
half-configured until something else poked the watcher.

Make the connection long-lived and take the periodic tick from a 1s `read`
timeout instead, then let the *output set* — not the event — decide: a new
`reconcile` diffs it and is called on both ticks and monitor events, so an
event now only makes the reaction instant rather than necessary. A newly
present external output is as unambiguous a physical plug as the event was,
so it may still override a manual sink choice. Wallpaper paints are retried
over the first few seconds because awww registers an output on its own
schedule, not the compositor's.

Hyprland calls every key-capable evdev node a "keyboard": the lid and power
buttons, a monitor's control channel, and each extra HID collection of a
real keyboard. One dock with one keyboard asked for a layout four times.
udev already draws the distinction properly, so take ID_INPUT_KEYBOARD from
it, map the names into Hyprland's spelling, and ask once per physical
keyboard — applying the answer to every node it brought with it.

Finally, a sink WirePlumber has been told to prefer is stored as the
configured default and outranks every priority rule. On UCM cards the
headphones are a sink of their own rather than a route, so once anything
pinned a sink (this feature on a dock plug, or the user in the Audio menu),
plugging headphones in could no longer take the audio back. The `watch`
loop already subscribed to exactly the right events with an empty handler;
finish it. Unplug needs no undo — the pin goes stale and WirePlumber falls
back by priority on its own, to the dock when docked and the speakers
otherwise.

Verified V2: checks.docking-ux and checks.dock-audio pass with new guards
against each regression; the keyboard filter and both sink selectors run
against the dev box's real devices and sink names. The hotplug paths need
hardware — queued as docking round 5.

Also queues #107, renaming theme.json now that it holds the system state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:24:48 +01:00
9550d2be5e fix(live): git-init the seeded flake so doctor stops false-warning
All checks were successful
Check / eval (push) Successful in 3m26s
BACKLOG #99: the live-session seed (nomarchy-seed-flake) did a bare
`cp -r` with no `git init`, so `~/.nomarchy` was not a repo in the live
session and nomarchy-doctor's `git ls-files --error-unmatch
theme-state.json` false-alarmed "theme-state.json is NOT git-tracked"
(installed systems commit at nomarchy-install.sh:654 and were always
fine). The seed now init+add+commits, exactly like the installer.

Verified: V2 — focused serial-log VM run
(/tmp/nomarchy-v2-theme-99/): doctor prints "theme-state.json is
git-tracked" and "flake checkout is clean"; GIT_REPO_OK / TRACKED_OK
markers. A drv-equivalence test proves the seed change is
build-neutral: homeConfigurations.<user>.activationPackage is
byte-identical path-seeded vs git-seeded
(bvl30ggn...-home-manager-generation.drv). nix flake check green.

The same run exposed a separate, pre-existing failure — offline theme
switching in the live session rebuilds from source and dies offline —
filed as BACKLOG #113 (NOT caused by this change: same drv either way).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:14:04 +01:00
b645573149 docs(agent): park #94/#98/#99 with findings and in-flight state
All checks were successful
Check / eval (push) Successful in 3m13s
Session wrap-up: progress notes so the next iteration resumes without
re-deriving — #94 installed-chromium path proven at V1 (live posture is
the gap, #103 covers it), #98 ruled out palette-level causes, #99
root-caused (live seed lacks git init) with the fix drafted but held on
the open live offline theme-switch failure; a serial-log VM run is in
flight. V0 (notes only); two deliberate working-tree drafts documented
in the entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:37:33 +01:00
79528ff392 fix(menu): explain unsupported battery-limit and BlueZ-down states
Some checks failed
Check / eval (push) Has been cancelled
BACKLOG #96 + #97 (Acer live pass). Battery limit: the System row now
gates on battery presence (same rule as Power profile) instead of the
charge-threshold sysfs node, so firmware without the control gets the
leaf's explanation ("no charge_control_end_threshold — hardware
capability") rather than a silently missing row that reads as a broken
feature. Bluetooth: the leaf checks bluetooth.service before launching
blueman-manager and replaces the raw "Connection to BlueZ failed" with
a state-aware message (service stopped vs no adapter in
/sys/class/bluetooth, with rfkill/firmware hints).

Verified: V1 — nomarchy-menu builds, bash -n green, both paths in the
generated script; V0 flake check green. QEMU can fake neither a
threshold-less battery nor a BT adapter: #96 behavioral check queued in
HARDWARE-QUEUE (Acer + T14s); #97's no-adapter message will be
click-checked in the next VM run (V2 pending).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:36:19 +01:00
10853f662c feat(menu): direct SUPER+CTRL+I / SUPER+CTRL+T binds for System and Tools
Some checks failed
Check / eval (push) Has been cancelled
BACKLOG #107: both submenus get conflict-free direct bindings through
the canonical keybinds.nix (single source for Hyprland binds and the
SUPER+? cheatsheet) — I follows the Super+I settings muscle memory,
T is Tools. The root-menu rows now show the shortcuts via menuHint.

Verified: V2 — eval audit: 41 binds, no duplicate mods+key; both binds
present in the generated Hyprland config; built nomarchy-menu passes
bash -n with cheatsheet rows + hints rendered. In-session on the live
ISO KVM run: viewed 40-system-menu.png / 41-tools-menu.png in
/tmp/nomarchy-v2-swap-93/ — each bind opens its submenu directly.
V0 flake check green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:33:54 +01:00
c97ecade63 fix(install): label and explain the swap-size prompt
Some checks failed
Check / eval (push) Has been cancelled
BACKLOG #93 (Acer live pass): the swap field appeared as a bare
prefilled number — gum never renders the placeholder once --value is
set, and the block had no section header, so nothing said what the
value meant. Add a visible "Swap & hibernation" section, two info lines
(default = RAM keeps hibernation possible; 0 = no swap), a labeled
--header on the input, and make the info + Review lines state the
outcome in full ("N GiB swapfile (enables hibernation)" /
"none (hibernation disabled)"). Default behavior unchanged (swap = RAM).

Verified: V2 — interactive KVM UI drive of the built ISO; viewed
10-swap-prompt.png (section + explanations + labeled input) and the
Review box ("Swap: 6 GiB swapfile (enables hibernation)") in
/tmp/nomarchy-v2-swap-93/; the wipe confirmation was aborted, nothing
touched. bash -n + nix flake check --no-build green.

Side-finding recorded on #112: QEMU's /dev/fd0 was listed first by the
disk picker (live repro evidence in the same artifacts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:33:01 +01:00
720d9a38fb feat(template): document Nextcloud client opt-in via upstream HM service
All checks were successful
Check / eval (push) Successful in 3m15s
BACKLOG #102: a commented services.nextcloud-client example in the
downstream template (enable + startInBackground) instead of only the
bare package line — the upstream Home Manager module installs the app
AND ties it to graphical-session.target, so one block covers install,
session autostart, and clean removal. No Nomarchy wrapper added.

Verified: V1 — pinned HM 26.05 module options confirmed; eval test of
the uncommented block through lib.mkFlake shows the user unit
(ExecStart nextcloud --background, WantedBy/PartOf
graphical-session.target); nix flake check --no-build green
(template-sot, downstream-template-home included). Runtime lifecycle is
the upstream module's contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 17:57:14 +01:00
5d97cf23fe docs(agent): queue desktop and menu follow-ups
All checks were successful
Check / eval (push) Successful in 3m18s
Promote NEXT items #102-#112 for Nextcloud, live ISO apps, airplane mode, menu navigation and hierarchy, keybinding discoverability, keyboard indicator parity, Control Center retirement, recovery terminology, and installer disk-picker safety. Expand #96 for the live-ISO power policy and record #92's completed KVM UI verification.

Verified: V0 source audit and git diff --check; #92 additionally reached V2 on the exact current ISO under KVM.
2026-07-13 17:19:18 +01:00
d09c11d872 fix(install): constrain keyboard selection to XKB catalog
All checks were successful
Check / eval (push) Successful in 3m17s
Make keyboard typing filter-only with Gum strict mode, validate interactive and unattended choices against the pinned XKB catalog before any disk mutation, and show resolved human-readable choices in review.

Verified: V1 flake eval, focused installer builds, shell/Python/diff checks; current-ISO KVM boot proved the packaged new flow while the pre-destructive interaction pass continued.
2026-07-13 17:07:08 +01:00
9d0abe5422 fix(install): normalize empty keyboard variants
All checks were successful
Check / eval (push) Successful in 3m18s
Treat the picker-only (none) row as an empty XKB variant at the shared input boundary and again in the template patcher. Guard the real downstream output and console keymap, and carry the sentinel through the offline installer VM.

Verified: V0 full flake evaluation plus shell/Python/Nix/diff checks; V1 installer-keyboard, installer-safety, and template-SoT builds; V2 full KVM offline LUKS+swap install and themed first boot. No V3 required.
2026-07-13 15:01:51 +01:00
cffe432912 docs(agent): queue live ISO hardware findings
All checks were successful
Check / eval (push) Successful in 3m20s
Record the Acer Aspire install and session feedback as separate NOW items #91-#99, with the literal (none) XKB build blocker first and acceptance criteria for each issue.

Verified: V0 — source screenshots reviewed and git diff --check passed. Implementation verification remains item-specific.
2026-07-13 12:03:13 +01:00
b898d59295 Merge remote-tracking branch 'origin/main' into main
All checks were successful
Check / eval (push) Successful in 3m2s
Lock bump / bump (push) Successful in 6m30s
2026-07-12 20:21:23 +01:00
ae6fe1d679 docs: update agent backlog, journal, and roadmap 2026-07-12 20:20:35 +01:00
7aae204014 feat(fingerprint): password OR fingerprint in parallel at one prompt
Some checks failed
Check / eval (push) Failing after 2m45s
Bernardo promoted the PROPOSED item live: with fingerprint PAM on,
sudo/login should accept whichever factor comes first instead of
pam_fprintd's wait-for-the-reader-then-password. Stock PAM cannot
express parallel factors (linux-pam#301), so this packages
pam-fprint-grosshack v0.3.0 (pkgs/, pinned from GitLab — the
field-standard fprintd fork), source-reviewed before packaging: every
failure path (no reader, no prints, fprintd absent/hung, timeout,
password typed) returns PAM_AUTHINFO_UNAVAIL and falls through; a
typed password is only ferried via PAM_AUTHTOK to the stock
`auth sufficient pam_unix.so … try_first_pass` rule — the module never
validates passwords itself, so it cannot lock out password login.

New option nomarchy.hardware.fingerprint.parallel, default TRUE (the
better UX is what opting into fingerprint PAM buys; false = stock
sequential). Wiring swaps the modulePath of stock fprintd's rule slot
(mkForce) so the sufficient-before-pam_unix ordering is inherited, not
recomputed. README + downstream template rows added.

Verified: V2 — checks.hardware-toggles extended to three nodes, green:
parallel node asserts the grosshack auth line precedes pam_unix in
/etc/pam.d/sudo and that with NO reader a correct password still
passes sudo while a wrong one fails (the lockout-safety invariant);
seqpam node gets stock pam_fprintd and no grosshack; nopam gets
neither. flake check + option-docs + template-sot green.
V3 pending (HARDWARE-QUEUE, AMD dev box): the real type-or-touch race,
fprintd-stopped fallback, hyprlock/greeter after a fingerprint win.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 19:03:56 +01:00
cc9caf802e docs(agent): #89 slice 2 done (EasyEffects icon); Signal pixmap open
Some checks failed
Check / eval (push) Failing after 4m39s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 08:38:32 +01:00
4cdc80c9bc docs(agent): close #76 — hibernate→resume V3 PASSED on TuringMachine
Some checks failed
Check / eval (push) Failing after 1m31s
Bernardo ran the real power-cycle on the T14s (LUKS + btrfs @swap
swapfile): hibernate → power off → single LUKS unlock → session
restored. BACKLOG #76 deleted; HARDWARE-QUEUE resume entry pruned
(outcome recorded in the remaining no-swap-notify entry, JOURNAL, and
the ROADMAP ✓-entry); Decisions hibernation note marked shipped. The
no-swap Hibernate notify check remains the one open leftover.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 07:52:10 +01:00
ac7e6bdb4e fix(waybar): scope the universal CSS reset to the bar — un-hides tray submenu arrows
Some checks failed
Check / eval (push) Failing after 1m22s
BACKLOG #90 root-caused: the arrows were never missing from the icon
theme — Papirus-Dark resolves pan-end-symbolic through its breeze-dark
inheritance (breeze ships inside the nixpkgs papirus output). The real
culprit: Waybar loads its stylesheet screen-wide at application
priority, so the tray menus it hosts for SNI apps (nm-applet & co)
inherit the bar's '* { min-height: 0; }', which out-prioritizes
adw-gtk3's 16px min-height on the menuitem arrow node and collapses
the submenu arrow to zero height. Property bisect confirmed
min-height:0 alone reproduces; the font/border resets don't.

- boreal + executive-slate waybar.css, waybar.nix generatedStyle:
  '*' block scoped to 'window#waybar, window#waybar *'; tooltip rules
  restate the font (tooltips are separate toplevels outside the scope).
- stylix.nix: drop the go-next-symbolic remap — a workaround for the
  misdiagnosis; native pan-end-symbolic resolves fine.
- BACKLOG #90 deleted; JOURNAL entry records the method (live GTK3
  repro + grim screenshots + CSS property bisect, V3 on TuringMachine).

V3 pending: human click on the real nm-applet tray icon post-switch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 07:47:37 +01:00
5e1bd057d5 docs(agent): enqueue stuck nm-applet submenu arrow issue
Some checks failed
Check / eval (push) Failing after 5m41s
2026-07-11 21:09:37 +01:00
6967d8973c fix(home): easyeffects waits for a live tray host, not waybar.service
Some checks failed
Check / eval (push) Failing after 4m10s
The tray-race guard (wwmm/easyeffects#4636) ordered the unit
After=waybar.service — inert here: Nomarchy's waybar runs from the
nomarchy-waybar supervisor (Hyprland exec-once), never as a systemd
unit, so the icon vanished whenever easyeffects won the boot race
(seen on TuringMachine 2026-07-11). Gate ExecStartPre on the SNI
watcher's IsStatusNotifierHostRegistered with a 30s timeout, non-fatal
so audio processing still starts on tray-less setups.

Also: #89 inventory narrowed by Bernardo's visual verdict — Telegram/
ZapZap fine; offenders are EasyEffects (slice 2) and Signal (slice 3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:28:55 +01:00
dbc7741f58 docs(backlog): #89 slice-1 tray inventory done; #76 unblocked by migration
Some checks failed
Check / eval (push) Failing after 1m37s
Tray SNI inventory run live on the T14s: nm-applet, udiskie, Telegram
and EasyEffects are name-based (icon-lookup overridable); Signal and
ZapZap publish pixmaps only (slice 3). Method: watcher's
RegisteredStatusNotifierItems + busctl get-property.

#76: the dev box now runs Nomarchy (migrated 2026-07-11), so the
'not a Nomarchy machine' blocker is gone — retagged [human]; queue
step 1 (swapfile/zram/resume_offset wiring) verified live, only the
power-cycle test remains.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:21:50 +01:00
Bernardo Magri
919dc4afc0 docs(backlog): promote tray icon parity → NEXT #89
Some checks failed
Check / eval (push) Failing after 1h16m53s
Bernardo accepted the EasyEffects tray-icon pitch and widened it: all
SNI apps (Signal/Telegram/ZapZap flatpaks too) should read monochrome
in the bar. Three slices: bus inventory (IconName vs IconPixmap),
HM shadow-icon override layer for name-based items, per-app knobs for
pixmap ones. PROPOSED pitch folded in.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:10:31 +01:00
Bernardo Magri
482cb87c70 fix(menu): release Left from kb-move-char-back before binding it to Back
Some checks failed
Check / eval (push) Failing after 8m34s
rofi hard-fails on a duplicate keybinding and reports it in a GUI error
dialog, not stderr — so 05bab55's Left=Back killed every menu entry
point on a real session while passing all stubbed tests. Rebind
kb-move-char-back to Control+b in both rofi_menu copies (menu + vpn).

V3 on the migrated T14s: error dialog reproduced with old flags, menu
renders with fixed flags. Journal + two PROPOSED pitches (EasyEffects 8
tray icon, parallel fingerprint/password PAM).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:58:04 +01:00
Bernardo Magri
13f1b2b70e feat(theme): finish neon-glass polish (#88)
Some checks failed
Check / eval (push) Has been cancelled
Rewrite whole-swap rofi.rasi to rofi-native @colors (was invalid CSS
var()); cyan glass panel matching Boreal's hex-alpha pattern. btop
titles/selection in accent cyan; ui rounding/opacity tuned; preview.png
from theme-shot. Bar stays generated from JSON (no waybar.css).

Verified: V2 — contrast 24×7; theme-wholeswap; theme-shot neon-glass
desktop+menu viewed; boreal theme-shot regression green.
2026-07-11 11:07:02 +01:00
Bernardo Magri
2cd38e168d docs(backlog): complete #88 promotion bookkeeping
Some checks failed
Check / eval (push) Has been cancelled
Drop neon-glass from PROPOSED; point VISION slices at NEXT #88 finish
path. Companion to 78a3d60.

Verified: V0 (docs only).
2026-07-11 11:00:55 +01:00
Bernardo Magri
78a3d60b02 docs(backlog): promote neon-glass polish → NEXT #88
Some checks failed
Check / eval (push) Has been cancelled
Bernardo triage: finish path (not quarantine). Whole-swap assets stay;
do not reintroduce waybar.css without @define-color parity. V2 theme-shot
required on implement.

Verified: V0 (backlog/docs only).
2026-07-11 11:00:48 +01:00
Bernardo Magri
fc63feaa9d docs(vision): mark shipped Theme A/B/C; refresh PROPOSED
Some checks failed
Check / eval (push) Has been cancelled
VISION was still listing Firmware/Fingerprint/doctor/rebuild as open
work after they shipped. Sync ✓ rows + rewrite suggested agent slices.
PROPOSED: post-install hardware hints; neon-glass finish-or-quarantine.

Verified: V0 (docs/backlog only). Queue remains #76 [blocked:hw] only.
2026-07-11 10:59:11 +01:00
Bernardo Magri
f47903a6a6 feat(audio): prefer dock/HDMI/USB sinks in WirePlumber (#87)
Some checks failed
Check / eval (push) Has been cancelled
Raise priority.session for HDMI/DisplayPort (1100) and USB (1050) ALSA
outputs so the default sink follows dock plug/unplug; built-in analog
wins again when externals disappear. Rules live in
modules/nixos/dock-audio-rules.nix; checks.dock-audio covers the contract.

Verified: V2 — flake check --no-build; checks.dock-audio green.
V3 pending: HARDWARE-QUEUE #87 real HDMI/USB-dock hotplug.
2026-07-11 10:36:07 +01:00
Bernardo Magri
27f18e4ccc feat(power): clamshell — ignore lid suspend when docked (#86)
Some checks failed
Check / eval (push) Has been cancelled
Explicit HandleLidSwitchDocked=ignore under services.logind so closing
the lid with an external display connected does not suspend. Undocked
lid path left to upstream defaults. checks.clamshell-logind asserts the
baked logind.conf and active logind; real multi-monitor lid V3 queued.

Verified: V2 — flake check --no-build; checks.clamshell-logind green.
V3 pending: HARDWARE-QUEUE #86 lid+external on a laptop.
2026-07-11 10:32:18 +01:00
Bernardo Magri
523e44bbf8 docs(hardware): install-tested hall of fame (#85)
Some checks failed
Check / eval (push) Has been cancelled
HARDWARE.md §9 lists T14s Gen 4 + Latitude 5410/5310 with DMI/profile
notes and a "send your DMI line" PR invite. Split dock life #84 into
#86 clamshell + #87 dock audio (stable new IDs).

Verified: V0 (docs/backlog only).
2026-07-11 10:28:52 +01:00
Bernardo Magri
fe0b972171 feat(doctor): first-boot pre-activate fail flag (#83)
Some checks failed
Check / eval (push) Has been cancelled
If the installer left /var/log/nomarchy-hm-preactivate.log and no Home
Manager generation exists, doctor reports ✖ with the recovery one-liner
(home-manager switch --flake ~/.nomarchy -b bak). Clears once a
generation is present. checks.doctor covers both arms.

Verified: V2 — checks.doctor green; local smoke.
2026-07-11 10:28:14 +01:00
Bernardo Magri
639f553cb7 feat(lifecycle): plain-language generation diffs (#82)
Some checks failed
Check / eval (push) Has been cancelled
nomarchy-what-changed wraps nvd into "N added, M removed, K updated"
summaries. nomarchy-rebuild / nomarchy-home print full nvd and toast
the one-liner; System › What changed? offers toast + floating report.
checks.what-changed fixtures nvd via NOMARCHY_NVD.

Verified: V2 — flake check --no-build; checks.what-changed green;
local smoke on real HM generations.
2026-07-11 10:26:57 +01:00
Bernardo Magri
8f720b1078 feat(session): first-boot welcome toast (#81)
All checks were successful
Check / eval (push) Successful in 4m23s
One-shot user unit fires a dismissible "You're set" notification on the
first graphical session (SUPER+M / SUPER+T / SUPER+? + Network + Doctor),
then writes settings.firstBootShown into the flake checkout. Skips the
live ISO hostname (hosts/live.nix keeps its own toast). Toggle:
nomarchy.firstBootWelcome.enable. checks.first-boot covers the gate.

Verified: V2 — flake check --no-build; checks.first-boot green; local
shim smoke; downstream-template-home ships the unit.
2026-07-11 10:19:46 +01:00
Bernardo Magri
71786bda6b feat(doctor): battery health readout (#80)
Some checks failed
Check / eval (push) Has been cancelled
Report-only cycle_count + retained capacity (charge_* or energy_*) as
% of design on system batteries; soft warn below 70% of design; skip
when attrs missing. NOMARCHY_POWER_SUPPLY_ROOT for the checks.doctor
fixture (test_power has no cycle/design attrs).

Verified: V2 — nix flake check --no-build; checks.doctor green (fake
BAT0 cycles/% + Device-scope ignore); local BAT0 smoke (energy_*).
2026-07-11 10:06:13 +01:00
Bernardo Magri
3132ba5ac8 docs(backlog): promote VISION-gap items → NEXT #80–#85 (human triage)
All checks were successful
Check / eval (push) Successful in 4m20s
Bernardo promoted: #80 battery health readout, #81 first-boot welcome
card, #82 generation readability, #83 doctor pre-activate fail flag,
#84 dock life polish [big — split before starting], #85 hardware hall
of fame. Wallpapers artifact split stays LATER. No implementation
started (explicit instruction).

Verification: V0 (backlog only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:51:51 +01:00
Bernardo Magri
56793c1c27 docs(backlog): six VISION-gap PROPOSED pitches (B/C/A-residual/F)
All checks were successful
Check / eval (push) Successful in 4m18s
VISION cross-checked against the tree: firmware menu, fingerprint,
doctor GPU/hibernate, rebuild-error hints, post-install hints and the
install contracts are shipped; the genuinely open gaps are filed as
PROPOSED with one-paragraph pitches — first-boot welcome card,
generation readability, battery health readout, doctor pre-activate
fail flag, dock life polish [big], hardware hall of fame.

Verification: V0 (backlog only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:44:56 +01:00
Bernardo Magri
656ebc9735 feat(menu): Tailscale exit-node rows show country/city
The Exit node picker listed bare hostnames; Mullvad-style nodes are
meaningless without their location. Rows are now
"hostname  — Country City", sliced from `tailscale exit-node list` by
the header's fixed-width column offsets (COUNTRY/CITY can be
multi-word, so field splitting is wrong by construction); nodes without
a location stay a bare hostname, and the pick strips back to the
hostname at the first space before `tailscale set --exit-node`.

Closes BACKLOG LATER "VPN exit-node richer display"; the VPN
HARDWARE-QUEUE entry now covers the location rows.

Verification: V1 — the awk was proven against a fabricated fixture
(one-word city, multi-word "United States / New York, NY", bare
self-hosted node), flake check exit 0, downstream-template-home builds
(writeShellScriptBin syntax-gates the script). Real `tailscale
exit-node list` output stays V3 (existing VPN menu live-paths entry).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:41:34 +01:00
Bernardo Magri
4ebd6770ec feat(nightlight): geo mode — lat/long-computed sunset via wlsunset
All checks were successful
Check / eval (push) Successful in 4m20s
Set BOTH nomarchy.nightlight.latitude/.longitude and the night light
switches backing unit: wlsunset computes sunrise/sunset from the
location daily (the fixed .sunrise/.sunset are ignored;
.temperature feeds the night temp). Everything user-facing is
unchanged — same nomarchy-nightlight toggle script, Waybar moon, and
live-state ExecCondition gate; the unit name is a single let-binding
so all three follow together. Declarative-only on purpose: coordinates
are machine config like the keyboard layout, so home.nix (commented
template example), not a menu writer.

checks.nightlight-geo guards the wiring at pure eval: rendered
wlsunset ExecStart carries the coords + night temperature, the
ExecCondition gate landed on the swapped unit, hyprsunset stays off in
geo mode.

Verification: V1 — nix flake check --no-build exit 0 (evals the new
asserts + option-docs sync); built downstream-template-home (default
hyprsunset path unregressed). V3 pending: visible warm shift at the
location's night + instant toggle/off-persistence on the swapped unit
(HARDWARE-QUEUE § Any machine).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:30:52 +01:00
Bernardo Magri
4c656b1e73 chore(agent): sync-sweep rule on task completion + apply it; zram user docs
All checks were successful
Check / eval (push) Successful in 4m45s
New standing rule (LOOP.md §5 step 4, echoed in AGENTS.md): when a task
ships, grep its number/feature name across agent/ and docs/ and update
or delete every cross-reference it made stale, in the same commit —
including pruning checked-off [x] HARDWARE-QUEUE entries whose outcome
is recorded (journal/ROADMAP; git history is the archive).

First application: BACKLOG PROPOSED groomed (the auto time-of-day theme
pitch was stale — shipped as #79; shipped/promoted placeholder lines
removed; v1.0 pointer rewritten; FUTURE promoted-stub dropped);
HARDWARE-QUEUE pruned of 29 checked entries (363→253 lines) with
dangling references reworded and the prune rule documented in its
header.

Also closes the zram user-doc gap: README §3 now documents the zram
default (zstd/50%/prio 100), hibernation working out of the box on new
installs, and the MIGRATION.md runbook for existing machines.

Verification: V0 (docs/meta only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 09:20:54 +01:00
Bernardo Magri
37615c85a4 test(hibernate): #76 checks.hibernate-swapfile — verify swapfile+offset in a VM
All checks were successful
Check / eval (push) Successful in 4m18s
Push #76's swapfile-resume residual into a VM as far as headlessly possible.
A full hibernate→resume from a RUNTIME swapfile turned out not to be
headless-reachable: systemd reports CanHibernate=no for a swap that isn't
declared in the boot config, and a runtime-computed resume_offset can't be
fed into the boot cmdline (the genuine chicken-and-egg). So the check now
verifies, bounded and deterministically, what the partition test
(checks.hibernate) can't and what a VM CAN prove: the installer's btrfs
@swap swapfile is built correctly and its offset is valid — NOCOW
mkswapfile, file-type swapon, a good `map-swapfile -r` offset the kernel
accepts (/sys/power/resume_offset), and zram (prio 100) above it.

The literal encrypted-swapfile power-cycle + LUKS-initrd unlock stays a V3
laptop check (the dev box runs exactly this layout, /proc/cmdline verified).

(First cut had an unbounded wait_for_shutdown that hung on the CanHibernate
refusal; rewritten with a thread-bounded wait + a CanHibernate pre-check,
which gave the clean verdict. Device is /dev/vdb without useNixStoreImage.)

Verification: V2 PASS (nix build .#checks.x86_64-linux.hibernate-swapfile,
fresh, exit 0); flake check --no-build green. #76 residual narrowed in
BACKLOG/ROADMAP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 08:34:17 +01:00
Bernardo Magri
49e0061dbb test(auto-theme): #79 V2 — simulate sunset in a VM (was mis-framed V3)
All checks were successful
Check / eval (push) Successful in 3m53s
Bernardo rightly pushed back on deferring #79 to a real session: a
simulated sunset IS VM-testable. Add checks.auto-theme, a runNixOSTest
that installs nomarchy-theme-sync, points NOMARCHY_DEFAULT_THEMES at the
presets, stubs the rebuild via NOMARCHY_REBUILD (a marker), configures
the day/night pair + schedule, then WALKS THE VM CLOCK across the
boundary: 19:55 -> 20:05 -> 20:06 -> next-day 07:30, asserting the active
theme goes summer-day -> night (1 rebuild) -> night (idempotent, still 1)
-> day (2 rebuilds).

This isolates the auto decision + apply + rebuild-trigger; the actual
home-manager switch is stubbed (that generic apply path is exercised by
every manual theme change). The only on-hardware residual is the user
timer firing on its OnCalendar schedule, which systemd-analyze validates.

#79 drops its [blocked:hw] tag — it's V2-verified. ROADMAP updated;
lesson recorded in MEMORY (don't default timer/session features to V3:
stub the generic step, simulate the clock).

Verification: V2 PASS (nix build .#checks.x86_64-linux.auto-theme, fresh,
exit 0); V0 flake check green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:04:38 +01:00
Bernardo Magri
860c70466f feat(menu): #79 slice 3 — Look & Feel › Auto theme (completes #79)
All checks were successful
Check / eval (push) Successful in 3m46s
Adds an "Auto theme (on/off)" row to Look & Feel and an `autotheme`
submenu: toggle the feature, pick the day/night themes (from
`nomarchy-theme-sync list`), and set the sunrise/sunset times (HH:MM,
validated). This is the menu surface over settings.autoTheme, completing
the auto time-of-day theme feature (slices: CLI / timer / menu).

Enabling writes the flag and defaults the summer-day/summer-night pair
--no-switch, then runs `auto --force`, so a single rebuild both installs
the timer and applies the correct theme for now. Disabling is instant
(--no-switch): the flag flips and `auto` self-gates, so the lingering
timer no-ops until the next rebuild. Day/night/time writes are live
--no-switch (read by `auto` at tick time).

Verification: V1 — HM generation builds (bash -n on nomarchy-menu at
build) + bash -n on the built menu. Logic test with stubbed
rofi/theme-sync/notify: selecting the toggle from off runs exactly `set
day/night/enable ... --no-switch` then `auto --force` — the intended
one-rebuild path. V0 flake check green. All three #79 slices shipped;
the live timer switch across sunset is a real-session (V3) check on a
Nomarchy machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:53:37 +01:00
Bernardo Magri
c44616aeb0 feat(autotheme): #79 slice 2 — systemd timer runs auto
All checks were successful
Check / eval (push) Successful in 3m53s
modules/home/autotheme.nix installs a user service + timer
(nomarchy-auto-theme) that periodically runs `nomarchy-theme-sync auto`,
so the day/night theme is applied on the current schedule through the
same one engine. Install gates on settings.autoTheme.enable (read from
state, the way updates.nix gates on its enable), so a machine not using
the feature carries no timer.

Timer: OnStartupSec=1min (settle after login) + OnCalendar=*:0/15 +
Persistent (catch a transition missed while off). Periodic rather than
exact sunrise/sunset timers — robust to suspend/DST, and `auto` is
idempotent (no rebuild unless the theme must change) so ticks are cheap.

Verification: V1 — real-config eval (state temporarily enabled, then
restored) confirms the units land when enabled (OnCalendar *:0/15,
ExecStart `... nomarchy-theme-sync auto`) and are absent when disabled;
systemd-analyze validates the schedule. V0 flake check green. The live
timer -> home-manager switch on a real Nomarchy session is pending a
Nomarchy machine (same blocker as #76).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:34:56 +01:00
Bernardo Magri
429de59b52 feat(theme-sync): #79 slice 1 — auto day/night theme primitive
All checks were successful
Check / eval (push) Successful in 3m58s
First slice of the auto time-of-day theme pair (VISION § D, split into
CLI / timer / menu). `nomarchy-theme-sync auto` reads
settings.autoTheme.{enable,day,night,sunrise,sunset}, picks day vs night
from the local clock, and applies the matching preset — but only if it
differs from the current one, so the slice-2 timer can fire freely
without triggering needless rebuilds. `--which` prints the decision
without switching; `--force` and `--no-switch` included.

cmd_apply's core is factored into apply_named() so `auto` drives the
exact same one engine (no second pipeline). apply behavior is unchanged.

Verification: V1 (package builds) + functional tests on the built binary
against scratch state — wide-day window → day slug, a 00:00–00:01 day
span → night, disabled → no-op, missing day/night → clear die, auto
--no-switch writes state, a re-run no-ops as "already on", and plain
apply still writes (refactor intact). V0 py_compile + flake check +
checks.theme-sync-validate green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:20:55 +01:00
Bernardo Magri
79d73cd623 docs(omarchy): #78 Omarchy → Nomarchy migrant one-pager
All checks were successful
Check / eval (push) Successful in 3m47s
Add docs/OMARCHY.md for users coming from Omarchy (VISION § F). Leads
with the one real difference — imperative dotfiles vs a declarative flake
built via the menu, with rollback through NixOS generations — then maps
the deltas: install (ISO/nomarchy-install or MIGRATION.md), a familiar
keybindings table, theming (SUPER+T / nomarchy-theme-sync), installing
software (home.packages / nix shell, no AUR), and update+rollback
(nomarchy-pull/-rebuild/-home, boot-menu generations, nomarchy-doctor).
Registered in docs/README.md; VISION § F marked shipped.

The Nomarchy side is grounded in the code: keybindings pulled from
keybinds.nix, and every cited command (7), the nsearch alias, and the
named theme slugs (gruvbox/rose-pine/nord) verified to exist. Omarchy-side
claims kept to known facts (Arch, dotfiles, pacman/AUR).

Verification: V0 (docs-only); commands/slugs/binds checked against the tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:15:36 +01:00
Bernardo Magri
e841251399 feat(doctor): #77 hibernate/sleep section (VISION § C)
All checks were successful
Check / eval (push) Successful in 3m41s
nomarchy-doctor gains a read-only hibernate/sleep section (the highest-ROI
Day-2 gap, a natural #76 follow-on): resume device on the kernel cmdline
(+ resume_offset for a swapfile), disk swap >= RAM, zram active, and any
suspend/hibernate error in the previous boot's kernel log. Self-gates
(skip on swap=0) and emits only ok/warn/skip, so it never fails the sheet.
Adds `hibernate` to the checks.doctor probe list so the row is asserted.

Fixes a set -euo pipefail hazard the first VM run caught (exit 1, no
verdict): a no-match grep inside $(...) and a `cond && action` both abort
under set -e — now guarded with || true / || echo 0 / if. Recorded in
MEMORY as a reusable gotcha for writeShellScriptBin scripts.

Verification: V2 PASS — checks.doctor VM test green (fresh build, exit 0;
asserts the hibernate row renders, healthy run stays 0). V1 — ran on real
LUKS+@swap hardware: all three ok rows render (resume set, swap 34G >= RAM,
zram 16G) and the sheet reaches its verdict. V0 bash -n + flake check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:06:45 +01:00
Bernardo Magri
cf97f5605d docs(agent): clean QA sweep + VISION-gap pass (3 PROPOSED)
All checks were successful
Check / eval (push) Successful in 3m44s
QA/drift sweep: deterministic checks + bash -n/py_compile all pass
(the 4 "needs-args" scripts pass via their flake derivations); drift
recon found none (dead code, stale refs, TODOs, changed-area
contradictions, counts all clean — 24 presets/previews consistent);
eval sweep green. No fixes needed.

VISION-gap pass: verified candidates against the tree first, which
killed two false gaps (nomarchy-rebuild/home already tail logs + point
at doctor on failure = §A; nvd diff = §B; Firmware menu already exists).
Filed three verified-open PROPOSED items: doctor hibernate/sleep section
(§C, natural #76 follow-on), Omarchy migrant one-pager (§F), auto
time-of-day theme pair (§D, big). Await human triage.

Verification: V0 (agent-notes/docs only); flake check --no-build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 20:51:36 +01:00
Bernardo Magri
7eb9b8ad4f feat(theme): opt-in cross-pack icon overrides, no default bloat
All checks were successful
Check / eval (push) Successful in 3m39s
theme.nix now resolves nomarchy.theme.iconThemePackage from a small
icon-pack registry (Papirus/Tela/Qogir/Reversal/Numix, matched by name
prefix). Default themes name Papirus-*, so the resolved package is the
plain papirus-icon-theme derivation — byte-identical closure, zero added
MB. Only when a theme's `icons` names a set from another pack is that
pack (and only it) symlinkJoined alongside papirus. stylix.nix consumes
it via mkDefault. Adding a pack is one iconPacks row.

Deliberately NOT shipping more packs by default: papirus alone is already
~1 GiB and each extra pack adds ~hundreds of MB, so variety is opt-in per
install (set `icons` in theme-state.json), not a default closure cost.
Documented in templates/downstream/home.nix + ROADMAP § Icon themes.

Verification: V1 (HM activationPackage builds). Eval proofs — default
resolves to plain papirus (zero delta); icons="Tela-dark" ->
nomarchy-icon-themes symlinkJoin (papirus+tela) with the name flowing,
without building tela; all 5 pack attrs exist in the pinned nixpkgs;
template-sot + option-docs + flake check green. No default visual change
(same papirus), so no screenshot; the opt-in render is downstream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 20:12:52 +01:00
Bernardo Magri
aff50e6070 docs(agent): correct #76 V3 scope — dev box isn't a Nomarchy install
All checks were successful
Check / eval (push) Successful in 4m20s
The dev box runs Bernardo's own NixOS (TuringMachine, 25.11), not
Nomarchy — so hibernating it tests that config's hibernate, not
Nomarchy's oom.nix/patch-template/rofi wiring, and can't close #76's V3.
Its layout (LUKS cryptroot + btrfs @ + /swap/swapfile + resume_offset)
does resume, which corroborates the MIGRATION.md runbook, but nothing
more. Rescope the two #76 HARDWARE-QUEUE entries to require a machine
actually running Nomarchy (a Latitude install, or the dev box after
migration), and fix the matching reference in the BACKLOG #76 stub.

Verification: V0 (docs/agent-notes only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 19:53:23 +01:00
Bernardo Magri
ba8963a385 test(hibernate): #76 slice 3 — V2 hibernate→resume VM check (PASS)
All checks were successful
Check / eval (push) Successful in 4m11s
Add checks.hibernate: a runNixOSTest modeled on nixpkgs' hibernate.nix,
wired to the actual risk #76 introduces — coexistence with the zram swap
that oom.nix now enables by default. It imports oom.nix, puts the disk
swap at a lower priority than zram with an explicit boot.resumeDevice,
and uses systemd-initrd. The test drives a full
hibernate -> power-off -> resume -> crash cycle: a volatile ramfs marker
survives resume but NOT a crash-boot (proving genuine resume), and it
asserts zram sits at priority 100 while the disk swap is lower, so the
hibernate image lands on disk rather than volatile zram.

What QEMU genuinely can't do — feed a boot-time resume_offset for a btrfs
swapfile (offset only exists after first-boot allocation) or drive a
LUKS-initrd unlock — stays a V3 laptop check (HARDWARE-QUEUE, dev box
already runs that exact layout), plus the no-swap notify in a real
session. With this, all V0/V1/V2 work on #76 is done; #76 collapses to a
[blocked:hw] stub and the design record moves to ROADMAP.

Verification: V2 PASS — nix build .#checks.x86_64-linux.hibernate built
the driver fresh and exited 0. nix flake check --no-build green.

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