Status: UI Components Complete (39/39)
Last Updated: 2026-01-25
Reference: specs/observability-ui.md
| System | Spec | Crates | Web Packages | Migration |
|---|---|---|---|---|
| Crash | specs/crash-system.md | loom-crash-core, loom-crash ✅, loom-crash-symbolicate ✅, loom-server-crash ✅ |
@loom/crash |
033_crash_analytics.sql |
| Crons | specs/crons-system.md | loom-crons-core ✅, loom-crons ✅, loom-server-crons ✅ |
@loom/crons ✅ |
034_cron_monitoring.sql |
| Sessions | specs/sessions-system.md | loom-sessions-core ✅, loom-server-sessions ✅ |
(in @loom/crash) |
035_sessions.sql (tables: app_sessions, app_session_aggregates) |
| UI | specs/observability-ui.md | — | web/loom-web/src/lib/components/ |
— |
Goal: Build Svelte 5 components for the observability UI.
Reference pattern: web/loom-web/src/lib/ui/, web/loom-web/src/lib/components/
Path: web/loom-web/src/lib/components/common/
-
StatCard.svelte— Metric display with trend -
Sparkline.svelte— Mini inline chart -
TimeRangePicker.svelte— Time range selector -
RelativeTime.svelte— "5 minutes ago" display -
CopyButton.svelte— Copy to clipboard
Path: web/loom-web/src/lib/components/crash/
Reference: specs/observability-ui.md#42-core-component-examples
-
IssueList.svelte— Paginated issue list with filters -
IssueListItem.svelte— Single issue row -
IssueDetail.svelte— Full issue view -
IssueStatusBadge.svelte— Status indicator (Unresolved, Resolved, Regressed) -
CrashEventCard.svelte— Event summary -
CrashEventDetail.svelte— Full event with context -
Stacktrace.svelte— Collapsible frame viewer -
StacktraceFrame.svelte— Single frame with expand -
SourceContext.svelte— Syntax-highlighted source lines -
Breadcrumbs.svelte— Breadcrumb timeline -
ActiveFlags.svelte— Feature flags at crash time -
UserContext.svelte— User info display -
SymbolUpload.svelte— Source map upload form
Path: web/loom-web/src/lib/components/crons/
-
MonitorList.svelte— Monitor list with health -
MonitorListItem.svelte— Single monitor row -
MonitorDetail.svelte— Monitor with history -
MonitorForm.svelte— Create/edit monitor -
MonitorStatusBadge.svelte— Status indicator -
MonitorHealthBadge.svelte— Health indicator -
CheckInTimeline.svelte— Check-in history -
CheckInItem.svelte— Single check-in -
CronScheduleInput.svelte— Cron expression input -
PingUrlDisplay.svelte— Ping URL with copy -
UptimeChart.svelte— Uptime visualization
Path: web/loom-web/src/lib/components/sessions/
-
ReleaseHealthOverview.svelte— Dashboard card -
ReleaseHealthCard.svelte— Single release health -
ReleaseList.svelte— All releases with metrics -
ReleaseListItem.svelte— Single release row -
ReleaseDetail.svelte— Release detail page -
CrashFreeChart.svelte— Crash-free rate over time -
AdoptionChart.svelte— Release adoption stacked area -
SessionList.svelte— Recent sessions -
AdoptionStageBadge.svelte— Adoption stage indicator
Following pattern: web/loom-web/src/lib/ui/Button.stories.ts
- Add
.stories.tsfile for each component - Define argTypes for interactive controls
- Create multiple story variations
- Use
createRawSnippet()for snippet props
Goal: Create SvelteKit page routes for observability UI.
Status: Core routes implemented ✅
Path: web/loom-web/src/routes/(app)/
Implemented routes:
-
/crashes/— Project list -
/crashes/[projectId]/— Issue list with filters -
/crashes/[projectId]/issues/[issueId]/— Issue detail with events -
/crons/— Monitor list with health filtering -
/crons/new/— Create new monitor -
/crons/[slug]/— Monitor detail with check-in timeline -
/sessions/— Release health overview -
/sessions/releases/[version]/— Release detail
- Crash: listCrashProjects, getCrashProject, listIssues, getIssue, resolveIssue, etc.
- Crons: listMonitors, getMonitor, createMonitor, updateMonitor, pauseMonitor, resumeMonitor
- Sessions: listAppSessions, listReleaseHealth, getReleaseHealth
- Handle authentication and authorization
- Update header navigation to include observability sections (dropdown menu)
- Add
/crons/newroute for creating monitors - Create sub-navigation for each section (deferred)
Goal: Wire up SSE for real-time updates across the UI.
Path: web/loom-web/src/lib/realtime/
-
observability-sse.ts— SSE connection manager for observability (CronsSSEClient, CrashSSEClient) - Event handlers for:
issue.new,issue.regressed,monitor.missed,checkin_ok,checkin_error
- Add SSE subscription to overview dashboard
- Add SSE subscription to issue list
- Add SSE subscription to monitor list (
/cronspage with LIVE badge indicator) - Add SSE subscription to release health
Reference: specs/observability-ui.md#62-notification-system
- Create
NotificationProvider.svelte - Create
showNotification()utility - Wire up regression alerts to observability pages
- Component unit tests with Testing Library
- Storybook interaction tests
- Visual regression tests (optional)
- README for
@loom/crash - README for
@loom/crons - README for
loom-crashcrate - README for
loom-cronscrate
- Getting started with crash analytics
- Setting up cron monitoring
- Understanding release health
Crash API endpoints verified via curl:
GET /api/crash/projects?org_id=...— List crash projects ✓POST /api/crash/capture— Capture crash event ✓GET /api/crash/projects/{id}/issues— List issues ✓GET /api/crash/projects/{id}/issues/{id}— Get issue details ✓POST /api/crash/projects/{id}/issues/{id}/resolve— Resolve issue ✓POST /api/crash/projects/{id}/issues/{id}/unresolve— Unresolve issue ✓POST /api/crash/projects/{id}/issues/{id}/ignore— Ignore issue ✓GET /api/crash/projects/{id}/events— List crash events ✓GET /api/crash/projects/{id}/api-keys— List API keys ✓GET /api/crash/projects/{id}/releases— List releases ✓
Crash CLI commands verified:
loom crash projects --org ...— List projects ✓loom crash issues --project ...— List issues ✓
Sessions API endpoints verified via curl:
POST /api/sessions/start— Start a session ✓POST /api/sessions/end— End a session ✓GET /api/app-sessions?project_id=...— List sessions ✓GET /api/app-sessions/releases?project_id=...— List release health ✓GET /api/app-sessions/releases/{version}?project_id=...— Get release health detail ✓
Sessions CLI commands verified:
loom sessions list --project ...— List sessions ✓loom sessions releases --project ...— List release health ✓loom sessions release --project ... --version ...— Get release health detail ✓
Tests: All 28 sessions authz tests pass (cargo test -p loom-server --test authz_tests sessions)
Bug fix: Fixed CLI display of crash-free rates (was multiplying by 100 twice, showing 10000% instead of 100%)
Full API endpoints verified via curl:
GET /api/crons/monitors?org_id=...— List monitors ✓POST /api/crons/monitors— Create monitor (returns ping_url) ✓GET /api/crons/monitors/{slug}?org_id=...— Get monitor details ✓PATCH /api/crons/monitors/{slug}— Update monitor (org_id in body) ✓DELETE /api/crons/monitors/{slug}?org_id=...— Delete monitor ✓POST /api/crons/monitors/{slug}/pause?org_id=...— Pause monitoring ✓POST /api/crons/monitors/{slug}/resume?org_id=...— Resume monitoring ✓GET /api/crons/monitors/{slug}/checkins?org_id=...— List check-ins ✓
Ping endpoints verified via curl (no auth required):
GET /ping/{key}— Success ping ✓GET /ping/{key}/start— Start ping (returns checkin_id) ✓GET /ping/{key}/fail?exit_code=...— Fail ping ✓POST /ping/{key}— Ping with body (output capture) ✓
All CLI commands verified:
loom crons monitors --org ...— List monitors ✓loom crons create --org ... --slug ... --name ... --cron "..."— Create monitor ✓loom crons get --org ... --slug ...— Get monitor details ✓loom crons update --org ... --slug ... --name ...— Update monitor ✓loom crons delete --org ... --slug ...— Delete monitor ✓loom crons pause --org ... --slug ...— Pause monitoring ✓loom crons resume --org ... --slug ...— Resume monitoring ✓loom crons checkins --org ... --slug ...— List check-ins ✓loom crons ping <key>— Send success ping ✓loom crons ping-fail <key>— Send fail ping ✓
Tests: All 42 crons authz tests pass (cargo test -p loom-server --test authz_tests crons)
Stats API endpoints implemented and verified via curl:
GET /api/crons/monitors/{slug}/stats?org_id=...&period=...— Get monitor stats ✓- Response includes: total_checkins, successful_checkins, failed_checkins, missed_checkins, timeout_checkins
- Duration metrics: avg_duration_ms, p50_duration_ms, p95_duration_ms, max_duration_ms
- Uptime percentage calculation
- Period options: day, week (default), month
GET /api/crons/stats/overview?org_id=...— Get org-wide stats overview ✓- Monitor counts: total_monitors, active_monitors, paused_monitors
- Health counts: healthy_monitors, failing_monitors, missed_monitors
- 24h metrics: total_checkins_24h, total_failures_24h, overall_uptime_percentage
CLI commands verified:
loom crons stats --org ... --slug ... --period ...— Get monitor stats ✓loom crons overview --org ...— Get stats overview ✓
Tests: All 49 crons authz tests pass (cargo test -p loom-server --test authz_tests crons)
- Added tests for: org_member_can_get_monitor_stats, unauthenticated_cannot_get_monitor_stats, org_b_member_cannot_get_org_a_monitor_stats, nonexistent_monitor_stats_returns_not_found, org_member_can_get_stats_overview, unauthenticated_cannot_get_stats_overview, org_b_member_cannot_get_org_a_stats_overview
Added navigation for observability features:
- Added dropdown menu in header navigation with Crashes, Crons, Sessions links
- Added i18n keys for navigation:
nav.observability,nav.crashes,nav.crons,nav.sessions - Created
/crons/newroute for creating new monitors using MonitorForm component
Files modified:
web/loom-web/src/routes/(app)/+layout.svelte— Added observability dropdown menuweb/loom-web/src/locales/en/messages.po— Added navigation i18n keysweb/loom-web/src/routes/(app)/crons/new/+page.svelte— New route for creating monitors
Build: Verified pnpm build succeeds
Created SSE clients for real-time observability updates:
CronsSSEClient— Connects to/api/crons/streamfor cron monitor eventsCrashSSEClient— Connects to/api/crash/projects/{id}/streamfor crash events- Both support auto-reconnect with exponential backoff
Created notification system:
NotificationProvider.svelte— Displays toast notifications for alertsshowNotification()/dismissNotification()utilities- Supports info, success, warning, error types with optional links
Files created:
web/loom-web/src/lib/realtime/observability-sse.ts— SSE clientsweb/loom-web/src/lib/components/notifications/NotificationProvider.svelteweb/loom-web/src/lib/components/notifications/index.ts
Files modified:
web/loom-web/src/lib/realtime/index.ts— Export new SSE clientsweb/loom-web/src/routes/(app)/+layout.svelte— Added NotificationProvider
Build: Verified pnpm build succeeds
Routes verified (all return HTTP 200):
https://loom.ghuntley.com/crashes✓https://loom.ghuntley.com/crons✓https://loom.ghuntley.com/crons/new✓https://loom.ghuntley.com/sessions✓
Note: Routes use plural form (/crons not /cron)
API fix deployed:
- Fixed
GET /api/crash/projectsto return{ projects: [...] }instead of plain array - Matches expected format in web client
CrashProjectListResponsetype
Analytics self-monitoring infrastructure:
- Added
analytics_api_keyfield toSelfMonitoringConfigstruct - Created
ensure_analytics_api_key()function to auto-generate internal analytics API keys - Added
GET /api/self-monitoring/analytics-configendpoint for web SDK configuration
loom-web analytics integration:
- Added
@loom/analyticsworkspace dependency to package.json - Created
$lib/analytics/self-monitoring.ts— Fetches config from server and initializes AnalyticsClient - Created
$lib/analytics/AnalyticsProvider.svelte— Wraps app with auto-identification:- Identifies users on login via
identify()with user ID, email, and display_name - Resets analytics identity on logout via
reset()
- Identifies users on login via
- Integrated into
(app)/+layout.sveltewith user data - Enabled autocapture for pageviews and pageleave events
Files created:
web/loom-web/src/lib/analytics/self-monitoring.tsweb/loom-web/src/lib/analytics/index.tsweb/loom-web/src/lib/analytics/AnalyticsProvider.svelte
Files modified:
crates/loom-server/src/self_monitoring.rs— Added analytics API key supportcrates/loom-server/src/routes/self_monitoring.rs— Added analytics config endpointcrates/loom-server/src/api.rs— Registered new routeweb/loom-web/package.json— Added @loom/analytics dependencyweb/loom-web/src/routes/(app)/+layout.svelte— Integrated AnalyticsProvider
API endpoints verified via curl:
POST /api/analytics/capture— Event capture ✓POST /api/analytics/batch— Batch capture ✓POST /api/analytics/identify— User identification ✓POST /api/analytics/set— Set properties ✓GET /api/orgs/{org_id}/analytics/api-keys— List API keys ✓POST /api/orgs/{org_id}/analytics/api-keys— Create API key ✓
Build: All components build successfully
Deployment verified:
GET /api/self-monitoring/analytics-configreturns API key, release, environment ✓POST /api/analytics/capturewith self-monitoring key — Event captured ✓POST /api/analytics/identifywith self-monitoring key — User identified ✓POST /api/analytics/batchwith self-monitoring key — Batch captured ✓
Added explicit click tracking across all major pages in loom-web:
Tracking helper functions added to $lib/analytics/self-monitoring.ts:
trackLinkClick(linkName, href, properties)— Track link clicks with destinationtrackButtonClick(buttonName, properties)— Track button clickstrackFormSubmit(formName, properties)— Track form submissionstrackModalOpen(modalName, properties)— Track modal openstrackModalClose(modalName, properties)— Track modal closestrackFilterChange(filterName, value, properties)— Track filter changestrackAction(action, resourceType, resourceId, properties)— Track user actions
Pages with tracking added:
-
App Layout (
+layout.svelte):- All header navigation links (threads, repos, weavers, crashes, crons, sessions, settings, admin)
- Logout button
-
Weavers page (
/weavers):- New Weaver button, Logs button, Attach link, Delete button
- Modal tracking (create weaver modal open/close)
- Image preset selection
-
Crashes pages (
/crashes,/crashes/[projectId],/crashes/[projectId]/issues/[issueId]):- Project card links, org filter changes
- Issue clicks, status filter, time range picker
- Resolve/unresolve/ignore actions, back links, event clicks
-
Crons pages (
/crons,/crons/new,/crons/[slug]):- New Monitor button, health filter, org filter, monitor clicks
- Form submit, cancel button
- Pause/resume/delete actions, back link
-
Sessions page (
/sessions):- Org/project/time range filters
- Release clicks
-
Repos page (
/repos):- New repo button, repo links
- Modal tracking (create repo modal open/close)
- Retry button
-
Settings pages:
- Settings nav links (sessions, profile, orgs)
- Profile save form, locale change
Commits:
f371b777— Add analytics click tracking to all major pages7ef80ff2— Add analytics tracking to detail pages and settings
Deployment verified: Changes pushed to trunk and auto-deployed
Self-monitoring infrastructure for Loom monitoring itself:
- Created
self_monitoring.rsmodule in loom-server - Automatically creates "Loom Internal" organization with well-known UUID
- Creates internal crash projects: loom-server, loom-web, loom-cli
- Generates internal API keys for crash capture
- Installs panic hook for automatic loom-server crash reporting
API endpoints for self-monitoring configuration:
GET /api/self-monitoring/web-config- Returns crash SDK config for loom-webGET /api/self-monitoring/cli-config- Returns crash SDK config for loom-cliGET /api/self-monitoring/projects- Returns internal project IDs
loom-web integration:
- Created
$lib/crash/self-monitoring.ts- Fetches config and initializes CrashClient - Created
SelfMonitoringProvider.svelte- Wraps app layout with crash monitoring - Added @loom/crash dependency
- Installs global error handlers for automatic crash capture
loom-cli integration:
- Created
self_monitoring.rsmodule - Added loom-crash dependency
- Initializes crash monitoring on CLI startup
- Installs panic hook for automatic crash reporting
Files created:
crates/loom-server/src/self_monitoring.rscrates/loom-server/src/routes/self_monitoring.rscrates/loom-cli/src/self_monitoring.rsweb/loom-web/src/lib/crash/self-monitoring.tsweb/loom-web/src/lib/crash/index.tsweb/loom-web/src/lib/crash/SelfMonitoringProvider.svelte
Build: All components build successfully (cargo build -p loom-server -p loom-cli, pnpm build)
| Phase | Description | Status |
|---|---|---|
| 1-6 | Backend foundation | ✅ Complete |
| 7-8 | SDKs | ✅ Complete |
| 9 | Web UI components | ✅ Complete (39/39 components) |
| 10 | Page routes | ✅ Complete (8 routes) |
| 11 | SSE integration | In Progress (SSE clients + notifications done) |
| 12 | Background jobs | ✅ Complete |
| 13 | Testing (backend) | ✅ Complete |
| 13 | Testing (UI) | Pending |
| 14 | Documentation (OpenAPI) | ✅ Complete |
| 14 | Documentation (SDK/Guides) | Pending |
| 15 | Deployment & verification | ✅ Complete |
Remaining effort: SSE integration, Storybook stories, UI tests
Status: Backend Complete, Frontend Integration Pending
Spec: specs/analytics-system.md
| Component | Location | Status |
|---|---|---|
| Core types | crates/loom-analytics-core/ |
✅ Complete |
| Rust SDK | crates/loom-analytics/ |
✅ Complete |
| Server handlers | crates/loom-server-analytics/ |
✅ Complete |
| TypeScript SDK | web/packages/analytics/ |
✅ Complete |
| Database | migrations/032_analytics.sql |
✅ Complete |
| API routes | /api/analytics/* |
✅ Complete |
| Config | loom-server-config/sections/analytics.rs |
✅ Complete |
| Flag integration | loom-flags/src/analytics.rs |
✅ Complete |
| Authz tests | tests/authz/analytics.rs |
✅ Complete |
| loom-web integration | $lib/analytics/ |
✅ Complete |
| Analytics UI pages | — | ❌ Not started |
Path: crates/loom-analytics-core/
-
person.rs— Person, PersonWithIdentities types -
identity.rs— PersonIdentity, IdentityType enum -
event.rs— Event, EventProperty types -
identify.rs— IdentifyPayload, AliasPayload -
api_key.rs— AnalyticsApiKey, AnalyticsKeyType -
error.rs— Error types with thiserror
Path: crates/loom-analytics/
-
client.rs— AnalyticsClient with builder pattern -
batch.rs— Event batching with flush interval -
properties.rs— Properties helper type -
error.rs— SDK error types
Path: crates/loom-server-analytics/
-
routes.rs— Axum route definitions -
handlers/capture.rs— Event capture endpoint -
handlers/identify.rs— Identity resolution -
handlers/persons.rs— Person queries -
handlers/events.rs— Event queries -
handlers/api_keys.rs— API key management -
repository.rs— Database operations -
identity_resolution.rs— Merge logic -
middleware.rs— API key auth middleware -
api_key.rs— Key validation
Migration: crates/loom-server/migrations/032_analytics.sql
-
analytics_persons— Tracked users -
analytics_person_identities— distinct_id → person mapping -
analytics_events— Event records -
analytics_person_merges— Merge audit trail -
analytics_api_keys— API key storage
Path: crates/loom-server/src/routes/analytics.rs
SDK Routes (API Key Auth):
-
POST /api/analytics/capture— Single event (Write key) -
POST /api/analytics/batch— Batch events (Write key) -
POST /api/analytics/identify— Identity resolution (Write key) -
POST /api/analytics/alias— Alias distinct_ids (Write key) -
POST /api/analytics/set— Set person properties (Write key) -
GET /api/analytics/persons— List persons (ReadWrite key) -
GET /api/analytics/persons/{id}— Get person (ReadWrite key) -
GET /api/analytics/persons/by-distinct-id/{id}— Lookup by distinct_id (ReadWrite key) -
GET /api/analytics/events— List events (ReadWrite key) -
GET /api/analytics/events/count— Count events (ReadWrite key) -
POST /api/analytics/events/export— Bulk export (ReadWrite key)
Management Routes (User Auth):
-
GET /api/orgs/{org_id}/analytics/api-keys— List keys -
POST /api/orgs/{org_id}/analytics/api-keys— Create key -
DELETE /api/orgs/{org_id}/analytics/api-keys/{id}— Revoke key
Path: web/packages/analytics/
-
client.ts— AnalyticsClient class -
batch.ts— BatchProcessor for event queuing -
storage.ts— DistinctIdManager (localStorage, cookie, memory) -
types.ts— TypeScript type definitions -
errors.ts— Error classes with isRetryable() -
index.ts— Public exports - Unit tests for all modules
Features:
-
capture()— Track events -
identify()— Link anonymous → authenticated -
alias()— Link two distinct_ids -
set()— Set person properties -
reset()— Generate new anonymous ID (logout) - Autocapture ($pageview, $pageleave)
- Configurable batching (interval, max size)
- Multiple storage modes
- Config section:
loom-server-config/sections/analytics.rsLOOM_ANALYTICS_ENABLEDLOOM_ANALYTICS_BATCH_SIZELOOM_ANALYTICS_FLUSH_INTERVAL_SECSLOOM_ANALYTICS_EVENT_RETENTION_DAYS
- Feature flag integration:
loom-flags/src/analytics.rs$feature_flag_calledevent capture
- Authorization tests:
tests/authz/analytics.rs(1017 lines)
Goal: Integrate @loom/analytics SDK into the web frontend.
- Add
@loom/analyticsdependency to loom-web package.json - Create
$lib/analytics/self-monitoring.ts— Fetch config and initialize AnalyticsClient - Create
AnalyticsProvider.svelte— Wrap app layout with user identification - Auto-track pageviews on route changes (via autocapture)
- Wire identify() to auth state changes
- Call reset() on logout
- Add self-monitoring analytics endpoint to loom-server (
GET /api/self-monitoring/analytics-config)
Goal: Create pages to view analytics data (persons, events).
Path: web/loom-web/src/routes/(app)/analytics/
-
/analytics/— Overview dashboard -
/analytics/persons/— Person list with search -
/analytics/persons/[id]/— Person detail with events -
/analytics/events/— Event explorer with filters -
/analytics/api-keys/— API key management
Components needed: web/loom-web/src/lib/components/analytics/
-
PersonList.svelte— Paginated person list -
PersonDetail.svelte— Person profile with identities -
EventList.svelte— Event timeline/table -
EventDetail.svelte— Single event view -
ApiKeyList.svelte— API key management -
ApiKeyForm.svelte— Create API key form
- README for
@loom/analytics - README for
loom-analyticscrate - Integration guide: Getting started with product analytics
- API reference documentation
| Phase | Description | Status |
|---|---|---|
| A1 | Backend crates | ✅ Complete |
| A2 | Database schema | ✅ Complete |
| A3 | API endpoints | ✅ Complete |
| A4 | TypeScript SDK | ✅ Complete |
| A5 | Config & integration | ✅ Complete |
| A6 | loom-web integration | ✅ Complete |
| A7 | Analytics UI pages | ❌ Not started |
| A8 | SDK documentation | ❌ Not started |
Remaining effort: Analytics UI pages, SDK documentation