fix(ash): sync sampling rate to zoom level; show actual data window (#763)#763
fix(ash): sync sampling rate to zoom level; show actual data window (#763)#763
Conversation
…763) Two UX fixes for /ash: 1. Zoom out now also increases refresh_interval_secs to match bucket_secs (capped at 60s). Previously zoom only changed display aggregation — you could be at 5min-bucket zoom with 1s sampling, which made no sense. Now zoom out = coarser but wider real data. 2. Status bar window label now shows actual data span (samples × bucket_secs) rather than ring-buffer capacity. Previously showed '10min' from the first second of launch. Now shows '5s', '30s', etc. growing as data accumulates — honest about what you're actually seeing. Remove now-unused window_label() from AshState. Add test: zoom_cycle_syncs_refresh_to_bucket.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #763 +/- ##
==========================================
+ Coverage 67.52% 67.60% +0.08%
==========================================
Files 52 52
Lines 33792 33819 +27
==========================================
+ Hits 22817 22863 +46
+ Misses 10975 10956 -19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
REV — PR #763 (zoom/window UX fixes)Branch: Fix 1:
|
Testing evidenceVerified behaviors:
|
PR #763 — Ready for mergeBranch: CI green (all checks pass). REV approved — 0 blocking findings. Testing evidence posted (zoom cycle + 1865 tests + clippy clean). Awaiting Nik merge. |
…om ash.samples (#761) * feat(ash): pre-populate ring buffer from ash.samples when pg_ash is installed (#761) When pg_ash is detected on the server, query ash.wait_timeline() for the configured window (default 10 min) and pre-populate the ring buffer before the live polling loop starts. Falls back to live-only when pg_ash is absent. - sampler.rs: add query_ash_history() using ash.wait_timeline(interval, '1s') - mod.rs: pre-populate ring buffer on startup when pg_ash.installed - state.rs: document pg_ash_installed field - 26 new unit tests covering history parsing, ring buffer capacity, graceful degradation when pg_ash absent Fixes #761 * test(ai): add slash-ash-pgash AI test for pg_ash history integration (#761) * chore(demos): move GIFs to demos/, rename to match AI test filenames; add 1s pause after commands - docs/ash-demo.gif -> demos/slash-ash-general.gif - docs/ash-demo.cast -> demos/slash-ash-general.cast - AI test recording paths updated accordingly - Added sleep 1 after /ash command in expect scripts (gives user time to read output) - slash-ash-pgash.md: same conventions applied * fix(ash): address REV findings — parameterize SQL, remove dead code, use configured window; add CI integration tests (#761) - BUG-1: parameterize ash.wait_timeline interval via $1 (no more format! injection) - BUG-2: remove dead history_snapshots() fn; wire History mode through query_ash_history() - WARN-1: use state.bucket_secs()*600 instead of hardcoded 600s window - Add 2 #[ignore] integration tests: test_pg_ash_history_live, test_pg_ash_history_graceful_degradation * fix(ash): address REV findings M1/M2 — parameterize interval $1, use int8 for samples; doc cleanups (#761) - M1: replace format!() string interpolation with $1 parameterized query in query_ash_history_inner; rename helper to _inner (no longer interval-keyed) - M2: use samples::int8 (was int4) to avoid silent overflow on busy servers; update row.get() to i64; use u32::MAX as saturating fallback - L3: fix double-sentence doc on pg_ash_installed in state.rs - L2: clarify retention_seconds is reserved, not yet wired - L4: add cpu_count comment in history snapshot construction - M3: add TODO comment for history-mode per-frame query caching * test(ash): add CI integration tests for pg_ash sampler (#761) Three tests in integration_repl.rs (run with --features integration): - ash_pg_extension_absent_in_test_db: verifies detection SQL returns false when pg_ash is not installed (CI baseline) - ash_wait_timeline_missing_returns_error: confirms the parameterized ash.wait_timeline query errors gracefully when schema is absent, validating the sampler's Ok(rows) fallback guard - ash_live_snapshot_query_shape: executes the exact live_snapshot() SQL against the test DB, validates row shape (non-empty wtype, non-negative cnt) * style(ash): cargo fmt — fix integration test formatting (#761) * fix(ash): sync refresh_interval_secs to bucket_secs on zoom; show actual data window (#763) - zoom_cycle_forward/back now call sync_refresh_to_zoom(), keeping refresh_interval_secs = bucket_secs (capped at 60s) so live sampling rate matches display granularity — zoom out = coarser but wider real data - Status bar window label now shows actual data span (samples × bucket_secs) rather than ring-buffer capacity — no more '10min' when you've been running for 5 seconds - Remove now-unused window_label() from AshState - Add test: zoom_cycle_syncs_refresh_to_bucket Fixes the misleading window label and zoom/sampling mismatch Nik found. * docs(ash): re-record general demo GIF with drill-down navigation (#756) - Show full drill-down: top level → wait type events → query level - Demonstrate b key navigation back up the stack - Show legend overlay toggle - Updated AI test file with drill-down steps and pass criteria - Resize to 800px wide for Telegram inline playback * docs(ash): record pg_ash history integration demo GIF (#761) Shows: history bars pre-populated from ash.wait_timeline on startup (left side full before live data arrives), drill-down navigation, legend overlay. 800px wide, Dracula theme. * docs(ash): native resolution GIFs (951px, no resize) * feat(ash): add X-axis timestamp labels to timeline Show HH:MM anchors at left (oldest visible bucket), right (newest/now), and midpoint when area >= 20 cols wide. Pure UTC arithmetic — no extra deps. Carves a 1-row strip inside the timeline inner area so bar height is preserved (same layout, one row taller overall inner area used for axis). Closes the UX gap where bars had no time context at all. * docs(ash): re-record GIFs with X-axis timestamp labels Both slash-ash-general.gif and slash-ash-pgash.gif re-recorded after feat(ash): add X-axis timestamp labels to timeline (feb6fab). HH:MM anchors now visible at left/mid/right of timeline bottom row. * docs(ash): re-record pgash GIF with actual historical data pre-populated Previous recording had only ~90s of history; sampler had just restarted. This recording has 1000+ samples (~10min) in ash.sample before launch, so bars are fully pre-populated from the left on first frame. * fix(ash): detect pg_ash via ash.wait_timeline in pg_proc, not pg_extension pg_ash installed via SQL (not CREATE EXTENSION) doesn't appear in pg_extension, causing detect_pg_ash() to return installed=false and silently skip history pre-population on startup. Fix: check for ash.wait_timeline in pg_proc/pg_namespace instead. This handles both install paths (extension + manual SQL) and is the only capability we actually need to verify. Update integration test name/query to match. * fix(ash): use format! for interval literal in wait_timeline query tokio-postgres cannot serialize a Rust String as a Postgres interval parameter ($1::interval) without a server-side type annotation that requires an explicit type OID. The client-side serialization fails with 'error serializing parameter 0', silently returning an empty vec and causing history pre-population to be skipped on every launch. Fix: embed the interval as a literal using format! on the u64 window_secs value (not user input — no injection risk). Also convert the Err arm from silent Ok(vec![]) to a proper Err return so callers can log if needed. * docs(ash): re-record pgash GIF — history bars visible from first frame Previous recordings failed because wait_timeline query silently returned empty (tokio-postgres interval serialization bug). Now fixed: bars pre-populated immediately on /ash launch (57s of history on first frame). * test(ash): fix ash_wait_timeline_missing_returns_error to use real query The sampler now uses a literal interval (format!) not a $1 parameter. Update the integration test to use the same SQL so it tests actual production behavior: ash.wait_timeline absent → query returns Err. * docs(ash): re-record both demo GIFs with all fixes applied - General /ash: live streaming, drill-down, X-axis timestamps, legend - pg_ash history: bars pre-populated from first frame (1min window) All three bugs fixed before recording: - detect_pg_ash uses pg_proc not pg_extension - wait_timeline interval serialization fixed - integration test matches production SQL * docs: add /ash section and GIFs to README - Add slash-ash-general.gif at top (after intro, before Features) - Add Active Session History to feature bullet list - Add ## Active Session History section with usage, keybindings, zoom levels, pg_ash history pre-population, and pgash GIF - Both GIFs already committed in demos/ on this branch * docs(ash): re-record GIFs using pgbench load per tests/ai spec - slash-ash-general: pgbench -c 8 load, drill-down, legend, zoom - slash-ash-pgash: history pre-populated (1min, active=22) on first frame Both recorded per exact expect scripts in tests/ai/ * docs: replace 2.8MB pspg_screenshot.png with 147K JPEG PNG was oversized for inline README rendering. Resized to 1200px wide, converted to JPEG (quality 80) — 147K vs 2.8MB. * docs(ash): re-record ASH GIFs with fresh pgbench load pgash GIF: 5min history pre-populated (active=26) on first frame general GIF: live pgbench load, drill-down, legend * docs: move /ash GIF to very top of README * docs: collapse secondary GIFs and pspg screenshot behind <details> * docs: add quickstart hero demo GIF (connect/version/fix/ash flow) * chore: bump version to 0.9.0 * docs: re-record quickstart demo with hostname 'demo' in status bar * docs: re-record quickstart demo v5 — hostname 'demo' in status bar * refactor(ash): extract group_timeline_rows and split_wait_event helpers Eliminates duplicated snapshot-grouping logic between query_ash_history_inner and the test suite. Tests now call the production helper directly instead of maintaining a copy. No behaviour change — all 82 ash tests pass. * fix(ash): show HH:MM:SS on X axis at zoom 1-2 (bucket ≤ 15 s) At zoom 1 (1-second buckets) all labels within the same minute were identical (13:40 / 13:40 / 13:40), making the axis appear static. Show seconds when bucket_secs ≤ 15 so the right label visibly ticks forward every second. Coarser zoom levels keep HH:MM as before. * docs: re-record quickstart demo (1.4s pause before /ash); add X-axis labels demo GIF - quickstart-demo: 1.4s deliberate pause after typing /ash so the command is visually distinct before the TUI opens; 265K - slash-ash-xaxis.gif: dedicated demo showing HH:MM:SS labels shifting every second at zoom 1 (645K, behind <details>) - README: update X-axis bullet to mention HH:MM:SS vs HH:MM behaviour; add <details> block with slash-ash-xaxis.gif * docs: quickstart demo v6 — clean prompt, fix timing, hostname fix - Shell prompt set to '$ ' via --command env override (no openclaw-server) - Wait for hint text before typing /fix (no more overlap) - 1.4s pause after typing /ash before TUI opens - 257K
Summary
Two UX fixes for
/ashfound during demo recording:Fix 1: Zoom changes sampling rate
Zooming out (→) now increases
refresh_interval_secsto matchbucket_secs(capped at 60s). Previously zoom only changed how existing 1s samples were aggregated for display — at 5min-bucket zoom you still had 1s live samples, so the ring buffer held only ~10min of real data regardless of zoom level. Now zoom-out = coarser sampling + wider real window.Fix 2: Window label shows actual data span
Status bar
window:now shows actual elapsed data (samples × bucket_secs) rather than ring-buffer capacity. Previously showed10minfrom the first second of launch, which was confusing. Now shows5s,30s, growing honestly as data accumulates.Removes now-unused
window_label()fromAshState.Tests
zoom_cycle_syncs_refresh_to_bucketunit testFixes issue found by Nik during demo recording.