Skip to content

Grok gRPC fallback, xAI dedupe, iOS canonical cards, dedupe - #306

Merged
jaywedgeworth22 merged 10 commits into
mainfrom
opencode/grok-xai-ios
Oct 24, 2026
Merged

jaywedgeworth22 merged 10 commits into
mainfrom
opencode/grok-xai-ios

Conversation

@jaywedgeworth22

@jaywedgeworth22 jaywedgeworth22 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Popup/plan fixes are in PR #304 (auto-merge armed). Folder-structure standardization is SWU-22; load-bearing paths (SPM/Xcode/release) intentionally unmoved.

Summary

This PR improves Grok quota completeness and consistency across local and fleet readings, adds a secondary Grok billing source, and simplifies the iOS quota experience to show one canonical reading per allowance.

Functional changes

  • Grok quota fallback

    • Adds a best-effort lookup of the Grok subscription tier to fill missing plan names.
    • When the CLI proxy does not provide a usage percentage, attempts a gRPC-web request to Grok’s billing endpoint and parses the returned usage percentage and current period.
    • Converts Grok’s used percentage into remaining percentage and fills missing period bounds without replacing proxy-provided reset information.
    • Supports framed gRPC-web and raw protobuf responses, including the documented implicit-zero case for an active period without an explicit percentage.
    • Leaves the original reading and its missing-percentage issue intact when the fallback cannot produce a valid result.
    • Adds the expected Grok authentication and JSON headers to billing requests.
  • Grok/xAI subscription deduplication

    • Collapses the local grok subscription reading and fleet xai:subscription reading into one dashboard entry.
    • Keeps the reading with the newer occurredAt timestamp and leaves unrelated xAI windows and single-sided subscription data unchanged.
  • Canonical iOS quota cards

    • Removes the source-scope picker from the main quota list.
    • Always renders quota cards using the canonical all-source resolution.
    • Removes source-count badges, source-disagreement warnings, and per-window update timestamps from the primary cards.
    • Keeps per-feed discrepancies and stale-reading details in the Data Sources & Mirrors section.
    • Standardizes the quota-meter layout so captions, percentages, and countdowns remain consistently aligned.
  • Other quota and branding updates

    • Marks Copilot quota windows as monthly.
    • Reuses the Muse artwork for Muse Assist and stops generating separate Muse Assist variants.

Test coverage

  • Added GrokGrpcCreditsTests covering framed and raw replies, product-share exclusion, active-period implicit zero, malformed input, period extraction, and the exact request frame.
  • Added GrokXaiDedupeTests covering fresher local and fleet readings, preservation of unrelated xAI windows, and single-sided subscription data.

Outstanding review items

  • Grok gRPC authentication, transport, and HTTP-status failures are currently swallowed rather than surfaced as provider issues.
  • The protobuf parser may interpret arbitrary length-delimited fields as period timestamps, and an out-of-range wire percentage can be ignored and treated as implicit zero when an active period exists.
  • The enrichment path can issue two additional serialized HTTP requests when no eligible subscription window exists, and the fallback does not recalculate quota status or exhaustion after filling the percentage.
  • Subscription matching is suffix-based, while deduplication does not account for providerGroups; this can select the wrong duplicate or allow a dropped duplicate to be reintroduced.
  • Simulator screenshot evidence for the revised iOS card layout is still missing.
  • The Muse Assist bundle test still expects a dedicated asset even though the branding now reuses Muse artwork.
  • The original task did not provide sufficient business-logic acceptance criteria for source precedence and canonical-card behavior.

Summary

Improves Grok quota accuracy and refresh speed, ensures the Grok / xAI credits pool stays as one row, brings iOS cap logic into parity with Mac, and adds Copilot provider artwork with faster, thread-safe logo loading.

What changed

Grok quota fallback and refresh:

  • Hardens the grok.com gRPC fallback parser to only accept the documented current-period field and weekly/monthly period lengths, and to distinguish a rejected/out-of-range percentage from an absent one to avoid false 0% usage.
  • Detects refused credentials via HTTP 401/403 and gRPC UNAUTHENTICATED trailer status, reporting a distinct "token rejected, sign in again" message with masked token preview instead of generic "no readable percentage".
  • Issues tier enrichment and credits fallback in parallel and only when needed - skips tier lookup when plan names exist and skips credits lookup when there is no window to fill or a percentage is already present.
  • Re-derives window status/exhausted state after the fallback fills in a percentage so filled pools are no longer shown as unknown/spent incorrectly.

Grok / xAI deduplication:

  • Identifies the credits pool by exact <providerKey>:subscription components instead of suffix matching, so unrelated windows like grok:legacy:subscription or xai:api-monthly are untouched.
  • Collects top-level windows and additive providerGroups first, then dedupes once, so the fleet xai:subscription reading does not re-add a duplicate after the merge.

iOS companion parity:

  • Replaces local zero/threshold checks with shared QuotaCapRule decisions everywhere - window rows, Antigravity pools, weekly rows, and exhaustion tracking - so a live reading outranks a stale provider isExhausted flag.
  • Makes a pool spent when any of its windows is spent, and ensures an exhausted weekly with no figure still publishes a reading so the cap line appears.
  • Adds copilot provider mapping and monochrome handling for consistent logos.

Provider marks / logos:

  • Adds github-copilot / copilot mappings to shared copilot artwork, adds Copilot color/dark/light generation targets, and consolidates muse-assist onto the shared muse mark.
  • Warms bundled-mark caches on launch via preloading, shares cache entries when hints resolve to the same artwork, guards bundle caching for concurrent access, and retries missing marks quickly at first for faster healing.

Tests:

  • Extends parser tests for period-field pinning, first-wins behavior, period-length validation, and rejected vs. absent percentages.
  • Adds dedupe tests for non-pool suffixes and survival across provider-group merge.
  • Updates companion and resource-bundle tests for shared-rule cap decisions, canonical-only main list behavior, and artwork consolidation.

…use dedupe

- Grok: x-xai-token-auth header, subscription_tier_display plan
  enrichment, and a best-effort grok.com gRPC-web credits fallback
  (documented wire contract, protobuf scanner with bounds) used
  only when the proxy publishes no percentage.
- xAI/Grok: the same subscription pool reported under both keys
  keeps only the fresher window.
- Copilot windows carry the monthly cadence (1m caption).
- iOS: source-scope toggle, source badges/banners, per-window
  Updated stamps gone from the main list; canonical resolution
  only, fixed meter columns; source detail stays in Settings.
- Muse Assist artwork deduped onto the Muse base (byte-identical).
@kody-ai

This comment has been minimized.

@kody-ai

kody-ai Bot commented Oct 22, 2026

Copy link
Copy Markdown

kody code-review Business Logic medium

Task description is insufficient for business logic validation.

🤔 Insufficient Task Context

I found a task linked to this PR, but it only contains minimal information (title only, no description or acceptance criteria). To perform a meaningful business rules validation, I need more details.

🔍 What I need to validate:

  • Business requirements and acceptance criteria
  • Expected behavior and business rules
  • Edge cases and constraints to consider

💡 How to improve the task context:

  • Add a description to the linked ticket
  • Include acceptance criteria or business rules
  • Describe the expected behavior after the change

⚠️ Important:

A task title alone is not sufficient to determine whether the implementation is correct or complete.


💡 This validation runs automatically only on the first review of a pull request. To run it again, comment @kody -v business-logic.

Prompt for LLM
Task description is insufficient for business logic validation.

<!-- task_context_status:weak -->
## 🤔 Insufficient Task Context

I found a task linked to this PR, but it only contains minimal information (title only, no description or acceptance criteria). To perform a meaningful business rules validation, I need more details.

### 🔍 What I need to validate:
- Business requirements and acceptance criteria
- Expected behavior and business rules
- Edge cases and constraints to consider

### 💡 How to improve the task context:
- Add a description to the linked ticket
- Include acceptance criteria or business rules
- Describe the expected behavior after the change

### ⚠️ Important:
A task title alone is not sufficient to determine whether the implementation is correct or complete.

---
> 💡 This validation runs automatically only on the first review of a pull request. To run it again, comment `@kody -v business-logic`.

​

Comment thread Sources/CodeCaps/PlatformLogo.swift
Comment thread Sources/QuotaCore/GrokGrpcCredits.swift Outdated
Comment thread Sources/QuotaCore/GrokGrpcCredits.swift
Comment thread Sources/QuotaCore/LocalQuotaReader.swift Outdated
Comment thread Sources/QuotaCore/LocalQuotaReader.swift Outdated
Comment thread Sources/QuotaCore/LocalQuotaReader.swift Outdated
Comment thread Sources/QuotaCore/QuotaModels.swift Outdated
Comment thread Sources/QuotaCore/QuotaModels.swift Outdated
Comment thread ios/CodeCapsCompanion/App/Views/CompanionContentView.swift
kody-ai[bot]
kody-ai Bot previously requested changes Oct 22, 2026

@kody-ai kody-ai Bot 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.

Found critical issues please review the requested changes

@swu-wise

swu-wise Bot commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Mac build is red on the merge ref with 4 test failures. Evidence per failure, all traced to the canonical-cards rewrite in db0cde1 (neither main nor the branch touched the test files since the merge-base, so the tests still encode the pre-redesign structure):

  1. PlatformLogoResourceTests.testTheResourceBundleCarriesArtwork - the commit removed the literal muse-assist* asset files and aliased "muse-assist": "muse" in PlatformLogo.swift (deliberate Muse dedupe), but the test still requires a muse-assist.svg|png in the bundle. Mechanical fix: teach the two resource tests to resolve aliased marks through the same base-art map instead of requiring a literal file.

  2. testTheCompanionCardShowsTheAtCapLine - test asserts the literal item.atCapSummary(scope: model.sourceScope); the rewrite has item.atCapSummary(scope: scope) (line ~360). The cap line itself is present and looks behavior-preserving; this is a source-literal mismatch.

  3. testSourceDiscrepancyGatingRequiresAllSourcesMultiSourceAndFreshness - test wants the model.sourceScope == .allSources, item.sourceCount > 1 gate. The rewrite's discrepancy UI lives in the Settings sources section with scope: .allSources hardcoded, which may make the runtime gate structural rather than explicit - but the owner-intent assertion (Oct 21: discrepancy UI must follow the multi-source chip gate) no longer has its literal. Worth a deliberate call: restore the explicit gate or update the test to the new structure.

  4. testSourceScopePickerShowsTheDeviceAwareLabel - Text(scope.displayTitle).tag(scope) is gone; no displayTitle or model.sourceScope reference remains in the view. If the source-scope picker moved or was redesigned, the owner-intent test (Mac build must name the Mac) needs its new home asserted.

Leaving the calls to your lane since this is an active redesign - flagging so the failures don't sit. The #304-era owner tests are the contract being asserted.

OpenCode and others added 2 commits October 22, 2026 13:16
Resolve the CopilotQuotaReader.swift conflict by taking main's line wrapping.
Both sides carry identical arguments (token streams match after whitespace is
removed); main only rewrapped the resetAt/window and remainingUnknown/status
lines. The branch's Grok xAI work is untouched.
Review threads:
- GrokGrpcCredits pins period decoding to the documented field 6, keeps the
  first match, and accepts only a weekly or monthly span, so an unrelated
  submessage can no longer invent resetAt/periodStart.
- A published percentage that is out of range or non-finite is tracked as
  seen-but-rejected, so it cannot reach the implicit-zero rule and report a
  spent pool as 100% remaining.
- The gRPC fallback returns a typed outcome; HTTP 401/403 and gRPC status 16
  report GROK_TOKEN with a masked first-8/last-4 preview instead of being
  swallowed as missing data.
- readGrok issues both enrichments concurrently and only when they have
  something to contribute, instead of always paying 2s + 6s serially.
- The fallback re-normalizes the window after filling the percentage, so
  status/isExhausted/skip describe the number just filled in.
- The Grok subscription pool is matched by canonical identity rather than by
  id suffix, and the dedupe runs over the merged collection so a provider
  group cannot re-add the duplicate.
- muse-assist leaves the bundle-file lists; the key still resolves through
  the PlatformLogo map to the shared muse mark.

Test failures repaired:
- PlatformLogoResourceTests no longer demands muse-assist artwork the PR
  removed.
- The three CompanionModelTests that pinned the pre-redesign card (picker in
  the header, model.sourceScope gate, atCapSummary signature) now assert the
  canonical-card behaviour the PR actually ships, and
  testSourceScopePickerShowsTheDeviceAwareLabel moved its premise to the
  model, where the device-aware label still lives.
@jaywedgeworth22

Copy link
Copy Markdown
Member Author

@kody start-review

withUnsafeBytes(of: &nanBits) needs a var; nanBits was a let constant, so
GrokGrpcCreditsTests did not compile.
The identity was built as providerKey + a hardcoded ":subscription", so
every grok/xai window matched it and testOtherXaiWindowsAreUntouched lost
its API-credits row. Match the last two id components in full instead:
parts[-2] == canonicalProviderKey && parts[-1] == "subscription". That
still catches the source-prefixed local-mac:grok:subscription and the
fleet xai:subscription, while excluding grok:legacy:subscription and
xai:api-monthly.
Resolved ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift by
keeping both sides: #307's shared QuotaCapRule.isAtCap decides the cap
instead of the local (x ?? 100) <= 0 copy, while this branch keeps the
suffix-free subtitle. sourceSuffix could not be restored because this
branch removed its definition along with the multi-source chip.
@kody-ai

kody-ai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug ✅
Performance ✅
Security ✅
Business Logic ✅

Access your configuration settings here.

​

Comment thread Sources/QuotaCore/LocalQuotaReader.swift Outdated
Comment thread Sources/QuotaCore/LocalQuotaReader.swift Outdated
Comment thread Sources/QuotaCore/QuotaModels.swift
@jaywedgeworth22

Copy link
Copy Markdown
Member Author

[GB-Designer] UI pass: nit (non-blocking for UI; the red Mac build is the real blocker)

Good to see sentenceGap used for the two-space rule. The new Companion detail copy leans developer-speak for a user-facing screen ("Per-feed detail for debugging", "hostnames are ignored", "canonical reading"). Suggested replacement:

Readings from each source. When two sources that reported within 20 minutes differ by 10 points or more, the difference shows here. The main list always shows the newest reading.

jaywedgeworth22 and others added 2 commits October 24, 2026 03:29
Brings in #309 through #313 from main.  The merge was clean: no file conflicted and no conflict marker remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The gRPC fallback fills the credits pool by its exact id (`local-mac:grok:subscription`), never by a `:subscription` suffix, and never overwrites a percentage the proxy published.
- Only an HTTP 401 from grok.com is a refused credential.  A 403 and gRPC status 16 are grok.com's documented answer to a bearer-only probe with no Web Key Exchange keypair, so they stay a plain fallback miss.  The 401 message names the refusal with the masked preview and no longer tells the owner to sign in again, since the CLI proxy accepted the same token.
- `isUnauthenticated` reads the header-style `grpc-status: 16` trailer as well as the unspaced form.
- A provider whose only reading the Grok and xAI dedupe removed gets no section, so it cannot render an empty "Quota unavailable" card beside the row that carries the pool.
- CI captures the main iOS quota list with the seeded windows (`codecaps-ios-quota.png`), with a second Claude window so one card shows both meter rows.
- The Data Sources footer takes the designer's plainer copy and uses `sentenceGap` between every sentence.

Tests cover the fallback fill, status 16, 403 and 401, both trailer spellings, and the emptied-group cases on the manifest and offline paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jaywedgeworth22

Copy link
Copy Markdown
Member Author

Sweep update for this PR (CLAUDE seat).  Merged main (#309 through #313) into the branch with no conflicts in 25dcc98, then fixed the open findings in 5bc9844:

  • Kody, LocalQuotaReader.swift line 382: the gRPC fallback matches the credits pool by its exact id, not a suffix.
  • Kody, LocalQuotaReader.swift line 462: only an HTTP 401 from grok.com is a refused credential; a 403 and gRPC status 16 are a plain fallback miss, and the 401 message no longer sends the owner to sign in again.
  • Kody, QuotaModels.swift line 658: a provider the Grok and xAI dedupe empties gets no section, so no empty "Quota unavailable" card.
  • Kody, iOS screenshot evidence: the companion CI job now captures the main quota list with the seeded windows as codecaps-ios-quota.png.  I will link the artifact on that thread once this run finishes.
  • GB-Designer copy note above: adopted.  The Data Sources footer now reads "Readings from each source.  When two sources that reported within 20 minutes differ by 10 points or more, the difference shows here.  The main list always shows the newest reading.", with sentenceGap between every sentence (the old text had a bare space before its last sentence).
  • swu-wise's four red tests: already fixed in 5e5832a, and "Mac package build and test" passed on 0b23d18.

Declined as optional hardening, with no behaviour change today: pinning the percentage to field 4, moving sawPercentField above its guard, and request-counting stubs for the skipped enrichments.  The business-logic note asks for acceptance criteria on the linked task, which is not a code change.

@jaywedgeworth22

Copy link
Copy Markdown
Member Author

@kody start-review

One conflict, in the Antigravity pool row of `CompanionQuotaModel.swift`.  Main now drives the headline and the exhausted state from the fresh windows; this branch dropped the "(N sources)" suffix from the subtitle.  The resolution keeps main's fresh-window logic and the subtitle without the suffix.  No conflict marker remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jaywedgeworth22
jaywedgeworth22 enabled auto-merge (squash) October 24, 2026 04:23
Kody's naming note on the grok.com fallback: the outcome case now means only that grok.com answered HTTP 401 to the credits fallback, so it is renamed from authRejected to refused, and the local flag in readGrok follows.  The doc comment on GrokGrpcOutcome had been split across two indents with a stray copy of the method's comment above the enum.  It now sits inside the struct at the member indent and says what the case means.  No behaviour changes.

GrokGrpcCredits.parse: the period comment now says "first valid match wins", since a malformed or off-cadence field-6 submessage is skipped and a valid one after it is still read, so nobody tightens it into "first field 6 only".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jaywedgeworth22
jaywedgeworth22 dismissed kody-ai[bot]’s stale review October 24, 2026 06:05

Stale: every finding fixed or answered at 6dbe420 (the one finding this review lists, "Grok gRPC authentication failures are silently swallowed", r4346459074: a typed outcome since 5e5832a, narrowed by the revised finding r4352695042 in 5bc9844 so only an HTTP 401 from grok.com is reported, with a first 8 and last 4 masked GROK_TOKEN preview and no sign-in instruction, and the case renamed to refused in 6dbe420; the other eleven Kody threads on this PR are fixed or answered and resolved), CI green.  Dismissed by CLAUDE per owner ruling 2026-10-17.

@jaywedgeworth22
jaywedgeworth22 merged commit b24e1a5 into main Oct 24, 2026
31 checks passed
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