Skip to content

Refactor temperature reading, improve error handling, and optimize CPU usage - #3

Merged
itsdanieldk merged 13 commits into
mainfrom
audit/phase-1-2-remediation
Aug 2, 2026
Merged

Refactor temperature reading, improve error handling, and optimize CPU usage#3
itsdanieldk merged 13 commits into
mainfrom
audit/phase-1-2-remediation

Conversation

@itsdanieldk

Copy link
Copy Markdown
Owner

This pull request introduces several improvements and clarifications to the repository's documentation, configuration, and automation. The most significant changes are the addition of a shell linting workflow, expanded and reorganized documentation for contributors (especially for Claude Code), and updates to the AeroSpace configuration and related documentation to clarify keybindings and window gap logic.

Automation and Linting

  • Added a new GitHub Actions workflow .github/workflows/lint.yml to automate shell script linting with shellcheck, parse the install script with zsh, and check SketchyBar configuration semantics. This helps catch errors early and enforce code quality for shell scripts.
  • Added shellcheck to the Brewfile for local linting support.

Documentation and Contributor Guidance

  • Major expansion and reorganization of CLAUDE.md:
    • Added a working agreement for Claude Code, clarified stow usage, documented hardware assumptions, keyboard hazards, SketchyBar pitfalls, install script gotchas, and required .zshrc load order. Also clarified theme and vendored script requirements.
  • Updated README.md to clarify that AeroSpace uses ctrl-1ctrl-9 for workspace switching (not alt), with rationale for avoiding alt due to keyboard layout conflicts.

Configuration Updates

  • Updated AeroSpace configuration (aerospace/.config/aerospace/aerospace.toml):
    • Removed upstream-specific comments, clarified documentation about workspace keybindings, and improved explanations for layout and gap settings.
    • Changed gaps.outer.top to a fixed value (43) to match the SketchyBar height and simplify per-monitor gap logic, removing previous per-monitor handling.
    • Improved comments and documentation throughout for maintainability and clarity. [1] [2] [3]

Package Management

  • Added media-control to the Brewfile for improved media key support.

These changes enhance code quality, onboarding clarity, and configuration maintainability.

macmon's temp.cpu_temp_avg is not trustworthy. Measured inside a SINGLE
invocation with CPU load flat at 1.5-3.7% and cpu_power at 0.06W:

    t+0s  cpu=37.74  gpu=35.33
    t+2s  cpu=31.79  gpu=35.27
    t+4s  cpu=20.19  gpu=35.27

A die cannot cool 17C in two seconds and reheat. Across trials the value
is bimodal, landing on ~31.9 or ~38.1 and never between, which is the
signature of a mean over a varying sensor set rather than a temperature.

helpers/thermal.swift reads the PMU tdie sensors directly through
IOHIDEventSystemClient (no sudo). Spread 0.30C over 20 reads, and it
tracks real load: 34.9 idle -> 37.6 under full load, monotonic.

The helper was untracked until now, while both install and system.sh
depended on it. Both guarded defensively, so a fresh clone produced a bar
with no temperature and no error at all.
The two helper build blocks tested `[[ -r $src ]] && command -v swiftc`
in one condition, so a MISSING SOURCE and a MISSING TOOLCHAIN were
indistinguishable and both exited without printing anything -- no ok, no
warn. thermal.swift was untracked in git, so a fresh clone hit exactly
that path and produced a bar with no temperature and no explanation.

Replace both with one build_sb_helper function carrying a three-way
guard, so each outcome reports itself.
…P errors

sketchybarrc pointed at "the privacy note in weather.sh"; no such note
existed. With LOCATION empty, wttr.in geolocates by SOURCE IP, so every
poll discloses the host's public IP to a third party. That is a fair
trade for zero configuration but it is not visible from the URL.

Write the note, and add SKETCHYBAR_WEATHER_LOCATION so the behaviour can
be pinned to a place or turned off.

Also curl -sf: without -f, curl exits 0 on a 5xx and the error page is
written to the cache, which is then served as the last-known-good reading
indefinitely.
The rule was commented out on the belief that Raycast was in the Brewfile
but not installed. /Applications/Raycast.app exists and its Info.plist
gives com.raycast.macos -- exactly the id the comment declined to guess.

Caveat recorded inline: the id came from Info.plist, not from
`aerospace list-apps` with the Settings window open, since Raycast was
not running.
volume showed "missing value%". osascript prints that literal string and
exits 0 for a device with no software volume, so the `[ -z "$vol" ]` guard
sailed past it. The output here is a Focusrite Scarlett 2i2, which exposes
neither kAudioDevicePropertyVolumeScalar nor Mute on any scope or element
-- confirmed against CoreAudio. There is genuinely no level to show.

brightness showed a constant 100%. It looped display ids 1..16 and took
the first DisplayServicesGetBrightness that answered; on this clamshell
desk that is the external, and the call SUCCEEDS for a non-Apple external
and returns a hardcoded 1.0. The 100% was fabricated, not stale.

Both now read the right source (brightness: gamma ramp for an external,
which is what MonitorControl's software dimming manipulates -- verified
0.75/0.50/0.25 -> 75/50/25%) and hide rather than invent a number.

colors.sh gains the compositing note left over from a frosted-bar
experiment that was reverted.
Three failure classes that are invisible when they occur, all found the
hard way:

  - a self-hiding item under the config-wide updates=when_shown default
    stops being updated entirely, so it can never un-hide. Eight items
    were affected; each worked after a reload and then went quiet.
  - SketchyBar fork_execs plugins and reports NOTHING when the execute
    bit is missing. The item simply never updates.
  - a plugin runs with less TCC access than a terminal, so ~/Library
    reads succeed by hand and fail with EPERM in the bar, at correct
    Unix permissions.

docs/audit-2026-08-02.md is a full production-readiness review with 20
findings, measured costs per plugin, and a four-phase roadmap.
…d data

mic was polled every 2s on the strength of a comment in mic.swift saying
the helper "runs in single-digit ms". Measured min/median/max over 10
consecutive runs: 60/63/65ms -- wrong by a factor of 8. At 2s that item
alone cost 1890ms of CPU per MINUTE, 36% of the whole bar's budget and
the single most expensive thing in the config. Now 5s.

The cost is inherent, not a Swift artefact: the helper enumerates every
CoreAudio device and queries two properties each, so it is O(devices) IPC
into coreaudiod per tick. Making it free means a property listener, not a
faster loop; noted in the source.

system was 30s at ~930ms per invocation (macmon alone measured 854ms,
because it samples a full SoC telemetry frame to extract three numbers).
Now 60s. CPU%, RAM and fan RPM are not acted on faster than that.

Together: 3750 -> 1686 ms/min, a 55% reduction in the bar's idle CPU.
Each of the nine workspace items ran its own copy of aerospace.sh, and
each copy shelled out to `aerospace list-workspaces` to ask a GLOBAL
question -- which workspaces hold windows -- whose answer is identical for
all nine. Measured 25ms per call, 104ms for the nine, plus nine greps.

Worse, the items were subscribed to front_app_switched as well as
aerospace_workspace_change, so all of that ran on every application
switch, one of the highest-frequency actions there is.

Replace with a zero-width driver item that owns the subscriptions and
rewrites every pill in a single sketchybar call: 2 aerospace calls and 1
sketchybar call regardless of workspace count. Full repaint now 54ms.

The occupancy test is parameter expansion instead of a grep per item, with
sentinel spaces so a future workspace 10 cannot match workspace 1.
Both PATH exports hardcoded /opt/homebrew, the Apple Silicon prefix. On
an Intel Mac every plugin depending on aerospace, macmon, gh or icalBuddy
would fail to find its binary -- and they all hide rather than error when
that happens, so the bar would come up merely looking quiet.

Prepending a directory that does not exist is harmless, and this avoids a
`brew --prefix` subprocess on every bar start.
hide() was defined identically in three plugins, the truncate idiom was
copy-pasted into three more, the helper build-on-demand block existed
twice, and amphetamine.sh had a six-line osascript block duplicated
verbatim inside itself.

lib.sh provides hide, truncate_label, state_file, ensure_helper and
require. Notes on two of them:

  hide() now closes any popup unconditionally. Two of the three local
  definitions did that and the third has no popup; popup.drawing=off on a
  popup-less item is accepted silently (verified). Doing it always removes
  a footgun -- a bare hide() that left a popup on screen would be a rare
  and subtle bug.

  truncate_label replaces `printf | cut -c1-N`, a two-subprocess pipeline
  run on every invocation of every plugin that shows a name. cut -c counts
  BYTES in some implementations while ${#s} and ${s:0:n} count characters,
  so the old form could cut a UTF-8 name mid-codepoint.

require() gives missing dependencies a voice: plugins previously failed
into empty output and then hid, which is indistinguishable from "nothing
to report".
Found by scripts/lint-sketchybar.sh, not by the hand audit that claimed to
have found all of them. music.sh hides via a hide() call rather than a
literal `--set "$NAME" drawing=off`, so the grep used to sweep for this
never saw it.

The item was live-broken: Apple Music is usually not running, so the item
was sitting hidden with updates=when_shown, meaning starting playback
would never have brought the pill back.
Three SketchyBar behaviours produce no error and no log line, so the only
symptom is an item that quietly stops updating:

  - a plugin without its execute bit; sketchybar fork_execs it and says
    nothing
  - a self-hiding item without updates=on, which can then never un-hide
  - a comment referencing a section that no longer exists, in a config
    where the comments carry the measurements

scripts/lint-sketchybar.sh checks all three, and earned its place
immediately by finding the music item bug above.

It is deliberately precise about two things it must NOT flag:
label.drawing=off and background.drawing=off hide a COMPONENT, leaving the
item drawn and updating (amphetamine and pomodoro both do this correctly);
and passive items like cpu and memory carry no script of their own, so
they are un-hidden by whichever item's script writes them.
@itsdanieldk
itsdanieldk merged commit 2e33608 into main Aug 2, 2026
2 checks passed
@itsdanieldk
itsdanieldk deleted the audit/phase-1-2-remediation branch August 2, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant