engsys 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/core/agents/aaron.md +152 -0
  4. package/core/agents/bert.md +115 -0
  5. package/core/agents/isabelle.md +136 -0
  6. package/core/agents/jody.md +150 -0
  7. package/core/agents/leith.md +111 -0
  8. package/core/agents/marcelo.md +282 -0
  9. package/core/agents/melvin.md +101 -0
  10. package/core/agents/nyx.md +152 -0
  11. package/core/agents/otto.md +168 -0
  12. package/core/agents/patricia.md +283 -0
  13. package/core/commands/design-audit-local.md +155 -0
  14. package/core/commands/design-audit.md +235 -0
  15. package/core/commands/design-critique.md +96 -0
  16. package/core/commands/file-issue.md +22 -0
  17. package/core/commands/generate-project.md +45 -0
  18. package/core/commands/implement-issue.md +37 -0
  19. package/core/commands/implement-project.md +40 -0
  20. package/core/commands/naturalize.md +61 -0
  21. package/core/commands/pre-push.md +29 -0
  22. package/core/commands/prep-review-collect.md +130 -0
  23. package/core/commands/prep-review-finalize.md +121 -0
  24. package/core/commands/prep-review-publish.md +113 -0
  25. package/core/commands/prep-review.md +65 -0
  26. package/core/commands/project-closeout.md +25 -0
  27. package/core/skills/agentic-eval/SKILL.md +195 -0
  28. package/core/skills/chrome-devtools/SKILL.md +97 -0
  29. package/core/skills/code-review/SKILL.md +26 -0
  30. package/core/skills/gh-cli/SKILL.md +2202 -0
  31. package/core/skills/git-commit/SKILL.md +124 -0
  32. package/core/skills/git-workflow-agents/SKILL.md +462 -0
  33. package/core/skills/git-workflow-agents/reference.md +220 -0
  34. package/core/skills/github-actions/SKILL.md +190 -0
  35. package/core/skills/github-issues/SKILL.md +154 -0
  36. package/core/skills/llm-structured-outputs/SKILL.md +323 -0
  37. package/core/skills/llm-structured-outputs/references/provider-details.md +392 -0
  38. package/core/skills/pre-push/SKILL.md +115 -0
  39. package/core/skills/refactor/SKILL.md +645 -0
  40. package/core/skills/web-design-reviewer/SKILL.md +371 -0
  41. package/core/skills/webapp-testing/SKILL.md +127 -0
  42. package/core/skills/webapp-testing/test-helper.js +56 -0
  43. package/core/templates/CLAUDE.md.tmpl +98 -0
  44. package/core/templates/adr-template.md +67 -0
  45. package/core/templates/gh-issue-templates/bug.md +39 -0
  46. package/core/templates/gh-issue-templates/content.md +42 -0
  47. package/core/templates/gh-issue-templates/enhancement.md +36 -0
  48. package/core/templates/gh-issue-templates/feature.md +39 -0
  49. package/core/templates/gh-issue-templates/infrastructure.md +41 -0
  50. package/core/templates/post-edit-reminders.sh.tmpl +19 -0
  51. package/core/templates/settings.json.tmpl +90 -0
  52. package/core/templates/settings.local.json.tmpl +3 -0
  53. package/core/workflows/agent-implementation-workflow.md +346 -0
  54. package/core/workflows/generate-project.md +258 -0
  55. package/core/workflows/implement-project-workflow.md +190 -0
  56. package/core/workflows/issue-tracking.md +89 -0
  57. package/core/workflows/project-closeout-ceremony.md +77 -0
  58. package/core/workflows/review-workflow.md +266 -0
  59. package/engsys.config.example.yaml +46 -0
  60. package/install +202 -0
  61. package/lessons-library/README.md +80 -0
  62. package/lessons-library/async-callbacks-verify-liveness.md +15 -0
  63. package/lessons-library/change-isnt-done-until-every-surface-updated.md +15 -0
  64. package/lessons-library/claim-then-act-for-irreversible-ops.md +16 -0
  65. package/lessons-library/co-commit-entangled-work.md +15 -0
  66. package/lessons-library/dependabot-triage-playbook.md +17 -0
  67. package/lessons-library/deploy-by-digest-and-verify-the-running-revision.md +15 -0
  68. package/lessons-library/enforce-your-guarantee-at-your-boundary.md +16 -0
  69. package/lessons-library/gate-changes-on-measurement-not-vibes.md +15 -0
  70. package/lessons-library/iac-first-no-console-changes.md +15 -0
  71. package/lessons-library/independent-objective-review-gate.md +15 -0
  72. package/lessons-library/keep-an-immutable-source-of-truth.md +15 -0
  73. package/lessons-library/long-agent-runs-checkpoint-not-poll.md +15 -0
  74. package/lessons-library/model-identity-with-stable-ids-and-provenance.md +15 -0
  75. package/lessons-library/operator-choices-are-first-class.md +15 -0
  76. package/lessons-library/prefer-tool-enforced-structured-output.md +15 -0
  77. package/lessons-library/prove-causation-before-acting.md +15 -0
  78. package/lessons-library/re-read-state-before-acting.md +14 -0
  79. package/lessons-library/read-layer-tolerates-unbackfilled-rows.md +15 -0
  80. package/lessons-library/shell-safety-pipefail-and-validate-before-teardown.md +14 -0
  81. package/lessons-library/shift-correctness-left-and-distrust-false-greens.md +15 -0
  82. package/lessons-library/stray-control-bytes-hide-changes.md +14 -0
  83. package/lessons-library/tests-can-assert-the-bug.md +15 -0
  84. package/lessons-library/verify-ground-truth-not-reports.md +15 -0
  85. package/lessons-library/worktrees-need-bootstrap-from-origin-main.md +15 -0
  86. package/lib/commands.js +356 -0
  87. package/lib/generate-team-avatars.mjs +251 -0
  88. package/lib/manifest.js +155 -0
  89. package/lib/render.js +135 -0
  90. package/lib/selftest.js +90 -0
  91. package/lib/util.js +89 -0
  92. package/lib/yaml.js +156 -0
  93. package/optional-agents/gary.md +86 -0
  94. package/optional-agents/jos.md +136 -0
  95. package/optional-agents/sandy.md +101 -0
  96. package/optional-agents/steve.md +161 -0
  97. package/package.json +43 -0
  98. package/stacks/cloud/aws/claude.fragment.md +17 -0
  99. package/stacks/cloud/aws/settings.fragment.json +39 -0
  100. package/stacks/cloud/aws/skills/aws-deployment-preflight/SKILL.md +165 -0
  101. package/stacks/cloud/aws/skills/cloud-architecture-aws/SKILL.md +265 -0
  102. package/stacks/cloud/azure/claude.fragment.md +17 -0
  103. package/stacks/cloud/azure/settings.fragment.json +45 -0
  104. package/stacks/cloud/azure/skills/azure-deployment-preflight/SKILL.md +175 -0
  105. package/stacks/cloud/azure/skills/cloud-architecture-azure/SKILL.md +211 -0
  106. package/stacks/cloud/cloudflare/claude.fragment.md +21 -0
  107. package/stacks/cloud/cloudflare/settings.fragment.json +31 -0
  108. package/stacks/cloud/cloudflare/skills/cloud-architecture-cloudflare/SKILL.md +294 -0
  109. package/stacks/cloud/cloudflare/skills/cloudflare-deployment-preflight/SKILL.md +175 -0
  110. package/stacks/cloud/gcp/claude.fragment.md +17 -0
  111. package/stacks/cloud/gcp/settings.fragment.json +40 -0
  112. package/stacks/cloud/gcp/skills/cloud-architecture-gcp/SKILL.md +208 -0
  113. package/stacks/cloud/gcp/skills/gcp-deployment-preflight/SKILL.md +137 -0
  114. package/stacks/db/mongo/skills/mongo-conventions/SKILL.md +96 -0
  115. package/stacks/db/prisma/claude.fragment.md +49 -0
  116. package/stacks/db/prisma/skills/docker-database-package-copy/SKILL.md +44 -0
  117. package/stacks/db/prisma/skills/prisma-conventions/SKILL.md +37 -0
  118. package/stacks/domain/mobile-growth/skills/apple-ads/SKILL.md +184 -0
  119. package/stacks/domain/mobile-growth/skills/apple-ads/references/benchmark-notes.md +47 -0
  120. package/stacks/domain/mobile-growth/skills/apple-ads/references/official-links.md +53 -0
  121. package/stacks/domain/mobile-growth/skills/google-play-growth/SKILL.md +197 -0
  122. package/stacks/domain/mobile-growth/skills/google-play-growth/references/benchmark-notes.md +47 -0
  123. package/stacks/domain/mobile-growth/skills/google-play-growth/references/official-links.md +45 -0
  124. package/stacks/iac/bicep/claude.fragment.md +14 -0
  125. package/stacks/iac/bicep/settings.fragment.json +20 -0
  126. package/stacks/iac/bicep/skills/iac-bicep/SKILL.md +113 -0
  127. package/stacks/iac/cdk/claude.fragment.md +14 -0
  128. package/stacks/iac/cdk/settings.fragment.json +23 -0
  129. package/stacks/iac/cdk/skills/iac-cdk/SKILL.md +104 -0
  130. package/stacks/iac/terraform/claude.fragment.md +13 -0
  131. package/stacks/iac/terraform/settings.fragment.json +25 -0
  132. package/stacks/iac/terraform/skills/iac-terraform/SKILL.md +93 -0
  133. package/stacks/iac/terraform/skills/terraform-conventions/SKILL.md +87 -0
  134. package/stacks/lang/kotlin/skills/android-testing/SKILL.md +263 -0
  135. package/stacks/lang/kotlin/skills/jetpack-compose/SKILL.md +264 -0
  136. package/stacks/lang/kotlin/skills/kotlin-coroutines/SKILL.md +329 -0
  137. package/stacks/lang/python/skills/python-conventions/SKILL.md +61 -0
  138. package/stacks/lang/shell/skills/shell-scripting/SKILL.md +110 -0
  139. package/stacks/lang/swift/skills/swift-concurrency/SKILL.md +423 -0
  140. package/stacks/lang/swift/skills/swift-concurrency/references/approachable-concurrency.md +80 -0
  141. package/stacks/lang/swift/skills/swift-concurrency/references/concurrency-patterns.md +233 -0
  142. package/stacks/lang/swift/skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
  143. package/stacks/lang/swift/skills/swift-concurrency/references/synchronization-primitives.md +341 -0
  144. package/stacks/lang/swift/skills/swift-testing/SKILL.md +497 -0
  145. package/stacks/lang/swift/skills/swift-testing/references/testing-advanced.md +106 -0
  146. package/stacks/lang/swift/skills/swift-testing/references/testing-patterns.md +504 -0
  147. package/stacks/lang/swift/skills/swiftdata/SKILL.md +334 -0
  148. package/stacks/lang/swift/skills/swiftdata/references/core-data-coexistence.md +504 -0
  149. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-advanced.md +975 -0
  150. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-queries.md +675 -0
  151. package/stacks/lang/swift/skills/swiftui-patterns/SKILL.md +371 -0
  152. package/stacks/lang/swift/skills/swiftui-patterns/references/architecture-patterns.md +486 -0
  153. package/stacks/lang/swift/skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
  154. package/stacks/lang/swift/skills/swiftui-patterns/references/design-polish.md +780 -0
  155. package/stacks/lang/swift/skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
  156. package/stacks/lang/typescript/skills/typescript-conventions/SKILL.md +91 -0
  157. package/stacks/platform/android/claude.fragment.md +40 -0
  158. package/stacks/platform/android/hooks/pre-push-gradle.sh +70 -0
  159. package/stacks/platform/android/settings.fragment.json +13 -0
  160. package/stacks/platform/android/skills/android-build-conventions/SKILL.md +247 -0
  161. package/stacks/platform/ios/claude.fragment.md +24 -0
  162. package/stacks/platform/ios/hooks/pre-push-xcodebuild.sh +82 -0
  163. package/stacks/platform/ios/settings.fragment.json +21 -0
  164. package/stacks/platform/ios/skills/xcodebuildmcp-simulator-logs/SKILL.md +76 -0
  165. package/stacks/platform/web/skills/frontend-testing/SKILL.md +246 -0
  166. package/stacks/platform/web/skills/react-conventions/SKILL.md +261 -0
  167. package/stacks/platform/web/skills/web-platform-conventions/SKILL.md +55 -0
  168. package/stacks/tooling/issue-tracker-github/claude.fragment.md +10 -0
  169. package/stacks/tooling/issue-tracker-github/settings.fragment.json +24 -0
  170. package/stacks/tooling/issue-tracker-github/skills/issue-tracker-github/SKILL.md +278 -0
  171. package/stacks/tooling/issue-tracker-linear/claude.fragment.md +17 -0
  172. package/stacks/tooling/issue-tracker-linear/settings.fragment.json +9 -0
  173. package/stacks/tooling/issue-tracker-linear/skills/issue-tracker-linear/SKILL.md +183 -0
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: shell-scripting
3
+ description: Shell scripting conventions for bash/sh/zsh in this repo. Use when writing or editing any *.sh file — including scripts/, .githooks/, and one-off shell snippets. Covers safety flags, structure, error handling, JSON/YAML parsing, and a script template.
4
+ ---
5
+
6
+ # Shell Scripting Guidelines
7
+
8
+ Applies to: any `*.sh` file in this repo. Common locations: `scripts/`, `.githooks/`.
9
+
10
+ ## General Principles
11
+
12
+ - Clean, simple, concise. Easy to read.
13
+ - Comments where helpful for understanding; avoid commentary on what well-named identifiers already say.
14
+ - Concise echo for status; avoid noisy logging.
15
+ - Use `shellcheck` when available.
16
+ - Default assumption: scripts are for automation/testing, not production systems, unless stated.
17
+ - Safe expansions: double-quote variable references (`"$var"`), use `${var}` for clarity, never `eval`.
18
+ - Prefer modern Bash (`[[ ]]`, `local`, arrays) when portability allows; fall back to POSIX only when needed.
19
+ - Use dedicated parsers (`jq`, `yq`) for structured data instead of ad-hoc grep/awk.
20
+
21
+ ## Error Handling & Safety
22
+
23
+ - Always: `set -euo pipefail`.
24
+ - Validate required parameters before doing anything.
25
+ - Provide error messages with context.
26
+ - Use `trap` to clean up temp files / unexpected exits.
27
+ - Declare constants with `readonly`.
28
+ - Use `mktemp` for temp files/dirs; remove in cleanup.
29
+
30
+ ## Script Structure
31
+
32
+ - Shebang: `#!/bin/bash` unless told otherwise.
33
+ - Header comment explaining purpose.
34
+ - Default values at top.
35
+ - Functions for reusable blocks.
36
+ - Keep `main` flow readable.
37
+
38
+ ## JSON / YAML
39
+
40
+ - Prefer `jq` for JSON, `yq` for YAML.
41
+ - Quote filters (`jq '.foo'`); use `--raw-output` for plain strings.
42
+ - Validate fields exist; check `jq` exit status or `// empty`.
43
+ - Combine with `set -euo pipefail` so parse errors are fatal.
44
+ - Document parser dependencies at the top; fail fast with a helpful message if missing.
45
+
46
+ ## Template
47
+
48
+ ```bash
49
+ #!/bin/bash
50
+
51
+ # ============================================================================
52
+ # Script Description Here
53
+ # ============================================================================
54
+
55
+ set -euo pipefail
56
+
57
+ cleanup() {
58
+ if [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]]; then
59
+ rm -rf "$TEMP_DIR"
60
+ fi
61
+ }
62
+
63
+ trap cleanup EXIT
64
+
65
+ # Default values
66
+ REQUIRED_PARAM=""
67
+ OPTIONAL_PARAM="default-value"
68
+ readonly SCRIPT_NAME="$(basename "$0")"
69
+
70
+ TEMP_DIR=""
71
+
72
+ usage() {
73
+ echo "Usage: $SCRIPT_NAME [OPTIONS]"
74
+ echo "Options:"
75
+ echo " -p, --param Required param"
76
+ echo " -h, --help Show this help"
77
+ exit 0
78
+ }
79
+
80
+ validate_requirements() {
81
+ if [[ -z "$REQUIRED_PARAM" ]]; then
82
+ echo "Error: --param is required" >&2
83
+ exit 1
84
+ fi
85
+ }
86
+
87
+ main() {
88
+ validate_requirements
89
+ TEMP_DIR="$(mktemp -d)"
90
+ # main logic here
91
+ }
92
+
93
+ while [[ $# -gt 0 ]]; do
94
+ case $1 in
95
+ -p|--param)
96
+ REQUIRED_PARAM="$2"
97
+ shift 2
98
+ ;;
99
+ -h|--help)
100
+ usage
101
+ ;;
102
+ *)
103
+ echo "Unknown option: $1" >&2
104
+ exit 1
105
+ ;;
106
+ esac
107
+ done
108
+
109
+ main "$@"
110
+ ```
@@ -0,0 +1,423 @@
1
+ ---
2
+ name: swift-concurrency
3
+ description: "Resolve Swift concurrency compiler errors, adopt approachable concurrency (SE-0466), and write data-race-safe async code. Use when fixing Sendable conformance errors, actor isolation warnings, or strict concurrency diagnostics; when adopting default MainActor isolation, @concurrent, nonisolated(nonsending), or Task.immediate; when designing actor-based architectures, structured concurrency with TaskGroup, or background work offloading; or when migrating from @preconcurrency to full Swift 6 strict concurrency."
4
+ ---
5
+
6
+ # Swift Concurrency
7
+
8
+ Review, fix, and write concurrent Swift code targeting Swift 6.3+. Apply actor
9
+ isolation, Sendable safety, and modern concurrency patterns with minimal
10
+ behavior changes.
11
+
12
+ ## Contents
13
+
14
+ - [Triage Workflow](#triage-workflow)
15
+ - [Swift 6.2 Language Changes](#swift-62-language-changes)
16
+ - [Actor Isolation Rules](#actor-isolation-rules)
17
+ - [Sendable Rules](#sendable-rules)
18
+ - [Structured Concurrency Patterns](#structured-concurrency-patterns)
19
+ - [Task Cancellation](#task-cancellation)
20
+ - [Actor Reentrancy](#actor-reentrancy)
21
+ - [AsyncSequence and AsyncStream](#asyncsequence-and-asyncstream)
22
+ - [@Observable and Concurrency](#observable-and-concurrency)
23
+ - [Synchronization Primitives](#synchronization-primitives)
24
+ - [Common Mistakes](#common-mistakes)
25
+ - [Review Checklist](#review-checklist)
26
+ - [References](#references)
27
+
28
+ ## Triage Workflow
29
+
30
+ When diagnosing a concurrency issue, follow this sequence:
31
+
32
+ ### Step 1: Capture context
33
+
34
+ - Copy the exact compiler diagnostic(s) and the offending symbol(s).
35
+ - Identify the project's concurrency settings:
36
+ - Swift language version (must be 6.2+).
37
+ - Whether approachable concurrency (default MainActor isolation) is enabled.
38
+ - Strict concurrency checking level (Complete / Targeted / Minimal).
39
+ - Determine the current actor context of the code (`@MainActor`, custom `actor`,
40
+ `nonisolated`) and whether a default isolation mode is active.
41
+ - Confirm whether the code is UI-bound or intended to run off the main actor.
42
+
43
+ ### Step 2: Apply the smallest safe fix
44
+
45
+ Prefer edits that preserve existing behavior while satisfying data-race safety.
46
+
47
+ | Situation | Recommended fix |
48
+ |---|---|
49
+ | UI-bound type | Annotate the type or relevant members with `@MainActor`. |
50
+ | Protocol conformance on MainActor type | Use an isolated conformance: `extension Foo: @MainActor Proto`. |
51
+ | Global / static state | Protect with `@MainActor` or move into an actor. |
52
+ | Background work needed | Use a `@concurrent` async function on a `nonisolated` type. |
53
+ | Sendable error | Prefer immutable value types. Add `Sendable` only when correct. |
54
+ | Cross-isolation callback | Use `sending` parameters (SE-0430) for finer control. |
55
+
56
+ ### Step 3: Verify
57
+
58
+ - Rebuild and confirm the diagnostic is resolved.
59
+ - Check for new warnings introduced by the fix.
60
+ - Ensure no unnecessary `@unchecked Sendable` or `nonisolated(unsafe)` was added.
61
+
62
+ ## Swift 6.2 Language Changes
63
+
64
+ Swift 6.2 introduces "approachable concurrency" -- a set of language changes
65
+ that make concurrent code safer by default while reducing annotation burden.
66
+
67
+ ### SE-0466: Default MainActor Isolation
68
+
69
+ With the `-default-isolation MainActor` compiler flag (or the Xcode 26
70
+ "Approachable Concurrency" build setting), all code in a module runs on
71
+ `@MainActor` by default unless explicitly opted out.
72
+
73
+ **Effect:** Eliminates most data-race safety errors for UI-bound code and
74
+ global/static state without writing `@MainActor` everywhere.
75
+
76
+ ```swift
77
+ // With default MainActor isolation enabled, these are implicitly @MainActor:
78
+ final class StickerLibrary {
79
+ static let shared = StickerLibrary() // safe -- on MainActor
80
+ var stickers: [Sticker] = []
81
+ }
82
+
83
+ final class StickerModel {
84
+ let photoProcessor = PhotoProcessor()
85
+ var selection: [PhotosPickerItem] = []
86
+ }
87
+
88
+ // Conformances are also implicitly isolated:
89
+ extension StickerModel: Exportable {
90
+ func export() {
91
+ photoProcessor.exportAsPNG()
92
+ }
93
+ }
94
+ ```
95
+
96
+ **When to use:** Recommended for apps, scripts, and other executable targets
97
+ where most code is UI-bound. Not recommended for library targets that should
98
+ remain actor-agnostic.
99
+
100
+ ### SE-0461: nonisolated(nonsending)
101
+
102
+ Nonisolated async functions now stay on the caller's actor by default instead
103
+ of hopping to the global concurrent executor. This is the
104
+ `nonisolated(nonsending)` behavior.
105
+
106
+ ```swift
107
+ class PhotoProcessor {
108
+ func extractSticker(data: Data, with id: String?) async -> Sticker? {
109
+ // In Swift 6.2+, this runs on the caller's actor (e.g., MainActor)
110
+ // instead of hopping to a background thread.
111
+ // ...
112
+ }
113
+ }
114
+
115
+ @MainActor
116
+ final class StickerModel {
117
+ let photoProcessor = PhotoProcessor()
118
+
119
+ func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
120
+ guard let data = try await item.loadTransferable(type: Data.self) else {
121
+ return nil
122
+ }
123
+ // No data race -- photoProcessor stays on MainActor
124
+ return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
125
+ }
126
+ }
127
+ ```
128
+
129
+ Use `@concurrent` to explicitly request background execution when needed.
130
+
131
+ ### @concurrent Attribute
132
+
133
+ `@concurrent` ensures a function always runs on the concurrent thread pool,
134
+ freeing the calling actor to run other tasks.
135
+
136
+ ```swift
137
+ class PhotoProcessor {
138
+ var cachedStickers: [String: Sticker] = [:]
139
+
140
+ func extractSticker(data: Data, with id: String) async -> Sticker {
141
+ if let sticker = cachedStickers[id] { return sticker }
142
+
143
+ let sticker = await Self.extractSubject(from: data)
144
+ cachedStickers[id] = sticker
145
+ return sticker
146
+ }
147
+
148
+ @concurrent
149
+ static func extractSubject(from data: Data) async -> Sticker {
150
+ // Expensive image processing -- runs on background thread pool
151
+ // ...
152
+ }
153
+ }
154
+ ```
155
+
156
+ To move a function to a background thread:
157
+ 1. Ensure the containing type is `nonisolated` (or the function itself is).
158
+ 2. Add `@concurrent` to the function.
159
+ 3. Add `async` if not already asynchronous.
160
+ 4. Add `await` at call sites.
161
+
162
+ ```swift
163
+ nonisolated struct PhotoProcessor {
164
+ @concurrent
165
+ func process(data: Data) async -> ProcessedPhoto? { /* ... */ }
166
+ }
167
+
168
+ // Caller:
169
+ processedPhotos[item.id] = await PhotoProcessor().process(data: data)
170
+ ```
171
+
172
+ ### SE-0472: Task.immediate
173
+
174
+ `Task.immediate` starts executing synchronously on the current actor before
175
+ any suspension point, rather than being enqueued.
176
+
177
+ ```swift
178
+ Task.immediate { await handleUserInput() }
179
+ ```
180
+
181
+ Use for latency-sensitive work that should begin without delay. There is also
182
+ `Task.immediateDetached` which combines immediate start with detached semantics.
183
+
184
+ ### SE-0475: Transactional Observation (Observations)
185
+
186
+ `Observations { }` provides async observation of `@Observable` types via
187
+ `AsyncSequence`, enabling transactional change tracking.
188
+
189
+ ```swift
190
+ for await _ in Observations { model.count } {
191
+ print("Count changed to \(model.count)")
192
+ }
193
+ ```
194
+
195
+ ### Isolated Conformances
196
+
197
+ A conformance that needs MainActor state is called an *isolated conformance*.
198
+ The compiler ensures it is only used in a matching isolation context.
199
+
200
+ ```swift
201
+ protocol Exportable {
202
+ func export()
203
+ }
204
+
205
+ // Isolated conformance: only usable on MainActor
206
+ extension StickerModel: @MainActor Exportable {
207
+ func export() {
208
+ photoProcessor.exportAsPNG()
209
+ }
210
+ }
211
+
212
+ @MainActor
213
+ struct ImageExporter {
214
+ var items: [any Exportable]
215
+
216
+ mutating func add(_ item: StickerModel) {
217
+ items.append(item) // OK -- ImageExporter is on MainActor
218
+ }
219
+ }
220
+ ```
221
+
222
+ If `ImageExporter` were `nonisolated`, adding a `StickerModel` would fail:
223
+ "Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be
224
+ used in nonisolated context."
225
+
226
+ ### Clock Epochs
227
+
228
+ `ContinuousClock` and `SuspendingClock` now expose `.epoch` (SE-0473), enabling instant comparison and conversion between clock types.
229
+
230
+ ```swift
231
+ let continuous = ContinuousClock()
232
+ let elapsed = continuous.now - continuous.epoch // Duration since system boot
233
+ ```
234
+
235
+ ## Actor Isolation Rules
236
+
237
+ 1. All mutable shared state MUST be protected by an actor or global actor.
238
+ 2. `@MainActor` for all UI-touching code. No exceptions.
239
+ 3. Use `nonisolated` only for methods that access immutable (`let`) properties
240
+ or are pure computations.
241
+ 4. Use `@concurrent` to explicitly move work off the caller's actor.
242
+ 5. Never use `nonisolated(unsafe)` unless you have proven internal
243
+ synchronization and exhausted all other options.
244
+ 6. Never add manual locks (`NSLock`, `DispatchSemaphore`) inside actors.
245
+
246
+ ## Sendable Rules
247
+
248
+ 1. Value types (structs, enums) are automatically `Sendable` when all stored
249
+ properties are `Sendable`.
250
+ 2. Actors are implicitly `Sendable`.
251
+ 3. `@MainActor` classes are implicitly `Sendable`. Do NOT add redundant
252
+ `Sendable` conformance.
253
+ 4. Non-actor classes: must be `final` with all stored properties `let` and
254
+ `Sendable`.
255
+ 5. `@unchecked Sendable` is a last resort. Document why the compiler cannot
256
+ prove safety.
257
+ 6. Use `sending` parameters (SE-0430) for finer-grained isolation control.
258
+ 7. Use `@preconcurrency import` only for third-party libraries you cannot
259
+ modify. Plan to remove it.
260
+
261
+ ## Structured Concurrency Patterns
262
+
263
+ ### Async Defer
264
+
265
+ `defer` blocks can now contain `await` (SE-0493). Use for async cleanup — closing connections, flushing buffers, or releasing resources that require an async call.
266
+
267
+ ```swift
268
+ func fetchData() async throws -> Data {
269
+ let connection = try await openConnection()
270
+ defer { await connection.close() }
271
+ return try await connection.read()
272
+ }
273
+ ```
274
+
275
+ **Task:** Unstructured, inherits caller context.
276
+ ```swift
277
+ Task { await doWork() }
278
+ ```
279
+
280
+ **Task.detached:** No inherited context. Use only when you explicitly need to
281
+ break isolation inheritance.
282
+
283
+ **Task.immediate:** Starts immediately on current actor. Use for
284
+ latency-sensitive work.
285
+ ```swift
286
+ Task.immediate { await handleUserInput() }
287
+ ```
288
+
289
+ **async let:** Fixed number of concurrent operations.
290
+ ```swift
291
+ async let a = fetchA()
292
+ async let b = fetchB()
293
+ let result = try await (a, b)
294
+ ```
295
+
296
+ **TaskGroup:** Dynamic number of concurrent operations.
297
+ ```swift
298
+ try await withThrowingTaskGroup(of: Item.self) { group in
299
+ for id in ids {
300
+ group.addTask { try await fetch(id) }
301
+ }
302
+ for try await item in group { process(item) }
303
+ }
304
+ ```
305
+
306
+ ## Task Cancellation
307
+
308
+ - Cancellation is cooperative. Check `Task.isCancelled` or call
309
+ `try Task.checkCancellation()` in loops.
310
+ - Use `.task` modifier in SwiftUI -- it handles cancellation on view disappear.
311
+ - Use `withTaskCancellationHandler` for cleanup.
312
+ - Cancel stored tasks in `deinit` or `onDisappear`.
313
+
314
+ ## Actor Reentrancy
315
+
316
+ Actors are reentrant. State can change across suspension points.
317
+
318
+ ```swift
319
+ // WRONG: State may change during await
320
+ actor Counter {
321
+ var count = 0
322
+ func increment() async {
323
+ let current = count
324
+ await someWork()
325
+ count = current + 1 // BUG: count may have changed
326
+ }
327
+ }
328
+
329
+ // CORRECT: Mutate synchronously, no reentrancy risk
330
+ actor Counter {
331
+ var count = 0
332
+ func increment() { count += 1 }
333
+ }
334
+ ```
335
+
336
+ ## AsyncSequence and AsyncStream
337
+
338
+ Use `AsyncStream` to bridge callback/delegate APIs:
339
+
340
+ ```swift
341
+ let stream = AsyncStream<Location> { continuation in
342
+ let delegate = LocationDelegate { location in
343
+ continuation.yield(location)
344
+ }
345
+ continuation.onTermination = { _ in delegate.stop() }
346
+ delegate.start()
347
+ }
348
+ ```
349
+
350
+ Use `withCheckedContinuation` / `withCheckedThrowingContinuation` for
351
+ single-value callbacks. Resume exactly once.
352
+
353
+ ## @Observable and Concurrency
354
+
355
+ - `@Observable` classes should be `@MainActor` for view models.
356
+ - Use `@State` to own an `@Observable` instance (replaces `@StateObject`).
357
+ - Use `Observations { }` (SE-0475) for async observation of `@Observable`
358
+ properties as an `AsyncSequence`.
359
+
360
+ ## Synchronization Primitives
361
+
362
+ When actors are not the right fit — synchronous access, performance-critical
363
+ paths, or bridging C/ObjC — use low-level synchronization primitives:
364
+
365
+ - **`Mutex<Value>`** (iOS 18+, `Synchronization` module): Preferred lock for
366
+ new code. Stores protected state inside the lock. `withLock { }` pattern.
367
+ - **`OSAllocatedUnfairLock`** (iOS 16+, `os` module): Use when targeting
368
+ older iOS versions. Supports ownership assertions for debugging.
369
+ - **`Atomic<Value>`** (iOS 18+, `Synchronization` module): Lock-free atomics
370
+ for simple counters and flags. Requires explicit memory ordering.
371
+
372
+ **Key rule:** Never put locks inside actors (double synchronization), and never
373
+ hold a lock across `await` (deadlock risk). See
374
+ [references/synchronization-primitives.md](references/synchronization-primitives.md) for full API details, code examples,
375
+ and a decision guide for choosing locks vs actors.
376
+
377
+ ## Common Mistakes
378
+
379
+ 1. **Blocking the main actor.** Heavy computation on `@MainActor` freezes UI.
380
+ Move to a `@concurrent` function.
381
+ 2. **Unnecessary @MainActor.** Network layers, data processing, and model code
382
+ do not need `@MainActor`. Only UI-touching code does.
383
+ 3. **Actors for stateless code.** No mutable state means no actor needed. Use a
384
+ plain struct or function.
385
+ 4. **Actors for immutable data.** Use a `Sendable` struct, not an actor.
386
+ 5. **Task.detached without good reason.** Loses priority, task-local values,
387
+ and cancellation propagation.
388
+ 6. **Forgetting task cancellation.** Store `Task` references and cancel them, or
389
+ use the `.task` view modifier.
390
+ 7. **Retain cycles in Tasks.** Use `[weak self]` when capturing `self` in
391
+ long-lived stored tasks.
392
+ 8. **Semaphores in async context.** `DispatchSemaphore.wait()` in async code
393
+ will deadlock. Use structured concurrency instead.
394
+ 9. **Split isolation.** Mixing `@MainActor` and `nonisolated` properties in one
395
+ type. Isolate the entire type consistently.
396
+ 10. **MainActor.run instead of static isolation.** Prefer `@MainActor func`
397
+ over `await MainActor.run { }`.
398
+ 11. **Using GCD APIs.** Never use DispatchQueue, DispatchGroup, DispatchSemaphore, or any GCD API. Use async/await, actors, and TaskGroups instead. GCD has no data-race safety guarantees.
399
+
400
+ ## Review Checklist
401
+
402
+ - [ ] All mutable shared state is actor-isolated
403
+ - [ ] No data races (no unprotected cross-isolation access)
404
+ - [ ] Tasks are cancelled when no longer needed
405
+ - [ ] No blocking calls on `@MainActor`
406
+ - [ ] No manual locks inside actors
407
+ - [ ] `Sendable` conformance is correct (no unjustified `@unchecked`)
408
+ - [ ] Actor reentrancy is handled (no state assumptions across awaits)
409
+ - [ ] `@preconcurrency` imports are documented with removal plan
410
+ - [ ] Heavy work uses `@concurrent`, not `@MainActor`
411
+ - [ ] `.task` modifier used in SwiftUI instead of manual Task management
412
+
413
+ ## References
414
+
415
+ - See [references/concurrency-patterns.md](references/concurrency-patterns.md) for detailed approachable concurrency patterns,
416
+ patterns, and migration examples.
417
+ - See [references/approachable-concurrency.md](references/approachable-concurrency.md) for the approachable concurrency
418
+ mode quick-reference guide.
419
+ - See [references/swiftui-concurrency.md](references/swiftui-concurrency.md) for SwiftUI-specific concurrency
420
+ guidance.
421
+ - See [references/synchronization-primitives.md](references/synchronization-primitives.md) for Mutex, OSAllocatedUnfairLock,
422
+ and guidance on choosing locks vs actors.
423
+
@@ -0,0 +1,80 @@
1
+ # Approachable Concurrency Quick Reference
2
+
3
+ Use this reference when the project has opted into the Swift 6.2 approachable
4
+ concurrency settings (default MainActor isolation / main-actor-by-default).
5
+
6
+ ## Detecting the Mode
7
+
8
+ **Xcode 26:** Check build settings under Swift Compiler > Concurrency:
9
+ - Swift language version: 6.2+
10
+ - Default Actor Isolation / Main Actor by Default: enabled
11
+ - Strict Concurrency Checking: Complete / Targeted / Minimal
12
+
13
+ **SwiftPM:** Inspect `Package.swift` `swiftSettings` for the corresponding flags.
14
+
15
+ ## Behavior Changes
16
+
17
+ ### Async functions stay on the caller's actor
18
+
19
+ In Swift 6.2, nonisolated async functions no longer hop to the global
20
+ concurrent executor. They stay on whichever actor called them. This eliminates
21
+ many "sending X risks causing data races" errors.
22
+
23
+ ### Default MainActor isolation
24
+
25
+ With SE-0466 enabled, all declarations in the module are implicitly `@MainActor`.
26
+ This means:
27
+ - Global and static variables are protected by default.
28
+ - Protocol conformances are implicitly isolated.
29
+ - Mutable state is safe without explicit annotation.
30
+
31
+ ### Isolated conformances
32
+
33
+ Protocol conformances can be explicitly isolated:
34
+ `extension Foo: @MainActor SomeProtocol`. The compiler prevents using the
35
+ conformance outside the matching isolation context.
36
+
37
+ ## Applying Fixes in This Mode
38
+
39
+ - **Prefer minimal annotations.** Let default MainActor isolation do the work
40
+ for UI-bound code.
41
+ - **Use isolated conformances** instead of `nonisolated` workarounds for
42
+ protocol conformances.
43
+ - **Keep global/shared mutable state on MainActor** unless there is a clear
44
+ performance need to offload.
45
+ - **Remove redundant `@MainActor` annotations** that are now implied by the
46
+ default isolation mode.
47
+
48
+ ## Offloading Work
49
+
50
+ - Use `@concurrent` on async functions that must run on the concurrent pool.
51
+ - Make types or members `nonisolated` only when they are truly thread-safe and
52
+ used off the main actor.
53
+ - Continue to respect `Sendable` boundaries when values cross actors or tasks.
54
+
55
+ ## Common Pitfalls
56
+
57
+ | Pitfall | Why it happens | Fix |
58
+ |---|---|---|
59
+ | CPU-heavy work on MainActor | Default isolation hides the problem | Move to `@concurrent` async function |
60
+ | `Task.detached` breaking isolation | Ignores inherited actor context | Use `Task { }` unless you truly need detachment |
61
+ | Redundant `@MainActor` everywhere | Default isolation already provides it | Remove explicit annotations |
62
+ | `nonisolated` on mutable state | Breaks the safety guarantee | Keep mutable state isolated |
63
+
64
+ ## Concurrency Keywords
65
+
66
+ | Keyword | What it does |
67
+ |---|---|
68
+ | `async` | Function can suspend |
69
+ | `await` | Suspend here until done |
70
+ | `Task { }` | Start async work, inherits context |
71
+ | `Task.detached { }` | Start async work, no inherited context |
72
+ | `Task.immediate { }` | Start immediately on current actor |
73
+ | `@MainActor` | Runs on main thread |
74
+ | `actor` | Type with isolated mutable state |
75
+ | `nonisolated` | Opts out of actor isolation |
76
+ | `Sendable` | Safe to pass between isolation domains |
77
+ | `@concurrent` | Always run on background thread pool (Swift 6.2+) |
78
+ | `async let` | Start parallel work (fixed count) |
79
+ | `TaskGroup` | Dynamic parallel work |
80
+ | `sending` | Parameter-level isolation transfer (SE-0430) |