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.
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.
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.
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_*).
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>
The skill shims became redundant once the auto-loaded entry point
(CLAUDE.md → AGENTS.md) explicitly instructs reading VERIFICATION.md /
THEME-DESIGN.md; their only remaining value was explicit /nomarchy and
/theme invocation. The adapter is now permissions + subagent defs only.
References updated (AGENTS.md, agent/README.md, docs/README.md, README
tree, MEMORY.md hygiene note).
Verification: V0 (docs/meta only).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The repo is maintained by agents from multiple vendors, so agent
instructions move out of vendor-specific locations into shared,
git-tracked markdown:
- AGENTS.md is the new entry point for any harness; CLAUDE.md becomes a
symlink to it (Claude Code keeps working unchanged).
- Skill bodies relocate to agent/: VERIFICATION.md (the enforcement
rules, ex .claude/skills/nomarchy), DELEGATION.md (capability tiers
light/standard/frontier, scout/runner role contracts, token economy,
parallel fan-out — consolidates the CLAUDE.md model table, LOOP.md's
economy section, and skill §6.5 into one place; vendor model names
survive only in the per-harness mapping table), THEME-DESIGN.md
(ex .claude/skills/theme, which previously lacked frontmatter).
- .claude/ shrinks to a thin Claude Code adapter: settings, subagent
defs, and skill shims that route into agent/.
- Maps updated: agent/README.md (instructions vs state vs adapters),
docs/README.md, README.md layout tree.
Cleanup: stale old_distro .gitignore entry dropped (dir long gone);
local result*/__pycache__ artifacts removed; JOURNAL.md rotated (29
recent entries kept, 120 older moved to agent/JOURNAL-ARCHIVE.md,
rotation rule documented in the header).
Verification: V0 (docs/meta only) — nix flake check --no-build exit 0
with the tracked symlink; grep sweep confirms no vendor model names
outside DELEGATION.md's mapping table.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Add an "Auto theme (day/night)" subsection to docs/OVERRIDES.md § 1
(Appearance), after the Icon pack section. Covers what it does (light/dark
switch on a schedule, same one engine), how to turn it on (Look & Feel ›
Auto theme, or the shell set/auto sequence), the settings.autoTheme
fields, and behavior (periodic ~15-min re-check, idempotent rebuilds,
instant disable). Names/fields/flags verified against rofi.nix,
nomarchy-theme-sync.py, and the icon section's style.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
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>
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>
The only doc for the icon-pack override was a comment in the downstream
template. Add an "Icon pack" subsection to docs/OVERRIDES.md § Appearance
(the user-facing appearance-customization doc): the
`nomarchy-theme-sync set icons "<name>"` command, why only Papirus ships
by default (~1 GiB; extra packs opt-in), a known-packs table, and that
the choice is sticky across `apply` (presets carry no icon field).
Example names verified by building all four packs and listing their real
theme directories — corrected Qogir to its capitalized Qogir-Dark/Light
(a lowercase name silently falls back to hicolor). Hibernation needs no
new doc (README swapfile note + MIGRATION.md runbook + Power-menu row).
Verification: V0 (docs-only); command + variant names checked live.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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>
Add a self-contained "Enabling hibernation on an existing machine (no
reinstall)" section to docs/MIGRATION.md, per the settled #76 call to
document migration rather than ship a tool. Covers: @swap subvolume
creation (subvolid=5), btrfs mkswapfile (NOCOW), reading the resume
offset via map-swapfile, and wiring fileSystems."/swap" + swapDevices +
resumeDevice + resume_offset into system.nix. Flags the /swap mount as
required on the hand-edit path (a fresh install inherits it from
disko-generated hardware-config). Notes the zram-priority reservation,
a no-LUKS variant, and the swap=0 opt-out.
Verification: V0. All read-only commands run live against this machine's
LUKS(cryptroot)+btrfs(@)+/swap/swapfile layout; nix flake check
--no-build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#69: audit-theme-design tags hue/CVD/ANSI-family noise on white,
vantablack, lumon, hackerman, matte-black, miasma as [identity] with a
legend — do not retune identity palettes into traffic lights.
#70: import-palettes keeps surface≠overlay when color0==color8, derives
light UI chips from base (ANSI-black trap), gentler light mantle; roles
are first-class. No bulk re-import of shipped theme JSON.
Verified: V0 (py_compile + synthetic import fixtures + audit report).
Installer: disko @snapshots → /.snapshots + first-boot nested
/home/.snapshots. Migration machines may use top-level
@home-snapshots (TuringMachine); either works for snapper.
Self-gated System › Fingerprint when fprintd is on PATH: enroll, list,
verify, delete-all (terminal), and Use for login writing
settings.fingerprint.pam (hardware.nix default from theme-state). Close#55.
Verified: V1 (HM menu rebuild). V3 pending: real-reader enroll (HARDWARE-QUEUE).
Package the installer’s pure hardware-db probe as nomarchy-detect-hw:
prints suggested hardwareProfile and system.nix snippets (or --raw
protocol lines). Does not rewrite the flake. On PATH via the distro
module; docs/HARDWARE.md §8 updated. Close#58.
Verified: V1 (build + run on AMD laptop with fingerprint/NPU/IR cam).
When hardwareProfile includes common-gpu-nvidia, emit a comment-only
plain NixOS block (PRIME / powerManagement / open-module) in the
installer region of system.nix — same pattern as ROCm/NPU opt-ins.
HARDWARE.md §6 product direction marked shipped. Close#59.
Verified: V0 (patch-template smoke + negative + bash -n).
Four new no-VM checks.* + CI py_compile expansion:
- hardware-db-modules: installer DB module names ∈ pinned
nixos-hardware.nixosModules (a lock bump can rename a module and break
profiled installs on just the matching DMI, invisible to any VM).
- installer-compose-lock: offline lock-composer contract on fixtures.
- installer-disko: pure Nix assert — swapSize "0"/"0G" → no @swap, "2G"
→ sized, withLuks wraps root (permanently guards the #46 install fix).
- windowrule-syntax: builds the generated hyprland.conf and fails on the
pre-0.53 grammar / windowrulev2 keyword (guards ed7fd93).
CI py_compile now covers all tracked *.py; docs/TESTING.md §1 synced.
Implemented by a worktree agent; diff reviewed. Verified V0 (flake check)
+ V1 (each checks.x86_64-linux.* built on main, exit 0); agent's negative
tests confirm each guard fails on a regression.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a self-gated Firmware row to the System submenu (present whenever
fwupdmgr is on PATH, i.e. services.fwupd.enable — default-on), its
dispatch, and a `firmware` handler that opens a terminal and runs
fwupdmgr refresh → get-updates → y/N confirm → fwupdmgr update. Never
auto-flashes: fwupdmgr update confirms each device and prompts for the
reboot a capsule needs (pillar 1 — no silent BIOS writes). HARDWARE.md
§4 updated to "shipped".
Verified V0 (nix flake check --no-build) + V1 (HM generation builds;
generated nomarchy-menu passes bash -n; firmware handler emitted
correctly). V2 (menu render in a session) queued in #43 and V3 (real
LVFS flash) in HARDWARE-QUEUE — the VM was held by the #46 install test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seed theme-state.json and the downstream template from themes/boreal.json
(including border + empty settings). Point eval/schema fallbacks at Boreal
(theme.nix preset, plymouth and live-ISO splash colors, theme-shot, sync
error examples). Docs/TESTING and RECOVERY follow the new default.
Verified: state matches boreal palette; theme-contrast all pass.
preview.png for boreal still missing (picker plain-name; V3 capture).
Add docs/VISION.md (product themes, v1.0 bar, agent slice hints) and
README maps under docs/ and agent/. Point ROADMAP, BACKLOG, LOOP,
CLAUDE, HARDWARE, and the root README at the split: VISION = intent,
BACKLOG = executable queue, ROADMAP = design history, .claude = harness
only. Mark v1.0 track in BACKLOG PROPOSED for human triage.
Add docs/HARDWARE.md (layers, install path, firmware/fingerprint/NVIDIA
deep dives, unsupported machines, contributing a DMI line). Link from
README and MIGRATION; queue firmware UI, doctor checks, detect-hw CLI,
and related PROPOSED items in BACKLOG.
Bernardo asked whether TuringMachine (NixOS 25.11) could adopt Nomarchy
without a reinstall, keeping all files. It can — it's a config switch that
reuses the existing hardware-configuration.nix, not a reformat. Adds
docs/MIGRATION.md: a generic "existing NixOS → Nomarchy" walkthrough with
TuringMachine as the worked example — candidacy checklist, the two
data-safety rules (never bump system.stateVersion; /home untouched), a
reversible Phase 0→4 (btrfs snapshot → build-only gate → nixos-rebuild
test → reconcile → cutover) and a triple rollback net. Bakes his decisions:
Nomarchy PPD (drop all ryzenadj), no Secure Boot (drop lanzaboote), full
cutover. Flake/system/home examples verified against lib.nix mkFlake +
templates/downstream, not guessed.
Also files a PROPOSED note: home.stateVersion should be mkDefault so
migrating users don't need mkForce.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Undo is one menu away: the new rollback flow lists the last ten Home
Manager generations (the theme/config history is already generations,
newest marked current) and activates the picked one in a terminal —
HM's supported rollback, reversible by re-picking a newer one, so no
typed-yes gate. System-level undo deliberately links out rather than
reimplementing destructive flows in rofi: the Snapshots row opens the
existing gated flow, and 'boot an older generation (how)' fires an
instruction notification pointing at docs/RECOVERY.md §3 (which now
cross-references the menu path).
V1: template-home built, generated menu bash -n clean, row rendering
and pick-parsing verified against fixture generations output. V3
exercise queued.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slice (c), the last of the docs review: the install/first-run story
read end to end against the tree. Facts held (mkFlake args match the
table, disko's @snapshots + hibernation swapfile, .#default/.#me
naming); the one seam — README §3 never pointed the manual path at the
template README's first-run steps — got a connective paragraph. Item 6
deleted from BACKLOG; ROADMAP § Full docs review flipped to ✓.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slice (d) of the docs review: docs/RECOVERY.md tells the recovery
story that existed only in pieces, ordered by symptom — bad theme/HM
change (generations, apply-known-good, git-revert the flake), desktop
won't start (TTY, greetd + user journals, VM-GL and preactivate-log
pointers), broken system change (older systemd-boot generation, then
make it stick), lost files (btrfs-assistant / nomarchy-snapshots), and
the ISO + nixos-enter last resort. Commands checked against the tree;
linked from the README file tree and §5.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slice (b) of the docs review: every factual claim in the three docs
spot-checked against the tree (mkDefault sites, bind priority, CLI
keys, awww naming, preset/wallpaper counts, summer-night's light bar,
live-ISO monitor rule, CI workflow, QEMU GL flags, template steps) —
all held. Two gaps fixed: OVERRIDES §2 predated nomarchy.monitors and
never routed readers to it (added, plus a quick-reference row);
TESTING §1 didn't list the shell-syntax check that §1b says CI runs
(added bash -n).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slice (a) of the docs review: 9 undocumented nomarchy.* options got
README rows or inline .leaf mentions (autoTimezone, camera IR-hide,
intel.guc, amd.pstate/vaapi, package, system.stateFile, restic.paths).
The diff is now a flake check: option names are eval-walked from the
four option-declaring modules (config halves stay lazy) and compared
to the tables by tools/check-option-docs.py — undocumented options and
stale table rows both fail. Negative-tested against the pre-fix README
(reports exactly the 9 gaps). Item 6 split into slices a–d in BACKLOG.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follow-on to the swaync fix (a643391): the generated Waybar CSS colored
the module row and #window @subtext and dim states @muted, and the
generated rofi theme put @fg text on @surface chips — invisible where
palettes use those roles as "on-surface" (flexoki-light: subtext==base,
surface==text; gruvbox: muted/base = 1.27). flexoki-light has no
whole-swap, so it ran this CSS. Generated surfaces only — the summer
whole-swaps are hand-authored and untouched.
- waybar: secondary text → alpha(@text, 0.85); dim states (inactive
workspaces, DND bell, muted volume) → alpha(@text, 0.5).
- rofi: @dim → text+"80", @surface → text+"1a" (#RRGGBBAA fg tints).
- check-theme-contrast.py pairings/docs now cover all three generated
surfaces; status glyphs (good/warn/bad on base, worst 2.05) are
deliberately not held to a text ratio — raising them is palette
design, a human decision.
Verified: V0; V1 (checks.theme-contrast green over 21 themes;
downstream-template-home builds; rendered waybar style has zero
@subtext/@muted/@surface; rendered custom.rasi shows fg-derived
tints). Honesty: NOT visually verified — V3 queued (flexoki-light
eyeball + dark-theme regression spot-check).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Palette roles aren't uniform: summer-day and flexoki-light use subtext
as text-ON-surface (== base) and surface as a dark chip (== text), so
swaync's @subtext-on-@base body text and @surface/@text buttons were
self-colored (caught on the Latitude).
- body/summary: @text on @base — the one text pairing that passes in
every palette (survey: worst WCAG ratio 5.18 across all 21 themes;
subtext/base bottomed at 1.00 twice, muted/base at 1.27).
- hover rows + widget-title buttons: alpha(@text, 0.1) tints —
contrast by construction; subtext/surface defines dropped.
- NEW checks.theme-contrast (tools/check-theme-contrast.py): asserts
the hex-on-hex text pairings across themes/*.json, cheap (no VM).
The audit also found the generated waybar/rofi CSS shares the bug
class, live on flexoki-light (no whole-swap) → BACKLOG item 27.
Verified: V0; V1 (checks.theme-contrast builds green, 21 themes;
rendered services.swaync.style has zero @subtext/@surface). Honesty:
NOT visually verified — V3 queued (notify-send on summer-day).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Recovers the crashed iteration #7 (items 21+23, Latitude QA findings):
- nomarchy-waybar supervisor (waybar.nix, exec-once'd from
hyprland.nix): any waybar exit respawns the bar; crash-loop guard
(5 fast exits -> critical notify + stop); TERM trapped for a real
stop. Fixes the bar-less session after a theme-switch crash.
- nomarchy-theme-sync prefers a clean `pkill -x waybar` (supervisor
restart with fresh config+style) over the crash-prone in-place
SIGUSR2 reload when the supervisor is running; SIGUSR2 stays as the
unsupervised fallback.
- sys-rebuild: snapshot-first system rebuild against the CURRENT lock
(no `nix flake update`) — the no-update twin of sys-update;
README §3/§5 + motd list it.
Verified: V0 re-run green after crash recovery (flake check --no-build
+ py_compile); V1/V2 per the crashed session's journal entry (HM
renders exec-once=nomarchy-waybar; headless software-GL VM:
SIGKILL -> new pid, clean kill -> respawn, SIGUSR2 -> alive).
V3 pending on the Latitude (queued in HARDWARE-QUEUE.md).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SUPER+SHIFT+K → hyprctl switchxkblayout current next, rendered only
when the session layout has a comma — the same gate as the Waybar
language indicator. Bind data is a separate multiLayoutBinds list in
keybinds.nix (still one source); hyprland.nix and the rofi cheatsheet
gate on the identical condition, so bind and cheatsheet cannot drift.
`current` targets the focused keyboard, so per-device-overridden
boards (single layout) are a no-op.
Parity: hyprland/language (with the keyboard glyph) added to both
summer waybar.jsonc whole-swaps + #language in their CSS. Static JSON
cannot eval-gate, so on summer themes the module shows even with one
layout — parity-over-minimalism, deliberate. Found en route:
summer-night has an idle_inhibitor the generated bar lacks (reverse
gap) — folded into backlog item 13.
Also — agent/BACKLOG.md gains items 21-24 from Bernardo's Latitude
5410 hardware QA (waybar crash on theme switch + no respawn; nameless
network-menu rows; a no-update sys-rebuild; Back-everywhere audit),
queued ahead of everything; HARDWARE-QUEUE's snapshot re-test now
carries the "update to >= a47aa3a + relogin" precondition, since
findings 5/6 match the pre-polkit-agent behavior exactly.
Verified: V0 (flake check, both summer JSONs parse) + V1 both
directions (default single-layout build renders no bind; us,de via
extendModules renders bind AND cheatsheet row). V3 cycle check queued.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-diagnosis of the "btrfs-assistant 2.2 segfaults on launch" bug, and
the fix is not the planned nixpkgs override:
1. gdb: the crash is btrfs_util_subvolume_iterator_next() —
libbtrfsutil's UNPRIVILEGED subvolume-iteration path in btrfs-progs
6.17.1 (upstream-fixed after 6.17.1, kdave/btrfs-progs 886571653).
Symbol versions were checked and match: not ABI/link drift.
2. VM A/B on real btrfs: as root it works (exit 0); as a user it
segfaults (139). The pkexec launcher runs it as root — so the GUI
path was fine all along, except…
3. …the distro shipped NO polkit authentication agent: every pkexec
prompt in the session failed silently. That is the actual root
cause of "the GUI doesn't open".
Shipped: hyprpolkitagent (Hyprland's Qt agent — the prompt is
Stylix-themed) via exec-once in hyprland.nix; `nomarchy-menu snapshot`
prefers btrfs-assistant-launcher again, with the nomarchy-snapshots
fzf flow kept as fallback (and for SSH); checks.snapshot-gui guards
the root path + offscreen GUI event loop on a real btrfs volume so a
lock bump can't silently regress it. No btrfs-progs patch: all our
flows are root-side; the unprivileged fix arrives with a lock bump.
Verified: V0 (flake check) + V2 (snapshot-gui check, executed green) +
V1 (HM generation builds; hyprland.conf carries the agent exec-once;
generated menu passes bash -n with the launcher wiring). Remaining V3
queued: first on-hardware themed polkit prompt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
modules/nixos/oom.nix — running out of memory now kills the offending
process (with a desktop notification via systembus-notify) instead of
freezing the desktop for minutes until the kernel OOM killer fires.
earlyoom over systemd-oomd, deliberately: oomd kills whole cgroups,
and a Hyprland session runs as ONE scope (nothing here spawns per-app
systemd scopes, unlike GNOME) — under pressure oomd would take out the
entire desktop to save it. earlyoom kills the single largest process
before the thrash point. nixpkgs default-enables oomd in an inert
state (no slices monitored); it is disabled outright so there is one
owner. Session plumbing is --avoid-listed (unanchored — NixOS wrappers
rename comm to .foo-wrapped); no --prefer tuning, largest-RSS
selection already finds the hog. All mkDefault; opt out with
services.earlyoom.enable = false (README note added).
Verified: V0 (flake check) + V2 — new checks.oom-protection
runNixOSTest, executed: a chunked allocator (686 MB peak, 1 GB VM) is
SIGTERM'd by earlyoom in 0.1 s, a bystander unit survives (the
process-level granularity the module exists for), and systemd-oomd is
asserted inactive. 25 s runtime.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The runner was alive all along (gitea/act_runner, ubuntu-latest label);
no run ever appeared because Gitea only reads .gitea/workflows/ (or
.github/workflows/) and ignores the .forgejo/ path the workflow shipped
under. The legacy repo's .gitea/workflows/check.yml was the clue.
References corrected (workflow header, TESTING.md, agent files); this
push doubles as the first live trigger of the workflow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.forgejo/workflows/check.yml runs on every push to main/v1 (+ manual
dispatch): nix flake check --no-build (full module-system eval incl.
the downstream template through mkFlake), py_compile of
nomarchy-theme-sync, and bash -n over tracked .sh files. The always-on
net under direct-to-main pushes — first slice of the ROADMAP lock-bump
CI item.
Scoped to the eval tier deliberately: the instance's runner is an
act_runner docker container (no systemd, no /dev/kvm — established
from the legacy repo's .gitea/workflows/check.yml, which ran 57 times
on it), so the checks.* VM suite and real builds can't run there. A
commented vm-checks job documents the KVM-runner upgrade path; the
legacy workflow's container gotchas (nixbld setup for the single-user
installer, sandbox=false for Stylix IFD, Nix pinned 2.31.5 vs
lazy-trees, no JS actions past node20) are carried over verbatim in
the header. docs/TESTING.md §1b documents what a green run does and
does not mean.
Verified: V0 locally (the same check commands, minus the container
Nix install) + YAML parse. A real green run depends on the runner
still being registered — not API-visible unauthenticated, so that is
queued as [human] BACKLOG item 20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>