Ariver
2026-06-30 70a1cc2e63d2c49a135bd89cec3ffb1d427d90ee
src/Scripts/pro_feature_gate_qa.sh
@@ -157,10 +157,32 @@
require(preferences, "private var settingsFixedProHeader: some View", "Settings pages must share one fixed-position Pro guidance header")
require(preferences, "private var settingsProHeaderText: String", "fixed Pro guidance header must switch copy by selected tab")
require(preferences, "private var settingsProHeaderFeature: ProFeature", "fixed Pro guidance header must route unlock prompts by selected tab")
require(preferences, "private var shouldShowSettingsFixedProHeader: Bool", "Language/About tabs must be able to hide the fixed Pro header")
require(preferences, "selectedTab != .language && selectedTab != .about", "fixed Pro header must be hidden on Language and About tabs")
require(preferences, "private var shouldCenterUnlockedIdentityHeader: Bool", "Language/Pro/About tabs need the centered unlocked Pro identity header")
center_identity_body = function_body(preferences, "private var shouldCenterUnlockedIdentityHeader: Bool")
require(center_identity_body, "guard proEntitlement.isUnlocked else { return false }", "centered Pro identity header must only apply after Pro unlock")
require(center_identity_body, "case .language, .pro, .about:", "centered Pro identity header must apply to Language, Pro, and About tabs")
require(center_identity_body, "case .general, .theme, .hotkeys, .tags, .data:", "centered Pro identity header must not affect the feature-specific settings tabs")
settings_header_text_body = function_body(preferences, "private var settingsProHeaderText: String")
require(settings_header_text_body, "case .language, .about:", "Language/About tabs must have an explicit fixed-header text branch")
require(settings_header_text_body, 'return ""', "Language/About tabs must not show the general Pro guidance sentence")
if "case .general, .language, .about:" in settings_header_text_body:
    fail("Language/About tabs must not reuse the General tab Pro guidance sentence")
require(preferences, "private func proHeaderStatusLabel(compact: Bool = false) -> some View", "fixed Pro guidance header must render the free status as plain text")
require(preferences, 'Text(tr("settings.proStatus.freeUser"))', "Free status must render as the full current-user sentence instead of a Free pill")
require(preferences, "private var shouldShowSettingsFixedProHeader: Bool", "all settings tabs must use the fixed Pro header gate")
require(function_body(preferences, "private var shouldShowSettingsFixedProHeader: Bool"), "true", "fixed Pro header must be shown on Language and About tabs too")
if "selectedTab != .language && selectedTab != .about" in preferences:
    fail("fixed Pro header must no longer be hidden on Language and About tabs")
require(preferences, "if shouldShowSettingsFixedProHeader", "Settings body must conditionally render the fixed Pro header")
fixed_header_body = function_body(preferences, "private var settingsFixedProHeader: some View")
require(fixed_header_body, "if shouldCenterUnlockedIdentityHeader", "fixed Pro header must use the centered unlocked identity branch")
require(fixed_header_body, 'text: tr("pro.status.unlocked")', "centered Pro identity header must start with the Pro unlocked pill")
require(fixed_header_body, "style: .unlocked", "centered Pro identity pill must use the unlocked style")
require(fixed_header_body, 'Text(tr("settings.proStatus.proUser"))', "centered Pro identity header must render the current Pro user sentence")
if fixed_header_body.find("if shouldCenterUnlockedIdentityHeader") > fixed_header_body.find('Text(tr("settings.proStatus.proUser"))'):
    fail("centered Pro identity sentence must be inside the centered branch")
if fixed_header_body.count("Spacer(minLength: 0)") < 2:
    fail("centered Pro identity header must be horizontally centered with leading and trailing spacers")
require(fixed_header_body, "if !proEntitlement.isUnlocked", "fixed Pro header must branch on unlocked state")
require(fixed_header_body, 'Button(tr("pro.card.restore"))', "fixed Pro header must keep restore purchase for free users")
if fixed_header_body.find("if !proEntitlement.isUnlocked") > fixed_header_body.find('Button(tr("pro.card.restore"))'):
@@ -213,6 +235,11 @@
require(preferences, 'selectedTab == .pro', "fixed Pro header must render the Pro tab user status state")
require(preferences, "proTabComparisonTable(width: dataPanelWidth)", "Pro tab must render the Free-vs-Pro comparison table")
require(preferences, "private struct ProTabComparisonFeature", "Pro tab comparison row model is missing")
comparison_body = function_body(preferences, "private func proTabComparisonTable(width: CGFloat)")
if comparison_body.count(".frame(width: 118, alignment: .leading)") < 4:
    fail("Pro comparison Free/Pro columns and status chips must be left-aligned")
if ".frame(width: 118, alignment: .center)" in comparison_body:
    fail("Pro comparison status columns must not be centered")
if 'Text(tr("settings.proCompare.title"))' in preferences:
    fail("Pro tab comparison table must not render the removed title")
require(preferences, 'Text(tr("settings.proCompare.free"))', "Pro tab comparison table free column is missing")