feat(entitlement): pre-release switch so Release builds can test 60 fps

The frame-rate boundary is correct as a product rule, but it only bites in a
Release build, where DevBuild.IsDevelopment is false and DeveloperMode cannot
unlock anything — exactly the build whose performance numbers are worth reading
(see docs/ai/environment.md: Debug pixel paths are several times slower). 60 fps
could not be tested at all.

OutputProfile.EntitlementEnforced is false: every shipped tier is selectable by
any build, and HasProOutput — the one property the tier setter, the dropdown's
strikethrough and the locked-item style all read — answers yes regardless of
ownership. static readonly rather than const, so the consumed `||` does not fold
into unreachable operand code (same reason as DevBuild.IsDevelopment).

The licence indicator row is suppressed while the switch is off: it exists to
explain what the boundary costs, and claiming "Steam ID Limit: 60fps" to an
unowned build would be a false statement. Docs corrected in place — the enforced
rule is unchanged, only its state is recorded.

Tests: one integration case covers both switch states (unowned build reaches
1080p60 with the switch off, 1080p30 with it on), so flipping at launch leaves no
red test behind. The two suites that anchor on the pinned licence row early-return
while it is absent; EntitlementTests covers that state meanwhile.

667 pass, build 0 warnings.
This commit is contained in:
2026-11-12 07:50:59 -08:00
parent 9c0c40c688
commit 1e3a4f7fe0
7 changed files with 81 additions and 6 deletions
+13
View File
@@ -19,6 +19,19 @@ public static class OutputProfile
/// <summary>Free ceiling: 30 frames per second.</summary>
public const int FreeMaxFps = 30;
/// <summary>
/// Pre-release switch. <c>false</c> means the frame-rate boundary is not enforced yet, so every
/// shipped tier is selectable by any build — the app is still being tested at 60 fps in
/// <b>Release</b>, where <c>DevBuild.IsDevelopment</c> is false and the developer override cannot
/// help. Flip to <c>true</c> when the paid edition ships; the rule itself already exists and its
/// tests do not change.
///
/// <para><b>Why <c>static readonly</c>:</b> a <c>const</c> folds the consuming <c>||</c> at compile
/// time and leaves an unreachable operand, which is a warning. The JIT folds a readonly field
/// just as well, and the build stays at zero warnings.</para>
/// </summary>
public static readonly bool EntitlementEnforced = false;
/// <summary>The default free tier and fallback when no shipped tier is available.</summary>
public const string FreeFallbackName = "1080p30";