Skip to content

fix(quota): let the phone's cap rule decide, not a copy of it - #307

Merged
jaywedgeworth22 merged 3 commits into
mainfrom
minimax/cc-186-phone-cap-pool
Oct 22, 2026
Merged

jaywedgeworth22 merged 3 commits into
mainfrom
minimax/cc-186-phone-cap-pool

Conversation

@jaywedgeworth22

@jaywedgeworth22 jaywedgeworth22 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Fixes CC-186

Summary

Centralizes quota exhaustion and cap-publication decisions in QuotaCapRule, then uses the shared rules in the iOS companion app and Antigravity pool grouping. For finite readings, the remaining percentage now takes precedence over the provider’s potentially stale isExhausted flag. When no reading is available, the provider’s exhaustion state remains the fallback.

Functional changes

  • Added shared helpers to:
    • Determine exhaustion from an available reading, falling back to the provider’s state only when no reading exists.
    • Publish zero for a known-spent allowance that has no percentage, while preserving any supplied reading.
  • Updated the phone’s window, pool, weekly-allowance, synthesized-row, and exhaustion-event paths to use the shared rule instead of local cap checks.
  • Ensured exhausted weekly allowances without a numeric reading publish zero, allowing the shared cap rule to bind the window and surface its cap status and countdown.
  • Updated Antigravity pool grouping on the Mac to use the same exhaustion and publication helpers.
  • Normalized at-cap cadence tokens so values such as "7-day window" resolve consistently with "7-day".
  • Added regression coverage for:
    • Reading precedence over provider exhaustion state.
    • Provider-state fallback when no reading exists.
    • Zero publication and cap binding for spent allowances without percentages.
    • Position-based binding when duplicate window IDs are present.
    • Whole-token cadence resolution.
    • Source-level verification that the phone delegates weekly and row exhaustion decisions to the shared rule.

Outstanding review items

  • A pool with a controlling reading can still ignore an exhausted provider flag from a reading-less sibling window.
  • Weekly-row zero publication can combine exhaustion state from one window with the reading from another selected window.
  • The widget retains its previous exhaustion logic and may disagree with the updated app behavior.
  • The phone’s now-unused weeklySpentThreshold declaration remains present and is retained by a source-text test.
  • Non-finite readings are not validated and can cause the provider fallback to be skipped.
  • The phone can publish an out-of-range reading, while the Mac path publishes bounded percentages.
  • The intended business behavior for mixed reading/no-reading pools and weekly-row window selection still requires explicit validation.

Summary

  • Centralized quota exhaustion decisions in QuotaCapRule and removed duplicate weekly-cap thresholds from the phone and Mac code.
  • Updated the phone to determine plan-level exhaustion from each primary window’s own exhaustion state. This prevents an exhausted weekly window without a numeric reading from being incorrectly evaluated against a healthy five-hour reading.
  • Ensured the phone’s weekly row considers the provider state of the window actually used to build that row and publishes 0% when an exhausted weekly window has no numeric reading.
  • Updated the widget to use the shared cap rule for five-hour, weekly, and plan-level exhaustion. Widget readings are now bounded consistently with the phone, including 0% fallbacks for exhausted windows without figures.
  • Treated non-finite percentages as missing readings and bounded out-of-range percentages before publishing them, so invalid values cannot be interpreted as headroom or displayed above 100%.
  • Ensured valid numeric readings take precedence over stale provider exhaustion flags, with provider state used as the fallback when no valid reading exists.

Test Coverage

  • Added coverage for shared-rule handling of missing, non-finite, and out-of-range readings.
  • Added tests ensuring the phone and widget no longer maintain their own cap comparisons or threshold constants.
  • Added coverage for phone pool exhaustion across individual windows and weekly rows built from a different window than the pool’s selected weekly window.
  • Added widget consistency tests for shared exhaustion decisions, bounded published percentages, and weekly/plan-level cap behavior.

Automated Review Findings

  • High — ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift:1859: Plan-level exhaustion now counts sub-model, scoped, and video windows that are excluded by the cap rule.
  • Medium — ios/CodeCapsCompanion/Widgets/WidgetPresentation.swift:776: The widget publishes 0% for a spent five-hour window with no numeric figure, while the phone publishes no reading.

CodeCaps PR #298 carried two of the four Kody findings raised on that branch, and the other two were left open on main.  Both are in the phone's Antigravity pool, which is the surface the owner reported as broken.

The phone's Antigravity pool let the wire's exhausted word override a healthy weekly reading, so the phone called a live pool exhausted while the Mac kept it live.  And the phone published a weekly row with no percentage when the provider reported the allowance as gone without a figure, so the shared rule had nothing to bind and the cap line never appeared.  The Mac avoided the second one only because its own pool grouper forces a zero.

Both now ask the shared rule.  Two helpers in QuotaCapRule answer the two questions the phone and the Mac were each answering for themselves:  whether an allowance is spent when the provider's word and its reading disagree, and what reading a row publishes when the provider reports a spent allowance without a figure.  The Mac's Antigravity pool grouper calls them too, so the decision has one home.

Every place that decides "is this spent" now reads the same rule, so the two surfaces cannot drift apart again.

Fixes CC-186
@linear-code

linear-code Bot commented Oct 22, 2026

Copy link
Copy Markdown

CC-186

@kody-ai

This comment has been minimized.

Comment thread ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift
@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/QuotaCore/QuotaWindowVocabulary.swift
Comment thread Sources/QuotaCore/QuotaWindowVocabulary.swift
Comment thread ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift Outdated
Comment thread ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift Outdated
Comment thread ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift
Comment thread ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift
@jaywedgeworth22
jaywedgeworth22 enabled auto-merge (squash) October 22, 2026 10:55
Answers the seven open threads on CC-186, where the phone moved to the
shared rule and the widget, the pool decision and the published reading
had each kept a copy of the question beside it.

QuotaCapRule.isExhausted now decides that a reading exists with the
shared `bounded` reader rather than a nil check.  A NaN or infinite value
passed the nil check, was then judged as headroom, and silently dropped
the provider's exhausted word, which was the only evidence there was.

QuotaCapRule.publishedReading bounds the reading it publishes, so the
phone's raw wire value and the Mac's already-clamped value come out the
same number instead of the phone rendering "150%" for a window the cap
rule judged at 100.

The phone's pool decision ORs its per-window decisions instead of
weighing the provider's word against `controllingPct`, which is a reading
belonging to a different window than the one with no reading at all:  a
weekly the provider reports as gone, beside a healthy five-hour reading,
no longer reports the plan live.

The weekly row also counts the exhausted word of the window it is built
from, so a spent weekly publishes zero even when the pool's weekly
selector picked a different window.

The widget asks the shared rule in all four places it decided for itself
(per-window, weekly, the five-hour row and the pool), and publishes the
same bounded readings, so app and widget stop disagreeing about one
payload (Sentry 18329264).

Both `weeklySpentThreshold` copies are deleted.  The weekly decision now
lives in the shared rule, so neither constant had a production reader;
they survived only as dead code held up by tests that could not see the
live copy of the same question in the widget.
@jaywedgeworth22

Copy link
Copy Markdown
Member Author

@kody start-review

@jaywedgeworth22
jaywedgeworth22 merged commit af5bc3f into main Oct 22, 2026
33 checks passed
jaywedgeworth22 added a commit that referenced this pull request Oct 22, 2026
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 ios/CodeCapsCompanion/App/Models/CompanionQuotaModel.swift
Comment thread ios/CodeCapsCompanion/Widgets/WidgetPresentation.swift
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