# Correlating analytics tables for evaluation

**Purpose:** Join spend, reflection outputs, and live state using shared identifiers.

## Canonical identifiers

| Column | Where | Notes |
|--------|--------|--------|
| `llm_call_log.trace_id` / `session_id` | Every LLM call when the harness sets trace context | Primary join key for cost ↔ behavior |
| `user_needs_history.trace_id` / `session_id` | Optional on inference rows | Populate when the post-conversation path has a stable id |
| `user_needs_history.trigger` | `soul_engine_tick`, post-conversation, backfill, … | Bucket counts in **Emotions → Mind analytics** deterministic stats (`user_needs_history_triggers`) |
| `emotional_weights.trace_id` / `session_id` | Optional | Same |
| `emotion_signal_events.trace_id` | Optional | Telegram can pass per-turn trace later |

## Recommended joins (examples)

- **Cost for a window:** `SELECT * FROM llm_call_log WHERE date(called_at) = ?`
- **Mind state + health:** `emotion_history.recorded_at` and `health_history.recorded_at` align with soul tick wall time (same bucket for analysis).
- **User needs over time:** `user_needs_history` stores JSON snapshots (levels + confidence per dimension). Join with soul tick timestamps the same way as `needs_history` for “human need track vs. Furoshiki derived needs” comparisons. Dashboard **Emotions → Mind state trends** charts **User needs over time**; API **`GET /dashboard/api/state-series`** (and **`furoshiki mind-state`**) return the merged series under `user_needs`.
- **Outreach cadence:** `outreach_state_history` filtered by `trigger` (`user_message`, `voice_send`, `pause_set`, …).

## Token spend source of truth

Use **`llm_call_log`** (and dashboard budget APIs). Legacy `memory/token-usage.json` is not authoritative for new work.

## Mind analytics report (operator)

- **Dashboard:** **Emotions → Mind analytics** — run a deterministic report (SQLite row counts, **`user_needs_history` trigger breakdown**, emotion stats, conversation alignment experiment, plus **`mind_state_series_summary`** counts including **`user_needs_samples`**), optional **AI narrative** via OpenRouter (`include_llm`, `model` query params). Source: `scripts/analytics_report.py`.
- **CLI:** `furoshiki analytics report [--json] [--hours N] [--llm] [--model …]`
- **API:** `GET /dashboard/api/analytics-report` and `GET /api/v1/analytics-report` (same query params; v1 requires Bearer).
- **Default LLM** for narrative: env **`FUROSHIKI_ANALYTICS_MODEL`**, else **`MODEL_FAST`** (tier). Override per run with `--model` or the dashboard field (shortcut, `MODEL_*` tier, or full OpenRouter id). LLM source key: **`analytics_report`** in `llm-routing.json` (max_tokens/temperature only; model comes from the request).
- **Persistence:** full JSON snapshots in SQLite table **`mind_analytics_reports`** — **`POST /dashboard/api/analytics-report/save`** / **`POST /api/v1/analytics-report/save`** (body `{ "report": { ... } }`, v1 needs **write** scope), **`GET /analytics-reports`**, **`GET /analytics-reports/{id}`**. CLI: **`furoshiki analytics report --save`**. Run **`furoshiki db migrate`** after pulling the schema.
