Skip to content

refactor(cli): switch compare to relayburn_sdk::normalize_since#421

Merged
willwashburn merged 2 commits into
mainfrom
claude/resolve-github-issue-YkoeY
May 12, 2026
Merged

refactor(cli): switch compare to relayburn_sdk::normalize_since#421
willwashburn merged 2 commits into
mainfrom
claude/resolve-github-issue-YkoeY

Conversation

@willwashburn
Copy link
Copy Markdown
Member

Summary

  • burn compare was the only presenter still carrying its own normalize_since + Hinnant civil-date math. The SDK exposes relayburn_sdk::normalize_since and the sibling summary / hotspots presenters already use it.
  • Switch compare.rs over and delete the local helpers (normalize_since, parse_relative, normalize_iso_to_utc_z, format_iso_z_ms, format_iso_z_ms_signed, ymd_to_days, days_to_ymd) plus their unit tests. Net change: +3 / -383 LOC in compare.rs.
  • util/time.rs:civil_from_days is still referenced by iso_now / iso_from_system_time (used in harnesses/claude.rs) so it stays.

Test plan

  • cargo test --workspace — 774 tests pass, including tests/golden.rs::golden_diff_against_cli_snapshots which is the gate the issue called out for --since regressions.
  • cargo build -p relayburn-cli.

Closes #333

https://claude.ai/code/session_01VFvKstujMoXwZkP5vwrHxQ


Generated by Claude Code

`burn compare` was the only presenter still carrying its own
`normalize_since` + Hinnant civil-date math; the SDK has exposed
`normalize_since` for a while and the sibling `summary` and `hotspots`
presenters already use it.

Delete the local helpers and their tests, drop ~380 LOC of duplicated
date plumbing. The cli-golden snapshot still matches.

Closes #333

https://claude.ai/code/session_01VFvKstujMoXwZkP5vwrHxQ
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a2c1fbf2-cc30-4b07-bef2-291098e55f0b

📥 Commits

Reviewing files that changed from the base of the PR and between 802d776 and 3fa5cc3.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • crates/relayburn-sdk/src/query_verbs.rs
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Removes the CLI's duplicate normalize_since and imports the SDK version; simultaneously the SDK's normalize_since was reworked to canonicalize inputs to UTC ISO with millisecond precision and expanded tests were added.

Changes

Deduplicate since-normalization logic

Layer / File(s) Summary
SDK import and local code removal
crates/relayburn-cli/src/commands/compare.rs
Import normalize_since from relayburn_sdk and delete the local normalize_since implementation plus supporting helpers for relative parsing, ISO→UTC canonicalization, ISO formatting, and YMD↔days conversion.
Test cleanup and changelog
crates/relayburn-cli/src/commands/compare.rs, CHANGELOG.md
Remove unit tests that validated the deleted local normalization helpers and add an Unreleased changelog entry documenting the alignment of compare with summary/hotspots.

Canonicalize normalize_since in SDK

Layer / File(s) Summary
Docs/update for normalize_since
crates/relayburn-sdk/src/query_verbs.rs
Update normalize_since documentation to require UTC ISO timestamps with millisecond precision (YYYY-MM-DDTHH:MM:SS.mmmZ) and explain lexicographic filtering rationale.
SDK parser, formatter, and calendar helpers
crates/relayburn-sdk/src/query_verbs.rs
Add normalize_iso_to_utc_z, format_iso_z_ms, system_now_secs, and YMD/day conversion helpers; emit relative ranges as .000Z, parse/convert ISO offsets to UTC, and return canonical millisecond-padded UTC strings.
SDK tests for canonicalization and properties
crates/relayburn-sdk/src/query_verbs.rs
Rework and expand tests to assert .000Z emission for relative ranges, whole-second widening, millisecond preservation/truncation/padding, offset-to-UTC correctness, acceptance of date-only/lowercase forms, garbage rejection, lex-order compatibility, and ymd/day round-trip.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • AgentWorkforce/burn#401: Modifies SDK time/ISO handling in query_verbs with a different approach (collapsing civil-date implementations onto the time crate).
  • AgentWorkforce/burn#314: Introduced the original local ISO/relative normalize_since implementation in compare.rs that this PR removes.

Poem

🐰 I nibbled duplicate lines away,
One normalize to rule the day.
UTC ticks in tidy rows,
Millis keep the ledger's prose.
A clean hop forward — hip hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor(cli): switch compare to relayburn_sdk::normalize_since' directly and concisely describes the main refactoring change: migrating the compare command from local normalize_since to the SDK version.
Description check ✅ Passed The description clearly explains the refactoring scope, deletions, test verification, and rationale—all directly related to the changeset.
Linked Issues check ✅ Passed The PR fully addresses issue #333 objectives: switched compare.rs to relayburn_sdk::normalize_since, removed ~380 LOC of local helpers and tests, confirmed civil_from_days necessity, and verified golden tests passed.
Out of Scope Changes check ✅ Passed All changes are scoped to issue #333: refactoring compare.rs to use SDK normalize_since, removing local helpers, and auditing util/time.rs. No unrelated changes are present.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/resolve-github-issue-YkoeY

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

if let Some(s) = normalize_since(args.since.as_deref())? {

P1 Badge Keep canonical UTC millisecond cutoff for --since

Switching compare to relayburn_sdk::normalize_since regresses timestamp filtering correctness for common --since inputs. The SDK helper currently returns relative ranges in second precision (...SSZ) and passes ISO strings through after only a YYYY-MM-DD shape check (query_verbs.rs), so values like 2026-05-06T00:00:00-07:00 are no longer normalized to UTC and values like ...00Z no longer widen to ...00.000Z. Because ledger filtering is lexicographic on ts >= ?, this can silently exclude same-second rows with milliseconds and misorder offset timestamps, changing burn compare results.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@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.

Actionable comments posted: 1

🤖 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 `@CHANGELOG.md`:
- Around line 9-11: The changelog entry for `relayburn-cli` currently includes
internal implementation detail ("uses relayburn_sdk::normalize_since" and "Drops
~250 LOC of duplicated date math"); update the `CHANGELOG.md` bullet for
`relayburn-cli: burn compare` to be user-focused by removing references to
`relayburn_sdk::normalize_since` and the LOC drop and instead describe the
user-visible impact (e.g., "improved date handling to match summary and
hotspots" or similar), keeping the entry concise and impact-first.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 35803fba-ea64-4f92-8232-b3600ac586a5

📥 Commits

Reviewing files that changed from the base of the PR and between 67d7255 and 802d776.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • crates/relayburn-cli/src/commands/compare.rs

Comment thread CHANGELOG.md Outdated
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

build_compare_table, has_minimum_fidelity, load_pricing, provider_for, summarize_fidelity,
AnalyzeCompareOptions as CompareOptions, CompareCell, CompareTable, EnrichedTurn,
FidelityClass, FidelitySummary, Ledger, LedgerOpenOptions, ProviderFilter, Query,
build_compare_table, has_minimum_fidelity, load_pricing, normalize_since, provider_for,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 SDK normalize_since omits millisecond suffix, breaking lexicographic ts >= filtering against .000Z ledger rows

The old local normalize_since formatted relative-range cutoffs as YYYY-MM-DDTHH:MM:SS.000Z (24 chars, with milliseconds). The SDK's replacement normalize_since (crates/relayburn-sdk/src/query_verbs.rs:77) uses format_iso_z which emits YYYY-MM-DDTHH:MM:SSZ (20 chars, no milliseconds). The ledger stores timestamps with .000Z precision (crates/relayburn-sdk/src/ledger/schema.rs:47ts TEXT NOT NULL), and the query filter at crates/relayburn-sdk/src/ledger/reader.rs:185 uses ts >= ? which is a lexicographic comparison in SQLite. At character position 19, . (0x2E) sorts before Z (0x5A), so "2026-05-05T12:00:00.000Z" < "2026-05-05T12:00:00Z" — meaning rows at the exact cutoff second (and any sub-second variants like .500Z) are incorrectly excluded. This shifts the effective --since boundary by ~1 second for every relative range (7d, 24h, etc.). The deleted tests at old lines 1289–1296 (normalize_since_relative_emits_milliseconds) and 1308–1317 (normalize_since_relative_format_is_lex_compatible_with_ledger_rows) specifically guarded against this regression.

Prompt for agents
The SDK's normalize_since (in crates/relayburn-sdk/src/query_verbs.rs, function format_iso_z around line 124) formats relative-range cutoffs as YYYY-MM-DDTHH:MM:SSZ without milliseconds. Ledger rows use .000Z format, and the SQL filter ts >= ? is lexicographic, so ...SS.000Z sorts before ...SSZ causing rows at the cutoff second to be incorrectly excluded. The fix should be in the SDK's format_iso_z to emit .000Z (matching the ledger format), which would fix this for compare, summary, hotspots, and sessions_list simultaneously. Change the format_description macro in format_iso_z to include [subsecond digits:3] or manually append .000Z.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

build_compare_table, has_minimum_fidelity, load_pricing, provider_for, summarize_fidelity,
AnalyzeCompareOptions as CompareOptions, CompareCell, CompareTable, EnrichedTurn,
FidelityClass, FidelitySummary, Ledger, LedgerOpenOptions, ProviderFilter, Query,
build_compare_table, has_minimum_fidelity, load_pricing, normalize_since, provider_for,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 SDK normalize_since passes ISO timestamps with timezone offsets through raw, causing wrong lexicographic cutoff

The old local normalize_since fully parsed ISO timestamps and re-emitted them as UTC (...Z), converting offsets like -07:00 to the correct UTC instant (e.g., 2026-05-06T00:00:00-07:002026-05-06T07:00:00.000Z). The SDK's replacement (crates/relayburn-sdk/src/query_verbs.rs:83-86) only checks looks_like_iso (a YYYY-MM-DD prefix check) and returns the raw string as-is. When a user passes --since "2026-05-06T00:00:00-07:00" to burn compare, the raw string is compared lexicographically against ledger ts values (at crates/relayburn-sdk/src/ledger/reader.rs:185). The -07:00 suffix causes the comparison to treat the cutoff as midnight UTC instead of 7am UTC — a 7-hour discrepancy that includes or excludes the wrong turns. The deleted tests at old lines 1243–1260 (normalize_iso_converts_negative_offset_to_utc / normalize_iso_converts_positive_offset_to_utc) specifically verified this offset-to-UTC conversion.

Prompt for agents
The SDK's normalize_since (crates/relayburn-sdk/src/query_verbs.rs:80-86) passes ISO strings through without converting timezone offsets to UTC. This causes incorrect lexicographic comparison in the SQL filter ts >= ? when users provide offset timestamps like 2026-05-06T00:00:00-07:00. The fix should be in the SDK's normalize_since to parse ISO timestamps with offsets and re-emit as UTC ...Z format (similar to the deleted normalize_iso_to_utc_z in compare.rs). This would fix the issue for compare, summary, hotspots, and all other commands using build_query. Alternatively, the deleted offset-normalization code from compare.rs could be moved into the SDK's normalize_since.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Codex / Devin / coderabbit flagged a latent regression on PR #421:
the SDK's `normalize_since` returned relative ranges as `...SSZ` (no
millis) and passed ISO inputs through unchanged. Because the ledger
filter is `ts >= ?` against `.mmmZ` rows in SQLite (lex-compared),
this silently dropped same-second turns (`.` < `Z`) and misordered
offset-bearing cutoffs (e.g. `-07:00` sorting before any UTC row).

This was already true for `burn summary` and `burn hotspots`; PR #421
just made `burn compare` adopt the same behavior. The fix is in the
SDK so all three presenters get consistent, correct date filtering:

- Relative ranges emit canonical `YYYY-MM-DDTHH:MM:SS.000Z` (24 chars).
- ISO inputs parse and re-emit as UTC `.mmmZ`: offsets get converted
  (`...-07:00` → `...07:00:00.000Z`), no-fraction widens to `.000Z`,
  sub-ms truncates to ms, `Z`/`z` and date-only forms supported.
- Garbage errors out (no more `looks_like_iso` permissive pass-through).

Tests cover each shape and the lex-compare property against ledger rows.
Also rewords the CHANGELOG bullet to be user-impact-first per
coderabbit's note.

https://claude.ai/code/session_01VFvKstujMoXwZkP5vwrHxQ
@willwashburn willwashburn merged commit ddc957f into main May 12, 2026
11 checks passed
@willwashburn willwashburn deleted the claude/resolve-github-issue-YkoeY branch May 12, 2026 03:13
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.

Rust refactor: switch compare.rs to relayburn_sdk::normalize_since

2 participants