chore(deps): Bump better-auth from 1.4.10 to 1.6.11 in /web#1
Open
dependabot[bot] wants to merge 1 commit into
Open
chore(deps): Bump better-auth from 1.4.10 to 1.6.11 in /web#1dependabot[bot] wants to merge 1 commit into
dependabot[bot] wants to merge 1 commit into
Conversation
public-repo-publish Bot
pushed a commit
that referenced
this pull request
Jun 6, 2026
…inned-tooltip survival fixes (#447) * [testing] Rename seed_may_data → seed_updated_data May was the month it was written; rename to a stable name that won't become misleading. Updates project name (maySeededData → updatedSeededData), run name prefix (may-seed → updated-seed), and usage docs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend] Show un-smoothed value in tooltip alongside smoothed value Adds a synthetic "Raw Value" column to the chart tooltip that appears only when smoothing is active (the chart has an `(original)` companion series). When smoothing is off, the tooltip is unchanged — single Value column. Also fixes a long-standing bug where the existing inline `value (raw)` rendering never fired: `buildSeriesConfig` overrides uPlot's `series.label` to `runId`, so the suffix check on `series.label` for ` (original)` always failed. The detection now reads `lineData.label` (the un-overridden original) and keys the rawValues map by `series.label` (which is the same for main + companion of a given run). Default tooltip column order updated to: Run Name | Series ID | Metric | Value | Raw Value (Series Name still available, disabled by default). Storage key bumped to v2 so existing saved configs roll forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Tooltip raw value: unit tests + restructured E2E spec Unit tests cover insertRawValueColumn (column-order contract) and formatRawValueContent (cell render contract incl. flag-takes-precedence and stale-style cleanup). Both helpers exported for testability. E2E spec rewritten to use forEachChartLocation: - Smoothing-on header check across NON_FS_LOCATIONS (6 locations). - Smoothing-off header check across AR-C + IR-C. - Numeric-raw-cell regression check across AR-C + IR-C — guards the series.label-vs-lineData.label bug fixed in this branch. 10 E2E tests total; 11 unit tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Replace polling loops with locator.waitFor; expand tooltip raw value spec to ALL_LOCATIONS Apply Gemini PR-446 review feedback (#2, #3, #4) across the chart e2e suite to align with the project styleguide rule against hardcoded arbitrary timeouts (.gemini/styleguide.md:328). #2 — pin-tooltip polling loop replaced with locator.waitFor in: - tooltip-columns.spec.ts - tooltip-pinned-highlight.spec.ts - tooltip-hide-show.spec.ts - tooltip-raw-value.spec.ts #3 — switch-toggle hardcoded waits replaced with expect.toHaveAttribute in: - hidden-run-sync.spec.ts (display-only-selected toggle) - tooltip-raw-value.spec.ts (smoothing toggle) #4 — tooltip-raw-value.spec.ts: read RAW VALUE cell by header index, not positional offset. Important because the recent column reorder (Display ID → Series ID, repositioned) means cells.length-1 silently mapped to the wrong cell on main, making test 3 falsely pass. Test scope expanded: - test 1 (header includes VALUE + RAW VALUE): NON_FS_LOCATIONS → ALL_LOCATIONS (6 → 12) - test 3 (raw cell is numeric): NON_FS_LOCATIONS → ALL_LOCATIONS (6 → 12) - test 2 (header omits RAW VALUE off): stays LOCATIONS_CHARTS_TAB (toggle is occluded by FS modal) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Fix flaky step-sync-media-types test (count race) scrollUntilVisible only waits for ONE step slider, but media widgets mount lazily — the audio/video step navigators usually render a beat after the histogram one. Counting immediately after the first slider becomes visible returned sliderCount=1 on slow CI, throwing "SKIPPED: need 2+ step navigators" even though both sliders would be present a moment later (visible in the failure screenshot). Fix: use the existing waitForStepNavigators(page, 2) helper, which calls expect.toHaveCount(2) under the hood and lets Playwright auto-retry until both sliders mount. Pre-existing flake unrelated to this branch's tooltip changes; bundled because the same PR is touching e2e helpers and the fix is one line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Fix tooltip-columns localStorage key + media sync wait semantics Two corrections from #447 review: 1. tooltip-columns.spec.ts referenced the pre-bump localStorage key (uplot-tooltip-columns) in beforeEach cleanup and the saved-config readback. Since the v2 storage key bump in this branch, the cleanup was a no-op and the readback returned null (silently passing the default). Updated both call sites to uplot-tooltip-columns-v2. 2. step-sync-media-types.spec.ts was using waitForStepNavigators(page, 2) which calls expect.toHaveCount(2) under the hood — exact match. The dashboard mounts up to 4 step navigators (audio + video + images + histogram) so the count never stably equals 2 → "skipped" thrown. Replaced with sliders.nth(1).waitFor({ state: "visible" }), which is "≥ 2" by construction and doesn't constrain the upper bound. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend][testing] Revert tooltip column default-order change The original PR re-ordered the tooltip column defaults (Run Name first, "Display ID" → "Series ID" relabeled and moved to second) and bumped the storage key to v2 to force existing users onto the new default. That turned out to be unnecessary: drag-to-reorder still works in the column header, so users who want a different order can do it themselves without the codebase changing the default for everyone. Reverts: - ALL_COLUMNS order back to: Display ID | Run Name | Metric | Value (Series Name remains in the list, enabled: false) - TOOLTIP_COLUMNS_KEY back to "uplot-tooltip-columns" (no v2 bump) - localStorage cleanup keys in tooltip-raw-value.spec.ts and tooltip-columns.spec.ts back to the original key Note: there is currently no UI to toggle Series Name on/off — the +Add button was removed in PR #373 (commit d9e46b17) as a fix for header grid alignment, leaving disabledColumns and addColumnDropdownOpen as dead code. Out of scope for this PR. Unit tests use generic column ids, so they continue to pass without modification. E2E tests assert on header presence, not order, so they continue to pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend][testing] Remove dead Series Name column + leftover toggle UI scaffolding PR #373 dropped the per-column show/hide UI from the tooltip column header (drag grips, × buttons, +Add) to fix a grid-alignment bug, but left behind dead code: the `name` ("Series Name") column had no way to be re-enabled by users (default was enabled: false), and the toggle scaffolding was retained as orphan vars and helpers. Cleaning up: tooltip-plugin.ts: - Drop "name" from TooltipColumnId, ALL_COLUMNS, DEFAULT_COL_WIDTHS. - Drop the `case "name"` rendering branch in createTooltipRow. - Drop nameSpan from the row-cache type and the fast-path cacheEntry type, plus the 6 dead `if (cached.nameSpan) ...fontWeight = ...` highlight-bold updates that were already guard-protected no-ops. - Drop nameColIdx + its cacheEntry assignment. - Drop nameSpan from the search-filter chain (the column was never rendered, so its textContent was always undefined). - Drop addColumnDropdownOpen flag, closeAddDropdown helper + 3 callsites, disabledColumns variable, and the stale "Settings panel replaced by Neptune-style column headers with +Add dropdown" comment. tooltip-plugin.test.ts: - Re-frame the "raw-value goes after Series Name" test as "raw-value goes after a user-reordered run-id" — same property (raw-value always last), Series Name no longer exists. Migration: existing users with `name: enabled: true` saved in localStorage get that entry silently dropped on next load — the init merge logic looks up each saved id in ALL_COLUMNS via .find() and skips entries with no def. No localStorage-key bump needed. Default order for a fresh user remains: Display ID | Run Name | Metric | Value (with Raw Value auto-appended when smoothing is on). Identical to what main ships, just without the unreachable 5th column. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend][testing] Add Min/Max tooltip columns + gear popover + FS legend min/max Three coupled features extending the Raw Value tooltip work: PART A — Restore the per-column show/hide UI as a gear popover. PR #373 removed the inline drag-grip / × / +Add controls because they widened the column-header grid and broke data-row alignment. The new gear icon (⚙) lives at the top-right of the pinned tooltip OUTSIDE the header grid (sibling to the close button), and clicking it opens a popover that lists the toggleable columns with checkboxes. The popover is appended to document.body so it can extend past the tooltip's bounds. Click-outside / Escape dismiss it. The plugin's "click outside pinned tooltip unpins" handler (handleDocumentMouseDown) now also treats clicks inside the popover as "inside the tooltip" so toggling a checkbox doesn't accidentally un-pin the parent tooltip. Escape similarly closes the popover first without unpinning. PART B — Min and Max as new optional tooltip columns. Both columns default to disabled in ALL_COLUMNS; users opt in via the gear popover. Detection mirrors the Raw Value pattern: walk uPlot's series, identify env_min / env_max companions via lineData.envelopeOf + envelopeBound, key the resulting Min/Max maps by the parent's series.label (the runId-overridden uPlot label, same key Raw Value uses). For series without envelope companions (raw individual-run charts) the maps stay empty for that label and the cell renders an em-dash. Cache adds minSpan / maxSpan; fast-path updates the new spans alongside rawValueSpan in all 4 sites. PART C — Fullscreen-sidebar legend numeric expansion + show-min/max toggle. The series.value formatter in series-config.ts now emits a "·"-separated list of bare numbers (no min=/max= labels) matching a new column-header strip rendered ABOVE the moved uPlot legend in the FS sidebar. The strip's labels adapt to smoothing state ("Raw Value" shown only when any series has _hasOriginal — a tag I added to series objects in buildSeriesConfig) and to the new toggle. The FS toggle is now a boolean (include/exclude min+max), not a min-vs-max switch. State persists in localStorage as "fullscreen-legend-show-minmax" ("true" | "false"; default false). The button label is "min/max" when off and "min/max ✓" when on. Clicking flips legendShowMinMaxRef.current and forces u.redraw(false, true) to refresh the legend cells. Header strip is right-aligned to match the uPlot legend's value column. Coverage: - 5 new vitest cases for formatMinMaxContent (16 total). - 6 new E2E tests for popover behavior + Min/Max columns (93 total). - Live browser walkthrough of all 6 acceptance sub-checks (a-f) passes. Persistence semantics: - Tooltip column config: existing "uplot-tooltip-columns" key (unchanged). ALL_COLUMNS gains min/max entries; the init merge logic at tooltip-plugin.ts:33-55 silently appends them to existing saved configs. - FS legend min/max toggle: new "fullscreen-legend-show-minmax" key. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend][testing] Fix tooltip horizontal-scroll alignment + add Min/Max + FS-legend specs Scroll/alignment fix: - Move the column-header strip INSIDE [data-tooltip-content] (the scroll container) as the first child, with position: sticky; top: 0. Previously the header was a sibling of the rows container, so any horizontal scroll on the rows desynced their cells from the header labels. Sticky-top keeps the header pinned during vertical scroll. - Match the header's background to hsl(var(--popover)) (same as the surrounding tooltip) so it blends instead of reading as a black bar. - Add min-width: max-content to both the header wrapper and the data rows so each row's box equals its grid's natural width — without this, the highlighted background only painted across the visible scroll-clipped width when columns overflowed. - Enable overflow-x: auto on data-tooltip-content so a horizontal scrollbar appears when the user resizes the tooltip narrower than the column total; both header and rows now scroll together. Tests added: - tooltip-raw-value.spec.ts (+6 across LOCATIONS_CHARTS_TAB): - Test G: clicking popover checkboxes does not unpin the tooltip (regression for the handleDocumentMouseDown body-portal click bug). - Test H: Escape closes the popover first; second Escape unpins. - Test I: Min/Max column toggles persist across page reloads. beforeEach uses a sessionStorage flag so localStorage clears once per test rather than on every navigation — otherwise reload would wipe the state we're verifying in Test I. - fs-legend-minmax.spec.ts (new, 6 tests): - Default header reads "Value · Raw Value" (×2 FS locations). - Min/max toggle flips header AND cell format (×2 FS locations). - Toggle state survives page reload (×1 FS location). - Smoothing-off + toggle-on shows "Value · Min · Max", no Raw Value. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend] Fix tooltip jumping on pin in fullscreen mode Radix DialogContent applies `transform` for centering, which establishes a containing block for `position: fixed` descendants. When pinTooltip reparents tooltipEl from document.body INTO the dialog (so Radix's focus scope allows the search input to receive keystrokes), the tooltip's left/top coords flip from viewport-relative to dialog-relative — making the tooltip visually jump on pin. Fix: capture getBoundingClientRect() before and after the reparent, compute the delta, and adjust style.left/style.top so the visual position stays pixel-identical. Same compensation applied in reverse in unpinTooltip when the tooltip moves back to document.body, so the unpin doesn't briefly flash the tooltip at the wrong spot before hover-following kicks back in. Verified with Playwright: BEFORE pin (left=576, top=474, parent=BODY) → AFTER pin (left=576, top=474, parent=dialog-content). dx=0, dy=0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend] Simplify FS legend + fix resize-handle flicker - Remove min/max from FS legend (kept in tooltip popover only) - Switch to compact "Value (Raw)" parens format (matches main) - rAF-throttle sidebar drag and pause moveLegend interval mid-drag to eliminate the per-mousemove React re-render + uPlot redraw stutter Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend] Stop destroying uPlot on every FS sidebar drag tick Root cause: useChartLifecycle's main effect had width/height in its deps array, and its cleanup unconditionally destroyed the chart. When the user dragged the resize handle, ResizeObserver fired ~5x/sec, each tick fired React's cleanup → chart.destroy() → null refs → effect body fell through the early-return (chartRef was null) → full uPlot recreation. The legend table briefly vanished from the sidebar each tick. Fix has two parts: 1. Track upcoming dim-only changes during render via isDimsOnlyChangeRef. The main cleanup checks this ref and skips destroy/listener teardown when the next run will hit the early-return. Listeners stay attached to the still-alive chart. 2. Add a separate unmount-only useEffect with [] deps for guaranteed chart destroy when the component actually unmounts. Also drop the in-drag setSidebarWidth and the moveLegend pause from the dialog — those were workarounds for the recreation, no longer needed now that resize is cheap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Regression tests for the tooltip + FS legend bugs found in this PR Unit (tooltip-plugin.test.ts, +7): - formatValueContent ~prefix when isInterpolated is true (italic, opacity 0.6) - formatValueContent: flagText overrides interpolated rendering - formatValueContent: stale interpolation styles cleared on next render - formatRawValueContent / formatMinMaxContent: rowHidden → "hidden" warning E2E (web/e2e/specs/charts/): - fs-legend-resize.spec.ts (new, 5 tests, [AR-C-FS]) * legend rows do not blank out during resize drag — guards against use-chart-lifecycle's cleanup destroying uPlot on every dim change * .uplot root has zero DOM mutations during drag — stronger sensor * sidebar width persists across reload via localStorage * pin tooltip does not jump on transform-containing-block reparent * FS popover stacks above tooltip rows (Max checkbox click hits the checkbox, not the MAY-16 row underneath) - tooltip-interpolated-value.spec.ts (new, 1 test, [AR-C]) * cursor at off-cadence step → tooltip shows ~prefixed value for the sparse run, plain value for the dense run - tooltip-raw-value.spec.ts (extended, +3 tests × 2 locations) * column header strip and rows share the same scrollLeft (sticky-top inside the scrollable content, not a sibling) * highlighted row's box width ≥ grid scrollWidth (min-width: max-content so the blue highlight covers all columns when scrolled) * hidden series propagates "hidden" into Value, Raw Value, Min, Max cells Seed (web/server/tests/setup.ts §5d-bis): - interp-dense-cadence + interp-sparse-cadence runs sharing interp/loss metric at every-1-step vs every-50-steps cadence, in smoke-test-project, far past createdAt so they don't auto-select. Used by the tooltip-interpolated-value E2E test. Also export formatValueContent for unit testing (was internal-only). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Expand E2E location coverage for hide / Min-Max / FS-resize / Escape - Min/Max numeric cells: LOCATIONS_CHARTS_TAB → AR_LOCATIONS (6 AR cells including Dashboard and FS) since AR multi-run charts have envelope companions in every AR context. - Hide propagation: LOCATIONS_CHARTS_TAB → ALL_LOCATIONS (12) and switch the hide mechanism from chart-legend click to tooltip-row click — tooltip-plugin row click toggles series.show in every location, so the test now exercises FS, dashboards, and IR uniformly. - FS-resize specs (legend rows persist, no .uplot mutations, pin no-jump, popover no-click-through): ["AR-C-FS"] → FS_LOCATIONS (6 cells) so the fix in use-chart-lifecycle is verified across multi-run, single-run, charts-tab, and dashboard FS contexts. - New: [AR-C-FS] Escape priority 3-level test (popover → tooltip → dialog) — the dialog's onEscapeKeyDown was untested. Net E2E count for the affected files: 58 → 85 tests across 3 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Sync E2E test improvements from PR 450 onto ryandev16 Brings the four new E2E spec files in line with the work iterated on PR 450 (the e2e-tests-only draft). Includes: - Remove low-signal tests: * fs-legend-resize "Legend rows persist throughout drag" × 6 — too weak a sensor; falsely passed in 5/6 locations even when the chart-recreation bug was firing. * fs-legend-minmax "No Min/Max toggle button" × 1 — defensive guard for a button that never existed on main. * fs-legend-resize "Sidebar width persistence" × 1 — defensive guard for an existing feature, flaky in CI on post-reload scrollIntoView. - Convert all test.skip() guards to descriptive expect() failures so a missing prerequisite (gear icon, tooltip, popover) shows up as a diagnosable failure rather than silently skipping. - Fast-fail timeouts on gear-icon clicks (2s) and setColumnEnabled (try/catch with 2s on click + 2s on popover wait) so missing-feature failures resolve in seconds instead of consuming the action timeout. - Force-narrow the pinned tooltip to 300px in the scroll-alignment and highlight-coverage tests so the grid actually overflows; otherwise on wide viewports those checks were vacuously skipping. - Per-test testTimeout values (30s default, 60s for reload-persistence, 45s for the smoothing-toggle test) so a hung test fails in seconds instead of consuming the project-level 6-min timeout. NOT included: retries=0. PR 450 set retries=0 to avoid wasted CI time on tests guaranteed to fail on main; PR 447 keeps the project default (2 retries) since the fixes here should make every test pass. Net new test count for the 4 specs: 89 → 81. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Fix three test-side bugs uncovered by the column-header DOM move PR 447 moved the tooltip column-header strip inside [data-tooltip-content] so it scrolls with the rows. The header's inner row also has display:grid, which broke selectors that assumed only data rows match. Fixed all three helpers + one inline scan + one inline click target. Also restricted the highlighted-row test to AR-C only and re-applied the metric filter after the persist-reload test's reload. Group A — column-header counted as a row (96 failures across 3 specs): - tooltip-hide-show.spec.ts: countTooltipRows now filters out elements inside [data-tooltip-column-headers]. Inline locators switched from '[data-tooltip-content] div[style*="grid"]' to direct-child selector '[data-tooltip-content] > div[style*="grid"]' so only data rows match. - tooltip-series-count.spec.ts: contentArea.children.length now skips the column-header wrapper. - tooltip-raw-value.spec.ts: readFirstRowCells, the highlighted-row scan, the hidden-row click target, and the hidden-row reader all filter out elements inside [data-tooltip-column-headers]. Group B — single-series chart has no highlighted row (1 failure): - tooltip-raw-value.spec.ts "Highlighted row covers all columns when scrolled" restricted from LOCATIONS_CHARTS_TAB (AR-C, IR-C) to ["AR-C"]. IR-C charts have a single series so hover-driven focus detection can't pick out one row to highlight. Group C — post-reload chart isn't a line chart (2 failures): - tooltip-raw-value.spec.ts "Min/Max persist across reload" now re-applies searchMetricGroups("metric") after page.reload(). Without this, the histograms group renders first (which has no .uplot .u-over overlay) and scrollIntoViewIfNeeded times out at 10s. Tests removed: 0. Tests scoped down: 1 (highlight-coverage from 2 to 1). Net new tests in PR: 81 → 80. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Fix bucket-alignment flake — filter metric tree before waitForCharts CI run b0sdkbnnhxpl9f9idjhdosbl had this single test fail with "TimeoutError: page.waitForSelector(.uplot canvas): Timeout 30000ms exceeded" while 5 of 6 sibling shards passed it cleanly. The screenshot showed the project page successfully loaded with the `media/*` group expanded and `logs (1)` below — but the train metric group's line charts hadn't attached to the DOM yet. Root cause: `navigateToFirstProject` lands on smoke-test-project which has many metric groups (train/*, media/*, logs/*, distributions/*, interp/*). LazyChart only mounts widgets when they enter the viewport. On unlucky shards where media/logs render at the top, the train line charts (which the test actually needs) live below the fold and never attach within 30s — `.uplot canvas` never matches and the test fails. Fix: apply `searchMetricGroups("metric")` after navigating, before `waitForCharts`. That filters the metric tree to only `train/metric_xx` line-chart groups, so a uPlot canvas attaches immediately. Doesn't change which runs are selected (auto-select stays the same), so the batch-bucketed alignment check still has multi-run data to verify. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend] Apply Gemini review feedback (4 items) 1. Column popover (toggleColumnPopover) now uses CSS-variable theme tokens (hsl(var(--popover)), hsl(var(--border)), hsl(var(--foreground))) instead of hardcoded #1a1a1a/#fff hex pairs — matches the main tooltip element and tracks any future theme-token changes. Drops the now-unused `theme` parameter from the function signature. 2. Extract `compensateFixedPositionAfterReparent(el, desired)` helper for the position-fixed coordinate compensation that was duplicated in pinTooltip, unpinTooltip, and toggleColumnPopover (3 sites). Each helper call replaces ~8 lines of rect-delta math. 3. toggleColumnPopover's click-outside dismiss now uses `anchor.contains(target)` instead of `target === anchor`. Equality works today (button only has a text-node child, which can't be an event target per DOM spec), but breaks the moment anyone wraps the ⚙ glyph in a <span> or swaps it for an SVG. Standard contains-check idiom is future-proof. 4. Extract `collectCompanionValues(u, lines, idx)` returning { rawValues, rawFlags, minValues, maxValues }. The full-rebuild path (updateTooltipContent) and fast-path (updateTooltipValues) had two near-identical ~25-line blocks doing the same companion-series scan. When Min/Max collection was added it had to be done twice; the next numeric column would have hit the same trap. No behavior change. All 792 unit tests pass; typecheck clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend][bugfix] Pinned tooltip survives auto-refresh + route preload Bug A — pinned tooltip vanished every few seconds on RUNNING runs because `useRefreshTime` scheduled its own `setInterval` in parallel with the RefreshButton's existing smart timer, bypassing the button's popup-defer and tab-pause logic. Deleted the redundant timer; the hook is now handler-only (timestamp persistence + manual `handleRefresh`). Bug B — pinned tooltip vanished when hovering any left-sidebar link because TanStack Router prefetch was past the 5s staleTime and recreated the chart underneath. Added a 250ms deferred-unpin protocol in `tooltip-plugin.ts`: destroy() defers clearing the pin; init() cancels the deferral when the same chartId re-mounts. Defends against any chart-recreation cause (refresh, preload, dim change, zoom refetch). Also pinned pnpm@10.9.0 in `web/app/Dockerfile` — the unpinned install was pulling pnpm 11.x, which fails the frontend build over `ERR_PNPM_IGNORED_BUILDS`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [frontend][bugfix] Fix smoothing-clobber race in URL↔IndexedDB sync Customer reports: smoothing toggle would reset itself to ON after a page reload on dashboards that had `?inherited=false` in the URL. Root cause — race in two cooperating useEffects: 1. `useLocalStorage` initializes React state to `defaultValue` synchronously, then async-resolves to the saved IndexedDB value ~150ms later. 2. The URL→setting effect at metrics-display.tsx:84-88 fires on mount during that 150ms window. It calls `updateSettings`, which spreads the closure-captured `settings` (still defaults!) and writes the whole object back to IndexedDB — clobbering the user's saved smoothing preference with the default `enabled: true`. Fix: `useLocalStorage`'s setter now accepts a functional updater `(prev) => T`. The updater receives the freshest persisted value (read directly from IndexedDB at write time), not the stale React closure. `useLineSettings`'s `updateSettings` / `updateSmoothingSettings` use the functional form. The merge always runs against the actual saved state, so partial writes can't clobber unrelated keys. Verified live against pre-fix and post-fix builds: - Pre-fix: seed `smoothing.enabled=false`, navigate to `?inherited=false`, reload → IndexedDB shows `smoothing.enabled=true` (clobbered). - Post-fix: same scenario → IndexedDB stays `false` (preserved). 4 new unit tests in local-cache.test.ts cover the race-safety contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] E2E coverage for tooltip-pin / smoothing-clobber fixes Adds 15 Playwright specs across 4 files, plus a seeded RUNNING-status run for tests that need to exercise auto-refresh polling. Spec 1: tooltip-survives-running-autorefresh (3 tests, IR non-FS, RUNNING) Bug A — pin must survive ~12s of 5s-interval auto-refresh polling. Forces interval via localStorage seed; asserts pin still present after multiple cycles. On main: pin vanishes within 1-2 cycles. Spec 2: tooltip-survives-sidebar-prefetch (3 tests, IR non-FS) Bug B — pin must survive hovering every left-sidebar nav link with 6s waits past the TanStack Router 5s prefetch staleTime. On main: pin vanishes after the first hover that triggers refetch+recreation. Spec 3: tooltip-pin-survives-chart-recreation (6 tests, NON_FS = IR+AR) Defensive coverage for the tooltip-plugin deferred-unpin protocol itself. Forces chart recreation via smoothing-toggle (trigger- independent of polling or prefetch); asserts pin survives a rapid off→on→off cycle that exercises the 250ms defer window edge. Symmetric across IR + AR + charts + dashboards (static & dynamic). Spec 4: smoothing-not-clobbered-by-inherited-url (3 tests, AR non-FS) Smoothing-clobber race fix — seeds IndexedDB with smoothing.enabled=false + showInheritedMetrics=false, reloads with ?inherited=false in URL, asserts smoothing.enabled stays false. On main, the URL→setting effect clobbers it to true via stale-defaults spread. Seed: one new RUNNING-status run (a-running-run-001) in setup.ts, sharing the same metric-seeding pipeline as the other bulk runs. Exposed via `TEST_RUN_RUNNING` constant in test-helpers.ts. Helpers: chart-locations.ts gains an `irRunName` option on `SetupOptions` so any forEachChartLocation call can target the RUNNING run instead of the default TEST_RUN_INDIVIDUAL, plus two new location groups (LOCATIONS_IR_NON_FS, LOCATIONS_AR_NON_FS) for asymmetric-coverage specs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Apply Gemini review feedback on new e2e tests Addresses 5 of 6 review comments on PR #447's e2e additions: 1. (high) smoothing-clobber spec: drop the buggy `getActiveOrgId` reimplementation (missing `?batch=1`, wrong response-shape parse). Import the existing `getOrganizationId` helper from test-helpers.ts. 2. (medium) smoothing-clobber spec: replace `waitForTimeout(2000)` with a fail-fast polling loop reading IndexedDB. Caps at 3s budget but exits the moment a regression is observed. 3. (medium) Tooltip pin/hover helpers were duplicated across 3 specs. Extracted `hoverUntilTooltipVisible`, `pinTooltip`, and `pinnedTooltip` into `web/e2e/utils/tooltip-helpers.ts`. All three specs now import from there. 4. (medium) running-autorefresh: replace the `document.querySelectorAll('*')` RUNNING-status fallback with a clean `[data-testid="run-status-badge"]` text check (the actual badge selector — `[data-run-status]` doesn't exist). 5. (medium) running-autorefresh: replace `waitForTimeout(12_000)` with `Promise.all` of two `waitForResponse` calls matching graph-batch refresh URLs. Event-driven; ~10-12s typical, fails fast on missing ticks, capped at 14s. #6 (sidebar-prefetch's `waitForTimeout(6_000)`) intentionally kept — the wait IS the trigger condition. TanStack Router's prefetch only fires past the 5s `runs.get` staleTime; replacing the wait with `waitForResponse(prefetch)` either short-circuits early (skipping past the buggy condition) or hangs when no refetch fires. The 6s is load-bearing, not a smell. Documented inline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Force hard reload in smoothing-clobber spec The previous version used `page.goto(sameUrlWithInheritedFalse)` to trigger the bug. That doesn't work: TanStack Router treats query-param-only navigations on the same path as soft navigations, so React components stay mounted and `useState` never re-runs with DEFAULT_SETTINGS. The buggy URL→setting effect re-evaluates with the current (post-seed) state instead of the stale DEFAULT it needs to spread — no clobber, no bug, test passes wrongly. Empirically verified on pre-fix code (dev container with source fix reverted): page.goto(sameUrlWithInheritedFalse) → smoothing.enabled stays false (bug doesn't fire). page.reload() → smoothing.enabled flips to true (clobber confirmed). Fix: ensure `?inherited=false` is in the URL, then `page.reload()`. The reload bypasses TanStack Router entirely — a real browser reload that unmounts React, so the next mount starts with `useState(DEFAULT_SETTINGS)`, which is the exact race window the bug exploits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Redesign smoothing-clobber spec as a real UI flow + setup cleanup Replaces the previous "seed IndexedDB directly, force reload, read IDB" test with the actual customer scenario, exercised through the real UI: 1. Default state — URL has no `inherited=` param. 2. Click `#toolbar-smoothing` to toggle smoothing OFF. 3. Reload — smoothing must remain OFF (auto-retry handles the legitimate post-reload flicker where React state inits to DEFAULT for ~150-300 ms before liveQuery hydrates from IndexedDB). 4. Open Line Settings drawer (gear-icon button, now with data-testid="line-settings-trigger" for stable targeting). 5. Toggle "Show inherited metrics" OFF via the actual switch — NOT by setting the URL param directly, since that would cause an unintended navigation/reload mid-test. 6. Close drawer (Escape). URL writer mirrors the saved setting and `?inherited=%22false%22` appears (TanStack Router JSON-encodes search values; checks parse the search param instead of substring). 7. Reload — this is the moment the pre-fix bug fires. URL→setting effect spreads stale DEFAULT_SETTINGS over the saved record, clobbering smoothing.enabled to ON. The flicker-aware `await expect(toggle).not.toBeChecked({ timeout })` polls past the legitimate flicker window — on the bug case the toggle stays ON forever and the assertion times out; on the fix case the toggle resolves to OFF as IndexedDB hydrates. Empirically verified locally against the dev container: • Post-fix (functional-updater) build: test PASSES. • Pre-fix build (source-fix files reverted): test FAILS at step 7 with "expected not to be checked, actual: checked". Cleanup #1: `setupAllRunsDashboard` now resolves the dashboard view ID and run SQIDs entirely via API context (no first goto for session context). Single `page.goto` with the resolved URL. Saves ~1.5 s per dashboard test setup and removes the brief "project loads with default 5 runs" visual flash before the real URL kicks in. Added `getDashboardViewIdViaApi` as the API-context variant of `getDashboardViewId`; existing page-form `getDashboardViewId` stays for other callers. Tracking tags: • `data-testid="line-settings-trigger"` added to the LineSettings drawer trigger button (line-settings.tsx:136). Makes the spec robust to icon changes or nearby button additions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Revert setupAllRunsDashboard goto cleanup The "skip the first goto" cleanup introduced by fb2f64fe broke dashboard-using tests across the suite. The first `page.goto(/o/$slug/projects/$name)` has a load-bearing side effect: visiting the org-scoped URL triggers better-auth's `setActiveOrganization(slug)` async call. Without it, the session's `activeOrganization` stays at whatever it was last set to (often null or a different org), so the subsequent `dashboardViews.list` API call filters by the wrong org and returns no views — failing every dashboard test with "Dashboard 'Line Chart Variants Test' not found. Available:". Reverting `setupAllRunsDashboard` to the original two-goto pattern and removing the unused `getDashboardViewIdViaApi` helper. The brief "project loads with default 5 runs" flash before the real URL kicks in is annoying but harmless; the alternative would be to explicitly call the set-active-organization tRPC mutation before resolving views, which is more invasive than the visual nicety is worth. The smoothing-spec redesign and `data-testid="line-settings-trigger"` from the same commit are kept — those are unrelated and functioning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [testing] Drop chart-recreation defensive spec; switch autorefresh to fixed wait Two adjustments after CI on ryandev16 (with source fix) surfaced false failures in the new test matrix: 1. **Drop `tooltip-pin-survives-chart-recreation.spec.ts` (6 tests).** This was defensive coverage for the tooltip-plugin defer-and-cancel protocol — force a chart recreation via the smoothing toggle, assert the pin survives. But the defer protocol only catches recreations where the chart re-mounts with the SAME `chartId` within 250 ms of destroy. The smoothing toggle path doesn't fit either constraint: adding/removing the `(original)` companion line takes longer than 250 ms to settle in uPlot, and React may remount the LineChart with a fresh `useId()`. So the spec was testing a scenario the fix never claimed to defend against — failing on the fix branch with "defer protocol regressed" was a false positive. Not one of the originally reported bugs (A or B), and the actual triggers for A and B are already covered by sibling specs `tooltip-survives-running-autorefresh` and `tooltip-survives-sidebar-prefetch`. Deleting cleanly. 2. **Revert autorefresh spec to fixed wait.** The earlier switch from `waitForTimeout(12_000)` to `Promise.all` of two `waitForResponse` calls (Gemini comment #5) was wrong-headed for this test. With the source fix in place the RefreshButton's popup-defer correctly skips its tick while the tooltip is pinned, so NO `graphBucketed` response ever fires — `waitForResponse` hangs the full 14 s and the test fails with TimeoutError. Reverting to a fixed 12 s wait is the right shape: "let time pass, see if the pin survives." On `main` (no fix), the redundant `useRefreshTime` setInterval bypasses popup-defer, chart recreates, pin dies, assertion catches it. On fix, nothing fires, pin trivially stays. Either branch behaves correctly. Inline comment explains why `waitForResponse` doesn't work here. Net change to the new e2e matrix: 15 tests → 9 tests (Spec 1 autorefresh: 3, Spec 2 prefetch: 3, Spec 4 smoothing: 3). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ubuntu <azureuser@ryan-dev-azure-ebd1-27ae-0.hws5gqy4aacuhplmnpd3zr4cxb.bx.internal.cloudapp.net> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
public-repo-publish Bot
pushed a commit
that referenced
this pull request
Jun 6, 2026
…n (#454)
* docs: spec for runs-table search "Other matches" dropdown
Adds a design spec for the search-while-filtered UX issue: when a filter
or "Display only selected" is active, users cannot add runs from outside
the current view to their selection without disabling the constraint.
The proposed dropdown beneath the search input surfaces out-of-view
matches, lets users select them, and relies on the existing
mergeSelectedRuns / ensureSelectedRunsIncluded logic to make them
sticky-visible in the table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: implementation plan for search "Other matches" dropdown
Bite-sized TDD plan covering the spec at
docs/superpowers/specs/2026-05-12-search-other-matches-dropdown-design.md.
Nine tasks: extract useLocalStorageBool, lift showOnlySelected, extend
handleRunSelection with runFallback, useSearchOtherMatches hook,
SearchOtherMatchesDropdown component, toolbar wiring, backend smoke
regression-guard, Playwright spec, final rebuild + hand-off.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: extract useLocalStorageBool hook from use-data-table-state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: lift showOnlySelected state to parent route
Move showOnlySelected localStorage state from useDataTableState into
index.tsx so the parent route can read it. DataTable now accepts it as
controlled props; useDataTableState receives it as parameters and resets
pageIndex via useEffect when it changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: guard pageIndex reset and drop setShowOnlySelected pass-through
Add isFirstShowOnlySelectedRender ref guard so the showOnlySelected
useEffect skips the initial mount and only resets pageIndex on real
user toggles. Remove setShowOnlySelected from useDataTableState's
return object — data-table.tsx already owns onShowOnlySelectedChange
as a prop and passes it directly to TableToolbar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: drop dead setShowOnlySelected param from useDataTableState
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(runs-table): handleRunSelection accepts optional runFallback
Adds a 3rd optional argument `runFallback?: Run` to `handleRunSelection`
so callers can supply a run object when the target run is outside the
current paginated `runs` array (e.g. "Other matches" dropdown). Falls
back to the live array entry when present, making the fallback truly a
last resort.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: configurable matchMedia stub to avoid future redefine errors
Code review nit from Task 3 — without configurable: true, any later
test that tries to redefine window.matchMedia in the same jsdom
process throws TypeError: Cannot redefine property.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(runs-table): useSearchOtherMatches hook for out-of-view matches
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: clarify useSearchOtherMatches contract (hasMore, resultCount, Set stability)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(runs-table): SearchOtherMatchesDropdown component
Dropdown renders out-of-view matches as clickable rows and in-view matches as grayed "In table" badges, with Esc-to-close and optional hasMore footer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(search-other-matches): fix createdAt type cast, loading state, Set lookup
Three nits from code review on Task 5:
- formatCreatedAt now accepts Date | string | undefined; drop the
unsound `as unknown as string` cast at the call site.
- Show a "Loading…" indicator when isLoading=true with no rows yet so
the dropdown does not flash an empty shell on first open.
- Precompute the in-view ID set so per-row lookup is O(1) instead of
O(N×M) (negligible at the 30-row cap, but free).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(runs-table): wire SearchOtherMatchesDropdown into toolbar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(runs-table): drop unneeded casts in filterActive memo
Code review nits on Task 6:
- ServerFilters already types fieldFilters/metricFilters/systemFilters
as concrete param arrays; `as unknown[]` adds noise without benefit.
- Add a comment to inViewRunIds explaining why the second loop matters
(covers IndexedDB-hydrated selected runs not in the current page).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(smoke): regression-guard runs.list unfiltered search (dropdown contract)
Adds Test 12.9 to Server-Side Search suite asserting that runs.list
with only `search` and `limit` (no filter params) returns matching
runs. The "Other matches" dropdown depends on this behavior to surface
runs that the main table's filter is hiding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): runs-table search-other-matches dropdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(search-other-matches): click-outside dismiss + paint-during-scroll glitch
Two bugs reported during manual testing:
1. Clicking outside the search bar did nothing. Expected: dismiss the
dropdown, but keep the typed query so the user can come back to it.
Add onDismiss prop wired to a document mousedown listener that fires
when the click lands outside the search input's `.relative` wrapper.
Parent (index.tsx) tracks otherMatchesDismissed; typing in the input
resets the dismissal so the dropdown can reappear. Esc keeps its old
semantics (clear input + close).
2. During fast table scroll, the dropdown's underlying toolbar buttons
("Filter", "Columns") would briefly paint through. Add `isolate`
(own stacking context) + `transform-gpu` (own composite layer) so
the dropdown is promoted off the main paint thread.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(runs-table): pagination total ignored sticky out-of-filter selected runs
When a filter is active and the user has runs selected that don't match
the filter, those runs are sticky-appended to displayedRuns via
ensureSelectedRunsIncluded. Pagination computed totalPages from runCount
(the server's filter total) alone, so the sticky rows landed past the
last UI page and became unreachable.
Example: filter matches 10 runs, 2 sticky selected, pageSize=10:
Before: totalPages = ceil(10/10) = 1 — sticky rows invisible
After: totalPages = ceil(max(10,12)/10) = 2 — sticky on page 2
`runsLength` is the size of the actual table-data array passed to
TanStack Table (filter rows + appended sticky). Clamping
effectiveCount to at least runsLength fixes both TablePagination and
PageInput. Verified manually at pageSize=5 (3 pages, sticky on p3) and
pageSize=10 (2 pages, sticky on p2).
Pre-existing bug — not introduced by the search-other-matches branch
but surfaced while debugging it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(search-other-matches): re-open dropdown when search input is refocused
After dismissing the dropdown via click-outside, the input still held
the typed query but the dropdown stayed hidden until the user typed a
new character. Add an onSearchFocus handler that resets the dismissal
state — clicking back into the search bar now restores the dropdown.
The render-gate stack still applies: if there are no out-of-view hits
(filter cleared, query empty, etc.) the dropdown remains hidden.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(search-other-matches): dropdown empty when "Display only selected" is on
inViewRunIds was always built from tableRuns (the full server fetch) +
selected. In Display-only-selected mode the table only renders selected
runs — but every loaded run still ended up in inViewRunIds, so a search
that hit any unselected run got partitioned as "in view" and the
dropdown gate (outOfView.length === 0) hid the popover.
Make inViewRunIds conditional on showOnlySelected:
on → just selected ids
off → tableRuns ids + selected ids (existing behavior)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(css): missing % on --popover lightness in dark theme
Line 98 had `--popover: 240 8.7% 7.8;` — no % on the lightness. The
generated `hsl(240 8.7% 7.8)` is invalid (lightness must be a
percentage), so any element relying on `bg-popover` for its background
had effectively no fill in dark mode. That's why the "Other matches"
dropdown showed through during fast scroll — the only painted pixels
were the rows themselves (which only have hover backgrounds), and the
container had nothing under it.
Every other variable in the same block uses `%` correctly; this was a
plain typo. Affects all Radix popovers, command menus, and the new
search-other-matches dropdown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(search-other-matches): paint bg-popover on each row, not just container
Even with bg-popover + isolate + transform-gpu on the container, fast
scrolls let the underlying toolbar (Filter/Columns buttons) leak through
between rows. The container's bg layer is opaque per computed-style, but
during rapid scroll the GPU compositor briefly draws the rows before
the parent's bg layer repaints, exposing what's underneath.
Painting bg-popover on each row (and on the header + footer) gives every
visible pixel an explicit fill, so the rendering never depends on the
parent's bg landing on the same frame as the row content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(search-other-matches): drop isolate/transform-gpu — they caused the bleed
Earlier I speculatively added `isolate transform-gpu` thinking they'd
fix paint-during-scroll. They don't — they CAUSE it. Promoting the
dropdown to its own composite layer means the scrolling-contents layer
and the parent bg layer can desync during fast scrolls, which is
exactly the symptom (rows paint a frame ahead of the bg, briefly
exposing the toolbar underneath).
Per-row `bg-popover` (committed earlier) is the actual fix — every
visible pixel has its own opaque fill so the rendering never depends
on layer sync. With per-row bg in place, isolate/transform-gpu were
purely a regression source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: E2E follow-up plan for search-other-matches branch
10 new test cases covering bugs caught during manual testing
(pagination + sticky runs, display-only-selected interaction, click-
outside dismiss + refocus reopens) plus a risk audit of existing specs
that may need expectation updates because the pagination total now
accounts for sticky out-of-filter selected runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(search-other-matches): Esc dismisses like click-outside (keeps input)
Previously Esc cleared the search input and click-outside left it
alone — two different semantics that the user didn't ask for. Make
Esc behave the same way: dismiss the dropdown but keep the typed
query so refocusing the input brings it back.
Also drop the per-row/header/footer bg-popover additions. They didn't
fix the see-through-on-fast-scroll bug for the user reporting it, and
were never validated as the actual cause. Container bg-popover stays —
that's the original styling.
Consolidates the onClose + onDismiss props into a single onDismiss
since they're now equivalent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): update search-other-matches spec for new dismiss semantics
- Tighten "display-only-selected" test (#3) to assert at least one
clickable out-of-view row exists. Catches the empty-dropdown
regression that existed before commit 2c6ceeff — the old assertion
(dropdown visible) passed even when the dropdown rendered with zero
actionable rows.
- Update "Esc" test (#4) to match the new dismiss-keeps-input behavior
(was: clears input).
- Add "click-outside dismisses but keeps input" (#6).
- Add "refocus re-opens dropdown with same query" (#7).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): sticky-selected-pagination spec for cfb54945 fix
Four cases covering the pagination fix that ensures sticky-selected
runs (in the selection set but outside the active filter) remain
reachable via the page indicator instead of being silently truncated:
1. pageSize=10 + 10 filter matches + 2 sticky → totalPages >= 2,
sticky runs visible on the last page.
2. pageSize=5 + same setup → totalPages >= 3, sticky runs on page 3
(reached via the page-input jump path, not just next-button clicks).
3. No sticky runs → totalPages stays at 1, guarding against the
inverse regression where the fix unconditionally bumps the count.
4. Clearing the filter mid-test → totalPages jumps up because the
sticky bump no longer applies; the previously-sticky runs are now
counted as regular rows.
Reuses existing test-helpers (searchAndSelectRun, getPageInfo) and
mirrors the filter-popover pattern from search-filtering.spec.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* review(other-matches): caller-side enabled gate on useSearchOtherMatches
Per PR review (Gemini #1–3): accept an optional `enabled` param on the
hook so the parent can suspend it when the dropdown is dismissed.
Practical effect is small — the hook is debounced and TanStack-cached
— but it does prevent refetchOnWindowFocus from re-firing during a
dismissal cycle, and the hook shouldn't run when its output is unused.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): fix Filter button locator — lucide aliased Filter → Funnel
The Filter button used `<Filter />` from lucide-react. In v0.487 that
export aliases to `Funnel`, so the rendered SVG carries class
`lucide-funnel`, not `lucide-filter`. The CSS-class selector
`button:has(svg.lucide-filter)` matches nothing — 9 e2e tests timed
out on `.click()` waiting for an element that never appeared.
The pre-existing `search-filtering.spec.ts` has the same bug but uses
`test.skip(...)` if the button isn't found within 3s, so it silently
skipped instead of failing — worth fixing separately.
Switch both my new specs to `getByRole("button", { name: /^Filter\\b/ })`
which matches by accessible name and won't break if lucide renames the
icon again.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): deselect-all in beforeEach — page auto-selects first 5 runs
A fresh visit to the runs page (no ?runs= URL param, no IndexedDB
cache) auto-selects the first 5 runs via use-selected-runs.ts:449
`buildDefaultSelection(runs, 5)`. The test environment hits this every
time, so previous tests assuming an empty starting selection were off
by 5 sticky runs:
- sticky-selected-pagination "no extra page" expected total=1, got 2
- sticky-selected-pagination pageSize=5 jumped to p3, but sticky tail
lived on p4
- Visible 5 colored avatars in the Stably failure video that I'd been
attributing to state leak
Call `deselectAllRuns` in beforeEach so every test starts from a
known-empty selection state and sets up only what it needs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): fix Name-field selection in applyNameContainsFilter
Two real bugs in the helper, both caused tests to fail at the
configure-step transition:
1. `filter({ hasText: /^name$/i })` — anchored regex against an option's
full textContent, which is "Name sys text" (label + source-badge +
type-badge concatenated). The regex never matched. `search-filtering.
spec.ts` has the same broken locator but silently `test.skip()`s
when not found, hiding the bug.
2. After picking the field, I pressed Enter to apply the filter instead
of clicking the explicit Apply button. Enter doesn't reliably submit
the form; handleApply is wired only to the button's onClick.
New flow walks every step of the popover UI:
- Click Filter toolbar button
- Type "name" in the cmdk search
- Press Enter → cmdk's keyboard handling selects the auto-highlighted
first match (the "Name" system field), popover transitions to step
= "configure". This sidesteps the brittle text-matching entirely.
- Fill the value input
- Click Apply → handleApply() validates, calls onAddFilter, and
closes the popover via setOpen(false). No trailing Esc needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): tighten applyNameContainsFilter — verified flow end-to-end
Walked through the popover steps manually with a headless-Playwright
script against the dev server before committing. Watched the actual
DOM at each transition:
- field step has cmdk search input (placeholder="Search columns...")
- press Enter on that input → cmdk's keyboard handler selects the
auto-highlighted "Name" item, popover transitions to configure
- configure step has 1 input with placeholder="Enter value..." and
4 buttons: contains / AND / Back / Apply
- filling the value input makes canApply=true, Apply enables
- clicking Apply runs handleApply → setFilters + setOpen(false)
- popover unmounts, Filter toolbar button gains "1" badge
Three concrete improvements:
1. Target the value input by its exact placeholder rather than
`.last()`. Removes any ambiguity if the cmdk input briefly
co-exists with the configure-step input during the React render
batch.
2. Click the value input before filling, so the autoFocus race
(filter-value-input.tsx:44) doesn't drop the typed characters.
3. After clicking Apply, assert BOTH the popover unmount AND the
Filter button's "Filter <count>" badge. Either failure points the
reviewer at the helper instead of cascading into "dropdown not
visible" five lines later.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): apply filter via DOM click + verify by Filter badge, not popover unmount
Previous helper waited for the popover wrapper to detach after Apply.
That's wrong: Radix often keeps the [data-radix-popper-content-wrapper]
element attached after the inner content unmounts (animation /
positioning state). The CI failure was the false-negative.
The actual load-bearing signal that Apply worked is the toolbar's
Filter button gaining its count badge (handleApply → onAddFilter →
setFilters([...prev, filter]) → button text becomes "Filter 1").
Assert that instead.
Also switched the click itself to a DOM-dispatched click via evaluate,
which bypasses Playwright's stability / pointer-events checks. The
visible button proves it's the right element; we just need onClick to
fire.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): use data-testid for Filter button / Apply / Clear all / count badge
Add four testids to filter-button.tsx:
- filter-button — toolbar trigger
- filter-button-count — badge that renders when filters.length > 0
- filter-popover-apply — Apply button in configure step
- filter-popover-clear-all — Clear all button in field step
Switch three specs to use them: search-other-matches.spec.ts,
sticky-selected-pagination.spec.ts, and search-filtering.spec.ts
(which was silently `test.skip`ping on the broken lucide-filter
selector). No more role/name string matching, no more popover-scope
ambiguity, no more CSS-class brittleness across lucide-react versions.
Verified locally end-to-end via headless Playwright probe — all four
testid locators resolve and the filter applies / clears as expected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): click cmdk item directly instead of Enter — fixes CI dev-mode flake
CI failure stack from build 2818 showed waiter at line 93
(input[placeholder="Enter value..."]) — field-picker step never
transitioned to configure step. Root cause: cmdk's Enter-keyboard nav
doesn't fire reliably in Vite dev mode, so handleSelectField never ran
and handleApply early-returned at `if (!selectedField) return`.
Replace columnSearch.press("Enter") with a direct click on the cmdk
item by data-value, which calls onSelect synchronously.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: secure-context-safe UUID generation in filter & dashboard
CI runs Playwright against http://<IP>:3000 (NOT localhost), which is
not a secure context per the WebCrypto spec — `crypto.randomUUID()` is
therefore undefined and calling it throws. `handleApply` in
filter-button.tsx threw inside the React event handler before
`onAddFilter` ran, so the filter never landed: no badge, popover stuck
open, every `runs.list` query fired with all filter params NULL.
Add `lib/uuid.ts` with a Math.random()-based v4 fallback for non-secure
contexts (test-only — these IDs are local React keys, not crypto
material). Route both call sites through it.
Also fix the tag-filter E2E: for `dataType: "multiOption"` with seeded
options (`needle-tag`), FilterValueInput renders a cmdk option list,
not a text input. Click the option by data-value instead of filling
`Enter value...`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: reopen 'Other matches' dropdown on click into already-focused input
Esc dismisses the dropdown but keeps the input focused, so clicking
back into it didn't fire onFocus and the dropdown stayed closed
(click-outside dismissals worked because the outside click blurred the
input first). Listen to onMouseDown in addition to onFocus — fires on
every click regardless of prior focus state, idempotent with onFocus.
Tests:
- E2E test 7 now uses `searchInput.click()` (real user action) instead
of `searchInput.focus()` (a no-op when already focused).
- Renamed test 5 to assert the dropdown CLOSES when the only
out-of-view match is clicked (it now has nothing to show), and that
the input value is preserved.
- New test 5b: with a multi-match query, clicking one row leaves
others out-of-view and the dropdown stays open.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore: remove internal planning docs from PR
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: guard invalid dates + title attr on truncated run names
Addresses Gemini review feedback on the "Other matches" dropdown:
- formatCreatedAt: an invalid date string produces an Invalid Date
object rather than throwing, so toLocaleString rendered the literal
"Invalid Date". Guard with isNaN(d.getTime()) and return "" instead.
- Truncated run names now carry a title attribute so the full name is
visible on hover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Ubuntu <azureuser@ryan-dev-azure-ebd1-27ae-0.hws5gqy4aacuhplmnpd3zr4cxb.bx.internal.cloudapp.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: ryan <ryan@trainy.ai>
public-repo-publish Bot
pushed a commit
that referenced
this pull request
Jun 6, 2026
* docs: media loading perf audit (images + videos) Trace-driven findings on the project comparison page: presigned-URL signature churn from getS3Url causes the browser HTTP cache to miss on every refetch and carousel click, costing ~4 MB per click and ~1.3 MB per minute of idle from the 1m auto-refresh. Source PNGs are also ~17x larger in area than displayed, and <img>/<video> tags are missing lazy/decoding/preload/poster attributes. Doc proposes four fixes ordered by ROI, starting with a server-side memo on getS3Url (highest impact, smallest change, helps images and videos, fully compatible with S3 / R2 / MinIO). No code changes in this PR; implementation handed off to another machine. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * perf: cache presigned S3 URLs + lazy-load media thumbnails Implements fixes #1 and #2 from docs/2026-05-23-media-loading-perf-audit.md. Backend: getS3Url now reads through the existing two-tier cache (L1 LRU + optional L2 Redis) keyed on sha1(endpoint):bucket:key:expiresIn with a 24h TTL. The underlying SigV4 URL is valid for 5 days, leaving > 4d of headroom. Eliminates the X-Amz-Date / X-Amz-Signature churn that defeats the browser HTTP cache on refetch / carousel-click / auto-refresh (~4 MB redownloaded per carousel click, ~1.3 MB/min idle). Frontend: thumbnail <img> in image-card.tsx and the GIF fallback in group/video.tsx get loading="lazy", decoding="async", width/height=256. Below-the-fold panels now defer until scrolled into view; decode happens off the main thread. Out of scope (per the audit doc's ROI ordering): - Fix #3 (video preload=none) — needs poster (fix #4). - Fix #4 (server-side thumbnails) — large Rust ingest lift. - Fix #5 (fetchpriority polish). Verification: - New unit test (server/tests/s3-url-memo.test.ts) asserts repeated calls return the same URL and the presigner is called once across 20 sequential reads. Watched test fail before the memo and pass after. - Type checks: 0 errors in both @mlop/app and @mlop/server. - Existing cache.test.ts (21 tests) still passes. - Frontend Network-tab verification recipe from the doc not executed — needs a deployed app. The HTML attribute additions are layout-neutral. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * review: address gemini + bugbot feedback on PR #481 Gemini: - Use the existing buildCacheKey utility (mlop: prefix, sorted params) instead of manual string concatenation. Matches the convention used by withCache / withBatchCache. - SHA-1 → SHA-256 for the endpoint hash. The hash is not security- sensitive (it's a key-compaction hash, never re-hashed for auth), but SHA-256 sidesteps security-scanner noise at zero perf cost. Bugbot: - Hoist setCached out of the try block that wraps the presigner. If setCached rejects (e.g. transient getRedisClient failure inside the cache layer), the already-valid URL is no longer discarded and the catch above no longer misattributes the failure as "Failed to generate R2 image URL". Cache write is now fire-and-forget with a console.warn, mirroring the pattern setCached itself uses for Redis writes. New regression test exercises the bugbot scenario directly: mockResolvedValueOnce(null) for the getCached read path, then mockRejectedValueOnce for the setCached write path. Verified red-green: with the fix reverted the test fails with the exact misleading error bugbot called out ("Failed to generate R2 image URL" despite the URL having been generated); with the fix in place it passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Ubuntu <azureuser@andrewDev.iqigxx5hkb2uxpbdcmttsvsjcc.cx.internal.cloudapp.net>
Bumps [better-auth](https://github.com/better-auth/better-auth/tree/HEAD/packages/better-auth) from 1.4.10 to 1.6.11. - [Release notes](https://github.com/better-auth/better-auth/releases) - [Changelog](https://github.com/better-auth/better-auth/blob/main/packages/better-auth/CHANGELOG.md) - [Commits](https://github.com/better-auth/better-auth/commits/better-auth@1.6.11/packages/better-auth) --- updated-dependencies: - dependency-name: better-auth dependency-version: 1.6.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
23b0893 to
0b04253
Compare
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.
Bumps better-auth from 1.4.10 to 1.6.11.
Release notes
Sourced from better-auth's releases.
... (truncated)
Changelog
Sourced from better-auth's changelog.
... (truncated)
Commits
f41514echore: release v1.6.11 (#9532)699b09afix(oidc-provider, mcp): drop "none" alg, default plain PKCE off, reject miss...b4bc65aMerge commit from forka1c9f3cfix(access): preserve exact role statement types (#9507)da7e50bfix(oauth): block OAuth linking to unverified local accounts (#9578)23094a6fix(organization): default-onrequireEmailVerificationOnInvitation& extend...1f2ff42fix(oidc-provider, mcp): authenticate confidential clients on refresh_token g...5f09d56fix(magic-link): consume verification token atomically on verify (#9572)99a254afix(device-authorization): bind approval to verifier session (#9573)0cbddb8refactor(db): renameclaimOneadapter primitive toconsumeOne(#9568)Maintainer changes
This version was pushed to npm by GitHub Actions, a new releaser for better-auth since your current version.