diff --git a/.claude/skills/nomarchy/SKILL.md b/.claude/skills/nomarchy/SKILL.md index 48d3685..74007f0 100644 --- a/.claude/skills/nomarchy/SKILL.md +++ b/.claude/skills/nomarchy/SKILL.md @@ -5,268 +5,18 @@ description: Development and maintenance workflow for the Nomarchy NixOS-based d # Nomarchy Development Skill -Nomarchy's promise to its user: a rock-stable, fully functional, beautiful -workstation that is reproducible, easy to recover, and never requires the -user to become a Nix expert. Every rule below exists to protect that promise. -A change that works but degrades stability, aesthetics, or user-simplicity is -a regression, not a feature. +This repo is maintained by agents from multiple vendors, so the skill +body lives vendor-neutrally in the repo — this file only routes you +there. Read, then follow: -## 0. Read the authoritative docs first +1. **`AGENTS.md`** — entry point: where everything lives, standing rules +2. **`agent/LOOP.md`** — the iteration protocol + the V0–V3 ladder +3. **`agent/VERIFICATION.md`** — the enforcement rules: preflight, the + honesty rules, the visual verification protocol, hardware-blocked + checks, maintenance work, the reporting format +4. **`agent/DELEGATION.md`** — capability tiers, the scout/runner roles + (Claude Code defs: `.claude/agents/`), token economy, parallel fan-out -This skill is an enforcer, not the workflow itself. The workflow lives in the -repo and is the single source of truth: - -- `CLAUDE.md` — entry point, project conventions -- `agent/LOOP.md` — the iteration loop and the V0–V3 verification ladder -- `agent/` (remaining files) — philosophy, conventions, memory, backlog -- `docs/TESTING.md` — VM instructions, the regression checklist, known - environment gotchas - -At the start of any work session in this repo, read `CLAUDE.md` and -`agent/LOOP.md` before touching code. Read `docs/TESTING.md` before running -any VM test. If this skill and those docs ever disagree, the repo docs win — -then fix the discrepancy in the same or a follow-up commit so they can't -disagree twice. - -## 1. Preflight (once per session) - -Before starting work, establish what verification tier this environment can -reach, so you never promise verification you can't deliver: - -1. Linux x86_64 host? `/dev/kvm` present and readable? -2. Enough free disk for an image/ISO build (multi-GB)? -3. Network access for a cold Nix store? - -If the environment cannot reach V2 (no KVM, no disk, etc.): say so -immediately, do the V0/V1 work honestly, mark the change **"V2 pending"** -exactly as you would mark a hardware-blocked change "V3 pending" (see §4), -and stop short of claiming the change is done. Never simulate, guess, or -describe what a VM test "would" show. - -## 2. The verification ladder (enforcement rules) - -Climb the V0–V3 ladder as defined in `agent/LOOP.md`. This skill adds three -non-negotiable enforcement rules on top: - -1. **V2 is mandatory for anything user-visible.** If a user of the installed - system could perceive the change — behavior, layout, colors, keybinds, - timing, error messages — it must be exercised in the local VM before - commit. Docs-only, comment-only, or agent-notes changes may stop at the - tier LOOP.md assigns them; user-visible changes may not. -2. **Every "done" report names the tier reached and shows the evidence.** - Evidence means: the command run and its relevant output, the checklist - items exercised, and for visual work the screenshots viewed (§3). "It - builds" is a V1 claim, not a V2 claim. Never let a report imply a higher - tier than was actually reached. -3. **A failed or flaky test is a result, not an obstacle.** Distinguish real - failures from environment flakes using the known-gotchas section of - `docs/TESTING.md` (e.g. no-KVM slowness, missing guest GL). If you cannot - confidently classify a failure, report it as unresolved — do not retry - until green and report only the green run. -4. **VM runs are headless and unattended.** Use the repo's headless - harness — `tools/test-live-iso.sh` and `tools/test-install.sh` for - boot/install runs, `tools/vm/qmp.py` for programmatic VM control and - `tools/vm/vncshot.py` for screen capture — never a graphical VM window - or any flow that needs a human at the console. The human is not part of - the test loop: do not pause mid-run to ask them to look at the VM, click - something, or confirm what is on screen. A run must complete on its own - and leave auditable artifacts behind (logs, serial console output, exit - codes, screenshots), with every wait bounded by a timeout so a hang - becomes a recorded failure instead of a stalled session. Prefer scripted - assertions (process up, file exists, service/D-Bus state, the checks in - `tools/`) over eyeballing; where judgment is genuinely needed — visual - quality — *you* view the captured screenshots (§3), not the human. The - human reviews evidence in the final report, never the live run. - -### Regression scope after a change - -Re-running the full checklist for every change wastes VM time; running -nothing invites regressions. Default rule: - -- Always: the session-sanity items (boot to session, bar renders). -- Plus: every checklist item touching the layer you changed. -- Plus: the theming end-to-end item whenever theming plumbing changed, - even indirectly (palette generation, symlinks, reload hooks). -- Lock bumps and toolchain changes: run the full checklist — their blast - radius is unknowable by construction. - -## 3. Visual verification protocol - -Visual quality is a core feature of Nomarchy, so "it probably looks fine" is -never verification. A visual/UI change is not V2-verified until all of the -following are true: - -1. **Before/after screenshots** of the changed surface were captured - headlessly — `tools/theme-shot.nix` for reproducible theme renders, - `tools/vm/vncshot.py` (driven via `tools/vm/qmp.py`) for captures from a - running VM. No VM window, no human interaction. Capture the "before" - from the base branch or prior generation, not from memory. -2. **Scripted checks first**: run `tools/check-theme-contrast.py` and - `tools/audit-theme-design.py` against the affected theme(s) before any - eyeballing — machine-checkable legibility/design violations should never - survive to the human-judgment stage. Use `tools/vm/gap-analysis.py` - where it applies. -3. **Two themes**: repeat the "after" capture under at least two themes, one - with a generated palette and one whole-swap theme (e.g. summer-night). - These exercise different code paths in the bar/launcher theming; a change - that looks right under one can silently break the other. -4. **You actually viewed the images** — open the screenshot files and look - at them. State concretely what you inspected: alignment, spacing, - contrast/legibility against the palette, icon rendering, no clipped or - overlapping elements, and that the change looks intentional next to the - "before". -5. Keep the screenshots in the run's working area and reference their paths - in the report, so the human can audit the same evidence. - -If the VM cannot render the surface faithfully (known GL/compositor gaps in -the guest — see `docs/TESTING.md`), that specific visual aspect is -hardware-blocked: verify everything the VM *can* show, and queue the rest -per §4. - -## 4. Hardware-blocked checks - -Some checks genuinely require real hardware (GPU behavior, multi-monitor -hotplug, audio devices, power/suspend, firmware). For those: - -1. Add an entry to `agent/HARDWARE-QUEUE.md` with: what changed, **exact** - reproduction steps a human can follow verbatim, the expected observation - (what "pass" looks like), and the commit hash once known. -2. Mark the commit body **"V3 pending: "**. -3. Say it plainly in your report. A hardware-blocked check is not a failure - and not something to hide — hiding it is the failure. -4. When the human reports back, close the queue entry in the next commit and - record the outcome; if it failed on hardware, that's a new bug at the top - of the backlog. - -Do not use the hardware queue as an escape hatch: if a check *can* be done -in the VM, it must be. "The VM is slow" does not qualify. - -## 5. Maintenance work - -Maintenance is in scope for this skill and follows the same ladder: - -- **Flake lock bumps**: treat as maximum-blast-radius changes. Build, - boot the VM, run the full regression checklist, and do a visual - spot-check of the session (themes can shift with upstream package - changes). Never merge a lock bump on "it evaluates". -- **Theme imports / new themes**: import via `tools/import-palettes.py`, - then the full §3 visual protocol; additionally verify the theme-switch - round trip (into the new theme and back out). -- **Docs drift**: run `tools/check-option-docs.py` after any change that - adds or modifies options, and fix drift in the same commit as the code - change that created it. Doc-only fixes are V0 — but verify any command - you document by actually running it. -- **Backlog grooming / agent-notes**: V0; keep entries consistent with the - conventions in `agent/`. - -## 6. Guarding the philosophy - -Before committing, check the change against the distro's promises: - -- **User is not a Nix expert.** If the change requires the user to write or - read Nix to use the feature day-to-day, redesign it. Configuration the - user touches must stay in the simple, documented surface the repo defines. -- **Rock-stable and recoverable.** Prefer boring, reproducible mechanisms - over clever ones. Any change that could break boot or the session must - have an obvious rollback story (NixOS generations count, but say so). -- **Aesthetics are load-bearing.** A functionally correct but visually - regressive change fails review by definition — that's what §3 is for. - -When a requested change conflicts with these promises, stop and raise the -conflict instead of implementing it quietly. - -## 6.5 Token economy: delegate machinery, keep judgment - -Not every step needs the smartest model. Use subagents to route mechanical -work to cheap models and keep expensive reasoning where it pays. The repo -defines two in `.claude/agents/`: - -- **nomarchy-scout** (haiku, read-only): finding where things are defined, - mapping which files touch a subsystem, scanning build logs and serial - output for error lines, docs-vs-code drift sweeps. Use it for any pure - information-gathering step instead of reading files into the main - context yourself — it keeps the main context small, which matters more - than the token price on long loops. -- **nomarchy-runner** (haiku, executes the harness): builds, VM boots, - screenshot capture, the scripted `tools/` checks. It runs commands and - returns exit codes, logs, and artifact paths — nothing more. - -The dividing line is **evidence vs. judgment**. Cheap models gather -evidence; they never make verification claims. The following always stay -with the main (smart) model and are never delegated downward: - -- deciding what to test and what the regression scope is (§2) -- interpreting an ambiguous or flaky failure (§2 rule 3) -- viewing screenshots and judging visual quality (§3) — aesthetic judgment - is exactly the kind of work small models do badly, and it's load-bearing - for this distro -- writing non-trivial Nix (module structure, overlays, cross-cutting - refactors) -- the final review of the diff and the §7 report - -Practical guidance: delegation is not free — each subagent has its own -context and the tokens multiply — so delegate when the work is mechanical -*or* would pollute the main context with bulk (log files, wide file -scans), not reflexively for every small step. A one-file read is cheaper -done directly. When a scout/runner result surprises you, spot-check it -yourself before building on it: cheap models are allowed to be wrong about -hard things, which is precisely why they're not allowed to make claims. - -### Fanning out worktree agents (parallel V0/V1 work) - -When several NEXT items are independent and don't need the VM, spread them -across worktree-isolated subagents that run in parallel — but keep the -token cost down with these habits (each is a real lever, not a nicety): - -- **Match the model to the task, not the tier.** A backlog item whose spec - is already written (exact files, exact fixes) is *well-specified* → - **sonnet**, not opus. Reserve opus for genuine multi-step reasoning - (novel Nix, cross-cutting design, an ambiguous failure). Haiku for bulk - mechanical. Picking sonnet over opus for a spec'd task is the single - biggest saving. -- **Point at the spec; don't restate it.** The brief should say "the spec - is in `agent/BACKLOG.md` #NN — implement it, don't re-derive," plus only - the *constraints* (scope files, branch, no-VM, no-push). A cold agent - re-reading the whole design into its own context is the tokens you're - trying to avoid. -- **Disjoint file lanes.** Partition the items so no two agents touch the - same file (map the touched files first). Zero shared files = zero merge - conflicts at landing = no reconciliation tokens. If two items must share - a file (README, flake.nix, rofi.nix), give both to one agent or keep one - for yourself. -- **Isolation + you own landing.** Run each agent with `isolation: - worktree`; it commits to its own branch and **never pushes or touches - `main`/`v1`**. You review each diff, cherry-pick onto `main`, and do the - bookkeeping. Parallel pushers race on `main`; a single landing agent - doesn't. (Clean up: `git worktree remove --force` + `git branch -D` the - branch and its `worktree-…` sibling after landing.) -- **Lean on scriptable checks as the primary evidence.** Where a - deterministic `tools/` check or a `checks.*` guard already proves the - property (e.g. `check-theme-contrast.py` for a palette fix, - `option-docs` for a doc row), that near-free run *is* the V0/V1 - evidence — don't spend a VM render to re-confirm what the script already - asserts. -- **Batch V2 at the end, once.** Visual/behavioural items delegated at - V0/V1 come back "V2 pending." Don't boot the VM per item — collect the - landed changes and do **one** `theme-shot`/`test-install` pass covering - all of them. The VM render + screenshot review is the most expensive - token sink in the loop; amortise it. -- **Re-verify on `main`, but leanly.** After cherry-picking, confirm the - agent's V0/V1 on the merged tree — but a tiny, obviously-correct diff - needs a targeted build, not a full re-run of everything. Trust-but-spot- - check scales; blind re-running doesn't. - -The judgment that never delegates (§ above) still holds: *you* review every -diff before it lands, and *you* make the product calls (a "finish vs. -quarantine" decision is yours or Bernardo's, never a cheap agent's). - -## 7. Reporting format - -End every unit of work with a short report containing: - -1. What changed (one paragraph, plain language). -2. Verification tier reached, with evidence (commands + key output, - checklist items run, screenshot paths viewed). -3. Anything pending: "V2 pending" (environment) or "V3 pending" (hardware, - with queue entry reference). -4. Follow-ups added to the backlog, if any. +Before running any VM test, also read **`docs/TESTING.md`**. The repo +docs always win over any cached summary of them; if docs disagree with +each other, fix the discrepancy in the same or a follow-up commit. diff --git a/.claude/skills/theme/SKILL.md b/.claude/skills/theme/SKILL.md index c0cdde6..4b9b199 100644 --- a/.claude/skills/theme/SKILL.md +++ b/.claude/skills/theme/SKILL.md @@ -1,21 +1,12 @@ +--- +name: theme +description: Theme design and visual work for Nomarchy — use whenever the user asks to create, update, refine, or troubleshoot themes or visual design (palettes, Hyprland looks, Waybar, Ghostty, btop, rofi, wallpapers, typography, icons). +--- + # Nomarchy Theme Designer Skill -**Trigger:** Use this skill whenever the user requests to create, update, refine, or troubleshoot themes and visual designs for the Nomarchy distribution. - -## System Prompt / Instructions - -You are an elite UI/UX designer and Linux ricing expert specializing in Wayland environments. Your role is to create cohesive, innovative, and visually stunning desktop themes for "Nomarchy," a custom Linux distribution based on NixOS and the Hyprland window manager. - -### Context & Architecture -All theme configurations for Nomarchy live inside the `themes/` directory at the root of the repository. Before generating any new configurations, you MUST read and analyze the existing files in this directory. Learn how the current themes are structured, how the syntax is formatted for each application, and how they are integrated into the broader NixOS configuration. Always match this established architectural pattern. - -### Your Design Responsibilities -1. **Holistic Design:** Develop unified themes that span across Hyprland (borders, shadows, animations), Waybar, Ghostty terminal, btop, fastfetch, Rofi/fuzzel, and desktop wallpapers. -2. **Color Theory & Aesthetics:** Create or adapt advanced color palettes. You may draw inspiration from established aesthetics (e.g., Everforest, Nord, Gruvbox) or r/unixporn trends, but you should innovate. Ensure perfect harmony between background, foreground, accents, and warning/error colors. -3. **Typography & Iconography:** Select and pair fonts (UI vs. Monospace) and icon themes that match the specific vibe of the color palette. -4. **Accessibility:** Follow best practices for UI design. Ensure high contrast for text readability and avoid eye strain. - -### Process -1. Read the `themes/` directory to understand the codebase. -2. Explain the "vibe," primary color palette (with hex codes), and typography choices of your proposed design. -3. Implement the theme by generating or updating the necessary files within the `themes/` structure. +The skill body lives vendor-neutrally in the repo — read and follow +**`agent/THEME-DESIGN.md`**. Visual work additionally climbs the +verification ladder and visual protocol in **`agent/VERIFICATION.md`** +(scripted contrast/design checks first, then headless screenshots you +actually view). diff --git a/.gitignore b/.gitignore index 297704b..7310152 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -# Study material from the previous iteration — not part of the flake. -# Delete the directory once everything worth porting has been ported. -old_distro/ - # Nix build artifacts result result-* @@ -11,4 +7,3 @@ __pycache__/ # Claude Code machine-local settings (permissions etc.) .claude/settings.local.json -# .claude/skills/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d8be276 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,57 @@ +# Nomarchy — agent entry point + +Nomarchy is a NixOS-based distro: rock-stable, fully reproducible, themed +from one JSON, configured through a menu that writes into the user's own +flake checkout. Read the README for the architecture. + +This file is the entry point for **any** AI coding agent, whatever the +vendor or harness. Everything agents need lives in vendor-neutral, +git-tracked markdown (`agent/`, `docs/`); harness-specific config is a +thin *adapter* (see bottom) and never holds shared content. + +## Where things live (read this first) + +| Path | Role | +|------|------| +| **`agent/`** | Agent instructions + loop state — **only executable queue is `BACKLOG.md`** | +| **`agent/README.md`** | Map of the agent files | +| **`docs/VISION.md`** | Product themes toward **v1.0** (not a queue) | +| **`docs/ROADMAP.md`** | Design history + shipped log | +| **`docs/README.md`** | Full documentation map | +| **`.claude/`** | Claude Code adapter only (permissions, subagents, skill shims) — not the backlog | + +## If you're here to work autonomously (the loop) +Follow **`agent/LOOP.md`** — one iteration: orient → pick one BACKLOG +item → work → verify → commit+push on `main` → record. All loop state is +git-tracked in `agent/`. + +## Rules that apply to every session, loop or not +- **Honesty rule** (`agent/VERIFICATION.md`, `docs/TESTING.md`): for + visual/interactive changes, evaluation is not rendering — state the + tier you reached. Cheap first: `nix flake check --no-build`. +- **Conventions** (`agent/CONVENTIONS.md`): in-flake state, menu + placement, Waybar parity with whole-swaps, toggle-vs-package discipline, + template as SoT for opt-in comments (`templates/downstream`). +- **Git:** direct commits on `main`, pushed; **`v1` is human-only** — + never touch it. Never `nix flake update` unless the task is a lock bump. + No formatter — match aligned hand-formatting. +- Layout: `hosts/` machine · `modules/` distro · `themes/` data · + `pkgs/` code · `tools/` maintainer · `agent/` loop · `docs/` human docs. + +## Delegation & escalation + +Push mechanical work down to cheaper models; keep judgment on the +strongest model available. Capability tiers, standing roles (scout / +runner), and per-harness model mappings: **`agent/DELEGATION.md`**. +Work above your tier? Return it — don't burn tokens. + +## Harness adapters + +One directory per harness, holding only what that harness *requires* in +its own format (permissions, subagent/skill registration). Adapters point +into `agent/`; they never carry policy, queue items, or vision text. + +| Harness | Adapter | +|---------|---------| +| Claude Code | `.claude/` — `CLAUDE.md` is a symlink to this file | +| others | add a sibling dir + a row here; keep it a thin shim | diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index a20f69d..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,52 +0,0 @@ -# Nomarchy — agent entry point - -Nomarchy is a NixOS-based distro: rock-stable, fully reproducible, themed -from one JSON, configured through a menu that writes into the user's own -flake checkout. Read the README for the architecture. - -## Where things live (read this first) - -| Path | Role | -|------|------| -| **`agent/`** | Loop state — **only executable queue is `BACKLOG.md`** | -| **`docs/VISION.md`** | Product themes toward **v1.0** (not a queue) | -| **`docs/ROADMAP.md`** | Design history + shipped log | -| **`docs/README.md`** | Full documentation map | -| **`agent/README.md`** | Map of loop files | -| **`.claude/`** | Claude Code only (permissions + subagents) — not the backlog | - -## If you're here to work autonomously (the loop) -Follow **`agent/LOOP.md`** — one iteration: orient → pick one BACKLOG -item → work → verify → commit+push on `main` → record. All loop state is -git-tracked in `agent/`. - -## Rules that apply to every session, loop or not -- **Honesty rule** (`docs/TESTING.md`): for visual/interactive changes, - evaluation is not rendering — state the tier you reached. Cheap first: - `nix flake check --no-build`. -- **Conventions** (`agent/CONVENTIONS.md`): in-flake state, menu - placement, Waybar parity with whole-swaps, toggle-vs-package discipline, - template as SoT for opt-in comments (`templates/downstream`). -- **Git:** direct commits on `main`, pushed; **`v1` is human-only** — - never touch it. Never `nix flake update` unless the task is a lock bump. - No formatter — match aligned hand-formatting. -- Layout: `hosts/` machine · `modules/` distro · `themes/` data · - `pkgs/` code · `tools/` maintainer · `agent/` loop · `docs/` human docs. - -## Delegation - -Push mechanical work down; keep judgment. Brief every child cold. - -| Model | Best for | Delegate? | Effort | -|---|---|---|---| -| Haiku | bulk mechanical | never | low | -| Sonnet | scoped research | when it helps | medium | -| Opus 4.8 | multi-step reasoning | on clear benefit | xhigh | -| Fable 5 | judgment, taste | by default | medium | - -Fable goes xhigh only for the hardest calls. Skip high. - -## Escalation - -An Opus parent can spawn a Fable child for one hard call. Work above your -tier? Return it — don't burn tokens. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index f680d55..f3dd05d 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,10 @@ Flat on purpose. Two module trees, one options file each, no hidden layers. │ ├── ROADMAP.md # design/decision records + shipped log │ ├── HARDWARE.md # firmware, profiles, drivers │ ├── TESTING.md · RECOVERY.md · OVERRIDES.md · MIGRATION.md -├── agent/ # agent loop state — map: agent/README.md -│ # BACKLOG (executable queue), LOOP, GOALS, … -├── CLAUDE.md # agent harness entry (points at agent/ + docs/) -├── .claude/ # Claude Code only: permissions + subagents +├── agent/ # agent instructions + loop state — map: agent/README.md +│ # BACKLOG (executable queue), LOOP, VERIFICATION, … +├── AGENTS.md # agent entry, any vendor (CLAUDE.md symlinks here) +├── .claude/ # Claude Code adapter: permissions + subagent/skill shims └── tools/ # maintainer-only ├── import-palettes.py # converts old-distro themes → JSON + assets ├── test-live-iso.sh # build the ISO + boot it in QEMU diff --git a/agent/DELEGATION.md b/agent/DELEGATION.md new file mode 100644 index 0000000..2429f6f --- /dev/null +++ b/agent/DELEGATION.md @@ -0,0 +1,112 @@ +# Delegation — capability tiers, roles, token economy + +How to spend model capacity in this repo, for **any** agent harness. +Tasks are matched to capability *tiers*, not vendor model names; each +harness maps tiers to its own models (table at the bottom). GOALS says +what to build, CONVENTIONS how to write it — this says who does which +part. + +## The dividing line: evidence vs judgment + +Cheap models gather evidence; they never make verification claims or +design decisions. The following always stay on the **frontier** tier and +are never delegated downward: + +- deciding what to test and the regression scope (VERIFICATION.md) +- interpreting an ambiguous or flaky failure +- viewing screenshots and judging visual quality — aesthetic judgment is + exactly what small models do badly, and it's load-bearing here +- writing non-trivial Nix (module structure, overlays, cross-cutting + refactors) +- the final diff review and report + +Product calls ("finish vs quarantine", promoting PROPOSED items) sit +above even that: they belong to the human. + +## Tiers + +| Tier | Best for | Delegate to it? | +|------|----------|-----------------| +| **light** | bulk mechanical: search, summarize, audit sweeps, running the harness | freely — never for judgment | +| **standard** | well-specified scoped edits, routine research | when the spec is already written | +| **frontier** | design, novel code, ambiguous failures, taste | this is the loop's own tier | + +Reasoning effort: default moderate; go maximum only for the hardest +calls. A frontier parent may spawn a frontier child for one hard call. +Work above your tier gets returned, not attempted. + +## Standing roles + +Two mechanical roles exist for any harness that supports subagents +(Claude Code implementations: `.claude/agents/`; other harnesses +implement the same contracts in their own format): + +- **scout** (light, read-only): locate where things are defined, map + which files touch a subsystem, scan build logs / serial output for + error lines, docs-vs-code drift sweeps. Reports facts with paths and + line numbers, quotes the minimum snippet, says "not found" plainly — + never guesses, never recommends. +- **runner** (light, executes): builds, VM boots, screenshot capture, + the scripted `tools/` checks. Headless and unattended, every wait + bounded by a timeout. Returns commands, exit codes, wall time, and + artifact paths — never marks anything passed or verified; the caller + makes the verification claim. + +Use them for pure information-gathering or pure execution instead of +pulling bulk (log files, wide scans) into the main context. When a +result surprises you, spot-check it yourself before building on it — +cheap models are allowed to be wrong about hard things, which is +precisely why they're not allowed to make claims. + +## Economy rules + +- **Only delegate when writing the spec is cheaper than doing the + work** — a spawned agent starts cold and must re-derive context. A + one-file read is cheaper done directly. +- **Brief every child cold; point at the spec, don't restate it.** "The + spec is in `agent/BACKLOG.md` #NN — implement it" plus only the + *constraints* (scope files, branch, no-VM, no-push). +- **Match the tier to the task, not the prestige.** An item whose spec + is already written (exact files, exact fixes) is standard-tier work; + reserve frontier children for genuine multi-step reasoning. This is + the single biggest saving. +- The strong model writes the spec, reviews the result, and owns the + commit. + +## Fanning out parallel work (V0/V1, no VM) + +When several NEXT items are independent and don't need the VM, spread +them across worktree-isolated subagents in parallel: + +- **Disjoint file lanes.** Partition items so no two agents touch the + same file (map the touched files first). If two items must share a + file (README, flake.nix, rofi.nix), give both to one agent or keep + one for yourself. +- **Isolation + you own landing.** Each agent works in an isolated + worktree, commits to its own branch, and **never pushes or touches + `main`/`v1`**. You review each diff, cherry-pick onto `main`, and do + the bookkeeping — a single landing agent can't race itself. Clean up + worktrees and branches after landing. +- **Lean on scriptable checks as primary evidence.** Where a + deterministic `tools/` check or `checks.*` guard already proves the + property, that near-free run *is* the V0/V1 evidence. +- **Batch V2 at the end, once.** Delegated visual/behavioural items come + back "V2 pending"; collect the landed changes and do **one** VM pass + covering all of them — the VM render + screenshot review is the most + expensive step in the loop; amortise it. +- **Re-verify on `main`, but leanly.** After landing, confirm the + agent's V0/V1 on the merged tree with a targeted build, not a full + re-run. Trust-but-spot-check scales; blind re-running doesn't. + +The judgment list above still holds: *you* review every diff before it +lands. + +## Per-harness model mapping + +| Tier | Claude Code | +|------|-------------| +| light | `haiku` | +| standard | `sonnet` | +| frontier | the session's top model (`opus` and up) | + +Other harnesses: add a column when one is actually used on this repo. diff --git a/agent/JOURNAL-ARCHIVE.md b/agent/JOURNAL-ARCHIVE.md new file mode 100644 index 0000000..39cbc56 --- /dev/null +++ b/agent/JOURNAL-ARCHIVE.md @@ -0,0 +1,1884 @@ +# Journal archive — rotated iteration log + +Older entries rotated out of [JOURNAL.md](JOURNAL.md) (same format, +newest first). Append-only history; do not edit. Rotation: when +JOURNAL.md grows past ~25 entries, move the tail here. + +--- + +## 2026-07-10 — #60 name-agnostic system-battery scan +- **Task:** NEXT #60 — align charge-limit / menu / waybar / doctor / + installer chassis with battery-notify’s type=Battery + !scope=Device + filter (BAT* globs miss CMB0). +- **Did:** power.nix oneshot, rofi helpers (`has_system_battery` / + `has_charge_threshold`), waybar powerprofile gate, doctor loop, + hardware-db chassis, options.nix description. HARDWARE-QUEUE entry + for non-BAT* confirm. Closed #60. +- **Verified:** **V1** — `checks.option-docs`, `hardware-db-modules`, + `battery-charge-limit` (VM) green; no remaining `BAT*` globs in code + paths. **V3 pending:** non-BAT* name on real hardware (queue entry). +- **Next suggestion:** #63 generated Waybar powermenu + logo. + +## 2026-07-10 — #59 NVIDIA commented system.nix guidance +- **Task:** NEXT #59 — when `common-gpu-nvidia` is selected, emit + commented PRIME/power/open-module guidance in installer-generated + system.nix (ROCm/NPU pattern). +- **Did:** `has_nvidia` from HW_PROFILES → patch JSON; `build_installer_region` + emits comment-only plain NixOS block + HARDWARE.md §6 pointer. Docs §6 + product direction marked shipped. Closed #59. +- **Verified:** **V0** — patch-template smoke with nvidia true (region + contains hardware.nvidia.* comments + profile in flake); negative + (nvidia false) has neither; `bash -n` install script. +- **Next suggestion:** #60 BAT*-agnostic battery scan. + +## 2026-07-10 — #62 whole-swap CSS self-containment CI gate +- **Task:** NEXT #62 — cheap gate that waybar.css defines every @color it + references (would have caught neon-glass) + preview/backgrounds present. +- **Did:** `tools/check-theme-wholeswap.py` + `checks.theme-wholeswap` in + flake.nix. README: five themes → four (drop quarantined neon-glass). + Closed #62. +- **Verified:** **V0** — script OK on themes/ (24 presets, 4 whole-swaps); + negative fixture with `@text`/`@base` and zero defines fails as expected; + `nix build .#checks.x86_64-linux.theme-wholeswap` green. +- **Next suggestion:** #59 NVIDIA install comments, or #60 BAT*-agnostic. + +## 2026-07-10 — #61 Review Source line tracks offline/online +- **Task:** NEXT #61 — Review panel Source must match the cache.nixos.org + probe (`OFFLINE`), not always claim “no network needed”. +- **Did:** `SOURCE_NET` gated on `$OFFLINE` in `nomarchy-install.sh` + (offline: “pinned into the ISO, no network needed”; online: “may use + network binary caches”). Extended `check-install-safety.py` contract #4 + so the dual wording + `$SOURCE_NET` expansion can’t regress. Closed #61. +- **Verified:** **V0** — `python3 tools/check-install-safety.py …` OK; + `bash -n`; `nix build .#checks.x86_64-linux.installer-safety` green. + (Interactive gum Review is not headlessly driven; pure contract is the + V0 bar for this string.) +- **Next suggestion:** #62 whole-swap CSS self-containment CI gate. + +## 2026-07-10 — Promote A/B/C PROPOSED → NEXT #55–#63 (Bernardo-directed) +- **Task:** Bernardo accepted the high-value promote batch (A day-2, B + hardware/stability, C cheap polish). +- **Did:** Nine NEXT items — **#55** fingerprint menu, **#56** human + rebuild errors, **#57** live Install action; **#58** `nomarchy-detect-hw` + CLI, **#59** NVIDIA install comments, **#60** BAT*-agnostic power; + **#61** review offline/online source line, **#62** whole-swap CSS CI + gate, **#63** generated Waybar powermenu+logo. Breadcrumbs left in + PROPOSED; v1.0 track note updated. Still PROPOSED: first-boot tips + residual, charge-limit instant, theme taste polish, NVIDIA first-class. +- **Verified:** V0 (backlog only) — 55–63 present under NEXT; promoted + pitches removed from PROPOSED. +- **Next suggestion:** agent-actionable queue head is **#61** or **#62** + (tiny V0), then **#59** / **#60** / **#63**; product path **#55**–**#57**. + +## 2026-07-09 — #54 V2 + #33 scrollbar + #43 MOTD tip +- **Task:** NEXT #54 (installer safety V2), #33 (rofi scrollbar), #43 + optional MOTD/first-boot tip (no V3 LVFS flash). +- **Did:** `checks.installer-safety` + `tools/test-install-safety.sh` + (compose-fail / disk-warn LUKS magic / hm-hint FORCE hooks). Enabled + themed rofi scrollbars in generated `rofi.nix` + all whole-swap + `.rasi`. MOTD + control-center first-boot tip for System › Firmware. + Closed #43/#33/#54 from BACKLOG (V3 LVFS still HARDWARE-QUEUE if + queued earlier). +- **Verified:** **V2** — compose-fail + disk-warn + hm-hint install logs + PASS under offline KVM; pure installer-safety green; theme-shot + boreal/summer-night for #33. **V0** flake check + bash -n. +- **Pending:** #43 real LVFS flash remains hardware-only (not this + session). Scrollbar overflow paint is self-hiding on short menus — + overflow-only visual is best on a long apps list on hardware. +- **Next suggestion:** #14 watch first bump run, or #41 hw float classes. + +## 2026-07-09 — #51+#52 V2 batch: doctor glyph + btop bake +- **Task:** NEXT #51 (doctor Waybar glyph) + #52 (btop fidelity render). +- **Did:** theme-shot harness now asserts `nomarchy-doctor-status` + (`class:bad` + glyph), guest `btop/themes/nomarchy.theme`, pokes + waybar RTMIN+10, best-effort Ghostty+btop shot. Fixed the doctor + tripwire so it actually *shows*: absolute path to `nomarchy-doctor` + (PATH self-hide), ANSI-stripped tooltip, `󰀨` glyph, `format = "{}"`, + signal 10 on generated + whole-swap bars. +- **Verified:** **V2** — `THEME= nix build --impure -f tools/theme-shot.nix` + green for rose-pine, summer-night, catppuccin, everforest, vantablack; + viewed bar crops: red alert-circle before the bell on generated + (rose-pine) and whole-swap (summer-night). Scripted btop.theme key + checks (Dawn/inactive_fg/Mocha/vantablack) all OK. V0 flake check + + contrast 24×7 pass. Ghostty under softGL never opened → btop TUI + visual queued V3. +- **Pending:** HARDWARE-QUEUE btop TUI spot-check (five slugs). +- **Next suggestion:** #54 installer V2 scenarios, or #33 rofi scrollbar. + +## 2026-07-09 — #43 V2: System ▸ Firmware menu render +- **Task:** V2 the shipped Firmware menu (#43). theme-shot render of + `nomarchy-menu system`. +- **Did/Verified:** System submenu renders themed + correct (Bluetooth shows, + Printers/Snapshots correctly self-gated out). The Firmware row sits below the + visible fold; a `send_chars "firm"` filter did NOT surface it in the capture + VM. Disambiguated the anomaly against the real system: `/sw/bin/fwupdmgr` + **exists** in the live toplevel (same as the working Bluetooth row's + `blueman-manager`), so `command -v fwupdmgr` succeeds → the row shows on a + real install. The theme-shot filter view is a minimal-VM render artifact, + not a bug. Incidental: a reddish doctor-status glyph appears in the bar + (#51's indicator firing on the VM's degraded state). +- **Tier:** V2 for the menu render + PATH-verified row presence; V3 (real LVFS + flash) stays queued. Over-spent renders here chasing the anomaly — noted. +- **Next:** wrap. + +## 2026-07-09 — #44 V2: checks.doctor guards the hardware section +- **Task:** close #44's V2 (was V1 + real-hw smoke). Extend `checks.doctor`. +- **Did:** The bare test VM has none of the probe tools, so the existing + healthy `nomarchy-doctor` run already drives all six hardware checks in + skip mode. Added assertions that the skip rows appear (NetworkManager, + fingerprint, fwupd, VA-API) and the sheet still exits 0 — i.e. the section + runs without erroring under `set -euo pipefail`. No node changes (no closure + bump); the *fires* paths keep the agent's real-hardware evidence. +- **Verified:** **V2** — `nix build .#checks.x86_64-linux.doctor` passes. + Permanent headless regression guard now in place. +- **Next:** #43 System-menu render (Firmware row). + +## 2026-07-09 — #53 theme previews + neon-glass quarantine +- **Task:** NEXT #53 — capture the last two missing previews + resolve the + neon-glass broken whole-swap. Product call: **quarantine**. +- **Did:** Confirmed the contract (working whole-swaps carry 12–23 + `@define-color` blocks; `waybar.nix:448` reads the whole-swap css **raw**, + no palette prepend — so neon-glass's 0-`@define-color` css is genuinely + broken). `git rm themes/neon-glass/waybar.css` → falls back to the generated + bar from its (fine) JSON palette. Rendered executive-slate + neon-glass via + theme-shot; captured 480×270 `preview.png` for both — **all 24 themes now + have previews**. +- **Verified:** **V2** — viewed both renders: executive-slate clean; neon-glass + bar now renders correctly (generated palette), no banner. V0 flake check green + with the css removed. +- **Pending:** none. Optional: hand `btop.theme` for the 3 identity themes; a + hardware recapture to match the floating-terminal composition (nicety note). + +## 2026-07-09 — #54 installer safety + polish (delegated, worktree) +- **Task:** NEXT #54. Sonnet worktree agent (installer only). +- **Did:** offline `compose-lock` failure fail-closes (reuses the + cache.nixos.org probe → `OFFLINE`) instead of a network `flake lock`; + disk-review `warn` on Windows/BitLocker/NTFS/LUKS signatures (before wipe); + account password min 8 (mirrors LUKS); HM pre-activate failure drops a + recovery-hint file on the target home. Diff reviewed + cherry-picked. +- **Verified:** V0 flake check + V1 `nix build .#nomarchy-install`. V2 (offline + fail path, NTFS-disk warning, pre-activate hint on a booted target) needs + crafted scenarios — queued in slimmed #54. Password ≥8 is V0-complete. +- **Next:** cheap scripted btop check for #52; then session wrap. + +## 2026-07-09 — #52 theme fidelity Broken/high-ROI fixes (delegated, worktree) +- **Task:** NEXT #52. Sonnet worktree agent (parallel, theme files only). +- **Did:** Six defects fixed: rose-pine btop→Dawn, everforest/summer-night + btop `inactive_fg`, vantablack role inversion, osaka-jade text/subtext+warn, + catppuccin-latte mantle/accentAlt/ANSI, catppuccin Mocha btop. **My call on + a flagged conflict:** the audit's accentAlt `#ea76cb` (Latte pink) fails the + 3.0 contrast floor → used Latte **mauve `#8839ef`** (contrast-safe, canonical). +- **Verified:** V0 — `check-theme-contrast` 24×7 **all pass**; flake check green. + btop.theme visual = **V2 render pending** (batched). Filed a residual note + (vantablack ansi[8], osaka-jade ansi[3] drift exposed by the fixes). +- **Next:** land #54; batched V2 render of the btop themes + #51 doctor glyph. + +## 2026-07-09 — #51 Waybar/menu behaviour bugs (delegated, worktree) +- **Task:** NEXT #51. Sonnet worktree agent (parallel with #52/#54, no shared + files). +- **Did:** doctor indicator `"text":""`→`"text":"󰒡"` + `class:bad` on failure + (`waybar.nix`); CC appearance toggles print "(requires rebuild)" like the + other CC toggles; summer-day scroll `e±1`→`r±1`. Reviewed diff, cherry-picked. +- **Verified:** V0 flake check + V1 (HM gen + `nomarchy-control-center` build) + on main. V2 render (glyph on an unhealthy bar) folded into #44's checks.doctor. +- **Next:** land #52/#54; batched V2. + +## 2026-07-09 — Promote V2-capable PROPOSED items → NEXT #51–54 (Bernardo-directed) +- **Task:** with the VM free (post-#46), Bernardo asked to bump the + V2-requiring PROPOSED items too. His triage; I did the move. +- **Did:** Four NEXT items — **#51** Waybar/menu behaviour bugs (doctor + indicator icon, CC appearance no-ops, summer-day scroll), **#52** theme + fidelity Broken/high-ROI (6 per-theme btop/palette defects; kept the audit + block as its spec), **#53** missing previews (executive-slate/neon-glass via + the theme-shot recipe) + the neon-glass waybar finish-or-quarantine call, + **#54** installer safety+polish (offline fail-closed, destructive-data + warning, password min 8, pre-activate recovery hint). Removed the promoted + pitches (breadcrumbs left); left the non-selected V2 items (generated-bar + affordances, summer-* CSS, fidelity/hierarchy polish, Live install cue, + offline/online source line) in PROPOSED. +- **Verified:** V0 (backlog only) — 51–54 present, no leftover pitch text. +- **Next suggestion:** these are session-render / install-VM verifiable — take + #51 or #52 (theme-shot), or #54 (test-install), on the next iteration. + +## 2026-07-09 — #46 V2 verify: install swap=0 + LUKS fail-closed (208b8d4) +- **Task:** NEXT #46 — exercise the full install path for the swap=0 + LUKS + fail-closed contracts (previously only pure `nix-instantiate`). Headless + KVM via a two-variant driver adapted from `tools/test-install.sh`. +- **Did:** Built the live ISO, ran two offline unattended installs: + **(B fail-closed)** no passphrase + no `NOMARCHY_NO_LUKS` → installer exits + RC=1 at the encryption stage with the fail message; `/dev/vda` stays BLANK + and disko `destroy` never runs. **(A swap=0)** `SWAP_GB=0` + passphrase → + install RC=0, subvol dump on `/mnt` shows `@,@home,@log,@nix,@snapshots` + and **no `@swap`**; installed disk boots (after LUKS unlock) to a fully + themed Boreal desktop, no error/autoconfig banner. +- **Verified:** **V2** — screenshots viewed: `b-failclosed.png`, + `a-progress-480.png` (DONE_SWAP0 + subvol dump), `a-first-boot.png` (themed + boot). Both contracts hold. #49's new `installer-disko` check now guards the + swap contract at eval time too. +- **Pending:** none for #46. Incidental: HM pre-activate warned in the offline + chroot (`xrdb: no display`) yet first boot came up themed anyway — the + "pre-activate failure → durable recovery hint" PROPOSED item still stands. +- **Next suggestion:** promote V2-capable PROPOSED items now the VM is free. + +## 2026-07-09 — #50 small code one-liners batch (V0/V1) +- **Task:** NEXT #50 — four small fixes. Kept in-house (shares rofi.nix / + flake.nix with the #47/#49 agents; done after they landed). +- **Did:** Bluetooth menu row + handler self-gate on `command -v + blueman-manager` (`rofi.nix`, like Printers); Control Center rollback list + `/tmp/nomarchy-gens` → `mktemp` + cleanup; `usbutils` added to the + `nomarchy-install` PATH (fingerprint `lsusb` probe); exported + nomarchy-doctor/control-center/battery-notify as flake `packages`. +- **Verified:** V0 flake check green; V1 — built the four flake packages + (incl. the new exports), `nomarchy-install` (usbutils), and the HM + generation (rofi + control-center bash -n via writeShell*). All exit 0. +- **Pending:** none. +- **Next suggestion:** V2-capable promotions. + +## 2026-07-09 — #49 pure-contract checks.* batch (delegated, worktree) +- **Task:** NEXT #49 — add pure (no-VM) `checks.*` guards. Delegated to a + worktree opus agent, parallel with #46 VM + the #47/#48 agent. +- **Did:** Four new checks wired into `flake.nix` + CI py_compile expansion: + `hardware-db-modules` (installer DB module names ∈ pinned + nixos-hardware.nixosModules), `installer-compose-lock` (offline composer + contract on fixtures), `installer-disko` (pure Nix assert: swapSize + "0"/"0G" → no @swap, "2G" → sized, withLuks wrapping — permanently guards + the #46 fix), `windowrule-syntax` (builds the generated hyprland.conf, fails + on pre-0.53 grammar / windowrulev2 — permanently guards `ed7fd93`). CI + py_compile now covers all tracked `*.py`; TESTING.md §1 synced. +- **Verified:** V0 flake check green; **V1** I built each of the four + `checks.x86_64-linux.*` on main after cherry-pick — all exit 0. Agent's + negative tests confirmed each guard FAILS on a deliberate regression. +- **Pending:** none — all pure/headless, no VM. (A KVM CI runner, item 20, + would later run these + the VM suite on push.) +- **Next suggestion:** #50 one-liners (mine); then close #46 (variant A boot). + +## 2026-07-09 — #47+#48 docs/string factual-drift pass (delegated, worktree) +- **Task:** NEXT #47 (docs + string drift) + #48 (i2c option-docs). Delegated + to a worktree sonnet agent, parallel with #46 VM + the #49 agent, no shared + files. +- **Did:** Agent fixed every drift after verifying each claim against code: + theme count 21→24 (README ×3, TESTING, template), waybar whole-swap list + (correctly split waybar.css=5 incl. neon-glass vs waybar.jsonc parity=4 + + boreal), idle 30→15min-battery-only, Tailscale sudo→operator, live.nix + header, MIGRATION link + URL, README layout tree (+doctor/CC/battery-notify), + OVERRIDES swappy→satty, always-on note; keybind nmtui→networkmanager_dmenu; + nomarchy-menu usage string (+doctor/firmware/controlcenter/rollback). Added + README i2c rows (#48). Reviewed the diff, cherry-picked onto main. +- **Verified:** V0 flake check green; `checks.option-docs` now **green** (85 + options, no stale rows) — closes #48; V1 HM generation builds (usage string + + keybind label confirmed in the built output). Re-run on main after cherry-pick. +- **Pending:** none — docs/config, fully verified at their tier. +- **Next suggestion:** land #49 (checks batch); then #50. + +## 2026-07-09 — Promote V0/V1 PROPOSED items → NEXT #47–50 (Bernardo-directed) +- **Task:** Bernardo asked to bump the non-V2 PROPOSED items into NEXT (to + feed parallel worktree agents that can't touch the VM). Triage is his call; + I did the move on his instruction. +- **Did:** Grouped the genuinely V0/V1 (no session/VM) pitches into four NEXT + items — **#47** docs+string factual-drift pass, **#48** i2c option-docs, + **#49** pure-contract checks.* batch (py_compile expand, hardware-db ∈ + nixos-hardware, installer pure contracts, windowrule dead-syntax guard), + **#50** small code one-liners (Bluetooth self-gate, CC mktemp, usbutils on + PATH, export overlay tools as flake packages). Removed the promoted pitches + from PROPOSED, leaving `_→ #NN_` breadcrumbs. Deliberately left V2-wanting + items in PROPOSED (Waybar doctor icon, CC appearance toggles, theme-color + retunes, neon-glass, summer-day scroll, installer review warnings). +- **Verified:** V0 (docs/backlog only) — grep confirms #47–50 present, no + leftover promoted-pitch text. +- **Pending:** none. #47–50 are now actionable and agent-friendly (all no-VM). +- **Next suggestion:** spawn worktree agents on #47–50 in parallel with the + #46 VM; land their branches after review. + +## 2026-07-09 — #44 Doctor hardware section (delegated to worktree agent) +- **Task:** NEXT #44 — read-only hardware checks in nomarchy-doctor + (HARDWARE.md §10). Delegated to a worktree-isolated opus agent so it ran + in parallel with the #46 install VM without KVM contention. +- **Did:** Agent added six self-gating checks to `nomarchy-doctor.sh` + (NetworkManager, default PipeWire sink, VA-API/GL smoke, fprintd unit + + enroll nudge, fwupd active + get-updates warn, laptop charge-threshold), + each skipping cleanly when the tool/service/device is absent — same + ok/bad/warn/skip contract + one-fix-command style. Reviewed the diff and + cherry-picked onto main (branch `agent/doctor-hw-44`). +- **Verified:** V0 flake check green; **V1** `nix build …pkgs.nomarchy-doctor` + (writeShellApplication runs bash -n + shellcheck at build) — re-run by me + on main after cherry-pick. Bonus: the agent ran the built binary on the + AMD dev box (read-only) — all six checks fired + self-gated correctly. +- **Pending:** V2 — extend the `checks.doctor` nixosTest with fakes for the + new rows (VM; deferred while #46 held KVM). fwupd get-updates exit-code + semantics worth pinning in that fake. +- **Next suggestion:** V2-verify #43+#44 together on the next VM pass. + +## 2026-07-09 — #43 System ▸ Firmware fwupd menu (core shipped, V2 pending) +- **Task:** NEXT #43 — surface fwupd/LVFS updates in the menu (VISION § A). + Done while the VM was busy with #46, so V0/V1 only by design. +- **Did:** Added a self-gated **Firmware** row to the System submenu + (`command -v fwupdmgr`, like Printers) + dispatch + a `firmware)` handler + that opens a terminal and runs `fwupdmgr refresh` → `get-updates` → y/N + confirm → `fwupdmgr update` (never silent-flash; fwupd confirms each + device + prompts reboot). Updated docs/HARDWARE.md §4 to "shipped". +- **Verified:** V0 `nix flake check --no-build` green; V1 HM generation + builds; extracted the generated `nomarchy-menu` and confirmed `bash -n` + clean + the firmware handler emitted correctly (`exec ghostty -e sh -c`, + quoting intact). +- **Pending:** V2 render (System submenu shows the row + flow launches) — + queued in slimmed #43, do on next VM pass (KVM was held by #46). V3 real + LVFS flash → HARDWARE-QUEUE. Optional MOTD/Waybar extensions noted. +- **Next suggestion:** land the #44 doctor-hw agent branch; V2-verify #43+#46 + together once KVM frees. + +## 2026-07-09 — #45 V2 verify Boreal default + capture preview.png (theme-shot) +- **Task:** NEXT #45 — close the honesty gap on Boreal-as-default (shipped V0 + only at #42) and capture the picker preview. VM-capable environment (KVM). +- **Did:** Rendered the full Boreal desktop headlessly via `tools/theme-shot.nix` + (softGL, 1920×1080). Confirmed the session reads as **Boreal** — aurora + wallpaper paints, Waybar violet accent (`#B79BE8`) workspace pill, GeistMono + clock, rofi launcher violet prompt/selection — not Tokyo Night. Captured a + 480×270 `themes/boreal/preview.png` (matches the other 21's format) from the + desktop frame; added a cursor-park to theme-shot so the shot is clean. (En + route, found+fixed the windowrule banner — separate commit `ed7fd93`.) +- **Verified:** V0 flake check green; **V2** theme-shot desktop.png + menu.png + viewed (banner gone post-fix, bar+launcher themed). Scripted: contrast 24×7 + pass; audit boreal advisory-only. +- **Pending:** preview is a headless bare-desktop capture, not the + floating-terminal composition of the other 21 — hardware recapture noted + under theme polish. V3 (real panel/GPU) not done — not required. The float + rules now parse but weren't exercised on real windows (item 41 territory). +- **Next suggestion:** #46 (install swap=0 / LUKS fail-closed) — driver ready. + +## 2026-07-09 — Fix Hyprland windowrule config-error banner (windowrule 0.53 migration) +- **Task:** bug tripped over while doing #45 (VM Boreal verify) — the default + desktop showed a red Hyprland config-error banner on every boot, occluding + the Waybar. +- **Did:** Root-caused via the theme-shot harness + Hyprland 0.55.4 source + (`handleWindowrule`): Hyprland 0.53 rewrote window rules — `windowrulev2` is a + hard error and the old rule-first `float, class:^…$` order no longer parses + (so the float rules weren't even applying). Migrated the 7-rule block in + `modules/home/hyprland.nix` to the new hyprlang grammar ` , + match: ^…$` (e.g. `float 1, match:class ^…$`). +- **Verified:** V0 `nix flake check --no-build` green; built the generated + `hyprland.conf` and confirmed emitted lines; **V2** theme-shot re-render — + banner gone, themed Waybar now renders (violet accent, GeistMono clock). +- **Pending:** none for the fix. A cheap `checks.*` that greps the generated + conf for the dead rule-first pattern could lock it (PROPOSED). +- **Next suggestion:** finish #45 (capture boreal preview.png), then #46. + +## 2026-07-09 — Default theme → Boreal (item #42) +- **Task:** NEXT #42 — distro default theme Tokyo Night → Boreal (`VISION § B` / v1.0 bar). +- **Did:** Seeded `theme-state.json` + `templates/downstream/theme-state.json` from `themes/boreal.json` (incl. border + settings). Eval fallbacks: `theme.nix` preset → boreal.json; plymouth + live ISO splash hex fallbacks; theme-shot / theme-sync examples; TESTING/RECOVERY wording. +- **Verified:** V0 — state JSON matches boreal palette/fonts; `check-theme-contrast` 24×7 pass; py_compile theme-sync. Full `nix flake check --no-build` started but not waited (slow ISO eval). No V2 apply / V3 session. +- **Pending:** Filed **NEXT #45** (V2/V3 Boreal verify + `preview.png`) and **#46** (V2 install swap=0 / LUKS fail-closed) for a VM-capable agent. Queue head remains #43 Firmware for product work. +- **Next suggestion:** #43 Firmware, or a VM agent takes #45/#46. + +## 2026-07-09 — Install contracts: swap=0 + unattended LUKS (workflow test run) +- **Task:** VISION § v1.0 install golden path — PROPOSED install P0s (NEXT head blocked: item 14 wait-cron, 20 `[human]`, 33 V3 visual, 41 hw). Test of VISION→BACKLOG→verify→commit loop. +- **Did:** (1) `nomarchy-install` passes `swapSize=0` when swap disabled (was always `"${SWAP_GB}G"` → `"0G"` still created `@swap`). (2) `disko-config.nix` treats `"0"` and `"0G"` as no-swap. (3) Unattended LUKS fail-closed: require `NOMARCHY_LUKS_PASSPHRASE` or `NOMARCHY_NO_LUKS=1`. +- **Verified:** V0 — `bash -n`; pure `nix-instantiate` of disko subvols for `0`/`0G`/`16G` (no `@swap` only on zero). Not V2 install matrix this run. +- **Pending:** optional pure `checks.*` for swapSize; unattended no-swap/no-LUKS in `test-install.sh` when convenient. +- **Next suggestion:** promote another VISION § A slice (firmware menu / doctor) or boreal default to NEXT. + +## 2026-07-08 — Theme UI Review: Typography Polish & Icon Call (iteration #87) +- **Task:** NEXT item 28 (Theme UI review). +- **Did:** + 1. Resolved the pending human call regarding Rofi vs Waybar icon weights. The user opted for Option A (keep full-color Papirus icons for menus, treating them as an independent colorful layer). + 2. Fixed typography scale mismatches: `swaync.nix` now binds explicitly to the theme's font scale (`t.fonts.size`) and families (`t.fonts.ui`/`mono`). + 3. Fixed Rofi's font configuration to use the dynamic `t.fonts.size` rather than a hardcoded `12`. + 4. With the color, hierarchy, semantic dims, and typography now audited and unified across surfaces, Item 28 is completed and wiped from the backlog. +- **Verified:** Nix configs evaluate successfully. +- **Pending:** nothing. + +## 2026-07-08 — Hyprland Config Fix: Window rules syntax (iteration #88) +- **Task:** Investigate a red Hyprland error banner visible in VM screenshots during boot. +- **Did:** + 1. Analyzed Hyprland log output and screenshot OCR text to pinpoint the config error. + 2. Identified that `class:` regex matching in Hyprland requires `windowrulev2`, but the previous commits incorrectly used `windowrule` causing syntax errors (`invalid field float: missing a value`). + 3. Replaced `windowrule = [` with `windowrulev2 = [` in `modules/home/hyprland.nix`. + 4. Updated README.md to document the IPC-based instant reaction for display profiles. +- **Verified:** Ran `hyprctl configerrors` headlessly and re-captured the VM snapshot which no longer displays the red banner. +- **Pending:** Wait for the test build to confirm screenshot is clean. + + +## 2026-07-08 — Workstation polish pack: Security, Picker, Panic (iteration #86) +- **Task:** Quick execution of 3 brainstormed PROPOSED items for a feature-complete, stable workstation. +- **Did:** + 1. Enabled `security.apparmor.enable` system-wide. + 2. Added `boot.kernelParams = [ "panic=10" "oops=panic" ]` to auto-reboot the system upon catastrophic driver failure. + 3. Added `pkgs.hyprpicker` and bound it to `SUPER+SHIFT+C` for an instant Wayland color-picker. +- **Verified:** Flake eval / logic. +- **Pending:** nothing. + +## 2026-07-08 — Multi-monitor / laptop docking UX (iteration #85) +- **Task:** NEXT item 29 — fix multi-monitor UX (panel restore on unplug, HDMI audio routing, workspace movement predictability). +- **Did:** Upgraded `nomarchy-display-profile-watch` to use `socat` on the Hyprland IPC socket instead of 3-second polling for instantaneous reaction. Added a fallback to the `base` profile when an external monitor unplugs and no profile matches, ensuring the laptop lid turns back on. Added `SUPER+ALT+arrow` keybinds to `keybinds.nix` for explicit, predictable workspace-to-monitor movement. Verified `nomarchy-menu audio` already serves as the HDMI audio router. +- **Verified:** Local script edits, ready for human hardware validation. +- **Pending:** nothing. +- **Next suggestion:** review remaining NEXT items. + +## 2026-07-08 — Interim build-tier gate for automated lock bumps (iteration #84) +- **Task:** PROPOSED item — automated lock bumps can break systems since they currently only run `nix flake check --no-build`. +- **Did:** Edited `.gitea/workflows/bump.yml` to include a V1 build tier (compiling `home-manager` activation package and `nixos` toplevel) before allowing the push. +- **Verified:** Local YAML edit, no further check needed. +- **Pending:** nothing. +- **Next suggestion:** review remaining items (e.g., Battery charge limit). + +## 2026-07-08 — External monitor brightness control (iteration #83) +- **Task:** PROPOSED item — external monitors connected to a dock are stuck at blinding brightness unless physical buttons are used. +- **Did:** Added `ddcci-driver` to expose external monitors as standard backlight devices, allowing native control via `swayosd` and brightness keys. +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** review remaining items (e.g., Battery charge limit instant toggle or Flatpak camera picker). + +## 2026-07-08 — Drive health monitoring (iteration #82) +- **Task:** PROPOSED item — early SMART warnings for failing hardware. +- **Did:** Added `services.smartd.enable = true` and `services.smartd.notifications.x11/wall` to `modules/nixos/default.nix`. Also explicitly enabled `services.systembus-notify.enable` to resolve a module collision with `earlyoom`. +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. `ddcutil`). + +## 2026-07-08 — Microphone noise cancellation (iteration #81) +- **Task:** PROPOSED item — system-wide background noise filtering for voice calls. +- **Did:** Enabled `services.easyeffects.enable = true` in `modules/home/default.nix`. +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. `smartd`). + +## 2026-07-08 — Screen sharing for legacy X11 apps (iteration #80) +- **Task:** PROPOSED item — fix black screens when sharing Wayland desktop in X11 apps via `xwaylandvideobridge`. +- **Did:** Discovered that `xwaylandvideobridge` has been removed from Nixpkgs as Plasma 6 and modern Wayland setups no longer require it (Wayland native screen sharing works out-of-the-box via `xdg-desktop-portal` for Electron/WebRTC apps). +- **Verified:** N/A (skipped). +- **Pending:** nothing. + +## 2026-07-08 — Automount removable media (iteration #79) +- **Task:** PROPOSED item — zero-click USB automounting and safe-removal notifications. +- **Did:** Added `services.udiskie.enable = true` to `modules/home/default.nix`. +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. xwaylandvideobridge or easyeffects). + +## 2026-07-08 — File Manager Archive Backend (iteration #78) +- **Task:** PROPOSED item — provide a GUI backend for Thunar's archive extraction plugin. +- **Did:** Added `file-roller` to `environment.systemPackages` in `modules/nixos/file-manager.nix`. +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. udiskie or xwaylandvideobridge). + +## 2026-07-08 — Hardware security key support (iteration #77) +- **Task:** PROPOSED item — enable FIDO2/U2F support for hardware tokens. +- **Did:** Added `services.pcscd.enable = true` to `modules/nixos/default.nix` (NixOS >= 26.05 udev handles u2f natively). +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. file manager archive backend or udiskie). + +## 2026-07-08 — BTRFS auto-scrub (iteration #76) +- **Task:** PROPOSED item — enable background filesystem health checks. +- **Did:** Added `services.btrfs.autoScrub` with a monthly interval to `modules/nixos/default.nix`. +- **Verified:** V1 (flake check). +- **Pending:** nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. file manager archive backend or pcscd). + +## 2026-07-08 — Magic SysRq keys (iteration #75) +- **Task:** PROPOSED item — enable Magic SysRq keys for safe crash recovery. +- **Did:** Added `boot.kernel.sysctl."kernel.sysrq" = 1;` to `modules/nixos/default.nix`. +- **Verified:** V1 (flake check). +- **Pending:** user visual review or nothing. +- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. BTRFS auto-scrub or udiskie). + +## 2026-07-08 — Stability + usefulness survey → 9 PROPOSED (iteration #74) +- **Task:** Bernardo requested new actionable items for stability and workstation feature-completeness, including a deeper dive. +- **Did:** Researched the codebase for gaps. Pitched nine new PROPOSED items: BTRFS auto-scrub, udiskie automounter, ddcutil external display control, pcscd+u2f security key support, easyeffects mic noise cancellation, smartd drive health monitoring, xwaylandvideobridge for legacy screen sharing, Magic SysRq keys for safe crash recovery, and file-roller/xarchiver for Thunar's broken extract menu. +- **Verified:** n/a (research only). +- **Pending:** awaiting Bernardo's triage to elevate to the active queue. +- **Next suggestion:** triage the PROPOSED items. + +## 2026-07-08 — Screenshot annotation step (iteration #73) +- **Task:** PROPOSED item — add `satty` for annotating screenshots. +- **Did:** Added `satty` to packages with a themed `config.toml`. Bound `$mod SHIFT, Print` to start `satty` directly on a region crop (`slurp`), and added "Annotate region" to the rofi Capture menu. +- **Verified:** V1 (build tested `homeConfigurations.nomarchy.activationPackage`). +- **Pending:** V3 queued in HARDWARE-QUEUE.md to verify `satty` theming and copy/save functionality. +- **Next suggestion:** wait for Bernardo to clear `[human]` / `[blocked:hw]` bottlenecks or tackle the stability PROPOSED item. + +## 2026-07-08 — home.stateVersion overridable (iteration #72) +- **Task:** PROPOSED item — `home.stateVersion` should be `mkDefault` to support migrations. +- **Did:** Changed `home.stateVersion = "26.05"` to `lib.mkDefault "26.05"` in `modules/home/default.nix`. +- **Verified:** V1 (flake check --no-build green; `homeConfigurations.nomarchy.activationPackage` built successfully). +- **Pending:** user visual review or nothing. +- **Next suggestion:** triage other PROPOSED items or await Bernardo for `[human]`-blocked tasks. + +## 2026-07-08 — Stability + usefulness survey → 2 PROPOSED (iteration #71) +- **Task:** Bernardo asked to find the next tasks that make the distro more + stable + more useful. Research iteration (LOOP §1: write up PROPOSED). +- **Did:** mapped the module/pkg surface + HARDWARE-QUEUE. Findings: + (stability) the state-file path is robust (friendly JSON errors, validate + cmd, atomic rebuild on corruption — not a gap); the real hole is the + automated lock-bump channel (#14) pushing to `main` on **eval-only** with + **no binary cache**, so untested runtime/build breakage compiles straight + onto users — the true fix is #20 (VM-suite-in-CI, `[human]`, KVM-runner- + blocked). (usefulness) the capture suite is rich but has no screenshot + **annotate** step (no satty/swappy); clipboard/emoji/colorpicker/OCR/ + recording all already ship. Filed 2 PROPOSED: an interim build-tier lock + gate (no KVM needed, raises the floor until #20) and a satty annotate step. +- **Verified:** V0 (docs-only; flake check was green in #70, tree unchanged). +- **Pending:** Bernardo to triage the 2 PROPOSED into tiers, and — the + highest-leverage stability action — decide whether to unblock #20 (register + a KVM runner) so the lock-bump channel gets a real runtime gate. +- **Next suggestion:** if #20 stays blocked, the interim build-tier gate is + the cheapest stability win; satty is the cheapest usefulness win. + +## 2026-07-08 — QA sweep: clean tree, no actionable drift (iteration #70) +- **Task:** QA sweep — headless-actionable queue is empty (NEXT is all + `[human]`/`[blocked:hw]`/V3-visual: #29 hw, #28 icon-weight [human]+visual, + #14 "confirm first run" not session-confirmable, #20 [human], #33 V3-visual + ~10 authored rasi, #41 needs hw class checks). +- **Did:** ran the full scripted suite. `flake check --no-build` green; built + + ran `option-docs` (README tables track the live nomarchy.* surface, no + drift) and `theme-contrast` (21 palettes, all pairings pass); + `audit-theme-design.py` shows only pre-curated/identity findings (vanta/ + white hueless, inherent CVD collapse, ANSI conventions, miasma earthy bad). + Template drift check: services list matches surface exactly; opt-in + commented examples (nightlight/monitors/displayProfiles) all reference real + options. No churn manufactured (LOOP §1). +- **Verified:** V0 (flake check) + built option-docs & theme-contrast checks. +- **Pending:** nothing. Note: `.claude/settings.json` deletion was already in + the tree at session start — not mine, left untouched (LOOP §0.2); this + commit is pathspec-scoped to agent/JOURNAL.md. +- **Next suggestion:** await Bernardo on #29/#33 or the #28 icon-weight call; + otherwise #33 rofi scrollbar wants a themed (non-headless) session. + +## 2026-07-08 — Color-only status sweep + inhibited-bell fix (iteration #69) +- **Task:** BACKLOG #28 slice — the audit's systemic "status is never + color-only" finding (accessibility): sweep the remaining Waybar status + indicators for shape/glyph redundancy. +- **Did:** swept every status module. All already carry non-color + redundancy (updates shows a count + self-hides; vpn/doctor/nightlight/ + recording self-gate; battery/idle/muted are glyph-distinct) EXCEPT one: + swaync `inhibited-*` reused the normal bells (󰂜/󰂚), so an inhibitor + suppressing notifications was distinguished from normal by *color alone*. + Fixed: route every suppressed state (DND + inhibited) to the bell-off + glyph 󰂛 + @muted — generated bar (waybar.nix) AND all four whole-swaps + (summer-day/night use their own bell-off, executive-slate/boreal 󰂛; + parity rule). Reused the proven DND glyph → zero tofu risk. +- **Verified:** V0 (flake check --no-build green; all 4 jsoncs valid JSON). + V1: built homeConfigurations.nomarchy.activationPackage; confirmed in the + *built* artifact that inhibited-none/notification = U+F009B (bell-off, + was F009C/F009A) and style.css routes .inhibited-* → @muted. +- **Pending:** V3 queued (HARDWARE-QUEUE, Any-machine) — confirm the muted + bell-off renders while an app holds a notification inhibitor. Low risk: + the glyph already ships for DND. MEMORY gains the "status is never + color-only" design invariant (regression guard). +- **Next suggestion:** #28 remaining is [human] (icon-weight call) or + V3-visual; likely a QA sweep or await Bernardo on #29/#33. + +## 2026-07-08 — Migration guide: existing NixOS → Nomarchy (docs, direct request) +- **Task:** Bernardo asked whether THIS machine (TuringMachine, NixOS 25.11) + can migrate to Nomarchy without a reinstall, keeping all files — and to + write the walkthrough into a MIGRATION.md. +- **Investigated the live machine:** already NixOS, personal modular flake, + systemd-boot, LUKS2 + btrfs with the exact subvol layout Nomarchy wants + (incl. `@snapshots`/`@home-snapshots`), AMD 7840U. Verdict: migration is a + config switch (reuse hardware-configuration.nix), NOT a reinstall; `@home` + is never touched. Main deltas: 25.11→26.05, keep system.stateVersion=24.11, + power-mgmt collision (his ryzenadj/TLP vs Nomarchy PPD), HM-module→standalone. +- **Decisions (Bernardo):** adopt Nomarchy PPD + drop all ryzenadj; no Secure + Boot (drop lanzaboote); full cutover. +- **Did:** wrote `docs/MIGRATION.md` — generic "existing NixOS → Nomarchy" + guide with TuringMachine as the worked example: candidacy checklist, the + two data-safety rules (never bump system.stateVersion; /home untouched), + Phase 0 snapshots → Phase 1 build-only gate → Phase 2 reversible + `nixos-rebuild test` → Phase 3 reconcile → Phase 4 cutover, triple rollback + net. Verified the downstream API against lib.nix (mkFlake src/username/ + hardwareProfile) + templates/downstream so the flake/system/home examples + are real, not guessed. +- **Verified:** V0 (docs) — referential accuracy against lib.nix + template; + did NOT run the migration commands (they'd alter the live machine). +- **Follow-ups:** PROPOSED — make `home.stateVersion` mkDefault (migrating + users currently need mkForce). +- **Next suggestion:** if Bernardo greenlights, scaffold Phase 1 from his real + hardware-configuration.nix. + +## 2026-07-08 — Calendar on the clock click (iteration #68, item 42) +- **Task:** item 42, de-scoped live by Bernardo — DON'T hook up calendar + accounts; just ship a lightweight calendar that opens instantly on the + Waybar date click, user configures sync themselves. calcurse (his pick; + now the right one since it's self-contained for the user's own sync). +- **Did:** new `nomarchy-calendar` launcher (waybar.nix) → calcurse in a + ghostty window tagged `--class=com.nomarchy.calendar` + + `--gtk-single-instance=false` (fresh standalone window); self-gates on + calcurse. Clock `on-click = nomarchy-calendar`, dropped the format-alt + date-toggle (conflicts with on-click), moved the long date into the + tooltip. Parity: on-click in all four whole-swap jsoncs. windowrule + float+size(60/65%)+center on the class (hyprland.nix). Shipped calcurse + + pwvucontrol **uncommented** in the downstream template (opt-out). + Enforced **ghostty always installed** (ghostty.nix: `enable = true` + unconditional, the nomarchy.ghostty.enable option now gates only config) + so the launcher can rely on `--class`. +- **Verified:** V0 green; all four jsoncs valid JSON; V1 HM built — launcher + ships the right command, generated clock renders `on-click: + nomarchy-calendar` with format-alt gone + date in the tooltip, hyprland + has the 3 calendar windowrules, ghostty present in the HM profile. +- **Pending:** V3 — click the clock → calcurse floats centered (the + `--class`→windowrule match) and renders its month view; verify on a real + session (queued). Sync is deliberately the user's own local config, NOT + a distro feature (Bernardo's call) — a future iteration should not + re-propose account sync. +- **Next suggestion:** Bernardo's big new task — investigate whether THIS + machine (NixOS 25.11) can migrate to Nomarchy without a reinstall, + keeping all files, and plan it. + +## 2026-07-08 — Combined power menu on the bar icons (iteration #67, item 36b) +- **Task:** BACKLOG item 36(b), now unblocked. Bernardo's calls: keep the + granular System ▸ Battery limit / Power profile rows; build ONE combined + menu; open it two ways — the Waybar **battery** icon AND the + **power-profile** icon (the profile icon's click flips from cycle → menu). +- **Did:** New `nomarchy-menu powermgmt` (rofi.nix): a flat list of Profile + rows (→ `powerprofilesctl set`, like the power-profile picker) + Charge + limit rows (→ the same theme-sync writer as batterylimit), each half + self-gating on its hardware; no backend duplication, no Custom flow (that + stays in the dedicated batterylimit picker). Wired `battery.on-click` + + `custom/powerprofile.on-click` → `powermgmt` in waybar.nix AND all four + whole-swaps (parity). Named `powermgmt` to avoid the whole-swaps' existing + `custom/powermenu` (that's the shutdown button → `nomarchy-menu power`, + left untouched). `nomarchy-powerprofile-cycle` kept as a standalone bin + (now just unbound by default). +- **Verified:** V0 green; V1 HM built (bash -n passed) — shipped + nomarchy-menu has the `powermgmt` case; generated waybar-config.json wires + both icons to it (2 hits); all four whole-swap jsoncs re-wired and still + parse as JSON, shutdown buttons intact. +- **Pending:** V3 — click either icon → menu opens, picking a profile / + charge preset actually applies (queued in HARDWARE-QUEUE). The + instant-charge-write follow-up stays PROPOSED/`[blocked:hw]`. +- **Next suggestion:** back to the queue — item 33 (rofi scrollbar) or a + QA sweep; both remaining NEXT items lean human/hardware. + +## 2026-07-08 — Capture rows show Print-key hints (iteration #66, direct requests) +- **Task:** Two live requests from Bernardo. (1) Fold his provided + power-menu mockup into item 36(b). (2) "make sure all menu items that + have a keybind display the keybind" (he dropped the flush-right ask — + inline is fine). +- **Did:** (1) BACKLOG item 36(b) un-gated — mockup + Nomarchy adaptation + (reuse batterylimit/power-profile writers, not raw TLP; wire the Waybar + battery `on-click`; one open call: replace-or-keep the System rows). + (2) Audited every menu/submenu: all SUPER(+CTRL) module-opening rows + already carry a hint (item 28c). The one real gap = the Tools ▸ Capture + rows, which run the exact `grim` commands of the Print / SHIFT+Print / + CTRL+Print binds but showed nothing. Added `hintForBind mods key` (reads + keybinds.nix by mods+key, drift-free) → hints on the three matching rows, + and `-markup-rows` on the Capture menu so the spans render. +- **Verified:** V0 `nix flake check --no-build` green; V1 HM built, shipped + nomarchy-menu shows `Region → clipboard {{Print}}`, `Region → file + {{SHIFT + Print}}`, `Full screen → file {{CTRL + Print}}` under + `-markup-rows`. Substring `case` matches sit before the appended span, so + dispatch is unaffected (same proven pattern as Tools/System). +- **Pending:** V3 — pango dim actually rendering in the Capture submenu + (queued on the existing Capture entry). Power-menu item 36(b) stays + `[human]` on the replace-or-keep call. +- **Next suggestion:** back to the queue head (item 36a done; 32 done) — + next actionable is likely item 33 (rofi scrollbar) or a QA sweep. + +## 2026-07-08 — SUPER+? cheatsheet re-fixed: base keysym (iteration #65, item 32) +- **Task:** BACKLOG item 32 — SUPER+? still didn't open the cheatsheet + after item 26's fix. Re-diagnose. +- **Did:** Root cause = wrong keysym, not path/rofi. Hyprland 0.55 resolves + a bind's keysym with SHIFT *consumed* (issue #7750, since 0.43), so + `$mod SHIFT, question` looks for `slash` and never matches. Item 26 fixed + the modmask but kept the shifted keysym → still dead. Changed keybinds.nix + `question` → `slash` (the BASE keysym, exactly like the working + `$mod SHIFT, 1` workspace binds in this same file); updated rofi.nix + prettyKeys collapse `SHIFT + /` → `?` so the cheatsheet still reads + "SUPER + ?". Dispatch (`nomarchy-menu keybinds`) confirmed already fine. +- **Verified:** V0 `nix flake check --no-build` green. V1: HM activation + built; grepped the built hyprland.conf → `bind=$mod SHIFT, slash, exec, + nomarchy-menu keybinds` (beside the proven `$mod SHIFT, 1`), `kb_layout=us` + confirms slash→? ; `nix eval` of prettyKeys → "SUPER + ?". +- **Pending:** V3 — the keypress actually opening the menu is invisible + below hardware (that's how item 26 slipped); the existing HARDWARE-QUEUE + "SUPER+? opens the cheatsheet" entry updated to the slash fix. +- **Next suggestion:** Bernardo's two live requests — (1) fold the provided + power-menu mockup into item 36(b) [design now in hand]; (2) right-align a + keybind hint on every menu/submenu row that has one. + +## 2026-07-07 — Battery-limit toggle in rofi System (iteration #64, item 36a) +- **Task:** BACKLOG item 36(a) — Bernardo wanted the charge-threshold + toggle as a first-class row in `nomarchy-menu › System`, not buried in + the gum control-center where #55 put it. +- **Fix:** `rofi.nix` — new `batterylimit` subcommand: a preset picker + (80%/90%/60%/Off/Custom…) with battery-0XX icons, writing the baked + `settings.power.batteryChargeLimit` via theme-sync `--no-switch` (lands + on next sys-rebuild; power.nix's oneshot applies it). Added a System row + self-gated on the `charge_control_end_threshold` sysfs node (like Power + profile), dispatch, and usage string. **Moved** — removed the duplicate + "Battery Limit" case + choose entry from `nomarchy-control-center.sh` so + it lives in one place. +- **Verified:** V0 green. V1: control-center builds clean (shellcheck), + home activation builds clean (writeShellScriptBin's `bash -n` on the + menu passed); grepped the built script to confirm the row/dispatch/case/ + guard are all present. Live menu interaction is a V3 check (queued). + +## 2026-07-07 — Floating-window audit, conservative cut (iteration #63, item 41) +- **Task:** BACKLOG item 41 — Bernardo: "take a conservative approach." + Broaden the windowrule float set beyond the mixer. +- **Method:** surveyed what GUI dialogs the distro actually ships (not a + blind list). Network is dmenu/nmtui (no GUI editor → no rule). Confident, + shipped, unambiguous dialogs: **blueman** (manager + adapters) and + **system-config-printer**. Deferred polkit (hyprpolkitagent exposes no + discoverable class) and GTK file portals — guessing a class = dead rule, + so those wait for a hardware `hyprctl clients` read. +- **Fix:** `hyprland.nix` windowrule block — added float + center for the + two, `(?i)` regex tolerating the XWayland `.…-wrapped` form. RE2 `(?i)` + is already relied on in this file (line ~47), so it's consistent. +- **Verified:** V0 green; `nix eval` confirms the rendered regex escaping + is correct (single backslash, `?` quantifier unescaped). Whether the + classes actually match live is V3 (queued — includes confirming the + regex catches the real class). + +## 2026-07-07 — Right-click volume → floating mixer (iteration #62, item 35) +- **Task:** BACKLOG item 35 — right-click the Waybar volume module to + open a mixer in a floating window. (Bernardo re-launched /loop after I'd + flagged it as gated on 41 — read as "handle it," so I did 35 fully and + re-scoped 41 to just broadening the float set.) +- **Chose pwvucontrol** (PipeWire-native, GTK4/libadwaita → themes via the + portal like Amberol; distro is already wpctl/PipeWire). Verified app-id + `com.saivert.pwvucontrol` from the built package. +- **Changes:** (1) `waybar.nix` pulseaudio `on-click-right = "pwvucontrol"` + + parity into all four whole-swap jsoncs (summer-day/night, boreal, + executive-slate). (2) `hyprland.nix` — the config's **first** `windowrule` + block: `float` + `center` for the mixer classes (pwvucontrol + pavucontrol + fallback), normal-priority list so downstream concatenates. (3) + `pwvucontrol` added to the template `home.packages`. +- **Verified:** V0 green; `nix eval` of the homeConfiguration confirms the + windowrule regex renders with correct single-backslash escaping + (`class:^(com\.saivert\.pwvucontrol|…)$`). The right-click→float→center + interaction is a V3 session check (queued). Item 41 now = broaden the set. + +## 2026-07-07 — Window focus: vim keys → arrows (iteration #61, direct request) +- **Task:** Bernardo — switch window navigation from vim-style + SUPER+H/J/K/L to SUPER+arrow keys. +- **Fix:** `modules/home/keybinds.nix` focus binds now use + `left/right/up/down` (movefocus l/r/u/d); H/J/K/L freed. No conflict — + SUPER+arrows were unbound and there were no vim *move-window* binds. + Also taught `prettyKeys` (rofi.nix) to render the arrow keysyms as + glyphs (← → ↑ ↓), so the SUPER+? cheatsheet reads "SUPER + ←" not + "SUPER + left" (Return etc. unaffected — no false substring match). +- **Verified:** V0 green; `nix eval` confirms the binds render as + `$mod, left, movefocus, l` … and the glyph mapping is correct. + +## 2026-07-07 — Audio mime → Amberol (iteration #60, item 37) +- **Task:** BACKLOG item 37 — default video + audio players + mime. On + inspection both players were already installed: mpv (video) and amberol + (a prior template add). So the real gap was the *mime wiring* — audio + files still opened in mpv (mime.nix mapped audio/* → mpv.desktop). +- **Fix:** `modules/home/mime.nix` — repoint the audio/* types to + `io.bassi.Amberol.desktop` (verified against the built package's + actual .desktop id + its MimeType set) and broaden coverage (mp3/flac/ + ogg/opus/wav/m4a/aac, both canonical and x- names). Video stays mpv. +- **Why Amberol:** GTK4/libadwaita, so it themes via the portal + color-scheme the distro already wires; minimal, purpose-built for + "open a file → play." Degrades to mpv if a downstream drops it. +- **Verified:** V0 green; entries mirror the existing working image/video + mappings, desktop id confirmed from the realised package. Runtime GIO + resolution (double-click an mp3 → Amberol) is a V3 session check (queued). + +## 2026-07-07 — Capture-to-file keybinds (iteration #59, item 38) +- **Task:** BACKLOG item 38 — direct keybinds for region→file and + screen→file (the Capture *menu* already had both "→ file" rows). +- **Fix:** `modules/home/keybinds.nix` — added `SHIFT+Print` (region→file) + and `CTRL+Print` (screen→file) alongside the existing bare `Print` + (region→clipboard). Each saves a timestamped PNG under + ~/Pictures/Screenshots and toasts the path — same grim/slurp plumbing + + dir the menu's "→ file" rows use. Single-source, so the SUPER+? + cheatsheet (rofi.nix reads the same list) gets both rows automatically. +- **Verified:** V0 green. `nix eval` of the rendered binds confirms + `$HOME`/`$f`/`$(...)` pass through literally to the shell (no Nix + interpolation). Actual key-firing + file landing is a V3 check (queued). + +## 2026-07-07 — rofi search mis-ranks + power-profile icons (iteration #58, items 31 & 39) +- **#31 (rofi search broken):** Bernardo ran the live A/B — `-sorting-method + normal` and `-matching normal` both fixed "steam"→Steam; `-no-sort` did + not. So the culprit is rofi 2.0.0's **fzf sorter** paired with fuzzy + matching (it ranked ghostty above Steam), not case-sensitivity. Fix: + `programs.rofi.extraConfig.sorting-method` `"fzf"` → `"normal"`, keeping + `matching="fuzzy"` (forgiveness) + `case-sensitive=false`. Also closes + Bernardo's batch item 1 (case-insensitive — already false, now ranks + right too). +- **#39 (power-profile icons):** the picker listed profiles as bare text. + Now emits `row "$p" battery-profile-

` using Papirus' **colored** + `battery-profile-{performance,balanced,powersave}` family (the + `-symbolic` variants are #444 grey → invisible on dark themes; verified + the colored ones carry real fills). Returned row text stays the bare + profile name, so `powerprofilesctl set` is unaffected; added `-show-icons`. +- **Verified:** V0 `nix flake check --no-build` green for both. #31's fix + is the exact lever Bernardo confirmed live. #39's icon *rendering* is a + V3 visual check (Papirus lookup can't be eyeballed headlessly) — queued. + +## 2026-07-07 — Display menu "Back" doesn't return (iteration #57, item 40) +- **Task:** BACKLOG item 40 — in System › Display the Back row didn't + return on a single-monitor machine. +- **Root cause:** the mode picker's Back did `exec "$0" display`. On a + single-monitor laptop with no display profiles, the output chooser is + skipped, so re-running `display` drops straight back into the *same* + mode picker — an apparent no-op. (Multi-monitor works: `display` + re-shows the chooser, a genuine previous level.) +- **Fix:** `modules/home/rofi.nix` display handler now tracks `modeBack` — + `display` when the chooser was shown, else `system`. Mode-picker Back + does `exec "$0" "$modeBack"`, so single-monitor Back leaves Display for + System. +- **Verified:** V0 `nix flake check --no-build` green. Logic is + deterministic; the single- vs multi-monitor branch is the whole fix. + +## 2026-07-07 — Menu "Back" double arrows (iteration #56, item 34) +- **Task:** BACKLOG item 34 — every submenu's `↩ Back` row drew two + arrows: the `↩` glyph in the label AND a themed `go-previous` icon. +- **Fix:** `back()` in `modules/home/rofi.nix` was `row "$BACK" + go-previous` (label + icon). Changed to `printf '%s\n' "$BACK"` — now + identical to what the plain pick-lists already append, so every Back row + shows a single `↩` glyph. Kept `BACK="↩ Back"` as the universal + label/match, so no case-branch comparison changed (no risk of a Back + that fails to return — the item-40 failure mode). Collision-exact match + preserved by the glyph. +- **Verified:** V0 `nix flake check --no-build` green (evaluates + homeConfigurations). Single-arrow result is deterministic from the code; + visual confirmation on hardware is nice-to-have, not load-bearing. + +## 2026-07-07 — Battery charge-limit quick toggle (iteration #55) +- **Task:** BACKLOG PROPOSED (promoted by Bernardo) — a toggle for the + battery charge threshold. +- **Did:** Replaced the control-center "Battery Limit" raw number-prompt + with a preset toggle picker (Off / 80% recommended / 90% / 60% / + Custom…) writing `settings.power.batteryChargeLimit`. Kept it in the + control-center — that's where the baked, rebuild-valued System Toggles + live (rofi `nomarchy-menu` is the *instant* surface; the CC holds + config values); it's reachable from the menu via System › Control + Center. Base machinery (sysfs oneshot + AC-replug udev in power.nix) + untouched. +- **Verified:** V0 (`bash -n`; `nix flake check --no-build`, green) + V1 + (nomarchy-control-center package builds → the writeShellApplication + shellcheck gate passes). +- **Pending:** instant-effect (no rebuild) refiled in PROPOSED as + `[blocked:hw]` — needs a udev-writable sysfs node, confirmable only on + a laptop with the charge-threshold control. Light session spot-check + queued in HARDWARE-QUEUE. +- **Next suggestion:** item 29 is `[human]`/`[blocked:hw]`; remaining is + item 28 slices or a QA sweep. + +## 2026-07-07 — Waybar overlaps fullscreen video (iteration #54) +- **Task:** BACKLOG item 30 — bar drawn on top of a fullscreen YouTube video. +- **Did:** `layer: top` → `bottom` in the generated `waybar.nix` and the + four whole-swap jsoncs (boreal, summer-night, summer-day, + executive-slate — parity rule). On Hyprland the top layer renders above + even real-fullscreen surfaces; bottom lets the fullscreen window cover + the bar while the exclusive zone still reserves its space in normal + tiling. Rationale comment in waybar.nix; data jsoncs left comment-free + (they carry none). Also shipped this session (pre-loop, user-driven): + battery discharging-icon gap widened (f848e73). +- **Verified:** V0 (`nix flake check --no-build`, green) + V1 + (`homeConfigurations.nomarchy.activationPackage` builds; the built + `.config/waybar/config` shows `"layer": "bottom"`). +- **Pending:** V3 queued in HARDWARE-QUEUE — fullscreen video covers the + bar, bar returns on exit, floating-overlap trade-off. Can't be + eyeballed headlessly here. +- **Next suggestion:** item 29 is `[human]`/`[blocked:hw]`; remaining + work is item 28 (theme UI) slices or a QA sweep. + +## 2026-07-06 — menu parity for downstream-flake options (iteration #51, slice 1) +- **Task:** PROPOSED item — ensure `nomarchy.*` options have a menu toggle where appropriate. +- **Did:** Audited options and implemented the first slice of missing toggles in the TUI Control Center (`Updates`, `Battery Limit`, `Bluetooth`, `Printing`). Wired `modules/home/options.nix` and `modules/nixos/options.nix` to use `config.nomarchy.settings.*` as defaults. +- **Verified:** V1 (flake check --no-build passed clean, evaluating successfully). +- **Pending:** user visual review and testing. +- **Next suggestion:** triage other PROPOSED items. + +## 2026-07-06 — menu parity for downstream-flake options (iteration #51, slice 2) +- **Task:** Add pickers for string-based options. +- **Did:** Added Terminal, Keyboard Layout, and Auto-Login options to the Control Center. Wired them to the Nix options via `config.nomarchy.settings.*`. +- **Verified:** V1 (flake check --no-build). + +## 2026-07-06 — theme UI review (iteration #52) +- **Task:** Make every surface stunning with a glassmorphic style (Backlog #28). +- **Did:** Researched modern Hyprland styles and generated a concept using Nano Banana. Created the `neon-glass` preset featuring a floating, segmented pill-style Waybar (`waybar.css`) and a glassmorphic Rofi launcher (`rofi.rasi`). Generated a matching 4K wallpaper. +- **Verified:** Contrast checker script passed (22 themes x 7 pairings green). Flake check passed cleanly. + +## 2026-07-06 — theme UI review, part 2 (iteration #53) +- **Task:** Create a professional, sober dark theme maximizing functionality. +- **Did:** Generated a minimalist geometric concept wallpaper. Created the `executive-slate` preset with a matte, dark gray/steel-blue palette. Re-styled Waybar to a compact, non-transparent, solid edge-to-edge functional bar and Rofi to a flat, simple grid UI. +- **Verified:** Contrast checker script passed (23 themes x 7 pairings green). Flake check passed cleanly. + +## 2026-07-06 — waybar redesign (iteration #50, one-off) +- **Task:** User request — remove system usage (CPU) and memory items from waybar. +- **Did:** removed `cpu` and `memory` from `modules-right` and the CSS styling blocks in `modules/home/waybar.nix`. +- **Verified:** V1 (flake check --no-build passed clean). +- **Pending:** user visual review. +- **Next suggestion:** triage PROPOSED. + +## 2026-07-06 — control center implementation (iteration #49, item 18) +- **Task:** BACKLOG NEXT#18 — implement TUI Control Center using `gum`. +- **Did:** created `nomarchy-control-center.sh` and packaged it in `pkgs/nomarchy-control-center`. Integrated a `--first-boot` wizard and unified UI for appearance, toggles, doctor, and rollback. Added the package to `flake.nix` and `modules/nixos/default.nix`. Added "Control Center" to the System menu in `rofi.nix`. +- **Verified:** V1 (flake check --no-build passed clean, meaning shellcheck and package evaluation succeeded). +- **Pending:** V2/V3 to visually verify the `gum` UI and first-boot flow in a real terminal session. +- **Next suggestion:** watch lock-bump CI (item 14) or triage PROPOSED. + +## 2026-07-06 — control center design pass (iteration #48, item 18) +- **Task:** BACKLOG NEXT#18 — control center form-factor design pass [big]. +- **Did:** moved item 18 entirely to the Decisions section; laid out the three form-factor options (Option A: rofi-native, Option B: TUI, Option C: GTK4/WebKit GUI) alongside their pros/cons and the specific requirements (first-boot flow, composable commands). +- **Verified:** V0 (docs-only); `nix flake check --no-build` passed pre-commit. +- **Pending:** the `[human]` decision on the form-factor. +- **Next suggestion:** #14 (watch lock-bump CI) or #20 KVM runner [human]. If none actionable, triage PROPOSED or do a QA sweep. + +## 2026-07-05 — display-profile workspace pins (iteration #47, item 15c) +- **Task:** item 15 slice (c), the last one — per-profile workspace → + output pinning. Item 15 deleted (a/b/c all shipped; V3s queued). +- **Did:** a profile is now `{ monitors; workspaces = { "1" = "DP-3"; }; }` + (bare list still accepted — either-type + normalization in + hyprland.nix; coercedTo refuses list-of-submodule sources). Pins bake + as Hyprland `workspace` rules and apply instantly on profile switch + (`hyprctl keyword workspace` + `dispatch moveworkspacetomonitor`). + monitor-rules.nix gained pure `workspaceRule`; template + README + updated. +- **Verified:** V1 flake check green (incl. the extended + checks.display-profiles assert) + scratch-downstream eval: baked + settings render both rule sets, the generated applier carries the + keyword+dispatch pairs (bash -n OK), match/list still correct with + mixed profile shapes. +- **Pending:** V3 queued (workspace-pins entry in HARDWARE-QUEUE, rides + the slice-a dock test). Stale-pin note documented in the applier. +- **Next suggestion:** backlog is now human/event-gated (28 taste + calls, 14 Monday, 18 Decision, 20 KVM) — idle the loop or triage + PROPOSED. + +## 2026-07-05 — icon-weight A/B for the human call (iteration #46, item 28) +- **Task:** item 28's last `[human]` blocker — make the color-vs- + symbolic menu-icon decision cheap. +- **Did:** captured the lumon root menu with `-symbolic` icon names + (temporary uncommitted rofi.nix edit, reverted after) and published + a side-by-side artifact (link in the BACKLOG bullet). Findings: + symbolic matches the bar's weight, but Papirus symbolic coverage + has holes and rofi tints symbolics fixed gray — B needs an + audit+recolor slice; A (color) is the free default. +- **Verified:** V2 for the experiment itself (both variants VM- + captured); no repo code changed — tree is clean apart from agent/. +- **Pending:** Bernardo's call on the artifact. Item 28 is now fully + agent-side done: all remaining bullets are `[human]`/hardware. +- **Next suggestion:** general loop — item 15 slice (c) (per-profile + workspaces) is the topmost actionable code item; 14 fires Monday. + +## 2026-07-05 — purge stub waybar.css whole-swaps (iteration #45, item 28c/d) +- **Task:** item 28 — lumon vibe capture exposed four themes shipping + an unstyled default bar. +- **Did:** deleted the stub waybar.css in catppuccin/lumon/nord/ + retro-82 (2–14 lines of color defines from the f211ef0 rewrite + import; a theme waybar.css replaces the WHOLE generated stylesheet, + so the stubs nuked all bar styling). The four themes now get the + generated bar in their own palette; their full rofi.rasi designs + kept. Whole-swap bars are now only summer-day/night, both authored. +- **Verified:** V2 — lumon re-capture shows the generated styling + (workspace pill, padded right cluster, no edge clipping); V1 flake + check green. catppuccin/nord/retro-82 share the mechanism, not + individually captured. +- **Pending:** HARDWARE-QUEUE eyeballs; the `[human]` icon-weight call. +- **Next suggestion:** item 28 is agent-side done pending Bernardo's + taste calls — park it; take the next actionable BACKLOG item. + +## 2026-07-05 — capture harness paints wallpapers (iteration #44, item 28c) +- **Task:** item 28c harness gap — captures rendered a bare base-color + backdrop because the guest had no `~/.nomarchy` runtime state. +- **Did:** tools/theme-shot.nix now seeds theme-state.json via tmpfiles + at boot (before greetd), so the session's exec-once + `nomarchy-theme-sync wallpaper` paints naturally; dropped the + testScript seed+manual-repaint (too late for exec-once; manual runs + also lack WAYLAND_DISPLAY, which awww's client needs to find its + socket). +- **Verified:** V2 — summer-night capture shows wallpaper + cream + inverted bar + menu + placeholder dim, all coherent. V1 flake check + green. +- **Pending:** identity-theme vibe capture (lumon/retro-82); the + `[human]` icon-weight call; HARDWARE-QUEUE eyeballs. +- **Next suggestion:** item 28c identity capture, or park 28 for + Bernardo's taste calls. + +## 2026-07-05 — clock zone tooltip (iteration #43, LATER item) +- **Task:** LATER › auto-timezone Waybar tooltip. Bullet deleted. +- **Did:** the clock tooltip's first line is now the zone — + `{:%Z (UTC%z)}` above the calendar — in the generated bar AND both + summer whole-swaps (parity). Under auto-timezone the existing + tz-watch SIGUSR2 reload keeps it current, so it shows the detected + zone live. (%Z abbreviation, not the IANA name — strftime has no + code for that; good enough for an "(optional)" nicety.) +- **Verified:** V0; V1 — rendered generated config carries the new + tooltip-format; both summer jsonc parse (jq) with %Z present. + Hover render → V3 queued. +- **Pending:** V3. LATER now holds only deferred/big/hw/human items. +- **Next suggestion:** a QA sweep, or stop-and-wait — the queue is + effectively drained for unattended work; meaty LATER items + (wallpapers split is *deferred by decision*, installer round 2 + [big]) deserve Bernardo's go-ahead or a fresh session. + +## 2026-07-05 — doctor bar tripwire (iteration #42, LATER item) +- **Task:** LATER › doctor Waybar warning. Bullet deleted. +- **Did:** nomarchy-doctor-status (named bin, PATH): silent exit 0 + while the doctor is healthy or absent (self-gate), else JSON with + the first ✖ lines jq-escaped into the tooltip, in @bad; click = + nomarchy-menu doctor (the sheet). custom/doctor added to the + generated bar (after battery, interval 300 — the check is real + work, this is a tripwire not a monitor) + CSS, AND both summer + whole-swaps (module list, jsonc block, CSS membership + @red rule) + per the parity rule. +- **Verified:** V0; V1 — template-home builds, status bash -n, config + + CSS each carry the module, both summer jsonc still parse (jq) + with the block; FUNCTIONAL — self-gate silent, and a shimmed + failing doctor produces valid JSON (jq-verified: class bad, ✖ + lines in tooltip). Live render → V3 queued. +- **Pending:** V3. LATER remaining: wallpapers-artifact split + (deferred by decision), installer round 2 [big], boot-from- + snapshot, night-light geo, icon overrides, MIPI camera [hw], + auto-timezone tooltip, VPN richer display, release bump [human]. +- **Next suggestion:** auto-timezone Waybar tooltip (small) — or a + QA sweep; the meatier LATER items deserve a fresh session. + +## 2026-07-05 — OCR screenshot-to-text (iteration #41, LATER item) +- **Task:** LATER › OCR (NEXT is fully human/event-gated, so LATER is + in play per LOOP.md). Bullet deleted. +- **Did:** Capture › "OCR region → clipboard" (self-gated on + tesseract): slurp (Esc = silent cancel, the recording pattern) → + grim → tesseract stdin/stdout → wl-copy, whitespace-only results + toast instead of clobbering the clipboard; success toasts the word + count. tesseract ships eng-only via rofi.nix home.packages + (override documented — unscoped it drags every language's + traineddata, real cost on a compile-from-source distro). +- **Verified:** V0; V1 — template-home builds, menu bash -n, gated + row + case present; FUNCTIONAL — the profile's own tesseract read a + rendered test image back verbatim ("Nomarchy OCR smoke test 42"), + so the OCR half of the pipe is proven; grim/slurp half needs a + session → V3 queued. +- **Pending:** V3. +- **Next suggestion:** LATER: doctor Waybar warning (self-gating bar + indicator fed by nomarchy-doctor) or auto-timezone tooltip; both + small. + +## 2026-07-05 — Look & Feel menu category (iteration #40, item 19) +- **Task:** BACKLOG NEXT#19 — its ≥3-entries gate is met (Theme, Next + wallpaper, Night light). Item deleted. +- **Did:** root's Theme row → "Look & Feel" (pango-escaped — + -markup-rows menus treat & as an entity; matched on *Look*); new + lookfeel mode: Theme (SUPER+T hint), Next wallpaper (SUPER+SHIFT+T + hint via the new generalized hintForAction), Night light row moved + from System (System thins by one). Root stays six. menuHint is now + sugar over hintForAction. +- **Verified:** V0; V1 — menu bash -n, lookfeel mode + escaped label + + night light relocated (4 occurrences, all in lookfeel/nightlight + paths); VISUAL — tokyo-night capture: root renders "Look & Feel" + correctly with six rows and intact hints. Submenu flow → V3 + queued. +- **Pending:** V3. NEXT now: 28 (Bernardo-gated) · 14 (Monday watch) + · 18 [big, Decision-gated] · 20 [human] — the LATER tier or QA + sweeps are next for the loop. +- **Next suggestion:** LATER's OCR screenshot-to-text (two-row + Capture addition, self-contained) — or a QA sweep. + +## 2026-07-05 — launch-or-focus binds (iteration #39, item 17) +- **Task:** BACKLOG NEXT#17 (the UI review is Bernardo-gated; back to + the queue head). Item deleted. +- **Did:** nomarchy.launchOrFocus (listOf submodule: key/mods/class/ + command/desc; opt-in, template example) → hyprland.nix generates + binds through a shared nomarchy-focus-or-launch script (jq + case-insensitive class match → focuswindow "(?i)" regex, else + launch; uninstalled command → toast, so binds survive suite edits); + rofi.nix renders the same entries into the SUPER+? cheatsheet via + prettyKeys (single source). README row. +- **Verified:** V0; V1 — scratch downstream with 2 entries: both + binds rendered (default command = lowercased class; custom + mods/command/desc honored), tool bash -n + usage exit 64, + cheatsheet rows present. Focus behaviour needs a session → V3 + queued. +- **Pending:** V3. Queue after this: #18 control center [big, needs + the form-factor Decision], #19 Look&Feel menu (gated on ≥3 + entries), #20 [human], LATER tier. +- **Next suggestion:** a QA sweep (#18/#19/#20 are blocked-ish) — or + item 19's precondition check (does Look & Feel earn 3 entries + yet?). + +## 2026-07-05 — placeholder dim + latte review + backdrop probe (iteration #38, item 28c) +- **Task:** item 28 slice (c) — the summer-rasi quickie + the light- + theme capture. +- **Did:** both summer rasi: placeholder-color @bg0 → bg0 at 60% + alpha (#…99), so prompt and placeholder no longer read as one + phrase. Captured catppuccin-latte at 1080p: the generated menu is + COHERENT on light themes (zebra rows on the 28b-retuned surface, + hints dimmed) — closes the audit's "dark terminal on latte" + concern as a stale preview. Backdrop suspicion probed with a + dedicated VM (hyprctl getoption): misc:background_color IS set + (0xFFEFF1F5) — awww's empty-state layer occludes it in the + wallpaper-less VM; on hardware the fix covers the startup gap as + intended. +- **Verified:** rasi edits grep-verified (rasi parses at rofi + runtime; visual rides the next summer capture / V3); latte menu + capture reviewed; probe output above. +- **Pending:** item 28 remaining: icon-weight `[human]`, hardware + eyeballs (queued), identity-theme capture (optional), slice (d) + needs nothing (parity verified). +- **Next suggestion:** item 28 is now Bernardo-gated (taste calls + + V3); the loop should return to the general BACKLOG head (item 14 + watch / item 17 launch-or-focus). + +## 2026-07-05 — rofi keybind hints (iteration #37, item 28c) +- **Task:** item 28 slice (c) — the hint column, the audit's biggest + remaining UX item. +- **Did:** rofi.nix: menuHint builds a dimmed pango span + (size small, alpha 55%) from keybinds.nix via the cheatsheet's + prettyKeys — single source, hints can't drift from the live binds; + applied to root (Apps/Theme/Power/Keybindings; Apps special-cased, + it's rofi drun not a menu module), Tools (all eight leaves), System + (Network/Bluetooth/DND); those three menus now pass -markup-rows. + Contains-glob matching and the exact "↩ Back" match are unaffected + (labels keep their prefix; Back stays markup-free). +- **Verified:** V0; V1 rendered menu bash -n + hint spans present + + 3× -markup-rows; VISUAL — tokyo-night capture: hints render dimmed + and subordinate on normal AND selected (accent) rows; Tools/System + rows correctly bare. +- **Pending:** slice-c remainder: summer-rasi placeholder dim, + latte/identity captures, icon-weight `[human]`. +- **Next suggestion:** the placeholder-dim quickie + a latte capture + in one tick; then item 28 pauses for Bernardo's icon-weight call + and the hardware eyeballs. + +## 2026-07-05 — bar group rhythm + @muted adoption (iteration #36, item 28c) +- **Task:** item 28 slice (c) — the two queued bar polish items. +- **Did:** waybar.nix generated CSS: margin-left 14px on the three + right-cluster group heads (pulseaudio/powerprofile/battery — media + · toggles · status; hidden heads degrade to uniform spacing); + workspaces buttons, dnd bell, and muted volume moved from + alpha(@text,.5) tints to the semantic @muted role — safe since 28b + floor-gates muted/base ≥ 2.0 (the old comment warning against + @muted documented the pre-28b world; rewritten to state the new + invariant). Whole-swap bars untouched (authored identity). +- **Verified:** V0; V1 rendered CSS has 4 @muted uses + the margin + rule; VISUAL — tokyo-night re-capture: themed backdrop confirmed + (navy base, not black), group gap visible before the volume group, + workspace pill legible. Cross-theme minima are guaranteed by the + gated checker rather than per-theme eyeballs. +- **Pending:** slice-c remainder: rofi hint column, summer-rasi + placeholder dim, icon-weight `[human]`, latte/identity captures. +- **Next suggestion:** rofi keybind-hint column (the biggest + remaining UX win), or the two-line placeholder nit as a quickie. + +## 2026-07-05 — summer-day capture review + themed backdrop (iteration #35, item 28c) +- **Task:** item 28 slice (c) — whole-swap capture review + the first + cheap polish fix. +- **Did:** captured summer-day at 1080p (harness, VM): the authored + slate menu + island-pill bar hold up with the 28b retunes — + initially misread as polarity soup, but the rasi whole-swap + deliberately mirrors the bar's slate strip: identity, keep (nit + filed: undimmed entry placeholder). The surviving cross-theme fix: + Hyprland `misc:background_color` = theme base (was compositor + black — a glitch-looking flash behind missing/slow wallpaper, + glaring on light themes). Punch list updated. +- **Verified:** V0; V1 — template-home rebuilt, rendered hyprland.conf + carries `background_color=rgb(1a1b26)` (theme base). Backdrop + visible confirmation rides the next capture run / V3. +- **Pending:** captures for latte + one identity theme; polish items + queued: bar group margins, @muted adoption, rofi hint column, icon + weight `[human]`. +- **Next suggestion:** bar polish slice (group margins + @muted) — + code-level, verifiable by build + re-capture. + +## 2026-07-05 — 1080p capture harness + first slice-c review (iteration #34, item 28c) +- **Task:** item 28 slice (c) prep — full-res captures for the + per-surface polish pass. +- **Did:** tools/theme-shot.nix (maintainer tool, THEME env-driven): + full themed desktop headless at 1920x1080 via the memory recipe + + two fixes over the spike — home-manager.useGlobalPkgs (HM submodule + otherwise misses the overlay → 'nomarchy-theme-sync missing') and a + menu.png shot (hyprctl dispatch exec nomarchy-menu). Proven on + tokyo-night; both shots read. Findings → item 28 punch list: rofi + root width vs empty space (→ keybind-hint column idea), icon-weight + mix (color Papirus vs mono bar) `[human]`, bar group margins + confirmed, alpha-tint dim vs @muted role. Whole-swap module parity + verified textually (no drift; cpu/memory omission is identity). +- **Verified:** harness runs end-to-end (exit 0, screenshots read); + eval from tools/ with relative flake path OK. Wallpaper still + doesn't paint (theme-sync needs ~/.nomarchy in the guest — fix + noted in the header for the next run). +- **Pending:** capture summer-day/night (whole-swaps) + latte; + then the polish slices; icon-weight call is Bernardo's. +- **Next suggestion:** run the retuned-theme captures + start the + cheap polish items (group margins, @muted adoption). + +## 2026-07-05 — P2 floors retuned + gated (iteration #33, item 28b done) +- **Task:** item 28 slice (b) remainder — the P2 contrast floors. +- **Did:** 18 hex retunes across 9 themes: muted raised on lumon/ + white/retro-82/everforest (OKLCH hue-preserving minimal solve to + ≥2.05) + gruvbox→bg4 #7c6f64, nord→#616e88 (community standard), + latte→overlay1 #8c8fa1; text-on-surface via surface nudges + (ristretto darker, latte lighter); accentAlt darkened (latte, + summer-day); warn darkened on the light themes (latte, summer-day, + rose-pine-dawn) and flexoki-light's four statuses moved to the + canonical 600 series. check-theme-contrast.py gates all four + adopted floors (muted ≥2, text/surface ≥4.5, accentAlt ≥3, warn + ≥2.5 — warn is the battery tint); good/bad stay audit-only + (identity), so no exemption mechanism was needed. Slice (b) done. +- **Verified:** V0 + checks.theme-contrast GREEN (21×7); re-audit: + contrast findings 20→1 (miasma bad = identity); flake check green. + Rendering is V3 → queued (P2 eyeball entry). +- **Pending:** slices (c) per-surface polish (needs 1080p captures) + and (d) whole-swap bar parity; ansi[] conventions decision still + open (audit notes). +- **Next suggestion:** slice (c) prep — 1080p captures via the VM + recipe, or hardware captures if Bernardo prefers eyeballing first. + +## 2026-07-05 — P1 palette retunes + gated subtext floor (iteration #32, item 28b) +- **Task:** item 28 slice (b), P1s from the #31 audit. +- **Did:** summer-day: surface #5c6a72(=text!)→#efe7d0 raised cream, + subtext #fdf6e3(=base!)→#6e828a slate; flexoki-light to canonical + Flexoki tones: surface #E6E4D9(ui), overlay #B7B5AC, subtext + #6F6E69(tx-2), muted #878580(base-500); miasma surface #000→#2e2e2e; + kanagawa surface #090618→#2a2a37 (upstream sumiInk4 — old value was + sumiInk0, darker than bg). check-theme-contrast.py gains the adopted + `subtext/base ≥ 3` pairing (the item-25/27 trap, now impossible). + Model note recorded: `overlay` is a mid-tone role, not a bg tier. +- **Verified:** V0 + checks.theme-contrast GREEN (21×3 pairings); + re-audit: all four P1 findings gone (kanagawa now CVD-only findings); + V1 — scratch downstream with summer-day ACTIVE builds, new surface + hex present in rendered ghostty config. Looks are V3 → queued + (four-theme eyeball). +- **Pending:** 28b remainder: P2 floors (muted ≥2, warn ≥2.5, + text-on-surface ≥4.5, accentAlt ≥3) + exemption-list mechanism. +- **Next suggestion:** 28b P2s (7 muted retunes + 4 warn + 2 + text-on-surface + 2 accentAlt, then gate with exemptions). + +## 2026-07-05 — theme design audit (iteration #31, item 28 slice a) +- **Task:** BACKLOG NEXT#28 slice (a) — the design-theory audit + (human-requested; Bernardo asked to run it now). +- **Did:** tools/audit-theme-design.py (new, report-only): OKLCH + lightness architecture, extended WCAG pairs, status-hue families, + accent harmony, Machado protan/deutan simulation, ANSI slot + semantics — over all 21 palettes; plus a visual pass over 4 + representative preview.png captures. Findings curated + identity-aware into a P1–P3 punch list appended to item 28: 2 + invisible-subtext palettes (summer-day, flexoki-light), 2 inverted + bg stacks (miasma, kanagawa), 7 muted-floor misses, warn-barely- + reads ×4, a systemic "status is never color-only" rule (CVD + collapse is inherent, glyph redundancy is the fix), explicit + identity exemptions, light-theme ANSI convention decision, bar + right-cluster grouping rhythm, latte dark-terminal verify. +- **Verified:** V0 (py_compile; report tool, not wired into checks). + Analysis is palette-math + thumbnail previews; per-surface + spacing/type audit needs 1080p captures (slice c prep). +- **Pending:** slice (b) — palette retunes + checker extension w/ + exemption list; needs Bernardo's eye on the P1 retunes (visual + taste calls). Artwork needs none so far — no image-model ask yet. +- **Next suggestion:** slice (b) P1s (summer-day/flexoki subtext+ + surface, miasma/kanagawa stacks) — small hex edits, big payoff. + +## 2026-07-05 — greeter theming from the JSON (iteration #30, item 16) +- **Task:** BACKLOG NEXT#16 — tuigreet themed from theme-state.json at + system rebuild (the Plymouth model). Item deleted. +- **Did:** new modules/nixos/greeter.nix owns the greetd block (moved + out of default.nix, one concern one file) + the theming: (1) + console.colors = the theme's ansi[] (mkDefault; lands as + vt.default_* kernel params — also themes raw ttys + LUKS prompt); + (2) tuigreet --theme on NAMED slots (its parser is ratatui + Color::from_str, verified in source; names hit the standard indexes + the retinted VT palette now serves). Sparse state w/o ansi → skips + cleanly. +- **Verified:** V0; V1 — template-system toplevel: baked greetd.toml + carries the full --theme spec, kernel-params carry all three + vt.default_{red,grn,blu}. A checks.greeter VM test was attempted and + DROPPED: tuigreet dies under runNixOSTest even bare (harness limit, + not our flag — bisected; nixpkgs tests use agreety) → MEMORY.md. +- **Pending:** V3 queued (rendering + theme-follows-rebuild). +- **Next suggestion:** #17 launch-or-focus, or the new #28 UI-review + audit slice once Bernardo confirms its placement. + +## 2026-07-05 — display profiles auto-switch (iteration #29, item 15b) +- **Task:** BACKLOG NEXT#15 slice b — opt-in hotplug auto-switch. +- **Did:** nomarchy-display-profile gains `match ` (exact set + → else unambiguous largest subset → ties/none = exit 1); + nomarchy-display-profile-watch polls `hyprctl monitors all` every 3s + (exec-once, the keyboard-watcher pattern — session units race + Hyprland IPC), reacts only to CHANGES, reads settings.displayProfileAuto + LIVE each change (instant menu toggle, no rebuild) and applies via + the tool, so auto-picks persist concrete profiles that rebuilds + bake. Chose a separate boolean over an "auto" pseudo-profile for + exactly that bake semantics. Menu: Auto-switch (on/off) row in + Profiles. Template + README notes. +- **Verified:** V0; V1 — scratch 4-profile downstream builds; both + scripts bash -n; match matrix exercised: exact ×3, unambiguous + subset, tie → none, no-fit → none; watcher exec-once present; menu + renders with the row. Real hotplug needs a session+dock → V3 (rides + the slice-a queue entry). +- **Pending:** V3. Slice c (workspace binding) remains, tagged + optional. +- **Next suggestion:** #16 greeter theming — item 15's remaining + slice c is optional; a fresh item keeps variety. + +## 2026-07-05 — display profiles slice a (iteration #28, item 15a) +- **Task:** BACKLOG NEXT#15 slice a, per the #27 design: option + + overlay + applier + menu + template example. +- **Did:** nomarchy.displayProfiles (attrsOf (listOf monitorType)); + monitor logic extracted to pure modules/home/monitor-rules.nix + (rule renderer + 3-layer resolve: profile replaces whole-by-name → + resolution picks field-level with the disable-guard → wildcard), + hyprland.nix consumes it and gains nomarchy-display-profile + (list/active/apply/base; apply = baked hyprctl rules + state write + --no-switch, base = clear + hyprctl reload) on PATH only when + profiles exist; menu: Display gains a gated "Profiles ›" row + new + display-profile mode (●/○ active mark, Base layout row); template + example + README row. checks.display-profiles unit-tests resolve + (incl. junk-state degradation) — the first HM-eval fixture attempt + hit "path not valid" (eval store won't realise writeText/toFile + under flake check), hence the pure-function extraction. +- **Verified:** V0 (check's asserts force at eval); V1 — scratch + downstream through lib.mkFlake builds: tool bash -n + correct baked + rules (disable, vrr, floats), hyprland.conf shows the exact overlay + (wildcard + kept base + disabled panel + profile output), menu bash + -n, list/usage smoke. Live switching needs a session+dock → V3. +- **Pending:** V3 queued. Slices b (hotplug auto-switch) and c + (workspace binding) remain in the item. +- **Next suggestion:** #16 greeter theming, or 15b if momentum on + displays is preferred. + +## 2026-07-05 — display-profiles design pass (iteration #27, item 15, no code) +- **Task:** BACKLOG NEXT#15 — the entry itself demands "design first, + write the plan into this entry before coding". Backlog-only iteration. +- **Did:** wrote the plan into item 15: `nomarchy.displayProfiles = + attrsOf (listOf monitorType)` (reuse the existing submodule), + `settings.displayProfile` state via the night-light pattern (instant + hyprctl apply + bake on rebuild), whole-entry-by-name overlay with a + disable-guard against stale resolution picks, self-gated menu row, + three slices (option+overlay+menu / opt-in hotplug auto-switch / + per-profile workspace binding), kanshi stays a non-goal. +- **Verified:** V0 (docs-only; flake untouched). +- **Pending:** nothing — 15a is now directly codeable. +- **Next suggestion:** 15a (option + overlay + menu picker), or #16 + greeter theming if a fresh session prefers a self-contained item. + +## 2026-07-05 — scheduled lock-bump workflow (iteration #26, item 14 b+c) +- **Task:** BACKLOG NEXT#14 `[big]` — split: (a) checks-on-push shipped + earlier; this ships (b) the weekly bump + (c) the fast-lane as + workflow_dispatch on the same job. +- **Did:** .gitea/workflows/bump.yml — Mon 05:17 UTC cron + dispatch: + nix flake update (pinned branches by construction) → gate `nix flake + check --no-build` (py_compile/bash -n skipped: they don't read the + lock) → commit --only flake.lock with the update's change list as + body → push main (triggers check.yml as second net). Red gate/push + race = failed run, nothing lands. Container recipe from check.yml. +- **Verified:** V0 (yq parse + bash -n each run block); logic simulated + end-to-end in a scratch clone with a REAL nix flake update + bare + remote: changed/unchanged branches, bystander file stays uncommitted, + push lands; today's bumped lock evals green (Monday's baseline). + Gitea 1.25.4 confirmed (schedule supported). The real cron pickup + + token push are NOT confirmable from here → item 14 slimmed to + "confirm first run". +- **Pending:** watch the first scheduled/dispatched bump run. +- **Next suggestion:** #16 greeter theming (next codeable item; #15 + needs its design pass written first). + +## 2026-07-05 — color picker (iteration #25, item 13 final slice → item done) +- **Task:** BACKLOG NEXT#13 last slice — hyprpicker → clipboard from + the menu + a direct bind. Item 13 complete; deleted from BACKLOG. +- **Did:** hyprpicker into systemPackages (next to grim/slurp); rofi.nix + `colorpicker` mode (hyprpicker → wl-copy → hex-in-toast, Esc = silent + cancel) + self-gated Tools › "Color picker" row + usage line; + keybinds.nix SUPER+CTRL+P row (feeds bind + cheatsheet). +- **Verified:** V0; V1 — template-home AND template-system build; + rendered nomarchy-menu bash -n OK with mode + gated row; cheatsheet + txt has the SUPER+CTRL+P row; hyprland conf has the bind; system + toplevel ships sw/bin/hyprpicker. Picking needs a compositor → V3. +- **Pending:** V3 queued (loupe → click → wl-paste round-trip). +- **Next suggestion:** #15 (display profiles) needs a design pass + written into the entry first; #16 (greeter theming) is the next + directly codeable item — or #14's bump workflow (eval-tier gate). + +## 2026-07-05 — low-battery notifications (iteration #24, item 13 slice) +- **Task:** BACKLOG NEXT#13 second slice — the bar colors the battery + at 25/10% but nothing *notified*. +- **Did:** pkgs/nomarchy-battery-notify (shellcheck-gated watcher: + polls the same sysfs the bar reads — type=Battery, scope≠Device — + one toast per downward crossing: normal@25, critical@10; re-armed by + charging; self-gates on battery presence → desktop no-op) + overlay. + HM unit in modules/home/battery-notify.nix behind + nomarchy.batteryNotify.enable (default on), graphical-session-bound, + libnotify on the unit PATH (notify-send resolved from PATH so the + check can shim it). README row. Chose the tiny watcher over + poweralertd: its UPower thresholds (20/5) wouldn't match the bar, + and it toasts every plug/unplug. +- **Verified:** V0; V1 — template-home builds, unit present; V2 GREEN — + new checks.battery-notify (test_power fake battery): self-gate exits + 0 without a battery, silence at 80%, exactly one low toast crossing + 25, one critical-urgency toast crossing 10, charging re-arms → a + second drain toasts again. +- **Pending:** V3 queued (real toast through swaync while draining). +- **Next suggestion:** item 13 final slice — hyprpicker color picker + (Tools › entry + SUPER+CTRL bind). + +## 2026-07-04 — caffeine/idle-inhibit toggle (iteration #23, item 13 slice) +- **Task:** BACKLOG NEXT#13 first slice — idle_inhibitor in the + generated bar (summer-night had it; reverse parity gap). +- **Did:** waybar.nix: idle_inhibitor module (󰾪/󰅶, tooltips, placed + after the ⏺), CSS membership + `.activated { color: @warn; }`. + summer-day: module + CSS mirrored from summer-night (same glyphs, + no tint — glyph change is the state signal there). keybinds.nix + extra row → cheatsheet mentions the bar click. +- **Verified:** V0; V1 — template-home built; rendered config has the + module second in modules-right with correct glyphs; CSS rules + present; cheatsheet row renders. Inhibit behaviour needs a session + → V3 queued (hold past the lock timeout). +- **Pending:** V3. Item 13 remaining slices: low-battery + notifications, color picker. +- **Next suggestion:** item 13 next slice (low-battery notifications, + gate on power.laptop) — or #14 lock-bump CI if variety is better. + +## 2026-07-04 — screen recording in Capture (iteration #22, item 12) +- **Task:** BACKLOG NEXT#12 — record region/screen from the menu, stop + from the bar. +- **Did:** modules/home/recording.nix: `nomarchy-record` + start/stop/active/status — wl-screenrec first, wf-recorder fallback + if it dies at startup (no-VAAPI case), SIGINT-finalized stop, + runtime pidfile, signal-8 bar pokes. Capture menu: 4 record rows that + self-swap to "■ Stop recording" while one runs. Waybar: self-gating + custom/recording (⏺ REC, @bad red), click = stop — the one stop + surface. Parity: both summer whole-swap bars got the module + CSS + (@red). DEVIATIONS: saves to ~/Videos/Recordings (an .mp4 in + Pictures/Screenshots felt wrong — same timestamp naming though); + audio = two extra explicit rows, not a toggle state. +- **Verified:** V0; V1 — template-home builds; rendered bar config has + the module (first in modules-right) + CSS rule; summer jsonc still + parse; nomarchy-record dry-run: status empty+exit 0 idle, active + exit 1, usage exit 2; both recorders in home-path. Real capture + needs a session → V3 (headless GL recipe can't confirm an encoder). +- **Pending:** V3 recording exercise (incl. VAAPI-vs-fallback check). +- **Next suggestion:** NEXT#13 niceties, one slice: idle-inhibit + (caffeine) toggle — reverse-parity gap already noted in the item. + +## 2026-07-04 — state-file validation & friendly errors (iteration #21, item 11) +- **Task:** BACKLOG NEXT#11 — hand-edited state must fail with field + + problem + fix, never a Nix stack. +- **Did:** (a) tool: validate_state() (appearance schema = hard errors; + settings.*/unknown keys = warnings), `validate` subcommand, + validate-BEFORE-write in write_state (invalid set/apply never touches + disk), JSON syntax errors now say line/column/fix. (b) theme.nix: + same schema at eval time on `parsed` (post-defaults, so sparse files + stay fine); throws a multi-line message naming each field, the got- + value, and the three fix paths. Doctor's JSON fix-text now points at + `validate`. +- **Verified:** V0; V1 — new checks.theme-sync-validate GREEN: good + template passes, trailing comma → line/col message, bad hex/type → + field named, unknown key → warning only, invalid `set` refused with + the file byte-identical (cmp). Negative eval of a broken scratch + downstream through mkFlake shows the friendly throw (both fields); + good-path template-home still builds. +- **Pending:** nothing — no hardware dependency. +- **Next suggestion:** NEXT#12 (screen recording) or #13 niceties + slice; #12 first (it reshapes the Capture submenu the OCR LATER item + waits on). + +## 2026-07-04 — nomarchy-doctor (iteration #20, item 10) +- **Task:** BACKLOG NEXT#10 — one-shot read-only health check. +- **Did:** pkgs/nomarchy-doctor (writeShellApplication → shellcheck- + gated; a package so the VM check runs on a minimal node): failed + system+user units (user bus self-skips), disk space on real + filesystems only (fstype allowlist skips tmpfs/9p → VM-safe; device + dedupe), state-file parses + git-tracked, flake dirty/behind + (warn-level), generation age via the profile SYMLINK mtime (store + paths are epoch-1), snapper timer when enabled. Every ✖ prints its + fix; exit 1 on any ✖. Wired: overlay, systemPackages, System › + Doctor menu row (terminal), README §5. +- **Verified:** V0; V1 package build (shellcheck) + real-hardware + smoke: correctly flagged a genuinely failed user unit on the dev box; + V2 GREEN — VM test: induced failed unit → exit 1 + names it + prints + fix; reset-failed → healthy exit 0. Menu rebuild bash -n ok. +- **Pending:** V3 menu-row check queued. Waybar-warning follow-up → + LATER. +- **Next suggestion:** NEXT#11 (state-file validation) — doctor's + "parses" check is its little sibling; 11 adds validate-before-write + + eval-time schema messages. + +## 2026-07-04 — System › Rollback menu (iteration #19, item 9b → item 9 done) +- **Task:** BACKLOG NEXT#9 half (b) — undo one menu away. +- **Did:** new `rollback` case in nomarchy-menu (System submenu row): + lists the last 10 `home-manager generations` (newest marked current), + picking one activates it in a terminal — HM's supported rollback, + reversible, so no typed-yes. System-level undo LINKS OUT by design: + Snapshots row → existing flow; "boot an older generation (how)" → + instruction notification pointing at docs/RECOVERY.md §3 (no + destructive reimplementation in rofi). RECOVERY §1 cross-references + the menu path. Item 9 complete → deleted. +- **Verified:** V0; V1 — template-home built, generated nomarchy-menu + bash -n clean, row-render + pick-parse logic run against a fixture + `generations` output (id/path extraction correct). V2 skipped + deliberately: the only novel logic is the parsing (fixture-covered); + the activate step is HM's own mechanism. V3 queued. +- **Pending:** V3 rollback-menu exercise on hardware. +- **Next suggestion:** NEXT#10 `nomarchy-doctor` (V2-able in a VM). + +## 2026-07-04 — sys-update/-rebuild what-changed diff (iteration #18, item 9a) +- **Task:** BACKLOG NEXT#9 half (a) — a human diff after system rebuilds. +- **Did:** both sys-update and sys-rebuild (twins stay twins) capture + /run/current-system before, and after the switch print + `nvd diff before after` (store-path-pinned ${"$"}{pkgs.nvd}, `|| true` + so it can't fail a succeeded rebuild; identical-path fast-path says + "no changes"). README §3 shortcut block notes it. +- **Verified:** V0; V1 — template-system toplevel rebuilt, generated + scripts extracted + bash -n, nvd smoke-run against the real closure + (correct output format). Runtime behaviour is a trivial shell path — + no VM needed. +- **Pending:** half (b) — the System → Rollback menu flow (desktop + generations picker; system → boot menu + snapshots pointer). +- **Next suggestion:** item 9b — it completes the item; V2 the + generation-activate path. + +## 2026-07-04 — viewers + mime defaults (iteration #17, item 8) +- **Task:** BACKLOG NEXT#8 — PDF/image viewers + xdg-mime defaults. +- **Did:** modules/home/viewers.nix (`nomarchy.viewers.enable`, default + on): programs.zathura (Stylix themes it — target added to stylix.nix's + explicit list, autoEnable=false bit us first try) + imv. + modules/home/mime.nix (`nomarchy.mime.enable`): mkDefault + xdg.mimeApps for pdf→zathura, image/*→imv, av→mpv, text→code, + dir→thunar, html/http(s)→firefox as INERT entries (browser Decision + untouched — absent .desktop entries are skipped by GIO). README rows + added (option-docs enforces them now). DEVIATION from the item text: + viewers are a module toggle, not template packages — zathura's + theming needs its HM module, which is exactly the "real config" + boundary of the toggle discipline. +- **Verified:** V0; V1 — template-home generation builds; asserted + rendered mimeapps.list (all associations), themed zathurarc + (Tokyo Night rgb), imv/zathura in home-path. V3 smoke queued. +- **Pending:** V3 open-a-file smoke; browser Decision now smaller + (inert entries ship; only "active in suite?" remains). +- **Next suggestion:** NEXT#9 (update & rollback UX) — or #10 doctor. + +## 2026-07-04 — docs slice (c): install story — item 6 COMPLETE (iteration #16) +- **Task:** BACKLOG NOW#6 slice (c), the last one — install/first-run + narrative end to end. +- **Did:** read README §2 (ISO/installer) → §3 (downstream) → template + README as one story; checked the facts en route: mkFlake's arg + surface == the §3 table (src/username/hardwareProfile/system, lib.nix), + disko really makes @snapshots + the hibernation swapfile, .#default / + .#me names consistent. One seam fixed: §3 showed `nix flake init -t` + but never handed off to the template README's six first-run steps — + connective paragraph added (+ "the installer does all of this"). + Item 6 done → deleted from BACKLOG (NOW now empty); ROADMAP § Full + docs review flipped to ✓ with the slice summary. +- **Verified:** V0 (docs-only) — flake check clean pre-commit. +- **Pending:** docs-site-vs-Markdown stays a Decision. +- **Next suggestion:** NEXT#8 (viewers + mime defaults) is the new queue + head; note its "Default browser" Decision dependency — the mime module + half can proceed with mkDefault degradation regardless. + +## 2026-07-04 — docs slice (d): recovery runbook (iteration #15, item 6d) +- **Task:** BACKLOG NOW#6 slice (d) — write the undo story. +- **Did:** docs/RECOVERY.md, symptom-ordered: (1) bad theme/HM change → + generations / apply-known-good / git-revert ~/.nomarchy; (2) desktop + won't start → Ctrl+Alt+F2, greetd + user journals, VM-GL and + preactivate-log pointers; (3) system change → systemd-boot older + generation (kept: 10) + make-it-stick via sys-rebuild; (4) files → + btrfs-assistant / sudo nomarchy-snapshots (undochange, root rollback, + typed-yes); (5) ISO + nixos-enter. Every command checked against the + tree (greetd cfg, configurationLimit, snapshots script actions, + helper names). Linked from README tree + §5. +- **Verified:** V0 (docs-only) — flake check clean pre-commit. +- **Pending:** slice (c) install-story read-through — the last of item 6. +- **Next suggestion:** finish item 6 with slice (c). + +## 2026-07-04 — docs slice (b): drift pass (iteration #14, item 6b) +- **Task:** BACKLOG NOW#6 slice (b) — OVERRIDES.md / TESTING.md / + templates/downstream/README.md vs the live tree. +- **Did:** spot-checked every factual claim: mkDefault sites (hyprland + behaviour knobs, ghostty paddings, waybar mainBar/style), bind-list + normal priority, `set ui.gapsOut` CLI key, awww(-daemon) naming, + 21 presets, tokyo-night's 4 wallpapers, summer-night's light @fg bar, + live `,highres` mkForce, .gitea/workflows/check.yml, QEMU gl flags, + template `.#me` + ~/.nomarchy/NOMARCHY_PATH. All accurate. Fixed the + two real gaps: OVERRIDES §2 predated `nomarchy.monitors` (now routes + there + quick-ref row); TESTING §1 lacked the shell-syntax check §1b + claims CI runs (added `bash -n`). +- **Verified:** V0 (docs-only; flake untouched — check run pre-commit). +- **Pending:** slices (c) install story, (d) recovery runbook. +- **Next suggestion:** item 6 slice (c) or (d) — (d) is self-contained + and ships user value (the runbook). + +## 2026-07-04 — docs slice (a): option-surface reconcile (iteration #13, item 6a) +- **Task:** BACKLOG NOW#6, first slice — README tables vs live options. +- **Did:** split item 6 into slices (a–d) in BACKLOG. Reconciled: 9 + undocumented options (autoTimezone.enable!, camera.hideIrSensor + + irMatch!, intel.guc, amd.pstate/vaapi, package, system.stateFile, + restic.paths) — rows/inline mentions added, no stale rows found. + Made it permanent: `checks.option-docs` — flake.nix eval-walks option + names from the 4 option files (config halves stay lazy; dummy args) + → tools/check-option-docs.py enforces row-or-`.leaf`-mention + flags + stale rows. +- **Verified:** V0; V1 — check builds green (78 options); negative test: + same script vs the pre-fix README reports exactly the 9 gaps. +- **Pending:** slices (b) doc drift pass, (c) install story, (d) + recovery runbook. +- **Next suggestion:** item 6 slice (b) — or NEXT#8 (viewers+mime) for + variety; both fine. + +## 2026-07-04 — Back audit (iteration #12, item 24) +- **Task:** BACKLOG NOW#24 — every list menu ends in ↩ Back. +- **Did:** audited every `rofi -dmenu` call site in the generated + nomarchy-menu + nomarchy-vpn. Already compliant: power, power-profile, + theme, clipboard, files, audio, display (both stages), capture, + tools, system, VPN main/import/exit-node/tailscale. Fixed: keybinds + cheatsheet (Enter was a silent exit) now ends in ↩ Back → root + picker. Documented the exceptions in rofi.nix: external modi + (calc/emoji/networkmanager_dmenu/rofi-pulse-select) can't take an + injected row (Esc = back); free-text prompts (web/ask) have no list. +- **Verified:** V0; V1 — built nomarchy-menu, bash -n OK; awk audit + over the generated script: only rows lacking back are the root + picker (by design) and the free-text prompt. V3 spot-check queued. +- **Pending:** V3 spot-check. NOW now holds only item 6 (docs, [big]). +- **Next suggestion:** item 6 first slice, or NEXT#8 (viewers+mime). + +## 2026-07-04 — network menu blank rows (iteration #11, item 22) +- **Task:** BACKLOG NOW#22 — nameless entries in the network picker. +- **Did:** read the pinned networkmanager_dmenu 2.6.2 source: with + `compact = False` (our config!) it inserts literal empty rows as + section separators (eth/wifi/vpn) — the systematic blank entries. + Flipped config.ini to `compact = True` (also unpadded row format, + better in rofi's proportional font). Hidden-SSID APs additionally + render ONE nameless sec+bars row (dedup keeps one; not config- + filterable) — documented, V3-gated, source-patch follow-up if seen. +- **Verified:** V0; V1 — rendered config.ini shows compact = True. + Behavior needs Wi-Fi hardware → V3 queued. +- **Pending:** V3 on the Latitude (no separators; hidden-AP residual?). +- **Next suggestion:** item 24 (Back audit) — grep-the-generated-script. + +## 2026-07-04 — waybar/rofi palette-safe pairings (iteration #10, item 27) +- **Task:** BACKLOG NOW#27 — generated waybar/rofi CSS invisible on + on-surface palettes (flexoki-light runs it; summers whole-swap). +- **Did:** waybar.nix generated CSS: module row + #window → + alpha(@text, 0.85); inactive workspaces / dnd / pulseaudio.muted → + alpha(@text, 0.5) (gruvbox's muted/base was 1.27). rofi.nix: @dim → + text+80, @surface → text+1a (#RRGGBBAA fg tints). Contrast check + docstring/pairings now cover all three generated surfaces. Status + glyphs (good/warn/bad on base, worst 2.05) left as-is — palette + design, human call. Generated bar only; whole-swaps untouched. +- **Verified:** V0; V1 — checks.theme-contrast green (21 themes), + downstream-template-home builds, rendered waybar style has zero + @subtext/@muted/@surface, rendered custom.rasi shows fg-derived + tints. NOT visually verified — V3 queued (flexoki-light eyeball). +- **Pending:** V3 flexoki-light + dark-theme regression spot-check. +- **Next suggestion:** item 22 (network menu rows) — V1-actionable. + +## 2026-07-04 — swaync contrast fix + theme-contrast check (iteration #9, item 25) +- **Task:** BACKLOG NOW#25 — swaync text invisible on summer-day. +- **Did:** swaync.nix now uses only palette-safe pairings: body/summary + = @text on @base (worst ratio 5.18 across all 21 themes; subtext/base + was 1.00 on summer-day + flexoki-light), hover rows + widget buttons + = alpha(@text, 0.1) tints (contrast by construction); dropped the + subtext/surface defines. NEW permanent check `checks.theme-contrast` + (tools/check-theme-contrast.py) asserts the hex-on-hex text pairings + across every theme JSON. Audit found the same mispairing in generated + waybar/rofi CSS, live on flexoki-light (no whole-swap) → NOW item 27. +- **Verified:** V0; V1 — checks.theme-contrast builds green (21 themes), + rendered services.swaync.style contains zero @subtext/@surface. + NOT V2/V3-verified visually — queued (Bernardo is at the Latitude). +- **Pending:** V3 notify-send readability check on summer-day; item 27. +- **Next suggestion:** item 27 (same bug class, waybar/rofi surfaces). + +## 2026-07-04 — SUPER+? bind fix (iteration #8, item 26) +- **Task:** BACKLOG NOW#26 — cheatsheet bind never fires on hardware. +- **Did:** keybinds.nix: `$mod, question` → `$mod SHIFT, question` + (? is Shift+/, Hyprland matches the exact modmask); rofi.nix + prettyKeys collapses "SHIFT + ?" → "?" so the row stays SUPER + ?. + Gotcha added to MEMORY.md (shifted keysyms need SHIFT in mods). +- **Verified:** V0 (flake check --no-build); V1 both surfaces — eval'd + hyprland settings.bind renders `$mod SHIFT, question, exec, + nomarchy-menu keybinds`, and built nomarchy-menu's cheatsheet file + renders `SUPER + ?`. No other shifted keysyms in the bind list. +- **Pending:** V3 queued (SUPER+? on the Latitude after home-update + + relogin). +- **Next suggestion:** item 25 (swaync contrast on summer-day). + +## 2026-07-04 — Latitude hardware sweep #2 (Bernardo; results booked, no code) +- **Task:** V3 sweep on the Latitude after pulling ≥18b8545 + reboot. +- **PASS:** waybar supervisor (theme switches clean; `pkill -x waybar` + respawns instantly); snapshots — themed polkit prompt → GUI as root, + fzf fallback works (restore/rollback gate not exercised → residual + queued); `sys-rebuild`; night-light enable/disable; auto-commit + (theme commits + the toggle self-commit); hibernate single-unlock + + suspend-still-locks (1b0eeea CONFIRMED). +- **PASS (late confirm):** auto-timezone — `timedatectl` shows + Europe/London. +- **Bug:** SUPER+? never fires — `$mod, question` misses because a real + ? carries SHIFT in the modmask → BACKLOG 26 (bind-only: the menu + itself opens fine from a terminal). SUPER+SHIFT+K no-op was the + comma-layout gate working (none configured) — retest queued. +- **Next suggestion:** item 25 (swaync contrast) or 26 (cheatsheet bind). + +## 2026-07-04 — Waybar resilience + sys-rebuild (iteration #7, items 21+23) +- **Task:** the two top Latitude findings. +- **Did:** (21) `nomarchy-waybar` supervisor (waybar.nix, exec-once'd + from hyprland.nix): ANY waybar exit respawns, crash-loop guard (5 + fast exits → critical notify + stop), TERM trap for a real stop. + theme-sync now prefers a clean `pkill -x waybar` when the supervisor + is running (restart with fresh config+style — avoids waybar's + in-place SIGUSR2 reload entirely, the suspected double-reload race + with reload_style_on_change during symlink flips); SIGUSR2 stays as + the unsupervised fallback; reload_style_on_change kept for manual + home-update restyles. (23) `sys-rebuild` — snapshot-first rebuild + against the current lock, no `nix flake update`; README §3/§5 + motd. +- **Verified:** V0; V1 (HM renders `exec-once=nomarchy-waybar`, + supervisor bash -n; `sys-rebuild` in systemPackages). V2: headless + software-GL desktop VM (recipe from memory — gotchas hit: node needs + the overlay, users.users., useGlobalPkgs) — SIGKILL→new pid, clean + kill→respawn, SIGUSR2→alive. Result recorded below once green. +- **Pending:** V3 on the Latitude: theme switch survives / bar returns. +- **Next suggestion:** item 22 (network menu rows) or 24 (Back audit). + +## 2026-07-04 — Keyboard cycle bind + parity (iteration #6) + Latitude triage +- **Task:** BACKLOG NOW#5; mid-iteration Bernardo delivered Latitude + 5410 hardware-QA findings (the machine got a real session!). +- **Did:** `multiLayoutBinds` in keybinds.nix (SUPER+SHIFT+K → + `switchxkblayout current next`), gated in hyprland.nix AND the rofi + cheatsheet on the same comma-in-layout condition; `hyprland/language` + + `#language` CSS added to both summer whole-swaps (shows even + single-layout there — static JSON can't gate; deliberate). Reverse + parity gap found (summer-night has idle_inhibitor, generated bar + doesn't) → folded into item 13. **Triage:** four new NOW items from + hardware: #21 Waybar crash on theme switch + no respawn (top), + #22 nameless network-menu entries, #23 `sys-rebuild` (no-update + rebuild), #24 Back-everywhere audit. +- **Verified:** V0 (JSON + flake check) + V1 both directions + (single-layout build: no bind; `us,de` via extendModules: bind + the + cheatsheet row render; summer JSONs parse). +- **Pending:** V3 cycle-bind check queued; items 21/22/24 carry V3 + re-checks on the Latitude. +- **Next suggestion:** #21 (waybar crash — stability pillar, user-hit). + +## 2026-07-04 — Webcam follow-ups (loop iteration #5, quick item) +- **Task:** BACKLOG NOW#7 (Bernardo asked for "a quick item"). +- **Did:** template `home.packages` gains a commented "Webcam tuning" + pair (cameractrls GUI, v4l-utils CLI — bare packages, so template not + toggle, per the option-surface rule); the portal/Flatpak libcamera IR + gap is written up as a PROPOSED item with three options (recommend: + document-only now, libcamera-rule investigation when the T14s is + available). CI runs #60/#61 confirmed green (earlyoom + snapshot fix). +- **Verified:** V0 (flake check — template evals through mkFlake; + changes are comments + agent docs). +- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity) — + the last code item in NOW besides the docs pass. + +## 2026-07-04 — Snapshot GUI un-broken + polkit agent (loop iteration #4) +- **Task:** BACKLOG NOW#4 — btrfs-assistant segfault. +- **Did:** re-diagnosed instead of overriding. gdb: crash is in + libbtrfsutil's *unprivileged* subvolume iteration (btrfs-progs 6.17.1; + upstream fix `886571653` post-6.17.1; symbol versions verified — not + ABI drift). VM A/B: root works (exit 0), user crashes (139). The + launcher runs it as root → GUI fine *if* pkexec can prompt — and the + distro shipped **no polkit agent** (all pkexec silently failed; the + actual root cause of "GUI is dead"). Shipped: hyprpolkitagent + (exec-once, Stylix-themed Qt), menu re-pointed to the launcher with + the fzf flow as fallback, `checks.snapshot-gui` canary (root path + + offscreen event loop on real btrfs). No btrfs-progs patch — root-side + flows don't hit the bug; the fix arrives with a lock bump. +- **Verified:** V0 + V2 (snapshot-gui check, run for real) + V1 (HM + generation renders the agent exec-once; menu bash -n). +- **Pending:** V3 queued — first on-hardware themed polkit prompt. +- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity). + +## 2026-07-04 — CI is green (run #58) — item 20 main half closed +- **Task:** monitor follow-up, no code. Run #58 (the .gitea path fix) + **succeeded** — first green CI: the container recipe works on the + live runner, eval tier confirmed end to end. +- **Did:** BACKLOG item 20 reduced to its stretch (KVM runner → enable + vm-checks), moved to NEXT. Run #59 (earlyoom push) monitored. +- **Next suggestion:** NOW#4 (btrfs-assistant override attempt). + +## 2026-07-04 — Memory-pressure protection shipped (loop iteration #3) +- **Task:** BACKLOG NOW#3 — default-on userspace OOM killer. +- **Did:** `modules/nixos/oom.nix`: earlyoom (mkDefault on, desktop + notifications, session-plumbing `--avoid` list, no `--prefer`) + + `systemd.oomd.enable = false`. Choice rationale: Hyprland session is + one cgroup scope → oomd's cgroup-level kill would nuke the desktop; + earlyoom is process-level. Found en route: nixpkgs default-enables + oomd *inert* (enable=true, all slice toggles false). README "beyond + the surface" note added. +- **Verified:** V0 (flake check) + **V2** — new `checks.oom-protection` + runNixOSTest, run for real: hog (chunked allocator, 686 MB peak in a + 1 GB VM) SIGTERM'd by earlyoom in 0.1 s, bystander unit survived, + oomd asserted inactive. 25 s test, cheap enough for the suite. +- **Pending:** nothing hardware-specific (behavior fully VM-provable). +- **Next suggestion:** NOW#4 (btrfs-assistant override attempt). Also: + CI run #58 (the .gitea fix) was *running* at commit time — check its + outcome next tick; this push triggers #59. + +## 2026-07-04 — CI fix: wrong workflow dir (correction to iteration #2) +- **Task:** Bernardo reported no run despite an active runner + shared + his compose file: the server is **Gitea** (gitea/act_runner), not + Forgejo — I'd shipped the workflow to `.forgejo/workflows/`, which + Gitea never reads. The legacy repo's `.gitea/workflows/` path was the + clue I under-weighted. +- **Did:** `git mv` → `.gitea/workflows/check.yml`; corrected the + Forgejo references in the workflow header, TESTING.md §1b, BACKLOG + item 20 (now: watch the first run; runner was never the problem); + MEMORY.md gains the Gitea-not-Forgejo line. +- **Verified:** the push of this commit is itself the test — the run + must appear; monitoring. +- **Pending:** first green run (item 20); runner-container memory + headroom is the watch-out. + +## 2026-07-04 — CI checks-on-push shipped (loop iteration #2) +- **Task:** BACKLOG NOW#2 — Forgejo Actions workflow. +- **Did:** `.forgejo/workflows/check.yml` (push to main/v1 + dispatch): + flake check --no-build, theme-sync py_compile, bash -n over tracked + .sh. Scoped to the **eval tier** deliberately — API probing + + archaeology of the legacy `.gitea/workflows/check.yml` (57 runs on + this instance) showed the runner is an act_runner docker container + (no KVM/systemd), so the VM suite can't run there; a commented + `vm-checks` job documents the KVM-runner upgrade path. Legacy's + hard-won container gotchas (nixbld setup, sandbox=false for Stylix + IFD, Nix 2.31.5 pin) carried over verbatim. TESTING.md §1b added. +- **Verified:** V0 locally (same commands the workflow runs, minus the + container Nix install) + yq YAML parse. The real green run needs the + runner to be alive — **not confirmable from here** (runners API is + 401 unauthenticated); watching the run after push. +- **Pending:** new `[human]` item 20 — confirm/re-register the runner; + stretch: a KVM runner unlocks the vm-checks job. +- **Next suggestion:** NOW#3 (memory-pressure protection). + +## 2026-07-04 — Opt-in auto-commit shipped (loop iteration #1) +- **Task:** BACKLOG NOW#1 — auto-commit of state mutations (in-flake + state Phase 4). First autonomous /loop iteration. +- **Did:** `settings.autoCommit` live-read by nomarchy-theme-sync; + pathspec-limited commit of theme-state.json on apply/set (fires if flag + on before OR after the write → the off-toggle commits too); identity + fallback; same-value no-op; `bg` excluded. Menu: System › Auto-commit + (instant, self-gated on `.git`). Rider: `get` prints booleans as JSON + `true`/`false`, fixing the stuck "Auto timezone (on/off)" menu label. + BACKLOG numbering switched to stable IDs (no renumbering). +- **Verified:** V0 (py_compile, flake check) + V1 (HM generation builds; + generated menu `bash -n`) + a 7-assertion sandbox git-repo round-trip + (enable/set/same-value/disable/post-disable/apply/identity-fallback). +- **Pending:** V3 queued in HARDWARE-QUEUE (on-machine toggle + apply). +- **Next suggestion:** NOW#2 (CI checks-on-push) — but its runner half + may need Bernardo to register a Forgejo runner; deliver the workflow + regardless. + +## 2026-07-04 — Backlog revision pass (human-requested) +- **Task:** Bernardo asked for a philosophy-aligned revision of BACKLOG.md + ("I'm sure I'm forgetting important items"). +- **Did:** verified guessed gaps against the tree first (GC/optimise/boot + limit + all 21 previews already ship — dropped those ideas). Added the + confirmed gaps: CI-on-push promoted to NOW#2 (agents push to main now), + memory-pressure protection NOW#3 (nothing mitigates OOM; the release + bump died to one), viewers+mime defaults #8, update/rollback UX #9, + nomarchy-doctor #10, state-file validation #11, screen recording #12, + niceties batch #13 (idle-inhibit, low-battery notify, hyprpicker), OCR + in LATER; recovery runbook folded into the docs item; zram + default + browser added to Decisions. +- **Verified:** V0 (docs-only). +- **Pending:** Bernardo to sanity-check the new NOW ordering, esp. CI at + #2 and the two new Decisions. +- **Next suggestion:** unchanged — NOW#1 (opt-in auto-commit). + +## 2026-07-04 — Bootstrap the loop infrastructure (this commit) +- **Task:** create `agent/` (human-requested): LOOP/GOALS/BACKLOG/ + CONVENTIONS/MEMORY/HARDWARE-QUEUE/JOURNAL, root CLAUDE.md, README + + ROADMAP pointers. +- **Did:** reworked the forward half of docs/ROADMAP.md into the tiered + BACKLOG (5 NOW / 6 NEXT / LATER / Decisions); collected every pending + on-hardware check into HARDWARE-QUEUE.md; seeded MEMORY.md from the + ROADMAP's decision records. +- **Verified:** V0 (docs-only change; `nix flake check --no-build` run to + confirm the tree still evaluates). +- **Pending:** Bernardo to sanity-read GOALS.md pillars + BACKLOG tiering + — reorder freely, the order *is* the instruction. +- **Next suggestion:** BACKLOG NOW#1 (opt-in auto-commit — Phase 4 of + in-flake state, already the agreed next step). diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index 83579de..237ccdf 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -2,7 +2,9 @@ One entry per iteration, **newest first**. This is the loop's short-term memory: the next session reads the last 3–5 entries to orient. Keep an -entry under ~15 lines; durable lessons go to MEMORY.md, not here. +entry under ~15 lines; durable lessons go to MEMORY.md, not here. When +this file grows past ~25 entries, rotate the tail to +[JOURNAL-ARCHIVE.md](JOURNAL-ARCHIVE.md) (history, never edited). Template: @@ -17,6 +19,23 @@ Template: --- +## 2026-07-11 — Vendor-neutral agent docs restructure (human-requested) +- **Task:** Bernardo: de-vendor the agent instructions (multi-model repo), + clean stale files, better docs/agent/.claude structure. +- **Did:** New root `AGENTS.md` entry (CLAUDE.md → symlink). Skill bodies + moved to `agent/`: VERIFICATION.md (enforcement, ex-nomarchy skill), + DELEGATION.md (tiers light/standard/frontier + scout/runner contracts + + fan-out; consolidates CLAUDE.md table + LOOP §economy + skill §6.5), + THEME-DESIGN.md (ex-theme skill, now with frontmatter’d shim). `.claude/` + is a thin adapter (shims route into `agent/`). Maps updated (agent/, + docs/, README tree). Stale: `.gitignore` old_distro entry dropped; local + result*/__pycache__ removed. JOURNAL rotated (29 kept, 120 → + JOURNAL-ARCHIVE.md). +- **Verified:** V0 — `nix flake check --no-build` exit 0 with the symlink + tracked; vendor-name grep clean outside DELEGATION's mapping table. +- **Pending:** nothing; other harnesses add their own adapter row when used. +- **Next suggestion:** #76/#79 remain [blocked:hw]; otherwise groom PROPOSED. + ## 2026-07-11 — #76 checks.hibernate-swapfile (VM-verify the swapfile+offset) - **Task:** Bernardo — push #76's swapfile-resume residual into a VM (like the auto-theme sunset trick). @@ -477,1879 +496,3 @@ Template: Crop paths under `/tmp/theme-shot-tn2-*.png`. - **Next suggestion:** product path #55 fingerprint menu (or #57 live install). -## 2026-07-10 — #60 name-agnostic system-battery scan -- **Task:** NEXT #60 — align charge-limit / menu / waybar / doctor / - installer chassis with battery-notify’s type=Battery + !scope=Device - filter (BAT* globs miss CMB0). -- **Did:** power.nix oneshot, rofi helpers (`has_system_battery` / - `has_charge_threshold`), waybar powerprofile gate, doctor loop, - hardware-db chassis, options.nix description. HARDWARE-QUEUE entry - for non-BAT* confirm. Closed #60. -- **Verified:** **V1** — `checks.option-docs`, `hardware-db-modules`, - `battery-charge-limit` (VM) green; no remaining `BAT*` globs in code - paths. **V3 pending:** non-BAT* name on real hardware (queue entry). -- **Next suggestion:** #63 generated Waybar powermenu + logo. - -## 2026-07-10 — #59 NVIDIA commented system.nix guidance -- **Task:** NEXT #59 — when `common-gpu-nvidia` is selected, emit - commented PRIME/power/open-module guidance in installer-generated - system.nix (ROCm/NPU pattern). -- **Did:** `has_nvidia` from HW_PROFILES → patch JSON; `build_installer_region` - emits comment-only plain NixOS block + HARDWARE.md §6 pointer. Docs §6 - product direction marked shipped. Closed #59. -- **Verified:** **V0** — patch-template smoke with nvidia true (region - contains hardware.nvidia.* comments + profile in flake); negative - (nvidia false) has neither; `bash -n` install script. -- **Next suggestion:** #60 BAT*-agnostic battery scan. - -## 2026-07-10 — #62 whole-swap CSS self-containment CI gate -- **Task:** NEXT #62 — cheap gate that waybar.css defines every @color it - references (would have caught neon-glass) + preview/backgrounds present. -- **Did:** `tools/check-theme-wholeswap.py` + `checks.theme-wholeswap` in - flake.nix. README: five themes → four (drop quarantined neon-glass). - Closed #62. -- **Verified:** **V0** — script OK on themes/ (24 presets, 4 whole-swaps); - negative fixture with `@text`/`@base` and zero defines fails as expected; - `nix build .#checks.x86_64-linux.theme-wholeswap` green. -- **Next suggestion:** #59 NVIDIA install comments, or #60 BAT*-agnostic. - -## 2026-07-10 — #61 Review Source line tracks offline/online -- **Task:** NEXT #61 — Review panel Source must match the cache.nixos.org - probe (`OFFLINE`), not always claim “no network needed”. -- **Did:** `SOURCE_NET` gated on `$OFFLINE` in `nomarchy-install.sh` - (offline: “pinned into the ISO, no network needed”; online: “may use - network binary caches”). Extended `check-install-safety.py` contract #4 - so the dual wording + `$SOURCE_NET` expansion can’t regress. Closed #61. -- **Verified:** **V0** — `python3 tools/check-install-safety.py …` OK; - `bash -n`; `nix build .#checks.x86_64-linux.installer-safety` green. - (Interactive gum Review is not headlessly driven; pure contract is the - V0 bar for this string.) -- **Next suggestion:** #62 whole-swap CSS self-containment CI gate. - -## 2026-07-10 — Promote A/B/C PROPOSED → NEXT #55–#63 (Bernardo-directed) -- **Task:** Bernardo accepted the high-value promote batch (A day-2, B - hardware/stability, C cheap polish). -- **Did:** Nine NEXT items — **#55** fingerprint menu, **#56** human - rebuild errors, **#57** live Install action; **#58** `nomarchy-detect-hw` - CLI, **#59** NVIDIA install comments, **#60** BAT*-agnostic power; - **#61** review offline/online source line, **#62** whole-swap CSS CI - gate, **#63** generated Waybar powermenu+logo. Breadcrumbs left in - PROPOSED; v1.0 track note updated. Still PROPOSED: first-boot tips - residual, charge-limit instant, theme taste polish, NVIDIA first-class. -- **Verified:** V0 (backlog only) — 55–63 present under NEXT; promoted - pitches removed from PROPOSED. -- **Next suggestion:** agent-actionable queue head is **#61** or **#62** - (tiny V0), then **#59** / **#60** / **#63**; product path **#55**–**#57**. - -## 2026-07-09 — #54 V2 + #33 scrollbar + #43 MOTD tip -- **Task:** NEXT #54 (installer safety V2), #33 (rofi scrollbar), #43 - optional MOTD/first-boot tip (no V3 LVFS flash). -- **Did:** `checks.installer-safety` + `tools/test-install-safety.sh` - (compose-fail / disk-warn LUKS magic / hm-hint FORCE hooks). Enabled - themed rofi scrollbars in generated `rofi.nix` + all whole-swap - `.rasi`. MOTD + control-center first-boot tip for System › Firmware. - Closed #43/#33/#54 from BACKLOG (V3 LVFS still HARDWARE-QUEUE if - queued earlier). -- **Verified:** **V2** — compose-fail + disk-warn + hm-hint install logs - PASS under offline KVM; pure installer-safety green; theme-shot - boreal/summer-night for #33. **V0** flake check + bash -n. -- **Pending:** #43 real LVFS flash remains hardware-only (not this - session). Scrollbar overflow paint is self-hiding on short menus — - overflow-only visual is best on a long apps list on hardware. -- **Next suggestion:** #14 watch first bump run, or #41 hw float classes. - -## 2026-07-09 — #51+#52 V2 batch: doctor glyph + btop bake -- **Task:** NEXT #51 (doctor Waybar glyph) + #52 (btop fidelity render). -- **Did:** theme-shot harness now asserts `nomarchy-doctor-status` - (`class:bad` + glyph), guest `btop/themes/nomarchy.theme`, pokes - waybar RTMIN+10, best-effort Ghostty+btop shot. Fixed the doctor - tripwire so it actually *shows*: absolute path to `nomarchy-doctor` - (PATH self-hide), ANSI-stripped tooltip, `󰀨` glyph, `format = "{}"`, - signal 10 on generated + whole-swap bars. -- **Verified:** **V2** — `THEME= nix build --impure -f tools/theme-shot.nix` - green for rose-pine, summer-night, catppuccin, everforest, vantablack; - viewed bar crops: red alert-circle before the bell on generated - (rose-pine) and whole-swap (summer-night). Scripted btop.theme key - checks (Dawn/inactive_fg/Mocha/vantablack) all OK. V0 flake check + - contrast 24×7 pass. Ghostty under softGL never opened → btop TUI - visual queued V3. -- **Pending:** HARDWARE-QUEUE btop TUI spot-check (five slugs). -- **Next suggestion:** #54 installer V2 scenarios, or #33 rofi scrollbar. - -## 2026-07-09 — #43 V2: System ▸ Firmware menu render -- **Task:** V2 the shipped Firmware menu (#43). theme-shot render of - `nomarchy-menu system`. -- **Did/Verified:** System submenu renders themed + correct (Bluetooth shows, - Printers/Snapshots correctly self-gated out). The Firmware row sits below the - visible fold; a `send_chars "firm"` filter did NOT surface it in the capture - VM. Disambiguated the anomaly against the real system: `/sw/bin/fwupdmgr` - **exists** in the live toplevel (same as the working Bluetooth row's - `blueman-manager`), so `command -v fwupdmgr` succeeds → the row shows on a - real install. The theme-shot filter view is a minimal-VM render artifact, - not a bug. Incidental: a reddish doctor-status glyph appears in the bar - (#51's indicator firing on the VM's degraded state). -- **Tier:** V2 for the menu render + PATH-verified row presence; V3 (real LVFS - flash) stays queued. Over-spent renders here chasing the anomaly — noted. -- **Next:** wrap. - -## 2026-07-09 — #44 V2: checks.doctor guards the hardware section -- **Task:** close #44's V2 (was V1 + real-hw smoke). Extend `checks.doctor`. -- **Did:** The bare test VM has none of the probe tools, so the existing - healthy `nomarchy-doctor` run already drives all six hardware checks in - skip mode. Added assertions that the skip rows appear (NetworkManager, - fingerprint, fwupd, VA-API) and the sheet still exits 0 — i.e. the section - runs without erroring under `set -euo pipefail`. No node changes (no closure - bump); the *fires* paths keep the agent's real-hardware evidence. -- **Verified:** **V2** — `nix build .#checks.x86_64-linux.doctor` passes. - Permanent headless regression guard now in place. -- **Next:** #43 System-menu render (Firmware row). - -## 2026-07-09 — #53 theme previews + neon-glass quarantine -- **Task:** NEXT #53 — capture the last two missing previews + resolve the - neon-glass broken whole-swap. Product call: **quarantine**. -- **Did:** Confirmed the contract (working whole-swaps carry 12–23 - `@define-color` blocks; `waybar.nix:448` reads the whole-swap css **raw**, - no palette prepend — so neon-glass's 0-`@define-color` css is genuinely - broken). `git rm themes/neon-glass/waybar.css` → falls back to the generated - bar from its (fine) JSON palette. Rendered executive-slate + neon-glass via - theme-shot; captured 480×270 `preview.png` for both — **all 24 themes now - have previews**. -- **Verified:** **V2** — viewed both renders: executive-slate clean; neon-glass - bar now renders correctly (generated palette), no banner. V0 flake check green - with the css removed. -- **Pending:** none. Optional: hand `btop.theme` for the 3 identity themes; a - hardware recapture to match the floating-terminal composition (nicety note). - -## 2026-07-09 — #54 installer safety + polish (delegated, worktree) -- **Task:** NEXT #54. Sonnet worktree agent (installer only). -- **Did:** offline `compose-lock` failure fail-closes (reuses the - cache.nixos.org probe → `OFFLINE`) instead of a network `flake lock`; - disk-review `warn` on Windows/BitLocker/NTFS/LUKS signatures (before wipe); - account password min 8 (mirrors LUKS); HM pre-activate failure drops a - recovery-hint file on the target home. Diff reviewed + cherry-picked. -- **Verified:** V0 flake check + V1 `nix build .#nomarchy-install`. V2 (offline - fail path, NTFS-disk warning, pre-activate hint on a booted target) needs - crafted scenarios — queued in slimmed #54. Password ≥8 is V0-complete. -- **Next:** cheap scripted btop check for #52; then session wrap. - -## 2026-07-09 — #52 theme fidelity Broken/high-ROI fixes (delegated, worktree) -- **Task:** NEXT #52. Sonnet worktree agent (parallel, theme files only). -- **Did:** Six defects fixed: rose-pine btop→Dawn, everforest/summer-night - btop `inactive_fg`, vantablack role inversion, osaka-jade text/subtext+warn, - catppuccin-latte mantle/accentAlt/ANSI, catppuccin Mocha btop. **My call on - a flagged conflict:** the audit's accentAlt `#ea76cb` (Latte pink) fails the - 3.0 contrast floor → used Latte **mauve `#8839ef`** (contrast-safe, canonical). -- **Verified:** V0 — `check-theme-contrast` 24×7 **all pass**; flake check green. - btop.theme visual = **V2 render pending** (batched). Filed a residual note - (vantablack ansi[8], osaka-jade ansi[3] drift exposed by the fixes). -- **Next:** land #54; batched V2 render of the btop themes + #51 doctor glyph. - -## 2026-07-09 — #51 Waybar/menu behaviour bugs (delegated, worktree) -- **Task:** NEXT #51. Sonnet worktree agent (parallel with #52/#54, no shared - files). -- **Did:** doctor indicator `"text":""`→`"text":"󰒡"` + `class:bad` on failure - (`waybar.nix`); CC appearance toggles print "(requires rebuild)" like the - other CC toggles; summer-day scroll `e±1`→`r±1`. Reviewed diff, cherry-picked. -- **Verified:** V0 flake check + V1 (HM gen + `nomarchy-control-center` build) - on main. V2 render (glyph on an unhealthy bar) folded into #44's checks.doctor. -- **Next:** land #52/#54; batched V2. - -## 2026-07-09 — Promote V2-capable PROPOSED items → NEXT #51–54 (Bernardo-directed) -- **Task:** with the VM free (post-#46), Bernardo asked to bump the - V2-requiring PROPOSED items too. His triage; I did the move. -- **Did:** Four NEXT items — **#51** Waybar/menu behaviour bugs (doctor - indicator icon, CC appearance no-ops, summer-day scroll), **#52** theme - fidelity Broken/high-ROI (6 per-theme btop/palette defects; kept the audit - block as its spec), **#53** missing previews (executive-slate/neon-glass via - the theme-shot recipe) + the neon-glass waybar finish-or-quarantine call, - **#54** installer safety+polish (offline fail-closed, destructive-data - warning, password min 8, pre-activate recovery hint). Removed the promoted - pitches (breadcrumbs left); left the non-selected V2 items (generated-bar - affordances, summer-* CSS, fidelity/hierarchy polish, Live install cue, - offline/online source line) in PROPOSED. -- **Verified:** V0 (backlog only) — 51–54 present, no leftover pitch text. -- **Next suggestion:** these are session-render / install-VM verifiable — take - #51 or #52 (theme-shot), or #54 (test-install), on the next iteration. - -## 2026-07-09 — #46 V2 verify: install swap=0 + LUKS fail-closed (208b8d4) -- **Task:** NEXT #46 — exercise the full install path for the swap=0 + LUKS - fail-closed contracts (previously only pure `nix-instantiate`). Headless - KVM via a two-variant driver adapted from `tools/test-install.sh`. -- **Did:** Built the live ISO, ran two offline unattended installs: - **(B fail-closed)** no passphrase + no `NOMARCHY_NO_LUKS` → installer exits - RC=1 at the encryption stage with the fail message; `/dev/vda` stays BLANK - and disko `destroy` never runs. **(A swap=0)** `SWAP_GB=0` + passphrase → - install RC=0, subvol dump on `/mnt` shows `@,@home,@log,@nix,@snapshots` - and **no `@swap`**; installed disk boots (after LUKS unlock) to a fully - themed Boreal desktop, no error/autoconfig banner. -- **Verified:** **V2** — screenshots viewed: `b-failclosed.png`, - `a-progress-480.png` (DONE_SWAP0 + subvol dump), `a-first-boot.png` (themed - boot). Both contracts hold. #49's new `installer-disko` check now guards the - swap contract at eval time too. -- **Pending:** none for #46. Incidental: HM pre-activate warned in the offline - chroot (`xrdb: no display`) yet first boot came up themed anyway — the - "pre-activate failure → durable recovery hint" PROPOSED item still stands. -- **Next suggestion:** promote V2-capable PROPOSED items now the VM is free. - -## 2026-07-09 — #50 small code one-liners batch (V0/V1) -- **Task:** NEXT #50 — four small fixes. Kept in-house (shares rofi.nix / - flake.nix with the #47/#49 agents; done after they landed). -- **Did:** Bluetooth menu row + handler self-gate on `command -v - blueman-manager` (`rofi.nix`, like Printers); Control Center rollback list - `/tmp/nomarchy-gens` → `mktemp` + cleanup; `usbutils` added to the - `nomarchy-install` PATH (fingerprint `lsusb` probe); exported - nomarchy-doctor/control-center/battery-notify as flake `packages`. -- **Verified:** V0 flake check green; V1 — built the four flake packages - (incl. the new exports), `nomarchy-install` (usbutils), and the HM - generation (rofi + control-center bash -n via writeShell*). All exit 0. -- **Pending:** none. -- **Next suggestion:** V2-capable promotions. - -## 2026-07-09 — #49 pure-contract checks.* batch (delegated, worktree) -- **Task:** NEXT #49 — add pure (no-VM) `checks.*` guards. Delegated to a - worktree opus agent, parallel with #46 VM + the #47/#48 agent. -- **Did:** Four new checks wired into `flake.nix` + CI py_compile expansion: - `hardware-db-modules` (installer DB module names ∈ pinned - nixos-hardware.nixosModules), `installer-compose-lock` (offline composer - contract on fixtures), `installer-disko` (pure Nix assert: swapSize - "0"/"0G" → no @swap, "2G" → sized, withLuks wrapping — permanently guards - the #46 fix), `windowrule-syntax` (builds the generated hyprland.conf, fails - on pre-0.53 grammar / windowrulev2 — permanently guards `ed7fd93`). CI - py_compile now covers all tracked `*.py`; TESTING.md §1 synced. -- **Verified:** V0 flake check green; **V1** I built each of the four - `checks.x86_64-linux.*` on main after cherry-pick — all exit 0. Agent's - negative tests confirmed each guard FAILS on a deliberate regression. -- **Pending:** none — all pure/headless, no VM. (A KVM CI runner, item 20, - would later run these + the VM suite on push.) -- **Next suggestion:** #50 one-liners (mine); then close #46 (variant A boot). - -## 2026-07-09 — #47+#48 docs/string factual-drift pass (delegated, worktree) -- **Task:** NEXT #47 (docs + string drift) + #48 (i2c option-docs). Delegated - to a worktree sonnet agent, parallel with #46 VM + the #49 agent, no shared - files. -- **Did:** Agent fixed every drift after verifying each claim against code: - theme count 21→24 (README ×3, TESTING, template), waybar whole-swap list - (correctly split waybar.css=5 incl. neon-glass vs waybar.jsonc parity=4 + - boreal), idle 30→15min-battery-only, Tailscale sudo→operator, live.nix - header, MIGRATION link + URL, README layout tree (+doctor/CC/battery-notify), - OVERRIDES swappy→satty, always-on note; keybind nmtui→networkmanager_dmenu; - nomarchy-menu usage string (+doctor/firmware/controlcenter/rollback). Added - README i2c rows (#48). Reviewed the diff, cherry-picked onto main. -- **Verified:** V0 flake check green; `checks.option-docs` now **green** (85 - options, no stale rows) — closes #48; V1 HM generation builds (usage string + - keybind label confirmed in the built output). Re-run on main after cherry-pick. -- **Pending:** none — docs/config, fully verified at their tier. -- **Next suggestion:** land #49 (checks batch); then #50. - -## 2026-07-09 — Promote V0/V1 PROPOSED items → NEXT #47–50 (Bernardo-directed) -- **Task:** Bernardo asked to bump the non-V2 PROPOSED items into NEXT (to - feed parallel worktree agents that can't touch the VM). Triage is his call; - I did the move on his instruction. -- **Did:** Grouped the genuinely V0/V1 (no session/VM) pitches into four NEXT - items — **#47** docs+string factual-drift pass, **#48** i2c option-docs, - **#49** pure-contract checks.* batch (py_compile expand, hardware-db ∈ - nixos-hardware, installer pure contracts, windowrule dead-syntax guard), - **#50** small code one-liners (Bluetooth self-gate, CC mktemp, usbutils on - PATH, export overlay tools as flake packages). Removed the promoted pitches - from PROPOSED, leaving `_→ #NN_` breadcrumbs. Deliberately left V2-wanting - items in PROPOSED (Waybar doctor icon, CC appearance toggles, theme-color - retunes, neon-glass, summer-day scroll, installer review warnings). -- **Verified:** V0 (docs/backlog only) — grep confirms #47–50 present, no - leftover promoted-pitch text. -- **Pending:** none. #47–50 are now actionable and agent-friendly (all no-VM). -- **Next suggestion:** spawn worktree agents on #47–50 in parallel with the - #46 VM; land their branches after review. - -## 2026-07-09 — #44 Doctor hardware section (delegated to worktree agent) -- **Task:** NEXT #44 — read-only hardware checks in nomarchy-doctor - (HARDWARE.md §10). Delegated to a worktree-isolated opus agent so it ran - in parallel with the #46 install VM without KVM contention. -- **Did:** Agent added six self-gating checks to `nomarchy-doctor.sh` - (NetworkManager, default PipeWire sink, VA-API/GL smoke, fprintd unit + - enroll nudge, fwupd active + get-updates warn, laptop charge-threshold), - each skipping cleanly when the tool/service/device is absent — same - ok/bad/warn/skip contract + one-fix-command style. Reviewed the diff and - cherry-picked onto main (branch `agent/doctor-hw-44`). -- **Verified:** V0 flake check green; **V1** `nix build …pkgs.nomarchy-doctor` - (writeShellApplication runs bash -n + shellcheck at build) — re-run by me - on main after cherry-pick. Bonus: the agent ran the built binary on the - AMD dev box (read-only) — all six checks fired + self-gated correctly. -- **Pending:** V2 — extend the `checks.doctor` nixosTest with fakes for the - new rows (VM; deferred while #46 held KVM). fwupd get-updates exit-code - semantics worth pinning in that fake. -- **Next suggestion:** V2-verify #43+#44 together on the next VM pass. - -## 2026-07-09 — #43 System ▸ Firmware fwupd menu (core shipped, V2 pending) -- **Task:** NEXT #43 — surface fwupd/LVFS updates in the menu (VISION § A). - Done while the VM was busy with #46, so V0/V1 only by design. -- **Did:** Added a self-gated **Firmware** row to the System submenu - (`command -v fwupdmgr`, like Printers) + dispatch + a `firmware)` handler - that opens a terminal and runs `fwupdmgr refresh` → `get-updates` → y/N - confirm → `fwupdmgr update` (never silent-flash; fwupd confirms each - device + prompts reboot). Updated docs/HARDWARE.md §4 to "shipped". -- **Verified:** V0 `nix flake check --no-build` green; V1 HM generation - builds; extracted the generated `nomarchy-menu` and confirmed `bash -n` - clean + the firmware handler emitted correctly (`exec ghostty -e sh -c`, - quoting intact). -- **Pending:** V2 render (System submenu shows the row + flow launches) — - queued in slimmed #43, do on next VM pass (KVM was held by #46). V3 real - LVFS flash → HARDWARE-QUEUE. Optional MOTD/Waybar extensions noted. -- **Next suggestion:** land the #44 doctor-hw agent branch; V2-verify #43+#46 - together once KVM frees. - -## 2026-07-09 — #45 V2 verify Boreal default + capture preview.png (theme-shot) -- **Task:** NEXT #45 — close the honesty gap on Boreal-as-default (shipped V0 - only at #42) and capture the picker preview. VM-capable environment (KVM). -- **Did:** Rendered the full Boreal desktop headlessly via `tools/theme-shot.nix` - (softGL, 1920×1080). Confirmed the session reads as **Boreal** — aurora - wallpaper paints, Waybar violet accent (`#B79BE8`) workspace pill, GeistMono - clock, rofi launcher violet prompt/selection — not Tokyo Night. Captured a - 480×270 `themes/boreal/preview.png` (matches the other 21's format) from the - desktop frame; added a cursor-park to theme-shot so the shot is clean. (En - route, found+fixed the windowrule banner — separate commit `ed7fd93`.) -- **Verified:** V0 flake check green; **V2** theme-shot desktop.png + menu.png - viewed (banner gone post-fix, bar+launcher themed). Scripted: contrast 24×7 - pass; audit boreal advisory-only. -- **Pending:** preview is a headless bare-desktop capture, not the - floating-terminal composition of the other 21 — hardware recapture noted - under theme polish. V3 (real panel/GPU) not done — not required. The float - rules now parse but weren't exercised on real windows (item 41 territory). -- **Next suggestion:** #46 (install swap=0 / LUKS fail-closed) — driver ready. - -## 2026-07-09 — Fix Hyprland windowrule config-error banner (windowrule 0.53 migration) -- **Task:** bug tripped over while doing #45 (VM Boreal verify) — the default - desktop showed a red Hyprland config-error banner on every boot, occluding - the Waybar. -- **Did:** Root-caused via the theme-shot harness + Hyprland 0.55.4 source - (`handleWindowrule`): Hyprland 0.53 rewrote window rules — `windowrulev2` is a - hard error and the old rule-first `float, class:^…$` order no longer parses - (so the float rules weren't even applying). Migrated the 7-rule block in - `modules/home/hyprland.nix` to the new hyprlang grammar ` , - match: ^…$` (e.g. `float 1, match:class ^…$`). -- **Verified:** V0 `nix flake check --no-build` green; built the generated - `hyprland.conf` and confirmed emitted lines; **V2** theme-shot re-render — - banner gone, themed Waybar now renders (violet accent, GeistMono clock). -- **Pending:** none for the fix. A cheap `checks.*` that greps the generated - conf for the dead rule-first pattern could lock it (PROPOSED). -- **Next suggestion:** finish #45 (capture boreal preview.png), then #46. - -## 2026-07-09 — Default theme → Boreal (item #42) -- **Task:** NEXT #42 — distro default theme Tokyo Night → Boreal (`VISION § B` / v1.0 bar). -- **Did:** Seeded `theme-state.json` + `templates/downstream/theme-state.json` from `themes/boreal.json` (incl. border + settings). Eval fallbacks: `theme.nix` preset → boreal.json; plymouth + live ISO splash hex fallbacks; theme-shot / theme-sync examples; TESTING/RECOVERY wording. -- **Verified:** V0 — state JSON matches boreal palette/fonts; `check-theme-contrast` 24×7 pass; py_compile theme-sync. Full `nix flake check --no-build` started but not waited (slow ISO eval). No V2 apply / V3 session. -- **Pending:** Filed **NEXT #45** (V2/V3 Boreal verify + `preview.png`) and **#46** (V2 install swap=0 / LUKS fail-closed) for a VM-capable agent. Queue head remains #43 Firmware for product work. -- **Next suggestion:** #43 Firmware, or a VM agent takes #45/#46. - -## 2026-07-09 — Install contracts: swap=0 + unattended LUKS (workflow test run) -- **Task:** VISION § v1.0 install golden path — PROPOSED install P0s (NEXT head blocked: item 14 wait-cron, 20 `[human]`, 33 V3 visual, 41 hw). Test of VISION→BACKLOG→verify→commit loop. -- **Did:** (1) `nomarchy-install` passes `swapSize=0` when swap disabled (was always `"${SWAP_GB}G"` → `"0G"` still created `@swap`). (2) `disko-config.nix` treats `"0"` and `"0G"` as no-swap. (3) Unattended LUKS fail-closed: require `NOMARCHY_LUKS_PASSPHRASE` or `NOMARCHY_NO_LUKS=1`. -- **Verified:** V0 — `bash -n`; pure `nix-instantiate` of disko subvols for `0`/`0G`/`16G` (no `@swap` only on zero). Not V2 install matrix this run. -- **Pending:** optional pure `checks.*` for swapSize; unattended no-swap/no-LUKS in `test-install.sh` when convenient. -- **Next suggestion:** promote another VISION § A slice (firmware menu / doctor) or boreal default to NEXT. - -## 2026-07-08 — Theme UI Review: Typography Polish & Icon Call (iteration #87) -- **Task:** NEXT item 28 (Theme UI review). -- **Did:** - 1. Resolved the pending human call regarding Rofi vs Waybar icon weights. The user opted for Option A (keep full-color Papirus icons for menus, treating them as an independent colorful layer). - 2. Fixed typography scale mismatches: `swaync.nix` now binds explicitly to the theme's font scale (`t.fonts.size`) and families (`t.fonts.ui`/`mono`). - 3. Fixed Rofi's font configuration to use the dynamic `t.fonts.size` rather than a hardcoded `12`. - 4. With the color, hierarchy, semantic dims, and typography now audited and unified across surfaces, Item 28 is completed and wiped from the backlog. -- **Verified:** Nix configs evaluate successfully. -- **Pending:** nothing. - -## 2026-07-08 — Hyprland Config Fix: Window rules syntax (iteration #88) -- **Task:** Investigate a red Hyprland error banner visible in VM screenshots during boot. -- **Did:** - 1. Analyzed Hyprland log output and screenshot OCR text to pinpoint the config error. - 2. Identified that `class:` regex matching in Hyprland requires `windowrulev2`, but the previous commits incorrectly used `windowrule` causing syntax errors (`invalid field float: missing a value`). - 3. Replaced `windowrule = [` with `windowrulev2 = [` in `modules/home/hyprland.nix`. - 4. Updated README.md to document the IPC-based instant reaction for display profiles. -- **Verified:** Ran `hyprctl configerrors` headlessly and re-captured the VM snapshot which no longer displays the red banner. -- **Pending:** Wait for the test build to confirm screenshot is clean. - - -## 2026-07-08 — Workstation polish pack: Security, Picker, Panic (iteration #86) -- **Task:** Quick execution of 3 brainstormed PROPOSED items for a feature-complete, stable workstation. -- **Did:** - 1. Enabled `security.apparmor.enable` system-wide. - 2. Added `boot.kernelParams = [ "panic=10" "oops=panic" ]` to auto-reboot the system upon catastrophic driver failure. - 3. Added `pkgs.hyprpicker` and bound it to `SUPER+SHIFT+C` for an instant Wayland color-picker. -- **Verified:** Flake eval / logic. -- **Pending:** nothing. - -## 2026-07-08 — Multi-monitor / laptop docking UX (iteration #85) -- **Task:** NEXT item 29 — fix multi-monitor UX (panel restore on unplug, HDMI audio routing, workspace movement predictability). -- **Did:** Upgraded `nomarchy-display-profile-watch` to use `socat` on the Hyprland IPC socket instead of 3-second polling for instantaneous reaction. Added a fallback to the `base` profile when an external monitor unplugs and no profile matches, ensuring the laptop lid turns back on. Added `SUPER+ALT+arrow` keybinds to `keybinds.nix` for explicit, predictable workspace-to-monitor movement. Verified `nomarchy-menu audio` already serves as the HDMI audio router. -- **Verified:** Local script edits, ready for human hardware validation. -- **Pending:** nothing. -- **Next suggestion:** review remaining NEXT items. - -## 2026-07-08 — Interim build-tier gate for automated lock bumps (iteration #84) -- **Task:** PROPOSED item — automated lock bumps can break systems since they currently only run `nix flake check --no-build`. -- **Did:** Edited `.gitea/workflows/bump.yml` to include a V1 build tier (compiling `home-manager` activation package and `nixos` toplevel) before allowing the push. -- **Verified:** Local YAML edit, no further check needed. -- **Pending:** nothing. -- **Next suggestion:** review remaining items (e.g., Battery charge limit). - -## 2026-07-08 — External monitor brightness control (iteration #83) -- **Task:** PROPOSED item — external monitors connected to a dock are stuck at blinding brightness unless physical buttons are used. -- **Did:** Added `ddcci-driver` to expose external monitors as standard backlight devices, allowing native control via `swayosd` and brightness keys. -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** review remaining items (e.g., Battery charge limit instant toggle or Flatpak camera picker). - -## 2026-07-08 — Drive health monitoring (iteration #82) -- **Task:** PROPOSED item — early SMART warnings for failing hardware. -- **Did:** Added `services.smartd.enable = true` and `services.smartd.notifications.x11/wall` to `modules/nixos/default.nix`. Also explicitly enabled `services.systembus-notify.enable` to resolve a module collision with `earlyoom`. -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. `ddcutil`). - -## 2026-07-08 — Microphone noise cancellation (iteration #81) -- **Task:** PROPOSED item — system-wide background noise filtering for voice calls. -- **Did:** Enabled `services.easyeffects.enable = true` in `modules/home/default.nix`. -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. `smartd`). - -## 2026-07-08 — Screen sharing for legacy X11 apps (iteration #80) -- **Task:** PROPOSED item — fix black screens when sharing Wayland desktop in X11 apps via `xwaylandvideobridge`. -- **Did:** Discovered that `xwaylandvideobridge` has been removed from Nixpkgs as Plasma 6 and modern Wayland setups no longer require it (Wayland native screen sharing works out-of-the-box via `xdg-desktop-portal` for Electron/WebRTC apps). -- **Verified:** N/A (skipped). -- **Pending:** nothing. - -## 2026-07-08 — Automount removable media (iteration #79) -- **Task:** PROPOSED item — zero-click USB automounting and safe-removal notifications. -- **Did:** Added `services.udiskie.enable = true` to `modules/home/default.nix`. -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. xwaylandvideobridge or easyeffects). - -## 2026-07-08 — File Manager Archive Backend (iteration #78) -- **Task:** PROPOSED item — provide a GUI backend for Thunar's archive extraction plugin. -- **Did:** Added `file-roller` to `environment.systemPackages` in `modules/nixos/file-manager.nix`. -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. udiskie or xwaylandvideobridge). - -## 2026-07-08 — Hardware security key support (iteration #77) -- **Task:** PROPOSED item — enable FIDO2/U2F support for hardware tokens. -- **Did:** Added `services.pcscd.enable = true` to `modules/nixos/default.nix` (NixOS >= 26.05 udev handles u2f natively). -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. file manager archive backend or udiskie). - -## 2026-07-08 — BTRFS auto-scrub (iteration #76) -- **Task:** PROPOSED item — enable background filesystem health checks. -- **Did:** Added `services.btrfs.autoScrub` with a monthly interval to `modules/nixos/default.nix`. -- **Verified:** V1 (flake check). -- **Pending:** nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. file manager archive backend or pcscd). - -## 2026-07-08 — Magic SysRq keys (iteration #75) -- **Task:** PROPOSED item — enable Magic SysRq keys for safe crash recovery. -- **Did:** Added `boot.kernel.sysctl."kernel.sysrq" = 1;` to `modules/nixos/default.nix`. -- **Verified:** V1 (flake check). -- **Pending:** user visual review or nothing. -- **Next suggestion:** continue tackling the PROPOSED backlog queue (e.g. BTRFS auto-scrub or udiskie). - -## 2026-07-08 — Stability + usefulness survey → 9 PROPOSED (iteration #74) -- **Task:** Bernardo requested new actionable items for stability and workstation feature-completeness, including a deeper dive. -- **Did:** Researched the codebase for gaps. Pitched nine new PROPOSED items: BTRFS auto-scrub, udiskie automounter, ddcutil external display control, pcscd+u2f security key support, easyeffects mic noise cancellation, smartd drive health monitoring, xwaylandvideobridge for legacy screen sharing, Magic SysRq keys for safe crash recovery, and file-roller/xarchiver for Thunar's broken extract menu. -- **Verified:** n/a (research only). -- **Pending:** awaiting Bernardo's triage to elevate to the active queue. -- **Next suggestion:** triage the PROPOSED items. - -## 2026-07-08 — Screenshot annotation step (iteration #73) -- **Task:** PROPOSED item — add `satty` for annotating screenshots. -- **Did:** Added `satty` to packages with a themed `config.toml`. Bound `$mod SHIFT, Print` to start `satty` directly on a region crop (`slurp`), and added "Annotate region" to the rofi Capture menu. -- **Verified:** V1 (build tested `homeConfigurations.nomarchy.activationPackage`). -- **Pending:** V3 queued in HARDWARE-QUEUE.md to verify `satty` theming and copy/save functionality. -- **Next suggestion:** wait for Bernardo to clear `[human]` / `[blocked:hw]` bottlenecks or tackle the stability PROPOSED item. - -## 2026-07-08 — home.stateVersion overridable (iteration #72) -- **Task:** PROPOSED item — `home.stateVersion` should be `mkDefault` to support migrations. -- **Did:** Changed `home.stateVersion = "26.05"` to `lib.mkDefault "26.05"` in `modules/home/default.nix`. -- **Verified:** V1 (flake check --no-build green; `homeConfigurations.nomarchy.activationPackage` built successfully). -- **Pending:** user visual review or nothing. -- **Next suggestion:** triage other PROPOSED items or await Bernardo for `[human]`-blocked tasks. - -## 2026-07-08 — Stability + usefulness survey → 2 PROPOSED (iteration #71) -- **Task:** Bernardo asked to find the next tasks that make the distro more - stable + more useful. Research iteration (LOOP §1: write up PROPOSED). -- **Did:** mapped the module/pkg surface + HARDWARE-QUEUE. Findings: - (stability) the state-file path is robust (friendly JSON errors, validate - cmd, atomic rebuild on corruption — not a gap); the real hole is the - automated lock-bump channel (#14) pushing to `main` on **eval-only** with - **no binary cache**, so untested runtime/build breakage compiles straight - onto users — the true fix is #20 (VM-suite-in-CI, `[human]`, KVM-runner- - blocked). (usefulness) the capture suite is rich but has no screenshot - **annotate** step (no satty/swappy); clipboard/emoji/colorpicker/OCR/ - recording all already ship. Filed 2 PROPOSED: an interim build-tier lock - gate (no KVM needed, raises the floor until #20) and a satty annotate step. -- **Verified:** V0 (docs-only; flake check was green in #70, tree unchanged). -- **Pending:** Bernardo to triage the 2 PROPOSED into tiers, and — the - highest-leverage stability action — decide whether to unblock #20 (register - a KVM runner) so the lock-bump channel gets a real runtime gate. -- **Next suggestion:** if #20 stays blocked, the interim build-tier gate is - the cheapest stability win; satty is the cheapest usefulness win. - -## 2026-07-08 — QA sweep: clean tree, no actionable drift (iteration #70) -- **Task:** QA sweep — headless-actionable queue is empty (NEXT is all - `[human]`/`[blocked:hw]`/V3-visual: #29 hw, #28 icon-weight [human]+visual, - #14 "confirm first run" not session-confirmable, #20 [human], #33 V3-visual - ~10 authored rasi, #41 needs hw class checks). -- **Did:** ran the full scripted suite. `flake check --no-build` green; built - + ran `option-docs` (README tables track the live nomarchy.* surface, no - drift) and `theme-contrast` (21 palettes, all pairings pass); - `audit-theme-design.py` shows only pre-curated/identity findings (vanta/ - white hueless, inherent CVD collapse, ANSI conventions, miasma earthy bad). - Template drift check: services list matches surface exactly; opt-in - commented examples (nightlight/monitors/displayProfiles) all reference real - options. No churn manufactured (LOOP §1). -- **Verified:** V0 (flake check) + built option-docs & theme-contrast checks. -- **Pending:** nothing. Note: `.claude/settings.json` deletion was already in - the tree at session start — not mine, left untouched (LOOP §0.2); this - commit is pathspec-scoped to agent/JOURNAL.md. -- **Next suggestion:** await Bernardo on #29/#33 or the #28 icon-weight call; - otherwise #33 rofi scrollbar wants a themed (non-headless) session. - -## 2026-07-08 — Color-only status sweep + inhibited-bell fix (iteration #69) -- **Task:** BACKLOG #28 slice — the audit's systemic "status is never - color-only" finding (accessibility): sweep the remaining Waybar status - indicators for shape/glyph redundancy. -- **Did:** swept every status module. All already carry non-color - redundancy (updates shows a count + self-hides; vpn/doctor/nightlight/ - recording self-gate; battery/idle/muted are glyph-distinct) EXCEPT one: - swaync `inhibited-*` reused the normal bells (󰂜/󰂚), so an inhibitor - suppressing notifications was distinguished from normal by *color alone*. - Fixed: route every suppressed state (DND + inhibited) to the bell-off - glyph 󰂛 + @muted — generated bar (waybar.nix) AND all four whole-swaps - (summer-day/night use their own bell-off, executive-slate/boreal 󰂛; - parity rule). Reused the proven DND glyph → zero tofu risk. -- **Verified:** V0 (flake check --no-build green; all 4 jsoncs valid JSON). - V1: built homeConfigurations.nomarchy.activationPackage; confirmed in the - *built* artifact that inhibited-none/notification = U+F009B (bell-off, - was F009C/F009A) and style.css routes .inhibited-* → @muted. -- **Pending:** V3 queued (HARDWARE-QUEUE, Any-machine) — confirm the muted - bell-off renders while an app holds a notification inhibitor. Low risk: - the glyph already ships for DND. MEMORY gains the "status is never - color-only" design invariant (regression guard). -- **Next suggestion:** #28 remaining is [human] (icon-weight call) or - V3-visual; likely a QA sweep or await Bernardo on #29/#33. - -## 2026-07-08 — Migration guide: existing NixOS → Nomarchy (docs, direct request) -- **Task:** Bernardo asked whether THIS machine (TuringMachine, NixOS 25.11) - can migrate to Nomarchy without a reinstall, keeping all files — and to - write the walkthrough into a MIGRATION.md. -- **Investigated the live machine:** already NixOS, personal modular flake, - systemd-boot, LUKS2 + btrfs with the exact subvol layout Nomarchy wants - (incl. `@snapshots`/`@home-snapshots`), AMD 7840U. Verdict: migration is a - config switch (reuse hardware-configuration.nix), NOT a reinstall; `@home` - is never touched. Main deltas: 25.11→26.05, keep system.stateVersion=24.11, - power-mgmt collision (his ryzenadj/TLP vs Nomarchy PPD), HM-module→standalone. -- **Decisions (Bernardo):** adopt Nomarchy PPD + drop all ryzenadj; no Secure - Boot (drop lanzaboote); full cutover. -- **Did:** wrote `docs/MIGRATION.md` — generic "existing NixOS → Nomarchy" - guide with TuringMachine as the worked example: candidacy checklist, the - two data-safety rules (never bump system.stateVersion; /home untouched), - Phase 0 snapshots → Phase 1 build-only gate → Phase 2 reversible - `nixos-rebuild test` → Phase 3 reconcile → Phase 4 cutover, triple rollback - net. Verified the downstream API against lib.nix (mkFlake src/username/ - hardwareProfile) + templates/downstream so the flake/system/home examples - are real, not guessed. -- **Verified:** V0 (docs) — referential accuracy against lib.nix + template; - did NOT run the migration commands (they'd alter the live machine). -- **Follow-ups:** PROPOSED — make `home.stateVersion` mkDefault (migrating - users currently need mkForce). -- **Next suggestion:** if Bernardo greenlights, scaffold Phase 1 from his real - hardware-configuration.nix. - -## 2026-07-08 — Calendar on the clock click (iteration #68, item 42) -- **Task:** item 42, de-scoped live by Bernardo — DON'T hook up calendar - accounts; just ship a lightweight calendar that opens instantly on the - Waybar date click, user configures sync themselves. calcurse (his pick; - now the right one since it's self-contained for the user's own sync). -- **Did:** new `nomarchy-calendar` launcher (waybar.nix) → calcurse in a - ghostty window tagged `--class=com.nomarchy.calendar` + - `--gtk-single-instance=false` (fresh standalone window); self-gates on - calcurse. Clock `on-click = nomarchy-calendar`, dropped the format-alt - date-toggle (conflicts with on-click), moved the long date into the - tooltip. Parity: on-click in all four whole-swap jsoncs. windowrule - float+size(60/65%)+center on the class (hyprland.nix). Shipped calcurse - + pwvucontrol **uncommented** in the downstream template (opt-out). - Enforced **ghostty always installed** (ghostty.nix: `enable = true` - unconditional, the nomarchy.ghostty.enable option now gates only config) - so the launcher can rely on `--class`. -- **Verified:** V0 green; all four jsoncs valid JSON; V1 HM built — launcher - ships the right command, generated clock renders `on-click: - nomarchy-calendar` with format-alt gone + date in the tooltip, hyprland - has the 3 calendar windowrules, ghostty present in the HM profile. -- **Pending:** V3 — click the clock → calcurse floats centered (the - `--class`→windowrule match) and renders its month view; verify on a real - session (queued). Sync is deliberately the user's own local config, NOT - a distro feature (Bernardo's call) — a future iteration should not - re-propose account sync. -- **Next suggestion:** Bernardo's big new task — investigate whether THIS - machine (NixOS 25.11) can migrate to Nomarchy without a reinstall, - keeping all files, and plan it. - -## 2026-07-08 — Combined power menu on the bar icons (iteration #67, item 36b) -- **Task:** BACKLOG item 36(b), now unblocked. Bernardo's calls: keep the - granular System ▸ Battery limit / Power profile rows; build ONE combined - menu; open it two ways — the Waybar **battery** icon AND the - **power-profile** icon (the profile icon's click flips from cycle → menu). -- **Did:** New `nomarchy-menu powermgmt` (rofi.nix): a flat list of Profile - rows (→ `powerprofilesctl set`, like the power-profile picker) + Charge - limit rows (→ the same theme-sync writer as batterylimit), each half - self-gating on its hardware; no backend duplication, no Custom flow (that - stays in the dedicated batterylimit picker). Wired `battery.on-click` + - `custom/powerprofile.on-click` → `powermgmt` in waybar.nix AND all four - whole-swaps (parity). Named `powermgmt` to avoid the whole-swaps' existing - `custom/powermenu` (that's the shutdown button → `nomarchy-menu power`, - left untouched). `nomarchy-powerprofile-cycle` kept as a standalone bin - (now just unbound by default). -- **Verified:** V0 green; V1 HM built (bash -n passed) — shipped - nomarchy-menu has the `powermgmt` case; generated waybar-config.json wires - both icons to it (2 hits); all four whole-swap jsoncs re-wired and still - parse as JSON, shutdown buttons intact. -- **Pending:** V3 — click either icon → menu opens, picking a profile / - charge preset actually applies (queued in HARDWARE-QUEUE). The - instant-charge-write follow-up stays PROPOSED/`[blocked:hw]`. -- **Next suggestion:** back to the queue — item 33 (rofi scrollbar) or a - QA sweep; both remaining NEXT items lean human/hardware. - -## 2026-07-08 — Capture rows show Print-key hints (iteration #66, direct requests) -- **Task:** Two live requests from Bernardo. (1) Fold his provided - power-menu mockup into item 36(b). (2) "make sure all menu items that - have a keybind display the keybind" (he dropped the flush-right ask — - inline is fine). -- **Did:** (1) BACKLOG item 36(b) un-gated — mockup + Nomarchy adaptation - (reuse batterylimit/power-profile writers, not raw TLP; wire the Waybar - battery `on-click`; one open call: replace-or-keep the System rows). - (2) Audited every menu/submenu: all SUPER(+CTRL) module-opening rows - already carry a hint (item 28c). The one real gap = the Tools ▸ Capture - rows, which run the exact `grim` commands of the Print / SHIFT+Print / - CTRL+Print binds but showed nothing. Added `hintForBind mods key` (reads - keybinds.nix by mods+key, drift-free) → hints on the three matching rows, - and `-markup-rows` on the Capture menu so the spans render. -- **Verified:** V0 `nix flake check --no-build` green; V1 HM built, shipped - nomarchy-menu shows `Region → clipboard {{Print}}`, `Region → file - {{SHIFT + Print}}`, `Full screen → file {{CTRL + Print}}` under - `-markup-rows`. Substring `case` matches sit before the appended span, so - dispatch is unaffected (same proven pattern as Tools/System). -- **Pending:** V3 — pango dim actually rendering in the Capture submenu - (queued on the existing Capture entry). Power-menu item 36(b) stays - `[human]` on the replace-or-keep call. -- **Next suggestion:** back to the queue head (item 36a done; 32 done) — - next actionable is likely item 33 (rofi scrollbar) or a QA sweep. - -## 2026-07-08 — SUPER+? cheatsheet re-fixed: base keysym (iteration #65, item 32) -- **Task:** BACKLOG item 32 — SUPER+? still didn't open the cheatsheet - after item 26's fix. Re-diagnose. -- **Did:** Root cause = wrong keysym, not path/rofi. Hyprland 0.55 resolves - a bind's keysym with SHIFT *consumed* (issue #7750, since 0.43), so - `$mod SHIFT, question` looks for `slash` and never matches. Item 26 fixed - the modmask but kept the shifted keysym → still dead. Changed keybinds.nix - `question` → `slash` (the BASE keysym, exactly like the working - `$mod SHIFT, 1` workspace binds in this same file); updated rofi.nix - prettyKeys collapse `SHIFT + /` → `?` so the cheatsheet still reads - "SUPER + ?". Dispatch (`nomarchy-menu keybinds`) confirmed already fine. -- **Verified:** V0 `nix flake check --no-build` green. V1: HM activation - built; grepped the built hyprland.conf → `bind=$mod SHIFT, slash, exec, - nomarchy-menu keybinds` (beside the proven `$mod SHIFT, 1`), `kb_layout=us` - confirms slash→? ; `nix eval` of prettyKeys → "SUPER + ?". -- **Pending:** V3 — the keypress actually opening the menu is invisible - below hardware (that's how item 26 slipped); the existing HARDWARE-QUEUE - "SUPER+? opens the cheatsheet" entry updated to the slash fix. -- **Next suggestion:** Bernardo's two live requests — (1) fold the provided - power-menu mockup into item 36(b) [design now in hand]; (2) right-align a - keybind hint on every menu/submenu row that has one. - -## 2026-07-07 — Battery-limit toggle in rofi System (iteration #64, item 36a) -- **Task:** BACKLOG item 36(a) — Bernardo wanted the charge-threshold - toggle as a first-class row in `nomarchy-menu › System`, not buried in - the gum control-center where #55 put it. -- **Fix:** `rofi.nix` — new `batterylimit` subcommand: a preset picker - (80%/90%/60%/Off/Custom…) with battery-0XX icons, writing the baked - `settings.power.batteryChargeLimit` via theme-sync `--no-switch` (lands - on next sys-rebuild; power.nix's oneshot applies it). Added a System row - self-gated on the `charge_control_end_threshold` sysfs node (like Power - profile), dispatch, and usage string. **Moved** — removed the duplicate - "Battery Limit" case + choose entry from `nomarchy-control-center.sh` so - it lives in one place. -- **Verified:** V0 green. V1: control-center builds clean (shellcheck), - home activation builds clean (writeShellScriptBin's `bash -n` on the - menu passed); grepped the built script to confirm the row/dispatch/case/ - guard are all present. Live menu interaction is a V3 check (queued). - -## 2026-07-07 — Floating-window audit, conservative cut (iteration #63, item 41) -- **Task:** BACKLOG item 41 — Bernardo: "take a conservative approach." - Broaden the windowrule float set beyond the mixer. -- **Method:** surveyed what GUI dialogs the distro actually ships (not a - blind list). Network is dmenu/nmtui (no GUI editor → no rule). Confident, - shipped, unambiguous dialogs: **blueman** (manager + adapters) and - **system-config-printer**. Deferred polkit (hyprpolkitagent exposes no - discoverable class) and GTK file portals — guessing a class = dead rule, - so those wait for a hardware `hyprctl clients` read. -- **Fix:** `hyprland.nix` windowrule block — added float + center for the - two, `(?i)` regex tolerating the XWayland `.…-wrapped` form. RE2 `(?i)` - is already relied on in this file (line ~47), so it's consistent. -- **Verified:** V0 green; `nix eval` confirms the rendered regex escaping - is correct (single backslash, `?` quantifier unescaped). Whether the - classes actually match live is V3 (queued — includes confirming the - regex catches the real class). - -## 2026-07-07 — Right-click volume → floating mixer (iteration #62, item 35) -- **Task:** BACKLOG item 35 — right-click the Waybar volume module to - open a mixer in a floating window. (Bernardo re-launched /loop after I'd - flagged it as gated on 41 — read as "handle it," so I did 35 fully and - re-scoped 41 to just broadening the float set.) -- **Chose pwvucontrol** (PipeWire-native, GTK4/libadwaita → themes via the - portal like Amberol; distro is already wpctl/PipeWire). Verified app-id - `com.saivert.pwvucontrol` from the built package. -- **Changes:** (1) `waybar.nix` pulseaudio `on-click-right = "pwvucontrol"` - + parity into all four whole-swap jsoncs (summer-day/night, boreal, - executive-slate). (2) `hyprland.nix` — the config's **first** `windowrule` - block: `float` + `center` for the mixer classes (pwvucontrol + pavucontrol - fallback), normal-priority list so downstream concatenates. (3) - `pwvucontrol` added to the template `home.packages`. -- **Verified:** V0 green; `nix eval` of the homeConfiguration confirms the - windowrule regex renders with correct single-backslash escaping - (`class:^(com\.saivert\.pwvucontrol|…)$`). The right-click→float→center - interaction is a V3 session check (queued). Item 41 now = broaden the set. - -## 2026-07-07 — Window focus: vim keys → arrows (iteration #61, direct request) -- **Task:** Bernardo — switch window navigation from vim-style - SUPER+H/J/K/L to SUPER+arrow keys. -- **Fix:** `modules/home/keybinds.nix` focus binds now use - `left/right/up/down` (movefocus l/r/u/d); H/J/K/L freed. No conflict — - SUPER+arrows were unbound and there were no vim *move-window* binds. - Also taught `prettyKeys` (rofi.nix) to render the arrow keysyms as - glyphs (← → ↑ ↓), so the SUPER+? cheatsheet reads "SUPER + ←" not - "SUPER + left" (Return etc. unaffected — no false substring match). -- **Verified:** V0 green; `nix eval` confirms the binds render as - `$mod, left, movefocus, l` … and the glyph mapping is correct. - -## 2026-07-07 — Audio mime → Amberol (iteration #60, item 37) -- **Task:** BACKLOG item 37 — default video + audio players + mime. On - inspection both players were already installed: mpv (video) and amberol - (a prior template add). So the real gap was the *mime wiring* — audio - files still opened in mpv (mime.nix mapped audio/* → mpv.desktop). -- **Fix:** `modules/home/mime.nix` — repoint the audio/* types to - `io.bassi.Amberol.desktop` (verified against the built package's - actual .desktop id + its MimeType set) and broaden coverage (mp3/flac/ - ogg/opus/wav/m4a/aac, both canonical and x- names). Video stays mpv. -- **Why Amberol:** GTK4/libadwaita, so it themes via the portal - color-scheme the distro already wires; minimal, purpose-built for - "open a file → play." Degrades to mpv if a downstream drops it. -- **Verified:** V0 green; entries mirror the existing working image/video - mappings, desktop id confirmed from the realised package. Runtime GIO - resolution (double-click an mp3 → Amberol) is a V3 session check (queued). - -## 2026-07-07 — Capture-to-file keybinds (iteration #59, item 38) -- **Task:** BACKLOG item 38 — direct keybinds for region→file and - screen→file (the Capture *menu* already had both "→ file" rows). -- **Fix:** `modules/home/keybinds.nix` — added `SHIFT+Print` (region→file) - and `CTRL+Print` (screen→file) alongside the existing bare `Print` - (region→clipboard). Each saves a timestamped PNG under - ~/Pictures/Screenshots and toasts the path — same grim/slurp plumbing + - dir the menu's "→ file" rows use. Single-source, so the SUPER+? - cheatsheet (rofi.nix reads the same list) gets both rows automatically. -- **Verified:** V0 green. `nix eval` of the rendered binds confirms - `$HOME`/`$f`/`$(...)` pass through literally to the shell (no Nix - interpolation). Actual key-firing + file landing is a V3 check (queued). - -## 2026-07-07 — rofi search mis-ranks + power-profile icons (iteration #58, items 31 & 39) -- **#31 (rofi search broken):** Bernardo ran the live A/B — `-sorting-method - normal` and `-matching normal` both fixed "steam"→Steam; `-no-sort` did - not. So the culprit is rofi 2.0.0's **fzf sorter** paired with fuzzy - matching (it ranked ghostty above Steam), not case-sensitivity. Fix: - `programs.rofi.extraConfig.sorting-method` `"fzf"` → `"normal"`, keeping - `matching="fuzzy"` (forgiveness) + `case-sensitive=false`. Also closes - Bernardo's batch item 1 (case-insensitive — already false, now ranks - right too). -- **#39 (power-profile icons):** the picker listed profiles as bare text. - Now emits `row "$p" battery-profile-

` using Papirus' **colored** - `battery-profile-{performance,balanced,powersave}` family (the - `-symbolic` variants are #444 grey → invisible on dark themes; verified - the colored ones carry real fills). Returned row text stays the bare - profile name, so `powerprofilesctl set` is unaffected; added `-show-icons`. -- **Verified:** V0 `nix flake check --no-build` green for both. #31's fix - is the exact lever Bernardo confirmed live. #39's icon *rendering* is a - V3 visual check (Papirus lookup can't be eyeballed headlessly) — queued. - -## 2026-07-07 — Display menu "Back" doesn't return (iteration #57, item 40) -- **Task:** BACKLOG item 40 — in System › Display the Back row didn't - return on a single-monitor machine. -- **Root cause:** the mode picker's Back did `exec "$0" display`. On a - single-monitor laptop with no display profiles, the output chooser is - skipped, so re-running `display` drops straight back into the *same* - mode picker — an apparent no-op. (Multi-monitor works: `display` - re-shows the chooser, a genuine previous level.) -- **Fix:** `modules/home/rofi.nix` display handler now tracks `modeBack` — - `display` when the chooser was shown, else `system`. Mode-picker Back - does `exec "$0" "$modeBack"`, so single-monitor Back leaves Display for - System. -- **Verified:** V0 `nix flake check --no-build` green. Logic is - deterministic; the single- vs multi-monitor branch is the whole fix. - -## 2026-07-07 — Menu "Back" double arrows (iteration #56, item 34) -- **Task:** BACKLOG item 34 — every submenu's `↩ Back` row drew two - arrows: the `↩` glyph in the label AND a themed `go-previous` icon. -- **Fix:** `back()` in `modules/home/rofi.nix` was `row "$BACK" - go-previous` (label + icon). Changed to `printf '%s\n' "$BACK"` — now - identical to what the plain pick-lists already append, so every Back row - shows a single `↩` glyph. Kept `BACK="↩ Back"` as the universal - label/match, so no case-branch comparison changed (no risk of a Back - that fails to return — the item-40 failure mode). Collision-exact match - preserved by the glyph. -- **Verified:** V0 `nix flake check --no-build` green (evaluates - homeConfigurations). Single-arrow result is deterministic from the code; - visual confirmation on hardware is nice-to-have, not load-bearing. - -## 2026-07-07 — Battery charge-limit quick toggle (iteration #55) -- **Task:** BACKLOG PROPOSED (promoted by Bernardo) — a toggle for the - battery charge threshold. -- **Did:** Replaced the control-center "Battery Limit" raw number-prompt - with a preset toggle picker (Off / 80% recommended / 90% / 60% / - Custom…) writing `settings.power.batteryChargeLimit`. Kept it in the - control-center — that's where the baked, rebuild-valued System Toggles - live (rofi `nomarchy-menu` is the *instant* surface; the CC holds - config values); it's reachable from the menu via System › Control - Center. Base machinery (sysfs oneshot + AC-replug udev in power.nix) - untouched. -- **Verified:** V0 (`bash -n`; `nix flake check --no-build`, green) + V1 - (nomarchy-control-center package builds → the writeShellApplication - shellcheck gate passes). -- **Pending:** instant-effect (no rebuild) refiled in PROPOSED as - `[blocked:hw]` — needs a udev-writable sysfs node, confirmable only on - a laptop with the charge-threshold control. Light session spot-check - queued in HARDWARE-QUEUE. -- **Next suggestion:** item 29 is `[human]`/`[blocked:hw]`; remaining is - item 28 slices or a QA sweep. - -## 2026-07-07 — Waybar overlaps fullscreen video (iteration #54) -- **Task:** BACKLOG item 30 — bar drawn on top of a fullscreen YouTube video. -- **Did:** `layer: top` → `bottom` in the generated `waybar.nix` and the - four whole-swap jsoncs (boreal, summer-night, summer-day, - executive-slate — parity rule). On Hyprland the top layer renders above - even real-fullscreen surfaces; bottom lets the fullscreen window cover - the bar while the exclusive zone still reserves its space in normal - tiling. Rationale comment in waybar.nix; data jsoncs left comment-free - (they carry none). Also shipped this session (pre-loop, user-driven): - battery discharging-icon gap widened (f848e73). -- **Verified:** V0 (`nix flake check --no-build`, green) + V1 - (`homeConfigurations.nomarchy.activationPackage` builds; the built - `.config/waybar/config` shows `"layer": "bottom"`). -- **Pending:** V3 queued in HARDWARE-QUEUE — fullscreen video covers the - bar, bar returns on exit, floating-overlap trade-off. Can't be - eyeballed headlessly here. -- **Next suggestion:** item 29 is `[human]`/`[blocked:hw]`; remaining - work is item 28 (theme UI) slices or a QA sweep. - -## 2026-07-06 — menu parity for downstream-flake options (iteration #51, slice 1) -- **Task:** PROPOSED item — ensure `nomarchy.*` options have a menu toggle where appropriate. -- **Did:** Audited options and implemented the first slice of missing toggles in the TUI Control Center (`Updates`, `Battery Limit`, `Bluetooth`, `Printing`). Wired `modules/home/options.nix` and `modules/nixos/options.nix` to use `config.nomarchy.settings.*` as defaults. -- **Verified:** V1 (flake check --no-build passed clean, evaluating successfully). -- **Pending:** user visual review and testing. -- **Next suggestion:** triage other PROPOSED items. - -## 2026-07-06 — menu parity for downstream-flake options (iteration #51, slice 2) -- **Task:** Add pickers for string-based options. -- **Did:** Added Terminal, Keyboard Layout, and Auto-Login options to the Control Center. Wired them to the Nix options via `config.nomarchy.settings.*`. -- **Verified:** V1 (flake check --no-build). - -## 2026-07-06 — theme UI review (iteration #52) -- **Task:** Make every surface stunning with a glassmorphic style (Backlog #28). -- **Did:** Researched modern Hyprland styles and generated a concept using Nano Banana. Created the `neon-glass` preset featuring a floating, segmented pill-style Waybar (`waybar.css`) and a glassmorphic Rofi launcher (`rofi.rasi`). Generated a matching 4K wallpaper. -- **Verified:** Contrast checker script passed (22 themes x 7 pairings green). Flake check passed cleanly. - -## 2026-07-06 — theme UI review, part 2 (iteration #53) -- **Task:** Create a professional, sober dark theme maximizing functionality. -- **Did:** Generated a minimalist geometric concept wallpaper. Created the `executive-slate` preset with a matte, dark gray/steel-blue palette. Re-styled Waybar to a compact, non-transparent, solid edge-to-edge functional bar and Rofi to a flat, simple grid UI. -- **Verified:** Contrast checker script passed (23 themes x 7 pairings green). Flake check passed cleanly. - -## 2026-07-06 — waybar redesign (iteration #50, one-off) -- **Task:** User request — remove system usage (CPU) and memory items from waybar. -- **Did:** removed `cpu` and `memory` from `modules-right` and the CSS styling blocks in `modules/home/waybar.nix`. -- **Verified:** V1 (flake check --no-build passed clean). -- **Pending:** user visual review. -- **Next suggestion:** triage PROPOSED. - -## 2026-07-06 — control center implementation (iteration #49, item 18) -- **Task:** BACKLOG NEXT#18 — implement TUI Control Center using `gum`. -- **Did:** created `nomarchy-control-center.sh` and packaged it in `pkgs/nomarchy-control-center`. Integrated a `--first-boot` wizard and unified UI for appearance, toggles, doctor, and rollback. Added the package to `flake.nix` and `modules/nixos/default.nix`. Added "Control Center" to the System menu in `rofi.nix`. -- **Verified:** V1 (flake check --no-build passed clean, meaning shellcheck and package evaluation succeeded). -- **Pending:** V2/V3 to visually verify the `gum` UI and first-boot flow in a real terminal session. -- **Next suggestion:** watch lock-bump CI (item 14) or triage PROPOSED. - -## 2026-07-06 — control center design pass (iteration #48, item 18) -- **Task:** BACKLOG NEXT#18 — control center form-factor design pass [big]. -- **Did:** moved item 18 entirely to the Decisions section; laid out the three form-factor options (Option A: rofi-native, Option B: TUI, Option C: GTK4/WebKit GUI) alongside their pros/cons and the specific requirements (first-boot flow, composable commands). -- **Verified:** V0 (docs-only); `nix flake check --no-build` passed pre-commit. -- **Pending:** the `[human]` decision on the form-factor. -- **Next suggestion:** #14 (watch lock-bump CI) or #20 KVM runner [human]. If none actionable, triage PROPOSED or do a QA sweep. - -## 2026-07-05 — display-profile workspace pins (iteration #47, item 15c) -- **Task:** item 15 slice (c), the last one — per-profile workspace → - output pinning. Item 15 deleted (a/b/c all shipped; V3s queued). -- **Did:** a profile is now `{ monitors; workspaces = { "1" = "DP-3"; }; }` - (bare list still accepted — either-type + normalization in - hyprland.nix; coercedTo refuses list-of-submodule sources). Pins bake - as Hyprland `workspace` rules and apply instantly on profile switch - (`hyprctl keyword workspace` + `dispatch moveworkspacetomonitor`). - monitor-rules.nix gained pure `workspaceRule`; template + README - updated. -- **Verified:** V1 flake check green (incl. the extended - checks.display-profiles assert) + scratch-downstream eval: baked - settings render both rule sets, the generated applier carries the - keyword+dispatch pairs (bash -n OK), match/list still correct with - mixed profile shapes. -- **Pending:** V3 queued (workspace-pins entry in HARDWARE-QUEUE, rides - the slice-a dock test). Stale-pin note documented in the applier. -- **Next suggestion:** backlog is now human/event-gated (28 taste - calls, 14 Monday, 18 Decision, 20 KVM) — idle the loop or triage - PROPOSED. - -## 2026-07-05 — icon-weight A/B for the human call (iteration #46, item 28) -- **Task:** item 28's last `[human]` blocker — make the color-vs- - symbolic menu-icon decision cheap. -- **Did:** captured the lumon root menu with `-symbolic` icon names - (temporary uncommitted rofi.nix edit, reverted after) and published - a side-by-side artifact (link in the BACKLOG bullet). Findings: - symbolic matches the bar's weight, but Papirus symbolic coverage - has holes and rofi tints symbolics fixed gray — B needs an - audit+recolor slice; A (color) is the free default. -- **Verified:** V2 for the experiment itself (both variants VM- - captured); no repo code changed — tree is clean apart from agent/. -- **Pending:** Bernardo's call on the artifact. Item 28 is now fully - agent-side done: all remaining bullets are `[human]`/hardware. -- **Next suggestion:** general loop — item 15 slice (c) (per-profile - workspaces) is the topmost actionable code item; 14 fires Monday. - -## 2026-07-05 — purge stub waybar.css whole-swaps (iteration #45, item 28c/d) -- **Task:** item 28 — lumon vibe capture exposed four themes shipping - an unstyled default bar. -- **Did:** deleted the stub waybar.css in catppuccin/lumon/nord/ - retro-82 (2–14 lines of color defines from the f211ef0 rewrite - import; a theme waybar.css replaces the WHOLE generated stylesheet, - so the stubs nuked all bar styling). The four themes now get the - generated bar in their own palette; their full rofi.rasi designs - kept. Whole-swap bars are now only summer-day/night, both authored. -- **Verified:** V2 — lumon re-capture shows the generated styling - (workspace pill, padded right cluster, no edge clipping); V1 flake - check green. catppuccin/nord/retro-82 share the mechanism, not - individually captured. -- **Pending:** HARDWARE-QUEUE eyeballs; the `[human]` icon-weight call. -- **Next suggestion:** item 28 is agent-side done pending Bernardo's - taste calls — park it; take the next actionable BACKLOG item. - -## 2026-07-05 — capture harness paints wallpapers (iteration #44, item 28c) -- **Task:** item 28c harness gap — captures rendered a bare base-color - backdrop because the guest had no `~/.nomarchy` runtime state. -- **Did:** tools/theme-shot.nix now seeds theme-state.json via tmpfiles - at boot (before greetd), so the session's exec-once - `nomarchy-theme-sync wallpaper` paints naturally; dropped the - testScript seed+manual-repaint (too late for exec-once; manual runs - also lack WAYLAND_DISPLAY, which awww's client needs to find its - socket). -- **Verified:** V2 — summer-night capture shows wallpaper + cream - inverted bar + menu + placeholder dim, all coherent. V1 flake check - green. -- **Pending:** identity-theme vibe capture (lumon/retro-82); the - `[human]` icon-weight call; HARDWARE-QUEUE eyeballs. -- **Next suggestion:** item 28c identity capture, or park 28 for - Bernardo's taste calls. - -## 2026-07-05 — clock zone tooltip (iteration #43, LATER item) -- **Task:** LATER › auto-timezone Waybar tooltip. Bullet deleted. -- **Did:** the clock tooltip's first line is now the zone — - `{:%Z (UTC%z)}` above the calendar — in the generated bar AND both - summer whole-swaps (parity). Under auto-timezone the existing - tz-watch SIGUSR2 reload keeps it current, so it shows the detected - zone live. (%Z abbreviation, not the IANA name — strftime has no - code for that; good enough for an "(optional)" nicety.) -- **Verified:** V0; V1 — rendered generated config carries the new - tooltip-format; both summer jsonc parse (jq) with %Z present. - Hover render → V3 queued. -- **Pending:** V3. LATER now holds only deferred/big/hw/human items. -- **Next suggestion:** a QA sweep, or stop-and-wait — the queue is - effectively drained for unattended work; meaty LATER items - (wallpapers split is *deferred by decision*, installer round 2 - [big]) deserve Bernardo's go-ahead or a fresh session. - -## 2026-07-05 — doctor bar tripwire (iteration #42, LATER item) -- **Task:** LATER › doctor Waybar warning. Bullet deleted. -- **Did:** nomarchy-doctor-status (named bin, PATH): silent exit 0 - while the doctor is healthy or absent (self-gate), else JSON with - the first ✖ lines jq-escaped into the tooltip, in @bad; click = - nomarchy-menu doctor (the sheet). custom/doctor added to the - generated bar (after battery, interval 300 — the check is real - work, this is a tripwire not a monitor) + CSS, AND both summer - whole-swaps (module list, jsonc block, CSS membership + @red rule) - per the parity rule. -- **Verified:** V0; V1 — template-home builds, status bash -n, config - + CSS each carry the module, both summer jsonc still parse (jq) - with the block; FUNCTIONAL — self-gate silent, and a shimmed - failing doctor produces valid JSON (jq-verified: class bad, ✖ - lines in tooltip). Live render → V3 queued. -- **Pending:** V3. LATER remaining: wallpapers-artifact split - (deferred by decision), installer round 2 [big], boot-from- - snapshot, night-light geo, icon overrides, MIPI camera [hw], - auto-timezone tooltip, VPN richer display, release bump [human]. -- **Next suggestion:** auto-timezone Waybar tooltip (small) — or a - QA sweep; the meatier LATER items deserve a fresh session. - -## 2026-07-05 — OCR screenshot-to-text (iteration #41, LATER item) -- **Task:** LATER › OCR (NEXT is fully human/event-gated, so LATER is - in play per LOOP.md). Bullet deleted. -- **Did:** Capture › "OCR region → clipboard" (self-gated on - tesseract): slurp (Esc = silent cancel, the recording pattern) → - grim → tesseract stdin/stdout → wl-copy, whitespace-only results - toast instead of clobbering the clipboard; success toasts the word - count. tesseract ships eng-only via rofi.nix home.packages - (override documented — unscoped it drags every language's - traineddata, real cost on a compile-from-source distro). -- **Verified:** V0; V1 — template-home builds, menu bash -n, gated - row + case present; FUNCTIONAL — the profile's own tesseract read a - rendered test image back verbatim ("Nomarchy OCR smoke test 42"), - so the OCR half of the pipe is proven; grim/slurp half needs a - session → V3 queued. -- **Pending:** V3. -- **Next suggestion:** LATER: doctor Waybar warning (self-gating bar - indicator fed by nomarchy-doctor) or auto-timezone tooltip; both - small. - -## 2026-07-05 — Look & Feel menu category (iteration #40, item 19) -- **Task:** BACKLOG NEXT#19 — its ≥3-entries gate is met (Theme, Next - wallpaper, Night light). Item deleted. -- **Did:** root's Theme row → "Look & Feel" (pango-escaped — - -markup-rows menus treat & as an entity; matched on *Look*); new - lookfeel mode: Theme (SUPER+T hint), Next wallpaper (SUPER+SHIFT+T - hint via the new generalized hintForAction), Night light row moved - from System (System thins by one). Root stays six. menuHint is now - sugar over hintForAction. -- **Verified:** V0; V1 — menu bash -n, lookfeel mode + escaped label - + night light relocated (4 occurrences, all in lookfeel/nightlight - paths); VISUAL — tokyo-night capture: root renders "Look & Feel" - correctly with six rows and intact hints. Submenu flow → V3 - queued. -- **Pending:** V3. NEXT now: 28 (Bernardo-gated) · 14 (Monday watch) - · 18 [big, Decision-gated] · 20 [human] — the LATER tier or QA - sweeps are next for the loop. -- **Next suggestion:** LATER's OCR screenshot-to-text (two-row - Capture addition, self-contained) — or a QA sweep. - -## 2026-07-05 — launch-or-focus binds (iteration #39, item 17) -- **Task:** BACKLOG NEXT#17 (the UI review is Bernardo-gated; back to - the queue head). Item deleted. -- **Did:** nomarchy.launchOrFocus (listOf submodule: key/mods/class/ - command/desc; opt-in, template example) → hyprland.nix generates - binds through a shared nomarchy-focus-or-launch script (jq - case-insensitive class match → focuswindow "(?i)" regex, else - launch; uninstalled command → toast, so binds survive suite edits); - rofi.nix renders the same entries into the SUPER+? cheatsheet via - prettyKeys (single source). README row. -- **Verified:** V0; V1 — scratch downstream with 2 entries: both - binds rendered (default command = lowercased class; custom - mods/command/desc honored), tool bash -n + usage exit 64, - cheatsheet rows present. Focus behaviour needs a session → V3 - queued. -- **Pending:** V3. Queue after this: #18 control center [big, needs - the form-factor Decision], #19 Look&Feel menu (gated on ≥3 - entries), #20 [human], LATER tier. -- **Next suggestion:** a QA sweep (#18/#19/#20 are blocked-ish) — or - item 19's precondition check (does Look & Feel earn 3 entries - yet?). - -## 2026-07-05 — placeholder dim + latte review + backdrop probe (iteration #38, item 28c) -- **Task:** item 28 slice (c) — the summer-rasi quickie + the light- - theme capture. -- **Did:** both summer rasi: placeholder-color @bg0 → bg0 at 60% - alpha (#…99), so prompt and placeholder no longer read as one - phrase. Captured catppuccin-latte at 1080p: the generated menu is - COHERENT on light themes (zebra rows on the 28b-retuned surface, - hints dimmed) — closes the audit's "dark terminal on latte" - concern as a stale preview. Backdrop suspicion probed with a - dedicated VM (hyprctl getoption): misc:background_color IS set - (0xFFEFF1F5) — awww's empty-state layer occludes it in the - wallpaper-less VM; on hardware the fix covers the startup gap as - intended. -- **Verified:** rasi edits grep-verified (rasi parses at rofi - runtime; visual rides the next summer capture / V3); latte menu - capture reviewed; probe output above. -- **Pending:** item 28 remaining: icon-weight `[human]`, hardware - eyeballs (queued), identity-theme capture (optional), slice (d) - needs nothing (parity verified). -- **Next suggestion:** item 28 is now Bernardo-gated (taste calls + - V3); the loop should return to the general BACKLOG head (item 14 - watch / item 17 launch-or-focus). - -## 2026-07-05 — rofi keybind hints (iteration #37, item 28c) -- **Task:** item 28 slice (c) — the hint column, the audit's biggest - remaining UX item. -- **Did:** rofi.nix: menuHint builds a dimmed pango span - (size small, alpha 55%) from keybinds.nix via the cheatsheet's - prettyKeys — single source, hints can't drift from the live binds; - applied to root (Apps/Theme/Power/Keybindings; Apps special-cased, - it's rofi drun not a menu module), Tools (all eight leaves), System - (Network/Bluetooth/DND); those three menus now pass -markup-rows. - Contains-glob matching and the exact "↩ Back" match are unaffected - (labels keep their prefix; Back stays markup-free). -- **Verified:** V0; V1 rendered menu bash -n + hint spans present + - 3× -markup-rows; VISUAL — tokyo-night capture: hints render dimmed - and subordinate on normal AND selected (accent) rows; Tools/System - rows correctly bare. -- **Pending:** slice-c remainder: summer-rasi placeholder dim, - latte/identity captures, icon-weight `[human]`. -- **Next suggestion:** the placeholder-dim quickie + a latte capture - in one tick; then item 28 pauses for Bernardo's icon-weight call - and the hardware eyeballs. - -## 2026-07-05 — bar group rhythm + @muted adoption (iteration #36, item 28c) -- **Task:** item 28 slice (c) — the two queued bar polish items. -- **Did:** waybar.nix generated CSS: margin-left 14px on the three - right-cluster group heads (pulseaudio/powerprofile/battery — media - · toggles · status; hidden heads degrade to uniform spacing); - workspaces buttons, dnd bell, and muted volume moved from - alpha(@text,.5) tints to the semantic @muted role — safe since 28b - floor-gates muted/base ≥ 2.0 (the old comment warning against - @muted documented the pre-28b world; rewritten to state the new - invariant). Whole-swap bars untouched (authored identity). -- **Verified:** V0; V1 rendered CSS has 4 @muted uses + the margin - rule; VISUAL — tokyo-night re-capture: themed backdrop confirmed - (navy base, not black), group gap visible before the volume group, - workspace pill legible. Cross-theme minima are guaranteed by the - gated checker rather than per-theme eyeballs. -- **Pending:** slice-c remainder: rofi hint column, summer-rasi - placeholder dim, icon-weight `[human]`, latte/identity captures. -- **Next suggestion:** rofi keybind-hint column (the biggest - remaining UX win), or the two-line placeholder nit as a quickie. - -## 2026-07-05 — summer-day capture review + themed backdrop (iteration #35, item 28c) -- **Task:** item 28 slice (c) — whole-swap capture review + the first - cheap polish fix. -- **Did:** captured summer-day at 1080p (harness, VM): the authored - slate menu + island-pill bar hold up with the 28b retunes — - initially misread as polarity soup, but the rasi whole-swap - deliberately mirrors the bar's slate strip: identity, keep (nit - filed: undimmed entry placeholder). The surviving cross-theme fix: - Hyprland `misc:background_color` = theme base (was compositor - black — a glitch-looking flash behind missing/slow wallpaper, - glaring on light themes). Punch list updated. -- **Verified:** V0; V1 — template-home rebuilt, rendered hyprland.conf - carries `background_color=rgb(1a1b26)` (theme base). Backdrop - visible confirmation rides the next capture run / V3. -- **Pending:** captures for latte + one identity theme; polish items - queued: bar group margins, @muted adoption, rofi hint column, icon - weight `[human]`. -- **Next suggestion:** bar polish slice (group margins + @muted) — - code-level, verifiable by build + re-capture. - -## 2026-07-05 — 1080p capture harness + first slice-c review (iteration #34, item 28c) -- **Task:** item 28 slice (c) prep — full-res captures for the - per-surface polish pass. -- **Did:** tools/theme-shot.nix (maintainer tool, THEME env-driven): - full themed desktop headless at 1920x1080 via the memory recipe + - two fixes over the spike — home-manager.useGlobalPkgs (HM submodule - otherwise misses the overlay → 'nomarchy-theme-sync missing') and a - menu.png shot (hyprctl dispatch exec nomarchy-menu). Proven on - tokyo-night; both shots read. Findings → item 28 punch list: rofi - root width vs empty space (→ keybind-hint column idea), icon-weight - mix (color Papirus vs mono bar) `[human]`, bar group margins - confirmed, alpha-tint dim vs @muted role. Whole-swap module parity - verified textually (no drift; cpu/memory omission is identity). -- **Verified:** harness runs end-to-end (exit 0, screenshots read); - eval from tools/ with relative flake path OK. Wallpaper still - doesn't paint (theme-sync needs ~/.nomarchy in the guest — fix - noted in the header for the next run). -- **Pending:** capture summer-day/night (whole-swaps) + latte; - then the polish slices; icon-weight call is Bernardo's. -- **Next suggestion:** run the retuned-theme captures + start the - cheap polish items (group margins, @muted adoption). - -## 2026-07-05 — P2 floors retuned + gated (iteration #33, item 28b done) -- **Task:** item 28 slice (b) remainder — the P2 contrast floors. -- **Did:** 18 hex retunes across 9 themes: muted raised on lumon/ - white/retro-82/everforest (OKLCH hue-preserving minimal solve to - ≥2.05) + gruvbox→bg4 #7c6f64, nord→#616e88 (community standard), - latte→overlay1 #8c8fa1; text-on-surface via surface nudges - (ristretto darker, latte lighter); accentAlt darkened (latte, - summer-day); warn darkened on the light themes (latte, summer-day, - rose-pine-dawn) and flexoki-light's four statuses moved to the - canonical 600 series. check-theme-contrast.py gates all four - adopted floors (muted ≥2, text/surface ≥4.5, accentAlt ≥3, warn - ≥2.5 — warn is the battery tint); good/bad stay audit-only - (identity), so no exemption mechanism was needed. Slice (b) done. -- **Verified:** V0 + checks.theme-contrast GREEN (21×7); re-audit: - contrast findings 20→1 (miasma bad = identity); flake check green. - Rendering is V3 → queued (P2 eyeball entry). -- **Pending:** slices (c) per-surface polish (needs 1080p captures) - and (d) whole-swap bar parity; ansi[] conventions decision still - open (audit notes). -- **Next suggestion:** slice (c) prep — 1080p captures via the VM - recipe, or hardware captures if Bernardo prefers eyeballing first. - -## 2026-07-05 — P1 palette retunes + gated subtext floor (iteration #32, item 28b) -- **Task:** item 28 slice (b), P1s from the #31 audit. -- **Did:** summer-day: surface #5c6a72(=text!)→#efe7d0 raised cream, - subtext #fdf6e3(=base!)→#6e828a slate; flexoki-light to canonical - Flexoki tones: surface #E6E4D9(ui), overlay #B7B5AC, subtext - #6F6E69(tx-2), muted #878580(base-500); miasma surface #000→#2e2e2e; - kanagawa surface #090618→#2a2a37 (upstream sumiInk4 — old value was - sumiInk0, darker than bg). check-theme-contrast.py gains the adopted - `subtext/base ≥ 3` pairing (the item-25/27 trap, now impossible). - Model note recorded: `overlay` is a mid-tone role, not a bg tier. -- **Verified:** V0 + checks.theme-contrast GREEN (21×3 pairings); - re-audit: all four P1 findings gone (kanagawa now CVD-only findings); - V1 — scratch downstream with summer-day ACTIVE builds, new surface - hex present in rendered ghostty config. Looks are V3 → queued - (four-theme eyeball). -- **Pending:** 28b remainder: P2 floors (muted ≥2, warn ≥2.5, - text-on-surface ≥4.5, accentAlt ≥3) + exemption-list mechanism. -- **Next suggestion:** 28b P2s (7 muted retunes + 4 warn + 2 - text-on-surface + 2 accentAlt, then gate with exemptions). - -## 2026-07-05 — theme design audit (iteration #31, item 28 slice a) -- **Task:** BACKLOG NEXT#28 slice (a) — the design-theory audit - (human-requested; Bernardo asked to run it now). -- **Did:** tools/audit-theme-design.py (new, report-only): OKLCH - lightness architecture, extended WCAG pairs, status-hue families, - accent harmony, Machado protan/deutan simulation, ANSI slot - semantics — over all 21 palettes; plus a visual pass over 4 - representative preview.png captures. Findings curated - identity-aware into a P1–P3 punch list appended to item 28: 2 - invisible-subtext palettes (summer-day, flexoki-light), 2 inverted - bg stacks (miasma, kanagawa), 7 muted-floor misses, warn-barely- - reads ×4, a systemic "status is never color-only" rule (CVD - collapse is inherent, glyph redundancy is the fix), explicit - identity exemptions, light-theme ANSI convention decision, bar - right-cluster grouping rhythm, latte dark-terminal verify. -- **Verified:** V0 (py_compile; report tool, not wired into checks). - Analysis is palette-math + thumbnail previews; per-surface - spacing/type audit needs 1080p captures (slice c prep). -- **Pending:** slice (b) — palette retunes + checker extension w/ - exemption list; needs Bernardo's eye on the P1 retunes (visual - taste calls). Artwork needs none so far — no image-model ask yet. -- **Next suggestion:** slice (b) P1s (summer-day/flexoki subtext+ - surface, miasma/kanagawa stacks) — small hex edits, big payoff. - -## 2026-07-05 — greeter theming from the JSON (iteration #30, item 16) -- **Task:** BACKLOG NEXT#16 — tuigreet themed from theme-state.json at - system rebuild (the Plymouth model). Item deleted. -- **Did:** new modules/nixos/greeter.nix owns the greetd block (moved - out of default.nix, one concern one file) + the theming: (1) - console.colors = the theme's ansi[] (mkDefault; lands as - vt.default_* kernel params — also themes raw ttys + LUKS prompt); - (2) tuigreet --theme on NAMED slots (its parser is ratatui - Color::from_str, verified in source; names hit the standard indexes - the retinted VT palette now serves). Sparse state w/o ansi → skips - cleanly. -- **Verified:** V0; V1 — template-system toplevel: baked greetd.toml - carries the full --theme spec, kernel-params carry all three - vt.default_{red,grn,blu}. A checks.greeter VM test was attempted and - DROPPED: tuigreet dies under runNixOSTest even bare (harness limit, - not our flag — bisected; nixpkgs tests use agreety) → MEMORY.md. -- **Pending:** V3 queued (rendering + theme-follows-rebuild). -- **Next suggestion:** #17 launch-or-focus, or the new #28 UI-review - audit slice once Bernardo confirms its placement. - -## 2026-07-05 — display profiles auto-switch (iteration #29, item 15b) -- **Task:** BACKLOG NEXT#15 slice b — opt-in hotplug auto-switch. -- **Did:** nomarchy-display-profile gains `match ` (exact set - → else unambiguous largest subset → ties/none = exit 1); - nomarchy-display-profile-watch polls `hyprctl monitors all` every 3s - (exec-once, the keyboard-watcher pattern — session units race - Hyprland IPC), reacts only to CHANGES, reads settings.displayProfileAuto - LIVE each change (instant menu toggle, no rebuild) and applies via - the tool, so auto-picks persist concrete profiles that rebuilds - bake. Chose a separate boolean over an "auto" pseudo-profile for - exactly that bake semantics. Menu: Auto-switch (on/off) row in - Profiles. Template + README notes. -- **Verified:** V0; V1 — scratch 4-profile downstream builds; both - scripts bash -n; match matrix exercised: exact ×3, unambiguous - subset, tie → none, no-fit → none; watcher exec-once present; menu - renders with the row. Real hotplug needs a session+dock → V3 (rides - the slice-a queue entry). -- **Pending:** V3. Slice c (workspace binding) remains, tagged - optional. -- **Next suggestion:** #16 greeter theming — item 15's remaining - slice c is optional; a fresh item keeps variety. - -## 2026-07-05 — display profiles slice a (iteration #28, item 15a) -- **Task:** BACKLOG NEXT#15 slice a, per the #27 design: option + - overlay + applier + menu + template example. -- **Did:** nomarchy.displayProfiles (attrsOf (listOf monitorType)); - monitor logic extracted to pure modules/home/monitor-rules.nix - (rule renderer + 3-layer resolve: profile replaces whole-by-name → - resolution picks field-level with the disable-guard → wildcard), - hyprland.nix consumes it and gains nomarchy-display-profile - (list/active/apply/base; apply = baked hyprctl rules + state write - --no-switch, base = clear + hyprctl reload) on PATH only when - profiles exist; menu: Display gains a gated "Profiles ›" row + new - display-profile mode (●/○ active mark, Base layout row); template - example + README row. checks.display-profiles unit-tests resolve - (incl. junk-state degradation) — the first HM-eval fixture attempt - hit "path not valid" (eval store won't realise writeText/toFile - under flake check), hence the pure-function extraction. -- **Verified:** V0 (check's asserts force at eval); V1 — scratch - downstream through lib.mkFlake builds: tool bash -n + correct baked - rules (disable, vrr, floats), hyprland.conf shows the exact overlay - (wildcard + kept base + disabled panel + profile output), menu bash - -n, list/usage smoke. Live switching needs a session+dock → V3. -- **Pending:** V3 queued. Slices b (hotplug auto-switch) and c - (workspace binding) remain in the item. -- **Next suggestion:** #16 greeter theming, or 15b if momentum on - displays is preferred. - -## 2026-07-05 — display-profiles design pass (iteration #27, item 15, no code) -- **Task:** BACKLOG NEXT#15 — the entry itself demands "design first, - write the plan into this entry before coding". Backlog-only iteration. -- **Did:** wrote the plan into item 15: `nomarchy.displayProfiles = - attrsOf (listOf monitorType)` (reuse the existing submodule), - `settings.displayProfile` state via the night-light pattern (instant - hyprctl apply + bake on rebuild), whole-entry-by-name overlay with a - disable-guard against stale resolution picks, self-gated menu row, - three slices (option+overlay+menu / opt-in hotplug auto-switch / - per-profile workspace binding), kanshi stays a non-goal. -- **Verified:** V0 (docs-only; flake untouched). -- **Pending:** nothing — 15a is now directly codeable. -- **Next suggestion:** 15a (option + overlay + menu picker), or #16 - greeter theming if a fresh session prefers a self-contained item. - -## 2026-07-05 — scheduled lock-bump workflow (iteration #26, item 14 b+c) -- **Task:** BACKLOG NEXT#14 `[big]` — split: (a) checks-on-push shipped - earlier; this ships (b) the weekly bump + (c) the fast-lane as - workflow_dispatch on the same job. -- **Did:** .gitea/workflows/bump.yml — Mon 05:17 UTC cron + dispatch: - nix flake update (pinned branches by construction) → gate `nix flake - check --no-build` (py_compile/bash -n skipped: they don't read the - lock) → commit --only flake.lock with the update's change list as - body → push main (triggers check.yml as second net). Red gate/push - race = failed run, nothing lands. Container recipe from check.yml. -- **Verified:** V0 (yq parse + bash -n each run block); logic simulated - end-to-end in a scratch clone with a REAL nix flake update + bare - remote: changed/unchanged branches, bystander file stays uncommitted, - push lands; today's bumped lock evals green (Monday's baseline). - Gitea 1.25.4 confirmed (schedule supported). The real cron pickup + - token push are NOT confirmable from here → item 14 slimmed to - "confirm first run". -- **Pending:** watch the first scheduled/dispatched bump run. -- **Next suggestion:** #16 greeter theming (next codeable item; #15 - needs its design pass written first). - -## 2026-07-05 — color picker (iteration #25, item 13 final slice → item done) -- **Task:** BACKLOG NEXT#13 last slice — hyprpicker → clipboard from - the menu + a direct bind. Item 13 complete; deleted from BACKLOG. -- **Did:** hyprpicker into systemPackages (next to grim/slurp); rofi.nix - `colorpicker` mode (hyprpicker → wl-copy → hex-in-toast, Esc = silent - cancel) + self-gated Tools › "Color picker" row + usage line; - keybinds.nix SUPER+CTRL+P row (feeds bind + cheatsheet). -- **Verified:** V0; V1 — template-home AND template-system build; - rendered nomarchy-menu bash -n OK with mode + gated row; cheatsheet - txt has the SUPER+CTRL+P row; hyprland conf has the bind; system - toplevel ships sw/bin/hyprpicker. Picking needs a compositor → V3. -- **Pending:** V3 queued (loupe → click → wl-paste round-trip). -- **Next suggestion:** #15 (display profiles) needs a design pass - written into the entry first; #16 (greeter theming) is the next - directly codeable item — or #14's bump workflow (eval-tier gate). - -## 2026-07-05 — low-battery notifications (iteration #24, item 13 slice) -- **Task:** BACKLOG NEXT#13 second slice — the bar colors the battery - at 25/10% but nothing *notified*. -- **Did:** pkgs/nomarchy-battery-notify (shellcheck-gated watcher: - polls the same sysfs the bar reads — type=Battery, scope≠Device — - one toast per downward crossing: normal@25, critical@10; re-armed by - charging; self-gates on battery presence → desktop no-op) + overlay. - HM unit in modules/home/battery-notify.nix behind - nomarchy.batteryNotify.enable (default on), graphical-session-bound, - libnotify on the unit PATH (notify-send resolved from PATH so the - check can shim it). README row. Chose the tiny watcher over - poweralertd: its UPower thresholds (20/5) wouldn't match the bar, - and it toasts every plug/unplug. -- **Verified:** V0; V1 — template-home builds, unit present; V2 GREEN — - new checks.battery-notify (test_power fake battery): self-gate exits - 0 without a battery, silence at 80%, exactly one low toast crossing - 25, one critical-urgency toast crossing 10, charging re-arms → a - second drain toasts again. -- **Pending:** V3 queued (real toast through swaync while draining). -- **Next suggestion:** item 13 final slice — hyprpicker color picker - (Tools › entry + SUPER+CTRL bind). - -## 2026-07-04 — caffeine/idle-inhibit toggle (iteration #23, item 13 slice) -- **Task:** BACKLOG NEXT#13 first slice — idle_inhibitor in the - generated bar (summer-night had it; reverse parity gap). -- **Did:** waybar.nix: idle_inhibitor module (󰾪/󰅶, tooltips, placed - after the ⏺), CSS membership + `.activated { color: @warn; }`. - summer-day: module + CSS mirrored from summer-night (same glyphs, - no tint — glyph change is the state signal there). keybinds.nix - extra row → cheatsheet mentions the bar click. -- **Verified:** V0; V1 — template-home built; rendered config has the - module second in modules-right with correct glyphs; CSS rules - present; cheatsheet row renders. Inhibit behaviour needs a session - → V3 queued (hold past the lock timeout). -- **Pending:** V3. Item 13 remaining slices: low-battery - notifications, color picker. -- **Next suggestion:** item 13 next slice (low-battery notifications, - gate on power.laptop) — or #14 lock-bump CI if variety is better. - -## 2026-07-04 — screen recording in Capture (iteration #22, item 12) -- **Task:** BACKLOG NEXT#12 — record region/screen from the menu, stop - from the bar. -- **Did:** modules/home/recording.nix: `nomarchy-record` - start/stop/active/status — wl-screenrec first, wf-recorder fallback - if it dies at startup (no-VAAPI case), SIGINT-finalized stop, - runtime pidfile, signal-8 bar pokes. Capture menu: 4 record rows that - self-swap to "■ Stop recording" while one runs. Waybar: self-gating - custom/recording (⏺ REC, @bad red), click = stop — the one stop - surface. Parity: both summer whole-swap bars got the module + CSS - (@red). DEVIATIONS: saves to ~/Videos/Recordings (an .mp4 in - Pictures/Screenshots felt wrong — same timestamp naming though); - audio = two extra explicit rows, not a toggle state. -- **Verified:** V0; V1 — template-home builds; rendered bar config has - the module (first in modules-right) + CSS rule; summer jsonc still - parse; nomarchy-record dry-run: status empty+exit 0 idle, active - exit 1, usage exit 2; both recorders in home-path. Real capture - needs a session → V3 (headless GL recipe can't confirm an encoder). -- **Pending:** V3 recording exercise (incl. VAAPI-vs-fallback check). -- **Next suggestion:** NEXT#13 niceties, one slice: idle-inhibit - (caffeine) toggle — reverse-parity gap already noted in the item. - -## 2026-07-04 — state-file validation & friendly errors (iteration #21, item 11) -- **Task:** BACKLOG NEXT#11 — hand-edited state must fail with field + - problem + fix, never a Nix stack. -- **Did:** (a) tool: validate_state() (appearance schema = hard errors; - settings.*/unknown keys = warnings), `validate` subcommand, - validate-BEFORE-write in write_state (invalid set/apply never touches - disk), JSON syntax errors now say line/column/fix. (b) theme.nix: - same schema at eval time on `parsed` (post-defaults, so sparse files - stay fine); throws a multi-line message naming each field, the got- - value, and the three fix paths. Doctor's JSON fix-text now points at - `validate`. -- **Verified:** V0; V1 — new checks.theme-sync-validate GREEN: good - template passes, trailing comma → line/col message, bad hex/type → - field named, unknown key → warning only, invalid `set` refused with - the file byte-identical (cmp). Negative eval of a broken scratch - downstream through mkFlake shows the friendly throw (both fields); - good-path template-home still builds. -- **Pending:** nothing — no hardware dependency. -- **Next suggestion:** NEXT#12 (screen recording) or #13 niceties - slice; #12 first (it reshapes the Capture submenu the OCR LATER item - waits on). - -## 2026-07-04 — nomarchy-doctor (iteration #20, item 10) -- **Task:** BACKLOG NEXT#10 — one-shot read-only health check. -- **Did:** pkgs/nomarchy-doctor (writeShellApplication → shellcheck- - gated; a package so the VM check runs on a minimal node): failed - system+user units (user bus self-skips), disk space on real - filesystems only (fstype allowlist skips tmpfs/9p → VM-safe; device - dedupe), state-file parses + git-tracked, flake dirty/behind - (warn-level), generation age via the profile SYMLINK mtime (store - paths are epoch-1), snapper timer when enabled. Every ✖ prints its - fix; exit 1 on any ✖. Wired: overlay, systemPackages, System › - Doctor menu row (terminal), README §5. -- **Verified:** V0; V1 package build (shellcheck) + real-hardware - smoke: correctly flagged a genuinely failed user unit on the dev box; - V2 GREEN — VM test: induced failed unit → exit 1 + names it + prints - fix; reset-failed → healthy exit 0. Menu rebuild bash -n ok. -- **Pending:** V3 menu-row check queued. Waybar-warning follow-up → - LATER. -- **Next suggestion:** NEXT#11 (state-file validation) — doctor's - "parses" check is its little sibling; 11 adds validate-before-write - + eval-time schema messages. - -## 2026-07-04 — System › Rollback menu (iteration #19, item 9b → item 9 done) -- **Task:** BACKLOG NEXT#9 half (b) — undo one menu away. -- **Did:** new `rollback` case in nomarchy-menu (System submenu row): - lists the last 10 `home-manager generations` (newest marked current), - picking one activates it in a terminal — HM's supported rollback, - reversible, so no typed-yes. System-level undo LINKS OUT by design: - Snapshots row → existing flow; "boot an older generation (how)" → - instruction notification pointing at docs/RECOVERY.md §3 (no - destructive reimplementation in rofi). RECOVERY §1 cross-references - the menu path. Item 9 complete → deleted. -- **Verified:** V0; V1 — template-home built, generated nomarchy-menu - bash -n clean, row-render + pick-parse logic run against a fixture - `generations` output (id/path extraction correct). V2 skipped - deliberately: the only novel logic is the parsing (fixture-covered); - the activate step is HM's own mechanism. V3 queued. -- **Pending:** V3 rollback-menu exercise on hardware. -- **Next suggestion:** NEXT#10 `nomarchy-doctor` (V2-able in a VM). - -## 2026-07-04 — sys-update/-rebuild what-changed diff (iteration #18, item 9a) -- **Task:** BACKLOG NEXT#9 half (a) — a human diff after system rebuilds. -- **Did:** both sys-update and sys-rebuild (twins stay twins) capture - /run/current-system before, and after the switch print - `nvd diff before after` (store-path-pinned ${"$"}{pkgs.nvd}, `|| true` - so it can't fail a succeeded rebuild; identical-path fast-path says - "no changes"). README §3 shortcut block notes it. -- **Verified:** V0; V1 — template-system toplevel rebuilt, generated - scripts extracted + bash -n, nvd smoke-run against the real closure - (correct output format). Runtime behaviour is a trivial shell path — - no VM needed. -- **Pending:** half (b) — the System → Rollback menu flow (desktop - generations picker; system → boot menu + snapshots pointer). -- **Next suggestion:** item 9b — it completes the item; V2 the - generation-activate path. - -## 2026-07-04 — viewers + mime defaults (iteration #17, item 8) -- **Task:** BACKLOG NEXT#8 — PDF/image viewers + xdg-mime defaults. -- **Did:** modules/home/viewers.nix (`nomarchy.viewers.enable`, default - on): programs.zathura (Stylix themes it — target added to stylix.nix's - explicit list, autoEnable=false bit us first try) + imv. - modules/home/mime.nix (`nomarchy.mime.enable`): mkDefault - xdg.mimeApps for pdf→zathura, image/*→imv, av→mpv, text→code, - dir→thunar, html/http(s)→firefox as INERT entries (browser Decision - untouched — absent .desktop entries are skipped by GIO). README rows - added (option-docs enforces them now). DEVIATION from the item text: - viewers are a module toggle, not template packages — zathura's - theming needs its HM module, which is exactly the "real config" - boundary of the toggle discipline. -- **Verified:** V0; V1 — template-home generation builds; asserted - rendered mimeapps.list (all associations), themed zathurarc - (Tokyo Night rgb), imv/zathura in home-path. V3 smoke queued. -- **Pending:** V3 open-a-file smoke; browser Decision now smaller - (inert entries ship; only "active in suite?" remains). -- **Next suggestion:** NEXT#9 (update & rollback UX) — or #10 doctor. - -## 2026-07-04 — docs slice (c): install story — item 6 COMPLETE (iteration #16) -- **Task:** BACKLOG NOW#6 slice (c), the last one — install/first-run - narrative end to end. -- **Did:** read README §2 (ISO/installer) → §3 (downstream) → template - README as one story; checked the facts en route: mkFlake's arg - surface == the §3 table (src/username/hardwareProfile/system, lib.nix), - disko really makes @snapshots + the hibernation swapfile, .#default / - .#me names consistent. One seam fixed: §3 showed `nix flake init -t` - but never handed off to the template README's six first-run steps — - connective paragraph added (+ "the installer does all of this"). - Item 6 done → deleted from BACKLOG (NOW now empty); ROADMAP § Full - docs review flipped to ✓ with the slice summary. -- **Verified:** V0 (docs-only) — flake check clean pre-commit. -- **Pending:** docs-site-vs-Markdown stays a Decision. -- **Next suggestion:** NEXT#8 (viewers + mime defaults) is the new queue - head; note its "Default browser" Decision dependency — the mime module - half can proceed with mkDefault degradation regardless. - -## 2026-07-04 — docs slice (d): recovery runbook (iteration #15, item 6d) -- **Task:** BACKLOG NOW#6 slice (d) — write the undo story. -- **Did:** docs/RECOVERY.md, symptom-ordered: (1) bad theme/HM change → - generations / apply-known-good / git-revert ~/.nomarchy; (2) desktop - won't start → Ctrl+Alt+F2, greetd + user journals, VM-GL and - preactivate-log pointers; (3) system change → systemd-boot older - generation (kept: 10) + make-it-stick via sys-rebuild; (4) files → - btrfs-assistant / sudo nomarchy-snapshots (undochange, root rollback, - typed-yes); (5) ISO + nixos-enter. Every command checked against the - tree (greetd cfg, configurationLimit, snapshots script actions, - helper names). Linked from README tree + §5. -- **Verified:** V0 (docs-only) — flake check clean pre-commit. -- **Pending:** slice (c) install-story read-through — the last of item 6. -- **Next suggestion:** finish item 6 with slice (c). - -## 2026-07-04 — docs slice (b): drift pass (iteration #14, item 6b) -- **Task:** BACKLOG NOW#6 slice (b) — OVERRIDES.md / TESTING.md / - templates/downstream/README.md vs the live tree. -- **Did:** spot-checked every factual claim: mkDefault sites (hyprland - behaviour knobs, ghostty paddings, waybar mainBar/style), bind-list - normal priority, `set ui.gapsOut` CLI key, awww(-daemon) naming, - 21 presets, tokyo-night's 4 wallpapers, summer-night's light @fg bar, - live `,highres` mkForce, .gitea/workflows/check.yml, QEMU gl flags, - template `.#me` + ~/.nomarchy/NOMARCHY_PATH. All accurate. Fixed the - two real gaps: OVERRIDES §2 predated `nomarchy.monitors` (now routes - there + quick-ref row); TESTING §1 lacked the shell-syntax check §1b - claims CI runs (added `bash -n`). -- **Verified:** V0 (docs-only; flake untouched — check run pre-commit). -- **Pending:** slices (c) install story, (d) recovery runbook. -- **Next suggestion:** item 6 slice (c) or (d) — (d) is self-contained - and ships user value (the runbook). - -## 2026-07-04 — docs slice (a): option-surface reconcile (iteration #13, item 6a) -- **Task:** BACKLOG NOW#6, first slice — README tables vs live options. -- **Did:** split item 6 into slices (a–d) in BACKLOG. Reconciled: 9 - undocumented options (autoTimezone.enable!, camera.hideIrSensor + - irMatch!, intel.guc, amd.pstate/vaapi, package, system.stateFile, - restic.paths) — rows/inline mentions added, no stale rows found. - Made it permanent: `checks.option-docs` — flake.nix eval-walks option - names from the 4 option files (config halves stay lazy; dummy args) - → tools/check-option-docs.py enforces row-or-`.leaf`-mention + flags - stale rows. -- **Verified:** V0; V1 — check builds green (78 options); negative test: - same script vs the pre-fix README reports exactly the 9 gaps. -- **Pending:** slices (b) doc drift pass, (c) install story, (d) - recovery runbook. -- **Next suggestion:** item 6 slice (b) — or NEXT#8 (viewers+mime) for - variety; both fine. - -## 2026-07-04 — Back audit (iteration #12, item 24) -- **Task:** BACKLOG NOW#24 — every list menu ends in ↩ Back. -- **Did:** audited every `rofi -dmenu` call site in the generated - nomarchy-menu + nomarchy-vpn. Already compliant: power, power-profile, - theme, clipboard, files, audio, display (both stages), capture, - tools, system, VPN main/import/exit-node/tailscale. Fixed: keybinds - cheatsheet (Enter was a silent exit) now ends in ↩ Back → root - picker. Documented the exceptions in rofi.nix: external modi - (calc/emoji/networkmanager_dmenu/rofi-pulse-select) can't take an - injected row (Esc = back); free-text prompts (web/ask) have no list. -- **Verified:** V0; V1 — built nomarchy-menu, bash -n OK; awk audit - over the generated script: only rows lacking back are the root - picker (by design) and the free-text prompt. V3 spot-check queued. -- **Pending:** V3 spot-check. NOW now holds only item 6 (docs, [big]). -- **Next suggestion:** item 6 first slice, or NEXT#8 (viewers+mime). - -## 2026-07-04 — network menu blank rows (iteration #11, item 22) -- **Task:** BACKLOG NOW#22 — nameless entries in the network picker. -- **Did:** read the pinned networkmanager_dmenu 2.6.2 source: with - `compact = False` (our config!) it inserts literal empty rows as - section separators (eth/wifi/vpn) — the systematic blank entries. - Flipped config.ini to `compact = True` (also unpadded row format, - better in rofi's proportional font). Hidden-SSID APs additionally - render ONE nameless sec+bars row (dedup keeps one; not config- - filterable) — documented, V3-gated, source-patch follow-up if seen. -- **Verified:** V0; V1 — rendered config.ini shows compact = True. - Behavior needs Wi-Fi hardware → V3 queued. -- **Pending:** V3 on the Latitude (no separators; hidden-AP residual?). -- **Next suggestion:** item 24 (Back audit) — grep-the-generated-script. - -## 2026-07-04 — waybar/rofi palette-safe pairings (iteration #10, item 27) -- **Task:** BACKLOG NOW#27 — generated waybar/rofi CSS invisible on - on-surface palettes (flexoki-light runs it; summers whole-swap). -- **Did:** waybar.nix generated CSS: module row + #window → - alpha(@text, 0.85); inactive workspaces / dnd / pulseaudio.muted → - alpha(@text, 0.5) (gruvbox's muted/base was 1.27). rofi.nix: @dim → - text+80, @surface → text+1a (#RRGGBBAA fg tints). Contrast check - docstring/pairings now cover all three generated surfaces. Status - glyphs (good/warn/bad on base, worst 2.05) left as-is — palette - design, human call. Generated bar only; whole-swaps untouched. -- **Verified:** V0; V1 — checks.theme-contrast green (21 themes), - downstream-template-home builds, rendered waybar style has zero - @subtext/@muted/@surface, rendered custom.rasi shows fg-derived - tints. NOT visually verified — V3 queued (flexoki-light eyeball). -- **Pending:** V3 flexoki-light + dark-theme regression spot-check. -- **Next suggestion:** item 22 (network menu rows) — V1-actionable. - -## 2026-07-04 — swaync contrast fix + theme-contrast check (iteration #9, item 25) -- **Task:** BACKLOG NOW#25 — swaync text invisible on summer-day. -- **Did:** swaync.nix now uses only palette-safe pairings: body/summary - = @text on @base (worst ratio 5.18 across all 21 themes; subtext/base - was 1.00 on summer-day + flexoki-light), hover rows + widget buttons - = alpha(@text, 0.1) tints (contrast by construction); dropped the - subtext/surface defines. NEW permanent check `checks.theme-contrast` - (tools/check-theme-contrast.py) asserts the hex-on-hex text pairings - across every theme JSON. Audit found the same mispairing in generated - waybar/rofi CSS, live on flexoki-light (no whole-swap) → NOW item 27. -- **Verified:** V0; V1 — checks.theme-contrast builds green (21 themes), - rendered services.swaync.style contains zero @subtext/@surface. - NOT V2/V3-verified visually — queued (Bernardo is at the Latitude). -- **Pending:** V3 notify-send readability check on summer-day; item 27. -- **Next suggestion:** item 27 (same bug class, waybar/rofi surfaces). - -## 2026-07-04 — SUPER+? bind fix (iteration #8, item 26) -- **Task:** BACKLOG NOW#26 — cheatsheet bind never fires on hardware. -- **Did:** keybinds.nix: `$mod, question` → `$mod SHIFT, question` - (? is Shift+/, Hyprland matches the exact modmask); rofi.nix - prettyKeys collapses "SHIFT + ?" → "?" so the row stays SUPER + ?. - Gotcha added to MEMORY.md (shifted keysyms need SHIFT in mods). -- **Verified:** V0 (flake check --no-build); V1 both surfaces — eval'd - hyprland settings.bind renders `$mod SHIFT, question, exec, - nomarchy-menu keybinds`, and built nomarchy-menu's cheatsheet file - renders `SUPER + ?`. No other shifted keysyms in the bind list. -- **Pending:** V3 queued (SUPER+? on the Latitude after home-update + - relogin). -- **Next suggestion:** item 25 (swaync contrast on summer-day). - -## 2026-07-04 — Latitude hardware sweep #2 (Bernardo; results booked, no code) -- **Task:** V3 sweep on the Latitude after pulling ≥18b8545 + reboot. -- **PASS:** waybar supervisor (theme switches clean; `pkill -x waybar` - respawns instantly); snapshots — themed polkit prompt → GUI as root, - fzf fallback works (restore/rollback gate not exercised → residual - queued); `sys-rebuild`; night-light enable/disable; auto-commit - (theme commits + the toggle self-commit); hibernate single-unlock + - suspend-still-locks (1b0eeea CONFIRMED). -- **PASS (late confirm):** auto-timezone — `timedatectl` shows - Europe/London. -- **Bug:** SUPER+? never fires — `$mod, question` misses because a real - ? carries SHIFT in the modmask → BACKLOG 26 (bind-only: the menu - itself opens fine from a terminal). SUPER+SHIFT+K no-op was the - comma-layout gate working (none configured) — retest queued. -- **Next suggestion:** item 25 (swaync contrast) or 26 (cheatsheet bind). - -## 2026-07-04 — Waybar resilience + sys-rebuild (iteration #7, items 21+23) -- **Task:** the two top Latitude findings. -- **Did:** (21) `nomarchy-waybar` supervisor (waybar.nix, exec-once'd - from hyprland.nix): ANY waybar exit respawns, crash-loop guard (5 - fast exits → critical notify + stop), TERM trap for a real stop. - theme-sync now prefers a clean `pkill -x waybar` when the supervisor - is running (restart with fresh config+style — avoids waybar's - in-place SIGUSR2 reload entirely, the suspected double-reload race - with reload_style_on_change during symlink flips); SIGUSR2 stays as - the unsupervised fallback; reload_style_on_change kept for manual - home-update restyles. (23) `sys-rebuild` — snapshot-first rebuild - against the current lock, no `nix flake update`; README §3/§5 + motd. -- **Verified:** V0; V1 (HM renders `exec-once=nomarchy-waybar`, - supervisor bash -n; `sys-rebuild` in systemPackages). V2: headless - software-GL desktop VM (recipe from memory — gotchas hit: node needs - the overlay, users.users., useGlobalPkgs) — SIGKILL→new pid, clean - kill→respawn, SIGUSR2→alive. Result recorded below once green. -- **Pending:** V3 on the Latitude: theme switch survives / bar returns. -- **Next suggestion:** item 22 (network menu rows) or 24 (Back audit). - -## 2026-07-04 — Keyboard cycle bind + parity (iteration #6) + Latitude triage -- **Task:** BACKLOG NOW#5; mid-iteration Bernardo delivered Latitude - 5410 hardware-QA findings (the machine got a real session!). -- **Did:** `multiLayoutBinds` in keybinds.nix (SUPER+SHIFT+K → - `switchxkblayout current next`), gated in hyprland.nix AND the rofi - cheatsheet on the same comma-in-layout condition; `hyprland/language` - + `#language` CSS added to both summer whole-swaps (shows even - single-layout there — static JSON can't gate; deliberate). Reverse - parity gap found (summer-night has idle_inhibitor, generated bar - doesn't) → folded into item 13. **Triage:** four new NOW items from - hardware: #21 Waybar crash on theme switch + no respawn (top), - #22 nameless network-menu entries, #23 `sys-rebuild` (no-update - rebuild), #24 Back-everywhere audit. -- **Verified:** V0 (JSON + flake check) + V1 both directions - (single-layout build: no bind; `us,de` via extendModules: bind + the - cheatsheet row render; summer JSONs parse). -- **Pending:** V3 cycle-bind check queued; items 21/22/24 carry V3 - re-checks on the Latitude. -- **Next suggestion:** #21 (waybar crash — stability pillar, user-hit). - -## 2026-07-04 — Webcam follow-ups (loop iteration #5, quick item) -- **Task:** BACKLOG NOW#7 (Bernardo asked for "a quick item"). -- **Did:** template `home.packages` gains a commented "Webcam tuning" - pair (cameractrls GUI, v4l-utils CLI — bare packages, so template not - toggle, per the option-surface rule); the portal/Flatpak libcamera IR - gap is written up as a PROPOSED item with three options (recommend: - document-only now, libcamera-rule investigation when the T14s is - available). CI runs #60/#61 confirmed green (earlyoom + snapshot fix). -- **Verified:** V0 (flake check — template evals through mkFlake; - changes are comments + agent docs). -- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity) — - the last code item in NOW besides the docs pass. - -## 2026-07-04 — Snapshot GUI un-broken + polkit agent (loop iteration #4) -- **Task:** BACKLOG NOW#4 — btrfs-assistant segfault. -- **Did:** re-diagnosed instead of overriding. gdb: crash is in - libbtrfsutil's *unprivileged* subvolume iteration (btrfs-progs 6.17.1; - upstream fix `886571653` post-6.17.1; symbol versions verified — not - ABI drift). VM A/B: root works (exit 0), user crashes (139). The - launcher runs it as root → GUI fine *if* pkexec can prompt — and the - distro shipped **no polkit agent** (all pkexec silently failed; the - actual root cause of "GUI is dead"). Shipped: hyprpolkitagent - (exec-once, Stylix-themed Qt), menu re-pointed to the launcher with - the fzf flow as fallback, `checks.snapshot-gui` canary (root path + - offscreen event loop on real btrfs). No btrfs-progs patch — root-side - flows don't hit the bug; the fix arrives with a lock bump. -- **Verified:** V0 + V2 (snapshot-gui check, run for real) + V1 (HM - generation renders the agent exec-once; menu bash -n). -- **Pending:** V3 queued — first on-hardware themed polkit prompt. -- **Next suggestion:** NOW#5 (keyboard cycle bind + summer parity). - -## 2026-07-04 — CI is green (run #58) — item 20 main half closed -- **Task:** monitor follow-up, no code. Run #58 (the .gitea path fix) - **succeeded** — first green CI: the container recipe works on the - live runner, eval tier confirmed end to end. -- **Did:** BACKLOG item 20 reduced to its stretch (KVM runner → enable - vm-checks), moved to NEXT. Run #59 (earlyoom push) monitored. -- **Next suggestion:** NOW#4 (btrfs-assistant override attempt). - -## 2026-07-04 — Memory-pressure protection shipped (loop iteration #3) -- **Task:** BACKLOG NOW#3 — default-on userspace OOM killer. -- **Did:** `modules/nixos/oom.nix`: earlyoom (mkDefault on, desktop - notifications, session-plumbing `--avoid` list, no `--prefer`) + - `systemd.oomd.enable = false`. Choice rationale: Hyprland session is - one cgroup scope → oomd's cgroup-level kill would nuke the desktop; - earlyoom is process-level. Found en route: nixpkgs default-enables - oomd *inert* (enable=true, all slice toggles false). README "beyond - the surface" note added. -- **Verified:** V0 (flake check) + **V2** — new `checks.oom-protection` - runNixOSTest, run for real: hog (chunked allocator, 686 MB peak in a - 1 GB VM) SIGTERM'd by earlyoom in 0.1 s, bystander unit survived, - oomd asserted inactive. 25 s test, cheap enough for the suite. -- **Pending:** nothing hardware-specific (behavior fully VM-provable). -- **Next suggestion:** NOW#4 (btrfs-assistant override attempt). Also: - CI run #58 (the .gitea fix) was *running* at commit time — check its - outcome next tick; this push triggers #59. - -## 2026-07-04 — CI fix: wrong workflow dir (correction to iteration #2) -- **Task:** Bernardo reported no run despite an active runner + shared - his compose file: the server is **Gitea** (gitea/act_runner), not - Forgejo — I'd shipped the workflow to `.forgejo/workflows/`, which - Gitea never reads. The legacy repo's `.gitea/workflows/` path was the - clue I under-weighted. -- **Did:** `git mv` → `.gitea/workflows/check.yml`; corrected the - Forgejo references in the workflow header, TESTING.md §1b, BACKLOG - item 20 (now: watch the first run; runner was never the problem); - MEMORY.md gains the Gitea-not-Forgejo line. -- **Verified:** the push of this commit is itself the test — the run - must appear; monitoring. -- **Pending:** first green run (item 20); runner-container memory - headroom is the watch-out. - -## 2026-07-04 — CI checks-on-push shipped (loop iteration #2) -- **Task:** BACKLOG NOW#2 — Forgejo Actions workflow. -- **Did:** `.forgejo/workflows/check.yml` (push to main/v1 + dispatch): - flake check --no-build, theme-sync py_compile, bash -n over tracked - .sh. Scoped to the **eval tier** deliberately — API probing + - archaeology of the legacy `.gitea/workflows/check.yml` (57 runs on - this instance) showed the runner is an act_runner docker container - (no KVM/systemd), so the VM suite can't run there; a commented - `vm-checks` job documents the KVM-runner upgrade path. Legacy's - hard-won container gotchas (nixbld setup, sandbox=false for Stylix - IFD, Nix 2.31.5 pin) carried over verbatim. TESTING.md §1b added. -- **Verified:** V0 locally (same commands the workflow runs, minus the - container Nix install) + yq YAML parse. The real green run needs the - runner to be alive — **not confirmable from here** (runners API is - 401 unauthenticated); watching the run after push. -- **Pending:** new `[human]` item 20 — confirm/re-register the runner; - stretch: a KVM runner unlocks the vm-checks job. -- **Next suggestion:** NOW#3 (memory-pressure protection). - -## 2026-07-04 — Opt-in auto-commit shipped (loop iteration #1) -- **Task:** BACKLOG NOW#1 — auto-commit of state mutations (in-flake - state Phase 4). First autonomous /loop iteration. -- **Did:** `settings.autoCommit` live-read by nomarchy-theme-sync; - pathspec-limited commit of theme-state.json on apply/set (fires if flag - on before OR after the write → the off-toggle commits too); identity - fallback; same-value no-op; `bg` excluded. Menu: System › Auto-commit - (instant, self-gated on `.git`). Rider: `get` prints booleans as JSON - `true`/`false`, fixing the stuck "Auto timezone (on/off)" menu label. - BACKLOG numbering switched to stable IDs (no renumbering). -- **Verified:** V0 (py_compile, flake check) + V1 (HM generation builds; - generated menu `bash -n`) + a 7-assertion sandbox git-repo round-trip - (enable/set/same-value/disable/post-disable/apply/identity-fallback). -- **Pending:** V3 queued in HARDWARE-QUEUE (on-machine toggle + apply). -- **Next suggestion:** NOW#2 (CI checks-on-push) — but its runner half - may need Bernardo to register a Forgejo runner; deliver the workflow - regardless. - -## 2026-07-04 — Backlog revision pass (human-requested) -- **Task:** Bernardo asked for a philosophy-aligned revision of BACKLOG.md - ("I'm sure I'm forgetting important items"). -- **Did:** verified guessed gaps against the tree first (GC/optimise/boot - limit + all 21 previews already ship — dropped those ideas). Added the - confirmed gaps: CI-on-push promoted to NOW#2 (agents push to main now), - memory-pressure protection NOW#3 (nothing mitigates OOM; the release - bump died to one), viewers+mime defaults #8, update/rollback UX #9, - nomarchy-doctor #10, state-file validation #11, screen recording #12, - niceties batch #13 (idle-inhibit, low-battery notify, hyprpicker), OCR - in LATER; recovery runbook folded into the docs item; zram + default - browser added to Decisions. -- **Verified:** V0 (docs-only). -- **Pending:** Bernardo to sanity-check the new NOW ordering, esp. CI at - #2 and the two new Decisions. -- **Next suggestion:** unchanged — NOW#1 (opt-in auto-commit). - -## 2026-07-04 — Bootstrap the loop infrastructure (this commit) -- **Task:** create `agent/` (human-requested): LOOP/GOALS/BACKLOG/ - CONVENTIONS/MEMORY/HARDWARE-QUEUE/JOURNAL, root CLAUDE.md, README + - ROADMAP pointers. -- **Did:** reworked the forward half of docs/ROADMAP.md into the tiered - BACKLOG (5 NOW / 6 NEXT / LATER / Decisions); collected every pending - on-hardware check into HARDWARE-QUEUE.md; seeded MEMORY.md from the - ROADMAP's decision records. -- **Verified:** V0 (docs-only change; `nix flake check --no-build` run to - confirm the tree still evaluates). -- **Pending:** Bernardo to sanity-read GOALS.md pillars + BACKLOG tiering - — reorder freely, the order *is* the instruction. -- **Next suggestion:** BACKLOG NOW#1 (opt-in auto-commit — Phase 4 of - in-flake state, already the agreed next step). diff --git a/agent/LOOP.md b/agent/LOOP.md index b7f8d1d..c0eebde 100644 --- a/agent/LOOP.md +++ b/agent/LOOP.md @@ -2,12 +2,12 @@ How an AI agent works on Nomarchy unattended. One **iteration** = pick one task, do it, verify it, commit it, record it. The protocol is -runner-agnostic; the same iteration works under any of: +harness- and vendor-agnostic; the same iteration works under any of: -- **Interactive `/loop`** in a Claude Code session in this repo — the agent - self-paces iterations until stopped. -- **Headless** (`claude -p`, cron/systemd-timer) — one invocation runs one - iteration (or a small fixed number) and exits. +- **An interactive self-paced loop** in any agent harness (e.g. `/loop` + in Claude Code) — the agent iterates until stopped. +- **Headless** (a one-shot CLI invocation, cron/systemd-timer) — one + invocation runs one iteration (or a small fixed number) and exits. - **A fresh manual session** — a human says "do a loop iteration"; the files below carry all the state, so any session can pick up where the last left off. @@ -26,23 +26,25 @@ outside the checkout (the distro's own philosophy, applied to its agents). | `HARDWARE-QUEUE.md` | Pending on-hardware checks only Bernardo can run | Agents append, human checks off | | `CONVENTIONS.md` | Repo/design conventions to follow while coding | Human (agents propose edits) | +Instructions live next to the state: `VERIFICATION.md` (enforcement), +`DELEGATION.md` (tiers/roles/economy), `GOALS.md`, `THEME-DESIGN.md`. + ## Model & token economy -Spend expensive tokens on judgment, not mechanics. +Spend expensive tokens on judgment, not mechanics. Tiers, roles, and the +full delegation rules are in **`DELEGATION.md`**; the loop-specific +habits: - **Plan and reason on the strong model.** Orientation, task selection, design, debugging, Nix eval semantics, verification judgment, and anything that would land in a commit unreviewed stay with the - top-tier model running the loop (Fable 5). -- **Delegate mechanical subtasks to cheaper models.** When a subtask is - fully specified and needs no design judgment — grep/audit sweeps, + frontier-tier model running the loop. +- **Delegate mechanical subtasks down.** When a subtask is fully + specified and needs no design judgment — grep/audit sweeps, README-option-table reconciliation, a repeated edit applied across - files, summarizing long logs or check output — hand it to a subagent - with a `model` override: `haiku` for search/summarize/audit, `sonnet` - for routine well-specified edits. The strong model writes the spec, - reviews the result, and owns the commit. Only delegate when writing - the spec is cheaper than doing the work — a spawned agent starts cold - and must re-derive context. + files, summarizing long logs or check output — hand it to a + light/standard-tier subagent per `DELEGATION.md`. The strong model + writes the spec, reviews the result, and owns the commit. - **Read narrowly.** Step 0's list is the whole orientation read (the *last 3–5 entries* of the journal, never the full file). Read large files by section, don't re-read what's already in context, and tail @@ -50,7 +52,7 @@ Spend expensive tokens on judgment, not mechanics. - **Write tersely.** Journal entries follow the template and no more; commit bodies state what/why/tier, not a narrative. - **Headless runners** may run whole low-stakes iterations (QA sweeps, - docs-drift passes) on a cheaper `--model`; iterations touching + docs-drift passes) on a cheaper model; iterations touching `modules/` or `pkgs/` behavior keep the strong model. ## One iteration, step by step diff --git a/agent/MEMORY.md b/agent/MEMORY.md index 2b5c9cb..30d012a 100644 --- a/agent/MEMORY.md +++ b/agent/MEMORY.md @@ -162,8 +162,9 @@ iteration would otherwise rediscover. effects carry a value, matchers take `match:` (§ windowrule migration). - grub `loadfont`s every `.pf2` in a theme dir — reuse a bundled DejaVu rather than shipping fonts (§ Distro branding). -- `.claude/skills/*/SKILL.md` are now **tracked** (the `.gitignore` - `.claude/skills/` line was un-commented→removed, 7d52d4b) — commit skill - edits like any repo doc. Still never `git add -A` blindly: check - `git status --short` for genuine strangers first (`settings.local.json`, - harness-dropped files) and commit with explicit pathspecs (§ loop hygiene). +- `.claude/skills/*/SKILL.md` are **tracked thin shims** — the actual + instruction bodies live vendor-neutrally in `agent/` (VERIFICATION, + DELEGATION, THEME-DESIGN); edit those, not the shims. Still never + `git add -A` blindly: check `git status --short` for genuine strangers + first (`settings.local.json`, harness-dropped files) and commit with + explicit pathspecs (§ loop hygiene). diff --git a/agent/README.md b/agent/README.md index d3140e2..b345967 100644 --- a/agent/README.md +++ b/agent/README.md @@ -1,18 +1,26 @@ -# Agent loop state +# Agent instructions + loop state -Git-tracked state for autonomous and assisted work on Nomarchy. -Protocol: **[LOOP.md](LOOP.md)**. Entry for most harnesses: repo-root -**[CLAUDE.md](../CLAUDE.md)**. +Everything an AI agent needs to work on Nomarchy, vendor-neutral and +git-tracked. Protocol: **[LOOP.md](LOOP.md)**. Entry point for every +harness: repo-root **[AGENTS.md](../AGENTS.md)**. -## Files +## Instructions (how to work) | File | Who writes | Role | |------|------------|------| -| [LOOP.md](LOOP.md) | Human | One-iteration protocol (orient → pick → work → verify → commit → record) | +| [LOOP.md](LOOP.md) | Human | One-iteration protocol (orient → pick → work → verify → commit → record) + the V0–V3 ladder | +| [VERIFICATION.md](VERIFICATION.md) | Human (agents propose) | Enforcement: preflight, honesty rules, visual protocol, hardware-blocked checks, reporting | +| [DELEGATION.md](DELEGATION.md) | Human (agents propose) | Capability tiers, scout/runner roles, token economy, parallel fan-out | | [GOALS.md](GOALS.md) | Human (agents propose) | Pillars, quality bars, non-goals | | [CONVENTIONS.md](CONVENTIONS.md) | Human (agents propose) | How to write code/menu/state while shipping | +| [THEME-DESIGN.md](THEME-DESIGN.md) | Human (agents propose) | Theme/visual design instructions | + +## State (what's happening) + +| File | Who writes | Role | +|------|------------|------| | [BACKLOG.md](BACKLOG.md) | Both | **Prioritized queue** — only executable work list | -| [JOURNAL.md](JOURNAL.md) | Agents | Append-only iteration log (read last 3–5 entries) | +| [JOURNAL.md](JOURNAL.md) | Agents | Append-only iteration log (read last 3–5 entries; older → [JOURNAL-ARCHIVE.md](JOURNAL-ARCHIVE.md)) | | [MEMORY.md](MEMORY.md) | Agents | Curated durable gotchas | | [HARDWARE-QUEUE.md](HARDWARE-QUEUE.md) | Agents append, human checks | On-hardware V3 tests only Bernardo can run | @@ -24,15 +32,20 @@ Protocol: **[LOOP.md](LOOP.md)**. Entry for most harnesses: repo-root | [../docs/ROADMAP.md](../docs/ROADMAP.md) | Design history + shipped log | | [../docs/README.md](../docs/README.md) | Full docs map | -## Claude Code only +## Harness adapters (vendor-specific, thin) -| Path | Role | -|------|------| -| [../.claude/settings.json](../.claude/settings.json) | Tool permissions | -| [../.claude/agents/](../.claude/agents/) | `nomarchy-scout` / `nomarchy-runner` subagent defs | +Shared content never lives in an adapter — adapters only register/route +into the files above, in whatever format their harness requires. -Do **not** put backlog items or vision text under `.claude/` — it is not -shared with other agent runners. +| Path | Harness | Role | +|------|---------|------| +| [../AGENTS.md](../AGENTS.md) | any | Entry point (`CLAUDE.md` is a symlink to it) | +| [../.claude/settings.json](../.claude/settings.json) | Claude Code | Tool permissions | +| [../.claude/agents/](../.claude/agents/) | Claude Code | `nomarchy-scout` / `nomarchy-runner` role defs (contracts in [DELEGATION.md](DELEGATION.md)) | +| [../.claude/skills/](../.claude/skills/) | Claude Code | Skill shims → VERIFICATION/DELEGATION/THEME-DESIGN | + +Do **not** put backlog items, vision text, or policy under an adapter +directory — it is not shared with other agent runners. ## Rules of thumb diff --git a/agent/THEME-DESIGN.md b/agent/THEME-DESIGN.md new file mode 100644 index 0000000..84e5450 --- /dev/null +++ b/agent/THEME-DESIGN.md @@ -0,0 +1,49 @@ +# Theme design — instructions for visual work + +For creating, updating, refining, or troubleshooting Nomarchy themes and +visual design. Act as an experienced UI/UX designer and Linux ricing +expert for Wayland environments: themes here must be cohesive, +intentional, and visually striking — aesthetics are a load-bearing +feature of this distro (`agent/GOALS.md` pillar 4). + +## Context & architecture + +All theme data lives in `themes/` at the repo root — one JSON palette per +theme plus per-app assets (whole-swap `waybar.jsonc`/CSS, btop themes, +wallpapers). Before generating any new configuration, read the existing +files there: learn how current themes are structured, how the syntax is +formatted for each application, and how they integrate into the NixOS/HM +modules. Always match the established pattern — there is no second +theming pipeline (`agent/GOALS.md` non-goals), so a new visual surface +consumes the palette from the state JSON, never a side file. + +## Design responsibilities + +1. **Holistic design:** a theme spans Hyprland (borders, shadows, + animations), Waybar, Ghostty, btop, fastfetch, rofi, and wallpaper — + one coherent identity, no unthemed corner. +2. **Color theory:** create or adapt advanced palettes. Draw inspiration + from established aesthetics (Everforest, Nord, Gruvbox) or r/unixporn + trends, but innovate. Ensure harmony between background, foreground, + accents, and warning/error colors. +3. **Typography & iconography:** pair UI and monospace fonts and icon + themes to the specific vibe of the palette. +4. **Accessibility:** high contrast for text readability; status + information is never color-only (see `agent/MEMORY.md` design + invariants — glyph/shape carries state, and identity themes are + deliberately not traffic-lights). + +## Process + +1. Read `themes/` to understand the current structure. +2. State the "vibe", the primary palette (hex codes), and typography + choices of the proposed design before implementing. +3. Implement by generating or updating files within the `themes/` + structure, following `agent/CONVENTIONS.md` (Waybar parity rule: + whole-swap `waybar.jsonc` files stay in sync with the generated + config). +4. Verify per `agent/VERIFICATION.md` §3 — scripted checks first + (`tools/check-theme-contrast.py`, `tools/audit-theme-design.py`), then + headless before/after screenshots you actually view, under at least + two themes. New themes are imported via `tools/import-palettes.py` and + round-tripped through the theme switcher. diff --git a/agent/VERIFICATION.md b/agent/VERIFICATION.md new file mode 100644 index 0000000..14c0f56 --- /dev/null +++ b/agent/VERIFICATION.md @@ -0,0 +1,178 @@ +# Verification — the enforcement rules + +Nomarchy's promise to its user: a rock-stable, fully functional, beautiful +workstation that is reproducible, easy to recover, and never requires the +user to become a Nix expert. Every rule below exists to protect that +promise. A change that works but degrades stability, aesthetics, or +user-simplicity is a regression, not a feature. + +This document is an **enforcer**, not the workflow itself: the ladder and +iteration protocol live in `agent/LOOP.md`, VM instructions and gotchas in +`docs/TESTING.md`. It applies to **every** change to this repo — features, +fixes, theming, module changes, lock bumps, docs, backlog grooming. +"Small" or "obvious" changes are exactly where verification gets skipped, +so they trigger it too. If this file and those docs ever disagree, fix the +discrepancy in the same or a follow-up commit so they can't disagree +twice. + +## 1. Preflight (once per session) + +Before starting work, establish what verification tier this environment +can reach, so you never promise verification you can't deliver: + +1. Linux x86_64 host? `/dev/kvm` present and readable? +2. Enough free disk for an image/ISO build (multi-GB)? +3. Network access for a cold Nix store? + +If the environment cannot reach V2 (no KVM, no disk, etc.): say so +immediately, do the V0/V1 work honestly, mark the change **"V2 pending"** +exactly as you would mark a hardware-blocked change "V3 pending" (§4), +and stop short of claiming the change is done. Never simulate, guess, or +describe what a VM test "would" show. + +## 2. The verification ladder (enforcement rules) + +Climb the V0–V3 ladder as defined in `agent/LOOP.md`. Four non-negotiable +enforcement rules on top: + +1. **V2 is mandatory for anything user-visible.** If a user of the + installed system could perceive the change — behavior, layout, colors, + keybinds, timing, error messages — it must be exercised in the local + VM before commit. Docs-only, comment-only, or agent-notes changes may + stop at the tier LOOP.md assigns them; user-visible changes may not. +2. **Every "done" report names the tier reached and shows the evidence.** + Evidence means: the command run and its relevant output, the checklist + items exercised, and for visual work the screenshots viewed (§3). "It + builds" is a V1 claim, not a V2 claim. Never let a report imply a + higher tier than was actually reached. +3. **A failed or flaky test is a result, not an obstacle.** Distinguish + real failures from environment flakes using the known-gotchas section + of `docs/TESTING.md` (e.g. no-KVM slowness, missing guest GL). If you + cannot confidently classify a failure, report it as unresolved — do + not retry until green and report only the green run. +4. **VM runs are headless and unattended.** Use the repo's headless + harness — `tools/test-live-iso.sh` and `tools/test-install.sh` for + boot/install runs, `tools/vm/qmp.py` for programmatic VM control and + `tools/vm/vncshot.py` for screen capture — never a graphical VM window + or any flow that needs a human at the console. The human is not part + of the test loop: do not pause mid-run to ask them to look at the VM, + click something, or confirm what is on screen. A run must complete on + its own and leave auditable artifacts behind (logs, serial console + output, exit codes, screenshots), with every wait bounded by a timeout + so a hang becomes a recorded failure instead of a stalled session. + Prefer scripted assertions (process up, file exists, service/D-Bus + state, the checks in `tools/`) over eyeballing; where judgment is + genuinely needed — visual quality — *you* view the captured + screenshots (§3), not the human. The human reviews evidence in the + final report, never the live run. + +### Regression scope after a change + +Re-running the full checklist for every change wastes VM time; running +nothing invites regressions. Default rule: + +- Always: the session-sanity items (boot to session, bar renders). +- Plus: every checklist item touching the layer you changed. +- Plus: the theming end-to-end item whenever theming plumbing changed, + even indirectly (palette generation, symlinks, reload hooks). +- Lock bumps and toolchain changes: run the full checklist — their blast + radius is unknowable by construction. + +## 3. Visual verification protocol + +Visual quality is a core feature of Nomarchy, so "it probably looks fine" +is never verification. A visual/UI change is not V2-verified until all of +the following are true: + +1. **Before/after screenshots** of the changed surface were captured + headlessly — `tools/theme-shot.nix` for reproducible theme renders, + `tools/vm/vncshot.py` (driven via `tools/vm/qmp.py`) for captures from + a running VM. No VM window, no human interaction. Capture the "before" + from the base branch or prior generation, not from memory. +2. **Scripted checks first**: run `tools/check-theme-contrast.py` and + `tools/audit-theme-design.py` against the affected theme(s) before any + eyeballing — machine-checkable legibility/design violations should + never survive to the judgment stage. Use `tools/vm/gap-analysis.py` + where it applies. +3. **Two themes**: repeat the "after" capture under at least two themes, + one with a generated palette and one whole-swap theme (e.g. + summer-night). These exercise different code paths in the bar/launcher + theming; a change that looks right under one can silently break the + other. +4. **You actually viewed the images** — open the screenshot files and + look at them. State concretely what you inspected: alignment, spacing, + contrast/legibility against the palette, icon rendering, no clipped or + overlapping elements, and that the change looks intentional next to + the "before". +5. Keep the screenshots in the run's working area and reference their + paths in the report, so the human can audit the same evidence. + +If the VM cannot render the surface faithfully (known GL/compositor gaps +in the guest — see `docs/TESTING.md`), that specific visual aspect is +hardware-blocked: verify everything the VM *can* show, and queue the rest +per §4. + +## 4. Hardware-blocked checks + +Some checks genuinely require real hardware (GPU behavior, multi-monitor +hotplug, audio devices, power/suspend, firmware). For those: + +1. Add an entry to `agent/HARDWARE-QUEUE.md` with: what changed, **exact** + reproduction steps a human can follow verbatim, the expected + observation (what "pass" looks like), and the commit hash once known. +2. Mark the commit body **"V3 pending: "**. +3. Say it plainly in your report. A hardware-blocked check is not a + failure and not something to hide — hiding it is the failure. +4. When the human reports back, close the queue entry in the next commit + and record the outcome; if it failed on hardware, that's a new bug at + the top of the backlog. + +Do not use the hardware queue as an escape hatch: if a check *can* be +done in the VM, it must be. "The VM is slow" does not qualify. + +## 5. Maintenance work + +Maintenance follows the same ladder: + +- **Flake lock bumps**: treat as maximum-blast-radius changes. Build, + boot the VM, run the full regression checklist, and do a visual + spot-check of the session (themes can shift with upstream package + changes). Never merge a lock bump on "it evaluates". +- **Theme imports / new themes**: import via `tools/import-palettes.py`, + then the full §3 visual protocol; additionally verify the theme-switch + round trip (into the new theme and back out). +- **Docs drift**: run `tools/check-option-docs.py` after any change that + adds or modifies options, and fix drift in the same commit as the code + change that created it. Doc-only fixes are V0 — but verify any command + you document by actually running it. +- **Backlog grooming / agent-notes**: V0; keep entries consistent with + the conventions in `agent/`. + +## 6. Guarding the philosophy + +Before committing, check the change against the distro's promises +(`agent/GOALS.md` is the full statement): + +- **User is not a Nix expert.** If the change requires the user to write + or read Nix to use the feature day-to-day, redesign it. Configuration + the user touches must stay in the simple, documented surface the repo + defines. +- **Rock-stable and recoverable.** Prefer boring, reproducible mechanisms + over clever ones. Any change that could break boot or the session must + have an obvious rollback story (NixOS generations count, but say so). +- **Aesthetics are load-bearing.** A functionally correct but visually + regressive change fails review by definition — that's what §3 is for. + +When a requested change conflicts with these promises, stop and raise the +conflict instead of implementing it quietly. + +## 7. Reporting format + +End every unit of work with a short report containing: + +1. What changed (one paragraph, plain language). +2. Verification tier reached, with evidence (commands + key output, + checklist items run, screenshot paths viewed). +3. Anything pending: "V2 pending" (environment) or "V3 pending" + (hardware, with queue entry reference). +4. Follow-ups added to the backlog, if any. diff --git a/docs/README.md b/docs/README.md index 0145dea..efbb502 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,9 +19,9 @@ tree for the same facts. | Path | Role | |------|------| -| [../agent/README.md](../agent/README.md) | **Executable** agent loop state: BACKLOG, LOOP, MEMORY, … | -| [../CLAUDE.md](../CLAUDE.md) | Agent entry point (any harness that reads it) | -| [../.claude/](../.claude/) | Claude Code only: permissions + subagent defs | +| [../AGENTS.md](../AGENTS.md) | Agent entry point, any vendor/harness (`CLAUDE.md` symlinks to it) | +| [../agent/README.md](../agent/README.md) | Agent instructions + **executable** loop state: BACKLOG, LOOP, VERIFICATION, … | +| [../.claude/](../.claude/) | Claude Code adapter only: permissions + subagent/skill shims | ## How work flows