v4: Performance profiling of session query, search, highlight, and cross-reference operations
| Operation | What it measures | Median | Min–Max | Stdev | Distribution |
|---|---|---|---|---|---|
| session_tool list | Full session listing: load YAML, query iTerm panes, read JSONL versions, detect quiescence via buffer reads, detect JSONL wait states — all in parallel | 784ms | 764–796 | ±12 | |
| session_tool cleanup | Load sessions.yaml + it2api list-sessions, find entries whose iTerm pane is gone | 403ms | 401–416 | ±6 | |
| rg content search | ripgrep counts of 'model' across -Users-tchklovski-all-code-rivus/ — the raw I/O cost of searching all session logs | 56ms | 53–355 | ±134 | |
| read 1 pane buffer | Single it2api get-buffer call — reads terminal screen contents from one iTerm pane. ~300ms is Python process startup overhead | 301ms | 292–317 | ±10 | |
| tab color cycle (1 pane) | Set tab_color → enable use_tab_color → disable use_tab_color on one pane. 3 sequential it2api calls | 961ms | 950–995 | ±18 | |
| highlight flow (1 panes) | End-to-end: it2api list-sessions → set tab color on 1 panes → unset all. Simulates 'find + highlight' without rg search | 1268ms | 1261–1330 | ±30 | |
| YAML ↔ iTerm cross-ref | Load sessions.yaml + it2api list-sessions, then match registered sessions to live panes. Measures the minimum cost of 'are my sessions still alive?' | 305ms | 295–319 | ±10 | |
| find --state idle | State-only filter: loads sessions.yaml + it2api list-sessions, filters by title state. No rg search, no JSONL reads — fastest possible find | 407ms | 405–422 | ±7 | |
| find 'model' | Keyword search: checks badge+name (in-memory), then rg across all JSONL files for content matches. Ranks by badge(+10) > name(+8) > JSONL hits(+3-8) | 778ms | 766–871 | ±42 | |
| find 'benchmark' --all | Keyword search including dead sessions (no live pane). Same as keyword search but doesn't skip sessions without iTerm panes — shows historical matches | 794ms | 766–826 | ±22 |
session find uses a 3-phase search: (1) metadata match — badge + name, (2) rg content search across JSONL, (3) parallel buffer reads for state enrichment.
Phases are skipped when not needed (e.g., state-only queries skip rg entirely).
| Query | Goal | Phases Used | Min (ms) | Median (ms) | Max (ms) | Bar |
|---|---|---|---|---|---|---|
find --state idle |
Find all idle sessions | metadata only | 405 | 407 | 422 | |
find 'model' |
766 | 778 | 871 | |||
find 'benchmark' --all |
Search including dead sessions | metadata + rg (all) | 766 | 794 | 826 |
| Phase | What | Cost | When |
|---|---|---|---|
| 1. Metadata | Load sessions.yaml + it2api list-sessions + hub badges | ~320ms | Always |
| 2. Content | rg across 620MB JSONL | ~60ms | Only if query provided |
| 3. Enrich | Parallel it2api get-buffer for alive matches | N × 320ms / threads | Only if state filter or permission detection needed |
With N panes: Phase 3 is the parallelism opportunity. Sequential: N × 320ms. ThreadPool(8): ceil(N/8) × 320ms. MCPretentious: N × 1.2ms.
| Scenario | 1 pane | 5 panes | 15 panes |
|---|---|---|---|
| Sequential it2api | 320ms | 1,600ms | 4,800ms |
| ThreadPool(8) | 320ms | 320ms | 640ms |
| MCPretentious | 1ms | 6ms | 18ms |
| Operation | Sequential (ms) | Concurrent (ms) | Speedup | Visual |
|---|---|---|---|---|
| buffer reads x1 | 298 | 306 | 0.97x |
|
| tab color x1 | 944 | 1052 | 0.90x |
|
| rg x4 patterns | 277 | 181 | 1.53x |
|
Each component of get_enriched_sessions() measured independently.
The total explains the ~14s wall time of session_tool list.
| Component | Median (ms) | % of Total | Waterfall |
|---|---|---|---|
| claude --version | 52 | 3.9% | |
| it2api list-sessions | 316 | 23.6% | |
| JSONL rglob+version (1 session) | 13 | 1.0% | |
| JSONL rglob+version (43 sessions) | 654 | 48.9% | |
| it2api get-buffer x1 seq (all alive) | 302 | 22.6% | |
| Hub DB badge load | 1 | 0.1% | |
| JSONL wait state detect (1 session) | 1 | 0.0% | |
| SUM (estimated) | 1339 | 100% |
| Operation | Category | Min | Median | Mean | Max | Stdev | Trials |
|---|---|---|---|---|---|---|---|
| session_tool list | sequential | 764 | 784 | 782 | 796 | 12 | 5 |
| session_tool cleanup | sequential | 401 | 403 | 406 | 416 | 6 | 5 |
| rg content search | sequential | 53 | 56 | 116 | 355 | 134 | 5 |
| read 1 pane buffer | sequential | 292 | 301 | 303 | 317 | 10 | 5 |
| tab color cycle (1 pane) | sequential | 950 | 961 | 966 | 995 | 18 | 5 |
| highlight flow (1 panes) | sequential | 1261 | 1268 | 1285 | 1330 | 30 | 5 |
| YAML ↔ iTerm cross-ref | sequential | 295 | 305 | 309 | 319 | 10 | 5 |
| find --state idle | sequential | 405 | 407 | 410 | 422 | 7 | 5 |
| find 'model' | sequential | 766 | 778 | 796 | 871 | 42 | 5 |
| find 'benchmark' --all | sequential | 766 | 794 | 795 | 826 | 22 | 5 |
| buffer reads x1 (sequential) | sequential | 288 | 298 | 298 | 306 | 8 | 5 |
| buffer reads x1 (concurrent) | concurrent | 290 | 306 | 305 | 317 | 10 | 5 |
| tab color x1 (sequential) | sequential | 937 | 944 | 966 | 1036 | 42 | 5 |
| tab color x1 (concurrent) | concurrent | 940 | 1052 | 1077 | 1328 | 151 | 5 |
| rg x4 patterns (sequential) | sequential | 206 | 277 | 321 | 624 | 173 | 5 |
| rg x4 patterns (concurrent) | concurrent | 166 | 181 | 188 | 212 | 22 | 5 |
Each it2api call spawns a Python process. With 15+ alive panes needing buffer reads,
that's 15 × 305ms = 4.5s just in process startup overhead.
| Scenario | Buffer Reads | JSONL Version | list-sessions | Other | Total | vs Current |
|---|---|---|---|---|---|---|
| Current (it2api, sequential) | 302ms | 654ms | 316ms | 62ms | 1334ms | baseline |
| + ThreadPool parallelism | 101ms | 654ms | 316ms | 62ms | 1132ms | 1.2x |
| + MCPretentious (255x buffer) | 1ms | 654ms | 158ms | 62ms | 875ms | 1.5x |
| + direct JSONL path (no rglob) | 1ms | 50ms | 158ms | 62ms | 271ms | 5x |
| # | Change | Saves | Effort |
|---|---|---|---|
| 1 | MCPretentious for get-buffer + send-text Replace it2api subprocess calls in supervisor/adapters/iterm2.py with persistent WebSocket |
~301ms (255x) | medium |
| 2 | Direct JSONL path instead of rglobJSONL_DIR / f"{sid}.jsonl" instead of rglob(f"{sid}.jsonl") |
~604ms | trivial |
| 3 | ThreadPool for remaining it2api calls Parallelize badge/title/profile property calls that can't use MCPretentious |
~201ms (if no MCP) | easy |
| 4 | Cache claude --version Version doesn't change within a session — cache for 5 min |
~52ms | trivial |
Primary bottleneck: it2api calls