refactor: drive usage pills from a provider registry - #617
Conversation
Usage was written as two hardcoded pipelines. Provider identity lived in raw "claude" / "codex" strings that had to agree across the selection helper, the pill IDs and a shortTitle switch. The island rebuilt each snapshot shape by hand, the summary text was duplicated per provider, and the Codex toggle only persisted, so switching it on mid-session left the pill blank until the next launch. Introduce UsageProvider as the single registry (labels, harness mapping, opt-in key, poll interval) and UsageSnapshotSummarizing in Core so both snapshot shapes normalize into UsageWindowSummary. The island, Settings and the monitor loops now iterate providers instead of naming them, and toggling a provider starts or stops its poll loop immediately. Claude and Codex stay the two registered providers. They are now covered by tests rather than being what the code is shaped around.
Usage caches are only rewritten while a harness runs. Claude Code's status line writes on each turn, Codex on each rollout append. A cache that outlives its window keeps reporting the previous window's number: a 12h-old cache whose 5h window reset 7h earlier still headlined the pill as "5h 10%", which read like the weekly figure sitting in the wrong slot because the peak picker promoted it over the live 7d window. Filter windows whose resets_at has passed out of both the island pill and the Settings summary, drop the provider entirely once every window has rolled over, and note the cache age in the pill tooltip once the numbers stop tracking the live quota. Adds AppModel-level coverage for the usage pipeline the island reads: opt-in gating, provider ordering, peak selection, and opt-in persistence across launches.
📝 WalkthroughWalkthroughThe PR replaces Codex-specific usage settings and monitoring with a generalized ChangesUsage provider system
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant SettingsView
participant AppModel
participant HookInstallationCoordinator
participant IslandPanelView
User->>SettingsView: enable or disable provider
SettingsView->>AppModel: setUsageProvider(provider, enabled:)
AppModel->>HookInstallationCoordinator: start or stopUsageMonitoring(for:)
HookInstallationCoordinator->>AppModel: refresh provider snapshot
IslandPanelView->>AppModel: read usageProviderStatuses
AppModel-->>IslandPanelView: return live provider status
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
Sources/OpenIslandCore/UsageSnapshot.swift (1)
11-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNew usage model types violate the
Sendable+Codablemodel rule. The guideline "All models must beSendableandCodable" is unmet by the usage models added in this PR; conformances chain through these three simple, trivially-conformable types.
Sources/OpenIslandCore/UsageSnapshot.swift#L11-L45: addCodabletoUsageWindowSummary.Sources/OpenIslandApp/UsageProvider.swift#L112-L141: addSendable, CodabletoUsageProviderStatus, and addCodableto theUsageProviderenum (needed forUsageProviderStatus's synthesizedCodable).As per coding guidelines: "All models must be
SendableandCodable".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/OpenIslandCore/UsageSnapshot.swift` around lines 11 - 45, Update UsageWindowSummary in Sources/OpenIslandCore/UsageSnapshot.swift:11-45 to conform to Codable. In Sources/OpenIslandApp/UsageProvider.swift:112-141, add Sendable and Codable to UsageProviderStatus, and add Codable to the UsageProvider enum so synthesized Codable conformance succeeds.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/OpenIslandApp/AppModel.swift`:
- Around line 1166-1168: Update the startup usage-monitoring loop around
isUsageProviderEnabled so the Claude provider is monitored only when
claudeUsageInstalled is true, while preserving existing behavior for other
providers. Ensure installClaudeUsageBridge() starts Claude provider monitoring
after a successful bridge installation so the usage pill populates immediately.
---
Nitpick comments:
In `@Sources/OpenIslandCore/UsageSnapshot.swift`:
- Around line 11-45: Update UsageWindowSummary in
Sources/OpenIslandCore/UsageSnapshot.swift:11-45 to conform to Codable. In
Sources/OpenIslandApp/UsageProvider.swift:112-141, add Sendable and Codable to
UsageProviderStatus, and add Codable to the UsageProvider enum so synthesized
Codable conformance succeeds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 382f7866-301c-40be-b87b-cd9edd72cfb0
📒 Files selected for processing (8)
Sources/OpenIslandApp/AppModel.swiftSources/OpenIslandApp/HookInstallationCoordinator.swiftSources/OpenIslandApp/UsageProvider.swiftSources/OpenIslandApp/Views/IslandPanelView.swiftSources/OpenIslandApp/Views/SettingsView.swiftSources/OpenIslandCore/UsageSnapshot.swiftTests/OpenIslandAppTests/AgentSessionPresentationTests.swiftTests/OpenIslandAppTests/AppModelUsageProviderTests.swift

Closes #501.
Overlaps with #613. That PR restructures the same UsageProviderPresentation and UsageWindowPresentation types in IslandPanelView.swift to show Codex usage as remaining percentage instead of used percentage. This PR takes a different approach (a UsageProvider registry instead of string IDs) and also fixes stale/expired windows still being displayed, which #613 does not address. Whichever merges first, the other needs a rebase.
Problem
Usage was written as two hardcoded pipelines. Provider identity lived in raw "claude" / "codex" strings that had to agree across the selection helper, the pill IDs, and a shortTitle switch. The island rebuilt each snapshot shape by hand, summary text was duplicated per provider, and the Codex toggle only persisted, so switching it on mid-session left the pill blank until the next launch.
Usage caches are also only rewritten while a harness runs, so a cache that outlives its window kept reporting the previous window's number. A 12h-old Claude cache whose 5h window had reset 7h earlier still headlined the pill as "5h 10%", reading like the weekly figure in the wrong slot, since the peak picker promotes the stale value over the live 7d window.
Changes
Testing
swift build, swift test. Pre-existing failures in AppModelSessionListTests and AgentsGridRightSlotTests reproduce identically against a clean main with none of this branch's changes applied. Unrelated flakiness, not introduced here.
Summary by CodeRabbit
New Features
Bug Fixes
Tests