Specify a webhook-driven task for OpenClaw
Describe event inputs, validation, deduplication, and intended actions before integration work.
Use when needing synchronous actor access in tests, legacy delegate callbacks, or performance-critical code. Covers MainActor.assumeIsolated, @preconcurrency protocol conformances, crash behavior, Task vs assumeIsolated.
---
name: axiom-assume-isolated
description: Use when needing synchronous actor access in tests, legacy delegate callbacks, or performance-critical code. Covers MainActor.assumeIsolated, @preconcurrency protocol conformances, crash behavior, Task vs assumeIsolated.
license: MIT
metadata:
version: "1.0.0"
---
# assumeIsolated — Synchronous Actor Access
Synchronously access actor-isolated state when you **know** you're already on the correct isolation domain.
## When to Use
✅ **Use when:**
- Testing MainActor code synchronously (avoiding Task overhead)
- Legacy delegate callbacks documented to run on main thread
- Performance-critical code avoiding async hop overhead
- Protocol conformances where callbacks are guaranteed on specific actor
❌ **Don't use when:**
- Uncertain about current isolation (use `await` instead)
- Already in async context (you have isolation)
- Cross-actor calls needed (use async)
- Callback origin is unknown or untrusted
## API Reference
### MainActor.assumeIsolated
```swift
static func assumeIsolated<T>(
_ operation: @MainActor () throws -> T,
file: StaticString = #fileID,
line: UInt = #line
) rethrows -> T where T: Sendable
```
**Behavior**: Executes synchronously. **Crashes** if not on MainActor's serial executor.
### Custom Actor assumeIsolated
```swift
func assumeIsolated<T>(
_ operation: (isolated Self) throws -> T,
file: StaticString = #fileID,
line: UInt = #line
) rethrows -> T where T: Sendable
```
## Task vs assumeIsolated
| Aspect | `Task { @MainActor in }` | `MainActor.assumeIsolated` |
|--------|--------------------------|---------------------------|
| Timing | Deferred (next run loop) | Synchronous (inline) |
| Async support | Yes (can await) | No (sync only) |
| Context | From any context | Must be sync function |
| Failure mode | Runs anyway | **Crashes** if wrong isolation |
| Use case | Start async work | Verify + access isolated state |
## Patterns
### Pattern 1: Testing MainActor Code
```swift
@Test func viewModelUpdates() {
MainActor.assumeIsolated {
let vm = ViewModel()
vm.update()
#expect(vm.state == .updated)
}
}
```
### Pattern 2: Legacy Delegate Callbacks
From WWDC 2024-10169 — When documentation guarantees main thread delivery:
```swift
@MainActor
class LocationDelegate: NSObject, CLLocationManagerDelegate {
var location: CLLocation?
// CLLocationManager created on main thread delivers callbacks on main thread
nonisolated func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
MainActor.assumeIsolated {
self.location = locations.last
}
}
}
```
### Pattern 3: @preconcurrency Shorthand
`@preconcurrency` is equivalent shorthand — wraps in `assumeIsolated` automatically:
```swift
// ❌ Manual approach (verbose)
extension MyClass: SomeDelegate {
nonisolated func callback() {
MainActor.assumeIsolated {
self.updateUI()
}
}
}
// ✅ Using @preconcurrency (equivalent, cleaner)
extension MyClass: @preconcurrency SomeDelegate {
func callback() {
self.updateUI() // Compiler wraps in assumeIsolated
}
}
```
**When protocol adds isolation**: `@preconcurrency` becomes unnecessary and compiler warns.
### Pattern 4: Thread Check Before assumeIsolated
When caller context is unknown (e.g., library code):
```swift
func getView() -> UIView {
if Thread.isMainThread {
return createHostingViewOnMain()
} else {
return DispatchQueue.main.sync {
createHostingViewOnMain()
}
}
}
private func createHostingViewOnMain() -> UIView {
MainActor.assumeIsolated {
let hosting = UIHostingController(rootView: MyView())
return hosting.view
}
}
```
### Pattern 5: Custom Actor Access
```swift
actor DataStore {
var cache: [String: Data] = [:]
nonisolated func synchronousRead(key: String) -> Data? {
// Only safe if called from DataStore's executor
assumeIsolated { isolated in
isolated.cache[key]
}
}
}
```
## Common Mistakes
### Mistake 1: Silencing Compiler Errors
```swift
// ❌ DANGEROUS: Using assumeIsolated to silence warnings
func unknownContext() {
MainActor.assumeIsolated {
updateUI() // Crashes if not actually on main actor!
}
}
// ✅ When uncertain, use proper async
func unknownContext() async {
await MainActor.run {
updateUI()
}
}
```
### Mistake 2: Assuming GCD Main Queue == MainActor
They're **usually** the same, but not guaranteed. Check documentation or use async.
### Mistake 3: Using in Async Context
```swift
// ❌ Unnecessary — you already have isolation
@MainActor
func updateState() async {
MainActor.assumeIsolated { // Pointless
self.state = .ready
}
}
// ✅ Direct access
@MainActor
func updateState() async {
self.state = .ready
}
```
## When @preconcurrency Becomes Unnecessary
If the protocol later adds MainActor isolation:
```swift
// Library update:
@MainActor
protocol CaffeineThresholdDelegate: AnyObject {
func caffeineLevel(at level: Double)
}
// Your code — @preconcurrency now warns:
// "@preconcurrency attribute on conformance has no effect"
extension Recaffeinater: CaffeineThresholdDelegate {
func caffeineLevel(at level: Double) {
// Direct access, no wrapper needed
}
}
```
## Crash Behavior
Per Apple documentation:
> "If the current context is not running on the actor's serial executor... this method will crash with a fatal error."
**Trapping is intentional**: Better to crash than corrupt user data with a race condition.
## Resources
**WWDC**: 2024-10169
**Docs**: /swift/mainactor/assumeisolated, /swift/actor/assumeisolated
**Skills**: axiom-swift-concurrency
---
name: axiom-assume-isolated
description: Use when needing synchronous actor access in tests, legacy delegate callbacks, or performance-critical code. Covers MainActor.assumeIsolated, @preconcurrency protocol conformances, crash behavior, Task vs assumeIsolated.
license: MIT
metadata:
version: "1.0.0"
---
# assumeIsolated — Synchronous Actor Access
Synchronously access actor-isolated state when you **know** you're already on the correct isolation domain.
## When to Use
✅ **Use when:**
- Testing MainActor code synchronously (avoiding Task overhead)
- Legacy delegate callbacks documented to run on main thread
- Performance-critical code avoiding async hop overhead
- Protocol conformances where callbacks are guaranteed on specific actor
❌ **Don't use when:**
- Uncertain about current isolation (use `await` instead)
- Already in async context (you have isolation)
- Cross-actor calls needed (use async)
- Callback origin is unknown or untrusted
## API Reference
### MainActor.assumeIsolated
```swift
static func assumeIsolated<T>(
_ operation: @MainActor () throws -> T,
file: StaticString = #fileID,
line: UInt = #line
) rethrows -> T where T: Sendable
```
**Behavior**: Executes synchronously. **Crashes** if not on MainActor's serial executor.
### Custom Actor assumeIsolated
```swift
func assumeIsolated<T>(
_ operation: (isolated Self) throws -> T,
file: StaticString = #fileID,
line: UInt = #line
) rethrows -> T where T: Sendable
```
## Task vs assumeIsolated
| Aspect | `Task { @MainActor in }` | `MainActor.assumeIsolated` |
|--------|--------------------------|---------------------------|
| Timing | Deferred (next run loop) | Synchronous (inline) |
| Async support | Yes (can await) | No (sync only) |
| Context | From any context | Must be sync function |
| Failure mode | Runs anyway | **Crashes** if wrong isolation |
| Use case | Start async work | Verify + access isolated state |
## Patterns
### Pattern 1: Testing MainActor Code
```swift
@Test func viewModelUpdates() {
MainActor.assumeIsolated {
let vm = ViewModel()
vm.update()
#expect(vm.state == .updated)
}
}
```
### Pattern 2: Legacy Delegate Callbacks
From WWDC 2024-10169 — When documentation guarantees main thread delivery:
```swift
@MainActor
class LocationDelegate: NSObject, CLLocationManagerDelegate {
var location: CLLocation?
// CLLocationManager created on main thread delivers callbacks on main thread
nonisolated func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
MainActor.assumeIsolated {
self.location = locations.last
}
}
}
```
### Pattern 3: @preconcurrency Shorthand
`@preconcurrency` is equivalent shorthand — wraps in `assumeIsolated` automatically:
```swift
// ❌ Manual approach (verbose)
extension MyClass: SomeDelegate {
nonisolated func callback() {
MainActor.assumeIsolated {
self.updateUI()
}
}
}
// ✅ Using @preconcurrency (equivalent, cleaner)
extension MyClass: @preconcurrency SomeDelegate {
func callback() {
self.updateUI() // Compiler wraps in assumeIsolated
}
}
```
**When protocol adds isolation**: `@preconcurrency` becomes unnecessary and compiler warns.
### Pattern 4: Thread Check Before assumeIsolated
When caller context is unknown (e.g., library code):
```swift
func getView() -> UIView {
if Thread.isMainThread {
return createHostingViewOnMain()
} else {
return DispatchQueue.main.sync {
createHostingViewOnMain()
}
}
}
private func createHostingViewOnMain() -> UIView {
MainActor.assumeIsolated {
let hosting = UIHostingController(rootView: MyView())
return hosting.view
}
}
```
### Pattern 5: Custom Actor Access
```swift
actor DataStore {
var cache: [String: Data] = [:]
nonisolated func synchronousRead(key: String) -> Data? {
// Only safe if called from DataStore's executor
assumeIsolated { isolated in
isolated.cache[key]
}
}
}
```
## Common Mistakes
### Mistake 1: Silencing Compiler Errors
```swift
// ❌ DANGEROUS: Using assumeIsolated to silence warnings
func unknownContext() {
MainActor.assumeIsolated {
updateUI() // Crashes if not actually on main actor!
}
}
// ✅ When uncertain, use proper async
func unknownContext() async {
await MainActor.run {
updateUI()
}
}
```
### Mistake 2: Assuming GCD Main Queue == MainActor
They're **usually** the same, but not guaranteed. Check documentation or use async.
### Mistake 3: Using in Async Context
```swift
// ❌ Unnecessary — you already have isolation
@MainActor
func updateState() async {
MainActor.assumeIsolated { // Pointless
self.state = .ready
}
}
// ✅ Direct access
@MainActor
func updateState() async {
self.state = .ready
}
```
## When @preconcurrency Becomes Unnecessary
If the protocol later adds MainActor isolation:
```swift
// Library update:
@MainActor
protocol CaffeineThresholdDelegate: AnyObject {
func caffeineLevel(at level: Double)
}
// Your code — @preconcurrency now warns:
// "@preconcurrency attribute on conformance has no effect"
extension Recaffeinater: CaffeineThresholdDelegate {
func caffeineLevel(at level: Double) {
// Direct access, no wrapper needed
}
}
```
## Crash Behavior
Per Apple documentation:
> "If the current context is not running on the actor's serial executor... this method will crash with a fatal error."
**Trapping is intentional**: Better to crash than corrupt user data with a race condition.
## Resources
**WWDC**: 2024-10169
**Docs**: /swift/mainactor/assumeisolated, /swift/actor/assumeisolated
**Skills**: axiom-swift-concurrency
Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.