swift-concurrency — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited swift-concurrency (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Enterprise-grade skill for Swift Concurrency crash prevention, strict concurrency migration, and Swift 6.2 readiness. Opinionated: prescribes structured concurrency over unstructured, actors for shared mutable state, @MainActor for UI isolation, Mutex for synchronous critical sections, compile-time isolation over runtime hops, and withCheckedContinuation over withUnsafeContinuation. Every rule in this skill has broken a real production app.
Application Layer -> Structured concurrency: TaskGroup, async let, .task modifier
Actor Layer -> actor for shared mutable state, @MainActor for UI state
Async/Await Layer -> Cooperative, non-blocking async functions on the cooperative pool
Sendable Layer -> Compile-time data-race safety at isolation boundary crossings
Compiler Layer -> SWIFT_STRICT_CONCURRENCY=complete, Swift 6 language mode, TSanDoes this type own mutable state shared across concurrency domains?
+-- YES -> Does it need to update UI?
| +-- YES -> @MainActor class/struct
| +-- NO -> Is the state complex or involves await points?
| +-- YES -> actor
| +-- NO -> Mutex<State> (Swift 6+, iOS 18+) or NSLock wrapper
+-- NO -> Is it a value type with only Sendable stored properties?
+-- YES -> Implicitly Sendable (internal types only; public needs explicit conformance)
+-- NO -> Can it be redesigned as a value type?
+-- YES -> Refactor to struct/enum
+-- NO -> Keep non-Sendable, contain within single isolation domainHow many concurrent operations?
+-- Known at compile time (2-5) -> async let (simplest, heterogeneous return types OK)
+-- Dynamic count (N items) -> TaskGroup
| +-- Need results?
| | +-- YES -> withThrowingTaskGroup (iterate with for await)
| | +-- NO -> withDiscardingTaskGroup (no result accumulation leak)
| +-- Unbounded input? -> Throttle: limit addTask to activeProcessorCount
+-- Fire-and-forget from sync context?
+-- Need caller's isolation -> Task { } (inherits actor context)
+-- Need background, no isolation -> Task.detached { }
WARNING: detached strips priority, task-locals, cancellation propagation. Rarely correct.Does the API block the current thread?
+-- YES (semaphore.wait, group.wait, Thread.sleep, sync file I/O, NSLock in long section)
| -> NEVER in async context. Deadlocks cooperative pool (capped at CPU core count).
| -> Wrap in DispatchQueue + withCheckedContinuation to move off cooperative pool.
+-- NO -> Is the work CPU-bound and takes >1ms?
+-- YES -> Use @concurrent func (Swift 6.2+) or nonisolated func on dedicated queue
+-- NO -> Safe in async contextWhen: First encounter with a codebase, or preparing for Swift 6 migration.
SWIFT_STRICT_CONCURRENCY setting (references/compiler-flags-ci.md)references/crash-patterns.md)@unchecked Sendable usage (references/sendable-transfer.md)references/actor-isolation.md)finish(), infinite sequences, no backpressure (references/asyncstream-memory.md)references/security-concurrency.md)refactoring/ directory with severity-ranked findings (references/refactoring-workflow.md)When: Enabling SWIFT_STRICT_CONCURRENCY=complete or Swift 6 mode.
The migration is a THREE-STEP progression, not a binary flip. Do not jump directly to complete or to Swift 6 language mode — you will drown in errors. The canonical progression is:
| Step | Setting | What it does |
|---|---|---|
| 1. Targeted | SWIFT_STRICT_CONCURRENCY=targeted | Surfaces warnings only in code explicitly marked Sendable or in @preconcurrency boundaries. Low-noise entry point. Ship this first, fix the surfaced warnings, then advance. |
| 2. Complete | SWIFT_STRICT_CONCURRENCY=complete | Enables all concurrency checks as warnings. Work through each module until the warning count is zero. Still compiles and ships while you migrate. |
| 3. Swift 6 language mode | SWIFT_VERSION=6.0 | Promotes all concurrency checks from warnings to errors. Only switch here AFTER the module is clean under complete. This is the final gate. |
Most teams spend weeks in step 2 before advancing to step 3. Enabling step 3 prematurely produces hundreds of errors with no actionable incremental path.
references/migration-strategy.md)references/migration-strategy.md)static var → actor, Mutex, or let (references/actor-isolation.md)sending where applicable (references/sendable-transfer.md)references/swift-6-2-changes.md)references/compiler-flags-ci.md)LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 (references/testing-debugging.md)When: Building a new feature from scratch with Swift Concurrency.
references/sendable-transfer.md)references/advanced-patterns.md)references/testing-debugging.md)withMainSerialExecutor (references/testing-debugging.md)When: Crash report points to continuation, actor, AsyncStream, or TaskGroup.
references/crash-patterns.md)references/actor-isolation.md)references/security-concurrency.md)references/testing-debugging.md)LIBDISPATCH_COOPERATIVE_POOL_STRICT=1<critical_rules> Whether reviewing, generating, or refactoring concurrent code, every output must be data-race-free, deadlock-free, and production-ready under Swift 6 strict concurrency. ALWAYS:
semaphore.wait(), group.wait(), Thread.sleep(), synchronous file I/O in any async contextwithCheckedThrowingContinuationawait -- actors are reentrant at suspension pointswithDiscardingTaskGroup for fire-and-forget child tasks -- prevents result accumulation memory leaksdeinit is not sufficient because an AsyncStream can outlive or be dropped independently of its owner; onTermination fires when the stream is finished or its task is cancelled, which is exactly when cleanup must run. Always pair every addObserver/delegate = self/registration call inside the stream setup with a symmetric removal in onTermination. Also call continuation.finish() from inside onTermination when your stream is an infinite observer patternSendable conformance -- no automatic inference across module boundariesProcessInfo.processInfo.activeProcessorCount@MainActor annotation for UI isolation, not MainActor.run {}CancellationError silently -- never surface "cancelled" to usersClock protocol for time-dependent code -- never hardcode Task.sleepnonisolated async now inherits caller's actor; use @concurrent for explicit background<thought> analyzing isolation domains, Sendable conformance, and potential reentrancy</critical_rules>
<fallback_strategies> When fixing concurrency issues, you may encounter cascading compiler errors. If you fail to fix the same issue twice, break the loop:
@preconcurrency import for the offending module and log a migration task in refactoring/discovered.md. Why: @preconcurrency suppresses Sendable checking at module boundaries while preserving local safety — it's the sanctioned escape hatch, unlike @unchecked Sendable which bypasses all checking.@MainActor to a class cascades into dozens of async call-site errors. Start by marking individual methods @MainActor instead of the whole class. Why: class-level isolation propagates to all methods, forcing every call site to be async — method-level isolation limits the blast radius.addTask, release inside the task. Limit to ProcessInfo.processInfo.activeProcessorCount * 2. Why: each child task retains its result until the group iterates — unbounded tasks accumulate results in memory.</fallback_strategies>
Before finalizing generated or refactored concurrent code, verify ALL:
[] No cooperative pool blocking -- no semaphore.wait, Thread.sleep, sync I/O in any async function
[] No continuation leaks -- every withChecked*Continuation resumes on every path (success, failure, cancellation)
[] No unchecked Sendable -- every @unchecked Sendable has documented synchronization (Mutex, NSLock, or actor)
[] Actor reentrancy -- state re-checked after every await inside actors
[] AsyncStream cleanup -- finish() called in onTermination, no infinite sequence without cancellation
[] TaskGroup bounded -- child task count limited for unbounded input
[] MainActor correct -- UI state @MainActor-isolated, no MainActor.run anti-pattern
[] Swift 6.2 ready -- nonisolated async caller-isolation understood, @concurrent used where needed
[] Cancellation handled -- withTaskCancellationHandler for long-running ops, CancellationError caught silently
[] Tests deterministic -- Clock injected, withMainSerialExecutor used, no flaky timing dependencies
[] Compiler flags -- SWIFT_STRICT_CONCURRENCY=complete set, TSan enabled in CIStart here for most tasks:crash-patterns.md,actor-isolation.md,sendable-transfer.md. Then consult the specific reference based on your workflow.
| Reference | When to Read |
|---|---|
references/rules.md | Do's and Don'ts quick reference: priority rules and critical anti-patterns |
references/crash-patterns.md | Production crash patterns: continuation misuse, cooperative pool deadlocks, TaskGroup OOM, watchdog kills, release-only crashes |
references/actor-isolation.md | Task.init inherits isolation, compile-time isolation, nonisolated deinit, isolated deinit (Swift 6.1+), split isolation, assumeIsolated, actors as advanced tools |
references/sendable-transfer.md | sending keyword, region-based isolation, @unchecked Sendable risks, public type inference, @preconcurrency scope |
references/asyncstream-memory.md | Infinite sequence leaks, continuation.finish(), backpressure policies, withDiscardingTaskGroup, Task.detached stripping |
references/swift-6-2-changes.md | Approachable Concurrency: caller isolation, @concurrent, MainActor default, @preconcurrency runtime crashes |
references/migration-strategy.md | Bottom-up module migration, third-party SDK blockers, global singleton conversion, actor hopping overhead |
references/security-concurrency.md | Token refresh serialization, TOCTOU at await, Keychain serialization, sensitive data, actor double-spend |
references/compiler-flags-ci.md | SWIFT_STRICT_CONCURRENCY, SPM per-target flags, Swift 6.2 feature flags, SwiftLint rules, CI pipeline |
references/advanced-patterns.md | Mutex vs actors, async let vs TaskGroup, isolated parameters, withTaskCancellationHandler, .task modifier, task-locals |
references/diagnostics-fix-mapping.md | Compiler error → fix mapping: "Sending risks data races", "non-sendable capture", "static property not safe", isolation errors |
references/cancellation-patterns.md | Cooperative cancellation, withTaskCancellationHandler, CancellationError handling, timeout patterns, SwiftUI .task |
references/memory-retain-cycles.md | Task retain cycles, weak self patterns, async sequence retention, isolated deinit, testing for leaks |
references/core-data-concurrency.md | NSManagedObject not Sendable, DAO pattern, NSManagedObjectID, actor-isolated context, @MainActor conflicts |
references/testing-async.md | Swift Testing async, confirmation(), Clock injection, withMainSerialExecutor, TSan, deterministic tests |
references/testing-debugging.md | withMainSerialExecutor, Clock injection, TSan, Instruments, deterministic tests, timeouts |
references/refactoring-workflow.md | refactoring/ directory protocol, per-feature plans, severity ordering, PR sizing, verification checklist |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.