diff --git a/Generation/Converters/Argumentum.AssetConverter.Tests/WebBasedGenerator/DocumentCardSetCmykContractTests.cs b/Generation/Converters/Argumentum.AssetConverter.Tests/WebBasedGenerator/DocumentCardSetCmykContractTests.cs
new file mode 100644
index 000000000..0b720b8ea
--- /dev/null
+++ b/Generation/Converters/Argumentum.AssetConverter.Tests/WebBasedGenerator/DocumentCardSetCmykContractTests.cs
@@ -0,0 +1,165 @@
+using Argumentum.AssetConverter;
+using FluentAssertions;
+using Xunit;
+
+namespace Argumentum.AssetConverter.Tests.WebBasedGenerator
+{
+ ///
+ /// Contract pin for the CMYK Debug/Release resolver on .
+ ///
+ /// The pipeline produces images with different color spaces per build mode (documented in the
+ /// project CLAUDE.md "Debug vs Release Builds" table):
+ ///
+ /// ModeCMYK conversion
+ /// - Debug (dotnet run)Disabled — RGB, preview-friendly, smaller files
+ /// - Release (-c Release)Enabled — CMYK, printer quality
+ ///
+ /// carries a XxxDebug/XxxRelease property pair and a
+ /// resolver:
+ /// config.UseDebugParams ? ConvertToCmykDebug : ConvertToCmykRelease, where
+ /// UseDebugParams = (isInDebugMode || ForceDebugParams) && !ForceReleaseParams.
+ ///
+ /// No test exercised this resolver before. A swapped ternary, drifted default, or a regression
+ /// making the legacy field leak into the resolution
+ /// would silently flip the color space per build mode — Debug previews would balloon to CMYK
+ /// size, or Release print output would ship as RGB. These tests pin the contract additively.
+ ///
+ /// Deterministic across build modes: the ForceDebugParams/ForceReleaseParams flags
+ /// drive UseDebugParams directly, so the assertions hold whether the test assembly is
+ /// compiled Debug or Release (independent of the #if DEBUG isInDebugMode term).
+ /// Additive only: no production code or existing test is modified. Dispatch #204 primaire.
+ ///
+ public class DocumentCardSetCmykContractTests
+ {
+ ///
+ /// Config forced into Debug-params resolution: ForceDebugParams sets the first term
+ /// of UseDebugParams true, so UseDebugParams is true regardless of the
+ /// compile-time #if DEBUG flag.
+ ///
+ private static AssetConverterConfig ForcedDebug() => new AssetConverterConfig
+ {
+ ForceDebugParams = true,
+ ForceReleaseParams = false
+ };
+
+ ///
+ /// Config forced into Release-params resolution: ForceReleaseParams makes the
+ /// && !ForceReleaseParams term false, so UseDebugParams is false.
+ /// This is the documented JSON override ("ForceReleaseParams = true to use Release params
+ /// in Debug builds").
+ ///
+ private static AssetConverterConfig ForcedRelease() => new AssetConverterConfig
+ {
+ ForceReleaseParams = true
+ };
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (1) DEFAULTS — the documented Debug/Release color-space contract. A fresh
+ // DocumentCardSet resolves to RGB in Debug and CMYK in Release. This is the table
+ // in CLAUDE.md; pinning it catches a drifted default (e.g. ConvertToCmykDebug=true)
+ // that would silently ship CMYK-sized Debug previews.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void Defaults_DebugResolution_YieldsRgb_NoCmykConversion()
+ {
+ var cardSet = new DocumentCardSet();
+ cardSet.ConvertToCmykDebug.Should().BeFalse(
+ "Debug builds use RGB (preview-friendly, smaller files) — the documented default");
+
+ cardSet.GetConvertToCmyk(ForcedDebug()).Should().BeFalse(
+ "in Debug-params resolution the resolver returns ConvertToCmykDebug, which defaults " +
+ "to false (RGB); a regression here would balloon Debug previews to CMYK size");
+ }
+
+ [Fact]
+ public void Defaults_ReleaseResolution_YieldsCmyk()
+ {
+ var cardSet = new DocumentCardSet();
+ cardSet.ConvertToCmykRelease.Should().BeTrue(
+ "Release builds enable CMYK (printer quality) — the documented default");
+
+ cardSet.GetConvertToCmyk(ForcedRelease()).Should().BeTrue(
+ "in Release-params resolution the resolver returns ConvertToCmykRelease, which " +
+ "defaults to true (CMYK); a regression here would ship RGB print output");
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (2) The resolver is a PURE PASSTHROUGH — it forwards the per-mode field verbatim,
+ // not a hardcoded color-space decision. Custom (even inverted) values are respected
+ // per mode. Catches a regression that hardcodes the result instead of reading the pair.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void Resolver_ForwardsCustomInvertedValues_PerMode()
+ {
+ var cardSet = new DocumentCardSet
+ {
+ ConvertToCmykDebug = true, // inverted: Debug wants CMYK
+ ConvertToCmykRelease = false // inverted: Release wants RGB
+ };
+
+ cardSet.GetConvertToCmyk(ForcedDebug()).Should().BeTrue(
+ "the resolver must forward ConvertToCmykDebug verbatim in Debug mode, even when " +
+ "custom-inverted — it is a passthrough, not a hardcoded RGB decision");
+ cardSet.GetConvertToCmyk(ForcedRelease()).Should().BeFalse(
+ "the resolver must forward ConvertToCmykRelease verbatim in Release mode, even when " +
+ "custom-inverted — it is a passthrough, not a hardcoded CMYK decision");
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (3) The legacy field is DECOUPLED from the
+ // resolver. GetConvertToCmyk keys only on the Debug/Release pair — setting the legacy
+ // field has no effect on the resolved color space. This pins the decoupling so a future
+ // change cannot silently reintroduce the legacy field into the resolution path.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void Legacy_ConvertToCmyk_Field_DoesNotAffectResolver()
+ {
+ // Legacy field says "convert", but the per-mode pair says "don't" — the resolver must
+ // follow the pair and ignore the legacy field.
+ var cardSet = new DocumentCardSet
+ {
+ ConvertToCmyk = true, // legacy — must be ignored
+ ConvertToCmykDebug = false,
+ ConvertToCmykRelease = false
+ };
+
+ cardSet.GetConvertToCmyk(ForcedDebug()).Should().BeFalse(
+ "the legacy ConvertToCmyk field must not feed the resolver; only ConvertToCmykDebug " +
+ "governs Debug resolution");
+ cardSet.GetConvertToCmyk(ForcedRelease()).Should().BeFalse(
+ "the legacy ConvertToCmyk field must not feed the resolver; only ConvertToCmykRelease " +
+ "governs Release resolution");
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (4) OVERRIDE PRIORITY — the documented "ForceReleaseParams = true to use Release params
+ // in Debug builds". With BOTH force flags set, Release wins because UseDebugParams is
+ // gated by `&& !ForceReleaseParams`. This pins the override priority so a
+ // forced Release run really yields the Release color space even in a Debug build.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void ForceReleaseParams_OverridesForceDebugParams_YieldsReleaseValue()
+ {
+ var cardSet = new DocumentCardSet
+ {
+ ConvertToCmykDebug = false,
+ ConvertToCmykRelease = true
+ };
+ var bothForced = new AssetConverterConfig
+ {
+ ForceDebugParams = true,
+ ForceReleaseParams = true
+ };
+
+ // ForceReleaseParams dominates: UseDebugParams = (… || ForceDebugParams) && !ForceReleaseParams = false.
+ cardSet.GetConvertToCmyk(bothForced).Should().BeTrue(
+ "ForceReleaseParams must win over ForceDebugParams (UseDebugParams is gated by " +
+ "'&& !ForceReleaseParams'), so a forced Release run yields the Release CMYK value " +
+ "even in a Debug build — the documented override");
+ }
+ }
+}