# Session Overview Browser + /jump Command

**Date:** 2026-02-22
**Status:** Design

## Problem

No way to see all active sessions at a glance in a browser. Finding "where did I work on X?" requires manually checking individual sessions. The `/hist` skill shows one session at a time — need a cross-session view and semantic search.

## Components

### 1. Browser Overview (new "Overview" tab in watch.localhost)

**What:** New Gradio tab showing all active Claude sessions, laid out vertically — every tab visible, no nesting.

**Layout:** One card per iTerm2 tab, each showing its panes stacked. Per pane:
- Topic tree (the ASCII art from watch API state)
- Badge text
- Recency indicator: green dot (< 5m), yellow (< 30m), gray (> 30m), dim (> 2h)

**Data flow:**
1. `it2 hierarchy` → JSON with `windows[].tabs[].sessions[]` (iTerm session IDs per pane, grouped by tab/window)
2. Watch DB `sessions` table → join `iterm_session_id` to get `session_id` (Claude UUID)
3. For each matched pane, load `~/.coord/helm_state/{claude_sid}.json` → tree, badge, last_update
4. Sort tabs by most-recently-active pane
5. Render as `gr.HTML` block with custom HTML/CSS

**Note:** `it2 sessions` is flat (no hierarchy). `it2 hierarchy` provides the window/tab/pane tree.
State JSON files don't store iTerm IDs — the helm DB is the join point.

**Refresh:** On-demand (page load + refresh button). No auto-refresh / WebSocket.

**Implementation:** New tab in `watch/app.py`, data fetching in a `refresh_overview()` function. HTML rendered server-side as a string (same pattern as billing summary).

### 2. `/jump` Skill

**Usage:** `/jump work on foo` in any Claude session

**Flow:**
1. Parse query text from skill args
2. Embed query via `lib/llm/embed.py` (OpenAI text-embedding-3-small)
3. Search Qdrant `sessions` collection via `lib/vectors/`
4. Display top 5 matches: session ID (linkable), topic tree snippet, score, recency, project
5. User can then manually switch to the desired session

**Index updates:** On each Helm API tree update, re-embed and upsert to sqlite-vec. Content = flattened topic tree + badge. Keyed by session_id + content_hash (skip if unchanged).

**Output format:** Scored list in terminal, not auto-navigation. Always shows options.

### 3. Shared Vector Infrastructure (`lib/vectors/`)

Replaces the current approach of JSON-serialized embeddings in SQLite columns with Qdrant local mode.

**Embedding provider:** OpenAI `text-embedding-3-small` via `lib/llm/embed.py`
- Change default model from `gemini/gemini-embedding-001` to `text-embedding-3-small`
- $0.02/M tokens — effectively free at our scale
- Already have OpenAI API key, litellm handles routing

**Vector storage:** Qdrant local mode (`qdrant-client` Python package)
- Sessions: `~/.coord/watch_vectors/` (managed by `supervisor/watch/`)
- Learnings: `~/.coord/learning_vectors/` (managed by `learning/`)
- Separate DBs — unrelated data, different lifecycles, no coupling
- Backed by SQLite internally, persistent across restarts
- Rich filtering: must/should/must_not on payload fields (project, type, recency)
- Hybrid search: dense + sparse vectors, fused via RRF
- Same API as Qdrant server — zero-migration path if we ever need to scale

**Collections:**

| Collection | Source | Content embedded | Payload fields |
|------------|--------|-----------------|----------------|
| `sessions` | Helm API state files | topic tree + badge text | project, last_update, iterm_id |
| `learnings` | learning.db instances | content + context + tags | project, type, domain_tags, confidence |
| `principles` | learning.db principles | title + text + rationale | category, domains, application_count |

**Consumers to migrate:**

| Consumer | Current approach | Migration |
|----------|-----------------|-----------|
| `learning/cli.py` (`learn find -s`) | JSON vectors in SQLite, manual cosine sim, Python boosting | Use `lib/vectors/` Qdrant search. Preserve principle boost + value boost as score modifiers. |
| `learning/cli.py` (`learn embed`) | Batch embed → store as JSON in learning.db | Batch embed → upsert to Qdrant `learnings`/`principles` collections |
| `learning/cli.py` (auto-embed hook) | Embed on insert → JSON in learning.db | Embed on insert → upsert to Qdrant |
| `learning/cli.py` (`retrieve_xml`) | Same as `learn find -s` | Same migration |
| `intel/people/cluster.py` | Uses `embed_texts()` + numpy | No change needed — only uses embedding function, not storage |
| `/jump` (new) | N/A | New consumer of `lib/vectors/` |

**Cost:** ~$0.01 per 1000 documents. 100 queries/day = ~$0.04/year.

## TODO (future)

- [ ] **Search box in browser overview** — text input that filters/highlights matching sessions using the same embedding backend as `/jump`
- [ ] **Face identity DB** — `intel/people/cluster.py` currently does one-off numpy clustering. Migrate to persistent Qdrant DB (`~/.coord/face_vectors/`) via `lib/vectors/` so identities accumulate across lookups. Different embedding model (ArcFace/face_recognition, not text), same storage layer. Vectors + metadata in Qdrant, actual photos/face files stay on disk — payload stores file paths only.

## Files to Create/Modify

| File | Change |
|------|--------|
| `lib/vectors/__init__.py` | New: shared Qdrant local wrapper (connect, upsert, search, collections) |
| `lib/llm/embed.py` | Change default model to `text-embedding-3-small` |
| `supervisor/watch/app.py` | Add "Overview" tab with `refresh_overview()` |
| `supervisor/watch/overview.py` | New: data fetching (it2 hierarchy + state files), HTML rendering |
| `helm/api.py` | After tree update, upsert session embedding to sqlite-vec |
| `learning/cli.py` | Migrate `find -s`, `embed`, auto-embed hook to use `lib/vectors/` |
| `learning/schema/learning_store.py` | Remove JSON embedding columns (or keep as fallback) |
| `~/.claude/skills/jump/skill.md` | New: `/jump` skill definition |

## Design Decisions

- **Gradio tab vs standalone page:** Gradio tab — keeps everything in watch.localhost, consistent with existing dashboard
- **Flat layout vs nested windows/tabs/panes:** Flat vertical list of tabs, each showing its panes — everything visible at once
- **Embedding provider:** OpenAI `text-embedding-3-small` — already have key, $0.02/M tokens, one provider to manage
- **Vector storage:** Qdrant local — richer filtering/faceting than ChromaDB, hybrid search out of box, same API as server mode
- **Shared lib over separate implementations:** `lib/vectors/` serves both learning search and session /jump — one vector DB, one embedding model
- **Jump always shows options:** Never auto-navigate — always dry-run with scored matches. Safer and more useful.
