The letters were only visible through the animated gradient's mask, so any
frame where the mask or gradient was not laid out yet blanked the name at
random. The text now carries a static flame fill under the sweep, which can
only brighten it, and the sweep runs 4 s each way instead of 1.5 s.
Switching providers stashed the outgoing payload and rendered it under the new
tab until that tab's own fetch landed. That is not continuity, it is a wrong
number: opening the Hermes tab printed the all-provider total, and the test
suite pinned that behaviour by asserting $12 under Hermes and $12.34 under
Cursor.
The fallback is gone. With no cached data for the selection, `payload` is
empty, `hasCachedData` is false, and the popover renders the loading state it
already has for a cold key, so no dollar figure is shown for a provider whose
data has not arrived. AgentTabStrip and MenuBarContent take the same
hasCachedData gate for the same reason.
When a scoped payload disagreed with its all-provider slice, the code re-ran
the fetch bypassing the resident child and, if it STILL disagreed, threw. That
verified payload is what the CLI actually reports for the provider, so
throwing traded a real number for an error banner and left the tab with
nothing at all.
The payload is now cached with a contradictsAll flag on CachedPayload
(add-only) and served, and the popover carries one subdued secondary line
saying the total may be incomplete. The refresh paths treat an
already-verified contradiction as usable, so a poll cannot re-run the one-shot
parse forever.
To guarantee the all-provider slice was cached before a scoped result could be
accepted, the code simply awaited the all-provider fetch BEFORE starting the
scoped one. That is two full parses back to back on every tab click, and the
ordering it bought is only needed at acceptance time, not at start time.
Both fetches now start together. fetchPayload awaits the all-provider task
after its own fetch has already returned and before the consistency check
reads it, so the evidence is still present when the scoped result is judged.
The resident-bypassing cold fetch was already conditional on a contradiction
actually being detected; it is unchanged.
inFlightKeys held only a timestamp, so the slot had no notion of WHICH fetch
owned it. The watchdog and the display-sleep reset both evict an occupant, and
a later fetch then legitimately claims the same key. When the evicted fetch
finally returned, its defer cleared the new owner's slot and resumed every
task parked on it, so a waiter woke on an empty cache and reported failure
while the real fetch was still running.
The slot now carries a token. A fetch releases it only if it still owns it;
the watchdog and the resets take an explicit force path, because they are
declaring the slot dead rather than reporting a fetch that ended.
The interactive refresh path bailed with false whenever the key was already
being fetched. recoverFromStuckLoading() reads that return value, so a healthy
fetch that was simply still running was announced as a failed recovery, and a
second interactive refresh threw away a result that was moments from landing.
Join the in-flight fetch and report on its outcome, which is what the quiet
path already did.
Adds a DataClient fetch hook under DEBUG: slot ownership and acceptance
ordering are only observable when two REAL fetches overlap, which needs a
fetch whose completion the test controls rather than a spawned CLI.
hasUsage is a new payload field, so every RELEASED CLI omits it. Both decoders
treated the absent case as "derive from cost", which is the strict reading of
a field that is not there: a provider with $0 spend in the period vanished
from the picker even though the user has it and is actively using it on an
included subscription.
Absent now means visible on both surfaces. The strict signal still applies
wherever the CLI actually emits the field, so an explicit false still hides an
idle discovery row. Settings.tsx carried the same fallback and is fixed with
it.
The Capacity Dock hover panel becomes a glance: a provider header, the
sessions running right now as pills with their context rings, a Today
strip, and a row of quota-window percentages, separated by hairlines.
Window percentages stay plain text until 70 percent so a healthy row
reads as text rather than a wall of colour. The panel stays 350pt wide;
every block height is a whole point and is summed into the panel frame,
since the hosting view does not size itself.
Running sessions are new CLI surface. `liveSessions` is add-only on the
menubar payload: sessions whose transcript was appended inside a 120s
window, carrying the branch, the model that answered last, and the
context that turn reported. Absence means "unknown", so the section
hides rather than claiming nothing is running. The block names projects
and branches, so sharing and MCP output strip it.
Today's totals needed no CLI change; the app simply was not decoding the
cache token counts the payload already carried.
The lock no longer covers the defaults write, so the main-queue hop is
what keeps the read-modify-write atomic; say so where the next reader
would be tempted to make it async. The regression test now cleans up its
observer and defaults domain on every exit and waits up to 5 seconds, so
a loaded runner cannot fake a deadlock.
A popover content view that outlived its window kept re-laying itself out
at display cadence once the popover had been shown during the first load
(loading overlay 520pt tall, then the real height): 6 to 7 percent idle
CPU until relaunch, reproduced on demand. Limiting the hosting controller
to preferredContentSize was not enough on its own. Building the 360pt view
fresh per open and releasing it on close removes the survivor entirely;
the dock's hidden bubble panel gets the same treatment.
Reproduction on the same machine (minute logs, popover opened during the
first load and closed): before 6.1 to 7.1 percent sustained; after 0.1
percent sustained through the data load.
At 60 percent the docked pad was 12 + 18.6 points, putting the rail body at
111.2 against a 111 point panel; the same fractional mismatch as #1210,
hidden inside the attachment compensation instead of the base metrics.
The detail panel is created on the first hover and kept alive hidden, and
its height was the one dock dimension still fractional (186 at 60 percent
is 111.6), so from the first hover on it re-laid itself out at display
cadence forever: 0.1 percent idle CPU before the hover, 5.7 to 6.1 percent
after, measured on the release build. Same bug as #1210, second window.
Agent tabs filtered strictly on cost > 0, so a provider whose sessions
carry tokens but no recorded dollar cost (Hermes on a flat subscription,
free-tier models, unpriced models) vanished from the strip entirely.
The menubar payload now carries an activity signal per provider, and the
strip keys visibility off it: providers with real usage stay listed, while
providers merely discovered on disk stay hidden. Older payloads without
the signal keep the previous detected-provider behavior.
Applies consistently across the mac menubar, the Electron renderer, and
the Windows payload types.
A background credential read held the provider-presence lock while UserDefaults synchronously notified SwiftUI. SwiftUI could re-read presence while holding its own update lock, leaving each thread waiting on the other. Serialize presence writes on the main queue and release the presence lock before publishing defaults changes.
At any scale except 100% the dock's dimensions were fractional (85% of 88
is 74.8), so SwiftUI's fitted content never agreed with the pixel-aligned
panel frame and the hosting view re-laid itself out at display cadence
forever: 5 to 7 percent idle CPU and hundreds of wakeups a second once
quota data existed, on every release since the dock shipped. Measured on
the same machine: 60% and 85% scale held 5 to 6 percent for an hour;
100% scale idled at 0.1 to 0.2 percent. Rounding every scaled metric to a
whole point makes all scales behave like 100%.
The status-item popover's hosting view is created once and lives for the
app's lifetime, and the flame wordmark inside it ran a repeat-forever
shimmer, so SwiftUI re-rendered the closed popover at display cadence
continuously: 5 to 7 percent idle CPU and roughly 650 wakeups a second
(Activity Monitor showed a 12-hour energy figure near two thirds of
Chrome's). The wordmark sweep and the loading flame pulse now start and
stop with popover visibility via a store flag set by the popover
delegate.
A Free or Individual plan reports premium_interactions with entitlement 0
and 0% remaining, which both the menubar and the desktop decoder turned
into a full 100% used ring. Verified against a live quota payload.
The Copilot CLI's Keychain item is written by a Node keyring library, so
/usr/bin/security is not in its partition list. Unlike the Claude Code item,
every read would raise the macOS "allow access?" dialog, and with the probe
cache a user who clicks Deny would be asked again every five minutes.
`gh auth token` already reaches the same users, because the Copilot CLI itself
falls back to gh, so the rung was risk without benefit.
The chain is now legacy plugin files, ~/.copilot/config.json and settings.json,
the environment variables, `gh auth token`, and a pasted token. No `security`
subprocess remains in the adapter, and the probe cache collapses to the single
gh answer.
Modern Copilot clients no longer write ~/.config/github-copilot/hosts.json or
apps.json, so the menubar reported "No GitHub Copilot credentials found" even
after signing in through VS Code and the Copilot CLI.
Discovery now walks an ordered read-only chain, first hit wins, every rung
tolerating an absent, locked, or malformed source: the legacy plugin files,
the Copilot CLI keychain item (service copilot-cli, read via /usr/bin/security
so no partition-list prompt can fire), ~/.copilot/config.json and
settings.json, COPILOT_GITHUB_TOKEN / GH_TOKEN / GITHUB_TOKEN, `gh auth token`,
and finally a token pasted into Settings. The two subprocess rungs are cached
for five minutes so eligibility checks and the 30s refresh cadence never spawn
per call, and hasCredential stays spawn-free.
VS Code's encrypted secret storage is deliberately untouched.
The no-credentials message now says usage tracking still works and names every
way to connect, and the Copilot settings pane gains a token field.
The #1170 hover synthesis rides on a global .mouseMoved monitor, but
macOS only delivers global mouse-moved events while the frontmost app
itself requests them. Chrome does, so hover appeared fixed there;
Finder and Terminal do not, so the dock went hover-dead over the
desktop and most native apps.
Add a 10 Hz pointer poll feeding the same passthrough + hover sync
path. A same-position diff guard makes idle ticks free, and the timer
lives and dies with the existing monitor lifecycle.
Require every Dependencies field explicitly so a test can never fall
through to the live adapter and read the local Cursor session. Surface
an unreadable local store as its own honest error instead of a network
failure. Read the state database off the main actor so a lock held by
Cursor cannot stall the UI. Report the tighter of the Auto and API
pools when Cursor omits the total, since averaging hides an exhausted
pool behind an idle one.
A credential-less adapter left in the dock selection was silently
reconnected by the next scheduled refresh, undoing the user's explicit
disconnect. Cursor is the first adapter that needs no saved credential,
so the disconnect intent now persists: the provider leaves the stored
selection without latching manual-selection mode.
SwiftUI onHover rides on NSTrackingAreas that only deliver while the
app is active. The dock lives in a nonactivating panel inside an
accessory app, so the first click into another app killed every hover
interaction until relaunch.
Hover enter/exit is now synthesized in CapacityDockController from the
existing local and global mouse monitors, which observe the pointer
regardless of activation. Provider rows are resolved by inverting the
layoutDetail row-midpoint math (CapacityDockPlacement.providerRowIndex,
unit-tested), and the SwiftUI onHover plumbing is removed so the
controller is the single hover source. Row exits no longer cancel the
rail's pending collapse, and a drag drop re-derives hover so a
stationary pointer resting on a row still opens its bubble.
The Capacity Dock settings sidebar listed the whole 69-provider catalog, but
only 7 have a live CodeBurn quota adapter; the rest just read 'coming soon'.
Filter the settings provider list to live-adapter providers so users only see
what actually works.
* feat(menubar): add native capacity dock
* fix(menubar): harden dock interaction boundaries
* feat(menubar): polish native Capacity Dock for launch
- Flush top-edge docking via NSHostingView safe-area opt-out; shielding
window level so the rail necks into the physical top like the system notch
- Liquid Glass legibility scrim so labels stay readable on light wallpapers
- Weekly-else-monthly headline window; unified severity colour bands
(green <50 / yellow / orange / red) across rings and detail bars
- Squircle gauge + graphite theme defaults; auto-show connected
subscriptions (up to 5) until the user edits the set, then respect it
- Capacity Dock Settings menu item; re-centre the settings window; info
glyph for the About row
- Trim dead provider-credential override fields down to apiKey only
- Reconcile and extend tests for all of the above
---------
Co-authored-by: iamtoruk <hello@agentseal.org>