Repository navigation
fix(quota): one reading decides every exhausted question, and a spent window is not a hidden one - #330
Conversation
The exhaustion path still had its own answer on three surfaces, so the Mac, the wire and the phone could each call the same account spent or live while the other two disagreed. The Mac's exhausted alert now reads the shared cap rule instead of testing for zero, so it fires at the same threshold as the "At cap" line it sits next to. The wire's exhausted flag does the same, and a reading decides it whenever there is one: a stale provider word, which the Mac's own export never clears, must not overrule a reading that says there is room left. The phone's card, the phone's pool and the phone's alert all read the rule too. The settings copy named the old rule, so it now names the threshold the app uses. Refs CC-184
The shared cap rule that #298 put in place calls any reading at or under 0.5% spent, because the interface rounds a reading to the nearest whole percent. When `skip` followed that rule, a window at 0.3% was dropped from the picture in picture together with its cap line, while the menu bar still announced the cap, so the same window was live in one place and gone in the other. `skip` means "a reading that must not be believed", which is a different question from "is the allowance spent". The provider's own exhausted word is believed only when the reading disagrees with it, because only the provider knows why (a rejected session, a suspended plan) and the reading cannot say. It is also the answer when there is no reading at all. An exact zero is still the one reading that is never shown as a meter, and the provider's own `skip` flag is never erased. The reading is what makes that rule safe to apply twice, which the display path does: the picture in picture rebuilds a snapshot from an already derived window, and a rule that read the derived `isExhausted` back as the provider's word would hide the window again on the second pass. That is why the rule is written around the reading and not around the flag, and why each of the three broken variants of it is covered by a test that fails without it. Refs CC-184
Code Review Completed! 🔥The code review was successfully completed based on your current configurations. Kody Guide: Usage and ConfigurationInteracting with Kody
Current Kody ConfigurationReview OptionsThe following review options are enabled or disabled:
|
|
Task description is insufficient for business logic validation. 🤔 Insufficient Task ContextI 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:
💡 How to improve the task context:
|
The notifications footer said "Quota Exhausted fires at the cap threshold", which names an internal rule rather than what the person reading it sees. It now reads "Quota Exhausted alerts when 0.5% or less remains." The threshold is a Double, so a plain interpolation would print 1.0% the day the rule reaches a whole number. It is now formatted with a fraction length of zero to one, which keeps 0.5% and gives 1%. A test asserts that formatting, so the whole-number form cannot come back. Refs CC-184
A provider can hide a window without saying why. The export path invented the words "quota exhausted" for any hidden window, so a consumer that filters on the reason saw a healthy window reported as spent. The reason now attaches only to a window that is hidden AND spent, so it explains a skip without contradicting the reading. The provider's own reason is still passed through untouched. The gate is isExhausted rather than skip alone on purpose. A window that stays visible at a near zero reading is a spent window, and giving it a reason would invite a consumer to hide exactly the window this work keeps on screen. Tested by LocalQuotaSnapshotTests.testSkipDoesNotFollowTheSharedCapThreshold: the hidden-but-healthy window carries no reason, the hidden-and-spent window carries one, and the visible near zero window carries none.
Fixes CC-184
Summary
Unifies all quota-exhausted decisions on the shared
QuotaCapRuleand decouples "allowance is spent" from "reading must be hidden".Previously the card / at-cap line treated near-zero readings (e.g. 0.3-0.4%) as spent while the Mac alert required an exact zero (
remaining > 0) and the export used a local zero test. The same window was therefore shown as "At cap" in one surface and quiet / live in another.skipalso followed the exhausted state, so a spent window disappeared from picture-in-picture with its cap line while the menu bar still announced the cap.What changed
MonitorModel): fires onQuotaCapRule.isAtCap(remainingPercent:)instead ofremaining > 0, so it fires at the same threshold as the "At cap" line and companion surfaces. Alert body reworded from "has no quota remaining." to "has reached its limit."QuotaPublisher): exportedisExhaustednow usesQuotaCapRule.isExhausted(remainingPercent:providerSaysExhausted:), where reading at/under threshold is spent, provider flag is fallback for windows with no figure and does not overrule a healthy reading.QuotaWindow.normalizedForExport):skipno longer equalsisExhausted.skipnow means "unbelievable reading": pre-existingskip, exact0, or providerisExhaustedwhen the reading disagrees (bounded > spentThreshold) or when there is no reading. A near-zero spent window stays visible; an exact zero stays hidden with reason "quota exhausted". Logic is stable when derivation is applied twice on the display / PiP path.SettingsNotificationsPage): updated from "fires at zero remaining" to "alerts when 0.5% or less remains", and the threshold is formatted so a whole-number rule prints1%rather than1.0%.QuotaWindow.normalizedForExport): the derived reason is attached only to a window that is hidden and spent, so a provider that hides a healthy window without saying why no longer puts "quota exhausted" on the wire. The provider's own reason is passed through untouched.Coverage
Adds
ExhaustionCapRuleTestsfor the shared rule and source pinning, and extendsLocalQuotaSnapshotTests,QuotaPublisherTests, andPipInteractionTestsfor near-zero visibility, skip-reason preservation, second-pass stability, and wire-flag agreement.Review follow-ups
A copy pass on this branch raised two nits, both taken here rather than in a follow-up. The threshold is a
Double, so a plain interpolation would print1.0%once the rule reaches a whole number, and it is now formatted with a fraction length of zero to one. The footer also named an internal rule rather than what the reader sees, so it now reads "Quota Exhausted alerts when 0.5% or less remains." A test asserts the formatting, so the whole-number form cannot come back.Kody's round on this branch found that the derived skip reason could claim an exhaustion a window did not have: a provider could hide a window that still had room, and the export attached "quota exhausted" to it, so a consumer filtering on the reason would act on a false exhaustion. The reason now attaches only to a window that is hidden and spent. The gate is
copy.isExhaustedand notcopy.skipalone, on purpose, because a near zero window is deliberately visible in this branch, and giving it a reason would invite a consumer to hide exactly the window this pull request keeps on screen. The test asserts that the hidden and healthy window carries no reason, the hidden and spent window keeps one, and the visible near zero window carries none. The non-vacuity proof has a fourth variant that restores the old gate, and that variant fails on the test.