diff options
| author | Craig Jennings <c@cjennings.net> | 2026-08-24 14:27:57 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-08-24 14:27:57 -0600 |
| commit | 8b5e438d787ff73c3344b4c5ea1141cf8b23ef69 (patch) | |
| tree | 25f64dea1f918117648a6e0a383da208b2e2018e /archive | |
| parent | 1bb5955b121d650302ba3eab08e44cc004470a91 (diff) | |
| download | archsetup-8b5e438d787ff73c3344b4c5ea1141cf8b23ef69.tar.gz archsetup-8b5e438d787ff73c3344b4c5ea1141cf8b23ef69.zip | |
Hyprland 0.57 drops the .conf format. The port is built and measured identical to the current config in a nested compositor — 38 settings, 103 binds, the host-override chain — but it is not deployed. I put it live this afternoon and took it back out the same hour, because it had not been run on real hardware and the machine has to stay usable until it has.
working/hyprland-lua-port/ carries what the switch needs, including the two reader changes as replayable patches. Those matter more than the config itself: four things read hyprland.conf as a source of truth, and dotfiles-validate globs hypr/*.conf, so with the .conf gone it finds nothing and reports a clean tree.
Two facts not worth re-deriving: with both files present 0.56.2 loads the .lua, so keeping the .conf beside it buys no rollback; and a running compositor rewrites a stub config within a second of the file vanishing, which is what fights a restow.
Also archives finished tasks and folds in several sessions of task-review edits.
Diffstat (limited to 'archive')
| -rw-r--r-- | archive/task-archive.org | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/archive/task-archive.org b/archive/task-archive.org index cb2081a..6c3eb1a 100644 --- a/archive/task-archive.org +++ b/archive/task-archive.org @@ -1786,3 +1786,204 @@ CLOSED: [2026-07-20 Mon] Fixed in 179fbd5 (plus 552736e for the gauge-level clamp test): socket check via find -type s, gen_tokens degenerate case pinned exactly as characterization, tick count as direct occurrences, and write-svg covered. All five items dispositioned. Grading: no runtime behavior change; test-suite quality. Group of five weak/missing tests from the S6/S7 audit. scripts/testing/tests/test_desktop.py:96 passes a shell glob to `test -S`, which breaks on zero or multiple sockets (masked today because the test always skips); tests/gallery-tokens/test_gen_tokens.py:181 asserts properties too weak to notice the marker output is garbled (impossible input, so low); tests/gallery-widgets/test-gallery-widget.el:77 counts ticks via split-string + cl-count-if :start 1 (a coincidence of split semantics, not a match count); :47 tests the needle-angle helper's clamp but never the rendered readout at an out-of-range value (exactly why the S7 readout/needle bug ships green — add a gauge-level boundary case); :159 leaves gallery-widget-write-svg uncovered (add a Normal write-to-temp case). See findings doc (S6, S7). +** DONE [#B] Installer GRUB_CMDLINE overwrite drops boot params :bug:solo: +CLOSED: [2026-07-21 Tue] +Fixed in f9da097: update_grub_cmdline merges the current value with archsetup's tokens (existing tokens survive, same-key conflicts resolve to archsetup's value) behind a refuse-to-write safety check, via awk + mv with a backup_system_file first. TDD via tests/installer-steps/test_grub_cmdline.py (8 cases incl. cryptdevice/resume/zfs survival and idempotence). +Grading: Critical severity (unbootable) x some-users-sometimes (machines whose base install set a cryptdevice=/resume=/zfs= cmdline param) = P2 = [#B]. +archsetup:3054 rewrites the whole GRUB_CMDLINE_LINUX_DEFAULT line with a fixed string; nothing re-adds a pre-existing cryptdevice/resume/zfs token, so grub-mkconfig (3059) can bake an unbootable config. Fix: read the current value and append only the missing tokens; assert any pre-existing boot-critical token survives before grub-mkconfig. See [[file:docs/design/2026-07-19-sentry-code-findings.org][sentry code findings]] (S3). +** DONE [#C] Maint status wall copy buttons :feature:maint:dotfiles: +CLOSED: [2026-07-21 Tue] +Shipped in dotfiles 8bc79ba per Craig's calls (one global button, rendered text): COPY on the doctor row serializes every category band via the same card_spec the GUI renders, through panelkit clipboard. TDD tests/maint/test_status_copy.py, full dotfiles make test green, inbox note sent. Live check pending: open the maint panel, press COPY, paste. +Craig's roam capture 2026-07-20, routed via .emacs.d sentry inbox-zero as archsetup-owned UI work. Dotfiles maint panel work; archsetup drives it end-to-end per the standing rule. +** DONE [#B] Build: desktop-settings panel :feature:hyprland:dotfiles: +CLOSED: [2026-07-22 Wed] +:PROPERTIES: +:SPEC_ID: d6bb1e73-ec90-4327-85ee-bfa762da5bce +:END: +The GTK build of the desktop-settings panel per the spec (docs/specs/2026-07-02-desktop-settings-panel-spec.org, DOING; normative reference: prototype 37). Work happens in dotfiles settings/ — archsetup drives the lifecycle. Two non-blocking build-time picks live in the spec's Review findings (wallpaper setter tool; store location/format) — decide in phase 1 and record there. +*** 2026-07-22 Wed @ 13:14:01 -0500 Built the backings engine (phase 1) — dotfiles 7a15237 +Landed as dotfiles settings/src/settings (10 modules) + tests/settings (118 tests against fake binaries, auto-discovered by make test — 81 suites green). Covers brightness/kbd (5% floor, x10 drum), toggles (dim, pointer cycle via toggle-touchpad, caffeine), DND class-split (dunst pause level 60, close-all before unpause, alarms punch through live), powerprofilesctl, nightlight (resident gammastep), hypridle.conf renderer + symlink-safe write + caffeine-respecting reload + hyprlock grace, suntimes (pure NOAA math), and the wallpaper engine (awww/mpvpaper/projector adapters, galleries, random draw, atomic JSON store). All three build-time picks recorded as DONE findings in the spec (setter=awww, store=state.json, nightlight=gammastep). Handoff note in ~/.dotfiles/inbox/. +*** 2026-07-22 Wed @ 15:26:44 -0500 Built the presenters (phase 2) — dotfiles 5172289 +Three GTK-free models per prototype 37, all at 100% line coverage (tests/settings/test_presenters.py, 100 tests; full repo suite green before and after). programs.py: the matrix — eight complete programs (Craig's four factory scenes drafted here per the pre-flight pick, slots 1-4 first-class), pin rows + power radio row, activate returns the full sets, member writes return apply/updated with active-is-live surviving. bench.py: drum mapping (screen never reads 0, floor 5%; kbd floors at 0), idle rail order clamping between enabled neighbors, park/unpark with re-clamp, caffeine bypass, view-state builder tolerant of no-backlight None. channels.py: the eight-channel bank, per-mode sources visibility, alpha/recency sort (unlabeled last), the shared mint/edit/delete grammar for pairs/sets/colors (press arm-cycle, two-picture set minimum, dup rejection, selection clamping), sources guardrails, interval wheel, previews. Handoff note in ~/.dotfiles/inbox/. +*** 2026-07-22 Wed @ 16:03:52 -0500 Ported prototype 37's instruments to GTK (phase 3) — dotfiles 33d82eb +The panel renders P37 end to end. New instruments.py carries the three Cairo instruments as clock-free humble objects: ProgramMatrix (glyph/numbered heads over jewel pins + CPU POWER paper letter wheels), DrumRoller (paper drums, drag-to-set, dimmed n/a on no-backlight machines), TripDial (sqrt 300° scale, colored stage tabs, OFF-notch parking, exact-minutes drag counter, BYPASSED · CAFFEINE stamp, bottom legend). gui.py rebuilt to P37's layout with the wallpaper sub-view: channel bank with drawn faces, minted pair/color/set trays (alpha/time sort, edit/delete chip feet), the three presses (pair arm-cycle, color picker, set press + interval wheel), sources with a folder picker. New GTK-free glue all unit-tested (test_panel_glue.py, 33 tests): dial geometry in bench, matrix/idle/wallpaper wiring in panel, presenter-vocabulary channels (pair/solid/random-from-set) in wallpaper.apply. AT-SPI smoke (make test-panel-settings) drives the real wiring against faked backings + a sandboxed store, pinned to its own child pid so it can never fire a live panel's backings. Visually verified on a headless output against P37 captures (main + pair/single/solid/random). Adaptations recorded in the handoff: five-stage dial (WATCH gets its own green — the engine runs watch separately, P37 merged the label), DESKTOP_SETTINGS_START_VIEW test seam. Full suite 84 suites green; window rule widened for the 540px panel. Handoff note in ~/.dotfiles/inbox/. +*** 2026-07-22 Wed @ 16:47:54 -0500 Integrated phase 4 — dotfiles 680b50d +Bar consolidation had landed early (74f723e); this pass shipped the rest. settings-project hosts the watch/clock/world channels as HTML faces (settings/faces/) on a gtk-layer-shell background window over WebKit2 — all three visually verified on a headless output, world reading the waybar worldclock roster via query param. settings-watch is the hypridle watch-stage host: throwaway-profile chrome kiosk that reveals only after its window maps behind the lock and relocks before teardown — a failed face degrades to the plain lock, never a bare desktop (unlocked lifecycle verified live; the locked swap goes to the manual checklist). Sun-pair location reads whereami live per transition with last-good cache in state.json (verified live: 9.5s first beat, New Orleans coords, Gogh day side applied); desktop-settings-tick.timer (2 min, enabled on ratio, added to the installer) drives flips and random draws — 23ms no-op beats. dunstrc history_length 100 protects held alarms (full DND cycle verified against live dunst; wtimer alarms already CRITICAL via the notify wrapper, no promotion rule needed). Live hypridle rewrite verified — five-stage regime rendered through the stow symlink, caffeine respected (found engaged, daemon correctly left stopped). Refresh signals needed no rewiring (touchpad signals itself via toggle-touchpad). 45 new tests; suite 84 suites green; smoke 13/13. Handoff note in ~/.dotfiles/inbox/. +Velox one-time steps (sync doesn't carry): mpvpaper (AUR), optionally power-profiles-daemon (service off), and systemctl --user enable --now desktop-settings-tick.timer. +*** 2026-07-22 Wed @ 17:05:58 -0500 Landed the 17-point end-to-end pass — dotfiles 9038eee +Prototype 37's 17-point suite re-derived against the real panel (the original Playwright script wasn't preserved; the functional surface in the spec's Final prototype section is the source). tests/settings/panel_e2e.py + run-panel-e2e.sh + =make test-panel-e2e=: points 1-14 drive the running panel over AT-SPI (program recall with per-backing verification across FOCUS/BATTERY/slot1, pointer console keys, all eight wallpaper channels including projected watch/world stop/start ordering, close); points 15-17 cover the Cairo instruments (drums, tripper dial clamp/park/render/reload, matrix pins + letter wheels with active-is-live) at the backing layer, since AT-SPI can't reach a DrawingArea's hit-tests. Same safety posture as the smoke: sandboxed store, faked backings, pid-pinned a11y node. 17/17 green on ratio's live compositor; full suite 85 green; smoke 13/13; ruff clean. The drag gestures go to the manual checklist below. Handoff note in ~/.dotfiles/inbox/. +*** 2026-07-22 Wed @ 17:05:58 -0500 Flipped the spec to IMPLEMENTED +docs/specs/2026-07-02-desktop-settings-panel-spec.org DOING → IMPLEMENTED with a dated history line naming the shipping commits (dotfiles 7a15237 / 74f723e / 5172289 / 33d82eb / 680b50d / 9038eee) and the verification evidence (85 suites, smoke 13/13, e2e 17/17). The four panel drag-gesture checks and the locked-path night-watch swap live under "Manual testing and validation" — human-eye checks, not implementation blockers. +** CANCELLED [#B] Hyprland layoutmsg crash — bad_variant_access (upstream) :bug:hyprland: +CLOSED: [2026-07-21 Tue] +Dropped 2026-07-21 (Craig's call) — not tracking the upstream report. The crash evidence (both reports + tmpfs log excerpts) and the voice-passed issue draft stay preserved in [[file:working/hyprland-layoutmsg-crash/][working/hyprland-layoutmsg-crash/]] if it recurs and is worth reviving. +Grading: Critical severity (SIGSEGV kills the whole desktop session; every GUI app's unsaved state lost) x rare edge case (twice in ~4.5 months: 2026-03-07 on v0.54.1, 2026-07-20 on v0.55.4) = P2 = [#B]. Upstream Hyprland bug, not this repo's code — the task tracks reporting it and picking up the fix. +A layoutmsg mfact dispatch (layout-resize, mod+H/L) throws std::bad_variant_access inside Layout::CAlgorithm::layoutMsg, uncaught, SIGSEGV. Both crashes fired from the layout-resize mfact path (keycode 104 shrink today, 108 grow in March). Layout at crash was master and the identical mfact had worked seconds earlier; the pre-crash window held monocle<->master toggles, two window closes dropping focus to "[Window nullptr]", and togglefloating x2. Monocle is a registered v0.55 layout (log shows graceful "Unknown monocle layoutmsg" rejects), so the config is not at fault; related edges are guarded ("mfact -> no window") while this path misses its variant guard. Repo has no newer build (0.55.4-1 installed and repo). +Evidence preserved in [[file:working/hyprland-layoutmsg-crash/][working/hyprland-layoutmsg-crash/]] (both crash reports + excerpts from the tmpfs session log, extracted before reboot loses it). +Next: Craig posts the issue himself (2026-07-20 decision) — the voice-passed draft is [[file:working/hyprland-layoutmsg-crash/issue-draft.md][issue-draft.md]], with both crash reports and the log excerpts beside it for attaching. Watch the repo for a fixed release and close on confirmation. The layout-resize script guard was declined (a script can't observe the internal desync). +** DONE [#C] WireGuard import is now config-less — decide feature fate :feature:network: +CLOSED: [2026-07-21 Tue] +Decided 2026-07-21 (Craig): KEEP the import feature. The out-of-band flow is already in place — =assets/wireguard-config/= carries a README documenting "drop plaintext =*.conf= locally at install time (gitignored); ship encrypted =*.conf.gpg= to track", and its =.gitignore= enforces it (=*.conf= blocked, =!*.conf.gpg= allowed). The script also already no-ops gracefully on an empty dir (=shopt -s nullglob= + a =found= flag), so nothing ships and nothing errors when no configs are present. Nothing to build; the fate decision was the whole task. +scripts/import-wireguard-configs.sh reads assets/wireguard-config/*.conf, but no configs ship in the repo anymore (removed as a public-leak fix; .gitignore blocks plaintext). +** DONE [#C] Dupre theme waybar.css drifted from live style.css :bug:dotfiles:waybar: +CLOSED: [2026-07-21 Tue] +Fixed in dotfiles 3e4e7ff (2026-07-20, "fix(theme): sync dupre waybar.css with the live weather rules") — dupre/waybar.css is byte-identical to live again, restoring the =#custom-weather= selectors/hover/gold divider, so =tests/theme-css= is green. +Grading: Minor severity (cosmetic, reverts only on a theme switch) × rare edge case (dupre is already the active theme) = P4 = [#D] on user impact, bumped to [#C] because the dotfiles =make test= stays RED until synced, poisoning the green baseline for every future commit. +The weather-kit work added =#custom-weather= selectors to =hyprland/.config/waybar/style.css= but never mirrored them into =hyprland/.config/themes/dupre/waybar.css=. =tests/theme-css= asserts the two files are identical (set-theme copies the theme file over the live one), so switching to dupre would silently revert the weather chip styling. Fix: sync the theme file to live. Pre-existing; found 2026-07-19 during an unrelated commit's green-baseline run. +** DONE [#B] Dotfiles tests leak state across files :bug:test:dotfiles:solo: +CLOSED: [2026-07-23 Thu] +Resolved 2026-07-23 as dotfiles =c333598=. The polluter was =tests/weather/test_weather.py=, and it accounted for all 38 failures on its own. + +The mechanism was not the env leak the body below guessed at — tests/weather never writes =os.environ=. Its whereami fake did =weather.subprocess.run = ...= on a freshly-loaded module object. The fresh module isolated the weather code, but =weather.subprocess= is the one shared stdlib module object every module in the process holds, so the assignment replaced =subprocess.run= process-wide and never restored it. Every later test file got weather's fake result back from =subprocess.run=; the tell was wtimer asserting on =r.returncode= and getting "'R' object has no attribute 'returncode'", where =R= is weather's fake result class. + +Triage: TEST HYGIENE, not production global state. The weather script reads env at import and never writes, so no long-lived-process caching defect sits behind it. A scan for the same pattern (patching a stdlib module attribute reached through another module's namespace) finds exactly one instance in the suite — the three other =setattr= sites all snapshot and restore. So the planned shared env helper across 28 files was aimed at the wrong target and wasn't needed. + +Fix: rebind the loaded module's own =subprocess= name to a stub namespace, so nothing outside that module changes and there is nothing to restore. + +Gate: =make test= now runs two gates per the add-don't-replace decision — =test-forked= (one process per file, catches order dependence) and the new =test-shared= (every suite in one process, catches leakage). Built on stdlib unittest rather than pytest, since pytest was only the diagnostic tool and isn't a project dependency. Verified as a real gate, not just green today: with the defect deliberately reintroduced it goes red, and green once restored. A focused test in tests/weather pins the invariant on the culprit as well, because the shared gate alone blames the three victim files. + +Verification: 3500 tests, both gates, exit 0. + +Original finding follows. + +Found 2026-07-23 during the speedrun. =make test= is green, but it runs each test file in its own =python3 -m unittest= process, which hides cross-file state leakage. A single-process whole-tree run (=python3 -m pytest tests/ -p no:randomly=) fails 38: 22 in =tests/wtimer/test_wtimer.py=, 10 in =tests/zoom-web/test_zoom_web.py=, 6 in =tests/wlogout-menu/test_wlogout_menu.py=. + +Not a regression — a worktree at the pre-speedrun commit produces the identical 22/10/6 profile, so this predates tonight's work. Those three files also pass cleanly when run together (170 passed), so the polluter is a fourth file somewhere in the tree that mutates global state (env var, cwd, or a module-level patch) without restoring it. 28 test files write =os.environ= directly. + +Why it matters: the green gate can't see this class of bug, so a real isolation defect — or a genuine failure that only appears under a different order — passes CI silently. Bisect by running the tree with subsets until the polluter is identified (pytest's =-p no:randomly= keeps the order stable while bisecting), fix its cleanup, then decide whether =make test= should gain a single-process pass so the gate covers it. +** DONE [#B] Wallpaper view freezes the panel — thumbnail decode :bug:dotfiles:solo: +CLOSED: [2026-07-23 Thu] +Craig reported 2026-07-23: selecting the wallpaper button freezes the module and the compositor asks whether to kill it. Root cause proven: =_Thumb._draw= decoded each source image with =new_from_file_at_scale= on the GTK main thread. Measured on Craig's 78 wallpapers — a viewport of the 8 largest takes 3.7s, the whole set 13s. That block trips Hyprland's "not responding" watchdog. + +Grading: Critical severity (panel unusable, watchdog kill) × every user every time the wallpaper view opens = P1 = [#A] by the matrix. Held at [#B] because step 1 already shipped and removes the user-visible freeze; the remainder is a latency enhancement, not a showstopper. + +*** 2026-07-23 Thu @ 15:40 Step 1 — async decode (dotfiles f45f321) +Moved the decode to a worker thread via a new =settings/thumbcache.py= (pure, injected decode/scheduler/thread; 6 tests). The thumb shows its dark ground until the pixbuf lands, then redraws. Verified live on a headless output: worst main-loop stall opening the pair view dropped from multi-second to 68ms; the cache filled with 81 decoded pixbufs (the one miss is a .webm, correctly falling back to the ▶ glyph). Full suite 3512, both gates, smoke OK. This alone fixes the reported freeze. + +*** 2026-07-23 Thu @ 16:30 Step 2 — persistent on-disk cache (dotfiles 463cc4f) +Built the persistent layer: =settings/thumbstore.py= decodes each source once to a 512px PNG under =~/.cache/settings/thumbs=, keyed by path + mtime so an edited wallpaper self-invalidates. The hot-path decode reads that PNG and scales in-memory. Warming rides the existing =settings tick= CLI verb (the 2-min timer already runs it), building up to =WARM_PER_BEAT=8= missing thumbnails per beat — best-effort, journals a line on failure, never blocks the wallpaper flip. thumbstore is pure (stat/decode/load/save injected); 10 tests. + +Went with incremental warming (8/beat, ~10 beats to full) as the safe default rather than full-warm-on-change — the per-beat cap is a one-line flip if Craig wants it faster. Measured: hot-path decode of a viewport dropped from 3.7s cold to 47ms warm. No installer change (the tick service already runs =settings tick=); cache lives outside the repo. Full suite 3522, both gates, smoke OK, live panel verified (81 pixbufs render, 48ms worst stall warm). +** DONE [#C] Panel scrollbars too short :bug:dotfiles:quick:solo: +CLOSED: [2026-07-23 Thu] +Shipped 2026-07-23 as dotfiles =0d64837= (22px scrollbar, 16px trough, 14px slider thickness with a 48px floor along the travel axis). Left open by oversight during the speedrun; closing now. + +Follow-on, and my own regression: enlarging the bar to 22px is what made it start covering the thumbnails, because nothing grew the tray to match. Craig reported it the same day ("scrollbars that obscure the images") and it's fixed in =c0ddf57= — the tray now reserves a 22px lane for the bar as a margin on the scrolled box, so the bar sits below the images instead of across them. Measured before: tray 68px, content 68px, a visible 14px bar inside the same 68px. After: tray 90, content 68, bar clear. The lane is a constant under the scrollbar CSS with a note to keep the two in step, since the coupling between bar thickness and tray height is exactly what broke. + +From the roam inbox (Craig, claimed 2026-07-23): all scrollbars need to be much taller than before. The always-visible scrollbars shipped in 7e8eb4a set =min-height: 10px; min-width: 10px= on the slider (=settings/src/settings/gui.py=, the =.dupre-panel scrollbar slider= rule) — that's the floor for a short slider, and the trough itself is thin. Raise both the slider floor and the trough thickness so the bar is comfortably grabbable. Cosmetic × every glance at the wallpaper trays = P3 = [#C]. +** DONE [#C] Velox refresh sweep :chore:maint: +CLOSED: [2026-07-23 Thu] +From the roam inbox (Craig, claimed 2026-07-23): velox needs bringing up to date, the mouse/touchpad module is still there, investigate what else didn't move over. + +Resolved 2026-07-23 by a full sweep over tailscale. The touchpad module was already gone — velox's running waybar (started 01:05, after the reboot) and its tracked config both carry zero =custom/touchpad= entries; what Craig saw was the pre-restow waybar process from before the reboot, and the reboot cleared it. Sweep results: both machines at dotfiles f9b6404 (all three hyprland lock/exit fixes live on velox, config errors clean, =allow_session_lock_restore= reads true); stow restow clean, only the expected skip-worktree files; rulesets pulled to 50fc7ca and =make install= run (agent-text verified working by invoking it — an earlier "MISSING" reading was a PATH artifact of the non-interactive ssh shell, not a real gap); desktop-settings tick timer active; mpvpaper, power-profiles-daemon, gtk4-layer-shell, webkit2gtk all present. + +Genuine remaining differences, all per-machine installs rather than sync failures: =cmail-action=, =gcalcli=, and =playwright= aren't installed on velox, and =obsbot-wb-guard.service= isn't enabled there (the OBSBOT lives on ratio). None block anything; file separately if velox should send mail or drive browser tests. +*** 2026-08-20 Thu @ 09:53:04 -0700 One of those three closed itself; two still stand +=cmail-action= is on both daily drivers now, and nothing did it deliberately. It moved into rulesets at =claude-templates/bin/=, and rulesets' =make install= links that whole directory into =~/.local/bin= at every session start — so velox picked it up on its own. Verified here: the symlink was written 05:44 this morning by this session's own startup, and the tool runs. + +=gcalcli= and =playwright= are still absent on velox, which stays correct until I say velox should send calendar invites or drive browser tests. =obsbot-wb-guard= is still right to be off here; the camera is on ratio. + +Leaving the paragraph above as written rather than striking it (rulesets suggested striking). It is the resolution note of a task closed 2026-07-23 and it was accurate that day. Editing a closed record to match today makes it a worse record, and the useful correction is this dated entry, not a redaction. +** DONE [#C] Weather tooltip sunrise and sunset :feature:waybar:weather:quick:solo: +CLOSED: [2026-07-23 Thu] +Shipped 2026-07-23 as dotfiles =de62e9d=. The two rows sit directly below Humidity in the current-conditions block, rendered in the footer's 12-hour format (=%-I:%M %p=) so the tooltip reads one way throughout. + +Confirmed the no-extra-round-trip premise held: =sunrise,sunset= joined the existing =&daily== block. Split =forecast_url= and =reading_from= out of =fetch= so both the request and the reading are testable without network — that's what let the new cases cover a payload missing the fields. Six tests (Normal/Boundary/Error): row placement and format, a pre-change cache with no sun fields, an unparseable stamp, today's pair picked out of the six-day arrays, and the API omitting them. Reused the existing =_at= helper rather than adding a near-duplicate =_first=. + +Live-verified against the real API: sunrise 6:14 AM, sunset 7:59 PM for today in New Orleans, rendering in the actual tooltip. Full suite 3506 tests, both gates, exit 0. + +Open, not blocking: every other header row carries a glyph (thermometer, droplet, wind arrow) and the sun rows are plain text. The file's glyphs are marked font-confirmed codepoints, and I haven't verified a sunrise/sunset glyph renders rather than showing tofu, so I left them bare. Craig's call. + +From the roam inbox (Craig, claimed 2026-07-23): in the weather module's hover text, the section immediately after the location ends with the current humidity. Add the sunrise and sunset times for the current location directly below it. + +Cheap to source: the module already calls Open-Meteo with a =&daily== block (=common/.local/bin/weather=, the forecast URL around line 336), so =sunrise,sunset= joins that same request with no extra round trip — normalise_daily already parses the daily arrays. Times arrive as local ISO strings; render in Craig's canonical clock format rather than re-deriving one. The settings package's =suntimes.py= (pure NOAA math, no network) stays the offline fallback path if the API field is ever absent — don't duplicate its math here. +** DONE [#C] Maint doctor-row copy button :refactor:maint:quick:solo: +CLOSED: [2026-07-23 Thu] +Shipped 2026-07-23 as dotfiles =761fa5c=, "fix(maint): drop the COPY key from the doctor row" — the key and its orphaned handler removed from =maint/src/maint/gui.py=. =viewmodel.status_copy_text= stays: it's a tested pure serializer and the obvious source if a copy surface returns somewhere better placed. + +Correction to the body below: it describes a per-row button and a separate global one. There is only one COPY key, and it IS the global one Craig added in 8bc79ba two days earlier. He tried it and wanted it gone, so the row now reads DOCTOR · CLEAN UP · REVIEW & FIX. + +From the roam inbox (Craig, claimed 2026-07-23): remove the per-doctor-row copy button (next to REVIEW and FIX) from the maint status wall. The global COPY key (dotfiles 8bc79ba, "one global button copying rendered text") stays the one copy surface — the per-row button turned out to be clutter next to it. +** DONE [#C] WiFi tooltip signal strength :feature:waybar:network: +CLOSED: [2026-07-22 Wed] +From the roam inbox (Craig, claimed 2026-07-22): add signal strength to the WiFi tooltip. + +Resolved 2026-07-22: the tooltip's signal line existed but never fired on ratio — the mt7925 driver leaves /proc/net/wireless empty (legacy WEXT procfs unimplemented), so the dBm read returned None and the bar glyph fell to the weakest tier. Fix in dotfiles net/: an iw-dev-link nl80211 fallback (only spawns when procfs is empty), a signal_percent mapping, and an enriched line — Signal: ▂▄▆█ 100% · -32 dBm (excellent) — bars by band, percent, raw dBm, band word. The bar icon tier fixed itself as a side effect. +** DONE [#B] Desktop-settings dropdown panel :feature:waybar: +CLOSED: [2026-07-22 Wed] +:PROPERTIES: +:LAST_REVIEWED: 2026-07-22 +:END: +Resolved 2026-07-22: shipped end to end via the "Build: desktop-settings panel" task (dotfiles 7a15237 → 9038eee; spec IMPLEMENTED, 85 suites + smoke 13/13 + e2e 17/17). Every open question below got settled in the spec: bar consolidation landed (74f723e), the wallpaper manager became the in-panel sub-view, and the format pickers split into their own sibling spec ([[file:docs/specs/2026-07-19-display-format-single-source-of-truth-spec.org]], DRAFT stub). Remaining human-eye checks live under "Manual testing and validation". + +Original body follows as the record. + +Initial spec written 2026-07-02: [[file:docs/specs/2026-07-02-desktop-settings-panel-spec.org]] (DRAFT — four decisions await Craig's review before build; architecture updated to the net panel's Blueprint/GTK4 stack). + +One waybar dropdown gathering the desktop toggles and sliders into a single settings panel, opened from a gear/settings glyph on the bar. Incorporate: +- *Auto-dim* toggle (the =custom/dim= feature just shipped — fold in here, or keep the standalone indicator and mirror it). +- *Brightness* slider (backlight, via brightnessctl). +- *Keyboard-backlight* brightness slider (brightnessctl on the kbd_backlight class). +- *Mouse* enable/disable toggle — shown only when a mouse is connected. +- *Trackpad* enable/disable toggle — shown only when a trackpad is connected (mirror =toggle-touchpad= / =touchpad-auto=). +- *Idle inhibitor* (the =custom/idle= module that replaced the built-in =idle_inhibitor= 2026-06-24 — toggles the hypridle daemon, state-synced icon). +- *Airplane mode* (the existing =airplane-mode= toggle; laptop-only). + +The conditional rows (mouse, trackpad, airplane) appear only when their hardware/context applies — reuse the laptop/device detection the airplane and touchpad indicators already do. + +Design / open questions (propose before building): +- Panel tech: sliders need a real toolkit (waybar can't host a slider), so a GTK4 + gtk4-layer-shell app like pocketbook is the likely shape. +- Which existing standalone bar modules (dim, touchpad, airplane, idle_inhibitor) collapse INTO this panel vs. stay on the bar as quick-access indicators. Craig's call. + +Implementation notes: a small GTK layer-shell app (mirror pocketbook's structure: src-layout Python package, pytest, Makefile) talking to brightnessctl / hyprctl / the touchpad + airplane helpers. Lives in the dotfiles repo or in-tree like pocketbook. TDD the backing toggle/slider logic. Sizable — worth a design doc first. + +Home handoff 2026-07-19 (inbox, resolving the open "few other things" decision — fold into the spec, close the open decision, extend the controls table, then run spec-review, may flip DRAFT→READY). Ownership: home drives the build (dotfiles settings/), archsetup keeps the canonical spec. Full reconciliation in home docs/design/2026-07-19-desktop-settings-module-brainstorm.org. +- ADD controls: night-light / color temperature; Do Not Disturb / notifications (dunst); lock / suspend quick actions; power profile (performance/balanced/saver); scenes/profiles — one control flipping several toggles at once (Focus, Presentation, Battery-saver, Night). Scenes are the payoff of consolidating everything. +- OUT (record reasons): volume / master-mute stays with the audio panel (no mirror here); theme light/dark goes to the theme-studio task. +- FORMAT PICKERS pulled to their own future sibling spec — time/date/weather format is out of THIS panel. Rationale: format settings live in many programs, so the design problem is a single source of truth for the canonical format. Track a future sibling-spec stub in docs/specs (time/date/weather format single-source-of-truth); Craig thinking it through separately, not started. +- STILL OPEN (spec already flags): wallpaper manager confirmed in scope, but row-that-opens-a-sub-view vs its own sub-spec undecided — resolve at spec-review. +** DONE [#C] Gallery probe: the fader-drag check is flaky :bug:test:design:quick:solo: +CLOSED: [2026-07-23 Thu] +Fixed 2026-07-23. Root cause confirmed rather than suspected: =panel-widget-gallery.html= line 74 sets =html{scroll-behavior:smooth}=, so =scrollIntoView= animates and the fixed 200ms sleep sometimes read =getBoundingClientRect= mid-scroll. The drag then dispatched at stale coordinates, the press missed the fader, and the check reported a dead widget. + +Fix: scroll with =behavior:'instant'=. The probe never needed the animation, so this removes the race instead of waiting it out. Also added a =settledRect= guard (rect stable across two reads AND on-screen) for zoom/column relayout, and a =hits()= assertion that the press actually lands on the fader before the drag goes out. + +Applied to the toggle-click check too — it shares the same fixed-sleep shape, and it failed for this exact reason during the diagnosis, so fixing only the fader would have left half the defect. + +Worth recording: my FIRST fix was wrong and made it worse. Polling until the rect stopped changing returned pre-scroll coordinates every time, because two identical samples are also what you get before the animation starts — an intermittent failure became a consistent one. The new hit-test assertion is what caught it, printing the press point at y=1326 against a 1200px window. That's the argument for asserting the press landed rather than only asserting the readout moved. + +Verified against the measured 1-in-6 failure rate: 8 consecutive runs, all three checks passing, with the press point identical every run (429,480) — deterministic, not lucky. Full probe 96 PASS, 0 FAIL, exit 0. + +=probe.mjs= check 3 ("fader drag tracks at 3x") intermittently reports =level 68 -> level 68=, i.e. the synthetic drag never registers. It has presumably been doing this all along unnoticed, since the suite is normally run once per batch. + +Grading: *Minor* severity (a false FAIL costs a re-run and a few minutes, and never ships a defect) x *most users, frequently* = P3 = =[#C]=. + +Frequency measured 2026-07-16, not estimated: 1 failure in 6 consecutive runs, having already fired twice in about fifteen that afternoon. The first grading guessed "some users, sometimes" (~1 in 10); at ~1 in 6, both people who run this suite hit it most sessions, so the row is "most users, frequently". The letter lands on =[#C]= either way, but the input was wrong and the matrix is only worth anything if its inputs are measured. + +Suspected cause: the check clicks the 3x size chip, calls =scrollIntoView=, waits a fixed 200ms, then reads =getBoundingClientRect= and dispatches the drag against those coordinates. If the zoom relayout or the smooth scroll hasn't settled, the rect is stale and the press lands off the fader — so the drag is a no-op and the readout never moves. The other timing-sensitive checks share the same fixed-sleep shape. + +*Do not fix this by raising the sleep.* That hides the race rather than removing it and leaves the check failing again on a slower run. Wait on the actual condition instead: poll until the rect stops changing between frames, or assert the press landed on the fader before dispatching the drag (the probes' own README already warns that a =find()= miss dispatches into nothing and reports as a widget bug). + +Why it matters beyond the annoyance: a gate that cries wolf gets its real failures ignored, and this suite is the only thing standing between the gallery and a silent regression. + +Recurrences: 2026-07-18 batch-6 gate (first run, passed 3 reruns); 2026-07-18 batch-9 gate (first cold run, =level 68 -> level 68=, passed 2 reruns); 2026-07-21 double-speedrun run (flashed one RED mid-run, passed on rerun). All were a session's first/early probe run — consistent with the stale-rect theory (cold-start relayout settling slower than the fixed 200ms sleep). +** DONE [#C] Dotfiles stow conflicts: first-launch risk + restow directory handling :bug:dotfiles:quick:solo: +CLOSED: [2026-07-23 Thu] +:PROPERTIES: +:LAST_REVIEWED: 2026-07-14 +:END: +Closed 2026-07-23. The last open item was the velox check, and velox is reachable again. Pulled it from =02df01a= to =de62e9d= (clean tree, fast-forward), then ran =make conflicts hyprland=, which dry-runs every tier: "No stow conflicts", exit 0. Its old conflict copy had already cleared against the updated repo, so there was nothing for =make reset= to do. + +Verified the pull is live through the symlinks rather than just present in the repo: =~/.local/bin/weather= resolves into the dotfiles tree and returns today's sun times on velox. + +Note for the record: =make conflicts common= is not a valid invocation — =check-de= rejects it, because common and the host tier are auto-included in the DE-scoped run. =make conflicts hyprland= is the whole check. +*** 2026-07-14 Tue @ 00:51:51 -0500 Ratio calibre check passed; waypaper canonical decided (dark-lion) +Ratio's ~/.config/calibre is a directory symlink into the dotfiles repo (stow folded the whole dir), so the first-launch gap never existed there — check closed. Craig decided dark-lion.jpg is the canonical waypaper wallpaper; the repo config.ini updated from the that-one-up-there.jpg placeholder (the file is skip-worktree volatile, unskipped for the commit and re-flagged). Remaining: when velox is back online, run make conflicts / make reset there so its old conflict copy clears against the updated repo. +*** 2026-07-02 Thu @ 17:30:00 -0400 Shipped the Makefile hardening + first-launch guard (dotfiles 42a82d2) +The solo-able subset landed in the speedrun. =make conflicts <de>= is the loud first-launch guard: dry-runs all tiers, parses all four stow error shapes (plain file conflict, foreign symlink, dir-over-file, and restow's unstow_contents non-directory ERROR), lists each blocker with a directory/foreign-symlink marker, exits 1 when any exist. =make reset= now pre-clears the directory and foreign-symlink blockers =--adopt= aborts atomically on (removals printed; repo version wins per the target's contract), then adopts + git-checkouts as before. =make restow='s overwrite path switched rm -f → rm -rf so directory conflicts clear. 8 sandbox tests drive the real Makefile against a throwaway HOME (44 suites green). Also verified on velox: the whereami and mpd-playlists conflicts noted in this task were already hand-converted 2026-06-29 — =make conflicts hyprland= reports clean live. REMAINING (deferred per Craig's speedrun pre-flight): the waypaper canonical decision (live velox dark-lion.jpg vs repo that-one-up-there.jpg) and the ratio calibre-symlink check (ratio paused). +From the velox calibre incident (2026-06-27, note in ~/.dotfiles/inbox/processed/): calibre was launched before =make stow= ran, wrote its own default config into =~/.config/calibre/=, and silently blocked its own stow — it ran on factory defaults while the rest of common/ stowed fine. General pattern: any GUI app that auto-creates config on first run, launched before stow, blocks its own stow the same way. Velox was repaired by hand (=ln -srf= symlinks byte-identical to =stow --no-folding= output). + +Remaining work (re-graded C 2026-07-02 — the first-launch risk and the Makefile handling shipped in the speedrun; what's left is a paused-machine check): +- Waypaper canonical decision (Craig): RESOLVED 2026-07-14 — dark-lion.jpg is canonical (dotfiles fea3e93), repo config.ini updated off the that-one-up-there.jpg placeholder. +- Ratio check: RESOLVED 2026-07-14 — ratio's =~/.config/calibre= is a directory symlink into the repo (stow folded the dir), so the first-launch gap never existed there. +- When velox is back online: run =make conflicts= / =make reset= there so its old conflict copy clears against the updated repo. (velox carries a separate boot-recovery task; check once it's reachable.) |
