From bedb145ee1413482763707fe73787b622cb52cbf Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sat, 13 Jun 2026 16:08:40 +0800
Subject: [PATCH] Fix Finder tab space activation

---
 C1.source/Sources/Aligner/Diagnostics/Round1SnapshotDump.swift                 |   12 
 C1.source/Sources/AlignerCore/Windows/FinderTabSpaceAttributionPolicy.swift    |  143 +++++++
 C3.tools/round1-finder-tabs-live-qa.sh                                         |  452 +++++++++++++++++++++++
 C1.source/Resources/Aligner-Info.plist                                         |    4 
 C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift |  342 +++++++++++++++++
 C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift                        |  156 ++++++++
 6 files changed, 1,096 insertions(+), 13 deletions(-)

diff --git a/C1.source/Resources/Aligner-Info.plist b/C1.source/Resources/Aligner-Info.plist
index dd38074..fdeb485 100644
--- a/C1.source/Resources/Aligner-Info.plist
+++ b/C1.source/Resources/Aligner-Info.plist
@@ -17,9 +17,9 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-    <string>0.0.63</string>
+    <string>0.0.64</string>
 	<key>CFBundleVersion</key>
-    <string>20260613.1401</string>
+    <string>20260613.1606</string>
 	<key>LSMinimumSystemVersion</key>
 	<string>26.0</string>
 	<key>NSHighResolutionCapable</key>
diff --git a/C1.source/Sources/Aligner/Diagnostics/Round1SnapshotDump.swift b/C1.source/Sources/Aligner/Diagnostics/Round1SnapshotDump.swift
index bb70050..c8e3b03 100644
--- a/C1.source/Sources/Aligner/Diagnostics/Round1SnapshotDump.swift
+++ b/C1.source/Sources/Aligner/Diagnostics/Round1SnapshotDump.swift
@@ -89,6 +89,7 @@
             "isMinimized": window.isMinimized,
             "isFullscreen": window.isFullscreen,
             "isGhost": window.isGhost,
+            "frame": frameDictionary(window.frame) as Any,
             "spaceIDs": window.spaceIDs,
             "app": appDictionary(window.app)
         ] as [String: Any]
@@ -98,6 +99,17 @@
         return result
     }
 
+    private static func frameDictionary(_ frame: CGRect?) -> Any {
+        guard let frame else { return NSNull() }
+
+        return [
+            "x": frame.origin.x,
+            "y": frame.origin.y,
+            "width": frame.size.width,
+            "height": frame.size.height
+        ]
+    }
+
     private static func appDictionary(_ app: AlignerApp) -> [String: Any] {
         let pathSummary = DevelopmentDiagnostics.pathSummary(app.executablePath)
         var result = [
diff --git a/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift b/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
index 87b6c01..b1b1cd3 100644
--- a/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
+++ b/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
@@ -162,9 +162,10 @@
         let privateActivationOutcome = activateViaPrivateWindowServerAPI(window)
         let didPrivatelyActivate = privateActivationOutcome?.succeeded == true
 
+        let currentAXMetadata = AXWindowMetadataReader.metadata(appCategorizer: appCategorizer)
         let axWindow = axWindow(
             for: window,
-            in: AXWindowMetadataReader.metadata(appCategorizer: appCategorizer),
+            in: currentAXMetadata,
             operation: "windowActivation",
             matchAttempt: "initial"
         )
@@ -188,6 +189,20 @@
             "spaceFocusError": spaceFocusOutcome?.error
         ])
         guard let axWindow else {
+            if let finderTabActivated = activateFinderTabPageIfNeeded(
+                window: window,
+                axMetadata: currentAXMetadata,
+                operation: "windowActivation",
+                reason: "axMissing"
+            ) {
+                DevelopmentDiagnostics.log("windowActivation.activate.finderTabResult", [
+                    "windowID": window.id,
+                    "pid": window.app.processIdentifier,
+                    "activated": finderTabActivated
+                ])
+                return finderTabActivated ? .activated : .activationFailed
+            }
+
             let activated = activateApplication(for: window)
             DevelopmentDiagnostics.log("windowActivation.activate.axMissing", [
                 "windowID": window.id,
@@ -393,15 +408,31 @@
             return nil
         }
 
-        guard let activationOutcome = activateViaPrivateWindowServerAPI(window),
-              activationOutcome.succeeded
-        else {
-            DevelopmentDiagnostics.log("windowClose.commandW.activationFailed", [
-                "windowID": window.id,
-                "pid": processIdentifier,
-                "reason": reason
-            ])
-            return nil
+        if let finderTabActivated = activateFinderTabPageIfNeeded(
+            window: window,
+            axMetadata: AXWindowMetadataReader.metadata(appCategorizer: appCategorizer),
+            operation: "windowClose",
+            reason: reason
+        ) {
+            guard finderTabActivated else {
+                DevelopmentDiagnostics.log("windowClose.commandW.finderTabActivationFailed", [
+                    "windowID": window.id,
+                    "pid": processIdentifier,
+                    "reason": reason
+                ])
+                return .failed("finderTabActivationFailed")
+            }
+        } else {
+            guard let activationOutcome = activateViaPrivateWindowServerAPI(window),
+                  activationOutcome.succeeded
+            else {
+                DevelopmentDiagnostics.log("windowClose.commandW.activationFailed", [
+                    "windowID": window.id,
+                    "pid": processIdentifier,
+                    "reason": reason
+                ])
+                return nil
+            }
         }
 
         Thread.sleep(forTimeInterval: 0.08)
@@ -564,6 +595,271 @@
                 && metadata.subrole == .standard
         }
         return finderCandidates.count == 1 ? finderCandidates[0] : nil
+    }
+
+    private func activateFinderTabPageIfNeeded(
+        window: AlignerWindow,
+        axMetadata: [AXWindowMetadata],
+        operation: String,
+        reason: String
+    ) -> Bool? {
+        guard isFinderCGOnlyPageCandidate(window),
+              let processIdentifier = window.app.processIdentifier
+        else {
+            return nil
+        }
+
+        guard let host = finderTabHostAXWindow(for: window, in: axMetadata) else {
+            DevelopmentDiagnostics.log("\(operation).finderTab.hostMissing", [
+                "windowID": window.id,
+                "pid": processIdentifier,
+                "reason": reason,
+                "spaceIDs": window.spaceIDs
+            ])
+            return false
+        }
+
+        let hostPrivateActivationSucceeded: Bool
+        if let hostWindowID = host.windowID,
+           let privateActivationBridge {
+            hostPrivateActivationSucceeded = privateActivationBridge.activate(
+                processIdentifier: processIdentifier,
+                windowID: hostWindowID
+            ).succeeded
+        } else {
+            hostPrivateActivationSucceeded = false
+        }
+
+        let applicationActivated = activateApplication(processIdentifier: processIdentifier)
+        let focusBeforeResult = focusWindowResult(host.element, processIdentifier: processIdentifier)
+        let raiseBeforeResult = raiseWindowResult(host.element)
+        let selectedTab = selectFinderTab(window, in: host)
+        Thread.sleep(forTimeInterval: 0.05)
+        let focusAfterResult = focusWindowResult(host.element, processIdentifier: processIdentifier)
+        let raiseAfterResult = raiseWindowResult(host.element)
+
+        let didFocusOrRaise = focusBeforeResult.success
+            || raiseBeforeResult.success
+            || focusAfterResult.success
+            || raiseAfterResult.success
+        let activated = selectedTab
+            && (hostPrivateActivationSucceeded || applicationActivated || didFocusOrRaise)
+
+        DevelopmentDiagnostics.log("\(operation).finderTab.result", [
+            "windowID": window.id,
+            "pid": processIdentifier,
+            "reason": reason,
+            "hostWindowID": host.windowID,
+            "hostTitleHash": DevelopmentDiagnostics.stableFingerprint(host.title),
+            "hostTitleLength": host.title?.count,
+            "hostPrivateActivated": hostPrivateActivationSucceeded,
+            "applicationActivated": applicationActivated,
+            "selectedTab": selectedTab,
+            "focusBeforeAXError": String(describing: focusBeforeResult.error),
+            "raiseBeforeAXError": String(describing: raiseBeforeResult.error),
+            "focusAfterAXError": String(describing: focusAfterResult.error),
+            "raiseAfterAXError": String(describing: raiseAfterResult.error),
+            "activated": activated
+        ])
+
+        return activated
+    }
+
+    private func isFinderCGOnlyPageCandidate(_ window: AlignerWindow) -> Bool {
+        window.identifierSource == .cgWindow
+            && window.app.bundleIdentifier == "com.apple.finder"
+            && !window.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+    }
+
+    private func finderTabHostAXWindow(
+        for window: AlignerWindow,
+        in axMetadata: [AXWindowMetadata]
+    ) -> AXWindowMetadata? {
+        guard let processIdentifier = window.app.processIdentifier else { return nil }
+        let candidates = axMetadata.filter { metadata in
+            metadata.processIdentifier == processIdentifier
+                && metadata.subrole == .standard
+                && metadata.frame?.isEmpty == false
+        }
+        guard !candidates.isEmpty else { return nil }
+
+        let targetSpaceIDs = Set(window.spaceIDs)
+        let candidateIDs = candidates.compactMap(\.windowID)
+        let candidateSpacesByID = targetSpaceIDs.isEmpty ? [:] : spaceIDsByWindowIDProvider(candidateIDs)
+        let spaceCompatibleCandidates = targetSpaceIDs.isEmpty
+            ? candidates
+            : candidates.filter { metadata in
+                guard let windowID = metadata.windowID else { return false }
+                return !Set(candidateSpacesByID[windowID] ?? []).isDisjoint(with: targetSpaceIDs)
+            }
+        let hostCandidates = spaceCompatibleCandidates.isEmpty ? candidates : spaceCompatibleCandidates
+        let targetFrame = cgWindowFrame(for: window) ?? window.frame
+        let scoredHosts = hostCandidates.compactMap { metadata -> (metadata: AXWindowMetadata, score: CGFloat)? in
+            guard let score = frameOverlapScore(targetFrame, metadata.frame),
+                  score >= 0.85
+            else {
+                return nil
+            }
+
+            return (metadata, score)
+        }
+        .sorted { lhs, rhs in
+            if lhs.score != rhs.score {
+                return lhs.score > rhs.score
+            }
+            return (lhs.metadata.windowID ?? UInt32.max) < (rhs.metadata.windowID ?? UInt32.max)
+        }
+
+        if let best = scoredHosts.first {
+            if scoredHosts.count == 1 {
+                return best.metadata
+            }
+
+            let secondBest = scoredHosts[1]
+            if best.score - secondBest.score >= 0.05 {
+                return best.metadata
+            }
+        }
+
+        return hostCandidates.count == 1 ? hostCandidates[0] : nil
+    }
+
+    private func selectFinderTab(
+        _ window: AlignerWindow,
+        in host: AXWindowMetadata
+    ) -> Bool {
+        let normalizedTargetTitle = normalizedWindowTitle(window.title)
+        guard !normalizedTargetTitle.isEmpty else { return false }
+
+        if normalizedWindowTitle(host.title ?? "") == normalizedTargetTitle {
+            return true
+        }
+
+        guard let tabButton = finderTabButton(
+            matchingNormalizedTitle: normalizedTargetTitle,
+            in: host.element
+        ) else {
+            DevelopmentDiagnostics.log("windowActivation.finderTab.tabButtonMissing", [
+                "windowID": window.id,
+                "hostWindowID": host.windowID,
+                "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
+                "targetTitleLength": window.title.count
+            ])
+            return false
+        }
+
+        let tabButtonRole = axStringAttribute(kAXRoleAttribute as String, for: tabButton)
+        let tabButtonSubrole = axStringAttribute(kAXSubroleAttribute as String, for: tabButton)
+        let pressResult = AXUIElementPerformAction(tabButton, kAXPressAction as CFString)
+        DevelopmentDiagnostics.log("windowActivation.finderTab.pressTab", [
+            "windowID": window.id,
+            "hostWindowID": host.windowID,
+            "tabButtonRole": tabButtonRole,
+            "tabButtonSubrole": tabButtonSubrole,
+            "axError": String(describing: pressResult)
+        ])
+        guard pressResult == .success else { return false }
+
+        for _ in 0..<10 {
+            Thread.sleep(forTimeInterval: 0.04)
+            let selectedTitle = normalizedWindowTitle(
+                axStringAttribute(kAXTitleAttribute as String, for: host.element) ?? ""
+            )
+            if selectedTitle == normalizedTargetTitle {
+                return true
+            }
+        }
+
+        let currentHostTitle = axStringAttribute(kAXTitleAttribute as String, for: host.element) ?? ""
+        DevelopmentDiagnostics.log("windowActivation.finderTab.selectionVerificationFailed", [
+            "windowID": window.id,
+            "hostWindowID": host.windowID,
+            "targetTitleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
+            "targetTitleLength": window.title.count,
+            "currentHostTitleHash": DevelopmentDiagnostics.stableFingerprint(currentHostTitle),
+            "currentHostTitleLength": currentHostTitle.count
+        ])
+        return false
+    }
+
+    private func finderTabButton(
+        matchingNormalizedTitle targetTitle: String,
+        in root: AXUIElement
+    ) -> AXUIElement? {
+        var visitedCount = 0
+
+        func search(_ element: AXUIElement, depth: Int, insideTabGroup: Bool) -> AXUIElement? {
+            visitedCount += 1
+            guard visitedCount <= 240, depth <= 9 else { return nil }
+
+            let role = axStringAttribute(kAXRoleAttribute as String, for: element)
+            let subrole = axStringAttribute(kAXSubroleAttribute as String, for: element)
+            let title = axStringAttribute(kAXTitleAttribute as String, for: element)
+                ?? axStringAttribute(kAXDescriptionAttribute as String, for: element)
+                ?? axStringAttribute(kAXValueAttribute as String, for: element)
+            let isTabGroup = role == "AXTabGroup"
+            let isTabControl = subrole == "AXTabButton"
+                || (insideTabGroup && (role == kAXRadioButtonRole as String || role == kAXButtonRole as String))
+
+            if normalizedWindowTitle(title ?? "") == targetTitle,
+               isTabControl {
+                return element
+            }
+
+            guard isTabGroup || insideTabGroup || depth < 7 else { return nil }
+            for child in axChildren(of: element) {
+                if let match = search(child, depth: depth + 1, insideTabGroup: insideTabGroup || isTabGroup) {
+                    return match
+                }
+            }
+
+            return nil
+        }
+
+        return search(root, depth: 0, insideTabGroup: false)
+    }
+
+    private func axChildren(of element: AXUIElement) -> [AXUIElement] {
+        var value: CFTypeRef?
+        guard AXUIElementCopyAttributeValue(element, kAXChildrenAttribute as CFString, &value) == .success,
+              let children = value as? [AXUIElement]
+        else {
+            return []
+        }
+
+        return children
+    }
+
+    private func axStringAttribute(_ attribute: String, for element: AXUIElement) -> String? {
+        var value: CFTypeRef?
+        guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success,
+              let value
+        else {
+            return nil
+        }
+
+        return value as? String
+    }
+
+    private func frameOverlapScore(_ lhs: CGRect?, _ rhs: CGRect?) -> CGFloat? {
+        guard let lhs,
+              let rhs,
+              !lhs.isEmpty,
+              !rhs.isEmpty
+        else {
+            return nil
+        }
+
+        let intersection = lhs.intersection(rhs)
+        guard !intersection.isNull,
+              !intersection.isEmpty
+        else {
+            return nil
+        }
+
+        let denominator = min(lhs.width * lhs.height, rhs.width * rhs.height)
+        guard denominator > 0 else { return nil }
+        return (intersection.width * intersection.height) / denominator
     }
 
     private func cgWindowFingerprint(for window: AlignerWindow) -> AXWindowFingerprint? {
@@ -895,7 +1191,31 @@
         }
 
         restorableAXWindowsByID = nextRestorableAXWindowsByID
-        return cgRecords + axOnlyRecords
+        let records = cgRecords + axOnlyRecords
+        let attributedRecords = FinderTabSpaceAttributionPolicy.attributedRecords(records)
+        DevelopmentDiagnostics.log("windowEnumeration.finderTabSpaceAttribution", [
+            "finderNoSpaceCandidateCount": records.filter { record in
+                record.app.bundleIdentifier == "com.apple.finder"
+                    && record.identifierSource == .cgWindow
+                    && record.spaceIDs.isEmpty
+                    && !record.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+                    && record.frame?.isEmpty == false
+            }.count,
+            "finderFullscreenHostCount": records.filter { record in
+                record.app.bundleIdentifier == "com.apple.finder"
+                    && record.isFullscreen
+                    && !record.spaceIDs.isEmpty
+                    && record.subrole == .standard
+                    && record.frame?.isEmpty == false
+            }.count,
+            "attributedCount": zip(records, attributedRecords).filter { before, after in
+                before.spaceIDs.isEmpty && !after.spaceIDs.isEmpty
+            }.count,
+            "attributedWindowIDs": zip(records, attributedRecords).compactMap { before, after in
+                before.spaceIDs.isEmpty && !after.spaceIDs.isEmpty ? after.id : nil
+            }
+        ])
+        return attributedRecords
     }
 
     private func app(
diff --git a/C1.source/Sources/AlignerCore/Windows/FinderTabSpaceAttributionPolicy.swift b/C1.source/Sources/AlignerCore/Windows/FinderTabSpaceAttributionPolicy.swift
new file mode 100644
index 0000000..0de281d
--- /dev/null
+++ b/C1.source/Sources/AlignerCore/Windows/FinderTabSpaceAttributionPolicy.swift
@@ -0,0 +1,143 @@
+import CoreGraphics
+import Foundation
+
+public enum FinderTabSpaceAttributionPolicy {
+    private static let finderBundleIdentifier = "com.apple.finder"
+    private static let minimumOverlapRatio: CGFloat = 0.85
+    private static let minimumBestScoreGap: CGFloat = 0.05
+
+    public static func attributedRecords(_ records: [WindowEnumerationRecord]) -> [WindowEnumerationRecord] {
+        let hosts = records.filter(isFinderFullscreenHost)
+        guard !hosts.isEmpty else { return records }
+
+        return records.map { record in
+            guard isFinderTabCandidate(record),
+                  let host = bestHost(for: record, in: hosts)
+            else {
+                return record
+            }
+
+            return copy(
+                record,
+                isFullscreen: true,
+                spaceIDs: host.spaceIDs
+            )
+        }
+    }
+
+    private static func isFinderTabCandidate(_ record: WindowEnumerationRecord) -> Bool {
+        WindowEnumerationPolicy.shouldInclude(record)
+            && record.identifierSource == .cgWindow
+            && record.app.bundleIdentifier == finderBundleIdentifier
+            && record.spaceIDs.isEmpty
+            && !record.isMinimized
+            && !record.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+            && record.frame?.isEmpty == false
+    }
+
+    private static func isFinderFullscreenHost(_ record: WindowEnumerationRecord) -> Bool {
+        WindowEnumerationPolicy.shouldInclude(record)
+            && record.app.bundleIdentifier == finderBundleIdentifier
+            && record.isFullscreen
+            && !record.spaceIDs.isEmpty
+            && record.subrole == .standard
+            && record.frame?.isEmpty == false
+    }
+
+    private static func bestHost(
+        for candidate: WindowEnumerationRecord,
+        in hosts: [WindowEnumerationRecord]
+    ) -> WindowEnumerationRecord? {
+        let compatibleHosts = hosts.filter { host in
+            sameProcess(candidate, host)
+        }
+        let scoredHosts = compatibleHosts.compactMap { host -> (record: WindowEnumerationRecord, score: CGFloat)? in
+            guard let score = overlapScore(candidate.frame, host.frame),
+                  score >= minimumOverlapRatio
+            else {
+                return nil
+            }
+
+            return (host, score)
+        }
+        .sorted { lhs, rhs in
+            if lhs.score != rhs.score {
+                return lhs.score > rhs.score
+            }
+            return lhs.record.id < rhs.record.id
+        }
+
+        guard let best = scoredHosts.first else { return nil }
+        if scoredHosts.count == 1 {
+            return best.record
+        }
+
+        let sameSpaceCompetitors = scoredHosts.filter { $0.record.spaceIDs == best.record.spaceIDs }
+        if sameSpaceCompetitors.count == scoredHosts.count {
+            return best.record
+        }
+
+        let secondBest = scoredHosts[1]
+        return best.score - secondBest.score >= minimumBestScoreGap ? best.record : nil
+    }
+
+    private static func sameProcess(
+        _ lhs: WindowEnumerationRecord,
+        _ rhs: WindowEnumerationRecord
+    ) -> Bool {
+        guard let lhsPID = lhs.app.processIdentifier,
+              let rhsPID = rhs.app.processIdentifier
+        else {
+            return false
+        }
+
+        return lhsPID == rhsPID
+    }
+
+    private static func overlapScore(_ lhs: CGRect?, _ rhs: CGRect?) -> CGFloat? {
+        guard let lhs,
+              let rhs,
+              !lhs.isEmpty,
+              !rhs.isEmpty
+        else {
+            return nil
+        }
+
+        let intersection = lhs.intersection(rhs)
+        guard !intersection.isNull,
+              !intersection.isEmpty
+        else {
+            return nil
+        }
+
+        let denominator = min(lhs.width * lhs.height, rhs.width * rhs.height)
+        guard denominator > 0 else { return nil }
+        return (intersection.width * intersection.height) / denominator
+    }
+
+    private static func copy(
+        _ record: WindowEnumerationRecord,
+        isFullscreen: Bool,
+        spaceIDs: [UInt64]
+    ) -> WindowEnumerationRecord {
+        WindowEnumerationRecord(
+            id: record.id,
+            app: record.app,
+            activationPolicy: record.activationPolicy,
+            title: record.title,
+            identifierSource: record.identifierSource,
+            size: record.size,
+            frame: record.frame,
+            subrole: record.subrole,
+            isMinimized: record.isMinimized,
+            isFullscreen: isFullscreen,
+            isGhost: record.isGhost,
+            isDesktopElement: record.isDesktopElement,
+            isSystemCritical: record.isSystemCritical,
+            isInteractable: record.isInteractable,
+            hasAXBacking: record.hasAXBacking,
+            isOnscreen: record.isOnscreen,
+            spaceIDs: spaceIDs
+        )
+    }
+}
diff --git a/C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift b/C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift
index b83efb7..042e94c 100644
--- a/C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift
+++ b/C1.source/Tests/AlignerCoreTests/AlignerCoreTests.swift
@@ -377,6 +377,162 @@
         XCTAssertNil(WindowEnumerationPolicy.window(from: tinyRecord))
     }
 
+    func testFinderTabSpaceAttributionInheritsFullscreenHostSpaceForMatchingFrame() {
+        let finderApp = AlignerApp(
+            bundleIdentifier: "com.apple.finder",
+            name: "访达",
+            category: .finder,
+            processIdentifier: 74678
+        )
+        let frame = CGRect(x: 0, y: 0, width: 960, height: 1080)
+        let host = WindowEnumerationRecord(
+            id: 33112,
+            app: finderApp,
+            title: "下载",
+            size: frame.size,
+            frame: frame,
+            subrole: .standard,
+            isFullscreen: true,
+            hasAXBacking: true,
+            spaceIDs: [843]
+        )
+        let inactiveTab = WindowEnumerationRecord(
+            id: 42286,
+            app: finderApp,
+            title: "Appcache",
+            size: frame.size,
+            frame: frame,
+            subrole: .standard,
+            hasAXBacking: false,
+            isOnscreen: false
+        )
+
+        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([inactiveTab, host])
+
+        XCTAssertEqual(attributed[0].spaceIDs, [843])
+        XCTAssertTrue(attributed[0].isFullscreen)
+    }
+
+    func testFinderTabSpaceAttributionDoesNotGuessNoSpaceWindowsWithoutHostOverlap() {
+        let finderApp = AlignerApp(
+            bundleIdentifier: "com.apple.finder",
+            name: "访达",
+            category: .finder,
+            processIdentifier: 74678
+        )
+        let otherApp = AlignerApp(
+            bundleIdentifier: "com.example.Other",
+            name: "Other",
+            category: .generic,
+            processIdentifier: 74678
+        )
+        let hostFrame = CGRect(x: 0, y: 0, width: 960, height: 1080)
+        let farFrame = CGRect(x: 1200, y: 0, width: 500, height: 600)
+        let host = WindowEnumerationRecord(
+            id: 33112,
+            app: finderApp,
+            title: "下载",
+            size: hostFrame.size,
+            frame: hostFrame,
+            subrole: .standard,
+            isFullscreen: true,
+            hasAXBacking: true,
+            spaceIDs: [843]
+        )
+        let farFinderTab = WindowEnumerationRecord(
+            id: 42286,
+            app: finderApp,
+            title: "Appcache",
+            size: farFrame.size,
+            frame: farFrame,
+            subrole: .standard,
+            hasAXBacking: false,
+            isOnscreen: false
+        )
+        let nonFinderWindow = WindowEnumerationRecord(
+            id: 50222,
+            app: otherApp,
+            title: "Appcache",
+            size: hostFrame.size,
+            frame: hostFrame,
+            subrole: .standard,
+            hasAXBacking: false,
+            isOnscreen: false
+        )
+
+        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([farFinderTab, nonFinderWindow, host])
+
+        XCTAssertEqual(attributed[0].spaceIDs, [])
+        XCTAssertFalse(attributed[0].isFullscreen)
+        XCTAssertEqual(attributed[1].spaceIDs, [])
+        XCTAssertFalse(attributed[1].isFullscreen)
+    }
+
+    func testFinderTabSpaceAttributionHandlesFinderSplitViewHostsByFrame() {
+        let finderApp = AlignerApp(
+            bundleIdentifier: "com.apple.finder",
+            name: "访达",
+            category: .finder,
+            processIdentifier: 74678
+        )
+        let leftFrame = CGRect(x: 0, y: 0, width: 960, height: 1080)
+        let rightFrame = CGRect(x: 960, y: 0, width: 960, height: 1080)
+        let leftHost = WindowEnumerationRecord(
+            id: 33112,
+            app: finderApp,
+            title: "下载",
+            size: leftFrame.size,
+            frame: leftFrame,
+            subrole: .standard,
+            isFullscreen: true,
+            hasAXBacking: true,
+            spaceIDs: [843]
+        )
+        let rightHost = WindowEnumerationRecord(
+            id: 41557,
+            app: finderApp,
+            title: "应用程序",
+            size: rightFrame.size,
+            frame: rightFrame,
+            subrole: .standard,
+            isFullscreen: true,
+            hasAXBacking: true,
+            spaceIDs: [843]
+        )
+        let leftInactiveTab = WindowEnumerationRecord(
+            id: 42286,
+            app: finderApp,
+            title: "Appcache",
+            size: leftFrame.size,
+            frame: leftFrame,
+            subrole: .standard,
+            hasAXBacking: false,
+            isOnscreen: false
+        )
+        let rightInactiveTab = WindowEnumerationRecord(
+            id: 61385,
+            app: finderApp,
+            title: "build",
+            size: rightFrame.size,
+            frame: rightFrame,
+            subrole: .standard,
+            hasAXBacking: false,
+            isOnscreen: false
+        )
+
+        let attributed = FinderTabSpaceAttributionPolicy.attributedRecords([
+            leftInactiveTab,
+            rightInactiveTab,
+            leftHost,
+            rightHost
+        ])
+
+        XCTAssertEqual(attributed[0].spaceIDs, [843])
+        XCTAssertEqual(attributed[1].spaceIDs, [843])
+        XCTAssertTrue(attributed[0].isFullscreen)
+        XCTAssertTrue(attributed[1].isFullscreen)
+    }
+
     func testWindowEnumerationPolicyKeepsMinimizedAndFullscreenCGOnlyOffscreenWindows() {
         let minimized = makeWindowRecord(
             title: "Minimized Document.md",
diff --git a/C3.tools/round1-finder-tabs-live-qa.sh b/C3.tools/round1-finder-tabs-live-qa.sh
new file mode 100755
index 0000000..99fc856
--- /dev/null
+++ b/C3.tools/round1-finder-tabs-live-qa.sh
@@ -0,0 +1,452 @@
+#!/bin/bash
+# Round01 real-desktop QA for Finder tabs/pages. It verifies that CG-only
+# Finder pages inherit the high-confidence fullscreen Space from their visible
+# host, and that clicking such a card actually selects the matching Finder tab
+# instead of only reporting a WindowServer activation success.
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+OUTPUT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+# shellcheck source=build-output-paths.sh
+source "$SCRIPT_DIR/build-output-paths.sh"
+APP="$BUILD_CURRENT_APP"
+RUN_ID="$(date +%Y%m%d_%H%M%S)"
+REPORT_DIR="$BUILD_REPORT_ROOT/round01-finder-tabs-live-$RUN_ID"
+DEV_LOG="$HOME/Library/Logs/Aligner/aligner-dev.log"
+REPORT_WAIT="${ALIGNER_ROUND1_FINDER_TABS_REPORT_WAIT:-7.0}"
+
+fail() {
+  echo "Round01 Finder tabs live QA failed: $*" >&2
+  exit 1
+}
+
+aligner_pids_for_current_app() {
+  ps -axo pid=,args= | while read -r pid command; do
+    case "$command" in
+      "$APP/Contents/MacOS/Aligner"*) echo "$pid" ;;
+    esac
+  done
+}
+
+stop_current_aligner() {
+  for pid in $(aligner_pids_for_current_app); do
+    kill "$pid" 2>/dev/null || true
+  done
+
+  for _ in {1..30}; do
+    [ -z "$(aligner_pids_for_current_app)" ] && return
+    sleep 0.1
+  done
+
+  fail "current Aligner app did not exit before QA"
+}
+
+dev_log_size() {
+  if [ -f "$DEV_LOG" ]; then
+    stat -f%z "$DEV_LOG"
+  else
+    echo 0
+  fi
+}
+
+extract_dev_log_since() {
+  local offset="$1"
+  local output="$2"
+  if [ ! -f "$DEV_LOG" ]; then
+    : >"$output"
+    return
+  fi
+
+  dd if="$DEV_LOG" bs=1 skip="$offset" 2>/dev/null >"$output" || : >"$output"
+}
+
+wait_for_report() {
+  local report="$1"
+  local mode="$2"
+  /usr/bin/python3 - "$report" "$REPORT_WAIT" "$mode" <<'PY'
+import json
+import sys
+import time
+
+path = sys.argv[1]
+timeout = float(sys.argv[2])
+mode = sys.argv[3]
+deadline = time.monotonic() + timeout
+last_report = None
+
+def ready(report):
+    if report.get("snapshotLoaded") is not True:
+        return False
+    if mode == "loaded":
+        return True
+    if mode == "activated":
+        return (
+            report.get("quickSwitchVisible") is False
+            and report.get("lastActivationResult") is not None
+        )
+    return False
+
+while time.monotonic() < deadline:
+    try:
+        with open(path, "r", encoding="utf-8") as file:
+            report = json.load(file)
+        last_report = report
+        if ready(report):
+            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"Finder tabs report {path} did not reach mode={mode} within {timeout:.1f}s", file=sys.stderr)
+sys.exit(1)
+PY
+}
+
+run_snapshot_dump() {
+  local snapshot="$1"
+  local stderr_log="$2"
+  stop_current_aligner
+  ALIGNER_DIAGNOSTICS_INCLUDE_SENSITIVE=1 \
+    "$APP/Contents/MacOS/Aligner" \
+    --round0-skip-permissions \
+    --round01-dump-window-snapshot \
+    --round01-dump-window-snapshot-pretty >"$snapshot" 2>"$stderr_log"
+}
+
+run_report() {
+  local report="$1"
+  local app_log="$2"
+  stop_current_aligner
+  rm -f "$report"
+  ALIGNER_DIAGNOSTICS_INCLUDE_SENSITIVE=1 \
+    "$APP/Contents/MacOS/Aligner" \
+    --round0-skip-permissions \
+    --round01-open-quick-switch \
+    --round01-disable-screenshot-refresh \
+    --round01-quick-switch-auto-hide-after=0.8 \
+    --round01-quick-switch-quit-after=1.3 \
+    --round01-quick-switch-report="$report" >"$app_log" 2>&1
+  wait_for_report "$report" "loaded"
+}
+
+run_click() {
+  local app_group_index="$1"
+  local window_index="$2"
+  local report="$3"
+  local app_log="$4"
+  stop_current_aligner
+  rm -f "$report"
+  ALIGNER_DIAGNOSTICS_INCLUDE_SENSITIVE=1 \
+    "$APP/Contents/MacOS/Aligner" \
+    --round0-skip-permissions \
+    --round01-open-quick-switch \
+    --round01-disable-screenshot-refresh \
+    --round01-debug-mouse-sequence="click-card:$app_group_index:$window_index" \
+    --round01-quick-switch-quit-after=1.8 \
+    --round01-quick-switch-report="$report" >"$app_log" 2>&1
+  wait_for_report "$report" "activated"
+}
+
+focused_finder_window_report() {
+  local output="$1"
+  swift - "$output" <<'SWIFT'
+import AppKit
+import ApplicationServices
+import Foundation
+
+func fail(_ message: String, code: Int32 = 2) -> Never {
+    fputs(message + "\n", stderr)
+    exit(code)
+}
+
+func jsonString(_ value: String) -> String {
+    let data = try! JSONSerialization.data(withJSONObject: ["value": value], options: [])
+    let text = String(data: data, encoding: .utf8)!
+    let prefix = "{\"value\":\""
+    let suffix = "\"}"
+    return String(text.dropFirst(prefix.count).dropLast(suffix.count))
+}
+
+func stableFingerprint(_ text: String) -> String {
+    var hash: UInt64 = 0xcbf29ce484222325
+    for byte in text.utf8 {
+        hash ^= UInt64(byte)
+        hash = hash &* 0x100000001b3
+    }
+    return String(format: "%016llx", hash)
+}
+
+func title(of element: AXUIElement) -> String? {
+    var value: CFTypeRef?
+    guard AXUIElementCopyAttributeValue(element, kAXTitleAttribute as CFString, &value) == .success else {
+        return nil
+    }
+    return value as? String
+}
+
+func focusedWindowID(_ element: AXUIElement) -> UInt32? {
+    var value: CFTypeRef?
+    if AXUIElementCopyAttributeValue(element, "AXWindowNumber" as CFString, &value) == .success,
+       let number = value as? NSNumber {
+        return number.uint32Value
+    }
+    return nil
+}
+
+let outputPath = CommandLine.arguments[1]
+guard let finder = NSWorkspace.shared.runningApplications.first(where: { $0.bundleIdentifier == "com.apple.finder" }) else {
+    fail("Finder is not running")
+}
+
+let appElement = AXUIElementCreateApplication(finder.processIdentifier)
+var windowValue: CFTypeRef?
+var axError = AXUIElementCopyAttributeValue(appElement, kAXFocusedWindowAttribute as CFString, &windowValue)
+if axError != .success || windowValue == nil || CFGetTypeID(windowValue!) != AXUIElementGetTypeID() {
+    axError = AXUIElementCopyAttributeValue(appElement, kAXMainWindowAttribute as CFString, &windowValue)
+}
+guard axError == .success,
+      let rawWindow = windowValue,
+      CFGetTypeID(rawWindow) == AXUIElementGetTypeID()
+else {
+    fail("Finder focused/main window is unavailable: \(axError)")
+}
+
+let window = rawWindow as! AXUIElement
+let windowTitle = title(of: window) ?? ""
+let output = """
+{
+  "bundleIdentifier": "com.apple.finder",
+  "pid": \(finder.processIdentifier),
+  "focusedWindowID": \(focusedWindowID(window).map(String.init) ?? "null"),
+  "title": "\(jsonString(windowTitle))",
+  "titleLength": \(windowTitle.count),
+  "titleHash": "\(stableFingerprint(windowTitle))"
+}
+"""
+try output.write(toFile: outputPath, atomically: true, encoding: .utf8)
+SWIFT
+}
+
+extract_attributed_ids() {
+  local log_file="$1"
+  /usr/bin/python3 - "$log_file" <<'PY'
+import re
+import sys
+
+ids = []
+for line in open(sys.argv[1], "r", encoding="utf-8", errors="replace"):
+    if "event=windowEnumeration.finderTabSpaceAttribution" not in line:
+        continue
+    match = re.search(r'attributedWindowIDs="?(\[[^\]"]*\])"?', line)
+    if not match:
+        continue
+    ids = [int(value) for value in re.findall(r"\d+", match.group(1))]
+
+if not ids:
+    print("No attributed Finder tab/page windowIDs found in this QA run", file=sys.stderr)
+    sys.exit(3)
+
+print(",".join(str(value) for value in sorted(set(ids))))
+PY
+}
+
+select_target() {
+  local snapshot="$1"
+  local report="$2"
+  local attributed_ids_csv="$3"
+  local output="$4"
+  /usr/bin/python3 - "$snapshot" "$report" "$attributed_ids_csv" "$output" <<'PY'
+import json
+import sys
+
+snapshot_path, report_path, ids_csv, output_path = sys.argv[1:5]
+attributed_ids = {int(value) for value in ids_csv.split(",") if value}
+with open(snapshot_path, "r", encoding="utf-8") as file:
+    snapshot = json.load(file)
+with open(report_path, "r", encoding="utf-8") as file:
+    report = json.load(file)
+
+def require(condition, message):
+    if not condition:
+        print(message, file=sys.stderr)
+        sys.exit(4)
+
+snapshot_windows = {window.get("id"): window for window in snapshot.get("windows", [])}
+finder_attributed = [
+    window for window_id, window in snapshot_windows.items()
+    if window_id in attributed_ids
+    and window.get("app", {}).get("bundleIdentifier") == "com.apple.finder"
+    and window.get("identifierSource") == "cgWindow"
+    and window.get("spaceIDs")
+    and window.get("title")
+]
+require(finder_attributed, "snapshot must contain at least one attributed Finder CG-only tab/page")
+
+root = report.get("rootView", {})
+cards = []
+for column in root.get("waterfallColumns", []):
+    if column.get("bundleIdentifier") != "com.apple.finder":
+        continue
+    for card in column.get("cards", []):
+        if card.get("windowID") not in attributed_ids:
+            continue
+        visible = card.get("visibleFrame") or {}
+        if visible.get("width", 0) <= 1 or visible.get("height", 0) <= 1:
+            continue
+        title = card.get("title") or snapshot_windows.get(card.get("windowID"), {}).get("title") or ""
+        if not title.strip():
+            continue
+        cards.append((column, card, title))
+
+require(cards, "Quick Switch report must expose at least one visible attributed Finder tab/page card")
+
+title_counts = {}
+for _, _, title in cards:
+    title_counts[title] = title_counts.get(title, 0) + 1
+
+cards.sort(key=lambda item: (
+    0 if title_counts[item[2]] == 1 else 1,
+    item[1].get("globalIndex", 10**9),
+    item[1].get("windowID", 10**9),
+))
+column, card, title = cards[0]
+target = {
+    "windowID": card["windowID"],
+    "appGroupIndex": column["appGroupIndex"],
+    "windowIndex": card["windowIndex"],
+    "primarySpaceID": card.get("primarySpaceID"),
+    "title": title,
+    "titleLength": len(title),
+    "titleHash": card.get("titleHash") or snapshot_windows.get(card.get("windowID"), {}).get("titleHash"),
+    "visibleFrame": card.get("visibleFrame"),
+}
+
+with open(output_path, "w", encoding="utf-8") as file:
+    json.dump(target, file, indent=2, ensure_ascii=False)
+
+print(f"TARGET_WINDOW_ID={target['windowID']}")
+print(f"APP_GROUP_INDEX={target['appGroupIndex']}")
+print(f"WINDOW_INDEX={target['windowIndex']}")
+print(f"TARGET_SPACE_ID={target['primarySpaceID']}")
+PY
+}
+
+assert_click_report() {
+  local report="$1"
+  local target="$2"
+  /usr/bin/python3 - "$report" "$target" <<'PY'
+import json
+import sys
+
+with open(sys.argv[1], "r", encoding="utf-8") as file:
+    report = json.load(file)
+with open(sys.argv[2], "r", encoding="utf-8") as file:
+    target = json.load(file)
+
+def require(condition, message):
+    if not condition:
+        print(message, file=sys.stderr)
+        print(json.dumps({
+            "targetWindowID": target.get("windowID"),
+            "lastCommittedWindowID": report.get("lastCommittedWindowID"),
+            "lastActivationWindowID": report.get("lastActivationWindowID"),
+            "lastActivationResult": report.get("lastActivationResult"),
+            "lastActivationError": report.get("lastActivationError"),
+            "quickSwitchVisible": report.get("quickSwitchVisible"),
+        }, indent=2, ensure_ascii=False), file=sys.stderr)
+        sys.exit(5)
+
+require(report.get("lastCommittedWindowID") == target["windowID"], "commit must target the clicked Finder card")
+require(report.get("lastActivationWindowID") == target["windowID"], "activation must receive exact Finder tab/page windowID")
+require(report.get("lastActivationResult") == "activated", "activation result must be activated")
+require(report.get("quickSwitchVisible") is False, "Quick Switch must close after click activation")
+PY
+}
+
+assert_focused_finder_title() {
+  local target="$1"
+  local focused="$2"
+  /usr/bin/python3 - "$target" "$focused" <<'PY'
+import json
+import sys
+
+with open(sys.argv[1], "r", encoding="utf-8") as file:
+    target = json.load(file)
+with open(sys.argv[2], "r", encoding="utf-8") as file:
+    focused = json.load(file)
+
+target_title = target.get("title", "").strip()
+focused_title = focused.get("title", "").strip()
+if target_title != focused_title:
+    print("Focused Finder window title must match clicked tab/page title", file=sys.stderr)
+    print(json.dumps({
+        "targetWindowID": target.get("windowID"),
+        "targetTitleHash": target.get("titleHash"),
+        "targetTitleLength": target.get("titleLength"),
+        "focusedWindowID": focused.get("focusedWindowID"),
+        "focusedTitleHash": focused.get("titleHash"),
+        "focusedTitleLength": focused.get("titleLength"),
+    }, indent=2, ensure_ascii=False), file=sys.stderr)
+    sys.exit(6)
+
+print(json.dumps({
+    "case": "finderTabRealActivation",
+    "targetWindowID": target.get("windowID"),
+    "targetSpaceID": target.get("primarySpaceID"),
+    "targetTitleHash": target.get("titleHash"),
+    "targetTitleLength": target.get("titleLength"),
+    "focusedWindowID": focused.get("focusedWindowID"),
+    "focusedTitleHash": focused.get("titleHash"),
+    "focusedTitleLength": focused.get("titleLength"),
+}, indent=2, ensure_ascii=False))
+PY
+}
+
+mkdir -p "$REPORT_DIR"
+stop_current_aligner
+"$SCRIPT_DIR/package-app.sh" >&2
+
+SNAPSHOT_JSON="$REPORT_DIR/snapshot.json"
+SNAPSHOT_STDERR="$REPORT_DIR/snapshot.stderr.log"
+SNAPSHOT_DEV_LOG="$REPORT_DIR/snapshot-dev.log"
+INITIAL_REPORT="$REPORT_DIR/initial-report.json"
+INITIAL_APP_LOG="$REPORT_DIR/initial-app.log"
+CLICK_REPORT="$REPORT_DIR/click-report.json"
+CLICK_APP_LOG="$REPORT_DIR/click-app.log"
+CLICK_DEV_LOG="$REPORT_DIR/click-dev.log"
+TARGET_JSON="$REPORT_DIR/target.json"
+FOCUSED_JSON="$REPORT_DIR/focused-finder.json"
+
+SNAPSHOT_LOG_OFFSET="$(dev_log_size)"
+run_snapshot_dump "$SNAPSHOT_JSON" "$SNAPSHOT_STDERR"
+sleep 0.4
+extract_dev_log_since "$SNAPSHOT_LOG_OFFSET" "$SNAPSHOT_DEV_LOG"
+ATTRIBUTED_IDS="$(extract_attributed_ids "$SNAPSHOT_DEV_LOG")"
+
+run_report "$INITIAL_REPORT" "$INITIAL_APP_LOG"
+eval "$(select_target "$SNAPSHOT_JSON" "$INITIAL_REPORT" "$ATTRIBUTED_IDS" "$TARGET_JSON")"
+
+CLICK_LOG_OFFSET="$(dev_log_size)"
+run_click "$APP_GROUP_INDEX" "$WINDOW_INDEX" "$CLICK_REPORT" "$CLICK_APP_LOG"
+sleep 0.8
+extract_dev_log_since "$CLICK_LOG_OFFSET" "$CLICK_DEV_LOG"
+focused_finder_window_report "$FOCUSED_JSON"
+
+assert_click_report "$CLICK_REPORT" "$TARGET_JSON"
+assert_focused_finder_title "$TARGET_JSON" "$FOCUSED_JSON"
+
+stop_current_aligner
+
+cat <<EOF
+Round01 Finder tabs live QA passed
+Log directory: $REPORT_DIR
+Covered:
+- Finder CG-only tab/page records inherit a high-confidence fullscreen Space.
+- Quick Switch click targets the exact Finder tab/page windowID.
+- Finder focused window title matches the clicked card after activation.
+EOF

--
Gitblit v1.9.3