Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions Cotabby.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cotabby/Services/Focus/DeepGeometryWalkThrottle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ final class DeepGeometryWalkThrottle {
cachedResult = result
return result
}

// Mirrors FieldStyleCache: keep deallocation off the back-deployment main-actor executor
// shim, whose StopLookupScope double-frees on macOS 26. Only test-scoped resolvers ever
// deallocate this type.
nonisolated deinit {}
}
6 changes: 6 additions & 0 deletions Cotabby/Services/Focus/FieldStyleCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,10 @@ final class FieldStyleCache {
style = resolved
return resolved
}

// Stored state is plain value types, safe to release anywhere. The nonisolated deinit keeps
// deallocation off the back-deployment main-actor executor shim, whose StopLookupScope
// double-frees on macOS 26 (see InputSuppressionController). Production's single long-lived
// instance never deallocates; test-scoped resolvers do.
nonisolated deinit {}
}
5 changes: 5 additions & 0 deletions Cotabby/Services/Suggestion/SuggestionDebugLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ final class SuggestionDebugLogger {
self.colorizedOutput = colorizedOutput ?? shouldUseColor
}

// All stored state is thread-safe to release (a Bool and an optional String). The nonisolated
// deinit prevents Swift from scheduling the teardown through the back-deployment main-actor
// executor shim, which double-frees in app-hosted tests (see InputSuppressionController).
nonisolated deinit {}

/// Emits only the model-boundary artifacts that are useful for debugging suggestion quality.
///
/// Lifecycle stages such as debounce, acceptance, and visual-context session dedup still update
Expand Down
13 changes: 11 additions & 2 deletions Cotabby/Support/FileLogHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,21 @@ final class FileLogWriter: @unchecked Sendable {
private var handle: FileHandle?
private var currentByteOffset: UInt64 = 0

init(sizeCapBytes: UInt64? = nil) {
/// `fileURL` overrides the default `~/Library/Logs/<bundle>/cotabby.jsonl` destination. Tests
/// inject a temp-directory URL so rotation and write behavior can be exercised against a real
/// file handle without touching the user's live logs.
init(sizeCapBytes: UInt64? = nil, fileURL: URL? = nil) {
self.sizeCapBytesOverride = sizeCapBytes
self.logFileURL = Self.makeLogFileURL()
self.logFileURL = fileURL ?? Self.makeLogFileURL()
openHandle()
}

// The target's default MainActor isolation applies to this unannotated class, so without this
// a deallocation routes through the back-deployment main-actor executor shim, which
// double-frees in its StopLookupScope on macOS 26 (see InputSuppressionController). The shared
// singleton never deallocates in production; tests deallocate per-case writers constantly.
nonisolated deinit {}

private let sizeCapBytesOverride: UInt64?
private var effectiveCap: UInt64 { sizeCapBytesOverride ?? sizeCapBytes }

Expand Down
12 changes: 10 additions & 2 deletions Cotabby/Support/LLMIOFileHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,20 @@ final class LLMIOFileWriter: @unchecked Sendable {
private var handle: FileHandle?
private var currentByteOffset: UInt64 = 0

init(sizeCapBytes: UInt64? = nil) {
/// `fileURL` overrides the default `~/Library/Logs/<bundle>/llm-io.jsonl` destination. Tests
/// inject a temp-directory URL so rotation and write behavior can be exercised against a real
/// file handle without touching the user's live logs.
init(sizeCapBytes: UInt64? = nil, fileURL: URL? = nil) {
self.sizeCapBytesOverride = sizeCapBytes
self.logFileURL = Self.makeLogFileURL()
self.logFileURL = fileURL ?? Self.makeLogFileURL()
openHandle()
}

// Mirrors FileLogWriter: the target's default MainActor isolation would otherwise route
// deallocation through the back-deployment executor shim, which double-frees in its
// StopLookupScope on macOS 26. Production only ever uses the never-deallocated singleton.
nonisolated deinit {}

private let sizeCapBytesOverride: UInt64?
private var effectiveCap: UInt64 { sizeCapBytesOverride ?? sizeCapBytes }

Expand Down
Loading