From 01bab5422712299b49f656e6b4c42a3bf314f519 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 02 Jul 2026 11:02:42 +0800
Subject: [PATCH] Release 8.3.5 free tag order persistence

---
 src/Scripts/pro_custom_container_quota_qa.sh |   45 +++++++++++++++++++++++++++++++++------------
 1 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/src/Scripts/pro_custom_container_quota_qa.sh b/src/Scripts/pro_custom_container_quota_qa.sh
index 7c7225f..2c69e92 100755
--- a/src/Scripts/pro_custom_container_quota_qa.sh
+++ b/src/Scripts/pro_custom_container_quota_qa.sh
@@ -26,6 +26,25 @@
         fail(message)
 
 
+def function_body(source: str, signature: str) -> str:
+    start = source.find(signature)
+    if start == -1:
+        fail(f"missing function signature: {signature}")
+    brace = source.find("{", start)
+    if brace == -1:
+        fail(f"missing function body for: {signature}")
+    depth = 0
+    for index in range(brace, len(source)):
+        char = source[index]
+        if char == "{":
+            depth += 1
+        elif char == "}":
+            depth -= 1
+            if depth == 0:
+                return source[brace + 1:index]
+    fail(f"unterminated function body for: {signature}")
+
+
 pro = read("ProEntitlement.swift")
 data_layer = read("DataLayer.swift")
 tag_editor = read("TagEditorView.swift")
@@ -57,7 +76,9 @@
 require(data_layer, "static func moveApp(path: String, from sourceTag: String, to targetTag: String, color: Int, copy: Bool) -> TagEditorMutationResult", "moveApp must return a mutation result")
 require(data_layer, "TagDatabase.markCustomMaintainedIfNeeded", "membership changes must mark maintained system tags")
 require(data_layer, "static func reorderTags(_ names: [String]) -> Bool", "tag order persistence must return whether it saved")
-require(data_layer, "guard ProEntitlementPolicy.isUnlocked(.persistentAppSorting) else { return false }", "tag order persistence must be Pro-gated")
+reorder_tags_body = function_body(data_layer, "static func reorderTags(_ names: [String]) -> Bool")
+if "ProEntitlementPolicy.isUnlocked(.persistentAppSorting)" in reorder_tags_body:
+    fail("tag list order persistence must be available to free users")
 
 require(tag_editor, "onCustomContainerQuotaExceeded", "TagEditorView must expose quota-blocked callback")
 require(tag_editor, "onCustomContainerQuotaStatusChanged", "TagEditorView must expose quota status refresh callback")
@@ -66,8 +87,10 @@
 require(tag_editor, "guard handleMutationResult(result) else { return }", "TagEditorView must not update local state before a save succeeds")
 require(tag_editor, "blockedCustomContainerQuota", "TagEditorView must surface quota blocks")
 
-require(content, "persistTagOrderIfAllowed()", "ContentView must route tag order save through a Pro gate")
-require(content, "pro.tagSorting.previewToast", "free tag sorting preview toast is missing")
+require(content, "persistTagOrder()", "ContentView must persist tag list order")
+persist_tag_order_body = function_body(content, "private func persistTagOrder()")
+if "proEntitlement.isUnlocked" in persist_tag_order_body or "pro.tagSorting.previewToast" in persist_tag_order_body:
+    fail("free users must be able to persist tag list order without a Pro preview toast")
 require(content, "presentCustomContainerQuotaPrompt", "ContentView must show custom container quota prompt")
 require(content, "customContainerQuotaAccessory", "ContentView tag editor must show quota count")
 require(content, "@State private var customContainerQuotaStatus = ProEntitlementPolicy.customContainerQuotaStatus()", "ContentView quota count must be backed by SwiftUI state")
@@ -84,19 +107,19 @@
 require(preferences, "onCustomContainerQuotaStatusChanged: refreshSettingsCustomContainerQuotaStatus", "Settings must refresh quota count after TagEditorView saves")
 require(preferences, "settingsCustomContainerQuotaOverviewStatus = quotaStatus", "Settings scanApps must initialize quota count from the same store")
 require(preferences, 'title: tr("pro.feature.maintainableTagCount")', "Pro comparison must include maintainable tag count row")
-require(preferences, 'title: tr("pro.feature.tagOrderPersistence")', "Pro comparison must include tag order row")
+if 'title: tr("pro.feature.tagOrderPersistence")' in preferences:
+    fail("Pro comparison must not list tag list order; it is available to free users")
 require(preferences, 'title: tr("pro.feature.containerAppOrderPersistence")', "Pro comparison must include container app order row")
 
-first_three = [
+first_two = [
     preferences.find('title: tr("pro.feature.maintainableTagCount")'),
-    preferences.find('title: tr("pro.feature.tagOrderPersistence")'),
     preferences.find('title: tr("pro.feature.containerAppOrderPersistence")'),
 ]
 custom_color_index = preferences.find('title: tr("pro.feature.customTagColors")')
-if any(index == -1 for index in first_three) or custom_color_index == -1:
+if any(index == -1 for index in first_two) or custom_color_index == -1:
     fail("could not locate Pro comparison feature rows")
-if not (first_three[0] < first_three[1] < first_three[2] < custom_color_index):
-    fail("new Pro comparison rows must be the first three rows")
+if not (first_two[0] < first_two[1] < custom_color_index):
+    fail("Pro comparison rows must begin with tag quota and container app order")
 
 required_l10n = [
     "pro.feature.customContainerQuota",
@@ -106,9 +129,7 @@
     "pro.customContainers.quotaUnlimitedShort",
     "pro.customContainers.quotaPrompt",
     "pro.customContainers.manageExisting",
-    "pro.tagSorting.previewToast",
     "pro.feature.maintainableTagCount",
-    "pro.feature.tagOrderPersistence",
     "pro.feature.containerAppOrderPersistence",
     "settings.proStatus.maxSixTags",
     "settings.proStatus.unlimitedTags",
@@ -139,5 +160,5 @@
 if zh_hans.get("settings.proStatus.unlimitedTags") != "✅没有限制":
     fail("zh-Hans unlimited tag status must include the approved leading checkmark")
 
-print("PASS Pro custom container quota QA: quota gate, tag-order preview, Pro table rows, and 29-language keys are wired")
+print("PASS Pro custom container quota QA: quota gate, free tag-order persistence, Pro table rows, and 29-language keys are wired")
 PY

--
Gitblit v1.9.3