Dashboard graph: lazy-load Mermaid bundle and serve compressed assets #74
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
The dashboard Graph tab loads Mermaid 10.9.1 (
crates/cognix-server/assets/mermaid.min.js, 3 335 717 bytes / 3.18 MiB raw) via a synchronous<script>tag on every dashboard page view, even though Graph is one of four tabs and most page loads never render a graph. Asset routes serve raw bytes with noContent-Encoding— Mermaid gzips to ~700 KB and brotli-11s to ~520 KB, so the wire payload is 4–6× larger than necessary.Source: PR #73 multi-agent code review (Performance dimension, two CRITICAL findings)
File:
crates/cognix-server/assets/index.html:74,crates/cognix-server/src/transport/ui/assets.rsBranch:
phase-8/dashboard-ui-pr4-graph(now merged target)Failure mode
defernorasync. PR #73 addeddefer, which unblocks parsing — but the asset is still fetched eagerly on every load.crates/cognix-server/src/transport/ui/;grep -E 'gzip|brotli|deflate|compress|content-encoding'returns zero hits.test_dashboard_bundle_size_excluding_mermaidasserts non-Mermaid assets stay under 200 KB. The assertion is technically accurate but operationally misleading: the actual payload users download is ~17× that, and future Mermaid upgrades or new vendor deps will not be caught by a budget that explicitly excludes the elephant.Why deferred from PR #73
.br/.gzblobs); it touches the asset table and route handler shape and warrants its own review.Proposed work
<script defer src="/ui/assets/mermaid.min.js">inindex.htmlwith on-demand loading:graph.jsinjects the script (or uses dynamicimport()after rewrap as an ES module) on the first call torenderGraph, gated by a memoizedmermaidLoadPromisesingleton. Subsequent Graph-tab clicks reuse the loaded engine.Loading graph engine…message while the bundle is in-flight on the first activation.include_bytes!("mermaid.min.js.br")andinclude_bytes!("mermaid.min.js.gz"), plus the raw bytes. Pick the variant matching the request'sAccept-Encoding; emitContent-Encoding: br|gzipandVary: Accept-Encoding. Apply the same to other text assets in the asset table.test_dashboard_bundle_size_excluding_mermaid(or add a sibling) into atest_dashboard_total_wire_payload_budgetthat asserts the total brotli'd payload of all served assets stays under a documented ceiling (suggested: 1.2 MB). Document the ceiling indocs/features/phases/phase-8-dashboard.md.docs/features/phases/phase-8-dashboard.mdwith the new lazy-load contract and the trust assumption (Mermaid only loads on Graph-tab activation).Acceptance
mermaid.min.js.Content-Encoding: br(orgzipfallback) is present on all asset responses when the request advertises support; uncompressed bytes still served when the client opts out.crates/cognix-server/src/transport/ui/assets.rscontinue to pass against the raw bytes (compression is transport-layer, not content-layer).test_dashboard_total_wire_payload_budgettest fails if the brotli'd total grows past the documented ceiling.transport::uiandtests/integration/ui_graph_routes.rspass without modification.cargo deny/cargo auditfindings introduced by the compression middleware.References
feat: add dashboard thought graph viewer.claude/rules/coding-style.md— file/function size budgets (informs the wire-payload budget pattern)