diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 8b96d6ba..b6263051 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -45,12 +45,13 @@ Plans:
2. User can full-text search across message bodies, headers, error messages, and stack traces and find matching transactions
3. User can retrieve a transaction's detail view showing the nested processor execution tree
4. Route diagrams are stored with content-addressable versioning (identical definitions stored once), each transaction links to its active diagram version, and diagrams can be rendered from stored definitions
-**Plans:** 1/3 plans executed
+**Plans:** 4 plans (3 executed, 1 gap closure)
Plans:
- [ ] 02-01-PLAN.md -- Schema extension, core domain types, ingestion updates for search/detail columns
- [ ] 02-02-PLAN.md -- Diagram rendering with ELK layout and JFreeSVG (SVG + JSON via content negotiation)
- [ ] 02-03-PLAN.md -- Search endpoints (GET + POST), transaction detail with tree reconstruction, integration tests
+- [ ] 02-04-PLAN.md -- Gap closure: populate diagram_content_hash during ingestion, fix Surefire classloader isolation
### Phase 3: Agent Registry + SSE Push
**Goal**: Server tracks connected agents through their full lifecycle and can push configuration updates, deep-trace commands, and replay commands to specific agents in real time
@@ -89,6 +90,6 @@ Note: Phases 2 and 3 both depend only on Phase 1 and could execute in parallel.
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Ingestion Pipeline + API Foundation | 3/3 | Complete | 2026-03-11 |
-| 2. Transaction Search + Diagrams | 1/3 | In Progress| |
+| 2. Transaction Search + Diagrams | 3/4 | Gap Closure | |
| 3. Agent Registry + SSE Push | 0/2 | Not started | - |
| 4. Security | 0/1 | Not started | - |
diff --git a/.planning/phases/02-transaction-search-diagrams/02-04-PLAN.md b/.planning/phases/02-transaction-search-diagrams/02-04-PLAN.md
new file mode 100644
index 00000000..ed2b7dd6
--- /dev/null
+++ b/.planning/phases/02-transaction-search-diagrams/02-04-PLAN.md
@@ -0,0 +1,159 @@
+---
+phase: 02-transaction-search-diagrams
+plan: 04
+type: execute
+wave: 1
+depends_on: ["02-01", "02-02", "02-03"]
+files_modified:
+ - cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java
+ - cameleer3-server-app/pom.xml
+ - cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java
+autonomous: true
+gap_closure: true
+requirements: ["DIAG-02"]
+
+must_haves:
+ truths:
+ - "Each transaction links to the RouteGraph version that was active at execution time"
+ - "Full test suite passes with mvn clean verify (no classloader failures)"
+ artifacts:
+ - path: "cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java"
+ provides: "Diagram hash lookup during batch insert"
+ contains: "findContentHashForRoute"
+ - path: "cameleer3-server-app/pom.xml"
+ provides: "Surefire fork configuration isolating ELK classloader"
+ contains: "reuseForks"
+ - path: "cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java"
+ provides: "Integration test proving diagram hash is stored during ingestion"
+ key_links:
+ - from: "ClickHouseExecutionRepository"
+ to: "DiagramRepository"
+ via: "constructor injection, findContentHashForRoute call in insertBatch"
+ pattern: "diagramRepository\\.findContentHashForRoute"
+---
+
+
+Close two verification gaps from Phase 2: (1) populate diagram_content_hash during ingestion instead of storing empty string, and (2) fix Surefire classloader conflict so `mvn clean verify` passes.
+
+Purpose: DIAG-02 requirement is architecturally complete but never populated. The test suite breaks in CI due to ELK static init poisoning the shared JVM.
+Output: Working diagram linking during ingestion + green `mvn clean verify`
+
+
+
+@C:/Users/Hendrik/.claude/get-shit-done/workflows/execute-plan.md
+@C:/Users/Hendrik/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/02-transaction-search-diagrams/02-VERIFICATION.md
+
+Prior plan summaries (needed — touches same files):
+@.planning/phases/02-transaction-search-diagrams/02-01-SUMMARY.md
+@.planning/phases/02-transaction-search-diagrams/02-03-SUMMARY.md
+
+
+
+
+From cameleer3-server-core/.../storage/DiagramRepository.java:
+```java
+Optional findContentHashForRoute(String routeId, String agentId);
+```
+
+From cameleer3-server-app/.../storage/ClickHouseExecutionRepository.java (line 141):
+```java
+ps.setString(col++, ""); // diagram_content_hash (wired later)
+```
+The class is @Repository annotated, constructor takes JdbcTemplate only. It needs DiagramRepository injected to perform the lookup.
+
+From cameleer3-server-app/.../storage/ClickHouseDiagramRepository.java:
+```java
+@Repository
+public class ClickHouseDiagramRepository implements DiagramRepository {
+ public Optional findContentHashForRoute(String routeId, String agentId) { ... }
+}
+```
+
+
+
+
+
+
+ Task 1: Populate diagram_content_hash during ingestion and fix Surefire forks
+
+ cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/ClickHouseExecutionRepository.java,
+ cameleer3-server-app/pom.xml,
+ cameleer3-server-app/src/test/java/com/cameleer3/server/app/storage/DiagramLinkingIT.java
+
+
+ - Test 1: When a RouteGraph is ingested before a RouteExecution for the same routeId+agentId, the execution's diagram_content_hash column contains the SHA-256 hash of the diagram (not empty string)
+ - Test 2: When no RouteGraph exists for a route, the execution's diagram_content_hash is stored as empty string (graceful fallback)
+
+
+ **Gap 1 — Diagram hash linking (DIAG-02):**
+
+ 1. Modify `ClickHouseExecutionRepository` constructor to accept `DiagramRepository` as a second parameter alongside `JdbcTemplate`. The class is `@Repository` and both dependencies are Spring-managed beans, so constructor injection will autowire both.
+
+ 2. In `insertBatch()`, inside the `BatchPreparedStatementSetter.setValues()` method, replace line 141:
+ ```java
+ ps.setString(col++, ""); // diagram_content_hash (wired later)
+ ```
+ with a lookup:
+ ```java
+ String diagramHash = diagramRepository
+ .findContentHashForRoute(exec.getRouteId(), exec.getAgentId())
+ .orElse("");
+ ps.setString(col++, diagramHash); // diagram_content_hash
+ ```
+ Note: `findContentHashForRoute` returns the most recent content_hash for the route+agent pair from `route_diagrams` table (ORDER BY created_at DESC LIMIT 1). If no diagram exists yet, it returns empty Optional, and we fall back to empty string.
+
+ 3. Performance consideration: The lookup happens per-execution in the batch. Since batches are flushed periodically (not per-request) and diagram lookups hit ClickHouse with a simple indexed query, this is acceptable. If profiling shows issues later, a per-batch cache of routeId+agentId -> hash can be added.
+
+ 4. Create `DiagramLinkingIT` integration test extending `AbstractClickHouseIT`:
+ - Test 1: Insert a RouteGraph via `ClickHouseDiagramRepository.store()`, then insert a RouteExecution for the same routeId+agentId via `ClickHouseExecutionRepository.insertBatch()`, then query `SELECT diagram_content_hash FROM route_executions WHERE execution_id = ?` and assert it equals the expected SHA-256 hash.
+ - Test 2: Insert a RouteExecution without any prior RouteGraph for that route. Assert `diagram_content_hash` is empty string.
+
+ **Gap 2 — Surefire classloader isolation:**
+
+ 5. In `cameleer3-server-app/pom.xml`, add a `` section (after the existing `spring-boot-maven-plugin`) with `maven-surefire-plugin` configuration:
+ ```xml
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ 1
+ false
+
+
+ ```
+ This forces Surefire to fork a fresh JVM for each test class, isolating ELK's static initializer (LayeredMetaDataProvider + xtext CollectionLiterals) from Spring Boot's classloader. Trade-off: slightly slower test execution, but correct results.
+
+
+ cd C:/Users/Hendrik/Documents/projects/cameleer3-server && mvn clean verify -pl cameleer3-server-app -am 2>&1 | tail -30
+
+
+ - diagram_content_hash is populated with the active diagram's SHA-256 hash during ingestion (not empty string)
+ - DiagramLinkingIT passes with both positive and negative cases
+ - `mvn clean verify` passes for cameleer3-server-app (no classloader failures from ElkDiagramRendererTest)
+
+
+
+
+
+
+1. `mvn clean verify` passes end-to-end (no test failures)
+2. DiagramLinkingIT confirms diagram hash is stored during execution ingestion
+3. All existing tests still pass (search, detail, diagram render)
+
+
+
+- DIAG-02 fully satisfied: transactions link to their active RouteGraph version via diagram_content_hash
+- `mvn clean verify` is green (all ~40+ tests pass without classloader errors)
+- No regression in existing search, detail, or diagram functionality
+
+
+