Skip to content

feat(dash): one redesigned dashboard: Overview rewrite, plain-words tabs, Benchmarks tab removed - #564

Merged
amiddavid merged 13 commits into
mainfrom
feat/dashboard-ux-overhaul
Oct 15, 2026
Merged

amiddavid merged 13 commits into
mainfrom
feat/dashboard-ux-overhaul

Conversation

@OsherElhadad

Copy link
Copy Markdown
Collaborator

Draft. Still open: design review round 4, the final load-time table, the full ./dash suite and the code review. The screenshots below are from the previous build (synthetic demo data, no real figures, paths or upstream names; two images that showed a path and internal upstream names were removed from the branch history); a regenerated set follows.

What this does

One dashboard, redesigned, at /dashboard/.

  • Overview now shows only the three savings (cache keep-alive with the time it saved, compaction, tools and skills filter), each in dollars and tokens against what was spent, with Estimated / Upper bound labels. Keep-alive is drawn as "up to $X possible" (it is the ledger's credit, a ceiling), not as money saved. Below: tools and skills worth removing (cost in the selected period, /context-guru:remove-mcp-and-skills and /context-guru:insights tools, link to Inventory), "About the other tabs" grouped like the nav, and one collapsed Detailed diagnostics with redesigned plots (net saving line, money saved and added, tokens removed, cache share, latency on two scales, requests) that load only when opened. Removed from Overview: prefix-change exposure, addressable spend, "Removed by you, not by us", "Our own LLM cost", the Diagnostics tile group, Caching insights, Cache reuse, Your sessions, the bottom-line banners.
  • Every tab opens with a title and a one-line "what this shows" (one table, TAB_INFO), plain headings, a sentence per chart or table, jargon replaced or defined in a hover tooltip (term()). Reworked: Keep-alive (one verdict), Inventory (one ranked list with the command), Usage (one daily chart), Sessions (6 columns, order by cost, short account labels, phone layout), Requests, Components (Step, Saved, Cost, Net; reason codes in words), KV-cache (verdict first), Config, Strategies.
  • Look: flat sections with hairlines, a bundled serif (Source Serif 4, OFL) for titles and figures, a sliding nav underline, a short rise on tab change; light and dark; phone layout (filters behind one button, scrolling nav); all motion off under prefers-reduced-motion.
  • Benchmarks tab removed: tab, /api/benchmarks routes, ingest code and table DDL. --dashboard-bench-dirs stays as an ignored flag so an existing unit file still starts.
  • The five-page home from feat(dash): five-page dashboard, bottom line first (#473) #477 is retired: the classic dashboard is the default again. #home lands on Overview, #cache on Keep-alive, #savings/#tools/#sessions already resolve, /dashboard/home.html redirects. What the five-page home had that the classic lacked moved over: sessions ordered by cost, and the provider's own cache discount stated as not counted.
  • Docs: docs/dashboard.md, a new plain guide (docs/how-to/dashboard-guide.md), reference updated; mkdocs build --strict passes.

Tests

dash/ui_smoke.mjs + TestUISmokeEveryTab: opens all 16 tabs in a real browser (fixture served as a hosted manager), fails on any JavaScript error, missing heading or intro, an error state on tabs whose data this package serves, any removed Overview section coming back, the Benchmarks tab or route returning, and the old five-page links not landing. Skips without node, playwright-core and a Chromium. Go UI tests updated only where they asserted removed tiles or old wording. Golden JSON untouched (API unchanged).

Load time (paired, interleaved A B, 3 rounds, both builds on reflink copies of the same snapshot, all-time window, host load about 1.5; medians, milliseconds)

tab main this branch
Overview 25,642 13,717
Keep-alive 12,002 15,855 (under investigation: same endpoints, see below)
Inventory 9,683 9,388
Usage 8,038 8,310
Sessions 4,221 2,702
Components 6,165 6,277
KV-cache 5,475 5,490
Tenants 3,551 3,746
the other 8 tabs 0.75 to 1.0 s within 50 ms

First visit to Overview on a cold process: main 55.6 s, branch 45.2 s (34 s of both is the first stats read of a 3.6 GB database). The Overview's savings rows paint from the stats?lean=1 read, about 6 s warm and 35 s on a cold process; the floor is the server's stats query, not the client (render after the last response is about 0.5 s). Details and the Keep-alive follow-up in a later comment.

Screenshots (previous build)

Overview before
Overview after
Detailed diagnostics open
Keep-alive
Inventory
Usage
Sessions

@amiddavid

Copy link
Copy Markdown
Collaborator

we need to talk in user terms
they know about
preset - conservative ( for instance)
cache strategy

and not in compaction and keep-alive
image

@OsherElhadad OsherElhadad left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review: REQUEST CHANGES at 1a719cd (draft). (Posted as a comment: GitHub does not let the author account request changes.)

Blocker: the Overview no longer shows our cache-reorder saving, and the test that guarded it was deleted.

  • /api/stats defines total_saved_usd as four disjoint addends of ours (dash/overview.go): net_saved_usd, cachesplit_saved_usd, keepalive_net_usd and decl_filter_usd. On main the Overview showed the total and a prefix-reorder tile (cachesplit_saved_usd + historical).
  • The new "What context-guru saved" ledger has three rows (keep-alive net, compaction net_saved_usd, filter decl_filter_usd). It drops the cachesplit saving and shows no total. The removed api_test assertion said exactly this: "the overview does not render cachesplit_saved_usd, the one cache saving we claim".
  • Nothing double counts: net_saved_usd = baseline − cost − cg_llm and excludes keep-alive, so the three rows are disjoint and the "% of spend" bars share one denominator.
  • But the section understates what we saved. Either add a fourth "Cache reorder" row (cachesplit_saved_usd plus the historical credit, as before) and restore the guard test, or record the omission as an owner decision and say so on the page.

Correct as checked:

  • Keep-alive token estimate. keepalive_saved_usd / (1.15 × input), with input recovered from cache_saved_usd / (0.9 × cache_read), has consistent units for 5-minute writes, and it is labelled "about" / "≈". It overstates tokens when the pings avoided 1h rewrites (2× input, gap 1.9×), and the input price is blended over the provider-cache mix rather than the keep-alive mix. A one-line caveat in the tooltip would cover both.
  • CSP.
    • No style= or inline handlers in index.html, and widths are set through the CSSOM.
    • The font is same-origin (default-src 'self' covers font-src) and served raw as woff2.
    • assetversion_test covers the font reference from style.css, and the OFL licence is included.
  • Benchmarks removal. The DDL is simply no longer created, so existing bench_* tables are left untouched and old databases open fine. The flag is kept as an ignored no-op, and TestBenchmarksRouteIsGone pins the route's removal.
  • Accessibility. The diagnostics use native <details>/<summary>, so they are keyboard and screen-reader friendly. Headings go h1 → h2 → h3 in order.
  • Tests.
    • go vet is clean, and the full go test -p 1 ./dash passes (69 s, idle host).
    • navhash.test.mjs passes 10/10, and the new browser smoke test TestUISmokeEveryTab passes with playwright-core and Chromium.
    • The deleted tests are for removed features (bench ingest, bottom line, v2), apart from the cachesplit guard above.
  • Publication. Screenshots use synthetic data (*@example.test, hashed ids, 2026 demo dates), and the PR text and docs have no internal paths or hosts. All 5 commits are signed off with no AI lines.

Non-blocking:

  1. charts.js is not lazy. It loads with <script defer> on every page; only the drawing waits until the diagnostics are opened. Either load it on the first toggle, or reword the claim.
  2. Likely cause of the +3.9 s Keep-alive median. The Overview now fetches api('keepalive') (loadOverviewKeepAlive), and the Keep-alive tab fetches the same heavy route again (loadKeepAlive) without reusing state.keepAliveLedger. Landing on Overview and then opening Keep-alive therefore queues a second ledger read behind the first. Reuse the in-flight promise or the cached ledger in loadKeepAlive, or defer the Overview's read until idle.
  3. Leftovers: dash/keepalivestrategy.go:91 still mentions /api/benchmarks, and the docs/design.md:335 diagram still lists bench_runs/tasks. The ignored --dashboard-bench-dirs could log one deprecation line when set.
  4. Clickable <tr class="click"> rows are mouse-only (no tabindex or Enter handler). This predates the PR, but they are worth a follow-up now that Sessions gained a sort control.

@OsherElhadad OsherElhadad left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Re-review: REQUEST CHANGES at c7d9425. The cache-reorder fold matches the agreed design, but there are three blockers. (Posted as a comment: GitHub does not let the author account request changes. This is a draft.)

Blockers

  1. Four backend tests were deleted by accident. This commit overwrites dash/savings_test.go with the new node wrapper, which removes TestSavingsTotalsAgreesWithOverview, TestSavingsTotalsScopesBySession, TestSavingsTotalsSessionInSumsAcrossSessionsInOneQuery and TestSavingsTotalsEmptyIsZeroNotError. They guard DB.SavingsTotals (still in dash/savings.go, used by /stats) against Overview's figures, and nothing replaces them. Restore the file from main and put TestSavingsPartsAddUp in a new file (for example savings_ui_test.go).
  2. keepAliveLedger() is shared across scopes. The promise is reused for 15 s whatever the filter is, but api('keepalive') takes its query from state.filter and the time range (qs()). Changing the tenant, session or range and then opening Keep-alive within 15 s shows the previous scope's ledger. Key the shared promise by qs(), or clear it when the filter changes.
  3. CI build is red. mkdocs build --strict aborts because how-to/dashboard-guide.md links ../img/dashboard/config-light.png and settings-light.png, which don't exist (the screenshots haven't been regenerated yet).

Checked and correct

  • Totals add up. savedParts() has cache = keepalive_net_usd + cachesplit_saved_usd + cachesplit_historical.usd, compaction = net_saved_usd, filter = decl_filter_usd. That is exactly the server's total_saved_usd: savingsArithmetic's net + cachesplit + kaNet, plus the historical credit in api.go, plus FilterUSD in SetDeclCredit, so nothing is counted twice. The "of which" sub-line is a subset of the cache row and is labelled. The "All three together" line is held back until the full read, which carries the filter.
  • Tests. node --test savings.test.mjs (2/2) and navhash.test.mjs (10/10) pass. The browser smoke test (TestUISmokeEveryTab) passes. The full go test -p 1 ./dash passes, and go vet is clean.
  • Plots and asset versioning. charts.js is now really lazy (loadScriptOnce when the fold opens), and the asset-versioning test lists it. savings.js is a quoted src, so it is versioned. No inline styles or handlers.
  • Keep-alive. The tooltip states the 5-minute-write caveat, and the tab now starts the recommendation together with the ledger.

Non-blocking

  • savings.test.mjs tests the arithmetic function only. Dropping the "of which" sub-line or the total line from the renderer fails no test, so have the smoke test assert saved-total and the sub-line.
  • docs/design.md:335 still lists bench_runs/tasks.
  • --dashboard-bench-dirs is ignored with a help-text note but logs no warning when set; the message said it would.

Osher-Elhadad added 8 commits October 15, 2026 12:05
Overview now shows the three savings (keep-alive, compaction, tools and skills filter) in
dollars and tokens against spend, tool and skill removal advice with the plugin commands, a
map of the other tabs, and one collapsed Detailed diagnostics area at the bottom.

Every tab gets a heading and a one-line intro from one table (TAB_INFO), plain section labels
and a sentence under each chart or table; jargon is replaced or defined with term(). Flat
sections, a bundled serif for titles and figures, a sliding nav underline, reduced-motion safe.

The Benchmarks tab, its route, its ingest code and the table DDL are removed;
--dashboard-bench-dirs stays as an ignored flag so an existing unit file still starts.

Adds a browser smoke test that loads every tab and checks the removed sections stay removed.

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
… on Inventory, compact Usage and Sessions, phone layout) and regenerated docs

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
…e-page home retired

New diagnostics plots (charts.js): net saving line, money saved and added as two-sided bars,
tokens removed, cache share, latency on separate scales, requests. Old five-page links
(#home, #cache) land on Overview and Keep-alive; /dashboard/home.html redirects. The sessions
list can be ordered by cost. The unused bottom-line banner code and its tests are removed.

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
… Overview

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
…uests, Sessions on a phone, Usage axis, Strategies form; Overview paints from the lean read and loads plots on demand; keep-alive drawn as an upper bound

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
…as waiting behind the ledger)

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
…um to total_saved_usd; shared keep-alive ledger; plots code loads when the fold opens; Keep-alive staggers its reads; stale benchmark mentions

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
…ctive names shown

Compaction becomes 'your preset' and keep-alive becomes 'cache strategy' in every heading, label, tooltip
and tab name (Cache strategy, Preset steps, Cache strategies, Strategy campaigns); the old ids and
anchors still work. The Overview rows show the preset the requests ran under and, for one account, the
cache strategy in force. Internal terms stay in Technical details folds.

Also: the single-flight group is keyed by cache and entry, because /api/stats and /api/tools collapsed
onto one computation when requested together.

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
@OsherElhadad
OsherElhadad force-pushed the feat/dashboard-ux-overhaul branch from c7d9425 to 325082c Compare October 15, 2026 12:06
…names; screenshots regenerated on the neutral demo

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
@OsherElhadad

Copy link
Copy Markdown
Collaborator Author

Replying to the comment that asked for user terms ("they know about preset, for instance conservative, and cache strategy, not compaction and keep-alive"). Done in this PR (head 708c8ec3), across every tab, tooltip, heading, the "About the other tabs" text and the docs guide.

before after
Cache keep-alive Cache strategy (for one account the row names it, for example "warm after 270 s idle")
Compaction Your preset (mostly conservative): the row names the preset the window's requests ran under
"Keep-alive savings", "Did keep-alive save you money?" "Savings from your cache strategy", "Did your cache strategy save you money?"
"Compaction savings", "Compacted or not" filter, "Why we didn't compact" "Savings from your preset", "Trimmed by your preset or not", "Why your preset left it alone"
tab Keep-alive tab Cache strategy
tab Components tab Preset steps
tab Strategies tab Cache strategies
tab Campaigns tab Strategy campaigns
keep-alive pings "a tiny request that keeps the cache warm"

Unchanged on purpose: MCP tools and skills, Inventory, Usage, Sessions, Requests, KV-cache. The old names appear only inside collapsed "Technical details" folds, with "(also called compaction)" / "(also called keep-alive)" so people can match them to logs. View ids and hash links (#/keepalive, #/components, ...) are unchanged, so old bookmarks still open the renamed tab. The plugin's own words match: /context-guru:save-more config for presets, /context-guru:save-more cache for cache strategies.

Overview with the new wording

Found while testing this: a bug in the dashboard's shared cache layer (fix in #565): /api/stats and /api/tools requested together collapsed onto one computation and /api/stats answered with the tools report. The Overview asks for both at once, so it exposed it; the same one-line fix is in this branch.

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
@OsherElhadad

Copy link
Copy Markdown
Collaborator Author

Full serial go test -p 1 -parallel 1 ./dash on head cfd6aba with the browser smoke test enabled: ok (96 s). The only failure in the earlier run was a test asserting the old button wording, now updated.

@OsherElhadad OsherElhadad left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Re-review: REQUEST CHANGES at 708c8ec. Two of the three blockers from c7d9425 are still open.

Blocking:

  1. The four DB.SavingsTotals tests are still deleted. On main, dash/savings_test.go holds TestSavingsTotalsAgreesWithOverview, TestSavingsTotalsScopesBySession, TestSavingsTotalsSessionInSumsAcrossSessionsInOneQuery and TestSavingsTotalsEmptyIsZeroNotError. At 708c8ec that file holds only TestSavingsPartsAddUp (127 lines changed, 120 removed), and the four tests exist nowhere in the tree. They guard the backend totals /stats serves. Restore the file from main and move TestSavingsPartsAddUp to its own file, for example savings_ui_test.go.
  2. keepAliveLedger() still ignores the scope. It reuses state.kaLedgerP for 15 s whatever the current filter, but api('keepalive') builds its query from the current tenant, session and range. Nothing resets kaLedgerP on a filter change. So changing tenant or range, then opening Keep-alive (or the Overview re-rendering) within 15 s, shows the previous scope's ledger. Key the shared promise by qs(), or clear it wherever the filter changes.

Fixed:

  • CI build (mkdocs --strict) is green. CI is green overall.
  • The cache row includes the cache-reorder credit, and the rows sum to total_saved_usd (TestSavingsPartsAddUp).
  • The singleflight key now includes the cache (%p of the jsonCache). That is a real bug fix: /api/stats and /api/tools collapsed onto one computation. There is no test for it in this PR. #565 changes the same jsonInflight.Do line, so the second to merge will conflict on dash/api.go. Keep one copy, plus #565's test.
  • JSON field names are unchanged apart from the removed benchmark types. The rename is UI text only.

Non-blocking, still open: dash/ui_smoke.mjs doesn't check that the "of which cache reorder" line or the "All three together" total renders.

…red ledger by query, the Together line shows estimate and upper bound apart, settings text in preset words, plain step fallback

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
@OsherElhadad

Copy link
Copy Markdown
Collaborator Author

Pushed fe579f2 for the three blockers and the small notes: (1) dash/savings_test.go is restored byte for byte from main (the four SavingsTotals tests are back; the arithmetic wrapper is in dash/savingsparts_test.go); (2) the shared keep-alive ledger is keyed by the whole query (test added); (3) docs: mkdocs build --strict passes on this head, the two images are in the tree. Also: the smoke test asserts the Together line and the cache-strategy row; docs/design.md no longer mentions bench_runs; the ignored flag logs a warning. From the design review: the line under the three rows now shows the estimate and the cache-strategy upper bound apart instead of one sum, and the Settings text says preset.

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
@OsherElhadad

Copy link
Copy Markdown
Collaborator Author

Design review, round 5: PASS at b2e7038.

Both round-4 blockers are closed. The Overview now reads "Together: $53.26 estimated saved, up to $157.68 more from your cache strategy (an upper bound), of $1,574 spent", with the estimate and the upper bound in separate colours and no blended sum. The Settings subtitle now says "preset settings"; no old term remains outside a definition or a Technical fold. All tabs have been judged on desktop light, with dark and phone checked for duplicates and error states.

Remaining POLISH before ready-for-review, none blocking:

  1. Rename the "KV-cache" tab (for example "Cache timing"); it is the one jargon tab name left.
  2. Cache strategy: shorten the two 3-line paragraphs and the clipped sessions table header.
  3. Settings and Preset steps: use the plain step names instead of raw identifiers in Settings.
  4. Style the date and file inputs on Strategy campaigns and Cache strategies.
  5. Requests: use the short account label as Sessions does; Sessions: one date format.
  6. Usage chart: add a y-axis maximum label.

…ut a description go in a fold

Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>

@OsherElhadad OsherElhadad left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Re-review: APPROVE at 875d960. (Posted as a comment: GitHub does not let the author account approve.) Both remaining blockers are fixed.

  • SavingsTotals tests restored. dash/savings_test.go is byte-identical to main (empty diff), so the four SavingsTotals backend tests are back. The savings-sum test lives in its own files (savings.test.mjs, savingsparts_test.go).
  • Ledger keyed by scope. keepAliveLedger() is keyed by the whole query (qs({}): tenant, window, filters), so a scope change within 15 s starts a fresh request instead of reusing the old scope's ledger. A failure clears the shared promise. The test is a source-pinning check in savingsparts_test.go, which is weaker than a behavioural test but adequate for a three-line guard.
  • Earlier fixes still hold: the three rows sum to total_saved_usd with the cache-reorder sub-line, CI build (mkdocs --strict) is green, and charts.js loads only when the fold opens.
  • go vet is clean. The dash SavingsTotals/SavingsParts/UI/Asset/Nav tests pass, and node navhash.test.mjs (10/10) and savings.test.mjs (2/2) pass. CI is green and the commits are signed off.

Non-blocking, carried over: the smoke test doesn't assert that the "of which" and "All three together" lines render. The single-flight key fix now lives in #574 — make sure this PR rebases cleanly onto it.

This approval covers 875d960.

@OsherElhadad

Copy link
Copy Markdown
Collaborator Author

Pushed 70750e7 with the polish round: KV-cache tab is 'Cache detail' (same anchor #/kvcache); Preset steps lists only steps the running build registers (from /api/options; a name in the history that the build no longer has is not shown, falls back to the described list if options are unavailable); plain step names (and one-clause jobs) for every step in Settings, the 'N of M keys set, read-only' lines shortened; date, time and file inputs styled like the other controls; account labels shortened (6 characters, full id in the tooltip) on the Cache strategy table too; the Cache strategy intro cut and its column header 'Avoided (at most)' no longer clips; Sessions and Requests share one date format. Full serial go test -p 1 -parallel 1 ./dash with the browser smoke test: ok (98 s). mkdocs --strict passes. Screenshots for the changed tabs regenerated on the neutral demo and OCR-scanned.

@OsherElhadad
OsherElhadad force-pushed the feat/dashboard-ux-overhaul branch from 70750e7 to 875d960 Compare October 15, 2026 13:15
@OsherElhadad
OsherElhadad marked this pull request as ready for review October 15, 2026 13:15
@OsherElhadad

Copy link
Copy Markdown
Collaborator Author

Frozen for release at 875d960 (approved, CI green): marked ready for review, no further pushes. My comment above mentioning 70750e7 describes the polish round, which is now a separate branch (feat/dashboard-ux-polish) and will be a small follow-up PR after this merges: Cache strategy paragraphs and clipped header, date/file input styling, the short account labels, one date format, Usage axis label, plain step names in Settings, and only-current steps in Preset steps.

@OsherElhadad OsherElhadad left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Re-review: APPROVE at 70750e7. Since 875d960, which I approved, there is one polish commit: steps limited to those the build registers (/api/options), plain step names in Settings, styled date and file inputs (CSS only; the added UI has no style= attributes, inline handlers or innerHTML), shorter labels, and refreshed screenshots. dash/savings_test.go is still identical to main. The single-flight change here is the same line as #574's, so after #574 merges it drops out or conflicts trivially. CI is green (build-test 9m36s, build/mkdocs, purego, trivy, DCO), and the commit is signed off. OK to leave draft from my side; merge order #574 → #565 → #564. This approval covers 70750e7.

@amiddavid
amiddavid merged commit 9b4ab5d into main Oct 15, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants