From d87380d50a7bc242c8f6b3011c90a8f0bda08e6c Mon Sep 17 00:00:00 2001 From: Nick Hamze Date: Wed, 22 Jul 2026 18:04:05 -0500 Subject: [PATCH] Gate game confidence behind developer tools --- Sources/SwanSongApp/RootView.swift | 58 +++++++++++++------ .../UISnapshotRegressionTests.swift | 18 ++++++ .../ui-perceptual-baselines.json | 8 +-- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/Sources/SwanSongApp/RootView.swift b/Sources/SwanSongApp/RootView.swift index 580c944..7a461bc 100644 --- a/Sources/SwanSongApp/RootView.swift +++ b/Sources/SwanSongApp/RootView.swift @@ -1714,6 +1714,7 @@ private struct LibraryView: View { game: game, artwork: model.gameArtwork[game.id], confidence: model.gameConfidence(for: game), + developerToolsEnabled: model.debugToolsEnabled, isSelected: model.selectedGameID == game.id, canPlay: model.canPlayGame(game), managedHealth: model.managedGameHealth[game.id], @@ -1737,6 +1738,7 @@ private struct LibraryView: View { game: game, artwork: model.gameArtwork[game.id], confidence: model.gameConfidence(for: game), + developerToolsEnabled: model.debugToolsEnabled, canPlay: model.canPlayGame(game), managedHealth: model.managedGameHealth[game.id], isCheckingManagedCopy: model.checkingManagedGameIDs.contains(game.id), @@ -7262,6 +7264,12 @@ enum GameConfidenceAccessibility { } } +enum GameConfidencePresentation { + static func isVisible(developerToolsEnabled: Bool) -> Bool { + developerToolsEnabled + } +} + enum GameInspectorAccessibility { static let systemIdentity = "game-inspector-system-identity" static let runtimeStatus = "game-inspector-runtime-status" @@ -7383,6 +7391,7 @@ private struct GameCard: View { let game: GameRecord let artwork: GameArtworkRecord? let confidence: GameConfidence + let developerToolsEnabled: Bool let isSelected: Bool let canPlay: Bool let managedHealth: ManagedGameHealth? @@ -7473,8 +7482,12 @@ private struct GameCard: View { .foregroundStyle(readinessStatus.color) .padding(.top, 3) } - GameCompatibilityBadge(status: confidence.compatibility) - .padding(.top, readinessStatus == nil ? 3 : 1) + if GameConfidencePresentation.isVisible( + developerToolsEnabled: developerToolsEnabled + ) { + GameCompatibilityBadge(status: confidence.compatibility) + .padding(.top, readinessStatus == nil ? 3 : 1) + } } .padding(.horizontal, 4) .padding(.top, 10) @@ -7553,25 +7566,29 @@ private struct GameCard: View { private var accessibilityValue: String { let system = game.systemTitle let artworkDescription = artwork == nil ? "procedural artwork" : "captured gameplay artwork" - let compatibility = "play status: \(confidence.compatibility.confidenceTitle)" + let compatibility = GameConfidencePresentation.isVisible( + developerToolsEnabled: developerToolsEnabled + ) + ? "; play status: \(confidence.compatibility.confidenceTitle)" + : "" if isRepairingManagedCopy { - return "\(system); \(artworkDescription); repairing private copy; \(compatibility)" + return "\(system); \(artworkDescription); repairing private copy\(compatibility)" } if isCheckingManagedCopy { - return "\(system); \(artworkDescription); checking private copy; \(compatibility)" + return "\(system); \(artworkDescription); checking private copy\(compatibility)" } if managedHealth == .missing { - return "\(system); \(artworkDescription); private copy missing; repair needed; \(compatibility)" + return "\(system); \(artworkDescription); private copy missing; repair needed\(compatibility)" } if managedHealth == .changed { - return "\(system); \(artworkDescription); private copy changed; repair needed; \(compatibility)" + return "\(system); \(artworkDescription); private copy changed; repair needed\(compatibility)" } if managedHealth == .invalidReference { - return "\(system); \(artworkDescription); library identity invalid; re-add needed; \(compatibility)" + return "\(system); \(artworkDescription); library identity invalid; re-add needed\(compatibility)" } return canPlay - ? "\(system); \(artworkDescription); ready to play; \(compatibility)" - : "\(system); \(artworkDescription); engine unavailable; \(compatibility)" + ? "\(system); \(artworkDescription); ready to play\(compatibility)" + : "\(system); \(artworkDescription); engine unavailable\(compatibility)" } private var sourceDetail: String { @@ -8258,6 +8275,7 @@ struct GameInspector: View { let game: GameRecord let artwork: GameArtworkRecord? let confidence: GameConfidence + let developerToolsEnabled: Bool let canPlay: Bool let managedHealth: ManagedGameHealth? let isCheckingManagedCopy: Bool @@ -8312,14 +8330,18 @@ struct GameInspector: View { .controlSize(.large) } - GameConfidencePanel( - confidence: confidence, - evidence: game.compatibilityEvidence, - noteDraft: $compatibilityNoteDraft, - onSetVerdict: onSetCompatibilityVerdict, - onSaveNote: onSaveCompatibilityNote, - geometryProbe: geometryProbe - ) + if GameConfidencePresentation.isVisible( + developerToolsEnabled: developerToolsEnabled + ) { + GameConfidencePanel( + confidence: confidence, + evidence: game.compatibilityEvidence, + noteDraft: $compatibilityNoteDraft, + onSetVerdict: onSetCompatibilityVerdict, + onSaveNote: onSaveCompatibilityNote, + geometryProbe: geometryProbe + ) + } DisclosureGroup("Game Details", isExpanded: $showsGameDetails) { VStack(alignment: .leading, spacing: 8) { diff --git a/Tests/SwanSongAppSnapshotTests/UISnapshotRegressionTests.swift b/Tests/SwanSongAppSnapshotTests/UISnapshotRegressionTests.swift index 83f1595..82b9726 100644 --- a/Tests/SwanSongAppSnapshotTests/UISnapshotRegressionTests.swift +++ b/Tests/SwanSongAppSnapshotTests/UISnapshotRegressionTests.swift @@ -143,6 +143,7 @@ final class UISnapshotRegressionTests: XCTestCase { "librarySortOption", "showsLibraryInspector", "settingsPane", + "SwanSong.debugToolsEnabled.v1", ].map { key in (key, UserDefaults.standard.object(forKey: key)) } @@ -624,6 +625,12 @@ final class UISnapshotRegressionTests: XCTestCase { 28 ) XCTAssertEqual(GameConfidenceAccessibility.panel, "game-confidence-panel") + XCTAssertFalse( + GameConfidencePresentation.isVisible(developerToolsEnabled: false) + ) + XCTAssertTrue( + GameConfidencePresentation.isVisible(developerToolsEnabled: true) + ) XCTAssertEqual( GameConfidenceAccessibility.launchReadiness, "game-confidence-launch-readiness" @@ -775,6 +782,7 @@ final class UISnapshotRegressionTests: XCTestCase { "libraryWindowHeight", "librarySortOption", "showsLibraryInspector", + "SwanSong.debugToolsEnabled.v1", ].map { key in (key, UserDefaults.standard.object(forKey: key)) } @@ -789,6 +797,7 @@ final class UISnapshotRegressionTests: XCTestCase { let fixture = try gameConfidenceFixture( root: root.appendingPathComponent("game-confidence-geometry") ) + fixture.model.setDebugToolsEnabled(true) XCTAssertEqual(fixture.model.games.count, 4) XCTAssertEqual( fixture.model.gameConfidence(for: fixture.readyUntested), @@ -1635,6 +1644,7 @@ final class UISnapshotRegressionTests: XCTestCase { AnyView(TranslationTextIntakeView(model: translationTextDraftingModel)) }, Scenario(name: "game-confidence-compact", size: CGSize(width: 820, height: 560)) { + confidenceFixture.model.setDebugToolsEnabled(true) UserDefaults.standard.set(false, forKey: "showsLibraryInspector") return AnyView( RootView( @@ -1644,6 +1654,7 @@ final class UISnapshotRegressionTests: XCTestCase { ) }, Scenario(name: "game-confidence-wide", size: CGSize(width: 1_040, height: 680)) { + confidenceFixture.model.setDebugToolsEnabled(true) UserDefaults.standard.set(true, forKey: "showsLibraryInspector") return AnyView( RootView( @@ -1653,6 +1664,7 @@ final class UISnapshotRegressionTests: XCTestCase { ) }, Scenario(name: "pocket-challenge-v2-compact", size: CGSize(width: 820, height: 560)) { + pocketChallengeFixture.model.setDebugToolsEnabled(false) UserDefaults.standard.set(false, forKey: "showsLibraryInspector") return AnyView( RootView( @@ -1668,6 +1680,7 @@ final class UISnapshotRegressionTests: XCTestCase { self.scrollLibraryInspectorToBottom(in: host) } ) { + pocketChallengeFixture.model.setDebugToolsEnabled(false) UserDefaults.standard.set(true, forKey: "showsLibraryInspector") return AnyView( RootView( @@ -1711,6 +1724,7 @@ final class UISnapshotRegressionTests: XCTestCase { size: CGSize(width: 640, height: 620), usesPolishOutput: true ) { + confidenceFixture.model.setDebugToolsEnabled(false) UserDefaults.standard.set(1, forKey: "settingsPane") return AnyView(self.settingsSurface(model: confidenceFixture.model)) }, @@ -1719,6 +1733,7 @@ final class UISnapshotRegressionTests: XCTestCase { size: CGSize(width: 900, height: 608), usesPolishOutput: true ) { + confidenceFixture.model.setDebugToolsEnabled(false) UserDefaults.standard.set(1, forKey: "settingsPane") return AnyView(self.settingsSurface(model: confidenceFixture.model)) }, @@ -1727,6 +1742,7 @@ final class UISnapshotRegressionTests: XCTestCase { size: CGSize(width: 900, height: 608), usesPolishOutput: true ) { + confidenceFixture.model.setDebugToolsEnabled(false) UserDefaults.standard.set(0, forKey: "settingsPane") return AnyView(self.settingsSurface(model: confidenceFixture.model)) }, @@ -1735,6 +1751,7 @@ final class UISnapshotRegressionTests: XCTestCase { size: CGSize(width: 900, height: 608), usesPolishOutput: true ) { + confidenceFixture.model.setDebugToolsEnabled(false) UserDefaults.standard.set(3, forKey: "settingsPane") return AnyView(self.settingsSurface(model: confidenceFixture.model)) }, @@ -1770,6 +1787,7 @@ final class UISnapshotRegressionTests: XCTestCase { game: game, artwork: model.gameArtwork[game.id], confidence: model.gameConfidence(for: game), + developerToolsEnabled: true, canPlay: model.canPlayGame(game), managedHealth: model.managedGameHealth[game.id], isCheckingManagedCopy: model.checkingManagedGameIDs.contains(game.id), diff --git a/Tests/SwanSongAppSnapshotTests/ui-perceptual-baselines.json b/Tests/SwanSongAppSnapshotTests/ui-perceptual-baselines.json index 0776967..31274b4 100644 --- a/Tests/SwanSongAppSnapshotTests/ui-perceptual-baselines.json +++ b/Tests/SwanSongAppSnapshotTests/ui-perceptual-baselines.json @@ -36,10 +36,10 @@ "player-recovery-compact-light" : "54008240c10020013043680365517611640144011201440144010a0020008020", "player-recovery-wide-dark" : "5000800081008100000140651035100d51db2e6540610001c000400040004000", "player-recovery-wide-light" : "500080008100810000216065606560612a25106540654041c000400040004000", - "pocket-challenge-v2-compact-dark" : "7010d9c75bc753c563cbc3cdc304c18c00202040404060840000020102010002", - "pocket-challenge-v2-compact-light" : "04a4079687968794cf9a0f9c0e780e600e80ca800a808d000a000a000c000800", - "pocket-challenge-v2-wide-dark" : "600766732675c6f3c66546c5800d400d604540440098008301010045000f0403", - "pocket-challenge-v2-wide-light" : "1c981cc89cc81ccc1988993814b0955095589a98130115181a00121814101814", + "pocket-challenge-v2-compact-dark" : "7010d9c75bc753c563cbc3cdc304c01400202040404060840000020102010002", + "pocket-challenge-v2-compact-light" : "04a4079687968794cf9a0f9c0e780ca00d40ca800a808d000a000a000c000800", + "pocket-challenge-v2-wide-dark" : "600366672663c6e7c66744c48003400460414047008b00800109004200020402", + "pocket-challenge-v2-wide-light" : "1c9a1cde9cda1cde199899381abc954895409a98131415001a00120014001800", "pocket-core-setup-compact-dark" : "401063504f55000026002c007800002040203c00228030000109380023612801", "pocket-core-setup-compact-light" : "aa609ca0b0a29240d801d0018001c2008080d0019500c001c4008000d480d000", "pocket-core-setup-wide-dark" : "20103cf434c4160025001c0020002c002680180000890d000c60000008002a86",