diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianHistoricalWriteProtocol.cs b/src/AVEVA.Historian.Client/Wcf/HistorianHistoricalWriteProtocol.cs
new file mode 100644
index 0000000..a5c223d
--- /dev/null
+++ b/src/AVEVA.Historian.Client/Wcf/HistorianHistoricalWriteProtocol.cs
@@ -0,0 +1,98 @@
+using System.Buffers.Binary;
+
+namespace AVEVA.Historian.Client.Wcf;
+
+///
+/// Serializer for the M3 historical (non-streamed original / backfill) value write — the
+/// HistoryService.AddStreamValues values buffer. Decoded byte-for-byte from a live
+/// capture of the native 2023 R2 gRPC client driving
+/// HistorianAccess.AddNonStreamedValue → SendValues against a sandbox tag (see
+/// docs/plans/revision-write-path.md §"R3.1 CAPTURED"). The native non-streamed write does
+/// NOT use the TransactionService AddNonStreamValues path the static decompile suggested — it
+/// rides HistoryService.AddStreamValues with an "ON" storage-sample buffer, the analog sibling
+/// of the AddS2 "OS" event buffer ().
+///
+///
+/// values buffer (single analog sample):
+/// 0x00 UInt16 0x4E4F // "ON" signature
+/// 0x02 UInt16 sampleCount = 1
+/// 0x04 UInt32 10 + valueBlob.Length // total buffer length
+/// 0x08 UInt16 valueBlob.Length
+/// 0x0A valueBlob (46 bytes for one analog double):
+/// +0x00 GUID tag GUID (the per-tag GUID = the value ParseTagInfoRecord reads as "typeId")
+/// +0x10 Int64 sample FILETIME (UTC — the VTQ value timestamp)
+/// +0x18 UInt16 OpcQuality = 192 (good)
+/// +0x1A 4 bytes analog value descriptor (constant for the Float path: C0 10 01 00)
+/// +0x1E Int64 received/version FILETIME (UTC)
+/// +0x26 UInt32 0 // value high dword (zero for a 4-byte Float)
+/// +0x2A Float32 value // the Float value sits in the high half of the 8-byte slot
+///
+///
+/// Captured against a Float tag: the value occupies an 8-byte slot as u32(0) + float32(value)
+/// (the 4-byte IEEE-754 float in the high dword, NOT an 8-byte double). Only the Float encoding is
+/// captured; Double/Int/string tag types use a different descriptor + value width and are rejected
+/// until captured.
+///
+internal static class HistorianHistoricalWriteProtocol
+{
+ public const ushort BufferSignature = 0x4E4F; // "ON"
+ public const ushort OpcQualityGood = 192;
+
+ // Captured constant for the analog double value path. The other observed quality/descriptor
+ // bytes in the AddS2 "OS" buffer are event-specific; here this 4-byte block sits between the
+ // quality and the received-time FILETIME and was constant across captured analog writes.
+ private static readonly byte[] AnalogDoubleDescriptor = [0xC0, 0x10, 0x01, 0x00];
+
+ private const int ValueBlobLength = 16 + 8 + 2 + 4 + 8 + 8; // 46
+
+ ///
+ /// Builds the AddStreamValues values buffer for a single analog historical sample.
+ /// is the per-tag GUID (from the gRPC tag-info read), and
+ /// is the storage/received timestamp the orchestrator stamps.
+ ///
+ public static byte[] SerializeAddStreamValuesBuffer(
+ Guid tagGuid,
+ DateTime sampleTimeUtc,
+ double value,
+ DateTime receivedTimeUtc,
+ ushort quality = OpcQualityGood)
+ {
+ byte[] valueBlob = SerializeValueBlob(tagGuid, sampleTimeUtc, value, receivedTimeUtc, quality);
+
+ byte[] buffer = new byte[10 + valueBlob.Length];
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(0, 2), BufferSignature);
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(2, 2), 1); // sampleCount
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(4, 4), checked((uint)(10 + valueBlob.Length)));
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(8, 2), checked((ushort)valueBlob.Length));
+ valueBlob.CopyTo(buffer.AsSpan(10));
+ return buffer;
+ }
+
+ private static byte[] SerializeValueBlob(
+ Guid tagGuid,
+ DateTime sampleTimeUtc,
+ double value,
+ DateTime receivedTimeUtc,
+ ushort quality)
+ {
+ byte[] blob = new byte[ValueBlobLength];
+ Span span = blob;
+
+ tagGuid.ToByteArray().CopyTo(span[..16]);
+ BinaryPrimitives.WriteInt64LittleEndian(span.Slice(16, 8), ToFileTime(sampleTimeUtc));
+ BinaryPrimitives.WriteUInt16LittleEndian(span.Slice(24, 2), quality);
+ AnalogDoubleDescriptor.CopyTo(span.Slice(26, 4));
+ BinaryPrimitives.WriteInt64LittleEndian(span.Slice(30, 8), ToFileTime(receivedTimeUtc));
+ // The value sits in an 8-byte slot as u32(0) + float32(value): the captured Float tag stored
+ // a 4-byte IEEE-754 float in the high dword, not an 8-byte double.
+ BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(38, 4), 0);
+ BinaryPrimitives.WriteSingleLittleEndian(span.Slice(42, 4), (float)value);
+ return blob;
+ }
+
+ private static long ToFileTime(DateTime value)
+ {
+ DateTime utc = value.Kind == DateTimeKind.Utc ? value : value.ToUniversalTime();
+ return utc.ToFileTimeUtc();
+ }
+}
diff --git a/tests/AVEVA.Historian.Client.Tests/WcfHistoricalWriteProtocolTests.cs b/tests/AVEVA.Historian.Client.Tests/WcfHistoricalWriteProtocolTests.cs
new file mode 100644
index 0000000..20ac4a4
--- /dev/null
+++ b/tests/AVEVA.Historian.Client.Tests/WcfHistoricalWriteProtocolTests.cs
@@ -0,0 +1,44 @@
+using AVEVA.Historian.Client.Wcf;
+
+namespace AVEVA.Historian.Client.Tests;
+
+public sealed class WcfHistoricalWriteProtocolTests
+{
+ // The exact 56-byte HistoryService.AddStreamValues "values" buffer captured live from the native
+ // 2023 R2 client writing a historical Float sample (124.5) to a sandbox tag — see
+ // docs/plans/revision-write-path.md §"R3.1 CAPTURED".
+ private const string CapturedBufferHex =
+ "4f4e0100380000002e00" + // "ON" + count(1) + totalLen(56) + payloadLen(46)
+ "d51d3107e9f8664793b155d3d1aef544" + // tag GUID 07311dd5-f8e9-4766-93b1-55d3d1aef544
+ "70df38b1d101dd01" + // sample FILETIME
+ "c000" + // OpcQuality = 192
+ "c0100100" + // analog double descriptor
+ "e64bcb74e201dd01" + // received/version FILETIME
+ "000000000000f942"; // double 124.5
+
+ [Fact]
+ public void SerializeAddStreamValuesBuffer_MatchesCapturedNativeBuffer()
+ {
+ var tagGuid = new Guid("07311dd5-f8e9-4766-93b1-55d3d1aef544");
+ DateTime sampleTime = DateTime.FromFileTimeUtc(0x01dd01d1b138df70);
+ DateTime receivedTime = DateTime.FromFileTimeUtc(0x01dd01e274cb4be6);
+
+ byte[] actual = HistorianHistoricalWriteProtocol.SerializeAddStreamValuesBuffer(
+ tagGuid, sampleTime, value: 124.5, receivedTime, quality: 192);
+
+ Assert.Equal(Convert.FromHexString(CapturedBufferHex), actual);
+ }
+
+ [Fact]
+ public void SerializeAddStreamValuesBuffer_HeaderDeclaresLengths()
+ {
+ byte[] buffer = HistorianHistoricalWriteProtocol.SerializeAddStreamValuesBuffer(
+ Guid.NewGuid(), DateTime.UtcNow, value: 1.0, DateTime.UtcNow);
+
+ Assert.Equal(56, buffer.Length);
+ Assert.Equal(0x4E4F, BitConverter.ToUInt16(buffer, 0)); // "ON"
+ Assert.Equal(1, BitConverter.ToUInt16(buffer, 2)); // sampleCount
+ Assert.Equal((uint)buffer.Length, BitConverter.ToUInt32(buffer, 4));
+ Assert.Equal(buffer.Length - 10, BitConverter.ToUInt16(buffer, 8));
+ }
+}