diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs
new file mode 100644
index 0000000..dba6d36
--- /dev/null
+++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs
@@ -0,0 +1,63 @@
+using System.Globalization;
+using System.Xml.Linq;
+
+namespace PRecAnnot.Core;
+
+/// Typed projection of the release Settings.xml sections consumed by production workflows.
+public sealed record ProductionSettingsContract(
+ IReadOnlyList CopyMissingObjects,
+ IReadOnlyList LayerSetup,
+ string AnnotationLayer,
+ string AnnotationTextStyle,
+ string AnnotationLeaderStyle,
+ string ConnectionPolylineLayer,
+ string ConnectionTextLayer,
+ string ConnectionTextStyle,
+ string ElevationLayer,
+ string ElevationTextStyle,
+ string SetLayer,
+ double? FixedSupportPlusLength,
+ string? ScriptFolder)
+{
+ public static ProductionSettingsContract Parse(string xml)
+ {
+ var root = XElement.Parse(xml, LoadOptions.PreserveWhitespace);
+ var copy = root.Element("CopyMissingObjects")?.Elements("Object")
+ .Select(value => new NamedDrawingObject(Required(value, "type"), Required(value, "name")))
+ .ToArray() ?? Array.Empty();
+ var layers = root.Element("LayersSetup")?.Elements("LayerSetup")
+ .Select(value => new LayerSetupRule(
+ Required(value, "filtertype"), Required(value, "filtervalue", allowEmpty: true),
+ ParseInt(value, "lineweight"), Required(value, "linetype", allowEmpty: true),
+ Required(value, "color", allowEmpty: true)))
+ .ToArray() ?? Array.Empty();
+ var annotations = root.Element("Annotations");
+ var connections = root.Element("LinenumberConnections");
+ var elevations = root.Element("Elevations");
+ var setLayer = root.Element("SetLayer")?.Attribute("name")?.Value;
+ if (string.IsNullOrWhiteSpace(setLayer)) throw Invalid("SetLayer.name");
+ var plusLength = root.Element("PlusLengthNO")?.Attribute("value")?.Value;
+ double? parsedPlusLength = double.TryParse(plusLength, NumberStyles.Float, CultureInfo.InvariantCulture, out var plus) ? plus : null;
+ return new(
+ copy,
+ layers,
+ Required(annotations, "layer"),
+ Required(annotations, "textstyle"),
+ Required(annotations, "multileaderstyle"),
+ Required(connections, "polylinelayer"),
+ Required(connections, "textlayer"),
+ Required(connections, "textstyle"),
+ Required(elevations, "layer"),
+ Required(elevations, "textstyle"),
+ setLayer,
+ parsedPlusLength,
+ root.Element("ScriptFolder")?.Attribute("path")?.Value);
+ }
+
+ private static int? ParseInt(XElement value, string name) => int.TryParse(value.Attribute(name)?.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed) ? parsed : null;
+ private static string Required(XElement? value, string name, bool allowEmpty = false) => value?.Attribute(name) is { Value: var result } && (allowEmpty || !string.IsNullOrWhiteSpace(result)) ? result : throw Invalid(name);
+ private static InvalidDataException Invalid(string field) => new("PRODUCTION_SETTINGS_INVALID:" + field);
+}
+
+public sealed record NamedDrawingObject(string Type, string Name);
+public sealed record LayerSetupRule(string FilterType, string FilterValue, int? LineWeight, string LineType, string Color);
diff --git a/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs
new file mode 100644
index 0000000..c749ac9
--- /dev/null
+++ b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs
@@ -0,0 +1,38 @@
+using PRecAnnot.Core;
+
+namespace PRecAnnot.Core.Tests;
+
+public sealed class ProductionSettingsContractTests
+{
+ [Fact]
+ public void ParsesReleaseSectionsAndPreservesAssetNames()
+ {
+ const string xml = """
+
+
+
+
+
+
+
+
+ """;
+
+ var result = ProductionSettingsContract.Parse(xml);
+
+ Assert.Equal("BLOCK_ELEVATION", result.CopyMissingObjects[0].Name);
+ Assert.Equal(60, result.LayerSetup[0].LineWeight);
+ Assert.Equal("PXP", result.AnnotationLeaderStyle);
+ Assert.Equal("AXIS", result.ConnectionPolylineLayer);
+ Assert.Equal(1.5, result.FixedSupportPlusLength);
+ Assert.Equal("C:\\PlantScripts", result.ScriptFolder);
+ }
+
+ [Fact]
+ public void RejectsMissingRequiredSection()
+ {
+ const string xml = "";
+ var exception = Assert.Throws(() => ProductionSettingsContract.Parse(xml));
+ Assert.Equal("PRODUCTION_SETTINGS_INVALID:layer", exception.Message);
+ }
+}