---
phase: 02-transaction-search-diagrams
plan: 04
type: execute
wave: 1
depends_on: ["02-01", "02-02", "02-03"]
files_modified:
  - cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java
  - cameleer-server-app/pom.xml
  - cameleer-server-app/src/test/java/com/cameleer/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: "cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java"
      provides: "Diagram hash lookup during batch insert"
      contains: "findContentHashForRoute"
    - path: "cameleer-server-app/pom.xml"
      provides: "Surefire fork configuration isolating ELK classloader"
      contains: "reuseForks"
    - path: "cameleer-server-app/src/test/java/com/cameleer/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"
---

<objective>
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`
</objective>

<execution_context>
@C:/Users/Hendrik/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/Hendrik/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.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

<interfaces>
<!-- Key types and contracts the executor needs. -->

From cameleer-server-core/.../storage/DiagramRepository.java:
```java
Optional<String> findContentHashForRoute(String routeId, String agentId);
```

From cameleer-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 cameleer-server-app/.../storage/ClickHouseDiagramRepository.java:
```java
@Repository
public class ClickHouseDiagramRepository implements DiagramRepository {
    public Optional<String> findContentHashForRoute(String routeId, String agentId) { ... }
}
```
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Populate diagram_content_hash during ingestion and fix Surefire forks</name>
  <files>
    cameleer-server-app/src/main/java/com/cameleer/server/app/storage/ClickHouseExecutionRepository.java,
    cameleer-server-app/pom.xml,
    cameleer-server-app/src/test/java/com/cameleer/server/app/storage/DiagramLinkingIT.java
  </files>
  <behavior>
    - 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)
  </behavior>
  <action>
    **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 `cameleer-server-app/pom.xml`, add a `<build><plugins>` section (after the existing `spring-boot-maven-plugin`) with `maven-surefire-plugin` configuration:
       ```xml
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-plugin</artifactId>
           <configuration>
               <forkCount>1</forkCount>
               <reuseForks>false</reuseForks>
           </configuration>
       </plugin>
       ```
       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.
  </action>
  <verify>
    <automated>cd C:/Users/Hendrik/Documents/projects/cameleer-server && mvn clean verify -pl cameleer-server-app -am 2>&1 | tail -30</automated>
  </verify>
  <done>
    - 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 cameleer-server-app (no classloader failures from ElkDiagramRendererTest)
  </done>
</task>

</tasks>

<verification>
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)
</verification>

<success_criteria>
- 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
</success_criteria>

<output>
After completion, create `.planning/phases/02-transaction-search-diagrams/02-04-SUMMARY.md`
</output>
