aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-26 14:05:40 -0500
committerCraig Jennings <c@cjennings.net>2026-05-26 14:05:40 -0500
commit5d3c5525f4b8bf742448d3a4be160f8c9ecdf4ec (patch)
tree9b138f2aae691d2bd7994de95b061805c3caae92 /docs
parent70e89e946cbdff307284d11a46558161f713607c (diff)
downloadarchsetup-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.
Diffstat (limited to 'docs')
-rw-r--r--docs/PLAN-per-host-overrides.org217
1 files changed, 217 insertions, 0 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?