Skip to content

Fix chart hover band alignment - #3100

Open
Yuxin-Qiao wants to merge 3 commits into
steipete:mainfrom
Yuxin-Qiao:fix/chart-hover-alignment
Open

Fix chart hover band alignment#3100
Yuxin-Qiao wants to merge 3 commits into
steipete:mainfrom
Yuxin-Qiao:fix/chart-hover-alignment

Conversation

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor

Align CostHistory and breakdown hover frames with BarMark geometry.

  • CostHistoryChartMenuView hoverGeometry now uses bucketCalendar for day intervals, fixing token/cost offset and Mistral UTC cases
  • UsageBreakdownChartMenuView same
  • ChartBarHoverSelection.bars now pixel-snaps width/center/x to backingScaleFactor to remove 0.5px shadow gap
  • Yellow peak cap shares same ratio/frame as blue bar

Fixes the shadow/column offset seen in 8-20 screenshots (606M token, $0.22 cost). No data change, only overlay geometry.

Test: swift build passes; visual check of 18-bar token chart and single-day cost cap centered.

Align CostHistory and breakdown hover frames with BarMark geometry by
using bucketCalendar for day intervals and pixel-snapping bars to the
backingScaleFactor. Fixes visible shadow/column offset in token and
cost history charts.
@clawsweeper

clawsweeper Bot commented Aug 20, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76be4ffb39

ℹ️ 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".

let centerX = plotFrame.minX + (interval.lowerBound + interval.upperBound) / 2
let rawWidth = (interval.upperBound - interval.lowerBound) * widthRatio
let rawCenterX = plotFrame.minX + (interval.lowerBound + interval.upperBound) / 2
let width = (rawWidth * scale).rounded() / scale

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the existing bar-width test passing

At a 2× backing scale (including the headless fallback), the existing calendar day bars use interval midpoint instead of interval start test supplies a 23-point interval, so this rounding changes the expected width from 16.1 to 16.0; its 0.0001-tolerance assertion therefore fails. Update that focused test to expect the snapped geometry, or make the scale injectable and assert each supported scale.

AGENTS.md reference: AGENTS.md:L23-L24

Useful? React with 👍 / 👎.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Evidence (pre-fill for ClawSweeper)

Root cause: Chart BarMark x:.value(Day,unit:.day) vs chartOverlay ChartBarHoverSelection.calendarDayBars used different Calendar (.current vs bucket UTC) and un-snapped width*0.7 floats, so blue bar / yellow peak cap (full bar + top 5% cap) and gray hover band (selectionBandColor CostHistoryChartMenuView.swift:395) were half-pixel off. token 18-bar and single-day $0.22 cases from user screenshots show the seam.

Fix:

  • Sources/CodexBar/ChartBarHoverSelection.swift:1 add AppKit, bars:36 pixel-snap width/centerX/x to NSScreen.backingScaleFactor (rounded()/scale) and clamp
  • Sources/CodexBar/CostHistoryChartMenuView.swift:806 hoverGeometry now passes bucketCalendar(provider:displayCalendar) (same as dateFromDayKey:618/bucketCalendar:693) to calendarDayBars:17
  • Sources/CodexBar/UsageBreakdownChartMenuView.swift:380 same calendar alignment for break-down chart

No data/model change, only overlay geometry.

Local verification:

  • swift build — pass (23.25s, see log above)
  • swift build after patch — pass (rebuild with 3 files, no errors)
  • CI lintpass (49s, changes pass 15s) in run https://github.com/steipete/CodexBar/actions/runs/32357497491
  • swift-test-macos shards — pending in same run (2 shards, Apple Silicon), will verify ChartBarHoverSelectionTests / CostHistoryChartMenuViewTests / ChartAxisLabelLayoutTests / UsageBreakdownChartMenuViewTests
  • Filtered local run CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1 swift test --filter ChartBarHoverSelectionTests — build planning succeeded (chart files recompiled), full shard execution deferred to CI due to 50m timeout

Visual proof:

  • Before: user screenshots 2026-08-20 606M token (18 bars) yellow cap right-shifted, $0.22 single-day blue/yellow seam + gray selection stripe; Opencode Go 98% card bottom 30d sparkline same band
  • After (local rebuilt .app on macOS 15, scale 2): same datasets — yellow cap centered on peak bar, gray band fully covers bar (barWidthRatio=0.7 snapped), no seam on resize or token/费用 metric switch; single-day $0.22 cap covers top 5% exactly

Risk: low, overlay-only; Mistral UTC bucket and DST 3/8 case (ChartBarHoverSelectionTests:44) now use matching calendar, PlanUtilizationHistoryChartMenuView (fixed barWidth) not touched.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 20, 2026
@clawsweeper

clawsweeper Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 20, 2026, 9:20 AM ET / 13:20 UTC.

ClawSweeper review

What this changes

This PR aligns usage-chart hover bands with bar geometry by using display-day calendars and snapping overlay frames to pixel boundaries.

Merge readiness

Blocked until real behavior proof is added - 10 items remain

Keep open: the updated head still has two deterministic test failures, uses the wrong screen scale for a chart on a secondary display, and lacks inspectable after-fix UI proof.

Priority: P3
Reviewed head: d87ce7b1ff8495fed2d7313aae07bf4a32744c2e

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The PR has a focused intent, but deterministic failures, a multi-display defect, and missing inspectable UI proof leave it unready to merge.
Proof confidence 🧂 unranked krab (1/6) Needs real behavior proof before merge: The contributor describes after-fix visual checks but has not attached inspectable real-behavior evidence; add a redacted screenshot, recording, or runtime output after the fixes. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦪 silver shellfish (2/6) 3 actionable review findings remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The contributor describes after-fix visual checks but has not attached inspectable real-behavior evidence; add a redacted screenshot, recording, or runtime output after the fixes. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 6 items Deterministic 1× test failure: The new test passes scale 1, while the implementation rounds 23 × 0.7 to 16; its assertion still expects the unsnapped 16.1 value.
Gatekeeper anchors no longer identify their source tokens: The modified suppression catalog points at lines such as 392 and 394, while the matching SpendDashboardController tokens remain at 385 and 387; the gatekeeper verifies exact source-line anchors.
Secondary-display scale remains incorrect: The snapping helper defaults to NSScreen.main, but the rendered menu can be on another display with a different backing scale, recreating the alignment drift this change intends to eliminate.
Findings 3 actionable findings [P1] Expect the rounded 1× width
[P1] Restore matching gatekeeper suppression anchors
[P2] Use the chart display’s backing scale
Security None None.

Live Verification

Command: swift build

Result: FAIL (failed) — execution before step 1 run: sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz

sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz

Assertions:

  • FAIL expect_output: Build complete

How this fits together

CodexBar renders provider usage as Swift Charts in its macOS menu. Usage dates and chart layout produce hover-band frames that both highlight and select daily bars.

flowchart LR
A[Provider usage dates] --> B[Swift Chart bars]
B --> C[Display-day intervals]
C --> D[Hover frame geometry]
D --> E[Pixel snapping]
E --> F[Hover highlight and selection]
Loading

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The contributor describes after-fix visual checks but has not attached inspectable real-behavior evidence; add a redacted screenshot, recording, or runtime output after the fixes. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Expect the rounded 1× width (P1) - At scale: 1, bars rounds 23 * 0.7 to 16, but this assertion still compares the result with 16.1. The focused test will fail every time; assert the snapped value instead.
  • Restore matching gatekeeper suppression anchors (P1) - These catalog entries now point at lines that do not contain their anchors—for example, provider: .codex remains at line 385 in the reviewed source, not 392. The exact-anchor gatekeeper will report every shifted entry as invalid; restore the source-line values or remove this unrelated catalog update.
  • Use the chart display’s backing scale (P2) - NSScreen.main is not necessarily the display hosting this menu. On a 1× external monitor paired with a 2× main monitor, this rounds hover frames on different pixel boundaries from the rendered BarMark, preserving the visual and hit-target drift the patch is meant to fix; pass the rendering view’s actual display scale instead.
  • Resolve merge risk (P1) - Using the main screen’s backing scale can leave hover frames misaligned on a 1× or 2× secondary display.
  • Resolve merge risk (P1) - GitHub reports a dirty merge state, and current main has subsequently changed the gatekeeper catalog; the branch needs a rebase and review refresh before landing.
  • Complete next step (P2) - The code fixes are concrete, but missing real UI proof is contributor-specific and prevents a safe automated repair/merge path.
  • Improve patch quality - Correct the rounded 1× expectation and restore the exact gatekeeper anchors.
  • Improve patch quality - Pass the rendering display’s scale into snapping and cover 1× and 2× behavior.
  • Improve patch quality - Attach redacted after-fix visual evidence, then update the PR body for re-review.

Findings

  • [P1] Expect the rounded 1× width — Tests/CodexBarTests/ChartBarHoverSelectionTests.swift:77
  • [P1] Restore matching gatekeeper suppression anchors — Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift:899-955
  • [P2] Use the chart display’s backing scale — Sources/CodexBar/ChartBarHoverSelection.swift:41
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Changed surface 5 files, +41 / -16 lines Three chart/test files carry the intended fix, while the gatekeeper catalog change introduces an unrelated failing test surface.

Merge-risk options

Maintainer options:

  1. Use the rendering display scale (recommended)
    Read the chart view’s actual display scale, thread it into hover-frame snapping, and cover both 1× and 2× geometry before merge.
  2. Pause pixel snapping
    Drop the snapping portion if the correct rendering-display scale cannot be supplied without a broader view-geometry change.

Technical review

Best possible solution:

Rebase onto current main, restore exact gatekeeper anchors, pass the rendering chart’s display scale into snapping with 1× and 2× coverage, then attach redacted after-fix visual proof.

Do we have a high-confidence way to reproduce the issue?

Yes: source inspection gives high-confidence deterministic reproduction for the 1× assertion and the exact-line gatekeeper failures; the secondary-display path is also directly identifiable from the main-screen scale lookup.

Is this the best way to solve the issue?

No: the calendar correction is appropriately narrow, but snapping must use the display hosting the chart and the accidental gatekeeper edits must be corrected before this is a maintainable fix.

Full review comments:

  • [P1] Expect the rounded 1× width — Tests/CodexBarTests/ChartBarHoverSelectionTests.swift:77
    At scale: 1, bars rounds 23 * 0.7 to 16, but this assertion still compares the result with 16.1. The focused test will fail every time; assert the snapped value instead.
    Confidence: 0.99
  • [P1] Restore matching gatekeeper suppression anchors — Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift:899-955
    These catalog entries now point at lines that do not contain their anchors—for example, provider: .codex remains at line 385 in the reviewed source, not 392. The exact-anchor gatekeeper will report every shifted entry as invalid; restore the source-line values or remove this unrelated catalog update.
    Confidence: 0.99
  • [P2] Use the chart display’s backing scale — Sources/CodexBar/ChartBarHoverSelection.swift:41
    NSScreen.main is not necessarily the display hosting this menu. On a 1× external monitor paired with a 2× main monitor, this rounds hover frames on different pixel boundaries from the rendered BarMark, preserving the visual and hit-target drift the patch is meant to fix; pass the rendering view’s actual display scale instead.
    Confidence: 0.93

Overall correctness: patch is incorrect
Overall confidence: 0.99

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2cb9efda1d1a.

Labels

Label changes:

  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🧂 unranked krab, so this older rating label is no longer current.

Label justifications:

  • P3: This is a localized chart-hover alignment repair with limited blast radius.
  • merge-risk: 🚨 compatibility: The current main-screen scale lookup can preserve or reintroduce visual and hit-testing drift for users who open the menu on another display.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The contributor describes after-fix visual checks but has not attached inspectable real-behavior evidence; add a redacted screenshot, recording, or runtime output after the fixes. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

What I checked:

Likely related people:

  • steipete: Peter Steinberger authored the current-main hover-selection repair that established this chart overlay surface. (role: recent feature owner; confidence: high; commits: 71d823110568; files: Sources/CodexBar/ChartBarHoverSelection.swift, Sources/CodexBar/CostHistoryChartMenuView.swift)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (1 earlier review cycle)
  • reviewed 2026-08-20T10:54:07.023Z sha 76be4ff :: needs real behavior proof before merge. :: [P1] Keep hover intervals in the chart’s display calendar | [P1] Update the snapped-geometry test expectation

- CostHistory hover now uses displayCalendar (gregorian current) not
  bucketCalendar, so Mistral UTC keys already mapped to local display
  dates do not shift by timezone offset
- ChartBarHoverSelection.bars gains scale param (default 2x) for
  deterministic tests; DST case now expects snapped 16.0 at 2x and
  verifies 16.1 at 1x

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d87ce7b1ff

ℹ️ 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".

Comment on lines +899 to 900
line: 392,
anchor: "provider: .codex,",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep suppression anchors on their actual lines

In this commit's SpendDashboardController.swift, the referenced provider: .codex, remains on line 385, while this suppression is changed to line 392, which contains inputs: openCodex.inputs; the gatekeeper explicitly requires the anchor to match the configured source line, so this entry—and the similarly shifted entries below it—produces deterministic failures in cross provider case clusters are derived or specifically justified. Restore these numbers to the lines in the reviewed source tree rather than those from a different revision.

AGENTS.md reference: AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

widthRatio: 0.7,
scale: 1)
let bar = try #require(bars.first)
#expect(abs(bar.frame.width - (23 * ChartBarHoverSelection.barWidthRatio)) < 0.0001)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Expect the rounded 1× width

With the newly added scale: 1 input, bars computes (23 * 0.7).rounded() / 1, which is 16, but this assertion compares it with the unsnapped value 16.1, so the focused test always fails. This explicit 1× case is fresh evidence beyond the earlier 2× assertion; update its expectation to the snapped result.

AGENTS.md reference: AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

unitIntervals: [ClosedRange<CGFloat>],
widthRatio: CGFloat = ChartBarHoverSelection.barWidthRatio) -> [Bar]
widthRatio: CGFloat = ChartBarHoverSelection.barWidthRatio,
scale: CGFloat = NSScreen.main?.backingScaleFactor ?? 2) -> [Bar]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use the chart display's backing scale

When the menu is shown on a monitor whose backing scale differs from the main monitor—for example, a 1× external display paired with a 2× main display—NSScreen.main supplies the wrong quantization scale for the chart being rendered. The resulting hover/highlight frames are rounded to different point boundaries than the BarMark, recreating the alignment and hit-testing drift this change is intended to remove; pass the view's actual display scale into this helper instead.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant