buildanything 1.7.1 → 2.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 (633) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +9 -3
  3. package/CHANGELOG.md +112 -0
  4. package/README.md +2 -2
  5. package/agents/a11y-architect.md +166 -0
  6. package/agents/business-model.md +80 -29
  7. package/agents/code-architect.md +75 -0
  8. package/agents/code-reviewer.md +255 -0
  9. package/agents/code-simplifier.md +64 -0
  10. package/agents/design-brand-guardian.md +293 -53
  11. package/agents/design-critic.md +139 -0
  12. package/agents/design-inclusive-visuals-specialist.md +6 -19
  13. package/agents/design-ui-designer.md +335 -56
  14. package/agents/design-ux-architect.md +403 -55
  15. package/agents/design-ux-researcher.md +264 -49
  16. package/agents/engineering-ai-engineer.md +26 -36
  17. package/agents/engineering-backend-architect.md +185 -36
  18. package/agents/engineering-data-engineer.md +225 -43
  19. package/agents/engineering-devops-automator.md +227 -74
  20. package/agents/engineering-frontend-developer.md +210 -34
  21. package/agents/engineering-mobile-app-builder.md +6 -1
  22. package/agents/engineering-rapid-prototyper.md +30 -9
  23. package/agents/engineering-security-engineer.md +263 -61
  24. package/agents/engineering-senior-developer.md +128 -19
  25. package/agents/engineering-sre.md +84 -0
  26. package/agents/engineering-technical-writer.md +285 -41
  27. package/agents/feature-intel.md +110 -0
  28. package/agents/ios-app-review-guardian.md +66 -0
  29. package/agents/ios-foundation-models-specialist.md +64 -0
  30. package/agents/ios-storekit-specialist.md +59 -0
  31. package/agents/ios-swift-architect.md +129 -0
  32. package/agents/ios-swift-search.md +137 -0
  33. package/agents/ios-swift-ui-design.md +136 -0
  34. package/agents/marketing-app-store-optimizer.md +246 -64
  35. package/agents/planner.md +216 -0
  36. package/agents/pr-test-analyzer.md +63 -0
  37. package/agents/product-feedback-synthesizer.md +8 -2
  38. package/agents/refactor-cleaner.md +102 -0
  39. package/agents/security-reviewer.md +128 -0
  40. package/agents/silent-failure-hunter.md +54 -0
  41. package/agents/swift-build-resolver.md +119 -0
  42. package/agents/swift-reviewer.md +112 -0
  43. package/agents/tech-feasibility.md +21 -1
  44. package/agents/testing-api-tester.md +236 -59
  45. package/agents/testing-evidence-collector.md +26 -1
  46. package/agents/testing-performance-benchmarker.md +21 -1
  47. package/agents/testing-reality-checker.md +6 -1
  48. package/agents/visual-research.md +116 -0
  49. package/bin/adapters/cycle-counter-tool.ts +155 -0
  50. package/bin/adapters/scribe-tool.ts +71 -0
  51. package/bin/adapters/state-save-tool.ts +130 -0
  52. package/bin/adapters/write-lease-tool.ts +127 -0
  53. package/bin/buildanything-runtime.js +15 -0
  54. package/bin/buildanything-runtime.ts +328 -0
  55. package/bin/setup.js +83 -8
  56. package/commands/add-feature.md +2 -0
  57. package/commands/build.md +752 -332
  58. package/commands/fix.md +65 -0
  59. package/commands/self-check.md +121 -0
  60. package/commands/setup.md +114 -0
  61. package/commands/ux-review.md +63 -0
  62. package/commands/verify.md +69 -0
  63. package/docs/migration/agents.yaml +729 -0
  64. package/docs/migration/phase-graph.yaml +1088 -0
  65. package/docs/migration/sdk-host-compat.md +18 -0
  66. package/hooks/compile-writer-owner-cache.ts +171 -0
  67. package/hooks/hooks.json +36 -0
  68. package/hooks/pre-tool-use +19 -0
  69. package/hooks/pre-tool-use.ts +776 -0
  70. package/hooks/record-mode-transitions.ts +178 -0
  71. package/hooks/session-start +89 -2
  72. package/hooks/subagent-start +17 -0
  73. package/hooks/subagent-start.ts +471 -0
  74. package/hooks/subagent-stop +17 -0
  75. package/hooks/subagent-stop.ts +153 -0
  76. package/package.json +28 -5
  77. package/protocols/architecture-schema.md +171 -0
  78. package/protocols/build-fix.md +52 -0
  79. package/protocols/cleanup.md +54 -0
  80. package/protocols/decision-log.md +131 -0
  81. package/protocols/eval-harness.md +61 -0
  82. package/protocols/fake-data-detector.md +64 -0
  83. package/protocols/ios-context.md +234 -0
  84. package/protocols/ios-frameworks-map.md +323 -0
  85. package/protocols/ios-phase-branches.md +337 -0
  86. package/protocols/ios-preflight.md +27 -0
  87. package/protocols/launch-readiness.md +258 -0
  88. package/protocols/metric-loop.md +153 -0
  89. package/protocols/smoke-test.md +118 -0
  90. package/protocols/state-schema.json +388 -0
  91. package/protocols/state-schema.md +172 -0
  92. package/protocols/verify.md +127 -0
  93. package/protocols/visual-dna.md +185 -0
  94. package/protocols/web-phase-branches.md +351 -0
  95. package/skills/ios/_VENDORED.md +62 -0
  96. package/skills/ios/activitykit/LICENSE +131 -0
  97. package/skills/ios/activitykit/SKILL.md +505 -0
  98. package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
  99. package/skills/ios/app-intents/LICENSE +131 -0
  100. package/skills/ios/app-intents/SKILL.md +494 -0
  101. package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
  102. package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
  103. package/skills/ios/apple-on-device-ai/LICENSE +131 -0
  104. package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
  105. package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
  106. package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
  107. package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
  108. package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
  109. package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
  110. package/skills/ios/hig-components-content/SKILL.md +86 -0
  111. package/skills/ios/hig-components-content/references/activity-views.md +79 -0
  112. package/skills/ios/hig-components-content/references/charts.md +180 -0
  113. package/skills/ios/hig-components-content/references/collections.md +48 -0
  114. package/skills/ios/hig-components-content/references/color-wells.md +42 -0
  115. package/skills/ios/hig-components-content/references/image-views.md +82 -0
  116. package/skills/ios/hig-components-content/references/image-wells.md +34 -0
  117. package/skills/ios/hig-components-content/references/lockups.md +78 -0
  118. package/skills/ios/hig-components-content/references/web-views.md +36 -0
  119. package/skills/ios/hig-components-controls/SKILL.md +88 -0
  120. package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
  121. package/skills/ios/hig-components-controls/references/controls.md +112 -0
  122. package/skills/ios/hig-components-controls/references/gauges.md +74 -0
  123. package/skills/ios/hig-components-controls/references/labels.md +92 -0
  124. package/skills/ios/hig-components-controls/references/pickers.md +128 -0
  125. package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
  126. package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
  127. package/skills/ios/hig-components-controls/references/sliders.md +92 -0
  128. package/skills/ios/hig-components-controls/references/steppers.md +40 -0
  129. package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
  130. package/skills/ios/hig-components-controls/references/text-views.md +56 -0
  131. package/skills/ios/hig-components-controls/references/toggles.md +127 -0
  132. package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
  133. package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
  134. package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
  135. package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
  136. package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
  137. package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
  138. package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
  139. package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
  140. package/skills/ios/hig-components-layout/SKILL.md +99 -0
  141. package/skills/ios/hig-components-layout/references/boxes.md +48 -0
  142. package/skills/ios/hig-components-layout/references/column-views.md +44 -0
  143. package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
  144. package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
  145. package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
  146. package/skills/ios/hig-components-layout/references/panels.md +75 -0
  147. package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
  148. package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
  149. package/skills/ios/hig-components-layout/references/split-views.md +110 -0
  150. package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
  151. package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
  152. package/skills/ios/hig-components-layout/references/windows.md +188 -0
  153. package/skills/ios/hig-components-menus/SKILL.md +81 -0
  154. package/skills/ios/hig-components-menus/references/action-button.md +61 -0
  155. package/skills/ios/hig-components-menus/references/buttons.md +261 -0
  156. package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
  157. package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
  158. package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
  159. package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
  160. package/skills/ios/hig-components-menus/references/menus.md +171 -0
  161. package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
  162. package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
  163. package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
  164. package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
  165. package/skills/ios/hig-components-search/SKILL.md +68 -0
  166. package/skills/ios/hig-components-search/references/page-controls.md +120 -0
  167. package/skills/ios/hig-components-search/references/path-controls.md +40 -0
  168. package/skills/ios/hig-components-search/references/search-fields.md +189 -0
  169. package/skills/ios/hig-components-status/SKILL.md +80 -0
  170. package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
  171. package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
  172. package/skills/ios/hig-components-status/references/status-bars.md +38 -0
  173. package/skills/ios/hig-components-system/SKILL.md +88 -0
  174. package/skills/ios/hig-components-system/references/app-clips.md +387 -0
  175. package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
  176. package/skills/ios/hig-components-system/references/complications.md +425 -0
  177. package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
  178. package/skills/ios/hig-components-system/references/live-activities.md +442 -0
  179. package/skills/ios/hig-components-system/references/notifications.md +153 -0
  180. package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
  181. package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
  182. package/skills/ios/hig-components-system/references/widgets.md +517 -0
  183. package/skills/ios/hig-foundations/SKILL.md +98 -0
  184. package/skills/ios/hig-foundations/references/accessibility.md +291 -0
  185. package/skills/ios/hig-foundations/references/app-icons.md +210 -0
  186. package/skills/ios/hig-foundations/references/branding.md +44 -0
  187. package/skills/ios/hig-foundations/references/color.md +274 -0
  188. package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
  189. package/skills/ios/hig-foundations/references/icons.md +263 -0
  190. package/skills/ios/hig-foundations/references/images.md +176 -0
  191. package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
  192. package/skills/ios/hig-foundations/references/inclusion.md +189 -0
  193. package/skills/ios/hig-foundations/references/layout.md +425 -0
  194. package/skills/ios/hig-foundations/references/materials.md +238 -0
  195. package/skills/ios/hig-foundations/references/motion.md +103 -0
  196. package/skills/ios/hig-foundations/references/privacy.md +231 -0
  197. package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
  198. package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
  199. package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
  200. package/skills/ios/hig-foundations/references/typography.md +1146 -0
  201. package/skills/ios/hig-foundations/references/writing.md +91 -0
  202. package/skills/ios/hig-inputs/SKILL.md +94 -0
  203. package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
  204. package/skills/ios/hig-inputs/references/camera-control.md +107 -0
  205. package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
  206. package/skills/ios/hig-inputs/references/eyes.md +120 -0
  207. package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
  208. package/skills/ios/hig-inputs/references/game-controls.md +156 -0
  209. package/skills/ios/hig-inputs/references/gestures.md +208 -0
  210. package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
  211. package/skills/ios/hig-inputs/references/keyboards.md +234 -0
  212. package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
  213. package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
  214. package/skills/ios/hig-inputs/references/remotes.md +67 -0
  215. package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
  216. package/skills/ios/hig-patterns/SKILL.md +104 -0
  217. package/skills/ios/hig-patterns/references/charting-data.md +81 -0
  218. package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
  219. package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
  220. package/skills/ios/hig-patterns/references/entering-data.md +69 -0
  221. package/skills/ios/hig-patterns/references/feedback.md +67 -0
  222. package/skills/ios/hig-patterns/references/file-management.md +135 -0
  223. package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
  224. package/skills/ios/hig-patterns/references/launching.md +81 -0
  225. package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
  226. package/skills/ios/hig-patterns/references/loading.md +59 -0
  227. package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
  228. package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
  229. package/skills/ios/hig-patterns/references/modality.md +82 -0
  230. package/skills/ios/hig-patterns/references/multitasking.md +131 -0
  231. package/skills/ios/hig-patterns/references/offering-help.md +117 -0
  232. package/skills/ios/hig-patterns/references/onboarding.md +69 -0
  233. package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
  234. package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
  235. package/skills/ios/hig-patterns/references/playing-video.md +180 -0
  236. package/skills/ios/hig-patterns/references/printing.md +50 -0
  237. package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
  238. package/skills/ios/hig-patterns/references/searching.md +70 -0
  239. package/skills/ios/hig-patterns/references/settings.md +84 -0
  240. package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
  241. package/skills/ios/hig-patterns/references/workouts.md +76 -0
  242. package/skills/ios/hig-platforms/SKILL.md +84 -0
  243. package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
  244. package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
  245. package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
  246. package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
  247. package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
  248. package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
  249. package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
  250. package/skills/ios/hig-project-context/SKILL.md +133 -0
  251. package/skills/ios/hig-technologies/SKILL.md +107 -0
  252. package/skills/ios/hig-technologies/references/airplay.md +125 -0
  253. package/skills/ios/hig-technologies/references/always-on.md +62 -0
  254. package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
  255. package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
  256. package/skills/ios/hig-technologies/references/carekit.md +224 -0
  257. package/skills/ios/hig-technologies/references/carplay.md +119 -0
  258. package/skills/ios/hig-technologies/references/game-center.md +343 -0
  259. package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
  260. package/skills/ios/hig-technologies/references/healthkit.md +120 -0
  261. package/skills/ios/hig-technologies/references/homekit.md +343 -0
  262. package/skills/ios/hig-technologies/references/icloud.md +52 -0
  263. package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
  264. package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
  265. package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
  266. package/skills/ios/hig-technologies/references/live-photos.md +54 -0
  267. package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
  268. package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
  269. package/skills/ios/hig-technologies/references/maps.md +221 -0
  270. package/skills/ios/hig-technologies/references/nfc.md +51 -0
  271. package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
  272. package/skills/ios/hig-technologies/references/researchkit.md +134 -0
  273. package/skills/ios/hig-technologies/references/shareplay.md +142 -0
  274. package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
  275. package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
  276. package/skills/ios/hig-technologies/references/siri.md +523 -0
  277. package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
  278. package/skills/ios/hig-technologies/references/voiceover.md +90 -0
  279. package/skills/ios/hig-technologies/references/wallet.md +420 -0
  280. package/skills/ios/ios-26-platform/SKILL.md +53 -0
  281. package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
  282. package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
  283. package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
  284. package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
  285. package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
  286. package/skills/ios/ios-bootstrap/SKILL.md +107 -0
  287. package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
  288. package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
  289. package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
  290. package/skills/ios/ios-debugger-agent/LICENSE +21 -0
  291. package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
  292. package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
  293. package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
  294. package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
  295. package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
  296. package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
  297. package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
  298. package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
  299. package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
  300. package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
  301. package/skills/ios/swift-accessibility/LICENSE +21 -0
  302. package/skills/ios/swift-accessibility/SKILL.md +371 -0
  303. package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
  304. package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
  305. package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
  306. package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
  307. package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
  308. package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
  309. package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
  310. package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
  311. package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
  312. package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
  313. package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
  314. package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
  315. package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
  316. package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
  317. package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
  318. package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
  319. package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
  320. package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
  321. package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
  322. package/skills/ios/swift-concurrency/LICENSE +21 -0
  323. package/skills/ios/swift-concurrency/SKILL.md +171 -0
  324. package/skills/ios/swift-concurrency/references/_index.md +50 -0
  325. package/skills/ios/swift-concurrency/references/actors.md +660 -0
  326. package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
  327. package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
  328. package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
  329. package/skills/ios/swift-concurrency/references/core-data.md +560 -0
  330. package/skills/ios/swift-concurrency/references/glossary.md +135 -0
  331. package/skills/ios/swift-concurrency/references/linting.md +155 -0
  332. package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
  333. package/skills/ios/swift-concurrency/references/migration.md +1104 -0
  334. package/skills/ios/swift-concurrency/references/performance.md +593 -0
  335. package/skills/ios/swift-concurrency/references/sendable.md +598 -0
  336. package/skills/ios/swift-concurrency/references/tasks.md +636 -0
  337. package/skills/ios/swift-concurrency/references/testing.md +592 -0
  338. package/skills/ios/swift-concurrency/references/threading.md +495 -0
  339. package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
  340. package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
  341. package/skills/ios/swift-security-expert/LICENSE +21 -0
  342. package/skills/ios/swift-security-expert/SKILL.md +470 -0
  343. package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
  344. package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
  345. package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
  346. package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
  347. package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
  348. package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
  349. package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
  350. package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
  351. package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
  352. package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
  353. package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
  354. package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
  355. package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
  356. package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
  357. package/skills/ios/swift-testing-expert/LICENSE +21 -0
  358. package/skills/ios/swift-testing-expert/SKILL.md +79 -0
  359. package/skills/ios/swift-testing-expert/references/_index.md +12 -0
  360. package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
  361. package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
  362. package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
  363. package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
  364. package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
  365. package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
  366. package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
  367. package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
  368. package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
  369. package/skills/ios/swiftdata-pro/LICENSE +21 -0
  370. package/skills/ios/swiftdata-pro/SKILL.md +102 -0
  371. package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
  372. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
  373. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
  374. package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
  375. package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
  376. package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
  377. package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
  378. package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
  379. package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
  380. package/skills/ios/swiftui-design-principles/LICENSE +21 -0
  381. package/skills/ios/swiftui-design-principles/README.md +41 -0
  382. package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
  383. package/skills/ios/swiftui-design-principles/metadata.json +10 -0
  384. package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
  385. package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
  386. package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
  387. package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
  388. package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
  389. package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
  390. package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
  391. package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
  392. package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
  393. package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  394. package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  395. package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
  396. package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
  397. package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  398. package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  399. package/skills/ios/swiftui-pro/LICENSE +21 -0
  400. package/skills/ios/swiftui-pro/SKILL.md +108 -0
  401. package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
  402. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
  403. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
  404. package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
  405. package/skills/ios/swiftui-pro/references/api.md +39 -0
  406. package/skills/ios/swiftui-pro/references/data.md +43 -0
  407. package/skills/ios/swiftui-pro/references/design.md +31 -0
  408. package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
  409. package/skills/ios/swiftui-pro/references/navigation.md +14 -0
  410. package/skills/ios/swiftui-pro/references/performance.md +46 -0
  411. package/skills/ios/swiftui-pro/references/swift.md +56 -0
  412. package/skills/ios/swiftui-pro/references/views.md +35 -0
  413. package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
  414. package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
  415. package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
  416. package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
  417. package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
  418. package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
  419. package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
  420. package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
  421. package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
  422. package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
  423. package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
  424. package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
  425. package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  426. package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  427. package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
  428. package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  429. package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
  430. package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  431. package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
  432. package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
  433. package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
  434. package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
  435. package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
  436. package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
  437. package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  438. package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
  439. package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
  440. package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
  441. package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
  442. package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
  443. package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
  444. package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
  445. package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
  446. package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
  447. package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
  448. package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
  449. package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
  450. package/skills/ios/widgetkit/LICENSE +131 -0
  451. package/skills/ios/widgetkit/SKILL.md +502 -0
  452. package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
  453. package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
  454. package/skills/web/accessibility/SKILL.md +146 -0
  455. package/skills/web/aceternity-ui/SKILL.md +719 -0
  456. package/skills/web/aceternity-ui/metadata.json +10 -0
  457. package/skills/web/api-design/SKILL.md +523 -0
  458. package/skills/web/chart-accessibility/SKILL.md +332 -0
  459. package/skills/web/composition-patterns/AGENTS.md +946 -0
  460. package/skills/web/composition-patterns/README.md +60 -0
  461. package/skills/web/composition-patterns/SKILL.md +89 -0
  462. package/skills/web/composition-patterns/metadata.json +11 -0
  463. package/skills/web/composition-patterns/rules/_sections.md +29 -0
  464. package/skills/web/composition-patterns/rules/_template.md +24 -0
  465. package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  466. package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
  467. package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  468. package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  469. package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
  470. package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
  471. package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
  472. package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
  473. package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
  474. package/skills/web/database-migrations/SKILL.md +429 -0
  475. package/skills/web/deployment-patterns/SKILL.md +427 -0
  476. package/skills/web/docker-patterns/SKILL.md +364 -0
  477. package/skills/web/e2e-testing/SKILL.md +326 -0
  478. package/skills/web/lighthouse-ci/SKILL.md +361 -0
  479. package/skills/web/mcp-server-patterns/SKILL.md +69 -0
  480. package/skills/web/next-best-practices/SKILL.md +153 -0
  481. package/skills/web/next-best-practices/async-patterns.md +87 -0
  482. package/skills/web/next-best-practices/bundling.md +180 -0
  483. package/skills/web/next-best-practices/data-patterns.md +297 -0
  484. package/skills/web/next-best-practices/debug-tricks.md +105 -0
  485. package/skills/web/next-best-practices/directives.md +73 -0
  486. package/skills/web/next-best-practices/error-handling.md +227 -0
  487. package/skills/web/next-best-practices/file-conventions.md +140 -0
  488. package/skills/web/next-best-practices/font.md +245 -0
  489. package/skills/web/next-best-practices/functions.md +108 -0
  490. package/skills/web/next-best-practices/hydration-error.md +91 -0
  491. package/skills/web/next-best-practices/image.md +173 -0
  492. package/skills/web/next-best-practices/metadata.md +301 -0
  493. package/skills/web/next-best-practices/parallel-routes.md +287 -0
  494. package/skills/web/next-best-practices/route-handlers.md +146 -0
  495. package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
  496. package/skills/web/next-best-practices/runtime-selection.md +39 -0
  497. package/skills/web/next-best-practices/scripts.md +141 -0
  498. package/skills/web/next-best-practices/self-hosting.md +371 -0
  499. package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
  500. package/skills/web/next-cache-components/SKILL.md +411 -0
  501. package/skills/web/postgres-best-practices/SKILL.md +14 -0
  502. package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
  503. package/skills/web/react-best-practices/AGENTS.md +3810 -0
  504. package/skills/web/react-best-practices/README.md +123 -0
  505. package/skills/web/react-best-practices/SKILL.md +149 -0
  506. package/skills/web/react-best-practices/metadata.json +15 -0
  507. package/skills/web/react-best-practices/rules/_sections.md +46 -0
  508. package/skills/web/react-best-practices/rules/_template.md +28 -0
  509. package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
  510. package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  511. package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
  512. package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
  513. package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
  514. package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  515. package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
  516. package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
  517. package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
  518. package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  519. package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
  520. package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
  521. package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
  522. package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  523. package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  524. package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
  525. package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
  526. package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
  527. package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  528. package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
  529. package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
  530. package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
  531. package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
  532. package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
  533. package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
  534. package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
  535. package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
  536. package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
  537. package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
  538. package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
  539. package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
  540. package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
  541. package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
  542. package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  543. package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
  544. package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  545. package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
  546. package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
  547. package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  548. package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  549. package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  550. package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
  551. package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
  552. package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
  553. package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  554. package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
  555. package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
  556. package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  557. package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
  558. package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  559. package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  560. package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  561. package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
  562. package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  563. package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
  564. package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  565. package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  566. package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
  567. package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  568. package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  569. package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
  570. package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
  571. package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
  572. package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
  573. package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
  574. package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
  575. package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
  576. package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
  577. package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
  578. package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
  579. package/skills/web/seo/SKILL.md +154 -0
  580. package/skills/web/web-design-guidelines/SKILL.md +39 -0
  581. package/skills/web/zap-scan-config/SKILL.md +444 -0
  582. package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
  583. package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
  584. package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
  585. package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
  586. package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
  587. package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
  588. package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
  589. package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
  590. package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
  591. package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
  592. package/src/lrr/aggregator.ts +80 -0
  593. package/src/orchestrator/hooks/context-header.ts +95 -0
  594. package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
  595. package/src/orchestrator/hooks/token-accounting.ts +101 -0
  596. package/src/orchestrator/mcp/cycle-counter.ts +129 -0
  597. package/src/orchestrator/mcp/scribe.ts +283 -0
  598. package/src/orchestrator/mcp/state-save.ts +149 -0
  599. package/src/orchestrator/mcp/write-lease.ts +167 -0
  600. package/src/orchestrator/phase4-shared-context.ts +41 -0
  601. package/src/orchestrator/schemas/backward-edge.ts +46 -0
  602. package/agents/agentic-identity-trust.md +0 -121
  603. package/agents/data-consolidation-agent.md +0 -39
  604. package/agents/design-image-prompt-engineer.md +0 -105
  605. package/agents/design-visual-storyteller.md +0 -147
  606. package/agents/design-whimsy-injector.md +0 -89
  607. package/agents/engineering-autonomous-optimization-architect.md +0 -105
  608. package/agents/market-intel.md +0 -35
  609. package/agents/marketing-instagram-curator.md +0 -111
  610. package/agents/marketing-reddit-community-builder.md +0 -121
  611. package/agents/marketing-social-media-strategist.md +0 -74
  612. package/agents/marketing-tiktok-strategist.md +0 -123
  613. package/agents/marketing-twitter-engager.md +0 -124
  614. package/agents/marketing-wechat-official-account.md +0 -143
  615. package/agents/marketing-xiaohongshu-specialist.md +0 -136
  616. package/agents/marketing-zhihu-strategist.md +0 -160
  617. package/agents/product-behavioral-nudge-engine.md +0 -78
  618. package/agents/project-management-experiment-tracker.md +0 -102
  619. package/agents/report-distribution-agent.md +0 -43
  620. package/agents/risk-analysis.md +0 -45
  621. package/agents/sales-data-extraction-agent.md +0 -46
  622. package/agents/specialized-cultural-intelligence-strategist.md +0 -65
  623. package/agents/specialized-developer-advocate.md +0 -146
  624. package/agents/support-analytics-reporter.md +0 -133
  625. package/agents/support-executive-summary-generator.md +0 -64
  626. package/agents/support-finance-tracker.md +0 -145
  627. package/agents/support-legal-compliance-checker.md +0 -129
  628. package/agents/support-support-responder.md +0 -91
  629. package/agents/testing-accessibility-auditor.md +0 -110
  630. package/agents/testing-test-results-analyzer.md +0 -97
  631. package/agents/testing-tool-evaluator.md +0 -76
  632. package/agents/testing-workflow-optimizer.md +0 -99
  633. package/agents/user-research.md +0 -40
@@ -0,0 +1,727 @@
1
+ # Migration & Legacy Stores
2
+
3
+ > **Scope:** Migrating sensitive data from UserDefaults, plists, NSCoding archives, and other insecure storage to Apple Keychain Services. Covers secure deletion of legacy data, first-launch keychain cleanup, versioned migration patterns, and the Team ID transfer edge case.
4
+ >
5
+ > **Applies to:** iOS 15+ (actor support, pre-warming), iOS 17+ (recommended deployment target)
6
+ >
7
+ > **Cross-references:** `keychain-fundamentals.md` (SecItem CRUD), `keychain-access-control.md` (accessibility classes), `common-anti-patterns.md` (UserDefaults secrets anti-pattern), `credential-storage-patterns.md` (token lifecycle post-migration), `testing-security-code.md` (protocol-based mocking)
8
+
9
+ ---
10
+
11
+ ## Why Migrate — The Risk of Legacy Storage
12
+
13
+ UserDefaults, `.plist` files, and NSCoding archives store data as unencrypted plaintext within the app sandbox. This data is readable on jailbroken devices and included in unencrypted iTunes/Finder backups — anyone with backup access can extract tokens, passwords, and PII. OWASP ranks insecure data storage as a top-10 mobile risk (M9).
14
+
15
+ | Store | Encrypted at rest | In backups | Survives app uninstall | Suitable for secrets |
16
+ | ----------------- | ----------------- | ---------------------------------- | ---------------------- | -------------------- |
17
+ | UserDefaults | No | Yes | No | **No** |
18
+ | .plist files | No (default) | Yes | No | **No** |
19
+ | NSCoding archives | No (default) | Yes | No | **No** |
20
+ | Keychain | Yes (AES-256-GCM) | `ThisDeviceOnly` variants excluded | **Yes** | **Yes** |
21
+
22
+ Keychain items are managed by the `securityd` daemon, encrypted with per-row keys protected by the Secure Enclave, and isolated from the app sandbox. This is the only appropriate location for tokens, passwords, API keys, and PII on Apple platforms.
23
+
24
+ ---
25
+
26
+ ## The Five Correctness Traps
27
+
28
+ Most AI-generated migration code contains at least one of these errors. Each passes testing but fails catastrophically in production.
29
+
30
+ **Trap 1 — Legacy data survives after migration.** Calling `UserDefaults.standard.removeObject(forKey:)` removes the key-value pair from the in-memory cache and plist file, but does not securely overwrite NAND flash. However, iOS achieves secure deletion through _cryptographic erasure_: every file has a per-file AES-256 key, and standard deletion APIs destroy that key via Effaceable Storage, rendering physical bits permanently inaccessible. The real risk vector is **unencrypted backups** created before migration completes — the plist stays on disk until the filesystem reclaims space. **Always delete all legacy keys explicitly after verified keychain writes.**
31
+
32
+ **Trap 2 — Keychain items survive app deletion.** When a user uninstalls your app, UserDefaults and sandbox files are wiped, but keychain items persist indefinitely. Apple attempted to change this in iOS 10.3 betas but reverted due to compatibility issues. On reinstall, stale keychain items (old tokens, expired credentials, outdated schemas) cause silent authentication failures or — worse — restore a _previous user's_ session.
33
+
34
+ **Trap 3 — Migration runs on every launch.** Checking UserDefaults for legacy data on every launch wastes cycles and risks data loss during iOS 15+ app pre-warming. When the system pre-warms your process before the device is unlocked, `UserDefaults` may return empty values (the encrypted plist is inaccessible). A migration that interprets empty results as "nothing to migrate" will skip real data or overwrite valid keychain entries with nil.
35
+
36
+ **Trap 4 — Non-atomic migration leaves data in limbo.** Writing to keychain then deleting from UserDefaults as two independent operations creates a failure window. If the app is killed between write and delete — or the keychain write silently fails — users lose their data entirely.
37
+
38
+ **Trap 5 — Changing `kSecAttrService` or `kSecAttrAccount` orphans existing items.** These attributes form the primary key for `kSecClassGenericPassword`. Changing either in a new version doesn't update existing items — it creates new ones. The old items become invisible orphans that waste keychain space and cause `errSecDuplicateItem` in unexpected contexts. Critically, `SecItemUpdate` **cannot change primary key attributes** — the call will error. You must perform a full rekey migration: read old → write new → verify → delete old.
39
+
40
+ ---
41
+
42
+ ## First-Launch Keychain Cleanup
43
+
44
+ The persistence asymmetry (UserDefaults deleted on uninstall, keychain not) enables a reliable reinstall detector. This pattern **must run before any other keychain or SDK initialization** — Firebase, analytics, and auth libraries all read keychain items during setup.
45
+
46
+ ```swift
47
+ // ✅ CORRECT: First-launch cleanup with protected data guard
48
+ // iOS 15+ required for isProtectedDataAvailable / pre-warming behavior
49
+
50
+ actor FirstLaunchGuard {
51
+ static let shared = FirstLaunchGuard()
52
+ private let hasRunKey = "com.myapp.hasCompletedFirstLaunch"
53
+
54
+ /// Call at the very start of app lifecycle, before SDK initialization.
55
+ func performCleanupIfNeeded() async {
56
+ let isSubsequentRun = UserDefaults.standard.bool(forKey: hasRunKey)
57
+ guard !isSubsequentRun else { return }
58
+
59
+ // iOS 15+ pre-warming guard: device may still be locked
60
+ guard await isProtectedDataAvailable() else {
61
+ await waitForProtectedData()
62
+ return
63
+ }
64
+
65
+ // Wipe stale keychain items from a previous installation
66
+ deleteAllKeychainItems()
67
+
68
+ // Set flag so this only runs once per install
69
+ UserDefaults.standard.set(true, forKey: hasRunKey)
70
+ }
71
+
72
+ private func deleteAllKeychainItems() {
73
+ let classes: [CFString] = [
74
+ kSecClassGenericPassword, kSecClassInternetPassword,
75
+ kSecClassCertificate, kSecClassKey, kSecClassIdentity
76
+ ]
77
+ for itemClass in classes {
78
+ let query: NSDictionary = [
79
+ kSecClass: itemClass,
80
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny
81
+ ]
82
+ SecItemDelete(query)
83
+ }
84
+ }
85
+
86
+ private func isProtectedDataAvailable() async -> Bool {
87
+ await MainActor.run {
88
+ UIApplication.shared.isProtectedDataAvailable
89
+ }
90
+ }
91
+
92
+ private func waitForProtectedData() async {
93
+ await withCheckedContinuation { continuation in
94
+ NotificationCenter.default.addObserver(
95
+ forName: UIApplication.protectedDataDidBecomeAvailableNotification,
96
+ object: nil, queue: .main
97
+ ) { _ in
98
+ Task {
99
+ self.deleteAllKeychainItems()
100
+ UserDefaults.standard.set(true, forKey: self.hasRunKey)
101
+ continuation.resume()
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ```swift
110
+ // ❌ INCORRECT: No first-launch cleanup — stale keychain from previous install
111
+ @main
112
+ struct BrokenApp: App {
113
+ init() {
114
+ // Reads keychain without checking for stale data
115
+ if let token = try? keychainRead(service: "com.myapp", account: "authToken") {
116
+ // This token might be from a PREVIOUS user who deleted the app.
117
+ // The new user inherits someone else's session.
118
+ AuthManager.shared.restoreSession(token: token)
119
+ }
120
+ }
121
+ var body: some Scene { WindowGroup { ContentView() } }
122
+ }
123
+ ```
124
+
125
+ The `isProtectedDataAvailable` check is critical. iOS 15 introduced app pre-warming — the system can launch your process before the user unlocks the device. During pre-warming, both UserDefaults and keychain items with `kSecAttrAccessibleWhenUnlocked` are unavailable. Multiple high-profile apps (including Twitter) suffered mass user logouts on iOS 15 because their startup code interpreted empty data during pre-warm as "no credentials" and wiped sessions.
126
+
127
+ > **Include `kSecAttrSynchronizableAny`** in cleanup queries. Without it, `SecItemDelete` skips iCloud-synced items, leaving them as invisible ghosts.
128
+
129
+ ---
130
+
131
+ ## Atomic Migration: Read → Write → Verify → Delete
132
+
133
+ The most dangerous pattern is deleting legacy data before confirming the keychain write succeeded. The correct sequence is always: **read → write → verify → delete**.
134
+
135
+ ```swift
136
+ // ✅ CORRECT: Atomic per-key migration with verification and rollback
137
+ actor AtomicMigrator {
138
+ struct MigrationResult {
139
+ let key: String
140
+ let succeeded: Bool
141
+ let error: Error?
142
+ }
143
+
144
+ private let keychain: any MigrationKeychainProtocol
145
+
146
+ init(keychain: any MigrationKeychainProtocol) {
147
+ self.keychain = keychain
148
+ }
149
+
150
+ /// Failed keys remain in UserDefaults for retry on next launch.
151
+ func migrateUserDefaultsKeys(
152
+ _ keys: [String],
153
+ service: String,
154
+ accessible: CFString = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
155
+ ) async -> [MigrationResult] {
156
+ var results: [MigrationResult] = []
157
+
158
+ for key in keys {
159
+ do {
160
+ // STEP 1: Read from legacy storage
161
+ guard let legacyValue = UserDefaults.standard.string(forKey: key),
162
+ let data = legacyValue.data(using: .utf8) else {
163
+ results.append(.init(key: key, succeeded: true, error: nil))
164
+ continue
165
+ }
166
+
167
+ // STEP 2: Write to keychain (add-or-update handles duplicates)
168
+ try await keychain.save(data, service: service,
169
+ account: key, accessible: accessible)
170
+
171
+ // STEP 3: Verify by reading back
172
+ let readBack = try await keychain.read(service: service, account: key)
173
+ guard readBack == data else {
174
+ throw MigrationError.verificationFailed(key: key)
175
+ }
176
+
177
+ // STEP 4: Delete from UserDefaults ONLY after verified write
178
+ UserDefaults.standard.removeObject(forKey: key)
179
+ results.append(.init(key: key, succeeded: true, error: nil))
180
+
181
+ } catch {
182
+ // ROLLBACK: Leave UserDefaults intact for this key
183
+ results.append(.init(key: key, succeeded: false, error: error))
184
+ }
185
+ }
186
+ return results
187
+ }
188
+
189
+ enum MigrationError: Error {
190
+ case verificationFailed(key: String)
191
+ case corruptArchive(path: String)
192
+ }
193
+ }
194
+ ```
195
+
196
+ ```swift
197
+ // ❌ INCORRECT: Deletes legacy data BEFORE verifying keychain write
198
+ func dangerousMigration() {
199
+ let keys = ["authToken", "refreshToken"]
200
+ for key in keys {
201
+ guard let value = UserDefaults.standard.string(forKey: key) else { continue }
202
+
203
+ // Deletes FIRST — if keychain write fails, data is gone forever
204
+ UserDefaults.standard.removeObject(forKey: key) // ← CATASTROPHIC
205
+
206
+ let query: [String: Any] = [
207
+ kSecClass as String: kSecClassGenericPassword,
208
+ kSecAttrService as String: "com.myapp",
209
+ kSecAttrAccount as String: key,
210
+ kSecValueData as String: value.data(using: .utf8)!
211
+ ]
212
+ let status = SecItemAdd(query as CFDictionary, nil)
213
+ // If status != errSecSuccess, the token is permanently lost.
214
+ }
215
+ }
216
+ ```
217
+
218
+ The migration is **idempotent by design**: already-migrated keys return `nil` from UserDefaults in Step 1 and are skipped. Failed keys retain their original values, ready for retry. This makes it safe to re-run after crash, app kill, or OOM termination.
219
+
220
+ ---
221
+
222
+ ## Versioned Migration with Schema Tracking
223
+
224
+ A production system needs version tracking to avoid re-running completed migrations and to handle users who skip versions. The schema version belongs in the **keychain** (survives reinstalls), not UserDefaults.
225
+
226
+ ```swift
227
+ // ✅ CORRECT: Versioned chain migration with schema version in keychain
228
+ actor MigrationCoordinator {
229
+ static let shared = MigrationCoordinator()
230
+
231
+ private let serviceName = "com.myapp.credentials"
232
+ private let schemaVersionAccount = "com.myapp.schema.version"
233
+ private static let currentSchemaVersion: Int = 3
234
+
235
+ enum MigrationState {
236
+ case upToDate
237
+ case migrated(from: Int, to: Int)
238
+ case deferred(reason: String)
239
+ case failed(Error)
240
+ }
241
+
242
+ func migrateIfNeeded() async -> MigrationState {
243
+ // Guard: protected data must be available (pre-warming defense)
244
+ let dataAvailable = await MainActor.run {
245
+ UIApplication.shared.isProtectedDataAvailable
246
+ }
247
+ guard dataAvailable else {
248
+ return .deferred(reason: "Device locked — protected data unavailable")
249
+ }
250
+
251
+ let storedVersion = readSchemaVersion()
252
+ guard storedVersion < Self.currentSchemaVersion else { return .upToDate }
253
+
254
+ do {
255
+ // Chain migration: each step runs sequentially
256
+ if storedVersion < 1 {
257
+ try await migrateV0toV1_UserDefaultsToKeychain()
258
+ }
259
+ if storedVersion < 2 {
260
+ try await migrateV1toV2_NSCodingArchivesToKeychain()
261
+ }
262
+ if storedVersion < 3 {
263
+ try await migrateV2toV3_UpgradeAccessibilityClass()
264
+ }
265
+
266
+ // Update version ONLY after all steps succeed
267
+ try saveSchemaVersion(Self.currentSchemaVersion)
268
+ return .migrated(from: storedVersion, to: Self.currentSchemaVersion)
269
+ } catch {
270
+ // Do NOT update schema version — retry on next launch
271
+ os_log(.error, log: .migration,
272
+ "Migration failed: %{public}@", error.localizedDescription)
273
+ return .failed(error)
274
+ }
275
+ }
276
+
277
+ // MARK: - Schema Version (stored in keychain, survives reinstall)
278
+
279
+ private func readSchemaVersion() -> Int {
280
+ guard let data = try? keychainRead(
281
+ service: serviceName, account: schemaVersionAccount),
282
+ let str = String(data: data, encoding: .utf8),
283
+ let version = Int(str) else { return 0 }
284
+ return version
285
+ }
286
+
287
+ private func saveSchemaVersion(_ version: Int) throws {
288
+ let data = "\(version)".data(using: .utf8)!
289
+ try keychainSave(data, service: serviceName,
290
+ account: schemaVersionAccount)
291
+ }
292
+
293
+ // MARK: - V1: UserDefaults → Keychain
294
+
295
+ private func migrateV0toV1_UserDefaultsToKeychain() async throws {
296
+ let migrator = AtomicMigrator(keychain: KeychainManager.shared)
297
+ let results = await migrator.migrateUserDefaultsKeys(
298
+ ["authToken", "refreshToken", "apiSecret"],
299
+ service: serviceName
300
+ )
301
+ // Check for critical failures (non-nil keys that didn't migrate)
302
+ let failures = results.filter { !$0.succeeded }
303
+ if !failures.isEmpty {
304
+ os_log(.error, log: .migration,
305
+ "V1 migration: %d keys failed", failures.count)
306
+ }
307
+ // Force-sync UserDefaults deletions to disk
308
+ UserDefaults.standard.synchronize()
309
+ }
310
+
311
+ // MARK: - V2: NSCoding Archives → Keychain
312
+
313
+ private func migrateV1toV2_NSCodingArchivesToKeychain() async throws {
314
+ let documentsURL = FileManager.default.urls(
315
+ for: .documentDirectory, in: .userDomainMask).first!
316
+ let archiveURL = documentsURL.appendingPathComponent("UserSession.archive")
317
+
318
+ guard FileManager.default.fileExists(atPath: archiveURL.path) else { return }
319
+
320
+ let archiveData = try Data(contentsOf: archiveURL)
321
+ guard let session = try NSKeyedUnarchiver.unarchivedObject(
322
+ ofClass: LegacySession.self, from: archiveData) else {
323
+ throw AtomicMigrator.MigrationError.corruptArchive(path: archiveURL.path)
324
+ }
325
+
326
+ let sessionData = try JSONEncoder().encode(session.toModernSession())
327
+ try keychainSave(sessionData, service: serviceName, account: "userSession")
328
+
329
+ // Verify before deleting archive file
330
+ let verified = try keychainRead(service: serviceName, account: "userSession")
331
+ guard verified == sessionData else {
332
+ throw AtomicMigrator.MigrationError.verificationFailed(key: "userSession")
333
+ }
334
+ try FileManager.default.removeItem(at: archiveURL)
335
+ }
336
+
337
+ // MARK: - V3: Upgrade accessibility class on existing items
338
+
339
+ private func migrateV2toV3_UpgradeAccessibilityClass() async throws {
340
+ let accounts = ["authToken", "refreshToken", "apiSecret", "userSession"]
341
+ for account in accounts {
342
+ guard let data = try? keychainRead(
343
+ service: serviceName, account: account) else { continue }
344
+ // Re-save with updated accessibility — add-or-update pattern
345
+ // updates the accessibility class via SecItemUpdate
346
+ try keychainSave(data, service: serviceName, account: account,
347
+ accessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
348
+ }
349
+ }
350
+ }
351
+
352
+ private extension OSLog {
353
+ static let migration = OSLog(
354
+ subsystem: Bundle.main.bundleIdentifier ?? "com.myapp",
355
+ category: "KeychainMigration"
356
+ )
357
+ }
358
+ ```
359
+
360
+ ```swift
361
+ // ❌ INCORRECT: Runs every launch, no version check, no verification, no legacy delete
362
+ func brokenMigration() {
363
+ // No version check — runs every single launch
364
+ // No isProtectedDataAvailable check — fails during pre-warm
365
+ if let token = UserDefaults.standard.string(forKey: "authToken") {
366
+ let query: [String: Any] = [
367
+ kSecClass as String: kSecClassGenericPassword,
368
+ kSecAttrService as String: "com.myapp",
369
+ kSecAttrAccount as String: "authToken",
370
+ kSecValueData as String: token.data(using: .utf8)!
371
+ ]
372
+ // No errSecDuplicateItem handling — crashes on second launch
373
+ SecItemAdd(query as CFDictionary, nil)
374
+ // Never deletes from UserDefaults — plaintext secret persists
375
+ // No verification that write succeeded
376
+ }
377
+ }
378
+ ```
379
+
380
+ The chain migration approach (v1 → v2 → v3 sequentially) is deliberately chosen over direct migration because it reuses tested migration logic from each version. For users upgrading from v1.0 directly to v3.0, all three steps run. The schema version only advances after all steps succeed — a crash mid-migration leaves the version at the old number for clean retry.
381
+
382
+ ---
383
+
384
+ ## Orphaned Items: Why You Must Never Rename kSecAttrService
385
+
386
+ ```swift
387
+ // ❌ INCORRECT: SecItemUpdate CANNOT change primary key attributes
388
+ let query: [String: Any] = [
389
+ kSecClass as String: kSecClassGenericPassword,
390
+ kSecAttrService as String: "OldServiceName",
391
+ kSecAttrAccount as String: "authToken"
392
+ ]
393
+ let update: [String: Any] = [
394
+ kSecAttrService as String: "com.mycompany.myapp" // ERROR: primary key
395
+ ]
396
+ // SecItemUpdate returns an error — primary keys are immutable via Update
397
+ SecItemUpdate(query as CFDictionary, update as CFDictionary)
398
+ ```
399
+
400
+ ```swift
401
+ // ✅ CORRECT: Full rekey migration when service name must change
402
+ func migrateServiceName() async throws {
403
+ let oldService = "OldServiceName"
404
+ let newService = "com.mycompany.myapp"
405
+ let accounts = ["authToken", "refreshToken"]
406
+
407
+ for account in accounts {
408
+ let oldData: Data
409
+ do {
410
+ oldData = try keychainRead(service: oldService, account: account)
411
+ } catch { continue } // Already migrated or never existed
412
+
413
+ try keychainSave(oldData, service: newService, account: account)
414
+
415
+ // Verify new location before deleting old
416
+ let verified = try keychainRead(service: newService, account: account)
417
+ guard verified == oldData else {
418
+ throw AtomicMigrator.MigrationError.verificationFailed(key: account)
419
+ }
420
+ try keychainDelete(service: oldService, account: account)
421
+ }
422
+ }
423
+ ```
424
+
425
+ **Lock down your `kSecAttrService` value early and never change it.** Use your bundle identifier (e.g., `com.mycompany.myapp`) — it's unique, stable, and conventional.
426
+
427
+ ---
428
+
429
+ ## Background Launch and the Locked-Device Trap
430
+
431
+ iOS 15+ pre-warming and background execution (push notifications, background fetch, Live Activities) can launch your app while the device is locked. The `kSecAttrAccessible` value you choose determines whether keychain operations succeed in these contexts.
432
+
433
+ > For the complete accessibility constant selection matrix with data protection tiers and security trade-offs, see `keychain-access-control.md` § The "When" Layer: Seven Accessibility Constants. The table below summarizes the four constants most relevant to background migration scenarios.
434
+
435
+ | Accessibility constant | Available when locked | Background safe | Notes |
436
+ | -------------------------------------------------- | --------------------- | --------------- | ---------------------------------------------------- |
437
+ | `kSecAttrAccessibleWhenUnlocked` (default) | No | No | Foreground only |
438
+ | `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | After first unlock | Yes | **Recommended** — background + device-bound |
439
+ | `kSecAttrAccessibleAfterFirstUnlock` | After first unlock | Yes | Background + backup migration (use only when needed) |
440
+ | `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | No | No | Biometric-gated items |
441
+ | `kSecAttrAccessibleAlways` | Yes | Yes | **Deprecated iOS 12** — do not use |
442
+
443
+ **Recommended default for migrated credentials:** `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` — background-safe, not synced to iCloud, not included in backups. Apple uses `AfterFirstUnlock` for Wi-Fi passwords and mail account credentials.
444
+
445
+ A critical trap: **`SecItemDelete` does NOT require the item's protection-class key material** — it succeeds even when the item's data is unreadable due to lock state. This enables a devastating anti-pattern:
446
+
447
+ ```swift
448
+ // ❌ DANGEROUS: Delete-on-read-failure destroys data during background launch
449
+ func dangerousTokenRefresh() {
450
+ var result: AnyObject?
451
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
452
+
453
+ if status != errSecSuccess {
454
+ // "Can't read? Must be corrupted. Delete and start fresh."
455
+ SecItemDelete(query as CFDictionary) // ← DESTROYS VALID TOKEN
456
+ // During background launch with WhenUnlocked, the read fails
457
+ // with -25308 (interaction not allowed), but delete succeeds.
458
+ }
459
+ }
460
+
461
+ // ✅ CORRECT: Distinguish "not found" from "device locked"
462
+ func safeTokenRead() throws -> Data? {
463
+ var result: AnyObject?
464
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
465
+
466
+ switch status {
467
+ case errSecSuccess:
468
+ return result as? Data
469
+ case errSecItemNotFound:
470
+ return nil // Genuinely absent
471
+ case errSecInteractionNotAllowed:
472
+ // Device locked — item exists but unreadable right now.
473
+ // Do NOT delete. Do NOT treat as missing. Retry later.
474
+ throw KeychainError.interactionNotAllowed
475
+ default:
476
+ throw KeychainError.unexpectedStatus(status)
477
+ }
478
+ }
479
+ ```
480
+
481
+ **Migration rule:** Always guard migration behind `UIApplication.shared.isProtectedDataAvailable`. If the device is locked, defer using `protectedDataDidBecomeAvailableNotification`. Never interpret an empty read during a locked state as "nothing to migrate."
482
+
483
+ ---
484
+
485
+ ## The Phantom Mismatch Bug
486
+
487
+ Including `kSecAttrAccessible` in a search query causes a "not-found then duplicate" paradox. The search filters by accessibility class, but the item was stored with a different class — so `SecItemCopyMatching` returns `errSecItemNotFound` while `SecItemAdd` sees the item via primary key and returns `errSecDuplicateItem`.
488
+
489
+ ```swift
490
+ // ❌ INCORRECT: kSecAttrAccessible in search query causes phantom mismatches
491
+ let query: [String: Any] = [
492
+ kSecClass as String: kSecClassGenericPassword,
493
+ kSecAttrService as String: service,
494
+ kSecAttrAccount as String: account,
495
+ kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked, // ← BUG
496
+ kSecReturnData as String: kCFBooleanTrue as Any
497
+ ]
498
+ // If stored with AfterFirstUnlock, query returns errSecItemNotFound.
499
+ // But SecItemAdd sees the item via primary key → errSecDuplicateItem. Deadlock.
500
+ ```
501
+
502
+ **Rule:** Use **only primary key attributes** (`kSecClass`, `kSecAttrService`, `kSecAttrAccount`) in search queries. Set `kSecAttrAccessible` only during `SecItemAdd` or in the update dictionary of `SecItemUpdate`.
503
+
504
+ ```swift
505
+ // ✅ CORRECT: search by primary key only
506
+ let query: [String: Any] = [
507
+ kSecClass as String: kSecClassGenericPassword,
508
+ kSecAttrService as String: service,
509
+ kSecAttrAccount as String: account,
510
+ kSecReturnData as String: kCFBooleanTrue as Any
511
+ ]
512
+ ```
513
+
514
+ ---
515
+
516
+ ## Team ID Change: The App Transfer Edge Case
517
+
518
+ When an app is transferred to a different Apple Developer account, the Team ID changes. Keychain access is permanently tied to the original Team ID — all existing keychain items become inaccessible under the new signing identity. Users are effectively logged out and lose all locally stored secrets on the first launch after updating.
519
+
520
+ **If a Team ID change is unavoidable**, you must release a "bridge" update under the **old** Team ID before the transfer:
521
+
522
+ 1. Bridge update reads all keychain items and exports them to a temporary, app-group-shared container (or encrypted file in the app sandbox)
523
+ 2. Transfer the app to the new developer account
524
+ 3. First release under the new Team ID reads from the temporary store, writes to the new keychain, verifies, and deletes the temporary data
525
+
526
+ This is a one-way operation and must be planned well in advance. There is no way to recover keychain items after a Team ID change without the bridge update.
527
+
528
+ ---
529
+
530
+ ## Deferred Legacy Cleanup with Rollback Window
531
+
532
+ The safest approach keeps legacy data as backup for one release cycle after migration. Track a migration timestamp in keychain:
533
+
534
+ ```swift
535
+ // ✅ CORRECT: Deferred cleanup with 30-day rollback window
536
+ actor DeferredCleanup {
537
+ private let cleanupDelayDays = 30
538
+ private let timestampAccount = "com.myapp.migration.timestamp"
539
+ private let serviceName = "com.myapp.credentials"
540
+
541
+ func cleanupIfExpired() async {
542
+ guard let data = try? keychainRead(
543
+ service: serviceName, account: timestampAccount),
544
+ let str = String(data: data, encoding: .utf8),
545
+ let migrationDate = ISO8601DateFormatter().date(from: str) else { return }
546
+
547
+ let days = Calendar.current.dateComponents(
548
+ [.day], from: migrationDate, to: Date()).day ?? 0
549
+ guard days >= cleanupDelayDays else { return }
550
+
551
+ // Past rollback window — safe to permanently delete legacy files
552
+ let documentsURL = FileManager.default.urls(
553
+ for: .documentDirectory, in: .userDomainMask).first!
554
+ for file in ["UserSession.archive", "Credentials.plist", "TokenCache.dat"] {
555
+ try? FileManager.default.removeItem(
556
+ at: documentsURL.appendingPathComponent(file))
557
+ }
558
+ if let bundleID = Bundle.main.bundleIdentifier {
559
+ UserDefaults.standard.removePersistentDomain(forName: bundleID)
560
+ }
561
+ }
562
+ }
563
+ ```
564
+
565
+ ---
566
+
567
+ ## Complete App Launch Sequence
568
+
569
+ The correct ordering at app startup is critical. Keychain cleanup must happen before SDK initialization, migration must wait for protected data, and schema version gates all logic.
570
+
571
+ ```swift
572
+ // ✅ CORRECT: Complete launch sequence with migration
573
+ @main
574
+ struct MyApp: App {
575
+ @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
576
+ var body: some Scene { WindowGroup { ContentView() } }
577
+ }
578
+
579
+ class AppDelegate: NSObject, UIApplicationDelegate {
580
+ func application(
581
+ _ application: UIApplication,
582
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
583
+ ) -> Bool {
584
+ Task {
585
+ // 1. First-launch cleanup (stale keychain from previous install)
586
+ await FirstLaunchGuard.shared.performCleanupIfNeeded()
587
+
588
+ // 2. Versioned migration
589
+ let state = await MigrationCoordinator.shared.migrateIfNeeded()
590
+ switch state {
591
+ case .upToDate: break
592
+ case .migrated(let from, let to):
593
+ os_log(.info, "Migrated schema v%d → v%d", from, to)
594
+ case .deferred(let reason):
595
+ os_log(.info, "Migration deferred: %{public}@", reason)
596
+ case .failed(let error):
597
+ os_log(.error, "Migration failed: %{public}@",
598
+ error.localizedDescription)
599
+ }
600
+
601
+ // 3. Deferred cleanup of legacy files past rollback window
602
+ await DeferredCleanup().cleanupIfExpired()
603
+
604
+ // 4. NOW initialize Firebase, analytics, auth SDKs
605
+ // Stale data cleared, migration complete or safely deferred
606
+ }
607
+ return true
608
+ }
609
+ }
610
+ ```
611
+
612
+ ---
613
+
614
+ ## Thread Safety Note
615
+
616
+ > **Cross-validation note:** One research source claims SecItem C-APIs are non-thread-safe and recommends a serial `DispatchQueue`. Apple's documentation and Quinn "The Eskimo" (DTS) confirm that **SecItem\* functions are thread-safe on iOS**. However, your wrapper's mutable state (caches, migration flags, version tracking) does need synchronization. An `actor` provides this naturally in modern Swift concurrency — prefer actors over serial queues for new code (iOS 15+).
617
+
618
+ ---
619
+
620
+ ## Testing Migration Paths
621
+
622
+ Keychain behavior differs between Simulator and real devices:
623
+
624
+ | Aspect | Simulator | Real device |
625
+ | ----------------------------- | ------------------------ | -------------------------------------- |
626
+ | Data Protection enforcement | Not enforced | Fully enforced (hardware) |
627
+ | Keychain entitlements | Loosely enforced | Strictly enforced |
628
+ | `errSecInteractionNotAllowed` | Rarely triggered | Triggered when locked |
629
+ | Lock state testing | Cannot meaningfully test | Essential for accessibility validation |
630
+
631
+ Use **protocol-based abstraction** for unit tests (runs in CI on simulators) and real-device integration tests for accessibility-class validation:
632
+
633
+ ```swift
634
+ // ✅ Protocol-based keychain abstraction for testable migrations
635
+ protocol MigrationKeychainProtocol: Actor {
636
+ func save(_ data: Data, service: String, account: String,
637
+ accessible: CFString) throws
638
+ func read(service: String, account: String) throws -> Data
639
+ func delete(service: String, account: String) throws
640
+ func deleteAll()
641
+ }
642
+
643
+ // In-memory mock for unit tests
644
+ actor MockMigrationKeychain: MigrationKeychainProtocol {
645
+ var store: [String: [String: Data]] = [:]
646
+ var simulatedError: KeychainError?
647
+
648
+ func save(_ data: Data, service: String, account: String,
649
+ accessible: CFString) throws {
650
+ if let error = simulatedError { throw error }
651
+ store[service, default: [:]][account] = data
652
+ }
653
+
654
+ func read(service: String, account: String) throws -> Data {
655
+ if let error = simulatedError { throw error }
656
+ guard let data = store[service]?[account] else {
657
+ throw KeychainError.itemNotFound
658
+ }
659
+ return data
660
+ }
661
+
662
+ func delete(service: String, account: String) throws {
663
+ store[service]?[account] = nil
664
+ }
665
+
666
+ func deleteAll() { store.removeAll() }
667
+ }
668
+ ```
669
+
670
+ ```swift
671
+ // ✅ Example: verify atomic behavior — legacy data preserved on failure
672
+ @Test func migrationPreservesLegacyDataOnKeychainFailure() async {
673
+ let mock = MockMigrationKeychain()
674
+ mock.simulatedError = .unexpectedStatus(-25308) // Simulate locked device
675
+
676
+ let defaults = UserDefaults(suiteName: "test")!
677
+ defaults.set("secret-token", forKey: "authToken")
678
+
679
+ let migrator = AtomicMigrator(keychain: mock)
680
+ let results = await migrator.migrateUserDefaultsKeys(
681
+ ["authToken"], service: "com.myapp"
682
+ )
683
+
684
+ #expect(results.contains(where: { !$0.succeeded }))
685
+ #expect(defaults.string(forKey: "authToken") == "secret-token") // Still intact
686
+ }
687
+ ```
688
+
689
+ Always clean up keychain items in `setUp()`/`tearDown()` — items persist between test runs on the same simulator. For integration tests hitting real keychain, create a Test Host app target with the Keychain capability enabled.
690
+
691
+ ---
692
+
693
+ ## Handling Very Old Versions and Collapse Strategy
694
+
695
+ The App Store always delivers the latest binary — a user jumping from v1.0 to v3.0 never installs v2.0. Your v3.0 binary must contain migration logic for every historical schema version.
696
+
697
+ Pragmatically, after sufficient time (when analytics show <1% of users on legacy versions), **collapse old migrations into a single mega-migration** from v0 to current, reducing code maintenance. For users on versions so old that the legacy format is unknown or corrupted, the migration should **fail gracefully** and prompt a fresh login rather than crashing.
698
+
699
+ ---
700
+
701
+ ## Secure Deletion: Trust Cryptographic Erasure
702
+
703
+ Do **not** attempt to manually overwrite files with zeros or random bytes before deletion — NAND flash wear-leveling makes this ineffective and wastes write cycles. iOS handles secure deletion through cryptographic erasure: every file has a per-file AES-256 key, and when the file is deleted via standard APIs (`FileManager.removeItem`, `UserDefaults.removeObject`), iOS destroys the per-file key through Effaceable Storage, rendering the physical bits permanently unrecoverable.
704
+
705
+ Standard deletion APIs are sufficient. The residual risk is unencrypted backups created _before_ migration — encourage users to use encrypted backups, and delete legacy data promptly after verified migration.
706
+
707
+ ---
708
+
709
+ ## Conclusion
710
+
711
+ The core insight of safe keychain migration: **deletion is the irreversible step, not the write**. Every pattern in this file follows from that principle — verify before deleting, defer when uncertain, and treat keychain persistence across reinstalls as a feature to plan for rather than a bug to fight. The five most impactful decisions are: using `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` for background-safe encrypted storage, implementing first-launch cleanup before SDK initialization, storing schema versions in keychain rather than UserDefaults, gating all migration behind `isProtectedDataAvailable`, and never changing `kSecAttrService` after shipping.
712
+
713
+ ---
714
+
715
+ ## Summary Checklist
716
+
717
+ 1. **First-launch cleanup runs before any SDK initialization** — uses UserDefaults flag to detect reinstall, wipes stale keychain items, includes `kSecAttrSynchronizableAny` to catch iCloud-synced items
718
+ 2. **Migration is atomic: read → write → verify → delete** — legacy data is never deleted until keychain write is confirmed by read-back; failed keys remain intact for retry
719
+ 3. **Schema version stored in keychain, not UserDefaults** — survives app reinstall; version only advances after all migration steps succeed
720
+ 4. **Protected data availability checked before any migration** — guards against iOS 15+ pre-warming and locked-device scenarios; defers via `protectedDataDidBecomeAvailableNotification`
721
+ 5. **`errSecInteractionNotAllowed` (-25308) is never treated as "item missing"** — distinguishes locked-device failures from genuine absence; never deletes on read failure without checking status code
722
+ 6. **`kSecAttrService` and `kSecAttrAccount` are immutable after shipping** — changing either orphans existing items; `SecItemUpdate` cannot modify primary keys; use full rekey migration if change is unavoidable
723
+ 7. **`kSecAttrAccessible` is never included in search queries** — causes phantom "not-found then duplicate" mismatches; set only during add or in update dictionary
724
+ 8. **Default accessibility is `AfterFirstUnlockThisDeviceOnly`** — background-safe, not synced, not backed up; matches Apple's own credential storage patterns
725
+ 9. **Deferred legacy cleanup with rollback window** — keep legacy data for 30 days post-migration as safety net; timestamp stored in keychain
726
+ 10. **Team ID changes sever all keychain access** — must release bridge update under old Team ID before app transfer; no recovery possible after transfer without bridge
727
+ 11. **Migration tested via protocol-based abstraction** — mock keychain in unit tests; real-device integration tests for accessibility class validation; clean up items in setUp/tearDown