Commit Graph

16 Commits

Author SHA1 Message Date
Bernardo Magri
34362d6a92 fix(tools): identity audit exemptions + import hierarchy (#69 #70)
#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).
2026-07-10 09:39:10 +01:00
Bernardo Magri
a640de4fd4 fix(theme): #66 fail-closed theme-state errors at mkFlake/read
Missing, empty, or non-object theme-state.json now throws a short
message pointing at the template and `nomarchy-theme-sync validate`
instead of a raw readFile/fromJSON stack. Shared reader:
modules/theme-state-read.nix — wired in theme.nix, plymouth/timezone/
hardware, and lib.mkFlake (seq-forced early gate). Field-level schema
in theme.nix kept; unknown border roles still fail there; resolveColor
no longer falls through on missing palette keys. Defaults added for
settings.displayProfile / displayProfileAuto.

Verified: V0 nix flake check --no-build; negative evals (missing,
empty, [], bad border role); good-path template mode + displayProfile
defaults.
2026-07-10 09:39:10 +01:00
Bernardo Magri
be4efd38ea fix(waybar): #51 doctor glyph renders + #52 V2 bake asserts
All checks were successful
Check / eval (push) Successful in 3m6s
Doctor tripwire was self-hiding under waybar (PATH miss / weak glyph).
Call nomarchy-doctor by store path, strip ANSI tooltips, use 󰀨 + format
+ signal 10 (generated and whole-swap). theme-shot asserts class:bad,
guest btop.theme, and pokes RTMIN+10 before desktop shots.

V2: theme-shot green for rose-pine, summer-night, catppuccin, everforest,
vantablack; bar crops show the red alert-circle. btop TUI visual is
softGL-blocked (Ghostty) → HARDWARE-QUEUE. Closes #51 and #52.
2026-07-09 20:56:51 +01:00
Bernardo Magri
4a99b64532 docs(skill): fan-out token-economy guidance + track nomarchy skill
Some checks failed
Check / eval (push) Has been cancelled
Add a "Fanning out worktree agents" subsection to §6.5: match model to
task (sonnet for spec'd work, opus for judgment), point briefs at the
backlog spec, disjoint file lanes, worktree isolation with the parent
owning landing, lean on scriptable checks as evidence, batch V2 in one
VM pass. First commit of the tracked skill (skills were un-ignored in
7d52d4b). MEMORY note updated: skills are now tracked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 19:35:01 +01:00
Bernardo Magri
ed7fd93e16 fix(hyprland): migrate window rules to Hyprland 0.53+ syntax
Some checks failed
Check / eval (push) Has been cancelled
The default desktop showed a red Hyprland config-error banner on every
boot, occluding the Waybar. Root cause: Hyprland 0.53 rewrote window
rules — the `windowrulev2` keyword is a hard error and the old
rule-first order (`float, class:^…$`) no longer parses, so the float
rules were not applying either. Confirmed against the Hyprland 0.55.4
source (`handleWindowrule`): each comma field is now `<keyword> <value>`,
so effects carry a value and matchers take a `match:` prefix.

Migrated the 7-rule block to `float 1, match:class ^…$` form.

Verified: V0 nix flake check --no-build; built the generated
hyprland.conf; V2 theme-shot re-render — banner gone, themed Waybar
renders. Journal + MEMORY gotcha added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 18:24:55 +01:00
Bernardo Magri
6bd03747c9 fix(waybar): inhibited notification state reads by shape, not color alone
All checks were successful
Check / eval (push) Successful in 2m59s
Item 28 color-only accessibility sweep. Swept every Waybar status
indicator for shape/glyph redundancy: all already carry non-color
distinction (updates shows a count + self-hides; vpn/doctor/nightlight/
recording self-gate; battery/idle/muted are glyph-distinct) except the
swaync notification module — its `inhibited-*` states reused the normal
bells (󰂜/󰂚), so an app-inhibited (suppressed) state was distinguished
from a normal one by color alone.

Route every suppressed state (DND + inhibited) to the bell-off glyph 󰂛
+ @muted, so "notifications are off right now" reads by SHAPE. Applied
to the generated bar and all four whole-swaps (summer-day/night use
their own bell-off glyph; executive-slate/boreal use 󰂛) — parity rule.
Reuses the glyph DND already ships, so no new codepoint / tofu risk.

Verified: V0 (flake check --no-build; 4 jsoncs valid JSON) + V1 (built
homeConfigurations.nomarchy.activationPackage — the built config has
inhibited-* = U+F009B and style.css routes .inhibited-* → @muted).
V3 pending: live inhibited-state render with an app holding an inhibitor
(HARDWARE-QUEUE). MEMORY gains the "status is never color-only" invariant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 21:07:10 +01:00
cfecb612a6 fix(waybar): drop the bar to the bottom layer so fullscreen covers it
Some checks failed
Check / eval (push) Has been cancelled
On Hyprland the `top` layer renders above every window, including a real
fullscreen surface — so a browser video gone fullscreen sat *under* the
bar (BACKLOG item 30). Move the bar to `layer: bottom` in the generated
waybar.nix and all four whole-swap jsoncs (parity rule); the exclusive
zone still reserves its space in normal tiling, but a fullscreen window
now covers it. Rationale comment kept in waybar.nix; data jsoncs left
comment-free (they carry none).

Verified: V0 (nix flake check --no-build, green) + V1
(homeConfigurations.nomarchy.activationPackage builds; built
.config/waybar/config shows "layer": "bottom"). V3 queued in
HARDWARE-QUEUE (fullscreen covers bar + returns on exit; floating-overlap
trade-off).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 18:51:38 +01:00
Bernardo Magri
a360bc87ca feat(greeter): tuigreet + console themed from the state JSON (item 16)
Some checks failed
Check / eval (push) Has been cancelled
New modules/nixos/greeter.nix owns the greetd block (moved out of
default.nix) plus the theming, the Plymouth model — baked at SYSTEM
rebuild from nomarchy.system.stateFile. Two parts, because tuigreet
draws on the VT with the 16 ANSI slots: console.colors becomes the
theme's ansi[] (vt.default_* kernel params — which also themes raw
ttys and the LUKS passphrase prompt), and tuigreet gets a --theme of
NAMED slots (its parser is ratatui Color::from_str, checked in source;
the names map to the standard indexes the retinted palette now
serves). A sparse state without ansi skips theming cleanly.

Verified: V0; V1 — the template-system toplevel bakes the full --theme
spec into greetd.toml and all three vt.default_{red,grn,blu} palettes
into kernel-params. A checks.greeter VM test was attempted and dropped
after bisecting: tuigreet dies under runNixOSTest even without the
flag (harness limit; nixpkgs's own greetd test uses agreety) —
recorded in agent/MEMORY.md. Remains: V3 rendering +
theme-follows-rebuild on hardware (HARDWARE-QUEUE).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 18:37:32 +01:00
Bernardo Magri
9df18261f9 feat(displays): display profiles — docked/undocked switching (item 15a)
All checks were successful
Check / eval (push) Successful in 3m4s
nomarchy.displayProfiles: named layouts for the same outputs, each a
list of nomarchy.monitors-style entries. The active profile lives in
the in-flake state (settings.displayProfile) on the night-light
pattern: nomarchy-display-profile apply <name> fires the baked hyprctl
rules instantly and writes the state with --no-switch; the next rebuild
bakes the profile's entries over nomarchy.monitors whole-by-name.
Monitor-rule composition is extracted to pure monitor-rules.nix
(renderer + 3-layer resolve, incl. a disable-guard so a stale menu
resolution pick can't resurrect a profile-disabled panel, and junk
state degrading to base config instead of an eval error) — because a
writeText/toFile state fixture is unreadable at flake-check eval time,
the new checks.display-profiles unit-tests the pure function directly.
Menu: System › Display gains a self-gated "Profiles ›" row (●/○ active
mark, Base layout to clear); commented template example + README row.

Verified: V0 (flake check, forcing the new check's asserts); V1 — a
scratch downstream through lib.mkFlake builds: the generated tool
passes bash -n with correct rules (disable/vrr/float scale), the baked
hyprland.conf shows the exact expected overlay, the menu renders, and
list/usage smoke-test clean. Remains: V3 live switching on a dock
(HARDWARE-QUEUE). Slices b (hotplug auto-switch) + c (workspace
binding) stay in item 15.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 17:52:19 +01:00
Bernardo Magri
5eadf0cff6 feat(ci): scheduled weekly lock bump + dispatch fast-lane (item 14 b+c)
Some checks failed
Check / eval (push) Has been cancelled
.gitea/workflows/bump.yml: Mondays 05:17 UTC (+ workflow_dispatch for
security bumps), nix flake update — within the pinned release branches
by construction, never a release jump — gated by nix flake check
--no-build, then a pathspec-limited flake.lock commit pushed to main
with the update's change list as the body. That push triggers check.yml
as a second net. Red gate or push race = a visibly failed run, nothing
lands. v1 promotion stays human. py_compile/bash -n deliberately not in
the gate: they don't read the lock. Container recipe inherited from
check.yml (nixbld, sandbox=false, pinned Nix 2.31.5).

Verified: V0 (yq parse, bash -n per run block); the update/commit/push
logic simulated end-to-end in a scratch clone with a real nix flake
update and a bare remote (changed + unchanged branches, bystander file
stays uncommitted, push lands); today's bumped lock evals green.
Remains: the first real run (cron pickup + Actions-token push) is not
confirmable from a session — BACKLOG item 14 slimmed to that watch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 17:18:54 +01:00
Bernardo Magri
352c681f48 docs(agent): MEMORY — check git status before add -A (harness drops files)
All checks were successful
Check / eval (push) Successful in 2m54s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 20:35:01 +01:00
Bernardo Magri
63136a8cb1 fix(keybinds): SUPER+? never fired — shifted keysym needs SHIFT in the modmask
All checks were successful
Check / eval (push) Successful in 2m53s
? is typed as Shift+/, so Hyprland saw mods SUPER+SHIFT with keysym
`question` and the exact-modmask match skipped the `$mod, question`
bind — the keypress fell through to the focused window (caught on the
Latitude, BACKLOG item 26; the menu itself was fine from a terminal).

- keybinds.nix: bind is now `$mod SHIFT, question`.
- rofi.nix prettyKeys collapses "SHIFT + ?" back to "?" so the
  cheatsheet row keeps the documented SUPER + ? label.
- MEMORY.md gotcha: shifted keysyms need SHIFT in mods — invisible to
  eval-tier tests.

Verified: V0 (flake check --no-build); V1 both surfaces (eval'd
settings.bind renders `$mod SHIFT, question, exec, nomarchy-menu
keybinds`; built nomarchy-menu's cheatsheet renders `SUPER + ?`).
V3 queued: SUPER+? on the Latitude after home-update + relogin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 18:46:50 +01:00
Bernardo Magri
a47aa3aff5 fix(snapshots): un-break the GUI — polkit agent + launcher, not an override
All checks were successful
Check / eval (push) Successful in 3m4s
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>
2026-07-04 10:07:40 +01:00
Bernardo Magri
938753273d fix(ci): move workflow to .gitea/workflows — the server is Gitea, not Forgejo
All checks were successful
Check / eval (push) Successful in 2m50s
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>
2026-07-04 09:42:15 +01:00
Bernardo Magri
46af2f0632 feat(ci): checks-on-push workflow (Forgejo Actions, eval tier)
.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>
2026-07-04 09:20:45 +01:00
Bernardo Magri
6d70bba8e6 feat(agent): autonomous-loop infrastructure + roadmap rework
A git-tracked agent/ directory so AI agents can iterate on the distro
unattended (runner-agnostic: /loop, headless claude -p, or a fresh
manual session — all state lives in the checkout, per the distro's own
philosophy):

- LOOP.md — the iteration protocol: orient → pick one BACKLOG task →
  verify up the V0–V3 ladder → commit+push main → record. Safety rails
  (v1 untouchable, no force-push, no surprise lock bumps) and
  stop-and-escalate conditions.
- BACKLOG.md — the forward half of docs/ROADMAP.md reworked into a
  prioritized queue (5 NOW / 6 NEXT / LATER / PROPOSED / Decisions);
  ROADMAP.md stays the design/decision record + shipped log.
- GOALS.md — the four pillars (stable > reproducible/zero-hidden-state >
  effortless config > beautiful), quality bars, non-goals.
- CONVENTIONS.md — coding/design rules (in-flake state, menu placement,
  Waybar whole-swap parity, toggle-vs-package, no formatter).
- MEMORY.md — curated hard-won lessons (VM recipes, btrfs-assistant
  segfault watch, rofi/WirePlumber/hyprlock gotchas).
- HARDWARE-QUEUE.md — every pending V3 on-hardware check collected from
  the ROADMAP, with exact steps, split by machine.
- JOURNAL.md — append-only iteration log, seeded with this bootstrap.

Plus a root CLAUDE.md entry point and README/ROADMAP pointers.

Verified: V0 — docs-only; nix flake check --no-build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 08:51:11 +01:00