aboutsummaryrefslogtreecommitdiff
path: root/todo.org
diff options
context:
space:
mode:
Diffstat (limited to 'todo.org')
-rw-r--r--todo.org213
1 files changed, 115 insertions, 98 deletions
diff --git a/todo.org b/todo.org
index dafc5a3..730da8e 100644
--- a/todo.org
+++ b/todo.org
@@ -21,26 +21,6 @@ The vocabulary is open — topic tags are coined as needed — so these are conv
- *Effort / autonomy*: =:quick:= a spare-moment fix (minutes, not a sitting); =:solo:= Claude can carry it end to end — there's a build path, a test path, and no upfront decision needed (a leftover manual spot-check doesn't disqualify it).
- *Topic / area* (open): the subsystem a task touches — e.g. =:hyprland:= =:waybar:= =:mpd:= =:music:= =:network:= =:tooling:= =:llm:= =:eask:= =:pocketbook:= =:cmail:=. Coin a new one when it aids filtering.
* Archsetup Open Work
-** DONE [#B] Panels moveable + resizable by drag :feature:waybar:network:bluetooth:
-CLOSED: [2026-07-04 Sat]
-Resolved by the 2026-07-03 instrument-console rebuild (dotfiles e993c3f): both net + bt panels switched from anchored gtk4-layer-shell overlays to normal floating windows (set_decorated(False), positioned by the net.cjennings.netpanel window rule), so Hyprland moves them on drag and resizes on corner-drag natively. That was exactly the "switch to a normal floating window" approach the design note flagged as the required decision.
-
-Both the net and bluetooth instrument-console panels should be repositionable and resizable at runtime: click-drag to move the panel anywhere on screen, drag the corners to resize. Raised from roam capture 2026-07-03.
-
-Design note: the panels are gtk4-layer-shell overlays anchored TOP+RIGHT with fixed margins — layer-shell surfaces are compositor-positioned, so free drag-move/resize needs either dynamic margin updates on pointer motion or a switch to a normal floating window (Hyprland moves/resizes those natively). Approach decision required before build.
-
-** CANCELLED [#B] Net panel wider initial width :waybar:network:quick:
-CLOSED: [2026-07-04 Sat]
-Superseded by the 2026-07-03 instrument-console rebuild (dotfiles e993c3f): the panel is now a floating, user-resizable window (set_default_size(420, 560)), no longer a right-anchored layer-shell surface. The task's mechanic ("keep the right edge fixed, extend the left border leftward") assumed the old anchored surface, which no longer exists — the width is now drag-adjustable. Cancelled per the 2026-07-04 audit (Craig's call to close rather than re-file a "bump the 420px default" task).
-
-Start the network panel a bit wider — keep the right edge fixed (it's right-anchored), extend the left border leftward. Raised from roam capture 2026-07-03.
-
-** DONE [#B] Net panel doctor results can't display :bug:waybar:network:
-CLOSED: [2026-07-04 Sat]
-Resolved by the 2026-07-03 instrument-console rebuild (dotfiles e993c3f): the panel gained a streaming output well (gui.py) with a "Copy results" button (via wl-copy) and a dismiss control that collapses the well back to the panel's pre-open height (_shrink_to_compact asks Hyprland to resize back). Doctor/speed-test output streams into it as appended lines — matching the task's ask for a tall results box, copy button, and collapse-back. This capture (filed 2026-07-03 morning) predates the same-day 22:06 redesign that addressed it.
-
-The doctor diagnostic output is unreadable — the results well is too constrained to show the multi-line result. It should open a results box tall enough for several lines with a copy-results button; closing it via an X in the box's upper-right collapses the space back to what it occupied before. Raised from roam capture 2026-07-03.
-
** TODO [#B] Scrolling/Carousel layout: frame fit + wrap-around :hyprland:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-13
@@ -64,6 +44,22 @@ Remaining work (re-graded C 2026-07-02 — the first-launch risk and the Makefil
- Waypaper canonical decision (Craig): live velox has =dark-lion.jpg=, repo has =that-one-up-there.jpg= (placeholder?). Decide the canonical, then =make reset= clears it.
- Ratio check: whether =~/.config/calibre/*.json= on ratio are symlinks or real files — same first-launch gap likely if calibre ever launched there before stow. On the ratio trip list.
+** DONE [#C] Instrument-console panel bugs (net/bt/audio) :bug:dotfiles:
+CLOSED: [2026-07-05 Sun]
+Batch from the roam inbox (2026-07-05). Panel code lives in =~/.dotfiles= (net/, bluetooth/, audio/). All eight shipped 2026-07-05, each verified live and covered by the panel test suites.
+*** 2026-07-05 Sun @ 17:49:51 -0400 Titled the panel windows Network/Bluetooth/Audio (was python3)
+The GTK app set no window title, so it fell back to the process name. dotfiles 2d03451.
+*** 2026-07-05 Sun @ 17:49:51 -0400 Faceplate word is the subsystem name, not live state (net/bt/audio)
+NETWORKING / BLUETOOTH / AUDIO; state now reads off the lamp colour + badges. Bluetooth's DOCTOR feedback (CHECKING/FIXING) moved to the status line and output well. dotfiles 5c58833.
+*** 2026-07-05 Sun @ 17:49:51 -0400 Net panel flags a signed-out Proton CLI as needs-login
+The proton probe reads =protonvpn info= first, since =protonvpn status= says Disconnected either way. The panel already blocks a needs-login row with a sign-in hint. dotfiles 2671472.
+*** 2026-07-05 Sun @ 17:49:51 -0400 Net panel sorts live tunnels to the top
+Stable within each backend group. dotfiles 2671472.
+*** 2026-07-05 Sun @ 17:49:51 -0400 Net panel refuses a second full-tunnel VPN while one is active
+tailscale is a mesh overlay, not a full tunnel, so it never conflicts. dotfiles 307a0fe.
+*** 2026-07-05 Sun @ 17:49:51 -0400 Net panel dedupes Proton's own wireguard row
+The proton CLI's =ProtonVPN <server>= NM profile no longer shows alongside the proton backend row. dotfiles dbc9ee8.
+
** TODO [#B] Audit dotfiles/common directory :chore:dotfiles:
:PROPERTIES:
:LAST_REVIEWED: 2026-07-02
@@ -198,14 +194,6 @@ From the 2026-07-04 roam capture. The waybar collapse mechanism (click the trian
:END:
Follow-up from the 2026-07-04 net-panel hardening speedrun (Craig's cj question on the no-WiFi item). The shipped no-wifi-hardware verdict covers "no adapter at all." This tier covers "adapter present but the driver is wedged": read-only health signals — =ip link= (device present but no-carrier / down), =dmesg= / =journalctl -k= for firmware-load failures, =rfkill= for a hard block, =modinfo= / =lsmod= for the driver module — classified before a generic reset. Remedy actions: a privileged =modprobe -r <mod> && modprobe <mod>= reload of the wifi driver, and a firmware-package pointer when the failure is a missing/failed firmware load. Dotfiles net-package work (handled per the archsetup-owns-dotfiles rule). Design pass first to decide whether it's worth a repair tier vs a needs-user-action pointer.
-** TODO [#B] Timer GTK panel :feature:waybar:
-:PROPERTIES:
-:LAST_REVIEWED: 2026-07-02
-:END:
-Initial spec written 2026-07-02: [[file:docs/specs/2026-07-02-timer-panel-spec.org]] (DRAFT — four decisions await Craig's review before build; net-panel Blueprint/GTK4 stack, wtimer stays the state owner).
-
-From Craig's roam capture 2026-07-02: give the timer a GTK UI/UX like the network panel.
-
** TODO [#B] Desktop-settings dropdown panel :waybar:
:PROPERTIES:
:LAST_REVIEWED: 2026-06-24
@@ -525,16 +513,6 @@ Read recommended resources to make informed security decisions (see metrics for
:END:
Practical guidelines for working in public spaces
-** CANCELLED [#B] Test each modernization thoroughly before replacing
-CLOSED: [2026-07-04 Sat]
-Retired in the 2026-07-04 audit (Craig's call): a standing-judgment umbrella with no completion criterion. The fleet is Hyprland-only now, and per-change test discipline is already carried by the actual work (TDD + the VM harness), so this adds nothing to track. Original intent: ensure new tools integrate with the Hyprland environment and don't break workflow (archsetup still supports DWM/X11 but no current machine uses it).
-
-** DONE [#C] Window focus lost when unhiding stashed windows :bug:hyprland:
-CLOSED: [2026-07-04 Sat]
-Verified fixed live on ratio 2026-07-04 (Craig at the machine). Stash (Super+O) → restore (Super+Shift+O) left the restored window focused, and Super+J/K (layout-navigate) cycled focus normally afterward — both original symptoms gone. Resolved by two fixes that postdated the filing: dotfiles 5619342 (raise window on focus nav, stop float focus-follow, 2026-06-28) and 09815f3 (cycle focus by address so j/k works in monocle, 2026-06-29). Both confirmed present in ratio's HEAD and in the live layout-navigate script at test time.
-
-From the roam inbox: hiding a window (e.g. the org-capture popup) then unhiding it should leave the unhidden window focused, but another window typically takes focus. Also =ctrl+j/k= (layout-navigate) can't reach the unhidden window afterward — it should always reach any visible window except the waybar. Involves stash-restore + layout-navigate; needs interactive reproduction with Craig. (Note: the actual bind is Super+J/K, not ctrl+j/k as the capture said.)
-
** TODO [#C] Ensure sleep/suspend works on laptops
:PROPERTIES:
:LAST_REVIEWED: 2026-06-09
@@ -609,6 +587,67 @@ Parse yay errors and provide specific, actionable fixes instead of generic error
Enhance existing indicators to show what's happening in real-time
** TODO Manual testing and validation
+*** Timer redesign: apply the package + wtimer, clear stale timers (precondition)
+What we're verifying: the redesigned =timer/= package + =wtimer= are live. Only a =git pull= is needed — the package .py files and =wtimer= are already stowed, and this build added no new launcher files, so no restow or reboot. Clear any pre-existing timers first so a stale-shape state file (an old pomodoro item lacking the new =cfg=) can't crash =wtimer render=.
+#+begin_src sh :results output
+cd ~/.dotfiles && git pull
+wtimer cancel-all
+#+end_src
+Expected: the pull succeeds and the bar timer glyph goes idle (no active items).
+*** Panel opens hero-on-top; close via the ✕, Esc, and the bar
+What we're verifying: the redesigned layout renders (header, hero, CONFIGURE, queue) and every close path works.
+- Click the timer module on the bar.
+Expected: the panel opens floating top-right — a header faceplate, a HERO block for the primary item, the CONFIGURE strip, then a QUEUE well below.
+- Click the ✕ in the header; reopen; press Esc; reopen; click the bar module again.
+Expected: each of ✕, Esc, and the bar click closes the panel.
+*** Timer with repeat
+What we're verifying: the REPEAT toggle re-arms a timer on fire instead of dropping it.
+- Pick TIMER, type =10s= (fast to observe), toggle REPEAT on, click +.
+Expected: the timer appears carrying a "repeat" badge and a "timer · repeats" sub-line.
+- Let it fire.
+Expected: it notifies and immediately re-arms to a fresh full countdown rather than vanishing.
+*** Alarm: recurring day, snooze, ringing, dismiss
+What we're verifying: a recurring alarm rings (not fire-and-vanish), snooze re-arms, and dismiss re-arms a recurring one.
+- Pick ALARM, type a clock time about a minute ahead (HH:MM), select today's weekday in the day picker, set SNOOZE to 1, click +.
+Expected: the row shows the fire clock time and a weekday badge; the hero donut is a countdown ring to the fire time.
+- Let it reach the fire time.
+Expected: the hero shows RING and the bar glyph goes urgent (RING); SNOOZE and DISMISS buttons appear on the hero.
+- Click SNOOZE.
+Expected: ringing stops and the alarm re-arms one minute out.
+- Let it ring again, click DISMISS.
+Expected: ringing stops; because it is recurring it re-arms to its next matching day (a one-shot alarm would be removed instead).
+*** Pomodoro: configurable cycle, auto vs await, cycle dots
+What we're verifying: the config grid drives the cycle and the AUTO toggle switches between auto-advance and awaiting a start.
+- Pick POMODORO, tap a named cycle (e.g. Deep) to fill the grid, set WORK S to 1 (min) for a fast run, toggle AUTO off, click + ADD CYCLE.
+Expected: a pomodoro starts in work; the hero shows cycle dots and "work · cycle 1/N".
+- Let the work phase fire with AUTO off.
+Expected: it enters the rest phase awaiting a start — the hero shows "ready · start break" and a START BREAK button, and it does not count down until pressed.
+- Press START BREAK.
+Expected: the rest phase counts down; the glyph/color shifts to the break (sage) state.
+*** Stopwatch: sweep dial, lap badge, promote, stop
+What we're verifying: the analog sweep dial animates, the last-lap ghost badge shows, promote works, and STOP just stops (run-save deferred).
+- Pick STOPWATCH, click +. If it isn't the hero, promote it (the ▲ on its queue row).
+Expected: the hero donut is an analog second-hand dial sweeping once per minute (not a percentage ring); the elapsed value counts up.
+- Hit LAP a couple of times (name one via the popover).
+Expected: a "LAP m:ss" ghost badge appears beside the elapsed value and the sub-line shows the lap count.
+- Hit STOP.
+Expected: the stopwatch is removed. No =~/org/stopwatch-runs.org= entry is written — run-save is deferred to a later version.
+*** Queue sort, promote/cycle, 10-item cap
+- Add a 25m timer, a 5m timer, and an alarm a few minutes out.
+Expected: the queue orders soonest-first; a ringing alarm jumps above everything.
+- Use ‹ / › on the hero (or ▲ on a queue row) to change the primary.
+Expected: the hero (bar-slot) item changes accordingly and the bar glyph follows.
+- Queue items until 10 exist.
+Expected: + disables with a "queue is full (10/10)" reason; running countdowns advance live (the =wtimer watch= subscription).
+*** Presets: locked defaults, half-past, custom add/delete
+- On TIMER, note the default chips carry no × (locked); add a custom preset via + preset, then delete it with its ×.
+Expected: locked defaults can't be deleted; a custom preset adds and deletes cleanly.
+- On ALARM, tap the half-past chip, click +.
+Expected: an alarm is created at the next :30.
+*** Bar tooltip parity
+What we're verifying: the bar tooltip mirrors the redesign verbatim.
+- With a pomodoro, a paused timer, and a ringing alarm active, hover the bar module.
+Expected: each item lists on its own line — the pomodoro as "label cycle/iv countdown" (no phase word), the paused one with a "(paused)" suffix, the ringing one as "RING (ringing)".
*** Audio panel: apply the new shims + configs (precondition for the tests below)
What we're verifying: the new =audio=/=audio-panel=/=waybar-audio= bin shims and the hyprland.conf + waybar config edits are live. New files need a restow (a plain =git pull= doesn't symlink them). Quit Hyprland or run from a TTY — restowing live Hyprland writes a stub hyprland.conf.
#+begin_src sh :results output
@@ -654,20 +693,8 @@ Expected: it reads as a sibling of the net and bt panels; nothing overflows the
- Right-click the waybar sound module.
Expected: the panel opens (left-click still mutes, scroll still changes volume).
-*** Timer dialog: Escape cancels at every step
-What we're verifying: Escape in the real fuzzel dialogs aborts the whole flow (the fix rides fuzzel's abort exit code; the unit tests fake it, this is the live confirmation).
-- Left-click the timer module on the bar, pick "timer", then hit Escape at the Duration prompt.
-Expected: no Label prompt appears and no timer is created (bar count unchanged).
-- Left-click again and hit Escape at the very first Timer-type menu, and once more at the Label prompt after entering a duration.
-Expected: each Escape ends the flow with nothing created.
-
-*** Timer alarm: 12-hour time accepted, bad input notifies
-What we're verifying: the alarm prompt takes 12h shapes live and errors are audible instead of silent.
-- Left-click the timer module, pick "alarm", enter 2:30pm (or any 12h form), Enter through the label.
-Expected: an alarm appears on the bar showing 14:30 as its fire time.
-- Repeat with garbage like 99:99.
-Expected: a fail notification names the bad input; nothing is created.
-- Cancel the test alarm (right-click the module, pick it).
+*** SUPERSEDED — Timer fuzzel dialogs (Escape-cancel, 12h alarm entry)
+These two tests exercised the old fuzzel creation dialogs, retired when the bar's =custom/timer= on-click became =timer-panel=. The panel redesign checklist above covers the same ground: alarm entry (12h shapes, bad-input reject) is now the panel's ALARM freeform + inline validation, and the whole-flow abort is the panel's Esc/✕/bar close. Nothing to run here; kept as a pointer so the intent isn't lost.
*** Speed test streams in the panel
What we're verifying: the panel's speed test fills in as phases complete instead of dumping everything at the end (the CLI path is live-verified; this is the GTK rendering).
@@ -677,7 +704,7 @@ Expected: a Ping/Download/Upload checklist appears under the running row; ping l
*** Proton VPN CLI sign-in (velox now, ratio on its trip)
What we're verifying: the proton CLI has its own account store (separate from the retired GTK app's), so the panel's proton rows can't toggle until you sign in once per machine.
-- Run in a terminal: protonvpn login <your-proton-username> (it prompts for the password).
+- Run in a terminal: protonvpn signin <your-proton-username> (it prompts for the password). The CLI's action is =signin=, not =login=.
#+begin_src sh :results output
protonvpn info
#+end_src
@@ -686,8 +713,8 @@ Expected: Account shows your Proton username instead of 'None'. After that, prot
*** Tunnels round-trip: panel rows + bar badge (first real tunnel-owned route)
What we're verifying: the panel's Tunnels tab drives a real wireguard tunnel up and down, and the bar indicator grows the vpn badge while the tunnel owns the default route (the badge has never rendered live — every prior check ran with the wlan owning the route).
- Open the net panel (left-click the bar's net module), switch Connections to the Tunnels page.
-- Confirm the rows: tailscale (up), and the seven Proton configs (USNY, USDC, USCALA, USCASF, USGAAT, switzerlan-zurich1/2), all down.
-- Select USNY, press Bring Up, wait for the row to land.
+- Confirm the rows: tailscale (up), and the three Proton configs (wg-US-TX-714, wg-US-CA-144, wg-NL-781), all down.
+- Select wg-US-TX-714, press Bring Up, wait for the row to land.
Expected: the bar's net glyph gains the small vpn badge; its tooltip names the owner ("Tunnel: default route via wgpvpn (wireguard)").
- Press Bring Down on the same row.
Expected: badge gone, tooltip back to normal, internet still works (the wlan owns the route again).
@@ -990,21 +1017,6 @@ Verified: the only =eval= left in =archsetup= is line 578 in =retry_install=, an
* Archsetup Resolved
-** DONE [#B] VM test harness shared one NVRAM file across filesystem profiles :bug:test:
-CLOSED: [2026-06-27 Sat]
-The harness shared one OVMF NVRAM file (=vm-images/OVMF_VARS.fd=) across the btrfs
-and zfs profiles (=init_vm_paths= suffixed the disk image per profile but not the
-NVRAM). NVRAM lives outside the qcow2, so a disk-snapshot revert can't restore it,
-and a zfs run's ZFSBootMenu boot entries clobbered the btrfs GRUB entry. With no
-removable =\EFI\BOOT\BOOTX64.EFI= fallback on the base ESP, the next btrfs run
-booted into UEFI with no bootable device ("BdsDxe: No bootable option or device
-was found", then PXE/HTTP, then SSH timeout before archsetup ran). Found
-2026-06-27 trying to VM-validate the installer refactor.
-
-Fixed: =OVMF_VARS= now carries the same per-profile suffix as the disk image
-(=OVMF_VARS${img_suffix}.fd=) in =vm-utils.sh init_vm_paths=, so btrfs and zfs keep
-separate NVRAM. Validated by a full green zfs run 2026-06-27 (ArchSetup exit 0,
-Testinfra 96 passed / 0 failed). Remaining hardening tracked below.
** DONE [#B] Guard against live mesa/hyprland/wayland-runtime updates :hyprland:
CLOSED: [2026-06-28 Sun]
:PROPERTIES:
@@ -1013,34 +1025,6 @@ CLOSED: [2026-06-28 Sun]
A live =pacman -Syu= that swaps mesa/hyprland/wayland runtime libs out from under a running Hyprland session can crash the compositor: the next GPU-lib call hits a now-"(deleted)" library and SIGABRTs, taking the Wayland clients down with it. Hit ratio 2026-06-07 (mesa 26.0.6 -> 26.1.2 + hyprland upgraded live; Hyprland SIGABRT took down awww/insync/emacs). Likely the driver behind ratio's high lifetime unsafe-shutdown ratio — a crashed compositor forces a hard reset.
Shipped as a pacman PreTransaction hook rather than a wrapper, so it fires no matter how the upgrade is launched (pacman, yay, topgrade). =scripts/hypr-live-update-guard= aborts the transaction before any package is swapped when the GPU/compositor runtime set is being upgraded AND Hyprland is running, pointing the user to re-run from a TTY with the session stopped; it stays quiet when Hyprland isn't running (the safe from-a-TTY path). Override via =HYPR_ALLOW_LIVE_UPDATE=1= or by touching the sentinel file named in the abort message. archsetup installs the script to =/usr/local/bin= and the hook to =/etc/pacman.d/hooks/= in the hyprland path. Decision logic unit-tested (=tests/hypr-live-update-guard=, 9 cases). Live firing test filed under Manual testing and validation. Commits: archsetup (this session).
-** DONE [#B] Collapsible waybar sides :waybar:
-CLOSED: [2026-06-27 Sat]
-:PROPERTIES:
-:LAST_REVIEWED: 2026-06-09
-:END:
-Let either side of the waybar collapse horizontally to a minimal base set, toggled by a click. Each collapsible side carries a small triangle / arrowhead pointing toward the screen edge it collapses into (away from center). Clicking it collapses that side to its base set and flips the arrow to point back toward center; clicking again restores the full side. Same shape-changes-with-state idea as the auto-dim indicator.
-
-Spec (2026-06-19): [[file:assets/2026-06-19-collapsible-waybar-sides-spec.org]]. Spike that settled the mechanism: [[file:assets/2026-06-18-collapsible-waybar-sides-spike-findings.org]].
-
-Decisions locked: right base set = date + worldclock + tray; left base set = menu + workspaces; per-side independent; host-agnostic (base set constant, full set is each host's existing config). Mechanism = config-swap + SIGUSR2 reload via an active-config copy in =$XDG_RUNTIME_DIR= (the CSS/state-file approach was disproven — GTK3 can't reflow-hide native modules). Lives in =~/.dotfiles/hyprland/=.
-
-Shipped per spec (dotfiles 804bef6): 3 TDD'd scripts (=waybar-active-config=, =waybar-collapse=, =waybar-arrow=; 22 cases), arrow modules wired into the config (left arrow innermost-left, right arrow innermost-right), CSS ×3, =$mod+[= / =$mod+]= keybinds, and =waybar-toggle= relaunch updated to load the active config so a crash preserves collapse state. Verified live: click, keybind, and per-side independence all work; expand round-trips exactly to canonical.
-** DONE [#C] Collapse waybar sysmonitor to a single icon + hover :feature:waybar:
-CLOSED: [2026-06-27 Sat]
-:PROPERTIES:
-:LAST_REVIEWED: 2026-06-24
-:END:
-From the roam inbox (2026-06-22): replace the spread-out sysmonitor readouts (temp, cpu, mem, storage) with one visible icon showing a single chosen metric, the rest in the hover tooltip. Open question: fold it into the battery component instead of a standalone module. Implementation lives in the waybar config under ~/.dotfiles.
-
-Shipped as a standalone =custom/sysmon= module (Craig's call: host-dependent primary — battery on laptop, disk on desktop — rather than fold into battery, which is laptop-only). Backing script =waybar-sysmon= gathers cpu/temp/mem/disk/battery, shows the host-appropriate metric, rest in tooltip; 13-case TDD suite; removed the 5 native modules + their CSS across all 3 themes. Dotfiles be7469b.
-** DONE [#C] Rename idle inhibitor to something more intuitive :chore:waybar:
-CLOSED: [2026-06-27 Sat]
-:PROPERTIES:
-:LAST_REVIEWED: 2026-06-24
-:END:
-From the roam inbox (2026-06-24): the "idle inhibitor" name doesn't work as a mnemonic — something like "sleep" (i.e. "keep awake" / "no-sleep") would land better. Decide the new name, then rename across the touchpoints: the =custom/idle= waybar module, the keybind mnemonic, and the backing script names (=hypridle-toggle= / =waybar-idle= from the 2026-06-24 idle-inhibitor work). Needs Craig's call on the name first, so not solo.
-
-Renamed to "caffeine" (Craig's call, 2026-06-27): =custom/caffeine= module, =waybar-caffeine= + =caffeine-toggle= scripts, tooltip "Caffeine: ON/OFF", CSS + test suites updated. Keybind stays =$mod+I= (=$mod+C= is hyprpicker). Shipped in dotfiles 8b45b51.
** DONE [#B] ZFS pre-pacman snapshot installer step (ZFS-root) :feature:zfs:
CLOSED: [2026-06-30 Tue]
Add a ZFS-root-gated installer step that installs the pre-pacman snapshot pacman hook plus a self-pruning =/usr/local/bin/zfs-pre-snapshot= (KEEP=10). The script is hand-placed on velox, not authored by archsetup, so a reinstall loses it; snapshots accumulated unbounded (53 since April) because nothing prunes them and Sanoid ignores non-autosnap_ names. Gate to ZFS-root (velox; ratio is btrfs). Also correct the stale 2026-01-17 security-doc line claiming it's "already in install-archzfs". Needs the hook file (source from velox) and a ZFS-root VM test.
@@ -1720,3 +1704,36 @@ CLOSED: [2026-07-03 Fri]
Went past the spec to a full build in a no-approvals speedrun. Spec is now IMPLEMENTED ([[file:docs/specs/2026-07-03-audio-panel-spec.org]], all 5 Decisions resolved). The panel shipped in the dotfiles repo (branch panel-bugfixing, commits 65e5bb0..9601420): pactl engine, GTK-free presenter, GTK instrument-console panel (OUTPUTS/INPUTS device rows with faders + per-device mute, LIVE/MUTED/PUSH·TALK mic keys, twin VU gauges, master quick-mute), Hyprland-bind push-to-talk, bar indicator, and the bar/keybind wiring (Super+A → panel, XF86AudioMute → master quick-mute). 102 unit tests + a passing AT-SPI smoke on velox. Live-eyeball validation filed under Manual testing and validation. Apply steps + follow-ups handed to the dotfiles project inbox.
Original ask (roam inbox, 2026-07-02): net/bt-panel kin — change default output/input, volume for both, push-to-talk mic mode for meetings, master quick-mute, bar sound-glyph state. Related bindings: Super+M audio-cycle ring, Super+Shift+A mic-toggle. Prototype: =docs/prototypes/2026-07-03-sound-panel-prototype.html=.
+** DONE [#B] Panels moveable + resizable by drag :feature:waybar:network:bluetooth:
+CLOSED: [2026-07-04 Sat]
+Resolved by the 2026-07-03 instrument-console rebuild (dotfiles e993c3f): both net + bt panels switched from anchored gtk4-layer-shell overlays to normal floating windows (set_decorated(False), positioned by the net.cjennings.netpanel window rule), so Hyprland moves them on drag and resizes on corner-drag natively. That was exactly the "switch to a normal floating window" approach the design note flagged as the required decision.
+
+Both the net and bluetooth instrument-console panels should be repositionable and resizable at runtime: click-drag to move the panel anywhere on screen, drag the corners to resize. Raised from roam capture 2026-07-03.
+
+Design note: the panels are gtk4-layer-shell overlays anchored TOP+RIGHT with fixed margins — layer-shell surfaces are compositor-positioned, so free drag-move/resize needs either dynamic margin updates on pointer motion or a switch to a normal floating window (Hyprland moves/resizes those natively). Approach decision required before build.
+** CANCELLED [#B] Net panel wider initial width :waybar:network:quick:
+CLOSED: [2026-07-04 Sat]
+Superseded by the 2026-07-03 instrument-console rebuild (dotfiles e993c3f): the panel is now a floating, user-resizable window (set_default_size(420, 560)), no longer a right-anchored layer-shell surface. The task's mechanic ("keep the right edge fixed, extend the left border leftward") assumed the old anchored surface, which no longer exists — the width is now drag-adjustable. Cancelled per the 2026-07-04 audit (Craig's call to close rather than re-file a "bump the 420px default" task).
+
+Start the network panel a bit wider — keep the right edge fixed (it's right-anchored), extend the left border leftward. Raised from roam capture 2026-07-03.
+** DONE [#B] Net panel doctor results can't display :bug:waybar:network:
+CLOSED: [2026-07-04 Sat]
+Resolved by the 2026-07-03 instrument-console rebuild (dotfiles e993c3f): the panel gained a streaming output well (gui.py) with a "Copy results" button (via wl-copy) and a dismiss control that collapses the well back to the panel's pre-open height (_shrink_to_compact asks Hyprland to resize back). Doctor/speed-test output streams into it as appended lines — matching the task's ask for a tall results box, copy button, and collapse-back. This capture (filed 2026-07-03 morning) predates the same-day 22:06 redesign that addressed it.
+
+The doctor diagnostic output is unreadable — the results well is too constrained to show the multi-line result. It should open a results box tall enough for several lines with a copy-results button; closing it via an X in the box's upper-right collapses the space back to what it occupied before. Raised from roam capture 2026-07-03.
+** DONE [#B] Timer GTK panel :feature:waybar:
+CLOSED: [2026-07-05 Sun]
+Built and shipped to dotfiles 2026-07-05 in a no-approvals speedrun (4 commits =1f4f270=..=78d3cbb=): wtimer gained watch/lap/save; a new =timer/= package holds a GTK-free PanelModel (62 tests) and the GTK instrument-console panel; the bar's =custom/timer= now opens the panel and the fuzzel creation flow retired. Spec: [[file:docs/specs/2026-07-02-timer-panel-spec.org]] (IMPLEMENTED). Code-complete; live GTK verification filed under Manual testing and validation below.
+
+From Craig's roam capture 2026-07-02: give the timer a GTK UI/UX like the network panel. Scope expanded via a later cj comment (queue/output-wall auto-sorted by fire time, stopwatch lap/stop + saveable runs, 5/25 configurable defaults, up to 10 timers, widget-gallery elements) — folded into the spec's Build scope and shipped.
+
+*** 2026-07-05 Sun @ 07:20:20 -0500 Redesign shipped — hero-on-top rebuild
+The UI/UX redesign (decided through the prototype process, final = [[file:docs/prototypes/2026-07-02-timer-panel-prototype-3.html]]) built and shipped to dotfiles in a no-approvals speedrun, 5 commits =c7ac193=..=5a863b5=: Phase 1 wtimer engine (timer repeat; recurring alarms with snooze/ringing/dismiss; =@half=/=@hour=/=+dur= alarm parse; the rebuilt configurable pomodoro cycle — work/rest short+long, long-every-N, auto vs awaiting); Phase 2 PanelModel view-data (=row_view=, ringing-first sort, per-type create options as wtimer flags, locked presets + half-past + named pomodoro cycles); Phase 3 GTK hero-on-top panel (Cairo progress ring + stopwatch analog sweep dial, per-type create strips, one transport row, close ✕/Esc); Phase 4 bar-tooltip parity. wtimer + timer suites 231 green, full =make test= green. Spec re-flipped DOING → IMPLEMENTED. Stopwatch run-save deferred to vNext. Live GTK render is the refreshed manual checklist below.
+** CANCELLED [#B] Test each modernization thoroughly before replacing
+CLOSED: [2026-07-04 Sat]
+Retired in the 2026-07-04 audit (Craig's call): a standing-judgment umbrella with no completion criterion. The fleet is Hyprland-only now, and per-change test discipline is already carried by the actual work (TDD + the VM harness), so this adds nothing to track. Original intent: ensure new tools integrate with the Hyprland environment and don't break workflow (archsetup still supports DWM/X11 but no current machine uses it).
+** DONE [#C] Window focus lost when unhiding stashed windows :bug:hyprland:
+CLOSED: [2026-07-04 Sat]
+Verified fixed live on ratio 2026-07-04 (Craig at the machine). Stash (Super+O) → restore (Super+Shift+O) left the restored window focused, and Super+J/K (layout-navigate) cycled focus normally afterward — both original symptoms gone. Resolved by two fixes that postdated the filing: dotfiles 5619342 (raise window on focus nav, stop float focus-follow, 2026-06-28) and 09815f3 (cycle focus by address so j/k works in monocle, 2026-06-29). Both confirmed present in ratio's HEAD and in the live layout-navigate script at test time.
+
+From the roam inbox: hiding a window (e.g. the org-capture popup) then unhiding it should leave the unhidden window focused, but another window typically takes focus. Also =ctrl+j/k= (layout-navigate) can't reach the unhidden window afterward — it should always reach any visible window except the waybar. Involves stash-restore + layout-navigate; needs interactive reproduction with Craig. (Note: the actual bind is Super+J/K, not ctrl+j/k as the capture said.)