From cba722f8a207982ade9e941eca3e6070e8705749 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sat, 20 Jun 2026 13:01:49 +0800
Subject: [PATCH] Commit single-window app shortcuts on first key

---
 C3.tools/round1-window-index-progressive-filter-fixture-qa.sh |  160 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 159 insertions(+), 1 deletions(-)

diff --git a/C3.tools/round1-window-index-progressive-filter-fixture-qa.sh b/C3.tools/round1-window-index-progressive-filter-fixture-qa.sh
index 3e1a74c..af1a9f4 100755
--- a/C3.tools/round1-window-index-progressive-filter-fixture-qa.sh
+++ b/C3.tools/round1-window-index-progressive-filter-fixture-qa.sh
@@ -27,6 +27,7 @@
 REPORT_HORIZONTAL_DARK="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-horizontal-dark-report.json"
 REPORT_COMMIT="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-commit-report.json"
 REPORT_INVALID="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-invalid-report.json"
+REPORT_SINGLE_WINDOW="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-single-window-report.json"
 SUMMARY_DIR="$BUILD_TMP_ROOT/round01-window-index-progressive-filter-fixture-qa"
 
 APP_PID=""
@@ -140,11 +141,16 @@
   echo "app:$APP_SYMBOL,windowInvalid:${APP_SYMBOL}Z"
 }
 
+single_window_expected_sequence() {
+  echo "app:$APP_SYMBOL,windowSingle:$APP_SYMBOL"
+}
+
 run_fixture() {
   local report="$1"
   local theme="$2"
   local mode="$3"
   local key_sequence="$4"
+  local windows_per_app="${5:-$FIXTURE_WINDOWS_PER_APP}"
 
   stop_current_aligner
   set_theme "$theme"
@@ -154,7 +160,7 @@
     --round0-skip-permissions \
     --round01-open-quick-switch \
     --round01-fixture-app-count="$FIXTURE_APP_COUNT" \
-    --round01-fixture-windows-per-app="$FIXTURE_WINDOWS_PER_APP" \
+    --round01-fixture-windows-per-app="$windows_per_app" \
     --round01-disable-screenshot-refresh \
     --round01-waterfall-view-mode="$mode" \
     --round01-debug-key-sequence="$key_sequence" \
@@ -306,6 +312,51 @@
 if last_report is not None:
     print(json.dumps(last_report, indent=2, ensure_ascii=False), file=sys.stderr)
 print(f"invalid report did not reach invalid state within {timeout:.1f}s", file=sys.stderr)
+sys.exit(1)
+PY
+}
+
+wait_for_single_window_report() {
+  local report="$1"
+  local expected_mode="$2"
+  local expected_sequence="$3"
+
+  /usr/bin/python3 - "$report" "$REPORT_WAIT" "$expected_mode" "$expected_sequence" "$APP_SYMBOL" <<'PY'
+import json
+import sys
+import time
+
+path = sys.argv[1]
+timeout = float(sys.argv[2])
+expected_mode = sys.argv[3]
+expected_sequence = [part for part in sys.argv[4].split(",") if part]
+app_symbol = sys.argv[5]
+deadline = time.monotonic() + timeout
+last_report = None
+
+while time.monotonic() < deadline:
+    try:
+        with open(path, "r", encoding="utf-8") as file:
+            report = json.load(file)
+        last_report = report
+        root = report.get("rootView", {})
+        if (
+            report.get("snapshotLoaded") is True
+            and root.get("waterfallViewMode") == expected_mode
+            and root.get("keyboardCommandsApplied") == expected_sequence
+            and root.get("lastWindowIndexKeyCommand") == f"windowSingle:{app_symbol}"
+            and root.get("lastCommitSource") == "keyboard"
+        ):
+            sys.exit(0)
+    except FileNotFoundError:
+        pass
+    except json.JSONDecodeError:
+        pass
+    time.sleep(0.2)
+
+if last_report is not None:
+    print(json.dumps(last_report, indent=2, ensure_ascii=False), file=sys.stderr)
+print(f"single-window report did not reach direct commit within {timeout:.1f}s", file=sys.stderr)
 sys.exit(1)
 PY
 }
@@ -689,6 +740,108 @@
   SUMMARY_FILES+=("$summary_file")
 }
 
+assert_single_window_report() {
+  local report="$1"
+  local case_name="single-window-first-key-commit-horizontal"
+  local summary_file="$SUMMARY_DIR/$case_name.json"
+
+  /usr/bin/python3 - \
+    "$report" \
+    "$case_name" \
+    "horizontalMasonry" \
+    "$(single_window_expected_sequence)" \
+    "$FIXTURE_APP_COUNT" \
+    "$TARGET_APP_INDEX" \
+    "$APP_SYMBOL" \
+    "$summary_file" <<'PY'
+import json
+import numbers
+import sys
+
+path = sys.argv[1]
+case_name = sys.argv[2]
+expected_mode = sys.argv[3]
+expected_sequence = [part for part in sys.argv[4].split(",") if part]
+fixture_app_count = int(sys.argv[5])
+target_app_index = int(sys.argv[6])
+app_symbol = sys.argv[7]
+summary_file = sys.argv[8]
+
+with open(path, "r", encoding="utf-8") as file:
+    report = json.load(file)
+
+def require(condition, message):
+    if not condition:
+        print(f"{case_name}: {message}", file=sys.stderr)
+        print(json.dumps(report, indent=2, ensure_ascii=False), file=sys.stderr)
+        sys.exit(1)
+
+def is_number(value):
+    return isinstance(value, numbers.Real) and not isinstance(value, bool)
+
+root = report.get("rootView", {})
+columns = root.get("waterfallColumns", [])
+cards = [card for column in columns for card in column.get("cards", [])]
+target_column = next((column for column in columns if column.get("appGroupIndex") == target_app_index), None)
+
+require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true")
+require(report.get("appCount") == fixture_app_count, "fixture appCount must match")
+require(report.get("windowCount") == fixture_app_count, "single-window fixture must have one window per App")
+require(root.get("waterfallViewMode") == expected_mode, "single-window run must use horizontal Waterfall")
+require(root.get("keyboardCommandsApplied") == expected_sequence, "single-window key sequence must direct-commit")
+require(root.get("appShelfIndexKeyDownCount") == 1, "single-window direct commit must use one physical keyDown")
+require(root.get("lastAppShelfIndexKeySymbol") == app_symbol, "last physical symbol must be the App key")
+require(root.get("lastWindowIndexKeyCommand") == f"windowSingle:{app_symbol}", "single-window direct command must be reported")
+require(root.get("lastWindowIndexKeyCommitCode") == app_symbol, "single-window commit code must be the one-key shortcut")
+require(root.get("lastCommitSource") == "keyboard", "single-window direct commit must be keyboard sourced")
+require(root.get("windowIndexKeyPendingAppGroupIndex") is None, "single-window direct commit must not leave pending App group")
+require(root.get("windowIndexKeyPendingAppSymbol") is None, "single-window direct commit must not leave pending App symbol")
+require(root.get("windowShortcutFilterActive") is False, "single-window direct commit must not enter progressive filter")
+require(root.get("windowShortcutFilterPhase") == "none", "single-window direct commit must keep filter phase none")
+require(root.get("windowShortcutFilterMatchedCount") == 0, "single-window direct commit must not leave matched filter count")
+require(root.get("windowShortcutFilterDimmedCount") == 0, "single-window direct commit must not leave dimmed filter count")
+require(root.get("lastWindowShortcutPreCommitFilterSnapshot") is None, "single-window direct commit must not expose two-key pre-commit snapshot")
+
+require(target_column is not None, "target App column must exist")
+target_cards = target_column.get("cards", [])
+require(len(target_cards) == 1, "target App must have exactly one visible window")
+target_card = target_cards[0]
+target_window_id = int(target_card.get("windowID"))
+require(root.get("lastCommittedAppGroupIndex") == target_app_index, "direct commit must target expected App")
+require(root.get("lastCommittedWindowIndex") == 0, "direct commit must target the only window")
+require(root.get("lastCommittedWindowID") == target_window_id, "direct commit must target expected window ID")
+require(root.get("selectedAppGroupIndex") == target_app_index, "selection must land on target App")
+require(root.get("selectedWindowIndex") == 0, "selection must land on the only window")
+require(root.get("selectedWindowID") == target_window_id, "selectedWindowID must match target window")
+
+for card in cards:
+    opacity = card.get("windowShortcutFilterOpacity")
+    states = card.get("visualStates", [])
+    require(card.get("windowShortcutFilterState") == "none", "single-window direct commit must not leave card filter state")
+    require(is_number(opacity), "cards must expose numeric filter opacity")
+    require(float(opacity) >= 0.99, "single-window direct commit must not dim cards")
+    require("shortcutDimmed" not in states, "single-window direct commit must not leave shortcutDimmed state")
+    require("shortcutMatched" not in states, "single-window direct commit must not leave shortcutMatched state")
+    require("shortcutTarget" not in states, "single-window direct commit must not leave shortcutTarget state")
+
+summary = {
+    "case": case_name,
+    "status": "passed",
+    "mode": root.get("waterfallViewMode"),
+    "report": path,
+    "commands": root.get("keyboardCommandsApplied"),
+    "targetWindowID": target_window_id,
+    "lastCommitSource": root.get("lastCommitSource"),
+    "lastWindowIndexKeyCommitCode": root.get("lastWindowIndexKeyCommitCode"),
+    "cardCount": len(cards)
+}
+with open(summary_file, "w", encoding="utf-8") as file:
+    json.dump(summary, file, indent=2, ensure_ascii=False)
+PY
+
+  SUMMARY_FILES+=("$summary_file")
+}
+
 assert_pending_summaries_consistent() {
   local baseline="$1"
   local candidate="$2"
@@ -775,6 +928,11 @@
 trap cleanup EXIT
 stop_current_aligner
 
+run_fixture "$REPORT_SINGLE_WINDOW" "light" "horizontal" "$(pending_key_sequence)" 1
+wait_for_single_window_report "$REPORT_SINGLE_WINDOW" "$(mode_report_value horizontal)" "$(single_window_expected_sequence)"
+assert_single_window_report "$REPORT_SINGLE_WINDOW"
+finish_case
+
 run_fixture "$REPORT_HORIZONTAL_LIGHT" "light" "horizontal" "$(pending_key_sequence)"
 wait_for_pending_report "$REPORT_HORIZONTAL_LIGHT" "$(mode_report_value horizontal)" "$(pending_expected_sequence)" "pending-horizontal-light"
 assert_pending_report "$REPORT_HORIZONTAL_LIGHT" "pending-horizontal-light" "light" "$(mode_report_value horizontal)"

--
Gitblit v1.9.3