diff --git a/NEW/src/JdeScoping.DataSync/Etl/Results/DestinationResult.cs b/NEW/src/JdeScoping.DataSync/Etl/Results/DestinationResult.cs
new file mode 100644
index 0000000..5916fd0
--- /dev/null
+++ b/NEW/src/JdeScoping.DataSync/Etl/Results/DestinationResult.cs
@@ -0,0 +1,12 @@
+namespace JdeScoping.DataSync.Etl.Results;
+
+///
+/// Represents the result of writing data to a destination.
+///
+/// The number of rows successfully written to the destination.
+/// The number of batches used to write the data.
+/// The total time taken to write all batches.
+public record DestinationResult(
+ long RowsProcessed,
+ int BatchCount,
+ TimeSpan Elapsed);
diff --git a/NEW/src/JdeScoping.DataSync/Etl/Results/PipelineResult.cs b/NEW/src/JdeScoping.DataSync/Etl/Results/PipelineResult.cs
new file mode 100644
index 0000000..9085fe5
--- /dev/null
+++ b/NEW/src/JdeScoping.DataSync/Etl/Results/PipelineResult.cs
@@ -0,0 +1,38 @@
+namespace JdeScoping.DataSync.Etl.Results;
+
+///
+/// Represents the complete result of an ETL pipeline execution.
+///
+/// Indicates whether the pipeline completed successfully.
+/// The total number of rows processed by the pipeline.
+/// The total time taken to execute the pipeline.
+/// The results of each step executed in the pipeline.
+/// The exception that caused the pipeline to fail, if any.
+public record PipelineResult(
+ bool Success,
+ long TotalRows,
+ TimeSpan Elapsed,
+ IReadOnlyList Steps,
+ Exception? Error = null)
+{
+ ///
+ /// Creates a successful pipeline result.
+ ///
+ /// The total number of rows processed.
+ /// The total execution time.
+ /// The results of each step.
+ /// A PipelineResult indicating success.
+ public static PipelineResult Succeeded(long totalRows, TimeSpan elapsed, IReadOnlyList steps)
+ => new(true, totalRows, elapsed, steps);
+
+ ///
+ /// Creates a failed pipeline result.
+ ///
+ /// The number of rows processed before failure.
+ /// The total execution time including failure.
+ /// The results of steps executed before failure.
+ /// The exception that caused the failure.
+ /// A PipelineResult indicating failure with error details.
+ public static PipelineResult Failed(long totalRows, TimeSpan elapsed, IReadOnlyList steps, Exception error)
+ => new(false, totalRows, elapsed, steps, error);
+}
diff --git a/NEW/src/JdeScoping.DataSync/Etl/Results/StepResult.cs b/NEW/src/JdeScoping.DataSync/Etl/Results/StepResult.cs
new file mode 100644
index 0000000..794b32e
--- /dev/null
+++ b/NEW/src/JdeScoping.DataSync/Etl/Results/StepResult.cs
@@ -0,0 +1,14 @@
+namespace JdeScoping.DataSync.Etl.Results;
+
+///
+/// Represents the result of a single pipeline step execution.
+///
+/// The name of the step that was executed.
+/// The type of step (e.g., "Transform", "Load").
+/// The number of rows affected by this step.
+/// The time taken to execute this step.
+public record StepResult(
+ string StepName,
+ string StepType,
+ long RowsAffected,
+ TimeSpan Elapsed);
diff --git a/NEW/tests/JdeScoping.DataSync.Tests/Etl/Results/PipelineResultTests.cs b/NEW/tests/JdeScoping.DataSync.Tests/Etl/Results/PipelineResultTests.cs
new file mode 100644
index 0000000..55a474b
--- /dev/null
+++ b/NEW/tests/JdeScoping.DataSync.Tests/Etl/Results/PipelineResultTests.cs
@@ -0,0 +1,149 @@
+using JdeScoping.DataSync.Etl.Results;
+using Shouldly;
+
+namespace JdeScoping.DataSync.Tests.Etl.Results;
+
+public class PipelineResultTests
+{
+ [Fact]
+ public void Succeeded_Creates_Result_With_Success_True()
+ {
+ // Arrange
+ var steps = new List
+ {
+ new("ExtractStep", "Extract", 100, TimeSpan.FromSeconds(1)),
+ new("TransformStep", "Transform", 100, TimeSpan.FromSeconds(2)),
+ new("LoadStep", "Load", 100, TimeSpan.FromSeconds(3))
+ };
+ var elapsed = TimeSpan.FromSeconds(6);
+
+ // Act
+ var result = PipelineResult.Succeeded(100, elapsed, steps);
+
+ // Assert
+ result.Success.ShouldBeTrue();
+ result.TotalRows.ShouldBe(100);
+ result.Elapsed.ShouldBe(elapsed);
+ result.Steps.ShouldBe(steps);
+ result.Error.ShouldBeNull();
+ }
+
+ [Fact]
+ public void Succeeded_With_Empty_Steps_Creates_Valid_Result()
+ {
+ // Arrange
+ var steps = Array.Empty();
+ var elapsed = TimeSpan.Zero;
+
+ // Act
+ var result = PipelineResult.Succeeded(0, elapsed, steps);
+
+ // Assert
+ result.Success.ShouldBeTrue();
+ result.TotalRows.ShouldBe(0);
+ result.Elapsed.ShouldBe(TimeSpan.Zero);
+ result.Steps.ShouldBeEmpty();
+ result.Error.ShouldBeNull();
+ }
+
+ [Fact]
+ public void Failed_Creates_Result_With_Success_False()
+ {
+ // Arrange
+ var steps = new List
+ {
+ new("ExtractStep", "Extract", 50, TimeSpan.FromSeconds(1))
+ };
+ var elapsed = TimeSpan.FromSeconds(1);
+ var error = new InvalidOperationException("Pipeline failed during transform");
+
+ // Act
+ var result = PipelineResult.Failed(50, elapsed, steps, error);
+
+ // Assert
+ result.Success.ShouldBeFalse();
+ result.TotalRows.ShouldBe(50);
+ result.Elapsed.ShouldBe(elapsed);
+ result.Steps.ShouldBe(steps);
+ result.Error.ShouldBe(error);
+ }
+
+ [Fact]
+ public void Failed_Preserves_Error_Details()
+ {
+ // Arrange
+ var innerException = new TimeoutException("Database timeout");
+ var error = new InvalidOperationException("Pipeline failed", innerException);
+ var steps = Array.Empty();
+
+ // Act
+ var result = PipelineResult.Failed(0, TimeSpan.FromSeconds(30), steps, error);
+
+ // Assert
+ result.Error.ShouldNotBeNull();
+ result.Error.Message.ShouldBe("Pipeline failed");
+ result.Error.InnerException.ShouldBe(innerException);
+ }
+
+ [Fact]
+ public void StepResult_Stores_All_Properties_Correctly()
+ {
+ // Arrange & Act
+ var stepResult = new StepResult("MyStep", "Transform", 500, TimeSpan.FromMilliseconds(250));
+
+ // Assert
+ stepResult.StepName.ShouldBe("MyStep");
+ stepResult.StepType.ShouldBe("Transform");
+ stepResult.RowsAffected.ShouldBe(500);
+ stepResult.Elapsed.ShouldBe(TimeSpan.FromMilliseconds(250));
+ }
+
+ [Fact]
+ public void DestinationResult_Stores_All_Properties_Correctly()
+ {
+ // Arrange & Act
+ var destResult = new DestinationResult(1000, 10, TimeSpan.FromSeconds(5));
+
+ // Assert
+ destResult.RowsProcessed.ShouldBe(1000);
+ destResult.BatchCount.ShouldBe(10);
+ destResult.Elapsed.ShouldBe(TimeSpan.FromSeconds(5));
+ }
+
+ [Fact]
+ public void PipelineResult_Records_Are_Equal_When_Properties_Match()
+ {
+ // Arrange
+ var steps = new List { new("Step1", "Extract", 100, TimeSpan.FromSeconds(1)) };
+ var elapsed = TimeSpan.FromSeconds(1);
+
+ // Act
+ var result1 = PipelineResult.Succeeded(100, elapsed, steps);
+ var result2 = PipelineResult.Succeeded(100, elapsed, steps);
+
+ // Assert
+ result1.ShouldBe(result2);
+ }
+
+ [Fact]
+ public void StepResult_Records_Are_Equal_When_Properties_Match()
+ {
+ // Arrange & Act
+ var step1 = new StepResult("Step", "Load", 100, TimeSpan.FromSeconds(1));
+ var step2 = new StepResult("Step", "Load", 100, TimeSpan.FromSeconds(1));
+
+ // Assert
+ step1.ShouldBe(step2);
+ }
+
+ [Fact]
+ public void DestinationResult_Records_Are_Equal_When_Properties_Match()
+ {
+ // Arrange & Act
+ var dest1 = new DestinationResult(500, 5, TimeSpan.FromSeconds(2));
+ var dest2 = new DestinationResult(500, 5, TimeSpan.FromSeconds(2));
+
+ // Assert
+ dest1.ShouldBe(dest2);
+ }
+}