swift-patterns
| Rule | Rationale | |------|-----------| | Always declare as `let` first | If the compiler accepts it, it is immutable — keep it | | Change to `var` only when compiler requires it | The compiler is the source of truth, not habit | | Never pre-emptively declare `var` in case it
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
| Rule | Rationale | |------|-----------| | Always declare as `let` first | If the compiler accepts it, it is immutable — keep it | | Change to `var` only when compiler requires it | The compiler is the source of truth, not habit | | Never pre-emptively declare `var` in case it
Agent definition
swift-patterns.mdSwift Patterns
Immutability
`let` vs. `var` Rules
| Rule | Rationale | |------|-----------| | Always declare as `let` first | If the compiler accepts it, it is immutable — keep it | | Change to `var` only when compiler requires it | The compiler is the source of truth, not habit | | Never pre-emptively declare `var` in case it changes | That change may never come; premature mutability creates data-race surface area | | Use `mutating func` in structs for controlled mutation | Keeps value semantics; each mutation is explicit at call sites |
// Wrong — var when let suffices
var name = "Alice"
print(name) // never reassigned
// Correct
let name = "Alice"
print(name)
`struct` vs. `class` Decision Matrix
| Use `struct` when... | Use `class` when... | |----------------------|---------------------| | Data is copied between contexts (DTO, model, config) | Identity matters — two references must point to the same object | | Thread-safety via value semantics is desired | Objective-C interoperability requires `NSObject` subclassing | | No subclassing is needed | Reference sharing is an intentional design decision (e.g., shared cache) | | All stored properties are value types or `Sendable` | Lifecycle management via `deinit` is required | | SwiftUI view models that do not need `ObservableObject` | `ObservableObject` / `@Published` Combine integration |
// Prefer struct for DTOs
struct UserProfile: Sendable {
let id: UUID
let displayName: String
let email: String
}
// class only when identity semantics are needed
final class ImageCache {
static let shared = ImageCache() // single shared instance
private var storage: [URL: UIImage] = [:]
private init() {}
}---
Concurrency
Core Principles
Swift 6 strict concurrency treats data races as compile-time errors. Every type crossing actor isolation boundaries must be `Sendable`.
Actor Isolation
// Use actors for shared mutable state — not DispatchQueue or locks
actor DownloadManager {
private var activeTasks: [URL: Task<Data, Error>] = [:]
func fetch(_ url: URL) async throws -> Data {
if let existing = activeTasks[url] {
return try await existing.value
}
let task = Task { try await URLSession.shared.data(from: url).0 }
activeTasks[url] = task
defer { activeTasks.removeValue(forKey: url) }
return try await task.value
}
}Sendable Requirements
- Value types (`struct`, `enum`) that contain only `Sendable` properties are automatically `Sendable`
- Add `@unchecked Sendable` only with documented proof of manual thread-safety; it is a last resort
- Pass `Sendable` closures across actor boundaries; non-`Sendable` closures must stay on the originating actor
Structured vs. Unstructured Concurrency
| Pattern | Use When | |---------|----------| | `async let` | Fixed number of independent operations with known result types | | `TaskGroup` / `withThrowingTaskGroup` | Dynamic number of concurrent operations | | `Task {}` | Background work not in an async context (UI event handlers, Combine sinks) | | Prefer `async let` / `TaskGroup` over naked `Task {}` when applicable | Unstructured tasks escape scope, making cancellation and error propagation harder |
// Prefer async let for fixed parallel fetches
async let profile = fetchProfile(userID)
async let posts = fetchPosts(userID)
let (p, feed) = try await (profile, posts)
// Use TaskGroup for dynamic concurrency
let results = try await withThrowingTaskGroup(of: Item.self) { group in
for id in ids {
group.addTask { try await fetchItem(id) }
}
return try await group.reduce(into: []) { $0.append($1) }
}Typed Throws (Swift 6+)
enum FetchError: Error {
case networkUnavailable
case decodingFailed(underlying: Error)
}
func fetchUser(id: UUID) async throws(FetchError) -> User {
guard NetworkMonitor.isAvailable else { throw .networkUnavailable }
do {
let data = try await urlSession.data(from: endpoint(id)).0
return try JSONDecoder().decode(User.self, from: data)
} catch {
throw .decodingFailed(underlying: error)
}
}---
Protocol-Oriented Design
Small Focused Protocols
Define protocols around a single capability. Conformers implement only what they need.
// Wrong — fat protocol
protocol DataService {
func fetch() async throws -> [Item]
func save(_ item: Item) async throws
func delete(_ id: UUID) async throws
func export() -> Data
}
// Correct — segregated protocols
protocol ItemFetcher { func fetch() async throws -> [Item] }
protocol ItemWriter { func save(_ item: Item) async throws; func delete(_ id: UUID) async throws }
protocol DataExporter { func export() -> Data }Protocol Extensions for Shared Defaults
protocol Loggable {
var logger: Logger { get }
}
extension Loggable {
var logger: Logger {
Logger(subsystem: Bundle.main.bundleIdentifier ?? "app", category: String(describing: Self.self))
}
}Dependency Injection via Protocol with Default Parameter
Production code uses the real implementation by default; tests inject a mock without any additional configuration in the production call sites.
protocol HTTPClient: Sendable {
func data(for request: URLRequest) async throws -> (Data, URLResponse)
}
extension URLSession: HTTPClient {} // URLSession already matches the protocol
struct UserRepository {
private let client: any HTTPClient
// Default parameter means production callers never see the seam
init(client: any HTTPClient = URLSession.shared) {
self.client = client
}
}
// In tests:
struct MockHTTPClient: HTTPClient {
var stubbedData: Data = Data()
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
(stubbedData, URLResponse())
}
}---
State Modeling
`LoadState<T>` En
Read more
Swift Patterns
Immutability
`let` vs. `var` Rules
| Rule | Rationale | |------|-----------| | Always declare as `let` first | If the compiler accepts it, it is immutable — keep it | | Change to `var` only when compiler requires it | The compiler is the source of truth, not habit | | Never pre-emptively declare `var` in case it changes | That change may never come; premature mutability creates data-race surface area | | Use `mutating func` in structs for controlled mutation | Keeps value semantics; each mutation is explicit at call sites |
// Wrong — var when let suffices var name = "Alice" print(name) // never reassigned // Correct let name = "Alice" print(name)
`struct` vs. `class` Decision Matrix
| Use `struct` when... | Use `class` when... | |----------------------|---------------------| | Data is copied between contexts (DTO, model, config) | Identity matters — two references must point to the same object | | Thread-safety via value semantics is desired | Objective-C interoperability requires `NSObject` subclassing | | No subclassing is needed | Reference sharing is an intentional design decision (e.g., shared cache) | | All stored properties are value types or `Sendable` | Lifecycle management via `deinit` is required | | SwiftUI view models that do not need `ObservableObject` | `ObservableObject` / `@Published` Combine integration |
// Prefer struct for DTOs
struct UserProfile: Sendable {
let id: UUID
let displayName: String
let email: String
}
// class only when identity semantics are needed
final class ImageCache {
static let shared = ImageCache() // single shared instance
private var storage: [URL: UIImage] = [:]
private init() {}
}---
Concurrency
Core Principles
Swift 6 strict concurrency treats data races as compile-time errors. Every type crossing actor isolation boundaries must be `Sendable`.
Actor Isolation
// Use actors for shared mutable state — not DispatchQueue or locks
actor DownloadManager {
private var activeTasks: [URL: Task<Data, Error>] = [:]
func fetch(_ url: URL) async throws -> Data {
if let existing = activeTasks[url] {
return try await existing.value
}
let task = Task { try await URLSession.shared.data(from: url).0 }
activeTasks[url] = task
defer { activeTasks.removeValue(forKey: url) }
return try await task.value
}
}Sendable Requirements
- Value types (`struct`, `enum`) that contain only `Sendable` properties are automatically `Sendable`
- Add `@unchecked Sendable` only with documented proof of manual thread-safety; it is a last resort
- Pass `Sendable` closures across actor boundaries; non-`Sendable` closures must stay on the originating actor
Structured vs. Unstructured Concurrency
| Pattern | Use When | |---------|----------| | `async let` | Fixed number of independent operations with known result types | | `TaskGroup` / `withThrowingTaskGroup` | Dynamic number of concurrent operations | | `Task {}` | Background work not in an async context (UI event handlers, Combine sinks) | | Prefer `async let` / `TaskGroup` over naked `Task {}` when applicable | Unstructured tasks escape scope, making cancellation and error propagation harder |
// Prefer async let for fixed parallel fetches
async let profile = fetchProfile(userID)
async let posts = fetchPosts(userID)
let (p, feed) = try await (profile, posts)
// Use TaskGroup for dynamic concurrency
let results = try await withThrowingTaskGroup(of: Item.self) { group in
for id in ids {
group.addTask { try await fetchItem(id) }
}
return try await group.reduce(into: []) { $0.append($1) }
}Typed Throws (Swift 6+)
enum FetchError: Error {
case networkUnavailable
case decodingFailed(underlying: Error)
}
func fetchUser(id: UUID) async throws(FetchError) -> User {
guard NetworkMonitor.isAvailable else { throw .networkUnavailable }
do {
let data = try await urlSession.data(from: endpoint(id)).0
return try JSONDecoder().decode(User.self, from: data)
} catch {
throw .decodingFailed(underlying: error)
}
}---
Protocol-Oriented Design
Small Focused Protocols
Define protocols around a single capability. Conformers implement only what they need.
// Wrong — fat protocol
protocol DataService {
func fetch() async throws -> [Item]
func save(_ item: Item) async throws
func delete(_ id: UUID) async throws
func export() -> Data
}
// Correct — segregated protocols
protocol ItemFetcher { func fetch() async throws -> [Item] }
protocol ItemWriter { func save(_ item: Item) async throws; func delete(_ id: UUID) async throws }
protocol DataExporter { func export() -> Data }Protocol Extensions for Shared Defaults
protocol Loggable {
var logger: Logger { get }
}
extension Loggable {
var logger: Logger {
Logger(subsystem: Bundle.main.bundleIdentifier ?? "app", category: String(describing: Self.self))
}
}Dependency Injection via Protocol with Default Parameter
Production code uses the real implementation by default; tests inject a mock without any additional configuration in the production call sites.
protocol HTTPClient: Sendable {
func data(for request: URLRequest) async throws -> (Data, URLResponse)
}
extension URLSession: HTTPClient {} // URLSession already matches the protocol
struct UserRepository {
private let client: any HTTPClient
// Default parameter means production callers never see the seam
init(client: any HTTPClient = URLSession.shared) {
self.client = client
}
}
// In tests:
struct MockHTTPClient: HTTPClient {
var stubbedData: Data = Data()
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
(stubbedData, URLResponse())
}
}---
State Modeling
`LoadState<T>` En
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

