commit 7ad4815ea00185d58b0d70649924fa629d88a505
parent 97b38cecda3a43ef5f5f73a7e21cef1811c0886a
Author: triesap <tyson@radroots.org>
Date: Wed, 9 Sep 2026 16:28:14 +0000
ios: decouple local product startup from refresh
- Start Today and Add concurrently after the authorized app transition.
- Own and coalesce startup tasks across cancellation and lifecycle changes.
- Reject stale startup completion while preserving unsent Add editing.
- Verify stalled refresh, replacement startup and identity authorization behavior.
Diffstat:
10 files changed, 349 insertions(+), 72 deletions(-)
diff --git a/Package.swift b/Package.swift
@@ -49,6 +49,7 @@ let package = Package(
"App/AppEntry.swift",
"App/TeraAppDelegate.swift",
"App/TeraAppModel.swift",
+ "App/TeraProductStores.swift",
"App/TeraProvider.swift",
"App/TeraRemoteQualification.swift",
"App/TeraRemoteQualificationEvidence.swift",
diff --git a/Tera.xcodeproj/project.pbxproj b/Tera.xcodeproj/project.pbxproj
@@ -49,6 +49,7 @@
BB71614D2A6017C071A39BED /* TeraMutationAdmissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F322CF98F8BEB908B93215 /* TeraMutationAdmissionTests.swift */; };
C219B5F4E24188E1116BF45B /* TeraRuntimeClientFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88ED360C84532DA7088BFC6E /* TeraRuntimeClientFixtures.swift */; };
C3177E827FE62639579C3EF5 /* TeraRemoteQualificationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CBDAE974FB8D7408108E69 /* TeraRemoteQualificationUITests.swift */; };
+ C46F8EB5B196FFDD37E0279A /* TeraProductStartupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF60EF9DCFDC4ECE77AED67 /* TeraProductStartupTests.swift */; };
CDBE38E6B521611436F28858 /* TeraMediaStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B6F0B16BFD8F046E9C163F /* TeraMediaStoreTests.swift */; };
D64AC2FDC6FE05A39114D2DB /* TeraMediaFileFixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C914B349DAE2EF66658229 /* TeraMediaFileFixture.swift */; };
D918A664D9A4B9859A1A6503 /* TeraErrorRecoveryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93A3358AFC23C567DB9CB02F /* TeraErrorRecoveryTests.swift */; };
@@ -107,6 +108,7 @@
73EBD10E201ECA26DCED523F /* TeraRuntimeInvalidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraRuntimeInvalidationTests.swift; sourceTree = "<group>"; };
74FCF1C4A8A7137623BBE223 /* TeraOpenedMediaCloseFixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraOpenedMediaCloseFixture.swift; sourceTree = "<group>"; };
794D475C70BA8ED994D1CA66 /* TeraTodayStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraTodayStoreTests.swift; sourceTree = "<group>"; };
+ 7AF60EF9DCFDC4ECE77AED67 /* TeraProductStartupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraProductStartupTests.swift; sourceTree = "<group>"; };
7C0836F5D5F71F9A54795C57 /* TeraSessionGenerationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraSessionGenerationTests.swift; sourceTree = "<group>"; };
8591841AD9AB9B718A37EB47 /* TeraRuntimeBackpressureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraRuntimeBackpressureTests.swift; sourceTree = "<group>"; };
88ED360C84532DA7088BFC6E /* TeraRuntimeClientFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TeraRuntimeClientFixtures.swift; sourceTree = "<group>"; };
@@ -214,6 +216,7 @@
21DD785439F415278214D746 /* TeraObservationCoalescingTests.swift */,
74FCF1C4A8A7137623BBE223 /* TeraOpenedMediaCloseFixture.swift */,
9B10B1F3C7FCD9A72F04FC00 /* TeraOpenedMediaTests.swift */,
+ 7AF60EF9DCFDC4ECE77AED67 /* TeraProductStartupTests.swift */,
E351F52E77C74A4E99095E45 /* TeraRemoteQualificationTests.swift */,
6B8677670E203FF68EC88983 /* TeraRootShellTests.swift */,
8591841AD9AB9B718A37EB47 /* TeraRuntimeBackpressureTests.swift */,
@@ -464,6 +467,7 @@
AC33144E09CB0384C4FB090C /* TeraObservationCoalescingTests.swift in Sources */,
2A68D8FEEC3B88D47D696589 /* TeraOpenedMediaCloseFixture.swift in Sources */,
6FED2D2255CC702B33CDC760 /* TeraOpenedMediaTests.swift in Sources */,
+ C46F8EB5B196FFDD37E0279A /* TeraProductStartupTests.swift in Sources */,
81AF9DDA0E64DC4EEFCEF25D /* TeraRemoteQualificationTests.swift in Sources */,
9AD968F0B37C64B5AF8D9A68 /* TeraRootShellTests.swift in Sources */,
2B50FA35AD753EFA15CF00CD /* TeraRuntimeBackpressureTests.swift in Sources */,
diff --git a/Tera/App/TeraAppModel.swift b/Tera/App/TeraAppModel.swift
@@ -2,68 +2,6 @@ import Foundation
import RadrootsKit
@MainActor
-final class TeraProductStores {
- let today: TeraTodayStore
- let add: TeraAddStore
- let search: TeraSearchStore
- let me: TeraMeStore
- let settings: TeraSettingsStore
- let media: TeraMediaStore
- private var generation = TeraSessionGeneration.initial
-
- init(
- runtimeClient: TeraRuntimeClient,
- addMedia: (any TeraAddMediaHandling)? = nil
- ) {
- today = TeraTodayStore(runtimeClient: runtimeClient)
- add = TeraAddStore(runtimeClient: runtimeClient, media: addMedia)
- search = TeraSearchStore(runtimeClient: runtimeClient)
- me = TeraMeStore(runtimeClient: runtimeClient)
- settings = TeraSettingsStore(runtimeClient: runtimeClient)
- media = TeraMediaStore(runtimeClient: runtimeClient)
- }
-
- func configure(snapshot: TeraRuntimeSnapshot) {
- generation = generation.invalidated()
- today.configure(snapshot: snapshot)
- add.configure(snapshot: snapshot)
- search.stop()
- search.configure(context: today.selectedContext)
- me.stop()
- me.configure(context: today.selectedContext)
- settings.configure(snapshot: snapshot)
- media.configure(snapshot: snapshot)
- }
-
- func resume() async {
- let requested = generation
- await today.start()
- guard generation == requested, generation.isActive, !Task.isCancelled else { return }
- await add.start()
- }
-
- func suspend() {
- generation = generation.invalidated()
- today.stop()
- add.suspend()
- search.stop()
- me.stop()
- settings.stop()
- media.reset()
- }
-
- func stop() {
- generation = generation.invalidated()
- today.stop()
- add.stop()
- search.stop()
- me.stop()
- settings.stop()
- media.reset()
- }
-}
-
-@MainActor
final class TeraAppModel: ObservableObject {
typealias Phase = TeraSessionPhase
@@ -274,6 +212,9 @@ final class TeraAppModel: ObservableObject {
productStores?.configure(snapshot: snapshot)
}
phase = result
+ if case .running = result {
+ productStores?.start()
+ }
await lifecycleCoordinator.record(
"ios.lifecycle.operation_completed",
level: Self.isFailure(result) ? .warning : .info,
diff --git a/Tera/App/TeraProductStores.swift b/Tera/App/TeraProductStores.swift
@@ -0,0 +1,101 @@
+import Foundation
+
+@MainActor
+final class TeraProductStores {
+ let today: TeraTodayStore
+ let add: TeraAddStore
+ let search: TeraSearchStore
+ let me: TeraMeStore
+ let settings: TeraSettingsStore
+ let media: TeraMediaStore
+ private var generation = TeraSessionGeneration.initial
+ private var startupTask: Task<Void, Never>?
+
+ init(
+ runtimeClient: TeraRuntimeClient,
+ addMedia: (any TeraAddMediaHandling)? = nil
+ ) {
+ today = TeraTodayStore(runtimeClient: runtimeClient)
+ add = TeraAddStore(runtimeClient: runtimeClient, media: addMedia)
+ search = TeraSearchStore(runtimeClient: runtimeClient)
+ me = TeraMeStore(runtimeClient: runtimeClient)
+ settings = TeraSettingsStore(runtimeClient: runtimeClient)
+ media = TeraMediaStore(runtimeClient: runtimeClient)
+ }
+
+ deinit {
+ startupTask?.cancel()
+ }
+
+ func configure(snapshot: TeraRuntimeSnapshot) {
+ let wasStarting = startupTask != nil
+ cancelStartup()
+ if wasStarting {
+ today.stop()
+ add.suspend()
+ }
+ today.configure(snapshot: snapshot)
+ add.configure(snapshot: snapshot)
+ search.stop()
+ search.configure(context: today.selectedContext)
+ me.stop()
+ me.configure(context: today.selectedContext)
+ settings.configure(snapshot: snapshot)
+ media.configure(snapshot: snapshot)
+ }
+
+ func start() {
+ guard startupTask == nil, generation.isActive, !Task.isCancelled else { return }
+ generation = generation.invalidated()
+ guard generation.isActive else { return }
+ let requested = generation
+ startupTask = Task { [weak self, today, add] in
+ async let todayStart: Void = today.start()
+ async let addStart: Void = add.start()
+ _ = await (todayStart, addStart)
+ self?.finishStartup(requested, cancelled: Task.isCancelled)
+ }
+ }
+
+ func resume() async {
+ guard !Task.isCancelled else { return }
+ start()
+ guard let task = startupTask else { return }
+ await withTaskCancellationHandler { await task.value } onCancel: { task.cancel() }
+ }
+
+ private func finishStartup(_ requested: TeraSessionGeneration, cancelled: Bool) {
+ guard generation == requested else { return }
+ startupTask = nil
+ if cancelled {
+ today.stop()
+ add.suspend()
+ }
+ }
+
+ func suspend() {
+ cancelStartup()
+ today.stop()
+ add.suspend()
+ search.stop()
+ me.stop()
+ settings.stop()
+ media.reset()
+ }
+
+ func stop() {
+ cancelStartup()
+ today.stop()
+ add.stop()
+ search.stop()
+ me.stop()
+ settings.stop()
+ media.reset()
+ }
+
+ private func cancelStartup() {
+ generation = generation.invalidated()
+ startupTask?.cancel()
+ startupTask = nil
+ }
+}
diff --git a/TeraTests/TeraProductStartupTests.swift b/TeraTests/TeraProductStartupTests.swift
@@ -0,0 +1,166 @@
+@testable import TeraApp
+import XCTest
+
+@MainActor
+final class TeraProductStartupTests: XCTestCase {
+ func testLocalAddIsEditableWhileTodayRefreshHasNotReturned() async throws {
+ let fixture = try await StartupFixture()
+ let refresh = await fixture.backend.pause(.refresh)
+ fixture.stores.start()
+ await refresh.entered.wait()
+ await TeraScopeFixtures.eventually { fixture.stores.add.isProductReady }
+ XCTAssertEqual(Set(fixture.stores.add.schemas.map(\.commandType)), Set(TeraAddCommandType.allCases))
+ XCTAssertEqual(fixture.stores.add.drafts.first?.form?.content, "old")
+ XCTAssertEqual(fixture.stores.today.presentation.refresh, .refreshing)
+ fixture.stores.add.updateForm(\.content, "Compose while refresh is held")
+ XCTAssertTrue(fixture.stores.add.canSave)
+ XCTAssertEqual(fixture.stores.add.form.content, "Compose while refresh is held")
+ fixture.stores.suspend()
+ await refresh.resume.open()
+ try await fixture.close()
+ }
+
+ func testSuspendCancelsBothStartupReadsBeforeTheirBackendsReturn() async throws {
+ let fixture = try await StartupFixture()
+ let page = await fixture.backend.pause(.page)
+ let drafts = await fixture.backend.pause(.drafts)
+ let task = Task { await fixture.stores.resume() }
+ await page.entered.wait()
+ await drafts.entered.wait()
+ fixture.stores.suspend()
+ await task.value
+ XCTAssertTrue(fixture.stores.today.cards.isEmpty)
+ XCTAssertTrue(fixture.stores.add.schemas.isEmpty)
+ XCTAssertTrue(fixture.stores.add.drafts.isEmpty)
+ XCTAssertFalse(fixture.stores.add.isProductReady)
+ XCTAssertEqual(fixture.stores.today.observationState, .stopped)
+ XCTAssertEqual(fixture.stores.add.observationState, .stopped)
+ await page.resume.open()
+ await drafts.resume.open()
+ try await fixture.close()
+ XCTAssertTrue(fixture.stores.add.schemas.isEmpty)
+ XCTAssertTrue(fixture.stores.add.drafts.isEmpty)
+ XCTAssertTrue(fixture.stores.today.cards.isEmpty)
+ }
+
+ func testCanceledResumeStopsBothObserversAndCanStartAgain() async throws {
+ let fixture = try await StartupFixture()
+ let refresh = await fixture.backend.pause(.refresh)
+ let drafts = await fixture.backend.pause(.drafts)
+ let task = Task { await fixture.stores.resume() }
+ await refresh.entered.wait()
+ await drafts.entered.wait()
+ task.cancel()
+ await task.value
+ XCTAssertEqual(fixture.stores.today.observationState, .stopped)
+ XCTAssertEqual(fixture.stores.add.observationState, .stopped)
+ XCTAssertFalse(fixture.stores.add.isProductReady)
+ await refresh.resume.open()
+ await drafts.resume.open()
+ await fixture.releaseObservers()
+ await fixture.stores.resume()
+ XCTAssertTrue(fixture.stores.add.isProductReady)
+ XCTAssertEqual(fixture.stores.add.schemas.count, 5)
+ try await fixture.close()
+ }
+
+ func testConcurrentStartsAndResumesKeepOneObserverPerStore() async throws {
+ let fixture = try await StartupFixture()
+ let refresh = await fixture.backend.pause(.refresh)
+ fixture.stores.start()
+ fixture.stores.start()
+ let first = Task { await fixture.stores.resume() }
+ let second = Task { await fixture.stores.resume() }
+ await refresh.entered.wait()
+ await TeraScopeFixtures.eventually { fixture.stores.add.isProductReady }
+ for observer in fixture.observers {
+ await observer.entered.wait()
+ }
+ let counts = await fixture.backend.counts
+ XCTAssertEqual(counts[.subscribe], 2)
+ XCTAssertEqual(counts[.refresh], 1)
+ XCTAssertEqual(counts[.drafts], 1)
+ await fixture.releaseObservers()
+ await refresh.resume.open()
+ await first.value
+ await second.value
+ let final = await fixture.backend.counts
+ XCTAssertEqual(final[.subscribe], 2)
+ XCTAssertEqual(final[.refresh], 1)
+ try await fixture.close()
+ }
+
+ func testReconfigurationRejectsOldStartupAndKeepsTheReplacementOwned() async throws {
+ for changed in [false, true] {
+ try await assertReplacement(changed: changed)
+ }
+ }
+
+ func testSuspendAndResumePreserveTheUnsentEditingForm() async throws {
+ let fixture = try await StartupFixture()
+ await fixture.releaseObservers()
+ await fixture.stores.resume()
+ fixture.stores.add.updateForm(\.content, "Unsent local editing")
+ fixture.stores.suspend()
+ await fixture.stores.resume()
+ XCTAssertTrue(fixture.stores.add.isProductReady)
+ XCTAssertEqual(fixture.stores.add.form.content, "Unsent local editing")
+ try await fixture.close()
+ }
+
+ private func assertReplacement(changed: Bool) async throws {
+ let fixture = try await StartupFixture()
+ let oldRefresh = await fixture.backend.pause(.refresh)
+ let oldDrafts = await fixture.backend.pause(.drafts)
+ let old = Task { await fixture.stores.resume() }
+ await oldRefresh.entered.wait()
+ await oldDrafts.entered.wait()
+ let snapshot = TeraScopeFixtures.snapshot(relay: changed ? "second" : "first")
+ await fixture.backend.configure(snapshot)
+ await fixture.backend.setDrafts([TeraScopeFixtures.draft("replacement", revision: 2)])
+ fixture.stores.configure(snapshot: snapshot)
+ let newRefresh = await fixture.backend.pause(.refresh)
+ let current = Task { await fixture.stores.resume() }
+ await newRefresh.entered.wait()
+ await old.value
+ await TeraScopeFixtures.eventually { fixture.stores.add.isProductReady }
+ XCTAssertEqual(fixture.stores.add.drafts.first?.form?.content, "replacement")
+ XCTAssertEqual(fixture.stores.today.selectedContext?.relayURLs, snapshot.relay?.relays.map(\.url))
+ fixture.stores.suspend()
+ await current.value
+ XCTAssertEqual(fixture.stores.today.observationState, .stopped)
+ await oldRefresh.resume.open()
+ await oldDrafts.resume.open()
+ await newRefresh.resume.open()
+ try await fixture.close()
+ XCTAssertEqual(fixture.stores.add.drafts.first?.form?.content, "replacement")
+ }
+}
+
+@MainActor
+private struct StartupFixture {
+ let backend: TeraScopeBackend
+ let client: TeraRuntimeClient
+ let stores: TeraProductStores
+ let observers: [ResourceTestPause]
+
+ init() async throws {
+ backend = try TeraScopeBackend()
+ client = try await TeraScopeFixtures.client(backend)
+ stores = TeraProductStores(runtimeClient: client)
+ stores.configure(snapshot: TeraScopeFixtures.snapshot())
+ observers = await [backend.pause(.subscribe), backend.pause(.subscribe)]
+ }
+
+ func releaseObservers() async {
+ for observer in observers {
+ await observer.resume.open()
+ }
+ }
+
+ func close() async throws {
+ stores.stop()
+ await releaseObservers()
+ _ = try await client.stop()
+ }
+}
diff --git a/TeraTests/TeraScopedStoreTests.swift b/TeraTests/TeraScopedStoreTests.swift
@@ -97,21 +97,30 @@ final class TeraScopedStoreTests: XCTestCase {
_ = try await client.stop()
}
- func testStoppedProductResumeCannotStartAddAfterLateTodayCompletion() async throws {
+ func testStoppedProductResumeCannotInstallLateStartupResults() async throws {
let backend = try TeraScopeBackend()
let client = try await TeraScopeFixtures.client(backend)
let stores = TeraProductStores(runtimeClient: client)
stores.configure(snapshot: TeraScopeFixtures.snapshot())
+ let firstObserver = await backend.pause(.subscribe)
+ let secondObserver = await backend.pause(.subscribe)
+ let drafts = await backend.pause(.drafts)
let pause = await backend.pause(.page)
let old = Task { await stores.resume() }
await pause.entered.wait()
+ await drafts.entered.wait()
stores.stop()
- await pause.resume.open()
await old.value
- XCTAssertEqual(stores.add.state, .idle)
+ XCTAssertFalse(stores.add.isProductReady)
+ XCTAssertTrue(stores.add.schemas.isEmpty)
+ XCTAssertTrue(stores.add.drafts.isEmpty)
XCTAssertEqual(stores.add.observationState, .stopped)
let calls = await backend.counts[.drafts, default: 0]
- XCTAssertEqual(calls, 0)
+ XCTAssertEqual(calls, 1)
+ await pause.resume.open()
+ await drafts.resume.open()
+ await firstObserver.resume.open()
+ await secondObserver.resume.open()
_ = try await client.stop()
}
}
diff --git a/TeraTests/TeraSessionResourceTests.swift b/TeraTests/TeraSessionResourceTests.swift
@@ -4,6 +4,54 @@ import RadrootsKit
import XCTest
final class TeraSessionResourceTests: XCTestCase {
+ @MainActor
+ func testAppModelStartsBothProductStoresAfterSessionAuthorization() async throws {
+ let fixture = try await Fixture()
+ defer { fixture.state.remove() }
+ let model = TeraAppModel(
+ sessionStore: fixture.session, runtimeClient: fixture.client,
+ lifecycleCoordinator: .disabled()
+ )
+ let stores = try XCTUnwrap(model.productStores)
+ XCTAssertEqual(stores.add.state, .idle)
+ XCTAssertNil(stores.today.presentation.readFailure)
+ await model.start()
+ if case .running = model.phase {} else {
+ XCTFail("The authorized session must run")
+ }
+ // This session fixture deliberately rejects product queries. Observing both
+ // typed failures proves launch dispatched them without another resume event.
+ await TeraScopeFixtures.eventually {
+ if case .failed = stores.add.state {
+ return stores.today.presentation.readFailure != nil
+ }
+ return false
+ }
+ await model.stop()
+ XCTAssertEqual(stores.today.observationState, .stopped)
+ XCTAssertEqual(stores.add.observationState, .stopped)
+ }
+
+ @MainActor
+ func testAppModelDoesNotStartProductStoresBeforeIdentityAuthorization() async throws {
+ let fixture = try await Fixture(createIdentity: false)
+ defer { fixture.state.remove() }
+ let model = TeraAppModel(
+ sessionStore: fixture.session, runtimeClient: fixture.client,
+ lifecycleCoordinator: .disabled()
+ )
+ let stores = try XCTUnwrap(model.productStores)
+ await model.start()
+ XCTAssertEqual(model.phase, .identityRequired)
+ XCTAssertEqual(stores.add.state, .idle)
+ XCTAssertTrue(stores.add.schemas.isEmpty)
+ XCTAssertTrue(stores.today.cards.isEmpty)
+ XCTAssertNil(stores.today.presentation.readFailure)
+ XCTAssertEqual(stores.today.observationState, .stopped)
+ XCTAssertEqual(stores.add.observationState, .stopped)
+ await model.stop()
+ }
+
func testStaleStartupSnapshotDoesNotStopNewSharedSession() async throws {
let fixture = try await Fixture()
defer { fixture.state.remove() }
@@ -57,7 +105,7 @@ final class TeraSessionResourceTests: XCTestCase {
let client: TeraRuntimeClient
let session: TeraSessionStore
- init() async throws {
+ init(createIdentity: Bool = true) async throws {
state = try StateFixture()
let secure = InMemorySecureStore()
let custody = try RadrootsIdentityCustody(
@@ -71,8 +119,14 @@ final class TeraSessionResourceTests: XCTestCase {
custody: custody, secureStore: secure,
servicePrefix: "org.tera.tests.session.\(UUID().uuidString.lowercased())"
)
- let created = try await identity.create()
- let backend = try ResourceTestBackend(publicKeyHex: XCTUnwrap(created.publicKeyHex))
+ let publicKey: String
+ if createIdentity {
+ let created = try await identity.create()
+ publicKey = try XCTUnwrap(created.publicKeyHex)
+ } else {
+ publicKey = String(repeating: "ab", count: 32)
+ }
+ let backend = ResourceTestBackend(publicKeyHex: publicKey)
self.backend = backend
let client = TeraRuntimeClient(factory: { _ in await backend.start() })
self.client = client
diff --git a/project.yml b/project.yml
@@ -63,6 +63,7 @@ targets:
- path: TeraTests/TeraTodayStoreTests.swift
- path: TeraTests/TeraTodayPresentationTests.swift
- path: TeraTests/TeraTodayCacheFirstTests.swift
+ - path: TeraTests/TeraProductStartupTests.swift
- path: TeraTests/TeraAddSchemaFixtures.swift
- path: TeraTests/TeraScopeFixtures.swift
- path: TeraTests/TeraScopeBackend.swift
diff --git a/release/provenance.json b/release/provenance.json
@@ -6,7 +6,7 @@
"info_plist_sha256": "15ef08b1cdd1096cfb9eeaf5be5bf8f814807a7ca9350bbbb47860fa72ec13ef",
"privacy_manifest_sha256": "a331d51864743ebe4e00dd22360b4a538b6b3ac26a6b3eb54094e60a36959a12",
"sbom_sha256": "6b5ce897d5273290febc8b831663e12dea759cb0084f182ba7949e1c593fc410",
- "xcode_project_sha256": "f7204400383da3d0aacd550654a772742352f99942153c3d8c4b88c685f979b3"
+ "xcode_project_sha256": "06f5970c0e75efb3ef97ad2bf0b3f13aad54b0b99787f24aa44076606fbb778a"
},
"disposition": "unsigned",
"platforms": [
diff --git a/test-fixtures/swiftlint-maintainability-baseline.v1.json b/test-fixtures/swiftlint-maintainability-baseline.v1.json
@@ -1 +1 @@
-[{"text":" init(","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Initializer body should span 50 lines or less excluding comments and whitespace: currently spans 52 lines","location":{"file":"Tera\/App\/TeraAppModel.swift","character":3,"line":83},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"actor TeraAddMediaCoordinator: TeraAddMediaHandling {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 250 lines or less excluding comments and whitespace: currently spans 330 lines","location":{"file":"Tera\/Runtime\/TeraAddMediaCoordinator.swift","character":1,"line":51},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"#endif","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 458","location":{"file":"Tera\/Runtime\/TeraAddMediaCoordinator.swift","character":1,"line":458},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"private final class TeraGeneratedRuntimeBackend: TeraRuntimeBackend, @unchecked Sendable {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 545 lines","location":{"file":"Tera\/Runtime\/TeraGeneratedRuntimeBackend.swift","character":15,"line":111},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1640","location":{"file":"Tera\/Runtime\/TeraGeneratedRuntimeBackend.swift","character":1,"line":1640},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"actor TeraRuntimeClient {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 787 lines","location":{"file":"Tera\/Runtime\/TeraRuntimeClient.swift","character":1,"line":291},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func changes(bufferCapacity: Int = 16) async throws -> AsyncStream<TeraRuntimeChange> {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 70 lines","location":{"file":"Tera\/Runtime\/TeraRuntimeClient.swift","character":3,"line":703},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1159","location":{"file":"Tera\/Runtime\/TeraRuntimeClient.swift","character":1,"line":1159},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1210","location":{"file":"Tera\/Runtime\/TeraRuntimeModels.swift","character":1,"line":1210},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraAddStore: ObservableObject {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 731 lines","location":{"file":"Tera\/State\/TeraAddStore.swift","character":7,"line":11},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func retract(_ card: TeraTodayCard) async {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 51 lines","location":{"file":"Tera\/State\/TeraAddStore.swift","character":3,"line":459},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 796","location":{"file":"Tera\/State\/TeraAddStore.swift","character":1,"line":796},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"actor TeraConfigurationStore {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 468 lines","location":{"file":"Tera\/State\/TeraConfigurationStore.swift","character":1,"line":78},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func load() throws -> TeraAppConfiguration {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 100 lines or less excluding comments and whitespace: currently spans 109 lines","location":{"file":"Tera\/State\/TeraConfigurationStore.swift","character":5,"line":148},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 759","location":{"file":"Tera\/State\/TeraConfigurationStore.swift","character":1,"line":759},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"actor TeraSessionStore {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 390 lines","location":{"file":"Tera\/State\/TeraSessionStore.swift","character":1,"line":39},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" private func start(acceptingReconfiguration: Bool) async -> TeraSessionPhase {","violation":{"severity":"error","ruleIdentifier":"cyclomatic_complexity","reason":"Function should have complexity 10 or less; currently complexity is 13","location":{"file":"Tera\/State\/TeraSessionStore.swift","character":11,"line":125},"ruleName":"Cyclomatic Complexity","ruleDescription":"Complexity of function bodies should be limited."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 452","location":{"file":"Tera\/State\/TeraSessionStore.swift","character":1,"line":452},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"struct TeraAddView: View {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Struct body should span 350 lines or less excluding comments and whitespace: currently spans 454 lines","location":{"file":"Tera\/Views\/TeraAddView.swift","character":1,"line":4},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 560","location":{"file":"Tera\/Views\/TeraAddView.swift","character":1,"line":560},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"struct TeraSettingsView: View {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Struct body should span 250 lines or less excluding comments and whitespace: currently spans 259 lines","location":{"file":"Tera\/Views\/TeraSupportingViews.swift","character":1,"line":365},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 629","location":{"file":"Tera\/Views\/TeraSupportingViews.swift","character":1,"line":629},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 474","location":{"file":"Tera\/Views\/TeraTodayView.swift","character":1,"line":474},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraAddStoreTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 665 lines","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":7,"line":6},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"private actor AddBackend: TeraRuntimeBackend {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 492 lines","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":9,"line":844},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" private func makeStatus(","violation":{"severity":"error","ruleIdentifier":"function_parameter_count","reason":"Function should have 5 parameters or less: it currently has 8","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":11,"line":1280},"ruleName":"Function Parameter Count","ruleDescription":"Number of function parameters should be low."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1431","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":1,"line":1431},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraRemoteQualificationTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 250 lines or less excluding comments and whitespace: currently spans 307 lines","location":{"file":"TeraTests\/TeraRemoteQualificationTests.swift","character":7,"line":7},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func testQualificationRejectsAmbiguousOrUnsafeHarnessValues() {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 94 lines","location":{"file":"TeraTests\/TeraRemoteQualificationTests.swift","character":3,"line":45},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" func testAuthorizationEvidenceIsRedactedEphemeralAndRelaunchCleaned() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 53 lines","location":{"file":"TeraTests\/TeraRemoteQualificationTests.swift","character":3,"line":212},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"final class TeraRuntimeClientTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 250 lines or less excluding comments and whitespace: currently spans 298 lines","location":{"file":"TeraTests\/TeraRuntimeClientTests.swift","character":7,"line":4},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"final class TeraStateMigrationTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 580 lines","location":{"file":"TeraTests\/TeraStateMigrationTests.swift","character":7,"line":6},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func testRelayValidationMatchesRuntimeProfiles() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 61 lines","location":{"file":"TeraTests\/TeraStateMigrationTests.swift","character":5,"line":7},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 622","location":{"file":"TeraTests\/TeraStateMigrationTests.swift","character":1,"line":622},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"private actor SupportingBackend: TeraRuntimeBackend {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 250 lines or less excluding comments and whitespace: currently spans 253 lines","location":{"file":"TeraTests\/TeraSupportingStoreTests.swift","character":9,"line":207},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 487","location":{"file":"TeraTests\/TeraSupportingStoreTests.swift","character":1,"line":487},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraRemoteQualificationUITests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 1493 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":7,"line":4},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func testLocalSocialFiveFlowScenario() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 73 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":3,"line":62},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" func testLocalSocialDeterministicPersonas() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 66 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":3,"line":174},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" func testRemoteBlossomUploadAndRecovery() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 54 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":3,"line":247},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" private func expectedAddFieldLabel(_ identifier: String, type: String) -> String {","violation":{"severity":"error","ruleIdentifier":"cyclomatic_complexity","reason":"Function should have complexity 10 or less; currently complexity is 18","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":11,"line":573},"ruleName":"Cyclomatic Complexity","ruleDescription":"Complexity of function bodies should be limited."}},{"text":" private func preparePhotoUpdate(_ app: XCUIApplication, marker: String) -> XCUIElement? {","violation":{"severity":"error","ruleIdentifier":"cyclomatic_complexity","reason":"Function should have complexity 10 or less; currently complexity is 11","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":11,"line":767},"ruleName":"Cyclomatic Complexity","ruleDescription":"Complexity of function bodies should be limited."}},{"text":" private func preparePhotoUpdate(_ app: XCUIApplication, marker: String) -> XCUIElement? {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 66 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":11,"line":767},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" static func environment(","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 67 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":10,"line":1736},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 2212","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":1,"line":2212},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}}]
-\ No newline at end of file
+[{"text":" init(","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Initializer body should span 50 lines or less excluding comments and whitespace: currently spans 52 lines","location":{"file":"Tera\/App\/TeraAppModel.swift","character":3,"line":21},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"actor TeraAddMediaCoordinator: TeraAddMediaHandling {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 250 lines or less excluding comments and whitespace: currently spans 330 lines","location":{"file":"Tera\/Runtime\/TeraAddMediaCoordinator.swift","character":1,"line":51},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"#endif","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 458","location":{"file":"Tera\/Runtime\/TeraAddMediaCoordinator.swift","character":1,"line":458},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"private final class TeraGeneratedRuntimeBackend: TeraRuntimeBackend, @unchecked Sendable {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 545 lines","location":{"file":"Tera\/Runtime\/TeraGeneratedRuntimeBackend.swift","character":15,"line":111},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1640","location":{"file":"Tera\/Runtime\/TeraGeneratedRuntimeBackend.swift","character":1,"line":1640},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"actor TeraRuntimeClient {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 787 lines","location":{"file":"Tera\/Runtime\/TeraRuntimeClient.swift","character":1,"line":291},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func changes(bufferCapacity: Int = 16) async throws -> AsyncStream<TeraRuntimeChange> {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 70 lines","location":{"file":"Tera\/Runtime\/TeraRuntimeClient.swift","character":3,"line":703},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1159","location":{"file":"Tera\/Runtime\/TeraRuntimeClient.swift","character":1,"line":1159},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1210","location":{"file":"Tera\/Runtime\/TeraRuntimeModels.swift","character":1,"line":1210},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraAddStore: ObservableObject {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 731 lines","location":{"file":"Tera\/State\/TeraAddStore.swift","character":7,"line":11},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func retract(_ card: TeraTodayCard) async {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 51 lines","location":{"file":"Tera\/State\/TeraAddStore.swift","character":3,"line":459},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 796","location":{"file":"Tera\/State\/TeraAddStore.swift","character":1,"line":796},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"actor TeraConfigurationStore {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 468 lines","location":{"file":"Tera\/State\/TeraConfigurationStore.swift","character":1,"line":78},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func load() throws -> TeraAppConfiguration {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 100 lines or less excluding comments and whitespace: currently spans 109 lines","location":{"file":"Tera\/State\/TeraConfigurationStore.swift","character":5,"line":148},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 759","location":{"file":"Tera\/State\/TeraConfigurationStore.swift","character":1,"line":759},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"actor TeraSessionStore {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 390 lines","location":{"file":"Tera\/State\/TeraSessionStore.swift","character":1,"line":39},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" private func start(acceptingReconfiguration: Bool) async -> TeraSessionPhase {","violation":{"severity":"error","ruleIdentifier":"cyclomatic_complexity","reason":"Function should have complexity 10 or less; currently complexity is 13","location":{"file":"Tera\/State\/TeraSessionStore.swift","character":11,"line":125},"ruleName":"Cyclomatic Complexity","ruleDescription":"Complexity of function bodies should be limited."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 452","location":{"file":"Tera\/State\/TeraSessionStore.swift","character":1,"line":452},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"struct TeraAddView: View {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Struct body should span 350 lines or less excluding comments and whitespace: currently spans 454 lines","location":{"file":"Tera\/Views\/TeraAddView.swift","character":1,"line":4},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 560","location":{"file":"Tera\/Views\/TeraAddView.swift","character":1,"line":560},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"struct TeraSettingsView: View {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Struct body should span 250 lines or less excluding comments and whitespace: currently spans 259 lines","location":{"file":"Tera\/Views\/TeraSupportingViews.swift","character":1,"line":365},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 629","location":{"file":"Tera\/Views\/TeraSupportingViews.swift","character":1,"line":629},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 474","location":{"file":"Tera\/Views\/TeraTodayView.swift","character":1,"line":474},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraAddStoreTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 665 lines","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":7,"line":6},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"private actor AddBackend: TeraRuntimeBackend {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 350 lines or less excluding comments and whitespace: currently spans 492 lines","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":9,"line":844},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" private func makeStatus(","violation":{"severity":"error","ruleIdentifier":"function_parameter_count","reason":"Function should have 5 parameters or less: it currently has 8","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":11,"line":1280},"ruleName":"Function Parameter Count","ruleDescription":"Number of function parameters should be low."}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 1431","location":{"file":"TeraTests\/TeraAddStoreTests.swift","character":1,"line":1431},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraRemoteQualificationTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 250 lines or less excluding comments and whitespace: currently spans 307 lines","location":{"file":"TeraTests\/TeraRemoteQualificationTests.swift","character":7,"line":7},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func testQualificationRejectsAmbiguousOrUnsafeHarnessValues() {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 94 lines","location":{"file":"TeraTests\/TeraRemoteQualificationTests.swift","character":3,"line":45},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" func testAuthorizationEvidenceIsRedactedEphemeralAndRelaunchCleaned() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 53 lines","location":{"file":"TeraTests\/TeraRemoteQualificationTests.swift","character":3,"line":212},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"final class TeraRuntimeClientTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 250 lines or less excluding comments and whitespace: currently spans 298 lines","location":{"file":"TeraTests\/TeraRuntimeClientTests.swift","character":7,"line":4},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"final class TeraStateMigrationTests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 580 lines","location":{"file":"TeraTests\/TeraStateMigrationTests.swift","character":7,"line":6},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func testRelayValidationMatchesRuntimeProfiles() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 61 lines","location":{"file":"TeraTests\/TeraStateMigrationTests.swift","character":5,"line":7},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 622","location":{"file":"TeraTests\/TeraStateMigrationTests.swift","character":1,"line":622},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"private actor SupportingBackend: TeraRuntimeBackend {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Actor body should span 250 lines or less excluding comments and whitespace: currently spans 253 lines","location":{"file":"TeraTests\/TeraSupportingStoreTests.swift","character":9,"line":207},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 400 lines or less: currently contains 487","location":{"file":"TeraTests\/TeraSupportingStoreTests.swift","character":1,"line":487},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}},{"text":"final class TeraRemoteQualificationUITests: XCTestCase {","violation":{"severity":"error","ruleIdentifier":"type_body_length","reason":"Class body should span 350 lines or less excluding comments and whitespace: currently spans 1493 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":7,"line":4},"ruleName":"Type Body Length","ruleDescription":"Type bodies should not span too many lines"}},{"text":" func testLocalSocialFiveFlowScenario() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 73 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":3,"line":62},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" func testLocalSocialDeterministicPersonas() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 66 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":3,"line":174},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" func testRemoteBlossomUploadAndRecovery() throws {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 54 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":3,"line":247},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" private func expectedAddFieldLabel(_ identifier: String, type: String) -> String {","violation":{"severity":"error","ruleIdentifier":"cyclomatic_complexity","reason":"Function should have complexity 10 or less; currently complexity is 18","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":11,"line":573},"ruleName":"Cyclomatic Complexity","ruleDescription":"Complexity of function bodies should be limited."}},{"text":" private func preparePhotoUpdate(_ app: XCUIApplication, marker: String) -> XCUIElement? {","violation":{"severity":"error","ruleIdentifier":"cyclomatic_complexity","reason":"Function should have complexity 10 or less; currently complexity is 11","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":11,"line":767},"ruleName":"Cyclomatic Complexity","ruleDescription":"Complexity of function bodies should be limited."}},{"text":" private func preparePhotoUpdate(_ app: XCUIApplication, marker: String) -> XCUIElement? {","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 66 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":11,"line":767},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":" static func environment(","violation":{"severity":"error","ruleIdentifier":"function_body_length","reason":"Function body should span 50 lines or less excluding comments and whitespace: currently spans 67 lines","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":10,"line":1736},"ruleName":"Function Body Length","ruleDescription":"Function bodies should not span too many lines"}},{"text":"}","violation":{"severity":"error","ruleIdentifier":"file_length","reason":"File should contain 1000 lines or less: currently contains 2212","location":{"file":"TeraUITests\/TeraRemoteQualificationUITests.swift","character":1,"line":2212},"ruleName":"File Length","ruleDescription":"Files should not span too many lines."}}]
+\ No newline at end of file