diff --git a/cameleer3-server-core/pom.xml b/cameleer3-server-core/pom.xml
index 5e2e517c..fcc2542d 100644
--- a/cameleer3-server-core/pom.xml
+++ b/cameleer3-server-core/pom.xml
@@ -37,6 +37,11 @@
spring-security-core
provided
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ test
+
org.junit.jupiter
junit-jupiter
diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/ExecutionChunk.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/ExecutionChunk.java
new file mode 100644
index 00000000..20066f09
--- /dev/null
+++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/ExecutionChunk.java
@@ -0,0 +1,42 @@
+package com.cameleer3.server.core.storage.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Chunk document: exchange envelope + list of FlatProcessorRecords.
+ * Mirrors cameleer3-common ExecutionChunk — replace with common lib import when available.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public record ExecutionChunk(
+ String exchangeId,
+ String applicationName,
+ String agentId,
+ String routeId,
+ String correlationId,
+ String status,
+ Instant startTime,
+ Instant endTime,
+ Long durationMs,
+ String engineLevel,
+ String errorMessage,
+ String errorStackTrace,
+ String errorType,
+ String errorCategory,
+ String rootCauseType,
+ String rootCauseMessage,
+ Map attributes,
+ String traceId,
+ String spanId,
+ String originalExchangeId,
+ String replayExchangeId,
+ int chunkSeq,
+ @JsonProperty("final") boolean isFinal,
+ List processors
+) {}
diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/FlatProcessorRecord.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/FlatProcessorRecord.java
new file mode 100644
index 00000000..deb89221
--- /dev/null
+++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/storage/model/FlatProcessorRecord.java
@@ -0,0 +1,42 @@
+package com.cameleer3.server.core.storage.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import java.time.Instant;
+import java.util.Map;
+
+/**
+ * Flat processor execution record with seq/parentSeq for tree reconstruction.
+ * Mirrors cameleer3-common FlatProcessorRecord — replace with common lib import when available.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public record FlatProcessorRecord(
+ int seq,
+ Integer parentSeq,
+ String parentProcessorId,
+ String processorId,
+ String processorType,
+ Integer iteration,
+ Integer iterationSize,
+ String status,
+ Instant startTime,
+ long durationMs,
+ String resolvedEndpointUri,
+ String inputBody,
+ String outputBody,
+ Map inputHeaders,
+ Map outputHeaders,
+ String errorMessage,
+ String errorStackTrace,
+ String errorType,
+ String errorCategory,
+ String rootCauseType,
+ String rootCauseMessage,
+ Map attributes,
+ String circuitBreakerState,
+ Boolean fallbackTriggered,
+ Boolean filterMatched,
+ Boolean duplicateMessage
+) {}
diff --git a/cameleer3-server-core/src/test/java/com/cameleer3/server/core/storage/model/ExecutionChunkDeserializationTest.java b/cameleer3-server-core/src/test/java/com/cameleer3/server/core/storage/model/ExecutionChunkDeserializationTest.java
new file mode 100644
index 00000000..de0a50cf
--- /dev/null
+++ b/cameleer3-server-core/src/test/java/com/cameleer3/server/core/storage/model/ExecutionChunkDeserializationTest.java
@@ -0,0 +1,109 @@
+package com.cameleer3.server.core.storage.model;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ExecutionChunkDeserializationTest {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new JavaTimeModule());
+
+ @Test
+ void roundTrip_fullChunk() throws Exception {
+ ExecutionChunk chunk = new ExecutionChunk(
+ "ex-1", "order-service", "pod-1", "order-route",
+ "corr-1", "COMPLETED",
+ Instant.parse("2026-03-31T10:00:00Z"),
+ Instant.parse("2026-03-31T10:00:01Z"), 1000L,
+ "REGULAR",
+ null, null, null, null, null, null,
+ Map.of("orderId", "ORD-1"),
+ "trace-1", "span-1", null, null,
+ 2, true,
+ List.of(new FlatProcessorRecord(
+ 1, null, null, "log1", "log",
+ null, null, "COMPLETED",
+ Instant.parse("2026-03-31T10:00:00.100Z"), 5L,
+ null, "body", null, null, null,
+ null, null, null, null, null, null,
+ null, null, null, null, null)));
+
+ String json = MAPPER.writeValueAsString(chunk);
+ ExecutionChunk deserialized = MAPPER.readValue(json, ExecutionChunk.class);
+
+ assertThat(deserialized.exchangeId()).isEqualTo("ex-1");
+ assertThat(deserialized.isFinal()).isTrue();
+ assertThat(deserialized.chunkSeq()).isEqualTo(2);
+ assertThat(deserialized.processors()).hasSize(1);
+ assertThat(deserialized.processors().get(0).seq()).isEqualTo(1);
+ assertThat(deserialized.processors().get(0).processorId()).isEqualTo("log1");
+ assertThat(deserialized.attributes()).containsEntry("orderId", "ORD-1");
+ }
+
+ @Test
+ void roundTrip_runningChunkWithIterations() throws Exception {
+ ExecutionChunk chunk = new ExecutionChunk(
+ "ex-2", "app", "agent-1", "route-1",
+ "ex-2", "RUNNING",
+ Instant.parse("2026-03-31T10:00:00Z"),
+ null, null, "REGULAR",
+ null, null, null, null, null, null,
+ null, null, null, null, null,
+ 0, false,
+ List.of(
+ new FlatProcessorRecord(
+ 1, null, null, "split1", "split",
+ null, 3, "COMPLETED",
+ Instant.parse("2026-03-31T10:00:00Z"), 100L,
+ null, null, null, null, null,
+ null, null, null, null, null, null,
+ null, null, null, null, null),
+ new FlatProcessorRecord(
+ 2, 1, "split1", "log1", "log",
+ 0, null, "COMPLETED",
+ Instant.parse("2026-03-31T10:00:00Z"), 5L,
+ null, "item-0", null, null, null,
+ null, null, null, null, null, null,
+ null, null, null, null, null),
+ new FlatProcessorRecord(
+ 3, 1, "split1", "log1", "log",
+ 1, null, "COMPLETED",
+ Instant.parse("2026-03-31T10:00:00Z"), 5L,
+ null, "item-1", null, null, null,
+ null, null, null, null, null, null,
+ null, null, null, null, null)));
+
+ String json = MAPPER.writeValueAsString(chunk);
+ ExecutionChunk deserialized = MAPPER.readValue(json, ExecutionChunk.class);
+
+ assertThat(deserialized.isFinal()).isFalse();
+ assertThat(deserialized.processors()).hasSize(3);
+
+ FlatProcessorRecord split = deserialized.processors().get(0);
+ assertThat(split.iterationSize()).isEqualTo(3);
+ assertThat(split.parentSeq()).isNull();
+
+ FlatProcessorRecord child0 = deserialized.processors().get(1);
+ assertThat(child0.parentSeq()).isEqualTo(1);
+ assertThat(child0.parentProcessorId()).isEqualTo("split1");
+ assertThat(child0.iteration()).isEqualTo(0);
+ }
+
+ @Test
+ void deserialize_unknownFieldsIgnored() throws Exception {
+ String json = """
+ {"exchangeId":"ex-1","routeId":"r1","status":"COMPLETED",
+ "startTime":"2026-03-31T10:00:00Z","chunkSeq":0,"final":true,
+ "futureField":"ignored","processors":[]}
+ """;
+ ExecutionChunk chunk = MAPPER.readValue(json, ExecutionChunk.class);
+ assertThat(chunk.exchangeId()).isEqualTo("ex-1");
+ assertThat(chunk.isFinal()).isTrue();
+ }
+}