The JSONB approach for time-series is pragmatic for this scale. The 90-day sleep query concern is real though — have you considered a partial index on the timestamp field within the JSONB, or is the aggregation layer from Terra making that unnecessary? Also curious about the MCP server design: are you streaming responses back to Claude or returning complete payloads? For trend analysis over 90 days that could be a meaningful difference in perceived latency.
Good distinction, but the 90-day trend queries actually don't touch JSONB at all, because trends hit scalar columns (avg_hrv, duration_seconds, start_time) where a regular B-tree index is sufficient. The JSONB arrays are only used for sample-level queries like "show me my HR during last night's sleep" which are inherently single-session lookups, not range aggregations.
On streaming: currently returning complete payloads. For this use case it hasn't been a problem, because the trend queries aggregate 90 rows of scalar data which is fast, and the response is compact text. Streaming would make sense if I were piping large sample arrays directly, but those get aggregated server-side before returning. Worth revisiting if I add something like full workout trace exports.