diff options
| author | Craig Jennings <c@cjennings.net> | 2026-08-26 16:22:37 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-08-26 16:22:37 -0600 |
| commit | 3df22bf89d9d808ffd49eb38c7ef06f4f6e0e33b (patch) | |
| tree | 8f2419cd523c39cf4dead44c2a7762adcebcd6e0 | |
| parent | e777ddca34f5ea8040d9905c55b853a7089d6682 (diff) | |
| download | archsetup-3df22bf89d9d808ffd49eb38c7ef06f4f6e0e33b.tar.gz archsetup-3df22bf89d9d808ffd49eb38c7ef06f4f6e0e33b.zip | |
chore(tasks): file the velox hibernate drain diagnosis and archive three done clusters
Two overnight hibernates ended with a dead battery. The journal and upower
history point at three separate faults, so I filed them as three children of
the sleep task: the Framework 13 AMD freeze on hibernate entry (with a
five-cycle observed test), the ZFS ARC starving the image preallocation so
suspend-then-hibernate only ever suspends, and the SD reader's media polling
blocking suspend outright. Until the freeze is confirmed the rule is written
down: no unattended hibernate on battery. Archived three finished clusters.
| -rw-r--r-- | archive/task-archive.org | 47 | ||||
| -rw-r--r-- | todo.org | 189 |
2 files changed, 189 insertions, 47 deletions
diff --git a/archive/task-archive.org b/archive/task-archive.org index 1dc69e1..7db6c88 100644 --- a/archive/task-archive.org +++ b/archive/task-archive.org @@ -2619,3 +2619,50 @@ CLOSED: [2026-07-24 Fri] From the roam inbox (Craig, 2026-07-24 via work), a research idea: Craig wants to save a specific window+app arrangement and have it reappear on demand. What has to be known and built to make that happen — is there prior art (another WM or OS that does session/layout save-restore), what information do those need (app identity, geometry, workspace, launch command), and what are their rules. Explore how far Hyprland can get (hyprctl clients + dispatch, exec rules, window rules by class/title), document thoroughly, and review with Craig next time. Not :solo: — the deliverable is an assessment he reads and decides on, and it may spawn a build task once the shape is clear. Offer to file the build separately if part of it turns out urgent. RESEARCHED, dotfiles =8cf4728=: =docs/2026-07-24-window-config-save-recall-assessment.org=. Prior art surveyed (i3/sway =append_layout= swallow, KDE window rules, macOS Moom). Three tiers from cheapest: (1) reposition open windows — buildable + testable now; (2) relaunch + place by class rule; (3) full swallow-by-title, which hits the same-class ambiguity every tool hands back to the user. Recommends shipping tier 1; tiers 2-3 need Craig's call on how much manual disambiguation he'll accept. +** DONE [#B] Weather tooltip caching :feature:waybar:weather:solo: +CLOSED: [2026-07-25 Sat 10:53] +From the roam inbox (Craig, claimed 2026-07-22): retrieve the weather tooltip data once per hour and cache it. If the network is unavailable, display the cached tooltip with explanatory text saying so. Dotfiles-side work (archsetup owns the lifecycle); touches common/.local/bin/weather. +Verified complete in the 2026-07-25 batch: the weather CLI already had the hourly default TTL, fresh-cache no-fetch path, stale fallback, and explicit offline footer. Its 33-test suite and the full dotfiles suite pass. +** DONE [#B] Settings gear becomes four device toggles :feature:waybar:dotfiles:solo: +CLOSED: [2026-07-25 Sat 10:53] +From the roam inbox (Craig, claimed 2026-07-23): the waybar gear should become four icons — touchpad, mouse, webcam, and a notification bubble. Clicking each toggles that setting directly. The first three turn red when disabled; the bubble turns red when DND is enabled. + +Today =custom/settings= (=hyprland/.config/waybar/config=) is one gear glyph () whose only job is =on-click: settings-panel=. The toggles themselves already exist and are tested — the settings package owns touchpad, mouse, and webcam (=webcam.py= is the USB-authorized kill switch from 2026-07-22), so this is a bar-side surface over existing backends rather than new capability. + +Note the state-polarity split when wiring the colors: three read "red = off" and DND reads "red = on". That asymmetry is deliberate (red means "something is disabled that normally isn't, or suppressed that normally isn't"), so encode it per-icon rather than deriving one rule. + +Decided 2026-07-23 (Craig): the gear STAYS alongside the four toggles as the panel launcher. So the bar's right side grows from 12 modules to 16 — the four toggles are net-new, the gear keeps its =on-click: settings-panel=. Open sub-question for build time, not blocking: whether the four toggles are four separate waybar modules or one custom module rendering four glyphs (fewer layout entries, one exec). Pick at build; the four-module shape is simplest and matches how mic/net already sit as individual modules. +Shipped in the 2026-07-25 batch as four independent JSON modules over the existing verified settings backends. Touchpad, mouse, and webcam turn terracotta when disabled; DND uses the deliberate inverse polarity; unavailable hardware dims. The gear remains the panel launcher. The live and Dupre theme CSS copies stay byte-identical. +** DONE [#C] Wallpaper panel selection and scroll state :feature:dotfiles:solo: +CLOSED: [2026-07-25 Sat 10:53] +:PROPERTIES: +:LAST_REVIEWED: 2026-07-25 +:END: +From the roam inbox (Craig, 2026-07-25). Screenshot: =~/pictures/screenshots/2026-07-25_013041.png=. Three related behaviors in the settings wallpaper panel (=settings/src/settings/wallpaper.py=): +1. Open at the wallpaper currently displayed, not the top of the list. +2. Highlight that wallpaper as selected in the scrollable pane while it shows in the preview. +3. Keep the scroll position when a picture is selected. Today selecting a picture snaps the scroll back to the top, which is the bug half of this. +Grade: minor scroll-reset defect x every panel selection = P3 = [#C]; the open-at-current and select-current behaviors are enhancements at the same level. One type tag, so filed =:feature:= with the scroll-reset called out as the bug. Solo: buildable in the settings GTK panel, agent-verifiable via headless capture plus the wallpaper.py tests, no design call — swww query gives the current wallpaper, and scroll-position preservation and row selection are standard GTK. +Shipped in the 2026-07-25 batch. The panel queries =awww query= off the UI thread, prefers the actually displayed image over stale stored state, highlights it, scrolls it into view on first open, and remembers the horizontal adjustment across selection-triggered rebuilds. +** DONE [#C] Net tooltip IPs and line order :feature:waybar:network:solo: +CLOSED: [2026-07-25 Sat 10:53] +From the roam inbox (Craig, claimed 2026-07-23): in the wifi hover, add the internal IP, external IP, and gateway IP just below the Interface line; move the Signal line to just above the keyboard-shortcuts line. Design constraint: the bar's hot path does no network I/O (status.py deliberately skips _address_facts on the 2s beat) — internal IP + gateway can ride cheap local reads, but the external IP must come from a cache the connectivity probe refreshes, never a live lookup in waybar-net. +Shipped in the 2026-07-25 batch. The slow connectivity probe caches local addressing and a validated external IP with the network identity; the Waybar hot path only reads that valid cache. Tooltip order is Interface, internal/external/gateway IPs, connectivity detail, throughput, Signal, shortcut. +** DONE [#B] Dupre Kit merge — casting additions :feature:tooling:solo: +CLOSED: [2026-07-25 Sat 10:53] +Fold docs/prototypes/dupre-kit-additions.js back into the kit proper: detentFader (NEW — multi-detent slide attenuator with speedbump drag physics: magnet + escape hysteresis, parked tick glow) and the drumRoller redefinition (UPGRADE — 1..N channels and min/max range; stock hardcodes two drums and throws on one, defaults reproduce stock exactly) and the guardedToggle redefinition (UPGRADE — lever throws with rotateX so it flips toward the viewer instead of the stock 180° planar spin that sweeps sideways mid-transition; contract unchanged). Merge means: builders into widgets.js, the additions CSS into DUPRE_CSS, additions-scoped gradients into the shared defs plate, gallery cards for both in panel-widget-gallery.html, and POLICY entries. Origin: the desktop-settings casting sitting 2026-07-21 — Craig's direction is that components get finished by being needed ("the ones needed most will have had the most attention"), so more additions may accrue here before the merge; batch them. +Shipped in the 2026-07-25 batch. =widgets.js= now owns all three builders, shared gradients/CSS, contracts, and policy records; additions no longer redefines them when older casting pages load it. The gallery has a three-detent fader card and a three-channel 0–12 drum demonstration (112 cards total). Static ownership tests, JS syntax checks, and the complete headless interaction probe pass. +** DONE [#C] Maint live-refresh hairline replacement :feature:maint:solo: +CLOSED: [2026-07-25 Sat 10:53] +:PROPERTIES: +:LAST_REVIEWED: 2026-07-14 +:END: +From the roam inbox (routed 2026-07-13): the memory-killer section seemed to update too often, and "it's a bit unclear what the line is doing; consider something else." Diagnosis (2026-07-14): the data cadence is already the requested 3s (gui live tier, _LIVE_SECONDS); the perceived churn is the live-refresh hairline — the 2px bar under the live sections that drains full-to-empty over each 3s window, redrawn at 150ms (gui._hair_tick, viewmodel.refresh_fraction). It exists to tell a stale board from a frozen one (2026-07-09), but it reads as constant unexplained motion. Design call for Craig: replace the draining line with something whose meaning is legible — candidates: a dot that blinks once per refresh, a "3s" age caption that only appears when refresh is overdue, slowing the drain redraw, or dropping the indicator on live tiers and keeping it only when data goes stale. Keep the stale-vs-frozen distinguishability that motivated the hairline. +*** 2026-07-21 Tue @ 08:35:00 -0500 Decided (Craig): silent-until-stale age caption +Replace the draining 2px hairline with an age caption that shows ONLY when refresh is overdue (e.g. "3s", "8s" once past the expected window) and shows nothing while the board is healthy. This keeps the stale-vs-frozen signal — a frozen board surfaces a growing age number, a live one stays clean — while removing the constant motion the hairline created. Implementation (dotfiles, archsetup-owned): drop =gui._hair_tick= / the hairline draw, add an overdue-age caption driven off =viewmodel.refresh_fraction= (or the last-refresh timestamp) rendered only past the live window. Now unblocked; needs a live visual check on the panel after. +Shipped in the 2026-07-25 batch. The animated draw area and 150ms timer are gone; the memory section header stays silent through the healthy three-second window, then shows a once-per-second growing age caption. Pure boundary tests and the full maintenance suite pass. +** DONE [#D] Test-framework + prototype refactor cluster :refactor:solo: +CLOSED: [2026-07-25 Sat 10:53] +Grading: no behavior change; parking lot. Refactors from the S5-S7 audit, distinct from the installer refactor rollup above. +scripts/testing/run-test.sh + run-test-baremetal.sh duplicate the run/poll/report skeleton and have drifted (VM uses setsid + copy helpers, baremetal uses nohup + hand-rolled sshpass scp) — extract the shared core so baremetal inherits the sturdier paths; run-maint-nspawn.sh:66 + run-maint-scenarios.sh:78 duplicate the transport-independent _scenario_var/_validate_scenario/run_scenario (a sourced lib/maint-scenario.sh); run-test.sh:251,265 uses two different mechanisms (pgrep vs ps|grep) for the same liveness check; docs/prototypes/gen_tokens.py:78 repeats the section-iteration skeleton across four emitters; gallery-widget.el:95,136 hardcodes SVG arc/hub path strings that duplicate the cx/cy/radius geometry (dial desyncs silently on a constant change); gallery-widget.el:72,84 leans on the private svg--append. See findings doc (S5, S6, S7). +Completed test-first in the 2026-07-25 batch. QEMU and bare-metal runners share liveness/report helpers; maintenance transports share scenario validation/execution; token emitters share ordered section traversal; and the Emacs SVG gauge shares semicircle geometry and uses the public DOM append API. Every fast Python/ERT suite passes. @@ -2071,6 +2071,122 @@ filed here at Craig's direction so the sleep task carries it. Nothing diagnosed yet — first step is =journalctl -b -1 -u systemd-suspend-then-hibernate= around those timestamps to see which device reported busy. +*** 2026-08-26 Wed @ 16:16:08 -0600 Diagnosed the hibernate battery drain: three separate faults, one task each +Craig hibernated twice in ten days and found the battery dead both times. Read +all 39 boots since the 08-13 reinstall, upower's charge history +(=/var/lib/upower/history-charge-Framewo-55-03F5.dat=, root-only, starts +08-19), sysfs, and the scripts inside =/efi/EFI/ZBM/zfsbootmenu.efi=. + +Hibernate is configured right and has worked: five hibernate+resume cycles +since reinstall (08-13, 08-17 14:31, and three suspend-then-hibernate cycles on +08-20/21). The two fatal events are the two overnight explicit +=systemctl hibernate= runs, 08-17 22:20 and 08-21 19:58. Both journals end at +"PM: hibernation: hibernation entry"; the next power-ons (08-18 10:13, 08-22 +17:22) were fresh boots whose resume hook found no image, no later swapon +reported a leftover suspend signature, and on 08-22 the battery read 2% at +power-on. The 08-23/24 night was on AC and not a battery death (three suspends +failed to enter, machine awake all night at the charge limit; the 09:57 end was +three power-key presses and a hard cut at 63%). The 08-19 death was the +caffeine/hypridle one already diagnosed. + +Three faults, tracked as the children below: +- Hibernate hard-freezes on entry (documented on Framework 13 AMD incl. Ryzen + AI 300: black screen, never powers off, intermittent, amdgpu-side). Fits + everything: the freeze precedes the swap signature, so the next boot is + fresh, and a frozen laptop at ~5 W empties 44.7 Wh in ~8 h. Unprovable from + logs by nature; the alternative (completed hibernate, unattended power-on to + the ZBM passphrase prompt) predicts a surviving image, which neither boot + had — see the VERIFY. +- ZFS ARC starves the hibernate image: "Image allocation is 8118265 pages + short" today 14:09, "390678 pages short" 08-20 09:14. ARC 58 GB of 93, + =zfs_arc_max=0= so =c_max= = RAM − 1 GiB; the kernel must free RAM − + =image_size= (37.4 GB) ≈ 56 GB. systemd falls back to s2idle and retries + every 90 min, so suspend-then-hibernate never actually hibernates. +- The SD card reader (090c:3350, =sda=, no media) can block suspend entirely: + "Freezing remaining freezable tasks failed after 20s (wq_busy=1)", pending + =disk_events_workfn= on =events_freezable_pwr_efficient=, three times on + 08-23/24. On battery that is a dead laptop by morning. + +Mistake worth remembering: =journalctl --since … -k= silently limits itself to +the current boot (=-k= implies =-b=); cross-boot kernel facts need +=_TRANSPORT=kernel= or an explicit =-b=. + +*** TODO Hibernate entry freeze — confirm under observation, then mitigate :bug:velox:hibernate: +Interim rule until this closes: do not hibernate unattended on battery. Shut +down, or suspend on AC. + +What is known: the two dead-battery hibernates match the Framework 13 AMD +"hard freeze on hibernate entry" reports (community threads 69516 and 53860, +Arch bbs 293242): screen black, power LED on, never powers off; intermittent +(one report: every 6–7 cycles); TTM/amdgpu warnings; improved by newer +=linux-firmware=; no confirmed fix. Board A9, BIOS 03.05, linux-lts 6.18.46, +=amdgpu.dcdebugmask=0x610= already on the cmdline. + +Confirm first: the "Hibernate entry freeze: five observed cycles on AC" test +under Manual testing and validation. A failed cycle there is the proof the +journal cannot give. + +Mitigations to try in order once confirmed, one at a time, re-running the +cycles after each: (1) =linux-firmware= at current, then =linux-firmware-git= +if the freeze persists; (2) =/sys/power/disk= = =shutdown= instead of +=platform= (a systemd =HibernateMode=shutdown= drop-in), which skips the ACPI +S4 path some Framework users found hanging; (3) a newer kernel (=linux= vs +=linux-lts=) for the amdgpu delta; (4) unload =mt7925e= in a pre-sleep hook +if the freeze survives the first three. Not =:solo:=: each cycle needs a +person watching the power LED. + +*** TODO ZFS ARC starves the hibernate image — cap it or shrink it pre-hibernate :bug:zfs:velox:solo: +The arithmetic: the kernel preallocates RAM − =image_size= pages before +snapshotting; with 93 GB RAM and the default =image_size= (2/5 of RAM, +37.4 GB) that is ~56 GB, and only free memory plus what shrinkers give back +counts. ARC was 58 GB today and the ZFS shrinker released little inside the +preallocation window, so it came up 31 GiB short. Nothing in +=/etc/modprobe.d/= sets =zfs_arc_max=. + +Two fixes, either or both: +- Cap the ARC: =options zfs zfs_arc_max=<bytes>= in =/etc/modprobe.d/zfs.conf= + (16 GiB leaves ~70 GB reclaimable) plus =echo <bytes> > + /sys/module/zfs/parameters/zfs_arc_max= for the running system. +- Or a =/usr/lib/systemd/system-sleep/= pre hook for the hibernate class that + lowers =zfs_arc_max=, waits for =size= in + =/proc/spl/kstat/zfs/arcstats= to fall, and restores it post-sleep. Keeps + the big ARC while awake. +- Raising =image_size= toward the kernel's ceiling (about half of RAM) also + shrinks the demand; combine with the cap. +Install it through archsetup so the next rebuild carries it (velox-only: ratio +has no swap partition). + +Verify: after the change =arcstats size= drops below the cap within seconds; +then one live suspend-then-hibernate cycle on AC with the delay temporarily +short shows "hibernation exit" and no "Image allocation … short" line in the +journal. That live cycle rides the entry-freeze test above; the ARC half is +checkable without it. + +*** TODO SD card reader media polling can block suspend :bug:velox:solo: +The reader (USB 090c:3350 Silicon Motion, =sda=, "Media removed, stopped +polling" at boot yet =events_poll_msecs= = −1 → default 2000 ms) left a +=disk_events_workfn= item pending on the freezable workqueue three times on +08-23/24, and the freezer gives up after 20 s: "Failed to put system to +sleep … Device or resource busy". Same symptom as the flaky expansion slot in +the ribbon task; a stalled poll never completes. + +Fix: a udev rule for that vendor/product setting +=ATTR{events_poll_msecs}="0"= (or =block.events_dfl_poll_msecs=0= on the +cmdline if every removable disk should stop polling), shipped by archsetup. +Verify with =rtcwake -m mem -s 20= on AC: journal shows "PM: suspend entry" +and "PM: suspend exit" with no "Freezing remaining freezable tasks failed", +and =/sys/block/sda/events_poll_msecs= reads 0 after a replug. Pulling the +card before sleeping is the manual workaround meanwhile. + +*** VERIFY After the 08-17 and 08-21 dead batteries, did the first power-on hang, or boot straight to a fresh login? +Decides between the two mechanisms. An entry freeze leaves no image, so the +next power-on boots straight through. A completed hibernate followed by an +unattended power-on (phantom power button, ZBM passphrase prompt until dead) +leaves the image in place, so the next power-on would try to resume — and the +only way that ends in the fresh boots the journal shows is a hung resume that +got force-cut. If both power-ons went straight to a fresh login, the freeze +is the answer. + ** TODO [#A] Port Hyprland config to Lua before 0.57 drops .conf support :hyprland:dotfiles: SCHEDULED: <2026-08-25 Tue> :PROPERTIES: @@ -2911,6 +3027,32 @@ NOTE (2026-07-04 audit): the "four-tab panel" framing predates the instrument-co - Expected: ↓/↑ Mbps + ping + server shown inline. - Byte-rate→Mbps unit: VERIFIED 2026-06-30 (velox). Raw =speedtest-go --json= dl_speed read ~3.66M, unambiguously bytes/s (29 down / 80 up Mbps); =net speedtest= reported 33.62 / 77.99 through the wired path. =BYTES_PER_SEC = True= + =* 8 / 1e6= are correct, no flip needed. Remaining here is only that the panel renders the inline result. +*** Hibernate entry freeze: five observed hibernate cycles on AC +What we're verifying: whether velox hard-freezes on hibernate entry (black +screen, power LED on, never powers off), the documented Framework 13 AMD +failure that fits both dead-battery events. The journal cannot show it; a +person watching the LED can. +- Plug in AC, lid open, nothing important unsaved. +- Note the cycle number, then hibernate from a terminal: +#+begin_src sh :results output +date; systemctl hibernate +#+end_src +- Watch: the screen goes black; within about two minutes the power LED goes + off and the fans stop. +- Press power, enter the ZBM passphrase, and confirm the same session comes + back (windows still open). +- Check that the cycle was a real hibernate and not a fallback: +#+begin_src sh :results output +journalctl -b -o short-iso | grep -E "systemd-sleep|hibernation (entry|exit)|Image allocation|Failed to put" | tail -6 +#+end_src +- Repeat until five cycles are logged. +Expected: all five cycles power off within two minutes and resume into the +same session, with "hibernation exit" and no "Image allocation … short" line. +A cycle where the screen stays black with the power LED on for more than five +minutes is the entry freeze: hold power for 10 s, and write down the cycle +number and whether the keyboard backlight was lit. A cycle that instead comes +straight back with "Cannot allocate memory" is the ARC task, not a freeze. + ** DOING [#B] Prepare for GitHub open-source release :PROPERTIES: :LAST_REVIEWED: 2026-08-17 @@ -3381,53 +3523,6 @@ carry a date and Craig's return date isn't known yet — date it and raise it to Workaround in the meantime: Bluetooth mouse, already in use. -** DONE [#B] Weather tooltip caching :feature:waybar:weather:solo: -CLOSED: [2026-07-25 Sat 10:53] -From the roam inbox (Craig, claimed 2026-07-22): retrieve the weather tooltip data once per hour and cache it. If the network is unavailable, display the cached tooltip with explanatory text saying so. Dotfiles-side work (archsetup owns the lifecycle); touches common/.local/bin/weather. -Verified complete in the 2026-07-25 batch: the weather CLI already had the hourly default TTL, fresh-cache no-fetch path, stale fallback, and explicit offline footer. Its 33-test suite and the full dotfiles suite pass. -** DONE [#B] Settings gear becomes four device toggles :feature:waybar:dotfiles:solo: -CLOSED: [2026-07-25 Sat 10:53] -From the roam inbox (Craig, claimed 2026-07-23): the waybar gear should become four icons — touchpad, mouse, webcam, and a notification bubble. Clicking each toggles that setting directly. The first three turn red when disabled; the bubble turns red when DND is enabled. - -Today =custom/settings= (=hyprland/.config/waybar/config=) is one gear glyph () whose only job is =on-click: settings-panel=. The toggles themselves already exist and are tested — the settings package owns touchpad, mouse, and webcam (=webcam.py= is the USB-authorized kill switch from 2026-07-22), so this is a bar-side surface over existing backends rather than new capability. - -Note the state-polarity split when wiring the colors: three read "red = off" and DND reads "red = on". That asymmetry is deliberate (red means "something is disabled that normally isn't, or suppressed that normally isn't"), so encode it per-icon rather than deriving one rule. - -Decided 2026-07-23 (Craig): the gear STAYS alongside the four toggles as the panel launcher. So the bar's right side grows from 12 modules to 16 — the four toggles are net-new, the gear keeps its =on-click: settings-panel=. Open sub-question for build time, not blocking: whether the four toggles are four separate waybar modules or one custom module rendering four glyphs (fewer layout entries, one exec). Pick at build; the four-module shape is simplest and matches how mic/net already sit as individual modules. -Shipped in the 2026-07-25 batch as four independent JSON modules over the existing verified settings backends. Touchpad, mouse, and webcam turn terracotta when disabled; DND uses the deliberate inverse polarity; unavailable hardware dims. The gear remains the panel launcher. The live and Dupre theme CSS copies stay byte-identical. -** DONE [#C] Wallpaper panel selection and scroll state :feature:dotfiles:solo: -CLOSED: [2026-07-25 Sat 10:53] -:PROPERTIES: -:LAST_REVIEWED: 2026-07-25 -:END: -From the roam inbox (Craig, 2026-07-25). Screenshot: =~/pictures/screenshots/2026-07-25_013041.png=. Three related behaviors in the settings wallpaper panel (=settings/src/settings/wallpaper.py=): -1. Open at the wallpaper currently displayed, not the top of the list. -2. Highlight that wallpaper as selected in the scrollable pane while it shows in the preview. -3. Keep the scroll position when a picture is selected. Today selecting a picture snaps the scroll back to the top, which is the bug half of this. -Grade: minor scroll-reset defect x every panel selection = P3 = [#C]; the open-at-current and select-current behaviors are enhancements at the same level. One type tag, so filed =:feature:= with the scroll-reset called out as the bug. Solo: buildable in the settings GTK panel, agent-verifiable via headless capture plus the wallpaper.py tests, no design call — swww query gives the current wallpaper, and scroll-position preservation and row selection are standard GTK. -Shipped in the 2026-07-25 batch. The panel queries =awww query= off the UI thread, prefers the actually displayed image over stale stored state, highlights it, scrolls it into view on first open, and remembers the horizontal adjustment across selection-triggered rebuilds. -** DONE [#C] Net tooltip IPs and line order :feature:waybar:network:solo: -CLOSED: [2026-07-25 Sat 10:53] -From the roam inbox (Craig, claimed 2026-07-23): in the wifi hover, add the internal IP, external IP, and gateway IP just below the Interface line; move the Signal line to just above the keyboard-shortcuts line. Design constraint: the bar's hot path does no network I/O (status.py deliberately skips _address_facts on the 2s beat) — internal IP + gateway can ride cheap local reads, but the external IP must come from a cache the connectivity probe refreshes, never a live lookup in waybar-net. -Shipped in the 2026-07-25 batch. The slow connectivity probe caches local addressing and a validated external IP with the network identity; the Waybar hot path only reads that valid cache. Tooltip order is Interface, internal/external/gateway IPs, connectivity detail, throughput, Signal, shortcut. -** DONE [#B] Dupre Kit merge — casting additions :feature:tooling:solo: -CLOSED: [2026-07-25 Sat 10:53] -Fold docs/prototypes/dupre-kit-additions.js back into the kit proper: detentFader (NEW — multi-detent slide attenuator with speedbump drag physics: magnet + escape hysteresis, parked tick glow) and the drumRoller redefinition (UPGRADE — 1..N channels and min/max range; stock hardcodes two drums and throws on one, defaults reproduce stock exactly) and the guardedToggle redefinition (UPGRADE — lever throws with rotateX so it flips toward the viewer instead of the stock 180° planar spin that sweeps sideways mid-transition; contract unchanged). Merge means: builders into widgets.js, the additions CSS into DUPRE_CSS, additions-scoped gradients into the shared defs plate, gallery cards for both in panel-widget-gallery.html, and POLICY entries. Origin: the desktop-settings casting sitting 2026-07-21 — Craig's direction is that components get finished by being needed ("the ones needed most will have had the most attention"), so more additions may accrue here before the merge; batch them. -Shipped in the 2026-07-25 batch. =widgets.js= now owns all three builders, shared gradients/CSS, contracts, and policy records; additions no longer redefines them when older casting pages load it. The gallery has a three-detent fader card and a three-channel 0–12 drum demonstration (112 cards total). Static ownership tests, JS syntax checks, and the complete headless interaction probe pass. -** DONE [#C] Maint live-refresh hairline replacement :feature:maint:solo: -CLOSED: [2026-07-25 Sat 10:53] -:PROPERTIES: -:LAST_REVIEWED: 2026-07-14 -:END: -From the roam inbox (routed 2026-07-13): the memory-killer section seemed to update too often, and "it's a bit unclear what the line is doing; consider something else." Diagnosis (2026-07-14): the data cadence is already the requested 3s (gui live tier, _LIVE_SECONDS); the perceived churn is the live-refresh hairline — the 2px bar under the live sections that drains full-to-empty over each 3s window, redrawn at 150ms (gui._hair_tick, viewmodel.refresh_fraction). It exists to tell a stale board from a frozen one (2026-07-09), but it reads as constant unexplained motion. Design call for Craig: replace the draining line with something whose meaning is legible — candidates: a dot that blinks once per refresh, a "3s" age caption that only appears when refresh is overdue, slowing the drain redraw, or dropping the indicator on live tiers and keeping it only when data goes stale. Keep the stale-vs-frozen distinguishability that motivated the hairline. -*** 2026-07-21 Tue @ 08:35:00 -0500 Decided (Craig): silent-until-stale age caption -Replace the draining 2px hairline with an age caption that shows ONLY when refresh is overdue (e.g. "3s", "8s" once past the expected window) and shows nothing while the board is healthy. This keeps the stale-vs-frozen signal — a frozen board surfaces a growing age number, a live one stays clean — while removing the constant motion the hairline created. Implementation (dotfiles, archsetup-owned): drop =gui._hair_tick= / the hairline draw, add an overdue-age caption driven off =viewmodel.refresh_fraction= (or the last-refresh timestamp) rendered only past the live window. Now unblocked; needs a live visual check on the panel after. -Shipped in the 2026-07-25 batch. The animated draw area and 150ms timer are gone; the memory section header stays silent through the healthy three-second window, then shows a once-per-second growing age caption. Pure boundary tests and the full maintenance suite pass. -** DONE [#D] Test-framework + prototype refactor cluster :refactor:solo: -CLOSED: [2026-07-25 Sat 10:53] -Grading: no behavior change; parking lot. Refactors from the S5-S7 audit, distinct from the installer refactor rollup above. -scripts/testing/run-test.sh + run-test-baremetal.sh duplicate the run/poll/report skeleton and have drifted (VM uses setsid + copy helpers, baremetal uses nohup + hand-rolled sshpass scp) — extract the shared core so baremetal inherits the sturdier paths; run-maint-nspawn.sh:66 + run-maint-scenarios.sh:78 duplicate the transport-independent _scenario_var/_validate_scenario/run_scenario (a sourced lib/maint-scenario.sh); run-test.sh:251,265 uses two different mechanisms (pgrep vs ps|grep) for the same liveness check; docs/prototypes/gen_tokens.py:78 repeats the section-iteration skeleton across four emitters; gallery-widget.el:95,136 hardcodes SVG arc/hub path strings that duplicate the cx/cy/radius geometry (dial desyncs silently on a constant change); gallery-widget.el:72,84 leans on the private svg--append. See findings doc (S5, S6, S7). -Completed test-first in the 2026-07-25 batch. QEMU and bare-metal runners share liveness/report helpers; maintenance transports share scenario validation/execution; token emitters share ordered section traversal; and the Emacs SVG gauge shares semicircle geometry and uses the public DOM append API. Every fast Python/ERT suite passes. ** DONE [#B] Two agent sessions sharing one git repo :chore:tooling: CLOSED: [2026-07-26 Sun] Craig approved the shared-rules-layer solution on 2026-07-26. |
