Skip to content

Port Upstream Accuracy and Pricing Fixes - #1217

Closed
mstallone wants to merge 113 commits into
robinebers:mainfrom
mstallone:port-upstream-accuracy-fixes
Closed

Port Upstream Accuracy and Pricing Fixes#1217
mstallone wants to merge 113 commits into
robinebers:mainfrom
mstallone:port-upstream-accuracy-fixes

Conversation

@mstallone

Copy link
Copy Markdown

TL;DR

Selective re-implementation of the OpenUsage commits since Runway #111 that still apply: Claude Desktop's account-prefixed token caches, Claude spend tiles without an OAuth login, Grok subagent session ledgers, Codex Business Premium, Cursor's Models/Other Models labels, and new model rates.

What was happening

What this changes

  • Desktop cache selection strips the acct:<user>| prefix, keeps only the signed-in account (from lastKnownAccountUuid), and lets a scoped tombstone suppress the matching legacy V1 alias.
  • Unauthenticated Claude refreshes still scan local logs. Spend tiles render under the existing Not logged in notice when those logs contain usage; an empty machine stays a hard error card.
  • Grok scans every durable updates.jsonl ledger. Prompt-id dedup still drops forked parent replays. summary.json is no longer required to keep a ledger.
  • Codex maps self_serve_business_prolite to Business Premium.
  • Cursor widget IDs are unchanged. Titles/labels become Cursor Models / Other Models to match Cursor's dashboard.
  • Pricing supplement: GPT-6 Astra (OpenAI card, 2× fast), Gemini 3.8 Flash (Cursor table, $3.50 output), Fable 5.1, GLM 5.3, and grok-bot-* → Grok 4.6.

Heads-up

Reviewed and not ported, with reasons:

Gemini 3.8 Flash output is $3.50, from Cursor's table, not upstream's $3.75 Google API rate. That matches how Runway priced Gemini 3.7.

Tests

  • Desktop: prefixed key for the signed-in account wins; foreign acct: keys are ignored; a scoped V2 tombstone suppresses the V1 alias; load() reads lastKnownAccountUuid.
  • Claude: no credentials plus local logs → spend tiles and Not logged in, not an error card. Empty machine still errors.
  • Grok: subagent ledger is included; fork replay of a shared prompt still counts once.
  • Codex: self_serve_business_prolite → Business Premium, weekly-only window.
  • Pricing: Astra / 3.8 Flash / Fable 5.1 / GLM 5.3 / grok-bot slugs and router labels resolve. testEveryAliasCanonicalResolves covers the new rules.
  • Cursor mapper tests updated to the new labels; widget IDs unchanged.

swift test --filter "ClaudeDesktopAuthStoreTests|ClaudeProviderTests|CursorProviderTests|CursorUsageSummaryTests|GrokLogUsageScannerTests|CodexUsageMapperTests|PricingBundledResourceTests|LayoutStoreTests" — 179 tests, 1 skipped, 0 failures.

mstallone added 30 commits July 27, 2026 12:10
Collapse provider details when the popover closes
* ci: move releases to NextByte infrastructure

* fix: make first release retries recoverable

* docs: finish release ownership migration
* Improve popover UI performance

* Keep popover chrome fixed during navigation
* Improve spend legend name display

* Show complete expanded legend names

* Handle extreme legend content widths

* Wrap only names wider than the legend

* Account for legend spacing overflow
* Fix first-launch account discovery race

* Bound subprocess pipe draining timeout

* Clean up timed-out subprocess resources

* Preserve subprocess group across fast exit

* Restore detached descendant timeout cleanup

* Clarify detached process ownership

* Revalidate descendants before timeout signals

* Make subprocess timeout cleanup ownership-safe

* Validate subprocess descendant parentage

* Remove unsafe test PID cleanup

* Resume interrupted first-run seeding
* Include emails in multi-account provider names

* Remove overview hover animation

* Stabilize total spend legend hover

* Preserve prices in legend hover

* Scroll overlong legend labels

* Clip legend marquee to row

* Drop redundant unscoped account runtimes

* Preserve ambient spend and reduce-motion labels

* Disambiguate identityless account sources

* Ignore inactive default account records

* Preserve account aliases and saved order

* Handle scoped-only account edge cases

* Route bare peer history without a local runtime

* Preserve unattributed synced spend

* Validate Claude credential files before attribution

* Validate account attribution and remote spend

* Reject stale account cache for ambient Claude
* Improve multi-account menu bar stats

* Preserve account pins across temporary absence
* Make panel height morphs a smooth in-window reveal

The popover window now opens once per session at the screen-clamped
maximum height and never resizes while open. The visible panel is a
height-framed, corner-clipped SwiftUI card pinned to the window top;
expanding a provider card (or switching screens) animates that frame on
SwiftUI's clock while the AppKit backdrop and shadow follow the same
values through a display-link-paced bridge. Per-frame NSWindow.setFrame
calls — each a synchronous window-server commit — were what made the
old growth stutter.

Also:
- Row reorder frames move to a reference box (ReorderFrameStore) so
  per-frame geometry churn during morphs no longer re-renders the whole
  list, and drag hit-testing reads live frames.
- The height bridge drops duplicate pushes and the display link pauses
  itself when a morph goes quiet, so an idle popover costs no timer
  wakeups and doesn't pin ProMotion.
- Outside-click dismissal hit-tests the visible panel rect instead of
  the (now taller) window frame.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Derive display-link rate from the anchored screen

Hardcoding a 120 Hz ceiling made the backdrop pacing tick below the
display cadence on higher-rate external displays. Also document the
verified window-server click-through behavior for the fixed window's
transparent region.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Let the backdrop follow spring overshoot past clamped targets

Targets (and the opening guess) are already clamped before they
animate, but the per-frame follower re-clamped interpolated values, so
a spring undershooting a target that sits exactly on the 200pt minimum
pinned the backdrop at the boundary while the SwiftUI panel dipped past
it, splitting the two bottom edges for the tail of the animation. The
follower now mirrors the raw rendered value; clamping stays at target
selection.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Co-animate the panel height with the caret toggle

The height retarget used to wait for the content measurement (~2
frames after the caret's row change), so the panel edge and the footer
started moving on a second, phase-shifted spring — the footer visibly
trailed the unfolding rows. The caret now retargets the height inside
its own withAnimation via a remembered per-provider expanded-section
delta (first toggle uses a row-count estimate; the measurement that
follows corrects it and is remembered exactly), so rows, panel edge,
and footer ride one spring clock from the first frame.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Remove leftover environment-key file

The caret co-animation went through the MenuBarPopover seam instead;
this environment key was unused.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Position the backdrop with direct frames, not Auto Layout

The per-frame constraint-constant update for the backdrop's height ran
the window's whole layout engine — the same engine holding the hosting
view's edge pins — which re-entered the SwiftUI host every frame and
knocked morph animation sampling off its vsync cadence (measured:
11-21ms/3-5ms alternating tick gaps, seen as bottom-edge and footer
jitter). Setting the backdrop's frame directly dirties only its own
subtree; ticks lock back to the display's 8.3ms cadence with even
steps.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Apply backdrop heights synchronously with the SwiftUI frame

Main-thread pushes from the height modifier now invoke the apply
directly — a constraint-free frame set plus a shadow invalidation,
nothing that re-enters layout — so the backdrop commits in the same
transaction as the SwiftUI frame it matches. Any deferred apply (the
display link, or a main-queue hop) landed 0-or-1 frames behind, and
during a shrink a late backdrop poked out below the footer as a
trailing tray strip that read as the footer lagging the bottom edge.

The display link, its idle pause, and the bridge's paced mode are all
deleted; the coalesced main-queue hop remains only as an off-main
safety net, and the bridge tests now cover the synchronous contract.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Track the visual height while the panel is hidden

Closing collapses every expanded card, but the collapsed re-measure
lands after the panel is ordered out — and the visibility guard in the
apply path skipped it while also poisoning the bridge's duplicate
filter, so a reopen kept the previous session's expanded height (stale
backdrop and remembered height). Hidden-time applies are now accepted:
they're plain backdrop bookkeeping (the window never resizes), they
keep the remembered height honest, and the reopen seed applies before
the first paint.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Address code review: clip drag preview, supersede queued heights, docs

- The reorder drag chip now renders inside the visual panel's rounded
  clip, so a drag past a short panel's bottom edge clips at the edge
  (as the old window bounds did) instead of floating into the fixed
  window's transparent remainder.
- A synchronous main-thread height apply clears any height still
  queued by the off-main safety net, so a stale older value can never
  land after a newer one (regression test added).
- The model-hover research doc no longer describes the removed
  dynamic-height window contract.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Decide each height push in a single critical section

The duplicate check and the off-main queueing ran under two separate
lock acquisitions, so a preempted off-main push could queue its older
height after a newer main-thread push had already applied and cleared
the slot. The whole decision — duplicate drop, sync-apply supersede,
off-main scheduling — now happens under one lock, making that
interleaving impossible by construction.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg

* Key learned expansion heights by section composition

A learned expanded-section delta survived Customize changes (moving a
metric across the caret, hiding one, links changes), so the first
toggle afterwards retargeted the panel by a stale amount and the
measurement correction reintroduced a small footer catch-up. Deltas
are now cached under a key derived from the provider's ordered On
Demand metric IDs and quick-links presence — order included, since
adjacent text rows condense — so a composition change misses the cache
and falls back to the estimate + re-learn path instead of using a
stale height.

Claude-Session: https://claude.ai/code/session_01Crq3aP2viXSmWtCgtkFneg
…#21)

* Compact footer: gear menu, short countdown, structurally pinned bar

The footer collapses to one compact 40pt line: app version on the left, a
short next-update countdown (like "5m", click or Cmd-R to refresh, mini
spinner while updating) and a gear menu button on the right replacing the
"Options" capsule. The bar is now fixed-height chrome rendered as a
bottom-aligned overlay on the animated panel frame, so it stays glued to
the panel's bottom edge through every height morph by construction; a
clear spacer in the scroll view's safe area keeps the content inset and
the native bottom scroll-edge blur. PanelHeightCoordinator takes the
footer height as an init constant (like the top bar) instead of
measuring it.

Claude-Session: https://claude.ai/code/session_01SWYFDiqdQ8gRGJTunKjcPP

* Hold the footer countdown perfectly still

The per-second tick rolled its digits (numericText) and narrowed the
label as digits dropped, nudging the refresh glyph sideways — the one
thing left moving in the pinned footer. The text now swaps in place with
no transition, in a slot reserved for the widest value ("59s").

Claude-Session: https://claude.ai/code/session_01SWYFDiqdQ8gRGJTunKjcPP

* Keep the footer riding the panel edge through two-phase morphs

Rendering the footer as a bottom-aligned overlay on the animated height
frame made its position animate as its own attribute: when a content
change landed in one transaction and the height retargeted in a second
(a provider appearing, a caret estimate correction), the overlay's
spring ran phase-shifted from the frame growth and the footer visibly
trailed the moving edge, sliding over content to catch up. The footer
now lives back in the safe-area bar, whose position is re-derived from
the per-frame layout of the animated frame — verified frame-by-frame to
hug the bottom edge through both co-animated and measurement-driven
morphs. The compact fixed-height design is unchanged.
Includes the release-time pricing snapshot refresh.
The refresh countdown brightens from secondary to primary on hover, and
the gear gets a subtle fill inside its glass circle. Both reset when the
popover closes or the control unmounts, so a screen switch or reopen
can't strand a stale highlight.

Claude-Session: https://claude.ai/code/session_01Lfvn6KcURg32FgptPoHcWd
…le error card (#24)

* Suppress Keychain UI on automatic refreshes and add an empty-state error card

Background and launch refreshes read classic login-keychain items (Claude
Code credentials, Chromium Safe Storage keys) guarded only by
LAContext.interactionNotAllowed, which the classic ACL confirmation dialog
ignores - so macOS could pop password prompts during automatic refreshes
and block them behind the dialog. A process-wide KeychainUISuppression
gate built on SecKeychainSetUserInteractionAllowed now keeps every
non-interactive Security.framework operation prompt-free (a protected item
fails fast into the friendly Permission Required state), while interactive
reads from a manual Refresh hold the gate so a racing background read
cannot disable UI beneath an open approval dialog.

Providers whose refresh failed with no displayable data now replace their
empty "No data" rows with an actionable error card (reason plus a Refresh
button - the one action allowed to show a Keychain prompt), keeping quick
links behind the caret, mirrored in the drag preview and share export.

Claude-Session: https://claude.ai/code/session_01Ur7PxLJ1avetsk3JegAWVY

* Fail non-interactive keychain operations when UI suppression cannot engage

SecKeychainSetUserInteractionAllowed's return status was ignored: had the
disable call ever failed, the suppression scope would have run its
protected Security.framework call with interaction still enabled - able to
show the exact background dialog the gate exists to prevent. The scope now
reports whether suppression actually engaged; when it did not (logged
loudly), non-interactive bodies skip the prompt-capable call and report
their unavailable outcome instead, and a failed restore is retried before
the next interactive operation so approval prompts cannot stay broken.

Claude-Session: https://claude.ai/code/session_01Ur7PxLJ1avetsk3JegAWVY
…ames (#25)

The refresh-error triangle now sits at the header's right edge in the
copy control's slot, the copy button overlays a reserved gutter so its
hover reveal never reflows the title, long provider/account names
marquee to their ending on hover (shared with the Total Spend legend's
machinery, now extracted to HoverMarqueeText), and the Outdated tag
keeps its full width instead of rendering glyph slivers.

Claude-Session: https://claude.ai/code/session_017bkz3Au6LY2mPBquPJWFYT
The fork is charting its own direction; the remaining unshipped phases
(Cowork-as-card, cswap) are not planned work here. Shipped behavior from
the executed phases is documented in docs/providers/claude.md and
docs/dashboard.md.

Claude-Session: https://claude.ai/code/session_01Mg9MQuFQjWPwW7vpaAC8Vc
mstallone and others added 17 commits August 4, 2026 14:54
## TL;DR

Records the approved v0.8.9 release notes before the release tag is
created.

## What was happening

- The two changes merged after v0.8.8 were not yet represented in the
changelog.

## What this changes

- Adds the approved v0.8.9 Bug Fixes section.
- Adds PR, author, commit, and full-comparison links for the release
range.

## Tests

- `swift test` — 1,736 XCTest cases passed, 4 skipped; 6 Swift Testing
cases passed.
- `git diff --check`
**TL;DR** — Move the TestFlight jobs into a dedicated reusable workflow
and make that workflow, rather than the shared Mac release workflow, the
iOS gate boundary. Unrelated Mac release and update-feed edits no longer
cause TestFlight uploads.

**What was happening**

- The gate treated any change to `.github/workflows/release.yml` as
iOS-relevant.
- That file also owns the Mac release, appcast, website, and update-feed
work, so unrelated edits such as the v0.8.7 landing-page change caused a
fresh TestFlight archive, upload, and Beta App Review submission.
- The gate did correctly skip v0.8.8, showing that the baseline logic
worked; the workflow path was simply too broad.

**What this changes**

- Extracts the existing iOS Gate, iOS TestFlight, and TestFlight
External jobs into `.github/workflows/release-ios.yml`, called in
parallel with the Mac release.
- Tracks that dedicated workflow and the TestFlight scripts as
iOS-relevant while excluding the general `release.yml`.
- Moves path classification into a small tested module and handles moves
out of `ios/` as relevant deletions.
- Adds the Node regression tests to CI and updates the release
documentation.

**Heads-up**

- The first release after this merges intentionally ships TestFlight
once because the TestFlight workflow itself changed. Later Mac-only
`release.yml` edits skip it.
- Genuine iOS packaging changes still ship: replaying history classifies
v0.8.9 as relevant because `script/release_ios.sh` changed.

**Tests**

- `swift test` — 1,736 passed, 4 skipped, 0 failed.
- `node --test script/lib/testflight_paths.test.mjs` — 3 passed.
- `actionlint` v1.7.12 on `ci.yml`, `release.yml`, and
`release-ios.yml`.
- Historical classifier replay: v0.8.7 skip, v0.8.8 skip, v0.8.9 ship.
- YAML parse and `git diff --check` pass.
## TL;DR

Remove Runway's deprecated, process-global Keychain UI suppression
switch. Automatic credential discovery is limited to noninteractive
metadata and local caches, while any potentially prompting Keychain
secret read happens only after an explicit user action.

## What was happening

- Runway called `SecKeychainSetUserInteractionAllowed(false)` around
synchronous `SecItemCopyMatching` reads, then restored the setting after
the read returned.
- Because the switch is process-global and the read can block
indefinitely, a wedged call could keep interaction disabled longer than
intended.
- Startup, periodic refresh, CLI refresh, and provider detection could
reach credential paths that read classic login-keychain secret data.
- A macOS 26.6 probe confirmed that `LAContext.interactionNotAllowed`
does not reliably suppress classic ACL prompts when secret data is
requested.

## What this changes

- Delete the deprecated global interaction switch and its suppression
wrapper.
- Split Keychain access into metadata/fingerprint probes and secret-data
reads.
- Attach a per-query `LAContext` with `interactionNotAllowed` to
automatic metadata-only queries.
- Restrict secret reads to explicit GUI refresh/recovery actions and
serialize them.
- Reuse a user-approved value for the running app session while the
item's metadata fingerprint remains unchanged, so scheduled refreshes
continue without another prompt or secret read.
- Stabilize newly modified Keychain metadata before the approved read,
avoiding stale process-lifetime caching when secret-only updates share
Keychain's one-second modification timestamp.
- Keep breaker retries, same-refresh deduplication, and no-fingerprint
fallback behavior bounded.
- Move hidden Codex keyring-home identity binding to manual Refresh All
instead of launch, CLI, or periodic refresh paths.
- Store Runway's iCloud device identity in a protected local Application
Support file, with one-time migration and explicit recovery for legacy
Keychain identities.
- Update provider errors, settings UI, architecture/debugging/provider
docs, and regression coverage for the new behavior.

## Heads-up

- The modern noninteractive context is intentionally not treated as a
reliable suppression mechanism for classic login-keychain secret ACL
reads; those reads remain manual.
- Existing legacy iCloud identities migrate automatically when possible.
If migration needs Keychain approval, Settings offers an explicit
Recover Identity action.

## Tests

- `swift test`: 1,745 XCTest cases passed, 4 skipped, 0 failures; 6
Swift Testing cases passed.
- Focused Keychain coordinator/accessor suite: 24 passed, 0 failures.
- Real macOS 26.6 probe confirmed Keychain modification dates collide
within the same second; regression coverage verifies the stabilization
barrier.
- `./script/build_and_run.sh verify`: signed release build verified and
relaunched.
- Local Codex review loop: clean after the lifetime and collision fixes.
- `git diff --check`: clean.
- Confirmed `SecKeychainSetUserInteractionAllowed` is absent from source
and the built Mach-O imports.
…99)

**TL;DR** — Ends the recurring keychain prompts for good: one Always
Allow per login, ever. Approved credentials now load silently on every
launch and refresh (no per-session Connect click), survive other apps
resetting the item's sharing rules, and expired Claude tokens renew
themselves under strict single-chain guards.

## What was happening

- Ad-hoc-signed dev builds (bare `swift build` output) could raise
approval dialogs whose Always Allow died with the next rebuild — the
source of the "prompts on every rebuild" storms, and of dead cdhash
entries accumulating in item ACLs.
- Every app relaunch parked keychain-backed providers on the Connect
state until a manual refresh, because automatic paths were
metadata-only: `LAContext.interactionNotAllowed` provably fails to
suppress classic ACL dialogs, so no background secret read was safe.
- A recorded permission denial silently softened to the neutral Connect
state after the 15-minute breaker revalidation, and a cancelled read
erased it entirely.
- Credential writers reset their keychain item's partition list on
rotation (observed live with Claude Code), stranding Runway on
`errSecAuthFailed` with intact ACL approvals — recoverable only by
typing the keychain password.
- Expired Claude logins (e.g. an idle second profile) flatlined to
"login needs renewal" until the user manually used that profile, because
Runway was strictly read-only after the refresh-token-reuse incidents
(OpenAI robinebers#516; CodexBar's Claude desync robinebers#1161).

## What this changes

- `InteractiveKeychainReadGate` refuses approval dialogs from
ad-hoc-signed builds (neutral Connect state + loud log naming the fix) —
an approval that can't outlive a rebuild is never requested.
- Background reads run one secret read inside a "quiet turn" with the
process-global UI switch off
(`SecKeychainSetUserInteractionAllowed(false)` — deprecated but still
Apple's documented answer for foreign-item reads, and empirically the
only mechanism that suppresses classic ACL dialogs). Approved logins
load silently; a would-prompt read falls to the neutral deferral.
Dialog-capable reads bail out behind a wedged quiet holder instead of
hanging.
- Denials are durable: they survive unattended revalidation of an
unchanged item and cancelled reads store no evidence; only a successful
interactive read or a proven item change clears them.
- `PartitionWallFallbackReader` recovers from partition-list resets by
reading through `/usr/bin/security` — only after proving from the item's
own ACL metadata that the helper is silently authorized, so it can never
prompt.
- `ClaudeTokenRenewal` renews an expired Claude token reactively
(expired ≥10 min, past Claude Code's own proactive-renewal horizon),
verifies a write path before consuming the refresh token, adopts a
concurrent writer's fresher chain instead of racing it, and writes the
rotated blob back to the exact store it came from — through the security
helper only, with the secret over stdin. `invalid_grant` is terminal
with backoff. Kill switch: `runway.claude.disableTokenRefresh`.
- Foreign keychain items are never written in-process: securityd
rewrites the partition list to the writer's partition on every update,
which locks the owning app's own tooling out (learned live, both
directions).

## Heads-up

- Two audited exemptions to the no-`security`-CLI rule (enforced by the
reshaped containment tests): the partition-wall fallback reader and the
renewal write-back, both gated on proven-silent authorization.
- The `security` CLI usage and the UI-switch call are each confined to
one file; `SecurityCLIUsageTests` fails on any new call site.
- Claude Desktop and environment credentials remain strictly read-only;
the legacy service-wide item is never written (unknown account).
- Docs updated: `refreshing.md`, `dashboard.md`, `debugging.md`,
`providers/claude.md`.

## Tests

Full suite passes (unit coverage for the gate, quiet reads, denial
persistence, partition fallback proof-before-launch, renewal
guards/CAS/write-back). Verified live on a real machine: cold launch
loads all 8 providers with zero clicks and zero dialogs; a
partition-reset item recovered silently; a day-expired second-profile
token renewed and wrote back on the first eligible cycle.

https://claude.ai/code/session_01RbrQY7hdj5JeZdk5cNiMcY
**TL;DR** — Records the v0.8.10 changelog ahead of tagging the release.

## What was happening

- The v0.8.10 release is ready to cut (keychain overhaul #99, plus #98
and #97), and the repo records each release's notes in CHANGELOG.md via
PR before the tag.

## What this changes

- Prepends the approved v0.8.10 section to CHANGELOG.md. No code
changes.

## Heads-up

- The v0.8.10 tag goes on this PR's squash-merge commit; the release
workflow then builds, signs, notarizes, and publishes.

https://claude.ai/code/session_01RbrQY7hdj5JeZdk5cNiMcY
**TL;DR** — The Key Limit meter now charges the current limit window's
spend against the key's cap, instead of the key's lifetime spend. Port
of upstream openusage robinebers#1109.

**What was happening**
- The `/key` endpoint reports `usage` (lifetime spend on the key) and
`limit_remaining` (what is left in the configured limit window).
- The Key Limit meter used lifetime `usage` against the cap, so once a
key had ever spent more than one window's cap, the meter pinned at or
past 100% even right after a daily/weekly/monthly reset.

**What this changes**
- `OpenRouterUsageMapper.keyMetrics` computes used as `max(0, limit −
limit_remaining)`, so the meter tracks the current window.
- Regression test: lifetime `usage` of 12 with `limit` 5 /
`limit_remaining` 3 now maps to 2 used of 5.
- `docs/providers/openrouter.md` describes the window semantics.

**Heads-up**
- A key whose payload has `limit` but no `limit_remaining` now shows as
fully used rather than at lifetime spend; this matches upstream's tested
behavior, and OpenRouter always returns `limit_remaining` alongside a
configured `limit`.

**Tests**
- `swift test --filter OpenRouter` — 30 tests, 0 failures.
**TL;DR** — Z.ai renamed its percentage quota type from `TOKENS_LIMIT`
to `CREDIT_LIMIT`; the mapper now accepts both, so current plans get
their Session and Weekly meters back. Port of upstream openusage robinebers#1105.

**What was happening**
- The quota mapper only matched `limits` entries typed `TOKENS_LIMIT`.
- Current Z.ai responses (e.g. GLM Coding Lite, captured 2026-08-13)
type the same percentage windows as `CREDIT_LIMIT`, so users on current
plans saw no Session/Weekly meters at all.

**What this changes**
- `ZAIUsageMapper` filters percentage quota entries by `CREDIT_LIMIT` or
legacy `TOKENS_LIMIT` (checking both the `type` and `name` fields, as
before).
- Adds a live-capture regression test mapping a `CREDIT_LIMIT` response
to the Session and Weekly meters, including an entry with no
`nextResetTime` on the active window.
- `docs/providers/zai.md` describes the renamed quota type.

**Tests**
- `swift test --filter ZAI` — all mapper, provider, and live-response
suites pass, including the new
`testMapsCurrentCreditLimitResponseToSessionAndWeekly`.
**TL;DR** — Routine dependency bump of Sparkle to 2.9.5, matching
upstream openusage robinebers#1082.

**What was happening**
- `Package.resolved` pinned Sparkle 2.9.4; 2.9.5 is out (bug-fix
release).

**What this changes**
- `swift package update Sparkle` — the pin moves to 2.9.5.
`Package.swift` already allows it (`from: "2.9.4"`). The `originHash`
was recomputed by the toolchain as a side effect.

**Tests**
- `swift build` succeeds against the new pin.
**TL;DR** — The pricing store now compares `updated_at` and serves
whichever of the on-disk cached supplement and the bundled supplement is
newer, instead of always preferring the cache. Port of upstream
openusage robinebers#1089.

**What was happening**
- `ModelPricingStore.loadSupplement` served the cached supplement
whenever one decoded, falling back to the bundled file only when the
cache was missing or unreadable.
- After an app update that ships a newer bundled supplement, the stale
cache from the previous version still won — for up to an hour until the
feed refetch, and forever for anyone who can't reach the feed.

**What this changes**
- `loadSupplement` decodes both copies and picks the newer by
`updated_at` (lexicographic on the zero-padded ISO date). Bundled wins
only when strictly newer, so the usual feed-ahead-of-shipped case keeps
serving the cache.
- A missing `updated_at` counts as oldest — an undated file never
displaces a dated one.
- Three regression tests cover newer-bundled-beats-older-cache,
newer-cache-beats-older-bundled, and the undated cases;
`docs/pricing.md` documents the behavior.

**Tests**
- `swift test --filter Pricing` — 56 tests, 0 failures (includes the 3
new ModelPricingStore tests).
**TL;DR** — Syncs the pricing supplement with Cursor's current published
pricing and the alias fixes upstream openusage shipped in
robinebers#1101/robinebers#1103/robinebers#1093/robinebers#1087: prices Grok 4.6, corrects Grok 4.5 Fast output,
and adds Kimi K3, Daybreak Blue, dashed-slug, bare-composer, and Cursor
Router label aliases.

**What was happening**
- The supplement was last synced 2026-08-02. Since then Cursor shipped
Grok 4.6, Kimi K3, and Router rows, and corrected Grok 4.5 Fast — so
those models showed the unpriced-model warning triangle or priced wrong.

**What this changes** (source: [Cursor models &
pricing](https://cursor.com/docs/models-and-pricing.md), cross-checked
against upstream openusage robinebers#1101/robinebers#1103/robinebers#1093/robinebers#1087)
- **`grok-4.6`**: $2 input / $2 cache write (not published separately,
defaults to input) / $0.50 cache read / $6 output. **`grok-4.6-fast`**:
$4 / $4 / $1 / $12. Cursor lists a 50% one-week launch discount from
2026-08-12; these are the table rates as of 2026-08-13.
- **`grok-4.5-fast` output corrected $18 → $12** to match the current
table.
- **Grok slug aliases** for 4.5/4.6 accept dashed versions
(`grok-4-6-xhigh`) via `[.-]`, matching real Cursor CSV slugs (upstream
robinebers#1103).
- **Kimi K3**: no supplement entry — models.dev's `moonshot/kimi-k3`
already matches Cursor's table ($3 / $0.3 cache read / $15), so the
existing alias is broadened to cover `-code` and effort suffixes. This
deliberately diverges from upstream robinebers#1087, which duplicated the rates
because its catalogs lacked the model.
- **`daybreak-blue-latest`** (and `gpt-` prefixed form) aliases to
`gpt-5.6-sol`, per OpenAI's Daybreak docs (upstream robinebers#1093).
- **Bare `composer`** slug aliases to `composer-2.5` (upstream robinebers#1087).
- **Cursor Router prose labels** ("Opus 5 (Auto Balanced)", "Kimi K3
(Auto Cost)", …) get case-insensitive alias rules to their routed models
— 20 rules covering the Claude, Composer, Grok, GPT, Gemini, GLM, and
Kimi rows (upstream robinebers#1087).
- `updated_at` bumped to 2026-08-13.

**Heads-up**
- On merge the supplement publishes to `update-feed`, so installed apps
pick this up within about an hour — no release needed. I'll verify the
publish workflow after merge.

**Tests**
- CI's supplement validator passes locally.
- `swift test --filter "ModelPricing|PricingBundledResource"` — 57
tests, 0 failures, including new suites for Grok 4.6, Kimi K3, bare
composer, Daybreak Blue, and all 20 Router labels.
**TL;DR** — The OpenCode Session / Weekly / Monthly meters now come from
OpenCode's official usage endpoint (`GET
https://opencode.ai/zen/go/v1/usage`, authorized with the local
`opencode-go` key) instead of being reconstructed from local SQLite logs
with hardcoded dollar caps and window math. Port of upstream openusage
robinebers#1097.

**What was happening**
- The Go plan meters were estimated locally: `OpenCodeGoWindows`
reimplemented the rolling 5-hour session, ISO-week, and anchored-month
windows over this machine's logs, bounded by hardcoded dollar caps.
- That undercounted account usage from other machines, drifted from
OpenCode's own accounting, and broke whenever OpenCode changed plan
caps.

**What this changes**
- New `OpenCodeUsageClient` calls the official usage API; the mapper
turns `{ usage: { rolling, weekly, monthly } }` into three percent
meters with reset countdowns — the same numbers the OpenCode dashboard
shows, account-wide.
- The provider distinguishes a rejected key (401), a valid key with no
Go subscription (403 `EntitlementError` → meters hidden, spend tiles
stay), and transport errors. If the local database is unreadable but the
API works, the meters still render and the failure is logged.
- `OpenCodeGoWindows` (147 lines of window math) and its tests are
deleted; the scanner now only feeds the spend tiles and trend from local
logs.
- Metric IDs (`opencode.session/weekly/monthly`) are unchanged, so
layout customizations, pins, and defaults carry over; the widgets switch
from bounded-dollar meters to percent meters. Docs updated
(`docs/providers/opencode.md`, `docs/local-http-api.md`).

**Heads-up**
- Runway-specific adaptations from upstream: no telemetry
`ErrorCategory` (Runway removed PostHog), and the scanner keeps Runway's
`DayKeyCache` day-key memoization.
- The local HTTP API's `session`/`weekly`/`monthly` units change from
`usd` (estimated) to `percent` — anything scripted against those units
will see the new unit field.

**Tests**
- `swift test` — full suite, 1772 tests, 0 failures (42 OpenCode tests,
ported from upstream with the Runway adaptations above).
**TL;DR** — Org-managed Copilot Business/Enterprise seats now show the
user's own `credits_used` as a personal Credits count, and the
org-billing lookup appends to those lines instead of replacing them.
Re-implementation of upstream openusage robinebers#1108 (issue robinebers#1094) on Runway's
diverged Copilot provider.

**What was happening**
- GitHub's token-based-billing placeholder response has a
zero-entitlement premium bucket, so the mapper returned no personal
lines at all — but the same bucket can carry a real `credits_used` count
(the user's own per-seat consumption, upstream issue robinebers#1094).
- The provider then *replaced* the mapped lines with the org-billing
outcome, so even if the mapper had produced a personal line, an org
lookup (or the managed-account badge) would have wiped it.
- A plain org member (no billing access) ended up with just the
managed-account badge and no usage at all, despite GitHub reporting
their own consumption.

**What this changes**
- The mapper emits a personal **Credits** count (unbounded — there's no
allotment to divide by) on org-managed seats when `credits_used` is a
real positive number; zero or missing stays "No data", and the
placeholder's `overage_permitted` still never produces an Extra Usage
row (issue robinebers#839 stays fixed).
- The provider appends org-billing lines (Org Credits / Org Spend, or
the managed-account badge) to the mapped lines instead of overwriting
them, so members see their own Credits beside the badge and admins see
both personal and org-wide numbers.
- `applicableMetricIDs` on org-managed seats now derives from the lines
present (including `copilot.premium` for the personal count) instead of
hard-coding the badge-only set.
- The `premiumCredits` export declares `unit: "credits"` with a
`.progressOrValue(kind: .count)` source, so the local HTTP API serves
the paid-plan percent meter and the org-seat personal count from the
same resource.
- Docs updated (`docs/providers/copilot.md`, `docs/local-http-api.md`).

**Heads-up**
- Runway divergence from upstream: Runway keeps its managed-account
badge and keychain-error handling in the org path — a keychain problem
still fails the refresh loudly rather than silently showing partial
data; upstream has no badge state.
- No new metric IDs; layout/pin defaults are untouched (the personal
count renders through the existing `copilot.premium` widget).

**Tests**
- `swift test` — full suite, 1776 tests, 0 failures. New coverage:
mapper emits/suppresses the personal count, member-with-403 keeps
Credits + badge, admin keeps Credits + Org Credits/Org Spend, and the
local API exports count vs percent correctly.
**TL;DR** — Prices Gemini 3.7 Flash (Cursor's CSV now emits
`gemini-3.7-flash-high`, which showed the unpriced-model warning),
broadens the Gemini alias rules to accept effort suffixes, and fixes the
Codex long-context (>272k) rate tier for GPT-5.6 Terra/Luna, which still
used the pre-reprice base rates. Selective port of upstream openusage
robinebers#1112.

**What was happening**
- `gemini-3.7-flash` was in no pricing source and had no alias rule, so
its spend showed the warning triangle and was excluded from the tiles.
- None of the Gemini alias rules accepted Cursor's effort suffixes
(`-low/-medium/-high/-xhigh`), so slugs like `gemini-3.6-flash-high`
didn't resolve either.
- The Codex long-context tier for GPT-5.6 Terra/Luna was derived from
the old base rates that were repriced away on 2026-07-30 — long-context
Terra spend was overestimated ~25% and Luna ~5×.

**What this changes**
- Adds a `gemini-3.7-flash` supplement entry: $0.75 input / $0.75 cache
write (unpublished, defaults to input) / $0.075 cache read / $3.50
output, per Cursor's published table (the repo rule: the Cursor page is
the source of truth for CSV pricing). Google and LiteLLM publish $3.75;
upstream robinebers#1112 went with that instead — the entry's comment records the
discrepancy for easy revisiting.
- Broadens every Gemini alias rule with
`(?:-(?:low|medium|high|xhigh))?`, adds the 3.7 rule, and merges the two
3.1 Pro rules into one.
- Adds Cursor Router rows for "Gemini 3.6 Flash (Auto…)" and "Gemini 3.7
Flash (Auto…)".
- Corrects `CodexLogUsageAggregation` long-context rates: Terra `(5,
22.5, 0.5)` → `(4, 18, 0.4)`, Luna `(2, 9, 0.2)` → `(0.4, 1.8, 0.04)` —
consistent with the current base rates (2× input, 1.5× output, 2× cache
read).
- `updated_at` → 2026-08-16.

**Heads-up**
- Deliberately not ported from robinebers#1112: the GPT-5.6 Terra/Luna supplement
rates and 2.0 fast multipliers (Runway already repriced these on
2026-07-30 — upstream caught up to us), and a `gemini-3.6-flash`
supplement entry (Runway's bundled LiteLLM snapshot already carries it
at the same $1.50 rate, so the alias-to-catalog rule stands).
- On merge, the supplement publishes OTA via `update-feed`, clearing the
Gemini 3.7 warning for installed apps within about an hour. The Codex
tier fix ships with the next release.

**Tests**
- CI's supplement validator passes locally.
- `swift test` — full suite, 1791 tests, 0 failures. New assertions:
`gemini-3.6-flash-high` and `gemini-3.7-flash-high` resolution (input
and output rates), the two new Router rows, and the corrected Terra/Luna
long-context costs.
**TL;DR** — Records the v0.8.11 changelog ahead of tagging the release.

**What was happening**
- Eight PRs (#101#108) merged since v0.8.10 — the upstream openusage
port batch plus the Gemini 3.7 / Codex long-context pricing fix — with
no changelog entry yet.

**What this changes**
- Prepends the owner-approved v0.8.11 section to `CHANGELOG.md`: seven
entries under Bug Fixes, the Sparkle 2.9.5 bump under Chores, and the
full commit list for the v0.8.10...v0.8.11 range.

**Heads-up**
- Once this merges, `v0.8.11` is tagged on the merge commit to start the
release run; the same notes are published onto the GitHub Release.
## TL;DR

Restore Grok CLI spend and token estimates for Grok 1.x by reading its
persisted per-session usage ledger while preserving the legacy
unified-log fallback.

## What was happening

- Runway only scanned the older Grok unified log for inference token
rows.
- Grok 1.x persists completed-turn usage under its sessions directory
instead, so active users could see No data in Today, Yesterday, and Last
30 Days.
- Current Grok CLI model IDs such as grok-4.5-build and grok-4.6-build
did not resolve to the shared pricing catalog.

## What this changes

- Reads measured per-model token buckets from completed Grok 1.x turns
with an incremental persisted JSONL cache.
- Excludes nested subagent sessions already represented by their parent
turn and deduplicates replayed fork history by prompt and model.
- Prefers modern session data on overlapping days while retaining
legacy-only history.
- Adds pricing aliases for the current Grok Build model IDs.
- Keeps cancelled scans from publishing the legacy fallback as
authoritative.
- Updates Grok provider documentation and adds parser, integration,
overlap, pricing, and cancellation regressions.

## Heads-up

- Dollar values remain estimates at public API rates and are separate
from Grok's weekly subscription utilization.
- A plain full-suite run encountered an existing hang in the tampered
provisioning-profile OpenSSL test. The broad suite below excludes only
that test.

## Tests

- swift test --filter Grok
- swift test --filter JSONLScannerCancellationTests
- swift test --skip
ProvisioningProfileScriptTests.testProfileDecoderRejectsATamperedCMSPayload
  - 1,794 XCTest tests plus 6 Swift Testing tests passed
  - 4 normal skips, 0 failures
- Signed release build, relaunch, process check, and live local API
verification against real Grok 1.x session data
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## TL;DR

Selective re-implementation of the new upstream OpenUsage commits that
still apply on Runway: Codex auto-review stays a first-class model in
spend breakdowns and prices as GPT-5.6 Luna from 2026-07-09, and Grok
CLI's `grok-proxy` slug resolves to Grok Build.

## What was happening

- Upstream has moved on since the last port wave (Runway #101#108).
Each new OpenUsage commit was reviewed against Runway's architecture,
existing ports, and the "don't bloat" bar.
- Codex still replaced `codex-auto-review` with its dated GPT fallback
before aggregation (openusage robinebers#1085), so the breakdown showed gpt-5.5
instead of auto-review.
- That fallback table still stopped at gpt-5.5 (openusage robinebers#1125). OpenAI
moved auto-review onto GPT-5.6 Luna on 2026-07-09, so every auto-review
event since July was priced 25× too high.
- Grok CLI now logs `grok-proxy` for Grok Build (openusage robinebers#1123); that
slug had no alias, so those tokens were dropped from the spend tiles.

## What this changes

- Parser keeps the measured `codex-auto-review` slug and stores the
dated GPT fallback on `Event.pricingModel` only for cost. Aggregation
keys the rate cache by both slugs so a day that spans the Luna cutoff
does not reuse the first event's rates.
- Adds `("2026-07-09", "gpt-5.6-luna")` at the top of the auto-review
fallback table (ours; ccusage's snapshot still stops at gpt-5.5). Bumps
the Codex JSONL cache schema to 2 so already-remapped events are
reparsed.
- Aliases `grok-proxy` → `grok-build-0.1` and stamps `updated_at` with a
same-day ISO timestamp so the feed wins over today's date-only cache.
- Documents the auto-review identity/pricing split on the Codex
spend-tiles page.

## Heads-up

Reviewed and **not** ported, with reasons:

- **openusage robinebers#1116 / robinebers#1127** (mandatory daily analytics ping, PostHog
bump) — Runway removed analytics in #9.
- **openusage robinebers#1111 / robinebers#1106** (scroll / reorder / SVG parse) — Runway
already has `ReorderFrameStore`, parsed-once `ProviderMark`, row-local
reorder gestures, and the rebuilt popover scroll path. Taking their
patch would duplicate and fight that work.
- **openusage robinebers#1033** (Reset All Settings) — Runway already has Reset
All Customization; a second Settings-wide reset is extra surface, and
earlier port waves already skipped it.
- **openusage robinebers#1128** (Sparkle 2.9.5 → 2.9.6) — relevant security bump,
but this environment cannot refresh `Package.resolved`'s `originHash`.
Dependabot is already scheduled weekly; worth a dedicated follow-up.
- Pricing/provider commits through robinebers#1112, robinebers#1108, robinebers#1110, robinebers#1109, robinebers#1105,
robinebers#1103, robinebers#1101, robinebers#1097, robinebers#1093, robinebers#1089, robinebers#1087, robinebers#1050, robinebers#1019, robinebers#1082 are
already in `main`.

## Tests

- New parser tests: auto-review lines keep the slug; post-2026-07-09
lines price as Luna; the 2026-07-08/09 boundary stays gpt-5.5.
- New aggregation tests: breakdown shows `codex-auto-review` while
costing the fallback; Luna and gpt-5.5 auto-review events on either side
of the cutoff keep independent rates (guards the rate-cache key).
- `grok-proxy` resolves to the same rates as `grok-build-0.1`.
- This environment is Linux, so `swift test` could not be run here. CI
on `macos-26` is the compile/test gate.
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-28a06551-9894-4f4b-ac34-9d5daf6e62b1?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-28a06551-9894-4f4b-ac34-9d5daf6e62b1&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Re-implement the OpenUsage commits that fix real usage gaps without adding new metrics or fighting Runway's architecture: Desktop account-prefixed caches, Claude spend without OAuth, Grok subagent ledgers, Codex Business Premium, Cursor Models/Other Models labels, and new model rates.
Copilot AI lite review requested due to automatic review settings September 5, 2026 00:44
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Thanks for your interest in contributing to OpenUsage!

External pull requests must reference an open issue that:

  1. Has been approved by a maintainer with the approved label.
  2. Is assigned to the pull request author (@mstallone).

Please discuss the change on an issue first, wait for a maintainer to approve and assign it to you, then reopen this pull request with Fixes #123 in its description.

Read the contribution guidelines.

@mstallone

Copy link
Copy Markdown
Author

Opened against the wrong repository by mistake — this was meant for the Runway fork (mstallone/runway), not OpenUsage. Sorry for the noise.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

script/classify_appcast_bootstrap.sh can fail under set -u when stdin is empty, which breaks the “no releases yet” path it is meant to support.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Ports a large set of upstream accuracy/pricing fixes into the Runway fork while continuing the OpenUsage → Runway rebrand, expanding shared provider/pricing infrastructure, and adding iOS companion app scaffolding + CI compile coverage.

Changes:

  • Rebrands user-facing copy/identifiers (Runway naming, bundle IDs, defaults keys, CLI/env vars) and updates policy/docs/templates accordingly.
  • Introduces/refactors shared infrastructure for pricing, history/snapshots, limits exports, UI surfaces (share cards, pills/popovers), and provider clients/mappers.
  • Adds iOS app + widgets project assets/config and extends CI to compile-check iOS plus gate TestFlight-relevant paths.
File summaries
File Description
TRADEMARK.md Rebrand trademark policy to Runway fork.
SECURITY.md Update vuln reporting links/copy.
CODE_OF_CONDUCT.md Update enforcement contact to repo owner.
.gitignore Ignore additional iOS/Xcode artifacts.
Package.resolved Dependency resolution update (Sparkle, PostHog pin removed).
.github/workflows/ci.yml Pin checkout SHA; add iOS build + node tests.
.github/workflows/stale.yml Remove stale bot workflow.
.github/workflows/labeler.yml Remove PR labeler workflow.
.github/workflows/deploy-pages.yml Remove Pages deploy workflow.
.github/PULL_REQUEST_TEMPLATE.md Update PR policy text; remove screenshots section.
.github/labeler.yml Remove labeler configuration.
.github/ISSUE_TEMPLATE/new_provider.yml Rebrand issue template text.
.github/ISSUE_TEMPLATE/feature_request.yml Rebrand issue template text.
.github/ISSUE_TEMPLATE/config.yml Allow blank issues.
.github/ISSUE_TEMPLATE/bug_report.yml Rebrand bug report labels/help text.
.github/CODEOWNERS Switch default reviewers to fork owner.
.cursor/rules/runway-preview-launch.mdc Rebrand local dev launch workflow guidance.
.codex/environments/environment.toml Rebrand Codex environment metadata.
.agents/skills/signing-entitlements/SKILL.md Update codesigning identity guidance.
.agents/skills/macos-signing-entitlements/SKILL.md Update macOS codesigning identity guidance.
.agents/README.md Document additional skills layout/notes.
docs/proxy.md Rebrand proxy docs + refresh cadence wording.
docs/providers/devin.md Rebrand provider doc copy.
script/update_pricing_snapshots.sh Update resource paths to Runway.
script/validate_release_tag.sh Add stable-release tag validator.
script/stamp_website_version.sh Stamp website mock footer version.
script/normalize_appcast_title.sh Normalize appcast RSS channel title.
script/decode_provisioning_profile.sh Decode provisioning profile via OpenSSL CMS.
script/classify_appcast_bootstrap.sh Classify appcast bootstrap mode from tag history.
script/apply_github_protections.sh Rebrand protections script + repo target.
script/embed_sparkle.sh Rebrand Sparkle embedding commentary.
script/Runway.release.entitlements.plist Add release entitlements for iCloud container.
script/Runway.local.entitlements.plist Add local debug entitlements.
script/Runway.dev.entitlements.plist Update dev entitlements + CloudKit.
script/lib/testflight_paths.mjs Add TestFlight path relevance filter.
script/lib/testflight_paths.test.mjs Add node tests for TestFlight path filter.
ios/RunwayMobile/RunwayMobileApp.swift Add iOS companion app entrypoint.
ios/RunwayMobileWidgets/RunwayWidgetsBundle.swift Add widgets bundle entrypoint.
ios/RunwayMobileWidgets/UsageConfigurationIntent.swift Add widget configuration intent.
ios/RunwayMobile/Assets.xcassets/Contents.json Add iOS asset catalog metadata.
ios/RunwayMobile/Assets.xcassets/AppIcon.appiconset/Contents.json Add iOS app icon catalog metadata.
ios/Config/RunwayMobileWidgets.Release.entitlements Add widgets release entitlements.
ios/Config/RunwayMobileWidgets.Debug.entitlements Add widgets debug entitlements.
ios/Config/RunwayMobileWidgets-Info.plist Add WidgetKit extension Info.plist.
ios/Config/RunwayMobile.Release.entitlements Add iOS app release entitlements.
ios/Config/RunwayMobile.Debug.entitlements Update iOS debug entitlements + CloudKit.
Sources/RunwayApp/RunwayApp.swift New Runway @main app + Settings command override.
Sources/OpenUsageApp/OpenUsageApp.swift Remove OpenUsage @main app entrypoint.
Sources/RunwayCLI/CLIArguments.swift Add CLI argument parsing + error types.
Sources/RunwayCLI/AppBundleLocator.swift Rebrand CLI defaults suite env var + bundle ID.
Sources/OpenUsage/Support/AppInfo.swift Remove OpenUsage version helper.
Sources/Runway/Support/AppInfo.swift Add Runway version helper with “dev” fallback.
Sources/Runway/Support/AboutPanel.swift Rebrand About panel + repo link.
Sources/Runway/Support/AppNotifications.swift Rebrand notification thread/id prefixes.
Sources/Runway/Support/AppShortcuts.swift Update shortcut help text for Settings window.
Sources/Runway/Support/ContainingAppBundle.swift Add helper to find containing .app bundle.
Sources/Runway/Support/LiquidGlassFallbacks.swift Update comments for Settings window panes.
Sources/Runway/Support/MenuBarIcon.swift Use Runway mark + updated icon shape init.
Sources/Runway/Support/MetricPeriod.swift Centralize common period durations (ms).
Sources/Runway/Support/PopoverSurfaceTreatment.swift Add environment value for surface treatment.
Sources/Runway/Services/CommandLineToolInstaller.swift Rebrand helper binary + paths + messages.
Sources/Runway/Services/HTTPClient.swift Rebrand proxy config path mention.
Sources/Runway/Services/LocalUsageServer.swift Rebrand dispatch queue label + doc wording.
Sources/Runway/Services/ProxyConfig.swift Rebrand config path + docs wording.
Sources/Runway/Services/ShellEnvironmentSnapshot.swift Add Kimi env keys + rebrand storage key.
Sources/Runway/Stores/LayoutPersistence.swift Remove expandedProviders persistence hooks.
Sources/Runway/Stores/LaunchAtLoginSetting.swift Improve failure message wording.
Sources/Runway/Stores/NotificationSettingsStore.swift Rebrand notification defaults keys.
Sources/Runway/Stores/OnboardingStore.swift Rebrand onboarding defaults key.
Sources/Runway/Stores/PopoverNavigationStore.swift Remove Settings from popover navigation.
Sources/Runway/Stores/QuotaNotificationEvaluator.swift Add prune API; rebrand ISO formatter usage.
Sources/Runway/Stores/RefreshSetting.swift Add shared fixed refresh cadence constants.
Sources/Runway/Stores/TimeFormatSetting.swift Adjust doc comment wording.
Sources/Runway/Stores/TotalSpendSetting.swift Add setting key for Total Spend card visibility.
Sources/Runway/Models/DashboardLayout.swift Add placed-widget model for dashboard layout.
Sources/Runway/Models/DeviceSnapshotDocument.swift Add CloudKit device snapshot payload model.
Sources/Runway/Models/LimitResourceDescriptor.swift Add /v1/limits export descriptors.
Sources/Runway/Models/MenuBarStyle.swift Add menu bar rendering style enum.
Sources/Runway/Models/MetricKind.swift Adjust doc wording.
Sources/Runway/Models/MetricLine.swift Add Connect badge + connect prompt classification.
Sources/Runway/Models/MetricValue.swift Add labeled value selection mode.
Sources/Runway/Models/Provider.swift Adjust provider links doc wording.
Sources/Runway/Models/ResetDisplayMode.swift Adjust doc wording.
Sources/Runway/Models/UsageHistoryDescriptor.swift Add explicit cross-device history scope metadata.
Sources/Runway/Models/UsageHistoryDocument.swift Rebrand schemas + error text.
Sources/Runway/Models/WidgetDescriptor.swift Add widget descriptor model + metadata fields.
Sources/Runway/Models/WidgetDescriptor+Factories.swift Add subtitle value labels + pinnable toggle.
Sources/Runway/Models/WidgetDisplayMode.swift Adjust doc wording.
Sources/Runway/Pricing/PricingCatalogCodecs.swift Rebrand doc wording.
Sources/Runway/Providers/JSONLScanCacheCoordination.swift Flush Grok persistent cache writes.
Sources/Runway/Providers/ProviderAuthRetry.swift Reword doc comment.
Sources/Runway/Providers/ProviderUsageErrorText.swift Add shared user-facing usage error copy.
Sources/Runway/Providers/SpendTileMapper.swift Use Runway ISO8601 parser for day keys.
Sources/Runway/Providers/UsageLogReadFailureReporter.swift Add edge-triggered unreadable-log reporter actor.
Sources/Runway/Providers/Antigravity/AntigravityErrors.swift Add keychain connect/denial error variants.
Sources/Runway/Providers/Antigravity/AntigravityMetric.swift Centralize Antigravity IDs/labels.
Sources/Runway/Providers/Antigravity/AntigravityUsageClient.swift Update doc wording re: OAuth creds.
Sources/Runway/Providers/Antigravity/AntigravityUsageMapper.swift Use Runway ISO8601 parsing for reset times.
Sources/Runway/Providers/Claude/ClaudeConfigDirDiscovery.swift Switch Keychain protocol type + parse helper.
Sources/Runway/Providers/Claude/ClaudeUsageClient.swift Add Claude usage client + shared error text.
Sources/Runway/Providers/Copilot/CopilotUsageClient.swift Add Copilot internal usage client.
Sources/Runway/Providers/Cursor/CursorCSVParser.swift Tighten CSV parser doc wording.
Sources/Runway/Providers/Cursor/CursorUsageClient.swift Remove refresh-token endpoint path in client.
Sources/Runway/Providers/Cursor/CursorUsageCSV.swift Clarify v1 cost modeling in docs.
Sources/Runway/Providers/Devin/DevinUsageClient.swift Add Devin usage client.
Sources/Runway/Providers/Grok/GrokAuthStore.swift Use Runway ISO8601 parsing.
Sources/Runway/Providers/Grok/GrokCreditsConfigDecoder.swift Use Runway ISO8601 parsing; rebrand docs.
Sources/Runway/Providers/Grok/GrokUsageClient.swift Rebrand User-Agent header.
Sources/Runway/Providers/OpenCode/OpenCodeUsageClient.swift Add OpenCode Go usage client.
Sources/OpenUsage/Providers/OpenCode/OpenCodeUsageMapper.swift Remove OpenUsage OpenCode usage mapper.
Sources/Runway/Providers/OpenRouter/OpenRouterAuthStore.swift Rebrand config path in error + paths.
Sources/Runway/Providers/OpenRouter/OpenRouterUsageMapper.swift Fix Key Limit to use current-window spend.
Sources/Runway/Providers/Pi/PiPaths.swift Add pi session log directory resolver.
Sources/Runway/Providers/ZAI/ZAIAuthStore.swift Rebrand config path in error + paths.
Sources/Runway/App/FirstRunSeeder.swift Rebrand docs.
Sources/Runway/App/SingleInstanceGuard.swift Rebrand docs.
Sources/Runway/App/SingleInstanceLock.swift Rebrand lock directory name + docs.
Sources/Runway/Views/APIKeysSection.swift Use compact density; interactive refresh call.
Sources/Runway/Views/ClosureMenuItem.swift Expand doc comment to additional NSMenu uses.
Sources/Runway/Views/CopyFeedbackButton.swift Add presence callback for layout reservation.
Sources/Runway/Views/CustomizeHintCard.swift Rebrand welcome title.
Sources/Runway/Views/CustomizeRow.swift Use compact density constant.
Sources/Runway/Views/ModelUsageDetail.swift Use compact density constant.
Sources/Runway/Views/PopoverSourceNote.swift Add shared source-note footer view.
Sources/Runway/Views/PopoverTopBar.swift Key top bar off page screen; remove Settings case.
Sources/Runway/Views/ProviderCard.swift Use compact density constant.
Sources/Runway/Views/ProviderListRow.swift Use compact density constant.
Sources/Runway/Views/ShareCardChrome.swift Rebrand watermark icon + tagline.
Sources/Runway/Views/ShareCardView.swift Add exported error-card rendering support.
Sources/Runway/Views/TotalSpendShareCardView.swift Add Total Spend share PNG renderer view.
Sources/Runway/Views/TransientPill.swift Add shared transient pill UI component.
Sources/Runway/Views/UpdateBannerCard.swift Rebrand update banner message.
Sources/Runway/Views/UsageSparkline.swift Use compact density constant; tighten docs.
Sources/Runway/Views/UsageTrendDetail.swift Tighten docs wording.
Sources/Runway/Resources/ProviderIcons/antigravity.svg Add/replace provider icon asset.
Sources/Runway/Resources/ProviderIcons/cursor.svg Add/replace provider icon asset.
Sources/Runway/Resources/ProviderIcons/opencode.svg Add/replace provider icon asset.
Sources/Runway/Resources/ProviderIcons/openrouter.svg Add/replace provider icon asset.
Sources/Runway/Resources/ProviderIcons/runway.svg Add Runway brand icon asset.
Sources/Runway/Resources/ProviderIcons/zai.svg Add/replace provider icon asset.
Tests/RunwayTests/ZAIQuotaValidationTests.swift Update imports + error assertion semantics.
Tests/RunwayTests/WidgetZeroUsageTests.swift Update import to Runway.
Tests/RunwayTests/WidgetUsagePeriodTests.swift Update import to Runway.
Tests/RunwayTests/WidgetRegistryTests.swift Update import + add account-card ordering test.
Tests/RunwayTests/WidgetPercentClampTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/WidgetMeterStyleTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/WidgetDataStorePlanTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/WidgetDataStoreNotificationTests.swift Update import to Runway.
Tests/RunwayTests/UsageTrendTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/UsageReaderTests.swift Update import to Runway.
Tests/RunwayTests/UsageHistoryRefreshTriggerTests.swift Rename callback to onLocalStateChanged + suites.
Tests/RunwayTests/UsageHistoryDocumentTests.swift Rebrand schema strings in tests.
Tests/RunwayTests/UsageHistoryClassificationTests.swift Add Sakana descriptors + expected scopes.
Tests/RunwayTests/UsageHistoryAggregatorTests.swift Update import to Runway.
Tests/RunwayTests/UIProfilerTests.swift Add tests for disabled UI profiler behavior.
Tests/RunwayTests/TransientNoticeTests.swift Update import to Runway.
Tests/RunwayTests/TotalSpendAggregatorTests.swift Update import to Runway.
Tests/RunwayTests/StaleWhileRevalidateTests.swift Rename callback to onLocalStateChanged + suites.
Tests/RunwayTests/StalenessLabelTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/SpendTileMapperTests.swift Update import to Runway.
Tests/RunwayTests/SingleInstanceLockTests.swift Rebrand lock temp paths.
Tests/RunwayTests/SingleInstanceGuardTests.swift Update import to Runway.
Tests/RunwayTests/SettingsPaneTests.swift Add raw-value stability tests.
Tests/RunwayTests/SecretCodeMatcherTests.swift Update import to Runway.
Tests/RunwayTests/SakanaLayoutTests.swift Add default placement regression test.
Tests/RunwayTests/ResetDisplayTests.swift Rebrand docs + add @mainactor.
Tests/RunwayTests/ReorderGeometryTests.swift Update import to Runway.
Tests/RunwayTests/RefreshWakeSignalTests.swift Update import to Runway.
Tests/RunwayTests/RefreshSettingTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/ProxyConfigTests.swift Rebrand proxy config path in docs.
Tests/RunwayTests/ProviderSnapshotCacheTests.swift Add deferred persistPending behavior test.
Tests/RunwayTests/ProviderSectionHeaderTests.swift Add tooltip affordance behavior tests.
Tests/RunwayTests/ProviderParseTests.swift Update import to Runway.
Tests/RunwayTests/ProviderLinksTests.swift Add providers; tighten doc wording.
Tests/RunwayTests/ProviderEnablementStoreTests.swift Rebrand defaults keys + suite names.
Tests/RunwayTests/ProviderEnablementEnforcementTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/ProviderAuthRetryTests.swift Update import to Runway.
Tests/RunwayTests/PopoverTransparencyStyleTests.swift Update import to Runway.
Tests/RunwayTests/PopoverTransparencyStoreTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/PopoverSurfaceOpacityTests.swift Update import to Runway.
Tests/RunwayTests/PopoverScreenTests.swift Remove Settings screen expectations; rebrand suites.
Tests/RunwayTests/PopoverKeyReaderTests.swift Update import to Runway.
Tests/RunwayTests/PiUsageScannerTests.swift Rebrand ISO8601 helper usage + docs.
Tests/RunwayTests/PanelOutsideClickPolicyTests.swift Add visualPanelRect tests.
Tests/RunwayTests/PanelGeometryTests.swift Update import to Runway.
Tests/RunwayTests/PaceNotificationLogicTests.swift Update import to Runway.
Tests/RunwayTests/OpenCodeLayoutTests.swift Update import to Runway.
Tests/RunwayTests/OpenCodeAuthStoreTests.swift Update import to Runway.
Tests/RunwayTests/NewProviderSeederTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/ModelUsageHoverTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/MockData.swift Rebrand fixture docs.
Tests/RunwayTests/MetricFormatterTests.swift Update import to Runway.
Tests/RunwayTests/MeterSeverityTests.swift Update import to Runway.
Tests/RunwayTests/MenuBarStripTrimTests.swift Update import to Runway.
Tests/RunwayTests/MenuBarPrivacyStoreTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/MenuBarBarsTests.swift Tighten doc wording.
Tests/RunwayTests/LogRedactionTests.swift Tighten doc wording.
Tests/RunwayTests/LogLevelSettingTests.swift Tighten docs + rebrand suite names.
Tests/RunwayTests/LocalTextFileAccessorTests.swift Rebrand temp directory name.
Tests/RunwayTests/LayoutBootstrapTests.swift Add shouldPersistPins assertions + suite names.
Tests/RunwayTests/LaunchAtLoginSettingTests.swift Update import to Runway.
Tests/RunwayTests/KimiLayoutTests.swift Add default placement regression test.
Tests/RunwayTests/JSONLScannerTestSupport.swift Add ChunkRecorder helper.
Tests/RunwayTests/ICloudTestSupport.swift Add shared iCloud fixtures + defaults helper.
Tests/RunwayTests/GrokCreditsConfigTests.swift Update import to Runway.
Tests/RunwayTests/GrokCreditsConfigFixtures.swift Update import to Runway.
Tests/RunwayTests/GrokAuthStoreTests.swift Rebrand ISO8601 helper usage.
Tests/RunwayTests/FirstRunSeederTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/FailureBackoffTests.swift Rebrand suite names for defaults.
Tests/RunwayTests/DevinProviderTests.swift Adjust assertions; extend FakeSQLite protocol stub.
Tests/RunwayTests/DensitySettingTests.swift Add tests pinning compact-only density.
Tests/RunwayTests/DashboardClockTests.swift Add tests for dashboard clock start/stop.
Tests/RunwayTests/CursorCSVBoundaryTests.swift Update import to Runway.
Tests/RunwayTests/CommandLineToolInstallerTests.swift Rebrand helper bundle/binary names + symlink text.
Tests/RunwayTests/ClaudeScopedAuthStoreTests.swift Update import to Runway.
Tests/RunwayTests/ClaudeConfigDirDiscoveryTests.swift Update import to Runway.
Tests/RunwayTests/AppNotificationsTests.swift Update import to Runway.
Tests/RunwayTests/AntigravityLayoutTests.swift Rebrand suite names for defaults.
Tests/RunwayCLITests/CLIArgumentsTests.swift Rebrand CLI module import.
Tests/OpenUsageTests/TelemetrySinkTests.swift Remove OpenUsage telemetry sink tests.
Tests/OpenUsageTests/ProvisioningProfileScriptTests.swift Remove OpenUsage provisioning profile script tests.
Tests/OpenUsageTests/ProviderMarksTests.swift Remove OpenUsage provider marks tests.
Tests/OpenUsageTests/ProcessRunnerTests.swift Remove OpenUsage process runner tests.
Tests/OpenUsageTests/OpenUsageISO8601Tests.swift Remove OpenUsage ISO8601 tests.
Tests/OpenUsageTests/KeychainAccessorTests.swift Remove OpenUsage keychain accessor tests.
Review details
  • Files reviewed: 133/680 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +7 to +11
RELEASE_COUNT=0
ONLY_RELEASE_TAG=""

while IFS= read -r release_tag || [ -n "$release_tag" ]; do
[ -n "$release_tag" ] || continue
Comment on lines +32 to 33
/// Locks `Application Support/Runway/<bundle id>.lock` — a directory the app already uses
/// for its own state, and stable no matter where the app bundle itself lives.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants