Life Dashboard: From 3 Bugs to a Full Telemetry Platform

claude-codelife-dashboardglobe-glanalyticshtmx

Life Dashboard: From 3 Bugs to a Full Telemetry Platform

The Starting Point

The life-dashboard MVP shipped two days ago: 55K events ingested, Globe.gl wired up, HTMX fragments pulling from Postgres. It worked. But three things on the homepage were broken: the Globe HUD wasn’t populating, toggle buttons did nothing, and the 7-Day Trends chart showed placeholder data. Quick bug fixes. Thirty minutes, tops.

That was the plan.

The Three Bugs Were One Bug

All three homepage issues traced to the same root cause: Globe.gl’s Globe()(container) replaces the container’s children. The HUD, toggles, and trend panels were siblings inside the globe container. On mount, Globe.gl destroyed them. The fix was a wrapper div — mount the globe into a child, keep the UI elements as siblings.

Once that was understood, all three bugs resolved in minutes. The 7-Day Trends needed one more fix: wiring the chart to the daily_summary materialized view via a new /api/globe/weekly endpoint instead of hardcoded placeholder data.

And then Today’s Telemetry was showing live HRV readings that were irrelevant to most days. Replaced with Events/Steps/Drives/Battery — metrics that actually exist for every day.

Four bugs fixed. The session should have ended here.

The Globe Deserved Its Own Page

The homepage globe panel was cramped. All the interactive features — heatmap, timeline, 3D mode, trails, labels — were fighting for space. So: a full-screen globe page at /static/globe.html. Six toggle buttons, a time slider with configurable window sizes, keyboard shortcuts.

The binary MAP toggle (on/off) felt limiting. Used a research agent to find free tile providers. CartoDB dark_nolabels won — zoom 20, retina tiles, no API key, perfect for dark-themed data visualization. Built a dropdown with 6 providers: CartoDB Dark, Esri Satellite, OSM Street, Stamen Toner, OpenTopoMap, and the default (none).

Then location search. The geocache table already had addresses from the reverse-geocoding pipeline. Added a search bar with autocomplete hitting /api/globe/search. Click a result, fly to it, automatically load nearby locations via haversine distance query in /api/globe/nearby. Pure SQL haversine, no PostGIS needed.

Progressive click-to-zoom: 1st click goes to city level, 2nd to street, 3rd to location, 4th opens Google Street View. A small thing, but it makes exploring locations feel intentional rather than random.

Zoom-adaptive scaling solved the skyscraper problem. Points, hexbins, arcs, labels — everything scales proportionally with camera altitude via an OrbitControls change listener.

Mobile zoom controls for iPad: the +/- buttons, because pinch-to-zoom was broken on touch devices.

The Entry Log

With 55,479 events across 7.2 years, there was no way to browse them. The globe shows spatial patterns but not temporal ones.

Built /log — a searchable, filterable event browser with HTMX infinite scroll. Eight card renderers covering all event types: journal entries with their full text, highlights with source links, social interactions, charger sessions with plug/unplug times, driving records, audio listening, sleep data, clipboard captures.

The 23 individual event types collapsed into 7 visual categories. The grouping is UI-only — the database and APIs still use individual types. This keeps the data model honest while making the browser usable.

Each card gets a Tabler icon from a self-hosted SVG sprite (~8KB, 24 icons). The sprite pattern works with HTMX fragments because it’s pure HTML reference — no JS execution needed in swapped fragments.

Analytics Endpoints

Five analytics endpoints, all following the SQL-first architecture (define the query, wrap it in a thin route):

  1. Dwell time — charger plug/unplug session durations by location
  2. HRV stress map — average HRV by location, surfacing where you’re most/least stressed
  3. Routine score — weekly percentage of time at your top 3 locations
  4. Radius of gyration — weekly roam distance, how far you range from center
  5. Top recurring routes — most-traveled driving arcs

The haversine query for radius of gyration uses the same LEAST(1, ...) floating-point clamp as the search proximity query. Without it, ACOS occasionally gets values slightly above 1.0 and returns NaN.

Documentation and API Docs

Built a hand-crafted API docs page at /docs-api with collapsible sections, method badges, and “Try it” links. Plus auto-generated Markdown at /docs-api.md with print-to-PDF via marked.js.

The llms.txt ecosystem got three variants: hand-curated /llms.txt, auto-generated /llms-core.txt (with params), and minified /llms-mini.txt. Core and mini are cached at startup via generate_llms_cache(app) to avoid per-request generation.

UTC to EST conversion at the display layer: all server-side timestamp rendering now uses America/New_York via zoneinfo. Database stays UTC epoch milliseconds. Display-layer-only timezone conversion is the right call — it keeps the data portable and the queries simple.

38 Tests in 1.26 Seconds

Integration tests covering the full API surface. 38 tests, all passing, all running in under 2 seconds. Not a heroic number, but enough to catch regressions on the routes that matter.

The Agent Architecture

This session spawned roughly 20 parallel agents for research, implementation, and testing. The pattern that worked: research agents run first, then implementation agents receive the research results in their prompts. The research agent that found CartoDB dark tiles saved hours of manual testing. The implementation agents that built the entry log card renderers ran in parallel because each card type is independent.

What Shipped

  • 4 bug fixes (all same root cause)
  • Full-screen globe page with 6 tile providers
  • Location search with GPS proximity
  • Progressive click-to-zoom to Google Street View
  • Zoom-adaptive scaling for all visual elements
  • Mobile zoom controls
  • Entry log with 8 card renderers and infinite scroll
  • 5 analytics endpoints
  • Tabler Icons SVG sprite (24 icons)
  • llms.txt ecosystem (3 variants)
  • Hand-built API docs page
  • UTC to EST conversion
  • 38 integration tests

What’s Next

The globe has temporal navigation now (time slider), but the analytics need time-series views — routine score over months, radius of gyration trends, seasonal patterns in dwell time. The 55K events span 7.2 years. That’s enough data for meaningful longitudinal analysis. The infrastructure is ready. The queries just need to be written.