The app built PATH by appending its curated directories after the
inherited value, so whichever `node` came first won -- on an nvm machine
that is the default alias, which can be an older major than the one the
CLI was installed under. Every spawn then died on the engine gate
("codeburn requires Node.js >= 22.13.0 (current: v20.20.2)") and the
popover showed "Could not load Today" with nothing pointing at Node.
augmentedPath now takes the resolved CLI path and, when an executable
node sits in the same directory, moves that directory to the front of
PATH -- promoting it if already present, dropping nothing. That
interpreter is the one that installed the CLI and satisfies its engine
range. A bare `codeburn` or a CLI with no sibling node leaves the
inherited order untouched, so #1263's Nix layout is unaffected.
makeProcess resolved the CLI twice, once for PATH and once for argv; it
now resolves once and passes the same value to both.
Three tests cover the reorder, the promote-not-duplicate case, and the
untouched case; removing only the reorder block makes the first two fail
with the reported shape while every pre-existing test still passes.
On a nix-darwin machine node lives only in /etc/profiles/per-user/$USER/bin,
which appears in neither additionalPathEntries nor userNodePaths. The CLI shim
begins '#!/usr/bin/env node', so the spawn dies with
'env: node: No such file or directory' and exit status 127.
GUI apps normally inherit those directories from 'launchctl config user path',
stored in /var/db/com.apple.xpc.launchd/config/user.plist, which a macOS system
update deletes. Re-applying it is not durable, so name the Nix locations in
userNodePaths instead. That feeds both installedArgv() discovery and
augmentedPath() interpreter resolution.
Also adds ~/.local/bin, the bin directory for the common 'prefix = ~/.local'
npm configuration, which installedArgv() could not otherwise discover.
Entries are appended, so existing matches keep their precedence.
Every 30s tick spawned 2-3 concurrent Node CLI processes at 100%+ CPU,
and an app-lifetime activity assertion disabled App Nap forever, putting
the menubar's energy use in the video-call class (#647).
- Closed popover now fetches only what the status item renders (menubar
period plus today); the popover's selected key refreshes on open via
the existing recovery path, which now also catches up the status
figure and clears a latched display-sleep flag.
- Spawn cadence stretches to 150s on battery and 300s in Low Power Mode
while the popover is closed; the 30s timer itself keeps running.
- The app-lifetime beginActivity is gone; each in-flight refresh
(including manual Refresh Now) holds its own scoped activity, and an
NSBackgroundActivityScheduler backstop tick every ~3min bounds status
staleness once App Nap starts coalescing the timer.
- Display sleep skips ticks entirely; wake and popover open clear it.
- The status-payload fallback honors the same spawn interval so the 30s
cache TTL cannot defeat the backoff.
Child process QoS stays .userInitiated on purpose: lowering it is
documented to slow parses 5-10x and starve the cadence; the win here is
spawn frequency, not priority. Measured on an idle machine: two
concurrent 100%+ CPU spawns per 30s window before, one spawn in 72s
after, with App Nap coalescing the rest.
Fixes#647
Run CodeBurn's own signed binary in --refresh-once mode under the LaunchAgent
so the spawned CLI inherits CodeBurn's TCC grant. The bare-node shell script
prompted "node would like to access data from other apps" because launchd-
spawned children lose the signed app's TCC attribution. Drops the generated
shell script, scriptEnvironment, and per-period regeneration; the plist is now
static and period is read from UserDefaults at refresh time.
GUI-launched apps inherit a minimal PATH that misses user-managed Node
directories. Scan ~/.volta/bin, ~/.npm-global/bin, ~/.asdf/shims, and
the newest nvm Node version at launch. All candidates pass isSafe()
validation. Closes#331.
Two independent causes for the stuck-label / only-refreshes-on-click
behaviour, both fixed here.
1. NSStatusItem button defers the status bar paint for accessory apps
that are not foreground, so after refreshStatusButton sets the new
attributed title the menu bar visually froze until the user opened
the popover (which triggers NSApp.activate and a forced redraw
cycle). Explicit needsDisplay + display() forces the paint every
cycle.
2. The codeburn subprocess inherited the accessory app's default QoS,
which macOS background-throttles. That could stretch a sub-1-second
parse into tens of seconds on large corpora and overrun the 15s
refresh cadence. Set .userInitiated so the CLI runs at the same
priority it does from a user-interactive terminal.