diff --git a/.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md b/.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md
new file mode 100644
index 00000000..a25ce16f
--- /dev/null
+++ b/.planning/phases/02-transaction-search-diagrams/02-RESEARCH.md
@@ -0,0 +1,577 @@
+# Phase 2: Transaction Search + Diagrams - Research
+
+**Researched:** 2026-03-11
+**Domain:** ClickHouse querying, full-text search, SVG diagram rendering, REST API design
+**Confidence:** HIGH
+
+## Summary
+
+Phase 2 transforms the ingestion-only server into a queryable observability platform. The work divides into three domains: (1) structured search with ClickHouse WHERE clauses over the existing `route_executions` table plus schema extensions for exchange snapshot data, (2) full-text search using ClickHouse's `tokenbf_v1` skip indexes (the text index GA feature requires ClickHouse 26.2+ and we run 25.3), and (3) route diagram retrieval and server-side SVG rendering using Eclipse ELK for layout and JFreeSVG for output.
+
+The existing Phase 1 code provides a solid foundation: `ClickHouseExecutionRepository` already flattens processor trees into parallel arrays, `ClickHouseDiagramRepository` already stores diagrams with SHA-256 content-hash deduplication, and `AbstractClickHouseIT` provides the Testcontainers base class. Phase 2 extends these with query methods, schema additions for exchange data and tree reconstruction metadata, and new search/diagram REST controllers.
+
+**Primary recommendation:** Extend the existing repository interfaces with query methods, add a `SearchService` abstraction in core (for future OpenSearch swap), store exchange snapshot data as JSON strings in new columns on `route_executions`, and use Eclipse ELK 0.11.0 + JFreeSVG 5.0.7 for diagram rendering.
+
+
+## User Constraints (from CONTEXT.md)
+
+### Locked Decisions
+- Both GET and POST endpoints for search: GET /api/v1/search/executions for basic filters, POST for advanced filters including full-text and per-field targeting
+- Response envelope: `{ "data": [...], "total": N, "offset": 0, "limit": 50 }`
+- Substring matching (LIKE '%term%') for full-text search -- not token-based only
+- Global `text` parameter searches all text fields; optional per-field targeting: textInBody, textInHeaders, textInErrors
+- Search service interface designed for future OpenSearch swap
+- Nested JSON tree returned by server for transaction detail -- server reconstructs processor tree from flat storage
+- Add depth and parent index arrays to ClickHouse schema (processor_depths, processor_parent_indexes) -- populated at ingestion time
+- Exchange snapshot data fetched separately per processor -- not inlined in detail response
+- Diagram accessed via separate endpoint; detail response includes diagram content hash for linking
+- Both SVG and JSON layout formats via Accept header content negotiation
+- Top-to-bottom node layout flow
+- Nested processors in swimlanes to highlight nesting/scope
+- Color-coded node types matching route-diagram-example.html style
+- Store everything the agent sends -- no server-side truncation
+- API designed to support future cmd+k cross-entity search UI
+
+### Claude's Discretion
+- Pagination implementation details (offset/limit vs cursor)
+- ClickHouse schema extension approach for exchange snapshot storage
+- SVG rendering library choice
+- Layout algorithm for diagram node positioning
+- Search service abstraction layer design
+
+### Deferred Ideas (OUT OF SCOPE)
+- Cursor-based pagination (ASRCH-01) -- v2
+- Saved search queries (ASRCH-02) -- v2
+- Web UI with cmd+k search overlay -- v2
+- Execution overlay on diagrams -- UI responsibility
+- OpenSearch for full-text search -- evaluate after Phase 2
+
+
+
+## Phase Requirements
+
+| ID | Description | Research Support |
+|----|-------------|-----------------|
+| SRCH-01 (#7) | Search transactions by execution status (COMPLETED, FAILED, RUNNING) | WHERE clause on `status` column (LowCardinality, in ORDER BY) -- highly efficient |
+| SRCH-02 (#8) | Search transactions by date/time range | WHERE clause on `start_time` (in ORDER BY, partition key) -- primary index range scan |
+| SRCH-03 (#9) | Search transactions by duration range (min/max ms) | WHERE clause on `duration_ms` -- simple range filter |
+| SRCH-04 (#10) | Search by correlationId for cross-instance correlation | WHERE + bloom_filter skip index on `correlation_id` (already exists) |
+| SRCH-05 (#11) | Full-text search across bodies, headers, errors, stack traces | LIKE '%term%' on text columns + tokenbf_v1 skip indexes; schema extension needed for body/header storage |
+| SRCH-06 (#12) | Transaction detail with nested processor execution tree | Reconstruct tree from parallel arrays using processor_depths + processor_parent_indexes; ARRAY JOIN query |
+| DIAG-01 (#20) | Content-addressable diagram versioning | Already implemented: ReplacingMergeTree with SHA-256 content_hash |
+| DIAG-02 (#21) | Transaction links to active diagram version | Add `diagram_content_hash` column to `route_executions`; populated at ingestion from latest diagram |
+| DIAG-03 (#22) | Server renders route diagrams from stored definitions | Eclipse ELK for layout + JFreeSVG for SVG output; JSON layout alternative via Accept header |
+
+
+## Standard Stack
+
+### Core (already in project)
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| Spring Boot | 3.4.3 | Web framework, DI, JdbcTemplate | Already established in Phase 1 |
+| ClickHouse JDBC | 0.9.7 | Database driver | Already established in Phase 1 |
+| Jackson | 2.17.3 | JSON serialization | Already established in Phase 1 |
+| springdoc-openapi | 2.8.6 | API documentation | Already established in Phase 1 |
+| Testcontainers | 2.0.3 | ClickHouse integration tests | Already established in Phase 1 |
+
+### New for Phase 2
+| Library | Version | Purpose | When to Use |
+|---------|---------|---------|-------------|
+| Eclipse ELK Core | 0.11.0 | Graph layout algorithm (layered/hierarchical) | Diagram node positioning |
+| Eclipse ELK Layered | 0.11.0 | Sugiyama-style top-to-bottom layout | The actual layout algorithm |
+| JFreeSVG | 5.0.7 | Programmatic SVG generation via Graphics2D API | Rendering diagram to SVG string |
+
+### Alternatives Considered
+| Instead of | Could Use | Tradeoff |
+|------------|-----------|----------|
+| Eclipse ELK | Manual layout algorithm | ELK handles edge crossing minimization, node spacing, layer assignment -- non-trivial to implement correctly |
+| JFreeSVG | Apache Batik | Batik is 98x more memory than JSVG; JFreeSVG is lightweight, 5x faster, zero dependencies beyond JDK 11+ |
+| JFreeSVG | Manual SVG string building | JFreeSVG handles SVG escaping, coordinate systems, text metrics correctly; manual strings are error-prone |
+| Separate exchange table | JSON columns on route_executions | Separate table adds JOINs; JSON strings on the main table keep queries simple and align with "fetch snapshot separately" pattern |
+
+**Installation (new dependencies for app module pom.xml):**
+```xml
+
+ org.eclipse.elk
+ org.eclipse.elk.core
+ 0.11.0
+
+
+ org.eclipse.elk
+ org.eclipse.elk.alg.layered
+ 0.11.0
+
+
+ org.jfree
+ org.jfree.svg
+ 5.0.7
+
+```
+
+## Architecture Patterns
+
+### Recommended Project Structure (additions for Phase 2)
+```
+cameleer3-server-core/src/main/java/com/cameleer3/server/core/
+├── search/
+│ ├── SearchService.java # Orchestrates search, delegates to SearchEngine
+│ ├── SearchEngine.java # Interface for search backends (ClickHouse now, OpenSearch later)
+│ ├── SearchRequest.java # Immutable search criteria record
+│ └── SearchResult.java # Paginated result envelope record
+├── detail/
+│ ├── DetailService.java # Reconstructs execution tree from flat data
+│ └── ExecutionDetail.java # Rich detail model with nested tree
+├── diagram/
+│ └── DiagramRenderer.java # Interface: render RouteGraph -> SVG or JSON layout
+└── storage/
+ ├── ExecutionRepository.java # Extended with query methods
+ └── DiagramRepository.java # Extended with lookup methods
+
+cameleer3-server-app/src/main/java/com/cameleer3/server/app/
+├── controller/
+│ ├── SearchController.java # GET + POST /api/v1/search/executions
+│ ├── DetailController.java # GET /api/v1/executions/{id}
+│ └── DiagramRenderController.java # GET /api/v1/diagrams/{hash} with content negotiation
+├── search/
+│ └── ClickHouseSearchEngine.java # SearchEngine impl using JdbcTemplate
+├── diagram/
+│ ├── ElkDiagramRenderer.java # DiagramRenderer impl: ELK layout + JFreeSVG
+│ └── DiagramLayoutResult.java # JSON layout format DTO
+└── storage/
+ └── ClickHouseExecutionRepository.java # Extended with query + detail methods
+```
+
+### Pattern 1: Search Engine Abstraction (for future OpenSearch swap)
+**What:** Interface in core module, ClickHouse implementation in app module
+**When to use:** All search operations go through this interface
+**Example:**
+```java
+// Core module: search engine interface
+public interface SearchEngine {
+ SearchResult search(SearchRequest request);
+ long count(SearchRequest request);
+}
+
+// Core module: search service orchestrates
+public class SearchService {
+ private final SearchEngine engine;
+
+ public SearchResult search(SearchRequest request) {
+ return engine.search(request);
+ }
+}
+
+// App module: ClickHouse implementation
+@Repository
+public class ClickHouseSearchEngine implements SearchEngine {
+ private final JdbcTemplate jdbcTemplate;
+
+ @Override
+ public SearchResult search(SearchRequest request) {
+ // Build dynamic WHERE clause from SearchRequest
+ // Execute against route_executions table
+ }
+}
+```
+
+### Pattern 2: Dynamic SQL Query Building
+**What:** Build WHERE clauses from optional filter parameters
+**When to use:** Search queries with combinable filters
+**Example:**
+```java
+public SearchResult search(SearchRequest req) {
+ var conditions = new ArrayList();
+ var params = new ArrayList