FEAT: add Resets view and earliest-expiry redemption - #48
Merged
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Preserve missing-versus-null expiry state and localize the newly added reset safety and inventory messages.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a dedicated Quota → Resets view with expiry warnings and earliest-known-credit redemption.
Changes:
- Adds responsive navigation, scrolling, preferences, and reset inventory details.
- Adds configurable expiry warnings and credit-bound redemption retries.
- Updates documentation, wire types, tests, and locale catalogues.
File summaries
| File | Summary |
|---|---|
README.md |
Documents reset views, warnings, and redemption behavior. |
main.go |
Adds warning-hours configuration. |
main_test.go |
Tests CLI validation and propagation. |
intro-post.md |
Documents the reset workflow. |
internal/ui/view.go |
Renders the Resets view. |
internal/ui/view_test.go |
Tests Resets rendering. |
internal/ui/theme.go |
Adds the Resets view. |
internal/ui/tabs.go |
Adds responsive navigation. |
internal/ui/tabs_test.go |
Tests tab behavior. |
internal/ui/reset_warning_test.go |
Tests warning visibility and geometry. |
internal/ui/reset_credits_test.go |
Tests ordering, selection, scrolling, and confirmation. |
internal/ui/quota_reset.go |
Implements reset inventory, warnings, and redemption. |
internal/ui/quota_api_eq_test.go |
Covers pricing exclusions. |
internal/ui/preferences.go |
Persists the Resets preference. |
internal/ui/preferences_test.go |
Tests preference persistence. |
internal/ui/model.go |
Adds reset state, navigation, and scrolling. |
internal/ui/localisation_test.go |
Tests reset localization. |
internal/ui/english_snapshot_test.go |
Updates presentation snapshots. |
internal/i18n/locales/zh-Hans.json |
Adds Simplified Chinese translations. |
internal/i18n/locales/tr.json |
Adds Turkish translations. |
internal/i18n/locales/sv.json |
Adds Swedish translations. |
internal/i18n/locales/ru.json |
Adds Russian translations. |
internal/i18n/locales/pt-PT.json |
Adds European Portuguese translations. |
internal/i18n/locales/pt-BR.json |
Adds Brazilian Portuguese translations. |
internal/i18n/locales/nl.json |
Adds Dutch translations. |
internal/i18n/locales/nb.json |
Adds Norwegian translations. |
internal/i18n/locales/ja.json |
Adds Japanese translations. |
internal/i18n/locales/it.json |
Adds Italian translations. |
internal/i18n/locales/fr.json |
Adds French translations. |
internal/i18n/locales/fi.json |
Adds Finnish translations. |
internal/i18n/locales/et.json |
Adds Estonian translations. |
internal/i18n/locales/es.json |
Adds Spanish translations. |
internal/i18n/locales/en-GB.json |
Adds English catalogue entries. |
internal/i18n/locales/de.json |
Adds German translations. |
internal/i18n/locales/da.json |
Adds Danish translations. |
internal/codex/types.go |
Adds reset-credit wire types. |
internal/codex/reset_credits_test.go |
Tests credit decoding. |
internal/codex/client.go |
Sends selected credit IDs. |
internal/codex/client_test.go |
Tests targeted reset requests. |
Review details
Suppressed comments (4)
internal/ui/quota_reset.go:382
- The Resets inventory's count-only disclosure is hard-coded English. With partial/count-only data, non-English users will see
Backend selects the next credit; expiry order is unknown.untranslated, so the safety disclosure is inconsistent with the translated expiry notices and the stated locale coverage; add a catalogue entry and translate this line.
if len(credits) == 0 {
return append(lines, "Backend selects the next credit; expiry order is unknown.")
internal/ui/quota_reset.go:292
- This newly added stale-data notice, along with the analogous no-credit and disappearing-credit notices below, is emitted as a raw English literal. It makes the new confirmation safety path mixed-language outside en-GB; use translated
i18nkeys for these messages.
if m.loading || m.err != nil || m.snapshot.FetchedAt.IsZero() || time.Since(m.snapshot.FetchedAt) > 2*m.refreshEvery {
m.resetNotice = "Quota data is not fresh; refresh and confirm again. No reset submitted."
return m, nil
}
if m.snapshot.RateLimitResetCredits == nil || m.snapshot.RateLimitResetCredits.AvailableCount <= 0 {
internal/ui/quota_reset.go:374
- Most of the new inventory UI text in this renderer (
AVAILABLE, the no-resets/backend-selection text, count summary, expiry/grant labels, and the expiry-soon warning) is hard-coded English. Since user-facing UI literals are routed throughinternal/i18nand every locale has a complete catalogue, the Resets view is only partially translated; add translatedText/Formatkeys for these labels.
lines := []string{fmt.Sprintf("AVAILABLE // %d", summary.AvailableCount)}
credits := m.availableResetCredits()
if notice := m.resetExpiryDataNotice(); notice != "" {
lines = append(lines, colors.label().Foreground(colors.warning).Render(notice))
}
internal/ui/quota_reset.go:275
- The new confirmation suffixes on these lines bypass the localization boundary:
Expiry order unavailable...andSelected: ...remain English in every non-en-GB locale, despite the PR's requirement that the reset safety messages be translated. Route these strings throughi18n.Text/i18n.Formatand add the corresponding catalogue entries.
m.resetNotice += " Expiry order unavailable; backend chooses the credit."
} else {
for _, credit := range m.availableResetCredits() {
if credit.ID == m.resetCreditID {
m.resetNotice += " Selected: " + resetCreditTitle(credit) + " — " + resetCreditExpiry(credit) + "."
- Files reviewed: 39/39 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Reset credits can expire independently of quota windows. A count alone does not let users spot an expiring credit, and leaving redemption to the backend does not guarantee earliest-expiry selection. This adds a dedicated place to review reset details without crowding the quota gauges.
Changes
vcycle, including saved view preferences and translated tab labels.--reset-warning-hours HOURS(default 72, 0 disables expiry warnings and their consumption-threshold bypass). For testing known later expiries, use--reset-warning-hours 168. Invalid or overflowing durations are rejected before launching the UI.creditIdto the app-server.Data limitations
Individual credit details are optional and may be capped by the backend. The view explicitly discloses partial/missing details. “Earliest” means earliest known expiry; undisclosed credits cannot be compared. With count-only data, redemption falls back to backend selection and the confirmation explains that its expiry order is unavailable. A supplied null expiry means the credit does not expire.
Confirmation explicitly warns that unused allowance does not carry over or stack and that the weekly reset schedule changes. Both the inventory and confirmation flag unavailable/incomplete expiry information: no warning does not mean no expiry, and undisclosed credits may expire sooner. These safety messages are translated across all supported locales. Expiry alerts are prompts to review, not recommendations to redeem immediately.
Verification
go vet ./...andgit diff --check.