Skip to content

fix: parse reset time when percentage shares same line#174

Merged
hanrw merged 1 commit intotddworks:mainfrom
frankhommers:fix/cli-reset-percent-same-line
Apr 15, 2026
Merged

fix: parse reset time when percentage shares same line#174
hanrw merged 1 commit intotddworks:mainfrom
frankhommers:fix/cli-reset-percent-same-line

Conversation

@frankhommers
Copy link
Copy Markdown
Contributor

@frankhommers frankhommers commented Apr 15, 2026

Summary

  • Newer Claude CLI versions (v2.1.109+) render the reset text and percentage on the same line instead of separate lines:
    Resets 3pm (Europe/Amsterdam)                      27% used
    
  • The trailing NN% used/left suffix prevented timezone stripping and date parsing in parseAbsoluteDate, causing resetsAt to be nil — which made the pace triangle marker disappear from the progress bar.
  • Fix: strip the % used/% left suffix before date parsing begins.

Changes

  • ClaudeUsageProbe.swift: Strip \d{1,3}% (used|left) suffix at the start of parseAbsoluteDate
  • ClaudeUsageProbeParsingTests.swift: Two new tests with real CLI output from v2.1.109

Test Plan

  • parses percentages when reset and percent share same line — verifies 27%, 40%, 0% are parsed correctly
  • parses resetsAt when reset and percent share same line — verifies resetsAt is populated (enables pace triangle)
  • Existing tests (blocked locally by pre-existing SwiftTerm Metal duplicate tasks build issue, should pass in CI)

Summary by CodeRabbit

  • Bug Fixes
    • Improved parsing of Claude usage data to correctly handle newer CLI output formats where reset information and usage percentage appear on the same line, ensuring accurate quota tracking.

Newer Claude CLI versions (v2.1.109+) render the reset text and
percentage on the same line instead of separate lines:

  Resets 3pm (Europe/Amsterdam)                      27% used

The trailing 'NN% used/left' suffix prevented timezone stripping and
date parsing, causing resetsAt to be nil and the pace triangle marker
to disappear. Strip the suffix before date parsing begins.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

📝 Walkthrough

Walkthrough

Added regex normalization to the date parsing function in ClaudeUsageProbe.swift to strip trailing percentage text (like "NN% used" or "NN% left") before reset-date extraction. Accompanying test fixture and test cases validate parsing when percentage and reset information appear on the same CLI output line.

Changes

Cohort / File(s) Summary
Reset-Date Parsing Enhancement
Sources/Infrastructure/Claude/ClaudeUsageProbe.swift
Added regex replacement to strip trailing percentage text (NN% used or NN% left) before existing reset-date parsing logic.
Test Coverage for Same-Line Output
Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift
Added fixture resetOnSameLineOutput and two new test functions validating correct percentage and reset-time extraction when both appear on the same CLI output line for session, weekly, and model-specific quotas.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 A hopping fix for numbers paired,
Percentages and resets squared,
Strip the trailing noise with care,
Same-line parsing, now so fair! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: parsing reset time when percentage shares the same line, which is the core fix addressed in both the source code and test changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift (1)

324-336: Strengthen this test to verify quota-specific reset mapping, not just non-nil.

Given the fixture uses different weekly and sonnet reset dates, asserting resetText content would prevent false positives where both quotas accidentally share one parsed reset.

Suggested assertion additions
     `@Test`
     func `parses resetsAt when reset and percent share same line`() throws {
         // When
         let snapshot = try ClaudeUsageProbe.parse(Self.resetOnSameLineOutput)

         // Then — all quotas should have resetsAt populated (enables pace triangle)
         `#expect`(snapshot.sessionQuota?.resetsAt != nil,
                 "Session resetsAt should be populated for 'Resets 3pm (TZ) ... 27% used' format")
         `#expect`(snapshot.weeklyQuota?.resetsAt != nil,
                 "Weekly resetsAt should be populated for 'Resets Apr 16 at 4:59pm (TZ) ... 40% used' format")
         `#expect`(snapshot.quota(for: .modelSpecific("sonnet"))?.resetsAt != nil,
                 "Sonnet resetsAt should be populated for 'Resets Apr 17 at 11:59am (TZ) ... 0% used' format")
+        `#expect`(snapshot.weeklyQuota?.resetText?.contains("Apr 16") == true)
+        `#expect`(snapshot.quota(for: .modelSpecific("sonnet"))?.resetText?.contains("Apr 17") == true)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift` around
lines 324 - 336, Update the test `parses resetsAt when reset and percent share
same line` to assert the specific reset mapping instead of only non-nil: after
calling ClaudeUsageProbe.parse(Self.resetOnSameLineOutput) compare
snapshot.sessionQuota?.resetsAt, snapshot.weeklyQuota?.resetsAt, and
snapshot.quota(for: .modelSpecific("sonnet"))?.resetsAt against the expected
values derived from the fixture (or assert their resetText strings match the
distinct expected reset strings) so the test verifies weekly vs sonnet reset
dates are parsed differently and not accidentally shared.
Sources/Infrastructure/Claude/ClaudeUsageProbe.swift (1)

708-712: Make percent-suffix stripping resilient to no-space formatting.

Current regex requires whitespace before NN% used|left. If CLI output ever collapses spacing (e.g., )...27% used), stripping won’t happen and date parsing can regress.

Suggested patch
-        cleaned = cleaned
-            .replacingOccurrences(of: #"\s+\d{1,3}%\s*(?:used|left)\s*$"#, with: "", options: .regularExpression)
+        cleaned = cleaned
+            .replacingOccurrences(of: #"\s*\d{1,3}%\s*(?:used|left)\s*$"#, with: "", options: .regularExpression)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Infrastructure/Claude/ClaudeUsageProbe.swift` around lines 708 - 712,
The regex that strips trailing "NN% used/left" in ClaudeUsageProbe.swift
requires a whitespace before the percent block, so cases like "...27% used" (no
extra space) won't match; update the replacingOccurrences call that operates on
the variable `cleaned` to use a regex that allows zero or more spaces before the
percent (e.g., make the whitespace before `\d{1,3}%` optional) so both " 27%
used" and "27% used" are removed reliably, leaving the rest of the parsing
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Sources/Infrastructure/Claude/ClaudeUsageProbe.swift`:
- Around line 708-712: The regex that strips trailing "NN% used/left" in
ClaudeUsageProbe.swift requires a whitespace before the percent block, so cases
like "...27% used" (no extra space) won't match; update the replacingOccurrences
call that operates on the variable `cleaned` to use a regex that allows zero or
more spaces before the percent (e.g., make the whitespace before `\d{1,3}%`
optional) so both " 27% used" and "27% used" are removed reliably, leaving the
rest of the parsing unchanged.

In `@Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift`:
- Around line 324-336: Update the test `parses resetsAt when reset and percent
share same line` to assert the specific reset mapping instead of only non-nil:
after calling ClaudeUsageProbe.parse(Self.resetOnSameLineOutput) compare
snapshot.sessionQuota?.resetsAt, snapshot.weeklyQuota?.resetsAt, and
snapshot.quota(for: .modelSpecific("sonnet"))?.resetsAt against the expected
values derived from the fixture (or assert their resetText strings match the
distinct expected reset strings) so the test verifies weekly vs sonnet reset
dates are parsed differently and not accidentally shared.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5eeb6f9-f330-4881-97f7-4613755bdc26

📥 Commits

Reviewing files that changed from the base of the PR and between 8b17283 and f447cec.

📒 Files selected for processing (2)
  • Sources/Infrastructure/Claude/ClaudeUsageProbe.swift
  • Tests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift

@frankhommers
Copy link
Copy Markdown
Contributor Author

CI failure is pre-existing (not related to this PR)

Both checks fail with the same Unexpected duplicate tasks error on the SwiftTerm Metal shader:

error: Unexpected duplicate tasks
    note: Target 'SwiftTerm_SwiftTerm' (project 'SwiftTerm') has compile command with input '.../SwiftTerm/Sources/SwiftTerm/Apple/Metal/Shaders.metal'
    note: Target 'SwiftTerm_SwiftTerm' (project 'SwiftTerm'): MetalLink .../default.metallib

This reproduces on main as well — the CI runners have been updated to Xcode 26.2/26.3 since the last green build on Apr 1, and the newer Xcode build system flags SwiftTerm's Metal shader as a duplicate task. Any PR against main will fail with this error until it's resolved.

Possible fixes:

  • Update SwiftTerm to a version compatible with Xcode 26 beta
  • Pin the CI runner to an older Xcode version (e.g., xcode-version: '16.2')

@hanrw hanrw merged commit 515de7d into tddworks:main Apr 15, 2026
1 of 3 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.

2 participants