diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-26 14:05:40 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-26 14:05:40 -0500 |
| commit | 5d3c5525f4b8bf742448d3a4be160f8c9ecdf4ec (patch) | |
| tree | 9b138f2aae691d2bd7994de95b061805c3caae92 | |
| parent | 70e89e946cbdff307284d11a46558161f713607c (diff) | |
| download | archsetup-5d3c5525f4b8bf742448d3a4be160f8c9ecdf4ec.tar.gz archsetup-5d3c5525f4b8bf742448d3a4be160f8c9ecdf4ec.zip | |
docs: add per-host override spec and backlog tasks
I added a spec for a per-machine override mechanism (docs/PLAN-per-host-overrides.org). It proposes per-host stow tiers keyed on hostname, with the existing conf.d glob as the first tenant, so HiDPI scaling can differ between ratio and velox without per-app patches. It's gated on review.
I also updated the task list: added a pocketbook development backlog and a waybar timer module task, fleshed out the per-host task with a spec link, and rewrote the stale velox "pocketbook not installed" review item as a dated entry now that nothing installs it.
| -rw-r--r-- | docs/PLAN-per-host-overrides.org | 217 | ||||
| -rw-r--r-- | todo.org | 47 |
2 files changed, 260 insertions, 4 deletions
diff --git a/docs/PLAN-per-host-overrides.org b/docs/PLAN-per-host-overrides.org new file mode 100644 index 0000000..fdeb3d1 --- /dev/null +++ b/docs/PLAN-per-host-overrides.org @@ -0,0 +1,217 @@ +#+TITLE: Per-Host Override Mechanism for the Dotfiles Repo +#+AUTHOR: Craig Jennings & Claude +#+DATE: 2026-05-26 + +* Status + +| Field | Value | +|--------------+-------------------------------------------------------------| +| State | Draft — gated on review before implementation | +| Trigger | Zoom launched enormous on ratio after a per-app QT_SCALE_FACTOR=1.5 patch meant for velox | +| Supersedes | The fragile "local real files shadowing stow" pattern on velox | +| Related task | =todo.org= → "Cleaner per-machine override mechanism for the dotfiles repo" | +| Related spec | [[file:PLAN-dotfiles-separation.org][PLAN-dotfiles-separation.org]] (the repo this extends) | + +* Problem + +The =~/.dotfiles= repo stows the same =common/= + =hyprland/= trees to every +machine. Both hosts (ratio = desktop, velox = Framework 13 laptop) symlink +identical files. There is no mechanism for a setting to differ per machine, and +the two machines genuinely differ in one structural way: *velox is HiDPI +(2256×1504), ratio is not.* + +Two concrete failures this causes: + +1. *Per-app scaling patches leak across hosts.* Someone set + =Exec=env QT_SCALE_FACTOR=1.5 /usr/bin/zoom %U= in + =hyprland/.local/share/applications/Zoom.desktop= to enlarge Zoom on velox's + HiDPI panel. Because that desktop file is shared via stow, the 1.5× also + applied on ratio, where Zoom then opened enormous. (Reverted to plain + =/usr/bin/zoom %U= on 2026-05-26; this spec is the durable fix for the + underlying need.) + +2. *velox's laptop overrides are fragile real files.* velox keeps + laptop-specific configs — =foot.ini= font size, =pypr= scratchpad sizing for + 2256×1504, waybar battery module — as *local real files shadowing the stow + symlinks*. Any =make restow= on velox re-conflicts: stow aborts on the real + files (hit exactly this during the 2026-05-22 migration). The override + survives only via manual backup/restore around every restow. + +* Background — how scaling works today + +Two lines in =hyprland/.config/hypr/hyprland.conf= set the scaling regime: + +- =monitor=,preferred,auto,auto= — Hyprland auto-scales each monitor by its DPI. + *Wayland-native apps already adapt per-machine automatically* — velox gets + fractional scaling, ratio gets 1×, with no config difference. Native apps are + not the problem. +- =xwayland { force_zero_scaling = true }= — XWayland apps (Zoom, any X11 app) + are deliberately *not* scaled by the compositor, so they stay sharp instead of + blurry-upscaled. The cost: on a HiDPI display they render tiny unless the app + scales itself via toolkit env vars (=GDK_SCALE=, =QT_SCALE_FACTOR=). + +So XWayland scaling has to come from environment variables, and *those env vars +need to differ per host.* The per-app =QT_SCALE_FACTOR= hack was the wrong layer +(one app, shared across hosts). The right layer is one per-host setting that +covers every XWayland app at once. + +* The seam already exists + +=hyprland.conf= line 342 already sources a glob: + +#+begin_src conf +source = $HOME/.config/hypr/conf.d/*.conf +#+end_src + +and the repo already ships =hyprland/.config/hypr/conf.d/local.conf= as a +"machine-local overrides" template (monitor scaling, keybinds, etc.). Two +properties make this the ideal mechanism: + +- *Glob source tolerates absence.* A host with no matching file just sources + nothing extra — no Hyprland error banner. +- *Last-wins.* Values set in =conf.d/local.conf= override the same keys set + earlier in =hyprland.conf=. + +The only flaw: =local.conf= currently lives in the *shared* =hyprland/= tier, so +it is one symlink shared by both machines. Editing it (through the symlink) +rewrites the single repo file and applies everywhere — and re-conflicts on +restow. *The fix is to make =local.conf= a per-host file instead of a shared +one.* + +* Goal + +A per-machine override that: + +1. Survives =make restow= without manual backup/restore. +2. Is version-controlled in the dotfiles repo (a fresh install of that host + gets its overrides automatically). +3. Covers the immediate HiDPI/XWayland-scaling need on velox. +4. Generalizes to the other per-host divergences (foot font, pypr sizing, + waybar battery) without per-app hacks. + +* Proposed mechanism — a per-host stow tier + +Add host-named stow packages alongside the existing =common/ dwm/ hyprland/ +minimal/= tiers: + +#+begin_example +~/.dotfiles/ + common/ + dwm/ + hyprland/ + minimal/ + ratio/ ← new: ratio-only files + velox/ ← new: velox-only files +#+end_example + +=make stow hyprland= stows =common + hyprland + <hostname>=. The host tier holds +*only files that exist nowhere else in the stow set*, so stow never conflicts. + +** Why no conflict + +GNU Stow refuses when two packages provide the same target path. So the host +tier cannot re-own a file already in =common/= or =hyprland/=. That constraint +is exactly why =conf.d/local.conf= is the right first tenant: once it is +*removed* from =hyprland/=, it exists *only* in the host tiers, and each host +symlinks its own copy with zero conflict. The Hyprland glob does the merge at +runtime. + +** First tenant: Hyprland per-host config (solves the scaling trigger) + +- Remove =local.conf= from =hyprland/.config/hypr/conf.d/=. +- Add =ratio/.config/hypr/conf.d/local.conf= and + =velox/.config/hypr/conf.d/local.conf=. + +=velox/...local.conf=: + +#+begin_src conf +# velox — Framework 13, HiDPI 2256×1504 +monitor = eDP-1,preferred,auto,1.566667 + +# XWayland apps don't scale via the compositor (force_zero_scaling=true), +# so scale the toolkits explicitly. Inherited by every app launched in the +# session, including XWayland clients spawned by the browser (e.g. Zoom). +env = GDK_SCALE,1.5 +env = QT_SCALE_FACTOR,1.5 +env = XCURSOR_SIZE,36 +#+end_src + +=ratio/...local.conf=: + +#+begin_src conf +# ratio — desktop, 1× scaling. Defaults in hyprland.conf are correct; +# nothing to override yet. File present so the host tier always has content. +#+end_src + +This removes the need for any per-app =QT_SCALE_FACTOR= in =.desktop= files. + +** Open design question: whole-file app configs + +=conf.d/local.conf= works cleanly because Hyprland merges fragments at runtime. +The other per-host divergences are *monolithic files* already present in a +shared tier, which a host tier cannot re-own via stow: + +| File | Native include? | Approach | +|------------------------------+----------------------------+----------| +| =foot.ini= | Yes — =include=path= | Keep base in shared tier; add =include=~/.config/foot/host.ini=; ship =host.ini= per host tier | +| waybar =config= (JSON) | No include directive | Either move the whole file into host tiers, or keep a documented local-override + =.stow-local-ignore= | +| pypr =config.toml= | No include directive | Same choice as waybar | + +Three candidate strategies for the no-include files, to decide in review: + +1. *Move the whole file into host tiers.* Cleanest at runtime, but duplicates + the ~95% shared content across =ratio/= and =velox/= — drift risk. +2. *Documented local-override + =.stow-local-ignore=.* Keep the shared file, + let the host keep a real file, and add the path to =.stow-local-ignore= so + restow skips it instead of aborting. Removes the fragility (no more abort) + but the override stays out of version control. +3. *Generator step.* A =make host-config= target (or an archsetup install step) + that assembles the per-host file from a shared base + a host fragment. Most + flexible, most machinery. + +Recommendation to start: strategy 1 for files with no include directive (small +count, low churn), native =include== for foot. Revisit if duplication drift +becomes real. + +* Implementation outline + +Gated on review. Rough phases: + +1. *Makefile — host tier.* Detect the host (=HOST := $(shell uname -n)= — note + the =hostname= binary is absent on these boxes; =uname -n= and + =cat /etc/hostname= both work). Add =$(HOST)= to the package list in =stow=, + =restow=, =reset=, =unstow=, guarded so a host with no package is skipped + with a clear message rather than a stow error. Update =help= text and the + archsetup =CLAUDE.md= Makefile section. + +2. *Hyprland per-host config.* Remove =local.conf= from =hyprland/=; create + =ratio/= and =velox/= host tiers each with =.config/hypr/conf.d/local.conf=. + Populate velox's with the HiDPI monitor scale + toolkit env; ratio's minimal. + +3. *Verify XWayland env propagation.* Confirm =env == lines in =conf.d= + actually reach XWayland clients launched outside the compositor (e.g. Zoom + spawned by the browser). Hyprland sets =env= for its children; the browser is + a session child, so its children should inherit. Verify on velox: launch Zoom + from a link, confirm normal size with no per-app patch. + +4. *Migrate velox's fragile overrides.* Move foot font / pypr sizing / waybar + battery off the local-real-file pattern into the chosen strategy from the + open question above. + +5. *archsetup install integration (optional follow-on).* Teach the installer to + stow the host tier for =DESKTOP_ENV in {dwm,hyprland}=. Decide whether the + host name comes from =/etc/hostname= at install time or a new conf key. + +* Open questions for Craig + +1. Host detection: =uname -n= / =/etc/hostname= auto-detect (proposed), or an + explicit =HOST== Makefile override / conf key? +2. Whole-file overrides (waybar, pypr): strategy 1 (whole file in host tier), + strategy 2 (=.stow-local-ignore= + local real file), or strategy 3 + (generator)? See the table above. +3. Scope now: just the Hyprland scaling fix (phases 1–3), or migrate velox's + existing overrides in the same pass (phase 4)? +4. Should =make stow= *always* include the host tier silently, or require it be + named (=make stow hyprland velox=) to keep the behavior explicit? +5. Does the archsetup installer need to know about host tiers (phase 5), or is + this a post-install =make= concern only? @@ -46,6 +46,42 @@ The =protonmail-bridge= package ships an enabled systemd user service (=/usr/lib Fix applied per-machine 2026-05-22: =systemctl --user disable --now protonmail-bridge.service=, leaving the Hyprland exec-once GUI as the sole bridge (tray icon returns, served cert matches, =mbsync -a= clean). A fresh install re-enables the package service, so make it durable: mask/disable =protonmail-bridge.service= during install (likely in =scripts/cmail-setup-finish.sh=) and document that the Hyprland exec-once is the intended launcher — never run both. Source: handoff from .emacs.d 2026-05-22. +** TODO [#C] Pocketbook development backlog :pocketbook: +:PROPERTIES: +:LAST_REVIEWED: 2026-05-26 +:END: +Pocketbook (GTK4 layer-shell notes panel, toggled via waybar) was pulled from publication 2026-05-26 — github repo + cjennings.net repo deleted, mirror hook removed — and folded into this repo at =pocketbook/= until it's ready to spin back out. Src-layout Python package with pytest tests and a Makefile. Develop it in-tree; the backing modules are =store/note/panel/layer_shell/app/note_widget= + =style.css=. + +Backlog (unordered; promote items to their own dated tasks as they're picked up): + +- Configurable options, possibly a dedicated configuration panel. +- Lose-focus hides pocketbook — configurable on/off. +- Configurable display order: chronological by creation date (asc/desc), manual, alphabetical (asc/desc). +- Search / filter notes. +- Global toggle keybind (Hyprland =bind=) alongside the waybar click; document the waybar integration. +- Note CRUD polish (create/edit/delete) + optional markdown rendering. +- Pin / favorite notes. +- Tags or notebooks / categories. +- Persistence: confirm store format + =~/.local/share/pocketbook/= location, add versioning/migration, decide a backup/sync story. +- Theming: track the dupre/hudson theme system so =style.css= follows =set-theme=. +- Layer-shell geometry config (anchor edge, width, margins) + HiDPI / multi-monitor behavior — ties into [[file:docs/PLAN-per-host-overrides.org][per-host overrides]] scaling work. +- Config file format (toml) + reload-without-restart. +- Expand test coverage (TDD per testing standards; =tests/= already exists). +- Release prep for the eventual spin-back-out: pyproject metadata, version, license. +- Re-wire the archsetup install (gtk4-layer-shell dep + install step + post-install clone) when pocketbook ships. Removed 2026-05-26 — see git history of =archsetup= / =scripts/post-install.sh=. + +** TODO [#B] Waybar timer module :waybar: +:PROPERTIES: +:LAST_REVIEWED: 2026-05-26 +:END: +A custom waybar module providing three time-keeping functions, surfaced in the bar with click/scroll controls and dunst notifications on completion. + +- *Alarm* — fire a notification at a wall-clock time (e.g. 2:00pm). Builds on the existing =notify= + =at= pattern from protocols.org. +- *Timer* — count down a duration (e.g. 25m) and notify when it elapses. +- *Pomodoro* — alternating work/break cycles (default 25/5, long break after 4) with the bar showing phase + remaining time. + +Implementation notes (to flesh out when picked up): waybar =custom= module(s) with =exec= polling or a persistent =exec= script emitting JSON; click actions to start/pause/reset; a small state file under =~/.local/state= or =~/.local/var=. Lives in the hyprland tier (=dotfiles/hyprland/.config/waybar/= + a backing script in =hyprland/.local/bin/=). TDD the backing script per testing.md. + ** DOING [#A] Separate dotfiles from archsetup SCHEDULED: <2026-05-21 Thu> :PROPERTIES: @@ -97,6 +133,11 @@ Now unblocked (ratio + velox both migrated). Remove =dotfiles/= from the archset *** TODO [#B] Cleaner per-machine override mechanism for the dotfiles repo velox keeps laptop-specific configs (foot font, pypr scratchpad sizing for 2256x1504, waybar battery) as local REAL files shadowing the stow symlinks. That's fragile: any =make restow= on velox re-conflicts (hit exactly this during the 2026-05-22 migration — stow aborts on the real files). The =~/.dotfiles= model needs a real per-machine override story (a =minimal/=-style per-host package, a documented local-override convention with =.stow-local-ignore=, or host-conditional includes) so overrides survive restows without manual backup/restore. +Spec: [[file:docs/PLAN-per-host-overrides.org][docs/PLAN-per-host-overrides.org]] — gated on review before implementation. + +**** 2026-05-26 Tue @ 10:21:08 -0500 AI Response: Spec written, gated on review +Surfaced by a HiDPI scaling failure: a per-app =QT_SCALE_FACTOR=1.5= in the shared =Zoom.desktop= (meant for velox) made Zoom open enormous on ratio. Reverted that patch to plain =/usr/bin/zoom %U=; the durable fix is this mechanism. Proposed approach: a per-host stow tier (=ratio/=, =velox/=) stowed as =common + hyprland + $(uname -n)=, with the existing =conf.d/*.conf= glob as the first clean tenant — move =local.conf= out of the shared =hyprland/= tier into per-host tiers so each machine gets its own (HiDPI monitor scale + =env = QT_SCALE_FACTOR/GDK_SCALE= on velox, minimal on ratio). XWayland apps don't scale via the compositor (=force_zero_scaling=true=), so toolkit env vars set in =conf.d= are the right layer — kills per-app =.desktop= hacks. Open question in the spec: whole-file configs with no include directive (waybar JSON, pypr toml) need a separate strategy. Full design + 5 open questions for Craig in the spec. + *** TODO [#A] Verify Phase 2 in the VM (hyprland + none) — pending clean run Phase 2 shipped without a full VM integration pass: four runs hit env issues (pacman timeout, an SSH drop at 60 min, then a port-2222 collision with an active archangel VM). The fixed clone code was exercised by run 3 and run 4 ran 60 min past it, but the stow-per-DESKTOP_ENV paths — especially =none=/minimal, which no run reached — are unverified end-to-end. Close the gap: =make test= (hyprland) and a =DESKTOP_ENV=none= run, once port 2222 is free. @@ -289,10 +330,8 @@ Paired Logi M650 via ~bluetoothctl scan on~, ~pair~, ~trust~. This is inherently interactive (scan, select device, pair, trust). Consider: document as post-install step. No automation possible. -*** TODO Review: pocketbook not installed -Custom Python package from ~/code/pocketbook needed ~pip install --user -e .~ -Data in ~/.local/share/pocketbook/ needed to be copied from ratio. -Consider: document in post-install steps (not automatable in archsetup). +*** 2026-05-26 Tue @ 13:32:31 -0500 pocketbook install concern moot — pulled from publication, folded in-tree +Resolved by removing pocketbook from archsetup's provisioning entirely. It's nowhere near ready, so the github mirror + cjennings.net repo were deleted and the project was folded into the archsetup tree at =pocketbook/=. Dropped the =gtk4-layer-shell= dep + =pip_install= from =archsetup= and the clone from =scripts/post-install.sh=. No fresh install pulls pocketbook now, so "not installed on velox" no longer applies. Re-wiring the install is tracked in the new pocketbook development backlog. *** TODO Review: Tailscale needs login after install ~tailscaled~ service was enabled but needed ~tailscale up~ for interactive auth. |
