opencode-starterkit 1.0.1
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.
- package/README.md +47 -0
- package/baseline/.env.example +196 -0
- package/baseline/.template-manifest.json +646 -0
- package/baseline/.version +1 -0
- package/baseline/AGENTS.md +410 -0
- package/baseline/AGENT_ALIGNMENT.md +564 -0
- package/baseline/README.md +79 -0
- package/baseline/agent/build.md +373 -0
- package/baseline/agent/explore.md +96 -0
- package/baseline/agent/general.md +186 -0
- package/baseline/agent/painter.md +74 -0
- package/baseline/agent/plan.md +435 -0
- package/baseline/agent/review.md +243 -0
- package/baseline/agent/runner.md +79 -0
- package/baseline/agent/scout.md +100 -0
- package/baseline/agent/vision.md +91 -0
- package/baseline/command/compound.md +143 -0
- package/baseline/command/create.md +213 -0
- package/baseline/command/design.md +112 -0
- package/baseline/command/handoff.md +147 -0
- package/baseline/command/init-context.md +273 -0
- package/baseline/command/init-user.md +105 -0
- package/baseline/command/init.md +117 -0
- package/baseline/command/lfg.md +170 -0
- package/baseline/command/plan.md +355 -0
- package/baseline/command/pr.md +161 -0
- package/baseline/command/research.md +125 -0
- package/baseline/command/resume.md +87 -0
- package/baseline/command/review-codebase.md +131 -0
- package/baseline/command/ship.md +342 -0
- package/baseline/command/start.md +158 -0
- package/baseline/command/status.md +117 -0
- package/baseline/command/ui-review.md +92 -0
- package/baseline/command/ui-slop-check.md +146 -0
- package/baseline/command/verify.md +160 -0
- package/baseline/context/README.md +29 -0
- package/baseline/dcp.jsonc +72 -0
- package/baseline/memory/README.md +89 -0
- package/baseline/memory/_templates/design.md +59 -0
- package/baseline/memory/_templates/prd.md +192 -0
- package/baseline/memory/_templates/project.md +58 -0
- package/baseline/memory/_templates/proposal.md +38 -0
- package/baseline/memory/_templates/roadmap.md +93 -0
- package/baseline/memory/_templates/state.md +89 -0
- package/baseline/memory/_templates/tasks.md +198 -0
- package/baseline/memory/_templates/tech-stack.md +85 -0
- package/baseline/memory/_templates/user.md +26 -0
- package/baseline/memory/project/gotchas.md +67 -0
- package/baseline/memory/project/project.md +92 -0
- package/baseline/memory/project/roadmap.md +142 -0
- package/baseline/memory/project/state.md +84 -0
- package/baseline/memory/project/tech-stack.md +53 -0
- package/baseline/memory/project/user.md +38 -0
- package/baseline/memory/research/benchmark-framework.md +162 -0
- package/baseline/memory/research/ccpm-analysis.md +334 -0
- package/baseline/memory/research/context-management-analysis.md +685 -0
- package/baseline/memory/research/effectiveness-audit.md +213 -0
- package/baseline/memory/research/opencode-mcp-bug-report.md +129 -0
- package/baseline/memory/research/openspec-analysis.md +226 -0
- package/baseline/memory/session-context.md +40 -0
- package/baseline/opencode.json +1431 -0
- package/baseline/opencode.json.tui-migration.bak +1380 -0
- package/baseline/package-lock.json +87 -0
- package/baseline/package.json +21 -0
- package/baseline/plans/1768385996691-silent-wizard.md +247 -0
- package/baseline/plans/1770006237537-mighty-otter.md +418 -0
- package/baseline/plans/1770006913647-glowing-forest.md +170 -0
- package/baseline/plans/1770013678126-witty-planet.md +278 -0
- package/baseline/plans/1770112267595-shiny-rocket.md +258 -0
- package/baseline/plans/swarm-protocol.md +123 -0
- package/baseline/plugin/README.md +70 -0
- package/baseline/plugin/copilot-auth.ts +607 -0
- package/baseline/plugin/lib/capture.ts +177 -0
- package/baseline/plugin/lib/context.ts +198 -0
- package/baseline/plugin/lib/curator.ts +234 -0
- package/baseline/plugin/lib/db/maintenance.ts +312 -0
- package/baseline/plugin/lib/db/observations.ts +299 -0
- package/baseline/plugin/lib/db/pipeline.ts +520 -0
- package/baseline/plugin/lib/db/schema.ts +356 -0
- package/baseline/plugin/lib/db/types.ts +211 -0
- package/baseline/plugin/lib/distill.ts +376 -0
- package/baseline/plugin/lib/inject.ts +126 -0
- package/baseline/plugin/lib/memory-admin-tools.ts +188 -0
- package/baseline/plugin/lib/memory-db.ts +58 -0
- package/baseline/plugin/lib/memory-helpers.ts +111 -0
- package/baseline/plugin/lib/memory-hooks.ts +195 -0
- package/baseline/plugin/lib/memory-tools.ts +341 -0
- package/baseline/plugin/lib/notify.ts +81 -0
- package/baseline/plugin/memory.ts +89 -0
- package/baseline/plugin/notification.ts.bak +64 -0
- package/baseline/plugin/package.json +7 -0
- package/baseline/plugin/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +178 -0
- package/baseline/plugin/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/baseline/plugin/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-api-types.ts +72 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-chat-language-model.ts +833 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-chat-options.ts +30 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +48 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-prepare-tools.ts +92 -0
- package/baseline/plugin/sdk/copilot/copilot-provider.ts +94 -0
- package/baseline/plugin/sdk/copilot/index.ts +5 -0
- package/baseline/plugin/sdk/copilot/openai-compatible-error.ts +30 -0
- package/baseline/plugin/sessions.ts +428 -0
- package/baseline/plugin/skill-mcp.ts +618 -0
- package/baseline/plugin/tsconfig.json +16 -0
- package/baseline/skill/accessibility-audit/SKILL.md +191 -0
- package/baseline/skill/agent-browser/SKILL.md +413 -0
- package/baseline/skill/agent-teams/SKILL.md +268 -0
- package/baseline/skill/augment-context-engine/SKILL.md +122 -0
- package/baseline/skill/augment-context-engine/mcp.json +6 -0
- package/baseline/skill/beads/SKILL.md +181 -0
- package/baseline/skill/beads/references/BEST_PRACTICES.md +27 -0
- package/baseline/skill/beads/references/BOUNDARIES.md +219 -0
- package/baseline/skill/beads/references/DEPENDENCIES.md +124 -0
- package/baseline/skill/beads/references/EXAMPLES.md +45 -0
- package/baseline/skill/beads/references/FILE_CLAIMING.md +101 -0
- package/baseline/skill/beads/references/GIT_SYNC.md +25 -0
- package/baseline/skill/beads/references/HIERARCHY.md +71 -0
- package/baseline/skill/beads/references/MULTI_AGENT.md +40 -0
- package/baseline/skill/beads/references/RESUMABILITY.md +177 -0
- package/baseline/skill/beads/references/SESSION_PROTOCOL.md +61 -0
- package/baseline/skill/beads/references/TASK_CREATION.md +38 -0
- package/baseline/skill/beads/references/TROUBLESHOOTING.md +38 -0
- package/baseline/skill/beads/references/WORKFLOWS.md +226 -0
- package/baseline/skill/beads-bridge/SKILL.md +321 -0
- package/baseline/skill/brainstorming/SKILL.md +114 -0
- package/baseline/skill/chrome-devtools/SKILL.md +76 -0
- package/baseline/skill/chrome-devtools/mcp.json +19 -0
- package/baseline/skill/cloudflare/SKILL.md +253 -0
- package/baseline/skill/cloudflare/references/agents-sdk/README.md +35 -0
- package/baseline/skill/cloudflare/references/agents-sdk/api.md +100 -0
- package/baseline/skill/cloudflare/references/agents-sdk/configuration.md +99 -0
- package/baseline/skill/cloudflare/references/agents-sdk/gotchas.md +59 -0
- package/baseline/skill/cloudflare/references/agents-sdk/patterns.md +89 -0
- package/baseline/skill/cloudflare/references/ai-gateway/README.md +695 -0
- package/baseline/skill/cloudflare/references/ai-search/README.md +14 -0
- package/baseline/skill/cloudflare/references/ai-search/api.md +38 -0
- package/baseline/skill/cloudflare/references/ai-search/configuration.md +52 -0
- package/baseline/skill/cloudflare/references/ai-search/gotchas.md +41 -0
- package/baseline/skill/cloudflare/references/ai-search/patterns.md +45 -0
- package/baseline/skill/cloudflare/references/analytics-engine/README.md +14 -0
- package/baseline/skill/cloudflare/references/analytics-engine/api.md +27 -0
- package/baseline/skill/cloudflare/references/analytics-engine/configuration.md +45 -0
- package/baseline/skill/cloudflare/references/analytics-engine/gotchas.md +3 -0
- package/baseline/skill/cloudflare/references/analytics-engine/patterns.md +36 -0
- package/baseline/skill/cloudflare/references/api/README.md +21 -0
- package/baseline/skill/cloudflare/references/api/api.md +31 -0
- package/baseline/skill/cloudflare/references/api/configuration.md +20 -0
- package/baseline/skill/cloudflare/references/api/gotchas.md +28 -0
- package/baseline/skill/cloudflare/references/api/patterns.md +47 -0
- package/baseline/skill/cloudflare/references/api-shield/README.md +20 -0
- package/baseline/skill/cloudflare/references/api-shield/api.md +78 -0
- package/baseline/skill/cloudflare/references/api-shield/configuration.md +128 -0
- package/baseline/skill/cloudflare/references/api-shield/gotchas.md +51 -0
- package/baseline/skill/cloudflare/references/api-shield/patterns.md +145 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/README.md +16 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/api.md +50 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/configuration.md +53 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/gotchas.md +16 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/patterns.md +45 -0
- package/baseline/skill/cloudflare/references/bindings/README.md +14 -0
- package/baseline/skill/cloudflare/references/bindings/api.md +3 -0
- package/baseline/skill/cloudflare/references/bindings/configuration.md +58 -0
- package/baseline/skill/cloudflare/references/bindings/gotchas.md +35 -0
- package/baseline/skill/cloudflare/references/bindings/patterns.md +37 -0
- package/baseline/skill/cloudflare/references/bot-management/README.md +71 -0
- package/baseline/skill/cloudflare/references/bot-management/api.md +168 -0
- package/baseline/skill/cloudflare/references/bot-management/configuration.md +114 -0
- package/baseline/skill/cloudflare/references/bot-management/gotchas.md +99 -0
- package/baseline/skill/cloudflare/references/bot-management/patterns.md +125 -0
- package/baseline/skill/cloudflare/references/browser-rendering/README.md +16 -0
- package/baseline/skill/cloudflare/references/browser-rendering/api.md +54 -0
- package/baseline/skill/cloudflare/references/browser-rendering/configuration.md +47 -0
- package/baseline/skill/cloudflare/references/browser-rendering/gotchas.md +29 -0
- package/baseline/skill/cloudflare/references/browser-rendering/patterns.md +29 -0
- package/baseline/skill/cloudflare/references/c3/README.md +264 -0
- package/baseline/skill/cloudflare/references/cache-reserve/README.md +93 -0
- package/baseline/skill/cloudflare/references/cache-reserve/api.md +176 -0
- package/baseline/skill/cloudflare/references/cache-reserve/configuration.md +164 -0
- package/baseline/skill/cloudflare/references/cache-reserve/gotchas.md +203 -0
- package/baseline/skill/cloudflare/references/cache-reserve/patterns.md +180 -0
- package/baseline/skill/cloudflare/references/containers/README.md +16 -0
- package/baseline/skill/cloudflare/references/containers/api.md +43 -0
- package/baseline/skill/cloudflare/references/containers/configuration.md +56 -0
- package/baseline/skill/cloudflare/references/containers/gotchas.md +21 -0
- package/baseline/skill/cloudflare/references/containers/patterns.md +40 -0
- package/baseline/skill/cloudflare/references/cron-triggers/README.md +85 -0
- package/baseline/skill/cloudflare/references/cron-triggers/api.md +198 -0
- package/baseline/skill/cloudflare/references/cron-triggers/configuration.md +151 -0
- package/baseline/skill/cloudflare/references/cron-triggers/gotchas.md +129 -0
- package/baseline/skill/cloudflare/references/cron-triggers/patterns.md +122 -0
- package/baseline/skill/cloudflare/references/d1/README.md +92 -0
- package/baseline/skill/cloudflare/references/d1/api.md +141 -0
- package/baseline/skill/cloudflare/references/d1/configuration.md +127 -0
- package/baseline/skill/cloudflare/references/d1/gotchas.md +70 -0
- package/baseline/skill/cloudflare/references/d1/patterns.md +144 -0
- package/baseline/skill/cloudflare/references/ddos/README.md +34 -0
- package/baseline/skill/cloudflare/references/ddos/api.md +136 -0
- package/baseline/skill/cloudflare/references/ddos/configuration.md +67 -0
- package/baseline/skill/cloudflare/references/ddos/gotchas.md +114 -0
- package/baseline/skill/cloudflare/references/ddos/patterns.md +158 -0
- package/baseline/skill/cloudflare/references/do-storage/README.md +62 -0
- package/baseline/skill/cloudflare/references/do-storage/api.md +89 -0
- package/baseline/skill/cloudflare/references/do-storage/configuration.md +116 -0
- package/baseline/skill/cloudflare/references/do-storage/gotchas.md +93 -0
- package/baseline/skill/cloudflare/references/do-storage/patterns.md +112 -0
- package/baseline/skill/cloudflare/references/durable-objects/README.md +125 -0
- package/baseline/skill/cloudflare/references/durable-objects/api.md +152 -0
- package/baseline/skill/cloudflare/references/durable-objects/configuration.md +148 -0
- package/baseline/skill/cloudflare/references/durable-objects/gotchas.md +158 -0
- package/baseline/skill/cloudflare/references/durable-objects/patterns.md +255 -0
- package/baseline/skill/cloudflare/references/email-routing/README.md +18 -0
- package/baseline/skill/cloudflare/references/email-routing/api.md +46 -0
- package/baseline/skill/cloudflare/references/email-routing/configuration.md +63 -0
- package/baseline/skill/cloudflare/references/email-routing/gotchas.md +16 -0
- package/baseline/skill/cloudflare/references/email-routing/patterns.md +46 -0
- package/baseline/skill/cloudflare/references/email-workers/README.md +598 -0
- package/baseline/skill/cloudflare/references/hyperdrive/README.md +62 -0
- package/baseline/skill/cloudflare/references/hyperdrive/api.md +137 -0
- package/baseline/skill/cloudflare/references/hyperdrive/configuration.md +133 -0
- package/baseline/skill/cloudflare/references/hyperdrive/gotchas.md +184 -0
- package/baseline/skill/cloudflare/references/hyperdrive/patterns.md +176 -0
- package/baseline/skill/cloudflare/references/images/README.md +14 -0
- package/baseline/skill/cloudflare/references/images/api.md +3 -0
- package/baseline/skill/cloudflare/references/images/configuration.md +45 -0
- package/baseline/skill/cloudflare/references/images/gotchas.md +23 -0
- package/baseline/skill/cloudflare/references/images/patterns.md +31 -0
- package/baseline/skill/cloudflare/references/kv/README.md +60 -0
- package/baseline/skill/cloudflare/references/kv/api.md +114 -0
- package/baseline/skill/cloudflare/references/kv/configuration.md +92 -0
- package/baseline/skill/cloudflare/references/kv/gotchas.md +117 -0
- package/baseline/skill/cloudflare/references/kv/patterns.md +139 -0
- package/baseline/skill/cloudflare/references/miniflare/README.md +64 -0
- package/baseline/skill/cloudflare/references/miniflare/api.md +144 -0
- package/baseline/skill/cloudflare/references/miniflare/configuration.md +203 -0
- package/baseline/skill/cloudflare/references/miniflare/gotchas.md +187 -0
- package/baseline/skill/cloudflare/references/miniflare/patterns.md +211 -0
- package/baseline/skill/cloudflare/references/network-interconnect/README.md +60 -0
- package/baseline/skill/cloudflare/references/network-interconnect/api.md +240 -0
- package/baseline/skill/cloudflare/references/network-interconnect/configuration.md +127 -0
- package/baseline/skill/cloudflare/references/network-interconnect/gotchas.md +171 -0
- package/baseline/skill/cloudflare/references/network-interconnect/patterns.md +171 -0
- package/baseline/skill/cloudflare/references/observability/README.md +18 -0
- package/baseline/skill/cloudflare/references/observability/api.md +51 -0
- package/baseline/skill/cloudflare/references/observability/configuration.md +60 -0
- package/baseline/skill/cloudflare/references/observability/gotchas.md +36 -0
- package/baseline/skill/cloudflare/references/observability/patterns.md +42 -0
- package/baseline/skill/cloudflare/references/pages/README.md +76 -0
- package/baseline/skill/cloudflare/references/pages/api.md +200 -0
- package/baseline/skill/cloudflare/references/pages/configuration.md +228 -0
- package/baseline/skill/cloudflare/references/pages/gotchas.md +161 -0
- package/baseline/skill/cloudflare/references/pages/patterns.md +145 -0
- package/baseline/skill/cloudflare/references/pages-functions/README.md +57 -0
- package/baseline/skill/cloudflare/references/pages-functions/api.md +201 -0
- package/baseline/skill/cloudflare/references/pages-functions/configuration.md +159 -0
- package/baseline/skill/cloudflare/references/pages-functions/gotchas.md +151 -0
- package/baseline/skill/cloudflare/references/pages-functions/patterns.md +190 -0
- package/baseline/skill/cloudflare/references/pipelines/README.md +664 -0
- package/baseline/skill/cloudflare/references/pulumi/README.md +107 -0
- package/baseline/skill/cloudflare/references/pulumi/api.md +194 -0
- package/baseline/skill/cloudflare/references/pulumi/configuration.md +216 -0
- package/baseline/skill/cloudflare/references/pulumi/gotchas.md +223 -0
- package/baseline/skill/cloudflare/references/pulumi/patterns.md +139 -0
- package/baseline/skill/cloudflare/references/queues/README.md +69 -0
- package/baseline/skill/cloudflare/references/queues/api.md +138 -0
- package/baseline/skill/cloudflare/references/queues/configuration.md +125 -0
- package/baseline/skill/cloudflare/references/queues/gotchas.md +112 -0
- package/baseline/skill/cloudflare/references/queues/patterns.md +155 -0
- package/baseline/skill/cloudflare/references/r2/README.md +61 -0
- package/baseline/skill/cloudflare/references/r2/api.md +127 -0
- package/baseline/skill/cloudflare/references/r2/configuration.md +76 -0
- package/baseline/skill/cloudflare/references/r2/gotchas.md +94 -0
- package/baseline/skill/cloudflare/references/r2/patterns.md +127 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/README.md +18 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/api.md +29 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/configuration.md +39 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/gotchas.md +20 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/patterns.md +46 -0
- package/baseline/skill/cloudflare/references/r2-sql/README.md +512 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/README.md +21 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/api.md +135 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/configuration.md +63 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/gotchas.md +75 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/patterns.md +102 -0
- package/baseline/skill/cloudflare/references/realtimekit/README.md +81 -0
- package/baseline/skill/cloudflare/references/realtimekit/api.md +164 -0
- package/baseline/skill/cloudflare/references/realtimekit/configuration.md +147 -0
- package/baseline/skill/cloudflare/references/realtimekit/gotchas.md +172 -0
- package/baseline/skill/cloudflare/references/realtimekit/patterns.md +155 -0
- package/baseline/skill/cloudflare/references/sandbox/README.md +90 -0
- package/baseline/skill/cloudflare/references/sandbox/api.md +178 -0
- package/baseline/skill/cloudflare/references/sandbox/configuration.md +131 -0
- package/baseline/skill/cloudflare/references/sandbox/gotchas.md +156 -0
- package/baseline/skill/cloudflare/references/sandbox/patterns.md +203 -0
- package/baseline/skill/cloudflare/references/secrets-store/README.md +58 -0
- package/baseline/skill/cloudflare/references/secrets-store/api.md +182 -0
- package/baseline/skill/cloudflare/references/secrets-store/configuration.md +140 -0
- package/baseline/skill/cloudflare/references/secrets-store/gotchas.md +129 -0
- package/baseline/skill/cloudflare/references/secrets-store/patterns.md +218 -0
- package/baseline/skill/cloudflare/references/smart-placement/README.md +91 -0
- package/baseline/skill/cloudflare/references/smart-placement/api.md +139 -0
- package/baseline/skill/cloudflare/references/smart-placement/configuration.md +129 -0
- package/baseline/skill/cloudflare/references/smart-placement/gotchas.md +87 -0
- package/baseline/skill/cloudflare/references/smart-placement/patterns.md +135 -0
- package/baseline/skill/cloudflare/references/snippets/README.md +15 -0
- package/baseline/skill/cloudflare/references/snippets/api.md +47 -0
- package/baseline/skill/cloudflare/references/snippets/configuration.md +33 -0
- package/baseline/skill/cloudflare/references/snippets/gotchas.md +21 -0
- package/baseline/skill/cloudflare/references/snippets/patterns.md +34 -0
- package/baseline/skill/cloudflare/references/spectrum/README.md +16 -0
- package/baseline/skill/cloudflare/references/spectrum/api.md +24 -0
- package/baseline/skill/cloudflare/references/spectrum/configuration.md +43 -0
- package/baseline/skill/cloudflare/references/spectrum/gotchas.md +42 -0
- package/baseline/skill/cloudflare/references/spectrum/patterns.md +40 -0
- package/baseline/skill/cloudflare/references/static-assets/README.md +14 -0
- package/baseline/skill/cloudflare/references/static-assets/api.md +3 -0
- package/baseline/skill/cloudflare/references/static-assets/configuration.md +47 -0
- package/baseline/skill/cloudflare/references/static-assets/gotchas.md +44 -0
- package/baseline/skill/cloudflare/references/static-assets/patterns.md +42 -0
- package/baseline/skill/cloudflare/references/stream/README.md +103 -0
- package/baseline/skill/cloudflare/references/stream/api.md +204 -0
- package/baseline/skill/cloudflare/references/stream/configuration.md +127 -0
- package/baseline/skill/cloudflare/references/stream/gotchas.md +131 -0
- package/baseline/skill/cloudflare/references/stream/patterns.md +152 -0
- package/baseline/skill/cloudflare/references/tail-workers/README.md +640 -0
- package/baseline/skill/cloudflare/references/terraform/README.md +76 -0
- package/baseline/skill/cloudflare/references/terraform/api.md +159 -0
- package/baseline/skill/cloudflare/references/terraform/configuration.md +156 -0
- package/baseline/skill/cloudflare/references/terraform/gotchas.md +207 -0
- package/baseline/skill/cloudflare/references/terraform/patterns.md +135 -0
- package/baseline/skill/cloudflare/references/tunnel/README.md +82 -0
- package/baseline/skill/cloudflare/references/tunnel/api.md +105 -0
- package/baseline/skill/cloudflare/references/tunnel/configuration.md +113 -0
- package/baseline/skill/cloudflare/references/tunnel/gotchas.md +115 -0
- package/baseline/skill/cloudflare/references/tunnel/patterns.md +157 -0
- package/baseline/skill/cloudflare/references/turn/README.md +699 -0
- package/baseline/skill/cloudflare/references/turnstile/README.md +14 -0
- package/baseline/skill/cloudflare/references/turnstile/api.md +3 -0
- package/baseline/skill/cloudflare/references/turnstile/configuration.md +19 -0
- package/baseline/skill/cloudflare/references/turnstile/gotchas.md +27 -0
- package/baseline/skill/cloudflare/references/turnstile/patterns.md +41 -0
- package/baseline/skill/cloudflare/references/vectorize/README.md +682 -0
- package/baseline/skill/cloudflare/references/waf/README.md +14 -0
- package/baseline/skill/cloudflare/references/waf/api.md +3 -0
- package/baseline/skill/cloudflare/references/waf/configuration.md +44 -0
- package/baseline/skill/cloudflare/references/waf/gotchas.md +24 -0
- package/baseline/skill/cloudflare/references/waf/patterns.md +29 -0
- package/baseline/skill/cloudflare/references/web-analytics/README.md +19 -0
- package/baseline/skill/cloudflare/references/web-analytics/api.md +52 -0
- package/baseline/skill/cloudflare/references/web-analytics/configuration.md +31 -0
- package/baseline/skill/cloudflare/references/web-analytics/gotchas.md +28 -0
- package/baseline/skill/cloudflare/references/web-analytics/patterns.md +52 -0
- package/baseline/skill/cloudflare/references/workerd/README.md +47 -0
- package/baseline/skill/cloudflare/references/workerd/api.md +199 -0
- package/baseline/skill/cloudflare/references/workerd/configuration.md +185 -0
- package/baseline/skill/cloudflare/references/workerd/gotchas.md +203 -0
- package/baseline/skill/cloudflare/references/workerd/patterns.md +216 -0
- package/baseline/skill/cloudflare/references/workers/README.md +96 -0
- package/baseline/skill/cloudflare/references/workers/api.md +137 -0
- package/baseline/skill/cloudflare/references/workers/configuration.md +147 -0
- package/baseline/skill/cloudflare/references/workers/gotchas.md +99 -0
- package/baseline/skill/cloudflare/references/workers/patterns.md +149 -0
- package/baseline/skill/cloudflare/references/workers-ai/README.md +116 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/README.md +48 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/api.md +169 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/configuration.md +136 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/gotchas.md +130 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/patterns.md +170 -0
- package/baseline/skill/cloudflare/references/workers-playground/README.md +16 -0
- package/baseline/skill/cloudflare/references/workers-playground/api.md +20 -0
- package/baseline/skill/cloudflare/references/workers-playground/configuration.md +3 -0
- package/baseline/skill/cloudflare/references/workers-playground/gotchas.md +35 -0
- package/baseline/skill/cloudflare/references/workers-playground/patterns.md +42 -0
- package/baseline/skill/cloudflare/references/workers-vpc/README.md +579 -0
- package/baseline/skill/cloudflare/references/workflows/README.md +62 -0
- package/baseline/skill/cloudflare/references/workflows/api.md +125 -0
- package/baseline/skill/cloudflare/references/workflows/configuration.md +177 -0
- package/baseline/skill/cloudflare/references/workflows/gotchas.md +136 -0
- package/baseline/skill/cloudflare/references/workflows/patterns.md +132 -0
- package/baseline/skill/cloudflare/references/wrangler/README.md +90 -0
- package/baseline/skill/cloudflare/references/wrangler/api.md +140 -0
- package/baseline/skill/cloudflare/references/wrangler/configuration.md +128 -0
- package/baseline/skill/cloudflare/references/wrangler/gotchas.md +93 -0
- package/baseline/skill/cloudflare/references/wrangler/patterns.md +150 -0
- package/baseline/skill/cloudflare/references/zaraz/README.md +360 -0
- package/baseline/skill/code-navigation/SKILL.md +130 -0
- package/baseline/skill/compaction/SKILL.md +317 -0
- package/baseline/skill/condition-based-waiting/SKILL.md +123 -0
- package/baseline/skill/condition-based-waiting/example.ts +158 -0
- package/baseline/skill/context-engineering/SKILL.md +176 -0
- package/baseline/skill/context-initialization/SKILL.md +70 -0
- package/baseline/skill/context-management/SKILL.md +163 -0
- package/baseline/skill/core-data-expert/SKILL.md +93 -0
- package/baseline/skill/core-data-expert/references/batch-operations.md +543 -0
- package/baseline/skill/core-data-expert/references/cloudkit-integration.md +259 -0
- package/baseline/skill/core-data-expert/references/concurrency.md +522 -0
- package/baseline/skill/core-data-expert/references/fetch-requests.md +643 -0
- package/baseline/skill/core-data-expert/references/glossary.md +233 -0
- package/baseline/skill/core-data-expert/references/migration.md +393 -0
- package/baseline/skill/core-data-expert/references/model-configuration.md +597 -0
- package/baseline/skill/core-data-expert/references/performance.md +300 -0
- package/baseline/skill/core-data-expert/references/persistent-history.md +553 -0
- package/baseline/skill/core-data-expert/references/project-audit.md +60 -0
- package/baseline/skill/core-data-expert/references/saving.md +574 -0
- package/baseline/skill/core-data-expert/references/stack-setup.md +625 -0
- package/baseline/skill/core-data-expert/references/testing.md +300 -0
- package/baseline/skill/core-data-expert/references/threading.md +589 -0
- package/baseline/skill/deep-research/SKILL.md +384 -0
- package/baseline/skill/defense-in-depth/SKILL.md +166 -0
- package/baseline/skill/design-system-audit/SKILL.md +153 -0
- package/baseline/skill/development-lifecycle/SKILL.md +356 -0
- package/baseline/skill/dispatching-parallel-agents/SKILL.md +191 -0
- package/baseline/skill/executing-plans/SKILL.md +247 -0
- package/baseline/skill/figma/SKILL.md +224 -0
- package/baseline/skill/finishing-a-development-branch/SKILL.md +357 -0
- package/baseline/skill/frontend-design/SKILL.md +235 -0
- package/baseline/skill/frontend-design/references/animation/motion-advanced.md +224 -0
- package/baseline/skill/frontend-design/references/animation/motion-core.md +181 -0
- package/baseline/skill/frontend-design/references/canvas/execution.md +90 -0
- package/baseline/skill/frontend-design/references/canvas/philosophy.md +94 -0
- package/baseline/skill/frontend-design/references/design/color-system.md +111 -0
- package/baseline/skill/frontend-design/references/design/interaction.md +149 -0
- package/baseline/skill/frontend-design/references/design/typography-rules.md +106 -0
- package/baseline/skill/frontend-design/references/design/ux-writing.md +99 -0
- package/baseline/skill/frontend-design/references/shadcn/accessibility.md +132 -0
- package/baseline/skill/frontend-design/references/shadcn/core-components.md +153 -0
- package/baseline/skill/frontend-design/references/shadcn/form-components.md +158 -0
- package/baseline/skill/frontend-design/references/shadcn/setup.md +69 -0
- package/baseline/skill/frontend-design/references/shadcn/theming.md +152 -0
- package/baseline/skill/frontend-design/references/tailwind/responsive.md +112 -0
- package/baseline/skill/frontend-design/references/tailwind/utilities-layout.md +134 -0
- package/baseline/skill/frontend-design/references/tailwind/utilities-styling.md +165 -0
- package/baseline/skill/frontend-design/references/tailwind/v4-config.md +147 -0
- package/baseline/skill/frontend-design/references/tailwind/v4-features.md +128 -0
- package/baseline/skill/gemini-large-context/SKILL.md +216 -0
- package/baseline/skill/index-knowledge/SKILL.md +413 -0
- package/baseline/skill/jira/SKILL.md +283 -0
- package/baseline/skill/jira/mcp.json +6 -0
- package/baseline/skill/memory-system/SKILL.md +84 -0
- package/baseline/skill/mockup-to-code/SKILL.md +184 -0
- package/baseline/skill/mqdh/SKILL.md +171 -0
- package/baseline/skill/obsidian/SKILL.md +192 -0
- package/baseline/skill/obsidian/mcp.json +22 -0
- package/baseline/skill/opensrc/SKILL.md +127 -0
- package/baseline/skill/opensrc/references/architecture.md +176 -0
- package/baseline/skill/opensrc/references/cli-usage.md +176 -0
- package/baseline/skill/opensrc/references/registry-support.md +137 -0
- package/baseline/skill/pdf-extract/SKILL.md +438 -0
- package/baseline/skill/playwright/SKILL.md +320 -0
- package/baseline/skill/playwright/mcp.json +16 -0
- package/baseline/skill/playwriter/SKILL.md +158 -0
- package/baseline/skill/polar/SKILL.md +102 -0
- package/baseline/skill/prd/SKILL.md +146 -0
- package/baseline/skill/prd-task/SKILL.md +182 -0
- package/baseline/skill/prd-task/references/prd-schema.json +124 -0
- package/baseline/skill/ralph/SKILL.md +296 -0
- package/baseline/skill/react-best-practices/AGENTS.md +2410 -0
- package/baseline/skill/react-best-practices/README.md +123 -0
- package/baseline/skill/react-best-practices/SKILL.md +133 -0
- package/baseline/skill/react-best-practices/metadata.json +15 -0
- package/baseline/skill/react-best-practices/rules/_sections.md +46 -0
- package/baseline/skill/react-best-practices/rules/_template.md +28 -0
- package/baseline/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/baseline/skill/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/baseline/skill/react-best-practices/rules/async-api-routes.md +38 -0
- package/baseline/skill/react-best-practices/rules/async-defer-await.md +80 -0
- package/baseline/skill/react-best-practices/rules/async-dependencies.md +36 -0
- package/baseline/skill/react-best-practices/rules/async-parallel.md +28 -0
- package/baseline/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/baseline/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/baseline/skill/react-best-practices/rules/bundle-conditional.md +31 -0
- package/baseline/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/baseline/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/baseline/skill/react-best-practices/rules/bundle-preload.md +50 -0
- package/baseline/skill/react-best-practices/rules/client-event-listeners.md +74 -0
- package/baseline/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/baseline/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/baseline/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/baseline/skill/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/baseline/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/baseline/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/baseline/skill/react-best-practices/rules/js-cache-storage.md +70 -0
- package/baseline/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/baseline/skill/react-best-practices/rules/js-early-exit.md +50 -0
- package/baseline/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/baseline/skill/react-best-practices/rules/js-index-maps.md +37 -0
- package/baseline/skill/react-best-practices/rules/js-length-check-first.md +49 -0
- package/baseline/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/baseline/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/baseline/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/baseline/skill/react-best-practices/rules/rendering-activity.md +26 -0
- package/baseline/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/baseline/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/baseline/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/baseline/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/baseline/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/baseline/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/baseline/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/baseline/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/baseline/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/baseline/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/baseline/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/baseline/skill/react-best-practices/rules/rerender-memo.md +44 -0
- package/baseline/skill/react-best-practices/rules/rerender-transitions.md +40 -0
- package/baseline/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/baseline/skill/react-best-practices/rules/server-cache-lru.md +41 -0
- package/baseline/skill/react-best-practices/rules/server-cache-react.md +76 -0
- package/baseline/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/baseline/skill/react-best-practices/rules/server-serialization.md +38 -0
- package/baseline/skill/receiving-code-review/SKILL.md +252 -0
- package/baseline/skill/requesting-code-review/SKILL.md +397 -0
- package/baseline/skill/requesting-code-review/review.md +160 -0
- package/baseline/skill/resend/SKILL.md +177 -0
- package/baseline/skill/resend/references/react-email.md +287 -0
- package/baseline/skill/resend/references/receive-email.md +248 -0
- package/baseline/skill/resend/references/send-email.md +318 -0
- package/baseline/skill/root-cause-tracing/SKILL.md +192 -0
- package/baseline/skill/root-cause-tracing/find-polluter.sh +63 -0
- package/baseline/skill/session-management/SKILL.md +9 -0
- package/baseline/skill/sharing-skills/SKILL.md +214 -0
- package/baseline/skill/skill-creator/SKILL.md +156 -0
- package/baseline/skill/source-code-research/SKILL.md +293 -0
- package/baseline/skill/source-code-research/references/analysis-tips.md +43 -0
- package/baseline/skill/source-code-research/references/anti-patterns.md +36 -0
- package/baseline/skill/source-code-research/references/common-patterns.md +57 -0
- package/baseline/skill/source-code-research/references/example-workflow.md +60 -0
- package/baseline/skill/source-code-research/references/further-reading.md +5 -0
- package/baseline/skill/source-code-research/references/source-structure.md +45 -0
- package/baseline/skill/stitch/SKILL.md +147 -0
- package/baseline/skill/stitch/mcp.json +9 -0
- package/baseline/skill/structured-edit/SKILL.md +181 -0
- package/baseline/skill/subagent-driven-development/SKILL.md +237 -0
- package/baseline/skill/supabase/SKILL.md +130 -0
- package/baseline/skill/supabase/mcp.json +27 -0
- package/baseline/skill/supabase-postgres-best-practices/AGENTS.md +1490 -0
- package/baseline/skill/supabase-postgres-best-practices/SKILL.md +65 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/advanced-full-text-search.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/advanced-jsonb-indexing.md +49 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-idle-timeout.md +46 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-limits.md +44 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-pooling.md +41 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-prepared-statements.md +46 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-batch-inserts.md +54 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-n-plus-one.md +53 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-pagination.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-upsert.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-advisory.md +56 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-deadlock-prevention.md +68 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-short-transactions.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-skip-locked.md +54 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/monitor-explain-analyze.md +45 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/monitor-pg-stat-statements.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/monitor-vacuum-analyze.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-composite-indexes.md +44 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-covering-indexes.md +40 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-index-types.md +45 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-missing-indexes.md +43 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-partial-indexes.md +45 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-data-types.md +46 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-foreign-key-indexes.md +59 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-lowercase-identifiers.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-partitioning.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-primary-keys.md +61 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/security-privileges.md +54 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/security-rls-basics.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/security-rls-performance.md +57 -0
- package/baseline/skill/swarm-coordination/SKILL.md +179 -0
- package/baseline/skill/swarm-coordination/references/architecture.md +39 -0
- package/baseline/skill/swarm-coordination/references/delegation-worker-protocol.md +145 -0
- package/baseline/skill/swarm-coordination/references/dependency-graph.md +50 -0
- package/baseline/skill/swarm-coordination/references/drift-check.md +90 -0
- package/baseline/skill/swarm-coordination/references/integration-beads.md +20 -0
- package/baseline/skill/swarm-coordination/references/launch-flow.md +186 -0
- package/baseline/skill/swarm-coordination/references/reconciler.md +172 -0
- package/baseline/skill/swarm-coordination/references/tier-enforcement.md +78 -0
- package/baseline/skill/swarm-coordination/references/tmux-integration.md +134 -0
- package/baseline/skill/swift-concurrency/SKILL.md +266 -0
- package/baseline/skill/swift-concurrency/references/actors.md +640 -0
- package/baseline/skill/swift-concurrency/references/async-algorithms.md +822 -0
- package/baseline/skill/swift-concurrency/references/async-await-basics.md +249 -0
- package/baseline/skill/swift-concurrency/references/async-sequences.md +670 -0
- package/baseline/skill/swift-concurrency/references/core-data.md +533 -0
- package/baseline/skill/swift-concurrency/references/glossary.md +128 -0
- package/baseline/skill/swift-concurrency/references/linting.md +142 -0
- package/baseline/skill/swift-concurrency/references/memory-management.md +542 -0
- package/baseline/skill/swift-concurrency/references/migration.md +1076 -0
- package/baseline/skill/swift-concurrency/references/performance.md +574 -0
- package/baseline/skill/swift-concurrency/references/sendable.md +578 -0
- package/baseline/skill/swift-concurrency/references/tasks.md +604 -0
- package/baseline/skill/swift-concurrency/references/testing.md +565 -0
- package/baseline/skill/swift-concurrency/references/threading.md +452 -0
- package/baseline/skill/swiftui-expert-skill/SKILL.md +329 -0
- package/baseline/skill/swiftui-expert-skill/references/animation-advanced.md +351 -0
- package/baseline/skill/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/baseline/skill/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/baseline/skill/swiftui-expert-skill/references/image-optimization.md +286 -0
- package/baseline/skill/swiftui-expert-skill/references/layout-best-practices.md +312 -0
- package/baseline/skill/swiftui-expert-skill/references/liquid-glass.md +377 -0
- package/baseline/skill/swiftui-expert-skill/references/list-patterns.md +153 -0
- package/baseline/skill/swiftui-expert-skill/references/modern-apis.md +400 -0
- package/baseline/skill/swiftui-expert-skill/references/performance-patterns.md +377 -0
- package/baseline/skill/swiftui-expert-skill/references/scroll-patterns.md +305 -0
- package/baseline/skill/swiftui-expert-skill/references/sheet-navigation-patterns.md +292 -0
- package/baseline/skill/swiftui-expert-skill/references/state-management.md +447 -0
- package/baseline/skill/swiftui-expert-skill/references/text-formatting.md +285 -0
- package/baseline/skill/swiftui-expert-skill/references/view-structure.md +276 -0
- package/baseline/skill/systematic-debugging/SKILL.md +402 -0
- package/baseline/skill/test-driven-development/SKILL.md +388 -0
- package/baseline/skill/testing-anti-patterns/SKILL.md +333 -0
- package/baseline/skill/testing-skills-with-subagents/SKILL.md +405 -0
- package/baseline/skill/tilth-cli/SKILL.md +180 -0
- package/baseline/skill/tool-priority/SKILL.md +299 -0
- package/baseline/skill/ui-ux-research/SKILL.md +9 -0
- package/baseline/skill/using-git-worktrees/SKILL.md +259 -0
- package/baseline/skill/using-skills/SKILL.md +117 -0
- package/baseline/skill/v0/SKILL.md +158 -0
- package/baseline/skill/v1-run/SKILL.md +175 -0
- package/baseline/skill/v1-run/mcp.json +6 -0
- package/baseline/skill/vercel-deploy-claimable/SKILL.md +124 -0
- package/baseline/skill/vercel-deploy-claimable/scripts/deploy.sh +249 -0
- package/baseline/skill/verification-before-completion/SKILL.md +236 -0
- package/baseline/skill/verification-before-completion/references/VERIFICATION_PROTOCOL.md +171 -0
- package/baseline/skill/visual-analysis/SKILL.md +154 -0
- package/baseline/skill/web-design-guidelines/SKILL.md +46 -0
- package/baseline/skill/writing-plans/SKILL.md +320 -0
- package/baseline/skill/writing-skills/SKILL.md +287 -0
- package/baseline/skill/writing-skills/anthropic-best-practices.md +1173 -0
- package/baseline/skill/writing-skills/graphviz-conventions.dot +172 -0
- package/baseline/skill/writing-skills/persuasion-principles.md +220 -0
- package/baseline/skill/writing-skills/references/anti-patterns.md +25 -0
- package/baseline/skill/writing-skills/references/claude-search-optimization.md +140 -0
- package/baseline/skill/writing-skills/references/discovery-workflow.md +11 -0
- package/baseline/skill/writing-skills/references/file-organization.md +32 -0
- package/baseline/skill/writing-skills/references/flowcharts-and-examples.md +57 -0
- package/baseline/skill/writing-skills/references/rationalization-hardening.md +75 -0
- package/baseline/skill/writing-skills/references/testing-skill-types.md +52 -0
- package/baseline/tool/context7.ts +191 -0
- package/baseline/tool/grepsearch.ts +143 -0
- package/baseline/tsconfig.json +21 -0
- package/baseline/tui.json +15 -0
- package/bin/mrc-opc.mjs +25 -0
- package/bin/ocp.mjs +25 -0
- package/bin/opc.mjs +25 -0
- package/bin/opencode-starterkit.mjs +25 -0
- package/docs/reports/2026-03-18-memory-db-architecture-note.md +47 -0
- package/docs/reports/2026-03-18-vat-opencode-classification-matrix.md +180 -0
- package/package.json +25 -0
- package/src/cli.mjs +41 -0
- package/src/config-merge.mjs +61 -0
- package/src/constants.mjs +58 -0
- package/src/fs-utils.mjs +46 -0
- package/src/install-global.mjs +102 -0
- package/src/install-project.mjs +81 -0
- package/src/memory-bootstrap.mjs +29 -0
- package/src/prompt.mjs +31 -0
- package/src/templates.mjs +31 -0
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
# Migration to Swift 6 and Strict Concurrency
|
|
2
|
+
|
|
3
|
+
A practical guide to migrating existing Swift codebases to Swift 6's strict concurrency model, including strategies, habits, tooling, and common patterns.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why Migrate to Swift 6?
|
|
8
|
+
|
|
9
|
+
Swift 6 doesn't fundamentally change how Swift Concurrency works—it **enforces existing rules more strictly**:
|
|
10
|
+
|
|
11
|
+
- **Compile-time safety**: Catches data races and threading issues at compile time instead of runtime
|
|
12
|
+
- **Warnings become errors**: Many Swift 5 warnings become hard errors in Swift 6 language mode
|
|
13
|
+
- **Future-proofing**: New concurrency features will build on this stricter foundation
|
|
14
|
+
- **Better maintainability**: Code becomes safer and easier to reason about
|
|
15
|
+
|
|
16
|
+
> **Important**: You can adopt strict concurrency checking gradually while still compiling under Swift 5. You don't need to flip the Swift 6 switch immediately.
|
|
17
|
+
|
|
18
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.2: The impact of Swift 6 on Swift Concurrency](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Project Settings That Change Concurrency Behavior
|
|
23
|
+
|
|
24
|
+
Before interpreting diagnostics or choosing a fix, confirm the target/module settings. These settings can materially change how code executes and what the compiler enforces.
|
|
25
|
+
|
|
26
|
+
### Quick matrix
|
|
27
|
+
|
|
28
|
+
| Setting / feature | Where to check | Why it matters |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Swift language mode (Swift 5.x vs Swift 6) | Xcode build settings (`SWIFT_VERSION`) / SwiftPM `// swift-tools-version:` | Swift 6 turns many warnings into errors and enables stricter defaults. |
|
|
31
|
+
| Strict concurrency checking | Xcode: Strict Concurrency Checking (`SWIFT_STRICT_CONCURRENCY`) / SwiftPM: strict concurrency flags | Controls how aggressively Sendable + isolation rules are enforced. |
|
|
32
|
+
| Default actor isolation | Xcode: Default Actor Isolation (`SWIFT_DEFAULT_ACTOR_ISOLATION`) / SwiftPM: `.defaultIsolation(MainActor.self)` | Changes the default isolation of declarations; can reduce migration noise but changes behavior and requirements. |
|
|
33
|
+
| `NonisolatedNonsendingByDefault` | Xcode upcoming feature / SwiftPM `.enableUpcomingFeature("NonisolatedNonsendingByDefault")` | Changes how nonisolated async functions execute (can inherit the caller’s actor unless explicitly marked `@concurrent`). |
|
|
34
|
+
| Approachable Concurrency | Xcode build setting / SwiftPM enables the underlying upcoming features | Bundles multiple upcoming features; recommended to migrate feature-by-feature first. |
|
|
35
|
+
|
|
36
|
+
## The Concurrency Rabbit Hole
|
|
37
|
+
|
|
38
|
+
A common migration experience:
|
|
39
|
+
|
|
40
|
+
1. Enable strict concurrency checking
|
|
41
|
+
2. See 50+ errors and warnings
|
|
42
|
+
3. Fix a bunch of them
|
|
43
|
+
4. Rebuild and see 80+ new errors appear
|
|
44
|
+
|
|
45
|
+
**Why this happens**: Fixing isolation in one place often exposes issues elsewhere. This is normal and manageable with the right strategy.
|
|
46
|
+
|
|
47
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.1: Challenges in migrating to Swift Concurrency](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Six Migration Habits for Success
|
|
52
|
+
|
|
53
|
+
### 1. Don't Panic—It's All About Iterations
|
|
54
|
+
|
|
55
|
+
Break migration into small, manageable chunks:
|
|
56
|
+
|
|
57
|
+
```swift
|
|
58
|
+
// Day 1: Enable strict concurrency, fix a few warnings
|
|
59
|
+
// Build Settings → Strict Concurrency Checking = Complete
|
|
60
|
+
|
|
61
|
+
// Day 2: Fix more warnings
|
|
62
|
+
|
|
63
|
+
// Day 3: Revert to minimal checking if needed
|
|
64
|
+
// Build Settings → Strict Concurrency Checking = Minimal
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Allow yourself 30 minutes per day to migrate gradually. Don't expect completion in a few days for large projects.
|
|
68
|
+
|
|
69
|
+
### 2. Sendable by Default for New Code
|
|
70
|
+
|
|
71
|
+
When writing new types, make them `Sendable` from the start:
|
|
72
|
+
|
|
73
|
+
```swift
|
|
74
|
+
// ✅ Good: New code prepared for Swift 6
|
|
75
|
+
struct UserProfile: Sendable {
|
|
76
|
+
let id: UUID
|
|
77
|
+
let name: String
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ❌ Avoid: Creating technical debt
|
|
81
|
+
class UserProfile { // Will need migration later
|
|
82
|
+
var id: UUID
|
|
83
|
+
var name: String
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
It's easier to design for concurrency upfront than to retrofit it later.
|
|
88
|
+
|
|
89
|
+
### 3. Use Swift 6 for New Projects and Packages
|
|
90
|
+
|
|
91
|
+
For new projects, packages, or files:
|
|
92
|
+
- Enable Swift 6 language mode from the start
|
|
93
|
+
- Use Swift Concurrency features (async/await, actors)
|
|
94
|
+
- Reduce technical debt before it accumulates
|
|
95
|
+
|
|
96
|
+
You can enable Swift 6 for individual files in a Swift 5 project to prevent scope creep.
|
|
97
|
+
|
|
98
|
+
### 4. Resist the Urge to Refactor
|
|
99
|
+
|
|
100
|
+
**Focus solely on concurrency changes**. Don't combine migration with:
|
|
101
|
+
- Architecture refactors
|
|
102
|
+
- API modernization
|
|
103
|
+
- Code style improvements
|
|
104
|
+
|
|
105
|
+
Create separate tickets for non-concurrency refactors and address them later.
|
|
106
|
+
|
|
107
|
+
### 5. Focus on Minimal Changes
|
|
108
|
+
|
|
109
|
+
- Make small, focused pull requests
|
|
110
|
+
- Migrate one class or module at a time
|
|
111
|
+
- Get changes merged quickly to create checkpoints
|
|
112
|
+
- Avoid large PRs that are hard to review
|
|
113
|
+
|
|
114
|
+
### 6. Don't Just @MainActor All the Things
|
|
115
|
+
|
|
116
|
+
Don't blindly add `@MainActor` to fix warnings. Consider:
|
|
117
|
+
- Should this actually run on the main actor?
|
|
118
|
+
- Would a custom actor be more appropriate?
|
|
119
|
+
- Is `nonisolated` the right choice?
|
|
120
|
+
|
|
121
|
+
**Exception**: For app projects (not frameworks), consider enabling **Default Actor Isolation** to `@MainActor`, since most app code needs main thread access.
|
|
122
|
+
|
|
123
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.3: The six migration habits for a successful migration](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Step-by-Step Migration Process
|
|
128
|
+
|
|
129
|
+
### 1. Find an Isolated Piece of Code
|
|
130
|
+
|
|
131
|
+
Start with:
|
|
132
|
+
- Standalone packages with minimal dependencies
|
|
133
|
+
- Individual Swift files within a package
|
|
134
|
+
- Code that's not heavily used throughout the project
|
|
135
|
+
|
|
136
|
+
**Why**: Fewer dependencies = less risk of falling into the concurrency rabbit hole.
|
|
137
|
+
|
|
138
|
+
### 2. Update Related Dependencies
|
|
139
|
+
|
|
140
|
+
Before enabling strict concurrency:
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
// Update third-party packages to latest versions
|
|
144
|
+
// Example: Vapor, Alamofire, etc.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Apply these updates in a separate PR before proceeding with concurrency changes.
|
|
148
|
+
|
|
149
|
+
### 3. Add Async Alternatives
|
|
150
|
+
|
|
151
|
+
Provide async/await wrappers for existing closure-based APIs:
|
|
152
|
+
|
|
153
|
+
```swift
|
|
154
|
+
// Original closure-based API
|
|
155
|
+
@available(*, deprecated, renamed: "fetchImage(urlRequest:)",
|
|
156
|
+
message: "Consider using the async/await alternative.")
|
|
157
|
+
func fetchImage(urlRequest: URLRequest,
|
|
158
|
+
completion: @escaping @Sendable (Result<UIImage, Error>) -> Void) {
|
|
159
|
+
// ... existing implementation
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// New async wrapper
|
|
163
|
+
func fetchImage(urlRequest: URLRequest) async throws -> UIImage {
|
|
164
|
+
return try await withCheckedThrowingContinuation { continuation in
|
|
165
|
+
fetchImage(urlRequest: urlRequest) { result in
|
|
166
|
+
continuation.resume(with: result)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Benefits**:
|
|
173
|
+
- Colleagues can start using async/await immediately
|
|
174
|
+
- You can migrate callers before rewriting implementation
|
|
175
|
+
- Tests can be updated to async/await first
|
|
176
|
+
|
|
177
|
+
**Tip**: Use Xcode's **Refactor → Add Async Wrapper** to generate these automatically.
|
|
178
|
+
|
|
179
|
+
### 4. Change Default Actor Isolation (Swift 6.2+)
|
|
180
|
+
|
|
181
|
+
For app projects, set default isolation to `@MainActor`:
|
|
182
|
+
|
|
183
|
+
**Xcode Build Settings**:
|
|
184
|
+
```
|
|
185
|
+
Swift Concurrency → Default Actor Isolation = MainActor
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Swift Package Manager**:
|
|
189
|
+
```swift
|
|
190
|
+
.target(
|
|
191
|
+
name: "MyTarget",
|
|
192
|
+
swiftSettings: [
|
|
193
|
+
.defaultIsolation(MainActor.self)
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This drastically reduces warnings in app code where most types need main thread access.
|
|
199
|
+
|
|
200
|
+
### 5. Enable Strict Concurrency Checking
|
|
201
|
+
|
|
202
|
+
**Xcode Build Settings**: Search for "Strict Concurrency Checking"
|
|
203
|
+
|
|
204
|
+
Three levels available:
|
|
205
|
+
|
|
206
|
+
- **Minimal**: Only checks code that explicitly adopts concurrency (`@Sendable`, `@MainActor`)
|
|
207
|
+
- **Targeted**: Checks all code that adopts concurrency, including `Sendable` conformances
|
|
208
|
+
- **Complete**: Checks entire codebase (matches Swift 6 behavior)
|
|
209
|
+
|
|
210
|
+
**Swift Package Manager**:
|
|
211
|
+
```swift
|
|
212
|
+
.target(
|
|
213
|
+
name: "MyTarget",
|
|
214
|
+
swiftSettings: [
|
|
215
|
+
.enableExperimentalFeature("StrictConcurrency=targeted")
|
|
216
|
+
]
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Strategy**: Start with Minimal → Targeted → Complete, fixing errors at each level.
|
|
221
|
+
|
|
222
|
+
### 6. Add Sendable Conformances
|
|
223
|
+
|
|
224
|
+
Even if the compiler doesn't complain, add `Sendable` to types that will cross isolation domains:
|
|
225
|
+
|
|
226
|
+
```swift
|
|
227
|
+
// ✅ Prepare for future use
|
|
228
|
+
struct Configuration: Sendable {
|
|
229
|
+
let apiKey: String
|
|
230
|
+
let timeout: TimeInterval
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This prevents warnings when the type is used in concurrent contexts later.
|
|
235
|
+
|
|
236
|
+
### 7. Enable Approachable Concurrency (Swift 6.2+)
|
|
237
|
+
|
|
238
|
+
**Xcode Build Settings**: Search for "Approachable Concurrency"
|
|
239
|
+
|
|
240
|
+
Enables multiple upcoming features at once:
|
|
241
|
+
- `DisableOutwardActorInference`
|
|
242
|
+
- `GlobalActorIsolatedTypesUsability`
|
|
243
|
+
- `InferIsolatedConformances`
|
|
244
|
+
- `InferSendableFromCaptures`
|
|
245
|
+
- `NonisolatedNonsendingByDefault`
|
|
246
|
+
|
|
247
|
+
**⚠️ Warning**: Don't just flip this switch for existing projects. Use migration tooling (see below) to migrate to each feature individually first.
|
|
248
|
+
|
|
249
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.5: The Approachable Concurrency build setting (Updated for Swift 6.2)](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
250
|
+
|
|
251
|
+
### 8. Enable Upcoming Features
|
|
252
|
+
|
|
253
|
+
**Xcode Build Settings**: Search for "Upcoming Feature"
|
|
254
|
+
|
|
255
|
+
Enable features individually:
|
|
256
|
+
|
|
257
|
+
**Swift Package Manager**:
|
|
258
|
+
```swift
|
|
259
|
+
.target(
|
|
260
|
+
name: "MyTarget",
|
|
261
|
+
swiftSettings: [
|
|
262
|
+
.enableUpcomingFeature("ExistentialAny"),
|
|
263
|
+
.enableUpcomingFeature("InferIsolatedConformances")
|
|
264
|
+
]
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Find feature keys in Swift Evolution proposals (e.g., SE-335 for `ExistentialAny`).
|
|
269
|
+
|
|
270
|
+
### 9. Change to Swift 6 Language Mode
|
|
271
|
+
|
|
272
|
+
**Xcode Build Settings**:
|
|
273
|
+
```
|
|
274
|
+
Swift Language Version = Swift 6
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Swift Package Manager**:
|
|
278
|
+
```swift
|
|
279
|
+
// swift-tools-version: 6.0
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
If you've completed all previous steps, you should have minimal new errors.
|
|
283
|
+
|
|
284
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.4: Steps to migrate existing code to Swift 6 and Strict Concurrency Checking](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Migration Tooling for Upcoming Features
|
|
289
|
+
|
|
290
|
+
Swift 6.2+ includes **semi-automatic migration** for upcoming features.
|
|
291
|
+
|
|
292
|
+
### Xcode Migration
|
|
293
|
+
|
|
294
|
+
1. Go to Build Settings → Find the upcoming feature (e.g., "Require Existential any")
|
|
295
|
+
2. Set to **Migrate** (temporary setting)
|
|
296
|
+
3. Build the project
|
|
297
|
+
4. Warnings appear with **Apply** buttons
|
|
298
|
+
5. Click Apply for each warning
|
|
299
|
+
|
|
300
|
+
**Example warning**:
|
|
301
|
+
```swift
|
|
302
|
+
// ⚠️ Use of protocol 'Error' as a type must be written 'any Error'
|
|
303
|
+
func fetchData() throws -> Data // Before
|
|
304
|
+
func fetchData() throws -> any Data // After applying fix
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Package Migration
|
|
308
|
+
|
|
309
|
+
Use the `swift package migrate` command:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
# Migrate all targets
|
|
313
|
+
swift package migrate --to-feature ExistentialAny
|
|
314
|
+
|
|
315
|
+
# Migrate specific target
|
|
316
|
+
swift package migrate --target MyTarget --to-feature ExistentialAny
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Output**:
|
|
320
|
+
```
|
|
321
|
+
> Applied 24 fix-its in 11 files (0.016s)
|
|
322
|
+
> Updating manifest
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The tool automatically:
|
|
326
|
+
- Applies all fix-its
|
|
327
|
+
- Updates `Package.swift` to enable the feature
|
|
328
|
+
|
|
329
|
+
**Available migrations** (as of Swift 6.2):
|
|
330
|
+
- `ExistentialAny` (SE-335)
|
|
331
|
+
- `InferIsolatedConformances` (SE-470)
|
|
332
|
+
- More features will add migration support over time
|
|
333
|
+
|
|
334
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.6: Migration tooling for upcoming Swift features](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
335
|
+
|
|
336
|
+
**Additional resource**: [Migration Tooling Video](https://youtu.be/FK9XFxSWZPg?si=2z_ybn1t1YCJow5k)
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Rewriting Closures to Async/Await
|
|
341
|
+
|
|
342
|
+
### Using Xcode Refactoring
|
|
343
|
+
|
|
344
|
+
Three refactoring options available:
|
|
345
|
+
|
|
346
|
+
1. **Add Async Wrapper**: Wraps existing closure-based method (recommended first step)
|
|
347
|
+
2. **Add Async Alternative**: Rewrites method as async, keeps original
|
|
348
|
+
3. **Convert Function to Async**: Replaces method entirely
|
|
349
|
+
|
|
350
|
+
**⚠️ Known Issue**: Refactoring can be unstable in Xcode. If you get "Connection interrupted" errors:
|
|
351
|
+
- Clean build folder
|
|
352
|
+
- Clear derived data
|
|
353
|
+
- Restart Xcode
|
|
354
|
+
- Simplify complex methods (shorthand if statements can cause failures)
|
|
355
|
+
|
|
356
|
+
### Manual Rewriting Example
|
|
357
|
+
|
|
358
|
+
**Before** (closure-based):
|
|
359
|
+
```swift
|
|
360
|
+
func fetchImage(urlRequest: URLRequest,
|
|
361
|
+
completion: @escaping @Sendable (Result<UIImage, Error>) -> Void) {
|
|
362
|
+
URLSession.shared.dataTask(with: urlRequest) { data, _, error in
|
|
363
|
+
do {
|
|
364
|
+
if let error = error { throw error }
|
|
365
|
+
guard let data = data, let image = UIImage(data: data) else {
|
|
366
|
+
throw ImageError.conversionFailed
|
|
367
|
+
}
|
|
368
|
+
completion(.success(image))
|
|
369
|
+
} catch {
|
|
370
|
+
completion(.failure(error))
|
|
371
|
+
}
|
|
372
|
+
}.resume()
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**After** (async/await):
|
|
377
|
+
```swift
|
|
378
|
+
func fetchImage(urlRequest: URLRequest) async throws -> UIImage {
|
|
379
|
+
let (data, _) = try await URLSession.shared.data(for: urlRequest)
|
|
380
|
+
guard let image = UIImage(data: data) else {
|
|
381
|
+
throw ImageError.conversionFailed
|
|
382
|
+
}
|
|
383
|
+
return image
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Benefits**:
|
|
388
|
+
- Less code to maintain
|
|
389
|
+
- Easier to reason about
|
|
390
|
+
- No nested closures
|
|
391
|
+
- Automatic error propagation
|
|
392
|
+
|
|
393
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.7: Techniques for rewriting closures to async/await syntax](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Using @preconcurrency
|
|
398
|
+
|
|
399
|
+
Suppresses `Sendable` warnings from modules you don't control.
|
|
400
|
+
|
|
401
|
+
### When to Use
|
|
402
|
+
|
|
403
|
+
```swift
|
|
404
|
+
// ⚠️ Third-party library doesn't support Swift Concurrency yet
|
|
405
|
+
@preconcurrency import SomeThirdPartyLibrary
|
|
406
|
+
|
|
407
|
+
actor DataProcessor {
|
|
408
|
+
func process(_ data: LibraryType) { // No Sendable warning
|
|
409
|
+
// ...
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Risks
|
|
415
|
+
|
|
416
|
+
- **No compile-time safety**: You're responsible for ensuring thread safety
|
|
417
|
+
- **Hides real issues**: Library might not be thread-safe at all
|
|
418
|
+
- **Technical debt**: Easy to forget to revisit later
|
|
419
|
+
|
|
420
|
+
### Best Practices
|
|
421
|
+
|
|
422
|
+
1. **Don't use by default**: Only add when compiler suggests it
|
|
423
|
+
2. **Check for updates first**: Library might have a newer version with concurrency support
|
|
424
|
+
3. **Document why**: Add a comment explaining why it's needed
|
|
425
|
+
4. **Revisit regularly**: Set reminders to check if library has been updated
|
|
426
|
+
|
|
427
|
+
```swift
|
|
428
|
+
// TODO: Remove @preconcurrency when SomeLibrary adds Sendable support
|
|
429
|
+
// Last checked: 2026-01-07 (version 2.3.0)
|
|
430
|
+
@preconcurrency import SomeLibrary
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
The compiler will warn if `@preconcurrency` is unused:
|
|
434
|
+
```
|
|
435
|
+
'@preconcurrency' attribute on module 'SomeModule' is unused
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.8: How and when to use @preconcurrency](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Migrating from Combine/RxSwift
|
|
443
|
+
|
|
444
|
+
### Observation Alternative
|
|
445
|
+
|
|
446
|
+
Swift 6 will include **Transactional Observation** (SE-475):
|
|
447
|
+
|
|
448
|
+
```swift
|
|
449
|
+
// Future API (not yet implemented)
|
|
450
|
+
let names = Observations { person.name }
|
|
451
|
+
|
|
452
|
+
Task.detached {
|
|
453
|
+
for await name in names {
|
|
454
|
+
print("Name updated to: \(name)")
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Current alternatives**:
|
|
460
|
+
- Use `@Observable` macro for SwiftUI
|
|
461
|
+
- Use `AsyncStream` for custom observation
|
|
462
|
+
- Consider [AsyncExtensions](https://github.com/sideeffect-io/AsyncExtensions) package
|
|
463
|
+
|
|
464
|
+
### Debouncing Example
|
|
465
|
+
|
|
466
|
+
**Combine**:
|
|
467
|
+
```swift
|
|
468
|
+
$searchQuery
|
|
469
|
+
.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
|
|
470
|
+
.sink { [weak self] query in
|
|
471
|
+
self?.performSearch(query)
|
|
472
|
+
}
|
|
473
|
+
.store(in: &cancellables)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
**Swift Concurrency**:
|
|
477
|
+
```swift
|
|
478
|
+
func search(_ query: String) {
|
|
479
|
+
currentSearchTask?.cancel()
|
|
480
|
+
|
|
481
|
+
currentSearchTask = Task {
|
|
482
|
+
do {
|
|
483
|
+
try await Task.sleep(for: .milliseconds(500))
|
|
484
|
+
performSearch(query)
|
|
485
|
+
} catch {
|
|
486
|
+
// Search was cancelled
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**SwiftUI Integration**:
|
|
493
|
+
```swift
|
|
494
|
+
struct SearchView: View {
|
|
495
|
+
@State private var searchQuery = ""
|
|
496
|
+
@State private var searcher = ArticleSearcher()
|
|
497
|
+
|
|
498
|
+
var body: some View {
|
|
499
|
+
List(searcher.results) { result in
|
|
500
|
+
Text(result.title)
|
|
501
|
+
}
|
|
502
|
+
.searchable(text: $searchQuery)
|
|
503
|
+
.onChange(of: searchQuery) { _, newValue in
|
|
504
|
+
searcher.search(newValue)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Mindset Shift
|
|
511
|
+
|
|
512
|
+
**Don't think in Combine pipelines**. Many problems are simpler without FRP:
|
|
513
|
+
|
|
514
|
+
```swift
|
|
515
|
+
// ❌ Looking for AsyncSequence equivalent of complex Combine pipeline
|
|
516
|
+
somePublisher
|
|
517
|
+
.debounce(for: .seconds(0.5))
|
|
518
|
+
.removeDuplicates()
|
|
519
|
+
.flatMap { ... }
|
|
520
|
+
.sink { ... }
|
|
521
|
+
|
|
522
|
+
// ✅ Rethink the problem with Swift Concurrency
|
|
523
|
+
Task {
|
|
524
|
+
var lastValue: String?
|
|
525
|
+
for await value in stream {
|
|
526
|
+
guard value != lastValue else { continue }
|
|
527
|
+
lastValue = value
|
|
528
|
+
try await Task.sleep(for: .seconds(0.5))
|
|
529
|
+
await process(value)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**For complex operators**: Check [Swift Async Algorithms](https://github.com/apple/swift-async-algorithms) package.
|
|
535
|
+
|
|
536
|
+
### ⚠️ Critical: Actor Isolation with Combine
|
|
537
|
+
|
|
538
|
+
**Problem**: `sink` closures don't respect actor isolation at compile time.
|
|
539
|
+
|
|
540
|
+
```swift
|
|
541
|
+
@MainActor
|
|
542
|
+
final class NotificationObserver {
|
|
543
|
+
private var cancellables: [AnyCancellable] = []
|
|
544
|
+
|
|
545
|
+
init() {
|
|
546
|
+
NotificationCenter.default.publisher(for: .someNotification)
|
|
547
|
+
.sink { [weak self] _ in
|
|
548
|
+
self?.handleNotification() // ⚠️ May crash if posted from background
|
|
549
|
+
}
|
|
550
|
+
.store(in: &cancellables)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private func handleNotification() {
|
|
554
|
+
// Expects to run on main actor
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Why it crashes**: Notification observers run on the same thread as the poster. If posted from a background thread, the `@MainActor` method is called off the main actor.
|
|
560
|
+
|
|
561
|
+
**Solutions**:
|
|
562
|
+
|
|
563
|
+
1. **Migrate to Swift Concurrency** (recommended):
|
|
564
|
+
```swift
|
|
565
|
+
Task { [weak self] in
|
|
566
|
+
for await _ in NotificationCenter.default.notifications(named: .someNotification) {
|
|
567
|
+
await self?.handleNotification() // ✅ Compile-time safe
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
2. **Use Task wrapper** (temporary):
|
|
573
|
+
```swift
|
|
574
|
+
.sink { [weak self] _ in
|
|
575
|
+
Task { @MainActor in
|
|
576
|
+
self?.handleNotification()
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.9: Migrating away from Functional Reactive Programming like RxSwift or Combine](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## When to Use AsyncAlgorithms
|
|
586
|
+
|
|
587
|
+
When migrating from Combine or RxSwift, you have multiple options for handling asynchronous patterns:
|
|
588
|
+
|
|
589
|
+
### Use AsyncAlgorithms for:
|
|
590
|
+
|
|
591
|
+
- **Time-based operations**: debounce, throttle, timers
|
|
592
|
+
- **Combining multiple async sequences**: merge, combineLatest, zip
|
|
593
|
+
- **Multi-consumer scenarios**: AsyncChannel for backpressure
|
|
594
|
+
- **Complex operator chains**: FRP-like patterns in Swift Concurrency
|
|
595
|
+
- **Specific operators**: removeDuplicates, chunks, adjacentPairs, compacted
|
|
596
|
+
|
|
597
|
+
### Use Standard Library for:
|
|
598
|
+
|
|
599
|
+
- **Bridging callbacks**: AsyncStream is sufficient
|
|
600
|
+
- **Simple iteration**: for await in sequence
|
|
601
|
+
- **Single-value operations**: async/await
|
|
602
|
+
- **Basic transformations**: map, filter, contains
|
|
603
|
+
|
|
604
|
+
### Use SwiftUI for:
|
|
605
|
+
|
|
606
|
+
- **UI observation**: @Observable macro
|
|
607
|
+
- **State management**: @State, @Published properties
|
|
608
|
+
- **User interactions**: onChange, onReceive modifiers
|
|
609
|
+
|
|
610
|
+
> **See**: [async-algorithms.md](async-algorithms.md) for detailed AsyncAlgorithms usage examples.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## Real-World Migration Examples
|
|
615
|
+
|
|
616
|
+
### Example: ArticleSearcher with AsyncAlgorithms
|
|
617
|
+
|
|
618
|
+
**Before: Manual Debouncing**
|
|
619
|
+
|
|
620
|
+
```swift
|
|
621
|
+
final class ArticleSearcher {
|
|
622
|
+
@MainActor private(set) var results: [Article] = []
|
|
623
|
+
private var currentSearchTask: Task<Void, Never>?
|
|
624
|
+
|
|
625
|
+
func search(_ query: String) {
|
|
626
|
+
currentSearchTask?.cancel()
|
|
627
|
+
|
|
628
|
+
currentSearchTask = Task {
|
|
629
|
+
do {
|
|
630
|
+
try await Task.sleep(for: .milliseconds(500))
|
|
631
|
+
await MainActor.run {
|
|
632
|
+
self.results = []
|
|
633
|
+
}
|
|
634
|
+
self.results = await APIClient.searchArticles(query)
|
|
635
|
+
} catch {
|
|
636
|
+
// Search was cancelled
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// SwiftUI integration
|
|
643
|
+
struct SearchView: View {
|
|
644
|
+
@State private var searchQuery = ""
|
|
645
|
+
@State private var searcher = ArticleSearcher()
|
|
646
|
+
|
|
647
|
+
var body: some View {
|
|
648
|
+
List(searcher.results) { result in
|
|
649
|
+
Text(result.title)
|
|
650
|
+
}
|
|
651
|
+
.searchable(text: $searchQuery)
|
|
652
|
+
.onChange(of: searchQuery) { _, newValue in
|
|
653
|
+
searcher.search(newValue)
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
**After: AsyncAlgorithms Debounce**
|
|
660
|
+
|
|
661
|
+
```swift
|
|
662
|
+
import AsyncAlgorithms
|
|
663
|
+
|
|
664
|
+
@Observable
|
|
665
|
+
final class ArticleSearcher {
|
|
666
|
+
@MainActor private(set) var results: [Article] = []
|
|
667
|
+
private var searchQueryContinuation: AsyncStream<String>.Continuation?
|
|
668
|
+
|
|
669
|
+
private lazy var searchQueryStream: AsyncStream<String> = {
|
|
670
|
+
AsyncStream { continuation in
|
|
671
|
+
searchQueryContinuation = continuation
|
|
672
|
+
}
|
|
673
|
+
}()
|
|
674
|
+
|
|
675
|
+
func search(_ query: String) {
|
|
676
|
+
searchQueryContinuation?.yield(query)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
func startDebouncedSearch() {
|
|
680
|
+
Task { @MainActor in
|
|
681
|
+
for await query in searchQueryStream.debounce(for: .milliseconds(500)) {
|
|
682
|
+
self.results = []
|
|
683
|
+
self.results = await APIClient.searchArticles(query)
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// SwiftUI integration
|
|
690
|
+
struct SearchView: View {
|
|
691
|
+
@State private var searchQuery = ""
|
|
692
|
+
@State private var searcher = ArticleSearcher()
|
|
693
|
+
|
|
694
|
+
var body: some View {
|
|
695
|
+
List(searcher.results) { result in
|
|
696
|
+
Text(result.title)
|
|
697
|
+
}
|
|
698
|
+
.searchable(text: $searchQuery)
|
|
699
|
+
.onChange(of: searchQuery) { _, newValue in
|
|
700
|
+
searcher.search(newValue)
|
|
701
|
+
}
|
|
702
|
+
.onAppear {
|
|
703
|
+
searcher.startDebouncedSearch()
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
**Benefits of using AsyncAlgorithms**:
|
|
710
|
+
- Automatic cancellation when new values arrive
|
|
711
|
+
- Backpressure handling (producer respects consumer pace)
|
|
712
|
+
- Cleaner code than manual Task.sleep management
|
|
713
|
+
- No need to track and cancel tasks manually
|
|
714
|
+
|
|
715
|
+
### Example: Notification Stream Migration
|
|
716
|
+
|
|
717
|
+
**Before: Combine Publisher**
|
|
718
|
+
|
|
719
|
+
```swift
|
|
720
|
+
import Combine
|
|
721
|
+
|
|
722
|
+
final class NotificationObserver: ObservableObject {
|
|
723
|
+
@Published private(set) var notifications: [AppNotification] = []
|
|
724
|
+
private var cancellables = Set<AnyCancellable>()
|
|
725
|
+
|
|
726
|
+
init() {
|
|
727
|
+
NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
|
|
728
|
+
.compactMap { notification in
|
|
729
|
+
notification.object as? AppNotification
|
|
730
|
+
}
|
|
731
|
+
.receive(on: DispatchQueue.main)
|
|
732
|
+
.assign(to: &$notifications)
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
**After: Standard Library Notifications**
|
|
738
|
+
|
|
739
|
+
```swift
|
|
740
|
+
@Observable
|
|
741
|
+
final class NotificationObserver {
|
|
742
|
+
@MainActor private(set) var notifications: [AppNotification] = []
|
|
743
|
+
|
|
744
|
+
func startObserving() {
|
|
745
|
+
Task {
|
|
746
|
+
for await notification in NotificationCenter.default.notifications(named: UIApplication.didBecomeActiveNotification) {
|
|
747
|
+
if let appNotification = notification.object as? AppNotification {
|
|
748
|
+
notifications.append(appNotification)
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
**When to use each approach**:
|
|
757
|
+
- Use `notifications(named:)` for standard system notifications
|
|
758
|
+
- Use `AsyncChannel` for custom multi-consumer notification scenarios
|
|
759
|
+
- Use `@Observable` + SwiftUI for UI state updates
|
|
760
|
+
|
|
761
|
+
### Example: Multi-Source Data Loading
|
|
762
|
+
|
|
763
|
+
**Before: Combine Merge**
|
|
764
|
+
|
|
765
|
+
```swift
|
|
766
|
+
import Combine
|
|
767
|
+
|
|
768
|
+
final class MultiSourceLoader: ObservableObject {
|
|
769
|
+
@Published private(set) var items: [Item] = []
|
|
770
|
+
private var cancellables = Set<AnyCancellable>()
|
|
771
|
+
|
|
772
|
+
func loadFromAllSources() {
|
|
773
|
+
let source1 = APIClient.fetchItems(from: .source1)
|
|
774
|
+
let source2 = APIClient.fetchItems(from: .source2)
|
|
775
|
+
let source3 = APIClient.fetchItems(from: .source3)
|
|
776
|
+
|
|
777
|
+
Publishers.Merge3(source1, source2, source3)
|
|
778
|
+
.flatMap { items in
|
|
779
|
+
Just(items)
|
|
780
|
+
.delay(for: .seconds(0.1), scheduler: DispatchQueue.main)
|
|
781
|
+
}
|
|
782
|
+
.scan([]) { accumulated, new in
|
|
783
|
+
accumulated + new
|
|
784
|
+
}
|
|
785
|
+
.receive(on: DispatchQueue.main)
|
|
786
|
+
.assign(to: &$items)
|
|
787
|
+
.store(in: &cancellables)
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**After: AsyncAlgorithms Merge + TaskGroup**
|
|
793
|
+
|
|
794
|
+
```swift
|
|
795
|
+
import AsyncAlgorithms
|
|
796
|
+
|
|
797
|
+
@Observable
|
|
798
|
+
final class MultiSourceLoader {
|
|
799
|
+
@MainActor private(set) var items: [Item] = []
|
|
800
|
+
|
|
801
|
+
func loadFromAllSources() async {
|
|
802
|
+
let sources = [
|
|
803
|
+
APIClient.fetchItems(from: .source1),
|
|
804
|
+
APIClient.fetchItems(from: .source2),
|
|
805
|
+
APIClient.fetchItems(from: .source3)
|
|
806
|
+
]
|
|
807
|
+
|
|
808
|
+
Task { @MainActor in
|
|
809
|
+
for await stream in sources.map { $0.values }.merge() {
|
|
810
|
+
for await newItems in stream {
|
|
811
|
+
self.items.append(contentsOf: newItems)
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Alternative: Using TaskGroup for parallel execution
|
|
818
|
+
func loadFromAllSourcesParallel() async {
|
|
819
|
+
await withTaskGroup(of: [Item].self) { group in
|
|
820
|
+
group.addTask {
|
|
821
|
+
await APIClient.fetchItems(from: .source1)
|
|
822
|
+
}
|
|
823
|
+
group.addTask {
|
|
824
|
+
await APIClient.fetchItems(from: .source2)
|
|
825
|
+
}
|
|
826
|
+
group.addTask {
|
|
827
|
+
await APIClient.fetchItems(from: .source3)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
for await newItems in group {
|
|
831
|
+
await MainActor.run {
|
|
832
|
+
self.items.append(contentsOf: newItems)
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
**Key differences**:
|
|
841
|
+
- Combine `merge()` combines publishers; AsyncAlgorithms `merge()` combines sequences
|
|
842
|
+
- For parallel execution, use `TaskGroup` instead of `flatMap`
|
|
843
|
+
- State updates can use `@MainActor` instead of `.receive(on:)`
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## Anti-Patterns to Avoid
|
|
848
|
+
|
|
849
|
+
### ❌ Don't Use Task.sleep for Debouncing
|
|
850
|
+
|
|
851
|
+
```swift
|
|
852
|
+
// ❌ Bad: Manual debouncing without backpressure
|
|
853
|
+
func search(_ query: String) {
|
|
854
|
+
Task {
|
|
855
|
+
try? await Task.sleep(for: .milliseconds(500))
|
|
856
|
+
await performSearch(query)
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
**Problem**: Every keystroke spawns a new task. If user types fast, multiple tasks execute simultaneously after 500ms, causing out-of-order results and wasted API calls.
|
|
862
|
+
|
|
863
|
+
**Solution**: Use `debounce()` from AsyncAlgorithms for automatic backpressure and cancellation.
|
|
864
|
+
|
|
865
|
+
### ❌ Don't Manually Combine Values
|
|
866
|
+
|
|
867
|
+
```swift
|
|
868
|
+
// ❌ Bad: Manual combination without operator
|
|
869
|
+
actor FormValidator {
|
|
870
|
+
private var currentUsername: String = ""
|
|
871
|
+
private var currentEmail: String = ""
|
|
872
|
+
private var currentPassword: String = ""
|
|
873
|
+
|
|
874
|
+
func updateUsername(_ username: String) {
|
|
875
|
+
currentUsername = username
|
|
876
|
+
checkForm()
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
func updateEmail(_ email: String) {
|
|
880
|
+
currentEmail = email
|
|
881
|
+
checkForm()
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
func updatePassword(_ password: String) {
|
|
885
|
+
currentPassword = password
|
|
886
|
+
checkForm()
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
private func checkForm() {
|
|
890
|
+
let state = validate(
|
|
891
|
+
username: currentUsername,
|
|
892
|
+
email: currentEmail,
|
|
893
|
+
password: currentPassword
|
|
894
|
+
)
|
|
895
|
+
// Update UI or emit validation state
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
**Problems**:
|
|
901
|
+
- More state management
|
|
902
|
+
- Boilerplate code for each field
|
|
903
|
+
- Harder to add new fields
|
|
904
|
+
- No stream composition benefits
|
|
905
|
+
|
|
906
|
+
**Solution**: Use `combineLatest()` for cleaner, composable validation.
|
|
907
|
+
|
|
908
|
+
### ❌ Don't Share Streams Without AsyncChannel
|
|
909
|
+
|
|
910
|
+
```swift
|
|
911
|
+
// ❌ Bad: Multiple consumers sharing same stream
|
|
912
|
+
let stream = AsyncStream<Int> { continuation in
|
|
913
|
+
for i in 1...10 {
|
|
914
|
+
continuation.yield(i)
|
|
915
|
+
}
|
|
916
|
+
continuation.finish()
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
Task {
|
|
920
|
+
for await value in stream {
|
|
921
|
+
print("Consumer 1: \(value)")
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
Task {
|
|
926
|
+
for await value in stream {
|
|
927
|
+
print("Consumer 2: \(value)")
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
**Problem**: Values are split between consumers unpredictably. Each value goes to only one consumer.
|
|
933
|
+
|
|
934
|
+
**Solution**: Use `AsyncChannel` for true multi-consumer scenarios with backpressure.
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
---
|
|
939
|
+
|
|
940
|
+
## Concurrency-Safe Notifications (iOS 26+)
|
|
941
|
+
|
|
942
|
+
Swift 6.2 introduces **typed, thread-safe notifications**.
|
|
943
|
+
|
|
944
|
+
### MainActorMessage
|
|
945
|
+
|
|
946
|
+
For notifications that should be delivered on the main actor:
|
|
947
|
+
|
|
948
|
+
```swift
|
|
949
|
+
// Old way
|
|
950
|
+
NotificationCenter.default.addObserver(
|
|
951
|
+
forName: UIApplication.didBecomeActiveNotification,
|
|
952
|
+
object: nil,
|
|
953
|
+
queue: .main
|
|
954
|
+
) { [weak self] _ in
|
|
955
|
+
self?.handleDidBecomeActive() // ⚠️ Concurrency warning
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// New way (iOS 26+)
|
|
959
|
+
token = NotificationCenter.default.addObserver(
|
|
960
|
+
of: UIApplication.self,
|
|
961
|
+
for: .didBecomeActive
|
|
962
|
+
) { [weak self] message in
|
|
963
|
+
self?.handleDidBecomeActive() // ✅ No warning, guaranteed main actor
|
|
964
|
+
}
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
**Key difference**: Observer closure is guaranteed to run on `@MainActor`.
|
|
968
|
+
|
|
969
|
+
### AsyncMessage
|
|
970
|
+
|
|
971
|
+
For notifications delivered asynchronously on arbitrary isolation:
|
|
972
|
+
|
|
973
|
+
```swift
|
|
974
|
+
struct RecentBuildsChangedMessage: NotificationCenter.AsyncMessage {
|
|
975
|
+
typealias Subject = [RecentBuild]
|
|
976
|
+
let recentBuilds: Subject
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// Enable static member lookup
|
|
980
|
+
extension NotificationCenter.MessageIdentifier
|
|
981
|
+
where Self == NotificationCenter.BaseMessageIdentifier<RecentBuildsChangedMessage> {
|
|
982
|
+
static var recentBuildsChanged: NotificationCenter.BaseMessageIdentifier<RecentBuildsChangedMessage> {
|
|
983
|
+
.init()
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
**Posting**:
|
|
989
|
+
```swift
|
|
990
|
+
let builds = [RecentBuild(appName: "Stock Analyzer")]
|
|
991
|
+
let message = RecentBuildsChangedMessage(recentBuilds: builds)
|
|
992
|
+
NotificationCenter.default.post(message)
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
**Observing**:
|
|
996
|
+
```swift
|
|
997
|
+
// Old way: Unsafe casting
|
|
998
|
+
NotificationCenter.default.addObserver(forName: .recentBuildsChanged, object: nil, queue: nil) { notification in
|
|
999
|
+
guard let builds = notification.object as? [RecentBuild] else { return }
|
|
1000
|
+
handleBuilds(builds)
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// New way: Strongly typed, thread-safe
|
|
1004
|
+
token = NotificationCenter.default.addObserver(
|
|
1005
|
+
of: [RecentBuild].self,
|
|
1006
|
+
for: .recentBuildsChanged
|
|
1007
|
+
) { message in
|
|
1008
|
+
handleBuilds(message.recentBuilds) // ✅ Direct access, no casting
|
|
1009
|
+
}
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
**Benefits**:
|
|
1013
|
+
- Strongly typed (no `Any` casting)
|
|
1014
|
+
- Compile-time thread safety
|
|
1015
|
+
- Clear isolation guarantees
|
|
1016
|
+
|
|
1017
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.10: Migrating to concurrency-safe notifications](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
## Common Challenges
|
|
1022
|
+
|
|
1023
|
+
### "It's Too Much Work"
|
|
1024
|
+
|
|
1025
|
+
Break it down:
|
|
1026
|
+
- Migrate one package at a time
|
|
1027
|
+
- Use 30-minute daily sessions
|
|
1028
|
+
- Create checkpoints with small PRs
|
|
1029
|
+
- Celebrate incremental progress
|
|
1030
|
+
|
|
1031
|
+
### "My Team Isn't Ready"
|
|
1032
|
+
|
|
1033
|
+
Start small:
|
|
1034
|
+
- Enable Swift 6 for new files only
|
|
1035
|
+
- Make new types `Sendable` by default
|
|
1036
|
+
- Share learnings in team meetings
|
|
1037
|
+
- Pair program on tricky migrations
|
|
1038
|
+
|
|
1039
|
+
### "Dependencies Aren't Ready"
|
|
1040
|
+
|
|
1041
|
+
Options:
|
|
1042
|
+
- Update to latest versions first
|
|
1043
|
+
- Use `@preconcurrency` temporarily
|
|
1044
|
+
- Contribute fixes to open-source dependencies
|
|
1045
|
+
- Wrap third-party APIs in your own concurrency-safe layer
|
|
1046
|
+
|
|
1047
|
+
### "I Keep Going in Circles"
|
|
1048
|
+
|
|
1049
|
+
This is the "concurrency rabbit hole":
|
|
1050
|
+
- Take breaks and revisit later
|
|
1051
|
+
- Temporarily disable strict checking to make progress elsewhere
|
|
1052
|
+
- Focus on one module at a time
|
|
1053
|
+
- Don't try to fix everything at once
|
|
1054
|
+
|
|
1055
|
+
> **Course Deep Dive**: This topic is covered in detail in [Lesson 12.11: Frequently Asked Questions (FAQ) around Swift 6 Migrations](https://www.swiftconcurrencycourse.com?utm_source=github&utm_medium=agent-skill&utm_campaign=lesson-reference)
|
|
1056
|
+
|
|
1057
|
+
---
|
|
1058
|
+
|
|
1059
|
+
## Summary
|
|
1060
|
+
|
|
1061
|
+
Migration to Swift 6 is a journey, not a sprint:
|
|
1062
|
+
|
|
1063
|
+
1. **Start small**: Find isolated code with minimal dependencies
|
|
1064
|
+
2. **Be incremental**: Use the three strict concurrency levels (Minimal → Targeted → Complete)
|
|
1065
|
+
3. **Use tooling**: Leverage Xcode refactoring and `swift package migrate`
|
|
1066
|
+
4. **Create checkpoints**: Small, focused PRs that can be merged quickly
|
|
1067
|
+
5. **Stay positive**: The concurrency rabbit hole is real, but manageable with the right habits
|
|
1068
|
+
6. **Think differently**: Let go of the threading mindset; trust the compiler
|
|
1069
|
+
|
|
1070
|
+
The result is **compile-time thread safety**, more maintainable code, and a future-proof codebase.
|
|
1071
|
+
|
|
1072
|
+
**Additional resources**:
|
|
1073
|
+
- [Approachable Concurrency Video](https://youtu.be/y_Qc8cT-O_g?si=y4C1XQDGtyIOLW81)
|
|
1074
|
+
- [Migration Tooling Video](https://youtu.be/FK9XFxSWZPg?si=2z_ybn1t1YCJow5k)
|
|
1075
|
+
- [Swift Concurrency Course](https://www.swiftconcurrencycourse.com) for in-depth migration strategies
|
|
1076
|
+
|