@@ -402,6 +402,68 @@ Design/decision records and a running log of shipped work (items marked
built-in. Rules in ` modules/nixos/dock-audio-rules.nix`;
` checks.dock-audio` pure + conf-file VM. Real hotplug V3 in
HARDWARE-QUEUE.
✓ **Dock audio round 2 — auto-follow watcher (2026-07-12):** the T14s
dock test showed priority rules alone can't deliver "just works":
WirePlumber's *stored* default (default-nodes state, written by any
past explicit device pick — every real machine has one) outranks
` priority.session`, so the default never moves on plug. Fix:
` nomarchy-dock-audio` (modules/home/dock-audio.nix,
` nomarchy.dockAudio.enable`, default on) — a ` pactl subscribe` watcher
that ` set-default-sink`s new dock-class sinks (regex data imported
from dock-audio-rules.nix, single source) + toasts; startup sweep for
login-while-docked (only when the default isn't already dock-class, so
restarts don't override a manual pick). EasyEffects needs nothing: EE 8
` useDefaultOutputDevice` defaults true and verified live — with a
stream playing, EE's output links moved to a fake dock-named null sink
and back on unload. Unplug = WirePlumber priority fallback to built-in.
Docked menu rows shipped alongside: Display gains "Laptop screen off ·
everything → <ext>" (live-only ` keyword monitor <eDP>,disable` — a
persisted disable could black-screen an undocked boot), "Screen on ·
<name>", "Move workspace · → next monitor"
(` movecurrentworkspacetomonitor +1`), "Swap workspaces"
(` swapactiveworkspaces`, exactly-2 gate); Audio gains a self-gated
"Send output → <dock sink>" quick row (the watcher's manual override).
Real-dock V3 for both in HARDWARE-QUEUE.
✓ **Undock blackout rescue (2026-07-12):** Bernardo asked whether the
panel comes back if you undock while "Laptop screen off" — VM answer
(tools/monitor-fallback.nix, full softGL desktop): **no**, Hyprland
0.55.4 leaves zero active outputs and never re-enables a soft-disabled
monitor. Fix: ` rescue_blackout` in the display hotplug watcher
(hyprland.nix) — on monitorremoved with zero active outputs,
immediately (and with retries) ` keyword monitor <name>,preferred,auto,
auto` every disabled output + toast; independent of the profile
auto-switch gate (safety invariant, not a preference). Sharp edge
found while testing: the zero-output state itself crashes Hyprland in
the VM ~4/5 runs (ABRT in aquamarine CBackend::dispatchIdle, ~0.5s —
faster than any userland rescue; control run with the panel active
survives 5/5, so it's the state, not headless removal; monitor-
disconnect crashes are a known upstream class). Posture: rescue ships
as defense-in-depth (recovers every surviving case; worst case on real
DRM is session-to-greeter on the re-lit panel, not a black brick);
V3 decides if the menu row needs a mirror-based rework. Also fixed in
the rescue: ` grep -c .` exits 1 at count 0 — an ` ||`-guard on that
pipeline silently disabled the rescue exactly when it mattered.
✓ **Parallel fingerprint-or-password (2026-07-12, Bernardo promoted
from PROPOSED):** ` nomarchy.hardware.fingerprint.parallel` (default
**true** — the better UX is what opting into fingerprint PAM gets you;
` false` restores stock sequential pam_fprintd). Implementation:
packaged ` pam-fprint-grosshack` v0.3.0 (pkgs/, GitLab pin — the
field-standard fprintd fork; stock PAM cannot express parallel
factors, linux-pam#301; the cleaner-concept ` pam-any` stays the
fallback candidate if grosshack fights greetd/hyprlock). Source
reviewed before packaging: every failure path returns
PAM_AUTHINFO_UNAVAIL (falls through to password) and a typed password
is only ferried via PAM_AUTHTOK to the stock
` auth sufficient pam_unix.so … try_first_pass` — the hack never
validates passwords itself, so password login cannot be locked out by
it. Wiring reuses stock fprintd's rule slot (mkForce'd modulePath) so
ordering is inherited, not recomputed. checks.hardware-toggles grew to
three nodes: parallel default (stack shape + password-works/
wrong-password-fails with no reader), parallel=false (stock
pam_fprintd), pam=false (neither). Real-reader race → HARDWARE-QUEUE
(AMD dev box). Accepted quirks (documented in the package): process-
wide SIGUSR1 handler, pthread_cancel'd prompt on fingerprint win,
prompt buffer not zeroized.
✓ **neon-glass finish (#88, ` VISION § D`):** rewrote whole-swap
` rofi.rasi` to rofi-native ` @color ` glass panel (cyan rim, soft
selection wash — same hex-alpha glass pattern as Boreal); btop title/
@@ -938,10 +1000,21 @@ Design/decision records and a running log of shipped work (items marked
shrinks the security-patch latency the single-input model otherwise imposes
(users are gated on the maintainer for nixpkgs CVEs), since a fix is usually
already eval/build/VM-tested and one hardware promotion away from ` v1` —
worth fast-laning lock-only/security bumps ahead of feature batches. The repo
has **no CI today** (manual ` nix flake update` only). Explicitly *not* a
binary cache: compile-from-source is a deliberate values call (Gentoo-style),
so this automates the *config/lock* channel, not artifact distribution .
worth fast-laning lock-only/security bumps ahead of feature batches.
**Shipped since:** ` .gitea/workflows/check.yml` (eval tier on every main
push) and ` bump.yml` (Monday 05:17 UTC scheduled lock bump, eval + V1
build gate, auto-lands on green — first landing 8fded63, 2026-07-06) .
2026-07-12: both gates moved from one big ` nix flake check --no-build`
(~6.0 GB peak RSS — OOM'd the 4 GB VPS, then could never fit the
runner's 2 GB container cap) to ` tools/ci-eval.sh`, one output per nix
process. **nomarchy-live is excluded by decision** (Bernardo, same
day): the ISO is built/distributed manually, so its guard rides that
manual flow — and its eval alone is 2.69 GB, the one output that can't
fit the cap (Actions run 315: everything else green, it OOM-killed).
With it out, the worst CI output is 0.99 GB — no server change needed. Explicitly
*not* a binary cache: compile-from-source is a deliberate values call
(Gentoo-style), so this automates the *config/lock* channel, not
artifact distribution.
- ✓ **Opt-in auto-commit of state mutations (in-flake state, Phase 4):**
` settings.autoCommit` (menu: **System › Auto-commit**, self-gated on the
flake being a git repo) makes every ` apply`/` set` also ` git commit`
@@ -960,6 +1033,18 @@ Design/decision records and a running log of shipped work (items marked
` get` now prints booleans JSON-style (` true`, not Python's ` True`) —
which also un-sticks the System menu's "Auto timezone (on/off)" label,
whose ` = true` comparison could never match before.
- ✓ **Auto-commit sweep before switch (2026-07-12):** the pathspec limit
above left hand edits (system.nix/home.nix, lock bumps) forever-dirty
even with ` settings.autoCommit` on. Now ` nomarchy-pull`/` -rebuild`/
` -home` run ` nomarchy-autocommit` (internal, in nomarchy-lifecycle)
first: same live-read flag, commits *everything* dirty with an honest
` nomarchy: auto-commit before <pull|rebuild|home switch>` message whose
body lists the swept files — so ` git log` mirrors the generation list
and hand edits still never ride a settings-named commit. Same fallback
identity; never fatal (` || true` at the call sites — a git hiccup can't
block a rebuild); before the ff-only pull, it also un-dirties the tree.
Guarded by ` checks.lifecycle-autocommit` (sandbox-repo round trip of
the real binary: sweep, no-op on clean/off/non-repo).
- **"nomarchy" control center:** a single TUI/GUI front-end over the common
toggles — theme, power profile, opt-in services, display, DND — built on
the same ` nomarchy-theme-sync` / ` nomarchy.*` surface the menu already