Skip to content

Eliminate all 68 Swift 6 forward-compat build warnings#684

Merged
FuJacob merged 1 commit into
mainfrom
fix-build-warnings
Jun 12, 2026
Merged

Eliminate all 68 Swift 6 forward-compat build warnings#684
FuJacob merged 1 commit into
mainfrom
fix-build-warnings

Conversation

@FuJacob

@FuJacob FuJacob commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Summary

A clean build of latest main emits 68 project warnings, and every one is the same class of problem: the target's SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor makes pure value types, statics, and Character extensions main-actor-isolated, and nonisolated contexts reference them (the llama runtime task logging through CotabbyLogger, sort comparators, timer closures, conformances used from plain XCTest classes). Each carries the compiler's "this is an error in the Swift 6 language mode" rider, so the whole set is a queued-up future build break.

The fix follows the pattern the codebase already established for CaretGeometryQuality and ObservedContentEdges: annotate pure helper enums (CotabbyLogger, TypoGate's decision, DecodeStopPolicy, OCRTextHygiene, the emoji catalog/popularity/synonym tables, and friends), value-type models (EmojiEntry, HFModelSearchResult, DisabledApplicationRule, FocusedInputIdentity, the trigger state machines), and the lock-guarded @unchecked Sendable log writers as nonisolated. None of these have main-actor state; isolation on them was an accident of the build setting.

The two notification-callback warnings (PowerSourceMonitor, KeyboardInputSourceMonitor) are real isolation decisions rather than annotations: both observers register with queue: .main, so the callbacks now enter the actor via MainActor.assumeIsolated, which documents the main-queue delivery contract as a checkable assertion instead of paying a re-dispatch hop.

Validation

rm -rf build/DerivedData
xcodebuild build-for-testing -project Cotabby.xcodeproj -scheme Cotabby -destination 'platform=macOS' \
  CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO
# Before: 68 project warnings on a clean build
# After:  0 project warnings on a clean build (the two appintentsmetadataprocessor
#         notices are toolchain noise about a framework the app does not link)

xcodebuild test ... -skip-testing:CotabbyTests/FoundationModelDriftEvalTests \
  CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO
# ** TEST SUCCEEDED ** 1436 tests, 0 failures, 0 crashes

swiftlint lint --quiet
# 0 findings on the files this PR touches

Linked issues

None filed; this came out of a "build latest main and fix every warning" sweep.

Risk / rollout notes

  • nonisolated on pure value types and statics only widens where they may be used; no call site changes behavior. The diff is +48/-41 across 30 files, almost entirely one-word annotations.
  • The two MainActor.assumeIsolated sites trap if the delivery queue ever stops being .main. That is intentional: a queue change there should be loud, not a silent data race on actor state.
  • FileLogWriter/LLMIOFileWriter becoming explicitly nonisolated matches what their @unchecked Sendable + internal NSLock already promised; their nonisolated deinit (from Raise deterministic-core test coverage to 95%+ (Models 97.1%, Support 96.0%) #678) is now redundant but harmless and kept for clarity.

Greptile Summary

This PR eliminates all 68 Swift 6 forward-compatibility build warnings generated by the SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor project setting, which was accidentally isolating pure value types, static-only namespaces, and Character extensions to the main actor. The fixes follow the two patterns already established in the codebase.

  • nonisolated on value types and static-only enums (26 types across 28 files): pure structs and caseless enums (CotabbyLogger, DecodeStopPolicy, OCRTextHygiene, state-machine types, emoji catalogs, log writers, etc.) that carry no main-actor state are explicitly annotated, widening where they may be used without changing any behavior.
  • MainActor.assumeIsolated in main-queue callbacks (PowerSourceMonitor, KeyboardInputSourceMonitor): instead of relying on the implicit isolation that the build setting provided, both NSWorkspace/DistributedNotificationCenter observers registered with queue: .main now explicitly assert main-actor execution, converting a silent assumption into a checkable runtime precondition.

Confidence Score: 4/5

Safe to merge; all changes are additive isolation annotations with no behavioral impact on app logic, and the two MainActor.assumeIsolated sites correctly document an existing queue contract.

The diff is almost entirely one-word nonisolated additions to pure value types and static namespaces that genuinely have no main-actor state. The only non-trivial changes are the two MainActor.assumeIsolated wraps in notification callbacks that were already delivered on the main queue. One minor inconsistency: MacroTriggerInput and EmojiTriggerInput are left unannotated in files where all their sibling enums received nonisolated; this doesn't cause any current warning but leaves those two types subject to accidental re-isolation if members are added later.

MacroTriggerStateMachine.swift and EmojiPickerModels.swift — both leave their respective TriggerInput enum unannotated while annotating every other state-machine type in the same file.

Important Files Changed

Filename Overview
Cotabby/Services/Power/PowerSourceMonitor.swift Adds MainActor.assumeIsolated to the NSWorkspace wake-observer closure (already present for the IOKit run-loop callback); correct because queue:.main delivers on main thread
Cotabby/Services/Input/KeyboardInputSourceMonitor.swift Wraps the DistributedNotificationCenter callback (queue:.main) in MainActor.assumeIsolated; mirrors the PowerSourceMonitor pattern correctly
Cotabby/Support/FileLogHandler.swift Adds nonisolated to FileLogWriter class; correct because the class is @unchecked Sendable and already has NSLock-guarded state, matching what nonisolated promises
Cotabby/Support/LLMIOFileHandler.swift Adds nonisolated to LLMIOFileWriter class; same NSLock-backed pattern as FileLogWriter; annotation matches the implementation contract
Cotabby/Support/SuggestionSessionReconciler.swift Two Character extensions (one internal, one private) get nonisolated; members are pure computed properties on a value type with no actor-state concerns
Cotabby/Support/SentenceBoundaryClassifier.swift Adds nonisolated to the enum and to a private Character extension; pure static/instance logic with no shared mutable state
Cotabby/Support/MacroTriggerStateMachine.swift Annotates MacroTriggerStateMachine, MacroTriggerAction, MacroTriggerState, and MacroQueryGrammar as nonisolated but leaves MacroTriggerInput unannotated—minor inconsistency within the file
Cotabby/Models/EmojiPickerModels.swift Annotates EmojiEntry, EmojiMatch, EmojiSelectionMove, EmojiCommitMode, EmojiTriggerAction, EmojiTriggerState as nonisolated but leaves EmojiTriggerInput unannotated—same pattern as MacroTriggerInput
Cotabby/Support/CotabbyDebugOptions.swift Adds nonisolated to CotabbyDebugOptions and CotabbyLogger enums; both are static-only namespaces with no main-actor-specific state
Cotabby/Models/SystemMetricsStore.swift Makes the static constant defaultInterval nonisolated; a simple TimeInterval literal that doesn't belong on the main actor

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor] -->|Before PR| B[All types implicitly @MainActor]
    B --> C{Type has no actor state?}
    C -->|Yes - pure enum/struct| D[Warning: nonisolated context references @MainActor member - error in Swift 6 mode]
    C -->|No - @MainActor class| E[OK]
    A -->|After PR| F{Classification}
    F -->|Pure value types / static namespaces| G[nonisolated annotation]
    F -->|Lock-guarded writers| H[nonisolated final class]
    F -->|Character extensions| I[nonisolated extension Character]
    F -->|Main-queue callbacks| J[MainActor.assumeIsolated]
    G --> K[0 warnings, usable from any context]
    H --> K
    I --> K
    J --> K
Loading

Comments Outside Diff (1)

  1. Cotabby/Support/MacroTriggerStateMachine.swift, line 115-123 (link)

    P2 Unannotated input enum amid nonisolated siblings

    MacroTriggerInput is the only enum in this file that wasn't given nonisolated; its siblings MacroTriggerAction, MacroTriggerState, and MacroQueryGrammar all were. The same pattern appears in EmojiPickerModels.swift where EmojiTriggerInput is left unannotated while every other state-machine enum in the file gets the annotation.

    The current 0-warning result makes sense: both *TriggerInput enums contain only cases with no methods or stored properties, so the compiler has nothing main-actor-isolated to warn about on them. If either enum gains a static helper (e.g. a factory or description property) in a future change, it will silently re-inherit @MainActor from the build setting and the new member will be isolated while its callers in the now-nonisolated state machines are not. Adding nonisolated here (and on EmojiTriggerInput) would make the isolation intent explicit and keep the file internally consistent.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Codex Fix in Claude Code

Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(concurrency): eliminate all 68 Swift..." | Re-trigger Greptile

A clean build of main emitted 68 warnings, every one the same root cause:
the target's default MainActor isolation makes pure value types, statics,
and Character extensions actor-isolated, and nonisolated contexts (the
llama runtime task, sort comparators, timer closures, plain XCTest classes)
reference them. Each is an error in the Swift 6 language mode, so they are
all future build breaks.

Fixes follow the codebase's existing pattern (CaretGeometryQuality,
ObservedContentEdges): annotate pure helper enums, value-type models, and
the lock-guarded log writers nonisolated. The two notification-callback
warnings are real isolation decisions, resolved with MainActor.assumeIsolated
since both observers register on the main queue, so entering the actor is an
assertion rather than a hop.

Clean rebuild from scratch now emits zero project warnings (the two
appintentsmetadataprocessor notices are toolchain noise about a framework
the app does not use). Full suite: 1436 tests, 0 failures.
@FuJacob FuJacob merged commit 292c399 into main Jun 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant