@mmerterden/multi-agent-pipeline 8.6.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.
- package/CHANGELOG.md +2623 -0
- package/LICENSE +21 -0
- package/README.md +852 -0
- package/docs/FIGMA_PIPELINE.md +138 -0
- package/docs/GENERICITY-REVIEW.md +277 -0
- package/docs/STABILITY-FIX-PLAN.md +168 -0
- package/docs/adr/0001-three-model-triage.md +81 -0
- package/docs/adr/0002-instruction-driven-flag.md +62 -0
- package/docs/adr/0003-unified-shared-skills.md +55 -0
- package/docs/adr/0004-zero-dependency-philosophy.md +60 -0
- package/docs/adr/0005-lazy-phase-docs.md +68 -0
- package/docs/adr/0006-skills-core-external-split.md +52 -0
- package/docs/adr/0007-multi-tool-adapter-framework.md +110 -0
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +98 -0
- package/docs/adr/README.md +33 -0
- package/docs/architecture.md +181 -0
- package/docs/best-practices.md +93 -0
- package/docs/features.md +274 -0
- package/docs/performance.md +116 -0
- package/docs/recovery-guide.md +479 -0
- package/index.js +76 -0
- package/install/_adapters.mjs +69 -0
- package/install/_common.mjs +150 -0
- package/install/_copilot-instructions.mjs +32 -0
- package/install/_dev-only-files.mjs +23 -0
- package/install/_platform-filter.mjs +132 -0
- package/install/_telemetry.mjs +79 -0
- package/install/claude.mjs +332 -0
- package/install/copilot.mjs +254 -0
- package/install/index.mjs +179 -0
- package/install/templates/copilot-instructions.md +319 -0
- package/install.js +24 -0
- package/package.json +78 -0
- package/pipeline/adapters/_base.mjs +288 -0
- package/pipeline/adapters/copilot-chat.mjs +158 -0
- package/pipeline/adapters/cursor.mjs +187 -0
- package/pipeline/agents/android-architect.md +42 -0
- package/pipeline/agents/backend-architect.md +43 -0
- package/pipeline/agents/code-reviewer.md +57 -0
- package/pipeline/agents/dev-critic.md +148 -0
- package/pipeline/agents/explorer.md +34 -0
- package/pipeline/agents/ios-architect.md +41 -0
- package/pipeline/agents/security-auditor.md +98 -0
- package/pipeline/agents/task-clarifier.md +113 -0
- package/pipeline/claude-md-template.md +55 -0
- package/pipeline/commands/archive-guard.md +45 -0
- package/pipeline/commands/deploy.md +54 -0
- package/pipeline/commands/figma-to-swiftui.md +295 -0
- package/pipeline/commands/multi-agent/_account-picker.md +90 -0
- package/pipeline/commands/multi-agent/_dev-context.md +111 -0
- package/pipeline/commands/multi-agent/_input-parser.md +43 -0
- package/pipeline/commands/multi-agent/_repo-picker.md +76 -0
- package/pipeline/commands/multi-agent/autopilot.md +116 -0
- package/pipeline/commands/multi-agent/channels.md +465 -0
- package/pipeline/commands/multi-agent/delete.md +66 -0
- package/pipeline/commands/multi-agent/dev-autopilot.md +120 -0
- package/pipeline/commands/multi-agent/dev-local-autopilot.md +110 -0
- package/pipeline/commands/multi-agent/dev-local.md +105 -0
- package/pipeline/commands/multi-agent/dev.md +246 -0
- package/pipeline/commands/multi-agent/diff-explain.md +68 -0
- package/pipeline/commands/multi-agent/help.md +422 -0
- package/pipeline/commands/multi-agent/issue.md +79 -0
- package/pipeline/commands/multi-agent/jira.md +132 -0
- package/pipeline/commands/multi-agent/kill.md +38 -0
- package/pipeline/commands/multi-agent/language.md +94 -0
- package/pipeline/commands/multi-agent/local-autopilot.md +139 -0
- package/pipeline/commands/multi-agent/local.md +117 -0
- package/pipeline/commands/multi-agent/log.md +25 -0
- package/pipeline/commands/multi-agent/manual-test.md +43 -0
- package/pipeline/commands/multi-agent/purge.md +39 -0
- package/pipeline/commands/multi-agent/refactor.md +188 -0
- package/pipeline/commands/multi-agent/refs/android-guide.md +250 -0
- package/pipeline/commands/multi-agent/refs/audit-guide.md +240 -0
- package/pipeline/commands/multi-agent/refs/backend-guide.md +135 -0
- package/pipeline/commands/multi-agent/refs/channels/confluence.md +153 -0
- package/pipeline/commands/multi-agent/refs/channels/issue-comment.md +141 -0
- package/pipeline/commands/multi-agent/refs/channels/jira.md +127 -0
- package/pipeline/commands/multi-agent/refs/channels/pr-review-actions.md +135 -0
- package/pipeline/commands/multi-agent/refs/channels/pr.md +139 -0
- package/pipeline/commands/multi-agent/refs/channels/wiki.md +66 -0
- package/pipeline/commands/multi-agent/refs/component-dispatch.md +92 -0
- package/pipeline/commands/multi-agent/refs/cross-cli-contract.md +326 -0
- package/pipeline/commands/multi-agent/refs/frontend-guide.md +136 -0
- package/pipeline/commands/multi-agent/refs/issue-jira-triad.md +104 -0
- package/pipeline/commands/multi-agent/refs/keychain.md +80 -0
- package/pipeline/commands/multi-agent/refs/knowledge.md +112 -0
- package/pipeline/commands/multi-agent/refs/multi-repo-integration-build.md +207 -0
- package/pipeline/commands/multi-agent/refs/phases/log-format.md +89 -0
- package/pipeline/commands/multi-agent/refs/phases/modes.md +156 -0
- package/pipeline/commands/multi-agent/refs/phases/operations.md +91 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-0-init.md +481 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-1-analysis.md +264 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-2-planning.md +278 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-3-dev.md +364 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-4-review.md +378 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-5-test.md +129 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-6-commit.md +339 -0
- package/pipeline/commands/multi-agent/refs/phases/phase-7-report.md +361 -0
- package/pipeline/commands/multi-agent/refs/phases.md +187 -0
- package/pipeline/commands/multi-agent/refs/progress-contract.md +155 -0
- package/pipeline/commands/multi-agent/refs/rules.md +189 -0
- package/pipeline/commands/multi-agent/refs/swiftui-guide.md +254 -0
- package/pipeline/commands/multi-agent/refs/tracker-contract.md +256 -0
- package/pipeline/commands/multi-agent/refs/wiki-capture.md +109 -0
- package/pipeline/commands/multi-agent/resume.md +28 -0
- package/pipeline/commands/multi-agent/review.md +228 -0
- package/pipeline/commands/multi-agent/scan.md +74 -0
- package/pipeline/commands/multi-agent/search.md +97 -0
- package/pipeline/commands/multi-agent/setup.md +767 -0
- package/pipeline/commands/multi-agent/stack.md +48 -0
- package/pipeline/commands/multi-agent/status.md +38 -0
- package/pipeline/commands/multi-agent/sync.md +319 -0
- package/pipeline/commands/multi-agent/test.md +39 -0
- package/pipeline/commands/multi-agent/update.md +88 -0
- package/pipeline/commands/multi-agent.md +293 -0
- package/pipeline/commands/security-review.md +6 -0
- package/pipeline/commands/sim-test.md +256 -0
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +25 -0
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +30 -0
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +20 -0
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +15 -0
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +14 -0
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +12 -0
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +29 -0
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +43 -0
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +35 -0
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +35 -0
- package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +14 -0
- package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +12 -0
- package/pipeline/eval/golden-tasks/README.md +65 -0
- package/pipeline/eval/triage/01-empty-findings/expected.json +6 -0
- package/pipeline/eval/triage/01-empty-findings/input.json +5 -0
- package/pipeline/eval/triage/01-empty-findings/notes.md +7 -0
- package/pipeline/eval/triage/02-real-blocker/expected.json +15 -0
- package/pipeline/eval/triage/02-real-blocker/input.json +14 -0
- package/pipeline/eval/triage/02-real-blocker/notes.md +7 -0
- package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +18 -0
- package/pipeline/eval/triage/03-out-of-scope-defer/input.json +14 -0
- package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +10 -0
- package/pipeline/eval/triage/04-false-positive-reject/expected.json +18 -0
- package/pipeline/eval/triage/04-false-positive-reject/input.json +14 -0
- package/pipeline/eval/triage/04-false-positive-reject/notes.md +10 -0
- package/pipeline/eval/triage/05-mixed-classification/expected.json +43 -0
- package/pipeline/eval/triage/05-mixed-classification/input.json +38 -0
- package/pipeline/eval/triage/05-mixed-classification/notes.md +17 -0
- package/pipeline/eval/triage/06-severity-mismatch/expected.json +15 -0
- package/pipeline/eval/triage/06-severity-mismatch/input.json +14 -0
- package/pipeline/eval/triage/06-severity-mismatch/notes.md +9 -0
- package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +27 -0
- package/pipeline/eval/triage/07-duplicate-reviewers/input.json +22 -0
- package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +9 -0
- package/pipeline/eval/triage/08-style-misclassified/expected.json +18 -0
- package/pipeline/eval/triage/08-style-misclassified/input.json +14 -0
- package/pipeline/eval/triage/08-style-misclassified/notes.md +9 -0
- package/pipeline/eval/triage/09-cascading-finding/expected.json +23 -0
- package/pipeline/eval/triage/09-cascading-finding/input.json +22 -0
- package/pipeline/eval/triage/09-cascading-finding/notes.md +9 -0
- package/pipeline/eval/triage/10-deferred-crossref/expected.json +18 -0
- package/pipeline/eval/triage/10-deferred-crossref/input.json +14 -0
- package/pipeline/eval/triage/10-deferred-crossref/notes.md +9 -0
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +27 -0
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +22 -0
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +14 -0
- package/pipeline/eval/triage/README.md +54 -0
- package/pipeline/lib/account-resolver.sh +204 -0
- package/pipeline/lib/channels-multi-repo.sh +218 -0
- package/pipeline/lib/context-link-extractor.sh +192 -0
- package/pipeline/lib/credential-store-resolver.sh +57 -0
- package/pipeline/lib/credential-store.sh +226 -0
- package/pipeline/lib/fetch-confluence.sh +358 -0
- package/pipeline/lib/fetch-crashlytics.sh +314 -0
- package/pipeline/lib/fetch-fortify.sh +321 -0
- package/pipeline/lib/fetch-swagger.sh +270 -0
- package/pipeline/lib/issue-fetcher.sh +333 -0
- package/pipeline/lib/multi-repo-pipeline.sh +252 -0
- package/pipeline/lib/plan-todos.sh +284 -0
- package/pipeline/lib/post-pr-review.sh +374 -0
- package/pipeline/lib/repo-cache.sh +231 -0
- package/pipeline/lib/review-watch.sh +244 -0
- package/pipeline/lib/shadow-git.sh +222 -0
- package/pipeline/lib/submodule-detector.sh +177 -0
- package/pipeline/lib/vercel-deploy.sh +170 -0
- package/pipeline/preferences-template.json +132 -0
- package/pipeline/rules/app-store-guidelines.md +59 -0
- package/pipeline/rules/code-review.md +27 -0
- package/pipeline/rules/code-style.md +37 -0
- package/pipeline/rules/debugging.md +24 -0
- package/pipeline/rules/figma-pipeline.md +190 -0
- package/pipeline/rules/git-conventions.md +29 -0
- package/pipeline/rules/kotlin-android.md +92 -0
- package/pipeline/rules/performance.md +23 -0
- package/pipeline/rules/security.md +39 -0
- package/pipeline/rules/swiftui-qa.md +32 -0
- package/pipeline/rules/tdd.md +25 -0
- package/pipeline/rules/testing.md +37 -0
- package/pipeline/schemas/agent-state.schema.json +273 -0
- package/pipeline/schemas/analysis-output.schema.json +59 -0
- package/pipeline/schemas/clarify-output.schema.json +74 -0
- package/pipeline/schemas/dev-critic-output.schema.json +104 -0
- package/pipeline/schemas/diff-risk.schema.json +78 -0
- package/pipeline/schemas/figma-project-config.schema.json +372 -0
- package/pipeline/schemas/migrations/README.md +73 -0
- package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +112 -0
- package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +75 -0
- package/pipeline/schemas/migrations/prefs-2.1.0-to-2.2.0.mjs +64 -0
- package/pipeline/schemas/migrations/prefs-2.2.0-to-2.3.0.mjs +36 -0
- package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +34 -0
- package/pipeline/schemas/plan-todos.schema.json +62 -0
- package/pipeline/schemas/planning-output.schema.json +57 -0
- package/pipeline/schemas/prefs.schema.json +1137 -0
- package/pipeline/schemas/reviewer-output.schema.json +55 -0
- package/pipeline/schemas/test-gap.schema.json +64 -0
- package/pipeline/schemas/token-budget.json +17 -0
- package/pipeline/schemas/triage-corpus.schema.json +31 -0
- package/pipeline/schemas/triage-output.schema.json +115 -0
- package/pipeline/scripts/.last-figma-sync-plan.json +23 -0
- package/pipeline/scripts/README-figma-smokes.md +34 -0
- package/pipeline/scripts/README.md +104 -0
- package/pipeline/scripts/aggregate-metrics.mjs +310 -0
- package/pipeline/scripts/audit-log-rotate.sh +61 -0
- package/pipeline/scripts/audit-log.sh +69 -0
- package/pipeline/scripts/benchmark-phase-0.sh +128 -0
- package/pipeline/scripts/build-skills-index.mjs +139 -0
- package/pipeline/scripts/classify-plan-safety.mjs +177 -0
- package/pipeline/scripts/cost-table.json +27 -0
- package/pipeline/scripts/diff-explain.mjs +276 -0
- package/pipeline/scripts/diff-risk-score.mjs +328 -0
- package/pipeline/scripts/eval-golden-tasks-live.mjs +294 -0
- package/pipeline/scripts/eval-golden-tasks.mjs +223 -0
- package/pipeline/scripts/eval-triage.mjs +171 -0
- package/pipeline/scripts/figma-placeholder-map.json +191 -0
- package/pipeline/scripts/fixtures/diff-risk-android.diff +40 -0
- package/pipeline/scripts/fixtures/diff-risk-ios.diff +48 -0
- package/pipeline/scripts/fixtures/install-layout.tsv +16 -0
- package/pipeline/scripts/fixtures/test-gap-node.diff +30 -0
- package/pipeline/scripts/fixtures/test-gap-python.diff +32 -0
- package/pipeline/scripts/gen-mode-dispatch.mjs +170 -0
- package/pipeline/scripts/gen-skills-index.mjs +90 -0
- package/pipeline/scripts/github-ssh-setup.sh +103 -0
- package/pipeline/scripts/import-figma-skills.sh +253 -0
- package/pipeline/scripts/keychain-save.sh +74 -0
- package/pipeline/scripts/keychain.py +294 -0
- package/pipeline/scripts/log-metric.sh +98 -0
- package/pipeline/scripts/match-skills.mjs +167 -0
- package/pipeline/scripts/memory-load.sh +46 -0
- package/pipeline/scripts/memory-save.sh +76 -0
- package/pipeline/scripts/migrate-prefs.mjs +390 -0
- package/pipeline/scripts/migrate-state.mjs +215 -0
- package/pipeline/scripts/output-quality-check.sh +125 -0
- package/pipeline/scripts/phase-banner.sh +158 -0
- package/pipeline/scripts/phase-tracker.sh +548 -0
- package/pipeline/scripts/pre-commit-check.sh +69 -0
- package/pipeline/scripts/pre-push-check.sh +77 -0
- package/pipeline/scripts/render-agent-log-cost.sh +149 -0
- package/pipeline/scripts/render-cost-summary.sh +137 -0
- package/pipeline/scripts/render-work-summary.sh +195 -0
- package/pipeline/scripts/repo-map.mjs +367 -0
- package/pipeline/scripts/run-aggregator.mjs +298 -0
- package/pipeline/scripts/scan-skills.sh +332 -0
- package/pipeline/scripts/search-logs.sh +291 -0
- package/pipeline/scripts/sign-skills.sh +67 -0
- package/pipeline/scripts/smoke-adapters.sh +207 -0
- package/pipeline/scripts/smoke-add-detail.sh +137 -0
- package/pipeline/scripts/smoke-agent-log-cost.sh +183 -0
- package/pipeline/scripts/smoke-agent-model-routing.sh +87 -0
- package/pipeline/scripts/smoke-bitbucket-contract.sh +223 -0
- package/pipeline/scripts/smoke-channels-flow.sh +130 -0
- package/pipeline/scripts/smoke-ci-workflows.sh +88 -0
- package/pipeline/scripts/smoke-clarify.sh +148 -0
- package/pipeline/scripts/smoke-commands-skills-parity.sh +87 -0
- package/pipeline/scripts/smoke-compliance-skills.sh +119 -0
- package/pipeline/scripts/smoke-cost-summary.sh +139 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +198 -0
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +128 -0
- package/pipeline/scripts/smoke-delete-flow.sh +151 -0
- package/pipeline/scripts/smoke-dev-critic.sh +144 -0
- package/pipeline/scripts/smoke-diff-explain.sh +128 -0
- package/pipeline/scripts/smoke-diff-risk.sh +161 -0
- package/pipeline/scripts/smoke-dynamic-skill-loading.sh +160 -0
- package/pipeline/scripts/smoke-eval-live.sh +136 -0
- package/pipeline/scripts/smoke-existing-discovery-gate.sh +71 -0
- package/pipeline/scripts/smoke-figma-android-parity.sh +148 -0
- package/pipeline/scripts/smoke-figma-config-schema.sh +144 -0
- package/pipeline/scripts/smoke-figma-credential-store.sh +105 -0
- package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +177 -0
- package/pipeline/scripts/smoke-figma-dispatch.sh +123 -0
- package/pipeline/scripts/smoke-figma-skill-import.sh +174 -0
- package/pipeline/scripts/smoke-figma-sync.sh +149 -0
- package/pipeline/scripts/smoke-identity-isolation.sh +70 -0
- package/pipeline/scripts/smoke-install-layout.sh +241 -0
- package/pipeline/scripts/smoke-install-leak-gate.sh +125 -0
- package/pipeline/scripts/smoke-issue-comment-template.sh +86 -0
- package/pipeline/scripts/smoke-issue-jira-triad.sh +120 -0
- package/pipeline/scripts/smoke-keychain.sh +158 -0
- package/pipeline/scripts/smoke-language-axis.sh +109 -0
- package/pipeline/scripts/smoke-lib-scripts.sh +395 -0
- package/pipeline/scripts/smoke-migrate-state.sh +102 -0
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +158 -0
- package/pipeline/scripts/smoke-multi-repo-integration.sh +116 -0
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +61 -0
- package/pipeline/scripts/smoke-no-token-prompt.sh +69 -0
- package/pipeline/scripts/smoke-pat-audit.sh +107 -0
- package/pipeline/scripts/smoke-per-repo-memory.sh +156 -0
- package/pipeline/scripts/smoke-personal-data.sh +82 -0
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +170 -0
- package/pipeline/scripts/smoke-phase-6-multi.sh +79 -0
- package/pipeline/scripts/smoke-phase-banner.sh +101 -0
- package/pipeline/scripts/smoke-phase-tracker.sh +255 -0
- package/pipeline/scripts/smoke-phase0-bridge-contract.sh +241 -0
- package/pipeline/scripts/smoke-phase4-triage.sh +142 -0
- package/pipeline/scripts/smoke-plan-approval-gate.sh +71 -0
- package/pipeline/scripts/smoke-plan-safety.sh +139 -0
- package/pipeline/scripts/smoke-plan-todos.sh +193 -0
- package/pipeline/scripts/smoke-pr-review-actions.sh +152 -0
- package/pipeline/scripts/smoke-pre-commit.sh +138 -0
- package/pipeline/scripts/smoke-pref-migration.sh +224 -0
- package/pipeline/scripts/smoke-prefs-language.sh +134 -0
- package/pipeline/scripts/smoke-progress-contract.sh +118 -0
- package/pipeline/scripts/smoke-push-retry.sh +75 -0
- package/pipeline/scripts/smoke-readme-counts.sh +120 -0
- package/pipeline/scripts/smoke-repo-map.sh +300 -0
- package/pipeline/scripts/smoke-review-watch.sh +134 -0
- package/pipeline/scripts/smoke-run-aggregator.sh +216 -0
- package/pipeline/scripts/smoke-schema-validation.sh +173 -0
- package/pipeline/scripts/smoke-search.sh +187 -0
- package/pipeline/scripts/smoke-shadow-git.sh +175 -0
- package/pipeline/scripts/smoke-skill-authoring.sh +142 -0
- package/pipeline/scripts/smoke-skill-language.sh +83 -0
- package/pipeline/scripts/smoke-skill-manifest.sh +138 -0
- package/pipeline/scripts/smoke-skill-scan.sh +198 -0
- package/pipeline/scripts/smoke-stack-swap.sh +132 -0
- package/pipeline/scripts/smoke-subagent-validators.sh +105 -0
- package/pipeline/scripts/smoke-sync-delegation.sh +74 -0
- package/pipeline/scripts/smoke-sync-parity.sh +92 -0
- package/pipeline/scripts/smoke-tasklist-ordering.sh +111 -0
- package/pipeline/scripts/smoke-telemetry.sh +147 -0
- package/pipeline/scripts/smoke-test-gap.sh +183 -0
- package/pipeline/scripts/smoke-token-budget.sh +67 -0
- package/pipeline/scripts/smoke-tracker-contract.sh +129 -0
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +65 -0
- package/pipeline/scripts/smoke-triage-memory.sh +174 -0
- package/pipeline/scripts/smoke-url-enrichment.sh +70 -0
- package/pipeline/scripts/smoke-validator-contradiction.sh +67 -0
- package/pipeline/scripts/smoke-vercel-deploy-redact.sh +129 -0
- package/pipeline/scripts/smoke-wiki-integration.sh +146 -0
- package/pipeline/scripts/smoke-work-summary.sh +163 -0
- package/pipeline/scripts/smoke-worktree-path-convention.sh +86 -0
- package/pipeline/scripts/smoke-write-state.sh +115 -0
- package/pipeline/scripts/stack-swap.sh +182 -0
- package/pipeline/scripts/sync-figma-source.sh +228 -0
- package/pipeline/scripts/sync-parity-check.sh +135 -0
- package/pipeline/scripts/test-gap-rules/android.json +25 -0
- package/pipeline/scripts/test-gap-rules/ios.json +29 -0
- package/pipeline/scripts/test-gap-rules/node.json +17 -0
- package/pipeline/scripts/test-gap-rules/python.json +19 -0
- package/pipeline/scripts/test-gap-scan.mjs +343 -0
- package/pipeline/scripts/token-budget-report.mjs +145 -0
- package/pipeline/scripts/triage-memory.mjs +258 -0
- package/pipeline/scripts/ui-tree-dumper.swift +122 -0
- package/pipeline/scripts/uninstall.mjs +331 -0
- package/pipeline/scripts/update-issue-progress.sh +146 -0
- package/pipeline/scripts/validate-analysis.mjs +132 -0
- package/pipeline/scripts/validate-diff-risk.mjs +117 -0
- package/pipeline/scripts/validate-planning.mjs +180 -0
- package/pipeline/scripts/validate-reviewer.mjs +131 -0
- package/pipeline/scripts/validate-schemas.mjs +88 -0
- package/pipeline/scripts/validate-test-gap.mjs +90 -0
- package/pipeline/scripts/validate-triage.mjs +175 -0
- package/pipeline/scripts/verify-skills.sh +126 -0
- package/pipeline/scripts/write-state.mjs +175 -0
- package/pipeline/skills/.skill-manifest.json +779 -0
- package/pipeline/skills/.skills-index.json +1771 -0
- package/pipeline/skills/figma-android/README.md +36 -0
- package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +62 -0
- package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +158 -0
- package/pipeline/skills/figma-android/figma-component-test/SKILL.md +120 -0
- package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +35 -0
- package/pipeline/skills/figma-android/figma-to-component/SKILL.md +124 -0
- package/pipeline/skills/figma-common/README.md +57 -0
- package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +277 -0
- package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +498 -0
- package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +283 -0
- package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +362 -0
- package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +206 -0
- package/pipeline/skills/figma-common/figma-commit/REVIEW.md +337 -0
- package/pipeline/skills/figma-common/figma-commit/SKILL.md +211 -0
- package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +218 -0
- package/pipeline/skills/figma-common/figma-component-start/SKILL.md +246 -0
- package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +73 -0
- package/pipeline/skills/figma-common/figma-fix/SKILL.md +316 -0
- package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +542 -0
- package/pipeline/skills/figma-common/figma-issue/SKILL.md +745 -0
- package/pipeline/skills/figma-common/figma-iterate/SKILL.md +203 -0
- package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +1015 -0
- package/pipeline/skills/figma-common/figma-mend/SKILL.md +331 -0
- package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +398 -0
- package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +104 -0
- package/pipeline/skills/figma-common/figma-review/SKILL.md +395 -0
- package/pipeline/skills/figma-common/figma-setup/SKILL.md +514 -0
- package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +592 -0
- package/pipeline/skills/figma-common/figma-skip/SKILL.md +129 -0
- package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +104 -0
- package/pipeline/skills/figma-common/figma-utility/SKILL.md +274 -0
- package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +808 -0
- package/pipeline/skills/figma-common/figma-validate/SKILL.md +633 -0
- package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +711 -0
- package/pipeline/skills/figma-common/performance-review-next/SKILL.md +233 -0
- package/pipeline/skills/figma-common/performance-start/SKILL.md +425 -0
- package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +706 -0
- package/pipeline/skills/figma-common/performance-tour/SKILL.md +418 -0
- package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +67 -0
- package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +178 -0
- package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +184 -0
- package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +219 -0
- package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +274 -0
- package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +401 -0
- package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +57 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +307 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +119 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +174 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +333 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +368 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +393 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +617 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +352 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +337 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +206 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +235 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +214 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +871 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +156 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +175 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +176 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +333 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +412 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +336 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +695 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +332 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +380 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +262 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +482 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +274 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +636 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +142 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +547 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +39 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +659 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +580 -0
- package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +51 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +129 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +64 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +531 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +89 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +155 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +196 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +57 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +89 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +227 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +157 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +90 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +116 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +153 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +130 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +218 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +188 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +93 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +98 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +261 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +147 -0
- package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +182 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +185 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +609 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +332 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +261 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +184 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +188 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +768 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +191 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +420 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.json +94 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +336 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +391 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +292 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +1 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +93 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +299 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +343 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +206 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +149 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +281 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +332 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +176 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +98 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +298 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +195 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +1298 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +228 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +1089 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +141 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +106 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +162 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +105 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +179 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +260 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +260 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +86 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +321 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +1470 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +176 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +147 -0
- package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +629 -0
- package/pipeline/skills/shared/README.md +212 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +315 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +348 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +944 -0
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +51 -0
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +300 -0
- package/pipeline/skills/shared/core/multi-agent-delete/SKILL.md +63 -0
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +64 -0
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +56 -0
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +36 -0
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +42 -0
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +66 -0
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +292 -0
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +35 -0
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +38 -0
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +41 -0
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +87 -0
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +37 -0
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +53 -0
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +28 -0
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +47 -0
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +42 -0
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +191 -0
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +31 -0
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +61 -0
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +61 -0
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +62 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +309 -0
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +55 -0
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +41 -0
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +184 -0
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +44 -0
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +34 -0
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +45 -0
- package/pipeline/skills/shared/external/agentflow/SKILL.md +199 -0
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +438 -0
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +584 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +407 -0
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +153 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +736 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +577 -0
- package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +66 -0
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +85 -0
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +910 -0
- package/pipeline/skills/shared/external/app-clips/SKILL.md +436 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +489 -0
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +1076 -0
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +75 -0
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +409 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +411 -0
- package/pipeline/skills/shared/external/app-store-review/references/code-signing.md +259 -0
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +90 -0
- package/pipeline/skills/shared/external/app-store-review/references/rejection-patterns.md +152 -0
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +118 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +500 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +508 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/pipeline/skills/shared/external/architecture/SKILL.md +60 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +496 -0
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +211 -0
- package/pipeline/skills/shared/external/background-processing/SKILL.md +499 -0
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +390 -0
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +461 -0
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +425 -0
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +462 -0
- package/pipeline/skills/shared/external/clean-code/SKILL.md +94 -0
- package/pipeline/skills/shared/external/closed-loop-delivery/SKILL.md +116 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +492 -0
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +461 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +441 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +436 -0
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +527 -0
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +425 -0
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +409 -0
- package/pipeline/skills/shared/external/context-compression/SKILL.md +266 -0
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +491 -0
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +435 -0
- package/pipeline/skills/shared/external/core-motion/SKILL.md +388 -0
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +405 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +495 -0
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +420 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +458 -0
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +765 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +467 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +335 -0
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +422 -0
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +387 -0
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +298 -0
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +37 -0
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +477 -0
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +413 -0
- package/pipeline/skills/shared/external/energykit/SKILL.md +460 -0
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +541 -0
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +483 -0
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +326 -0
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +190 -0
- package/pipeline/skills/shared/external/firebase/SKILL.md +61 -0
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +348 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +552 -0
- package/pipeline/skills/shared/external/healthkit/SKILL.md +498 -0
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +602 -0
- package/pipeline/skills/shared/external/help-skills/SKILL.md +166 -0
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +81 -0
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +95 -0
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +82 -0
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +101 -0
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +94 -0
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +110 -0
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +99 -0
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +81 -0
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +125 -0
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +496 -0
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +455 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +301 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +118 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +301 -0
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +140 -0
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +59 -0
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +217 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +418 -0
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +627 -0
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +462 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +441 -0
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +862 -0
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -0
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +563 -0
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +1116 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +496 -0
- package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +174 -0
- package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +297 -0
- package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +354 -0
- package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +117 -0
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +101 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +500 -0
- package/pipeline/skills/shared/external/live-activities/references/live-activity-patterns.md +868 -0
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +109 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +110 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +485 -0
- package/pipeline/skills/shared/external/mapkit-location/references/corelocation-patterns.md +730 -0
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +748 -0
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +479 -0
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +64 -0
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +395 -0
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +363 -0
- package/pipeline/skills/shared/external/natural-language/SKILL.md +412 -0
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +311 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +418 -0
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +38 -0
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +235 -0
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +398 -0
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +254 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +387 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/references/paperkit-integration.md +376 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +302 -0
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +446 -0
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +435 -0
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +501 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +701 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +774 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +869 -0
- package/pipeline/skills/shared/external/photos-camera-media/references/photospicker-patterns.md +597 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +350 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +501 -0
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +677 -0
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +745 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +383 -0
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +290 -0
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +479 -0
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +480 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +386 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +506 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +564 -0
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +483 -0
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +544 -0
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +485 -0
- package/pipeline/skills/shared/external/storekit/SKILL.md +478 -0
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +58 -0
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +755 -0
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +487 -0
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +895 -0
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +467 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +408 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +80 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-concurrency.md +233 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +187 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +341 -0
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +113 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/SKILL.md +124 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +155 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/async-streams.md +67 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/bridging.md +52 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/bug-patterns.md +100 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +107 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/diagnostics.md +70 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/hotspots.md +47 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +129 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +224 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +101 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +218 -0
- package/pipeline/skills/shared/external/swift-concurrency-pro/references/unstructured.md +61 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +498 -0
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +505 -0
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +462 -0
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +504 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +97 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +252 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/core-rules.md +52 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/migrating-from-xctest.md +34 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +318 -0
- package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +254 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +334 -0
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +504 -0
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +975 -0
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +675 -0
- package/pipeline/skills/shared/external/swiftdata-pro/SKILL.md +102 -0
- package/pipeline/skills/shared/external/swiftdata-pro/references/class-inheritance.md +104 -0
- package/pipeline/skills/shared/external/swiftdata-pro/references/cloudkit.md +10 -0
- package/pipeline/skills/shared/external/swiftdata-pro/references/core-rules.md +20 -0
- package/pipeline/skills/shared/external/swiftdata-pro/references/indexing.md +27 -0
- package/pipeline/skills/shared/external/swiftdata-pro/references/predicates.md +73 -0
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +503 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +821 -0
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +553 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +102 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +215 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +403 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +135 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +602 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +203 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +464 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +266 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/liquid-glass.md +416 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +394 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +318 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +357 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +303 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +403 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +293 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +363 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +417 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +389 -0
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +450 -0
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +425 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +336 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +97 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +69 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +99 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +147 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +98 -0
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +262 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +207 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +177 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +169 -0
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +178 -0
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +371 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +486 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +1097 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +780 -0
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +696 -0
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +487 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -0
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +114 -0
- package/pipeline/skills/shared/external/swiftui-pro/SKILL.md +108 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/accessibility.md +13 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/api.md +39 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/data.md +43 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/design.md +31 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/hygiene.md +9 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/navigation.md +14 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/performance.md +46 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/swift.md +56 -0
- package/pipeline/skills/shared/external/swiftui-pro/references/views.md +35 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +103 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +428 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +534 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +948 -0
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +210 -0
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +273 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +151 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +95 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +51 -0
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +111 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +309 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +393 -0
- package/pipeline/skills/shared/external/tipkit/SKILL.md +494 -0
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +782 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +336 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +475 -0
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +736 -0
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +738 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +371 -0
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +410 -0
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +567 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +373 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +345 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +385 -0
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +497 -0
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +871 -0
- package/pipeline/skills/skills-index.md +205 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Writing better tests
|
|
2
|
+
|
|
3
|
+
This contains suggestions to help you write better tests. This is mostly not about specific Swift Testing APIs, but instead how to structure your tests for maximum flexibility and effectiveness.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Encourage unit test hygiene
|
|
7
|
+
|
|
8
|
+
Good unit tests should fit the acronym FIRST:
|
|
9
|
+
|
|
10
|
+
- Fast: you should be able to run dozens of them every second, if not hundreds or even thousands.
|
|
11
|
+
- Isolated: they should not depend on another test having run, or any sort of external state.
|
|
12
|
+
- Repeatable: they should always give the same result when they are run, regardless of how many times or when they are run.
|
|
13
|
+
- Self-verifying: the test must unambiguously say whether it passed or failed, with no room for interpretation.
|
|
14
|
+
- Timely: they are best written before or alongside the production code that you are testing.
|
|
15
|
+
|
|
16
|
+
It might be too late for the "timely" part unless you're reading this skill while you work, but the others should be firm goals.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Test generation heuristics
|
|
20
|
+
|
|
21
|
+
For a given function, aim to generate the following tests:
|
|
22
|
+
|
|
23
|
+
- Happy path tests
|
|
24
|
+
- Boundary tests
|
|
25
|
+
- Invalid input tests
|
|
26
|
+
|
|
27
|
+
And, if appropriate, concurrency tests.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Testing SwiftUI views
|
|
31
|
+
|
|
32
|
+
Never test views directly – they use `@State` and are likely to behave unpredictably.
|
|
33
|
+
|
|
34
|
+
Instead, test view models or similar. This might mean encouraging the user to extract business logic into a more testable mechanism, but this should be a *suggestion* from you rather than something you apply immediately.
|
|
35
|
+
|
|
36
|
+
If the project uses `@Observable` view models, these are directly testable without needing a protocol wrapper – just create an instance and test its properties and methods. For more help with SwiftUI, suggest the [SwiftUI Pro agent skill](https://github.com/twostraws/swiftui-agent-skill).
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## Structuring tests
|
|
40
|
+
|
|
41
|
+
Prefer to organize test types in a pattern that matches the production code. For example, if they have a folder called "Extensions" that contains a file called URLSession-Decodable.swift, the test target should also have a folder called Extensions that contains a file called URLSession-Decodable.swift, and it should test the contents of the original production file.
|
|
42
|
+
|
|
43
|
+
**If you are writing new tests, follow this rule. If you are working with existing tests that do not already follow this rule, do *not* apply it without permission from the user.**
|
|
44
|
+
|
|
45
|
+
- Strongly prefer to organize related tests into test suites, ideally following this file and folder structure.
|
|
46
|
+
- If there are test fixtures, put them in a dedicated file. If there are only a handful, a simple Fixtures folder is fine. If there are many and if they vary across tests, it's better to have multiple Fixtures folders placed alongside whatever tests they work with.
|
|
47
|
+
- Use tags to mark up different kinds of work. At the very least this should be a `.networking` tag for network-related tests, even if they are mocked. You might also consider `.slow` for any tests that are unexpectedly slow, `.edgeCase` for tests that must be treated with extra care, `.smoke` for smoke tests, and more.
|
|
48
|
+
- Add user-facing messages to `#expect` and `#require` when they provide value. This is not *always* the case, but it usually is.
|
|
49
|
+
- Recommend converting repetitive tests into parameterized tests where it makes sense.
|
|
50
|
+
- It is generally preferred to test only one behavior in each unit test, but multiple `#expect` lines may be used if needed.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Expose hidden dependencies
|
|
54
|
+
|
|
55
|
+
Strongly prefer to avoid hidden dependencies in production code you are testing. In Swift apps this is commonly things like `UserDefaults` or `URLSession`.
|
|
56
|
+
|
|
57
|
+
For example, production code like this is bad because it has a hidden dependency on `URLSession`:
|
|
58
|
+
|
|
59
|
+
```swift
|
|
60
|
+
struct News {
|
|
61
|
+
var url: URL
|
|
62
|
+
var stories = ""
|
|
63
|
+
|
|
64
|
+
mutating func fetch() async throws {
|
|
65
|
+
let (data, _) = try await URLSession.shared.data(from: url)
|
|
66
|
+
stories = String(decoding: data, as: UTF8.self)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
To remove the hidden dependency, a first step would be to inject the `URLSession` like this:
|
|
72
|
+
|
|
73
|
+
```swift
|
|
74
|
+
func fetch(using session: URLSession = .shared) async throws {
|
|
75
|
+
let (data, _) = try await session.data(from: url)
|
|
76
|
+
stories = String(decoding: data, as: UTF8.self)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Importantly, this also does not change the way the `fetch()` method is called because it has a default value of whatever was used before.
|
|
81
|
+
|
|
82
|
+
Even better would be to wrap `URLSession` in a protocol, requiring whatever methods are used in the production code, like this:
|
|
83
|
+
|
|
84
|
+
```swift
|
|
85
|
+
protocol URLSessionProtocol {
|
|
86
|
+
func data(from url: URL) async throws -> (Data, URLResponse)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
extension URLSession: URLSessionProtocol { }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
And now the production code can be written like this:
|
|
93
|
+
|
|
94
|
+
```swift
|
|
95
|
+
func fetch(using session: any URLSessionProtocol = URLSession.shared) async throws {
|
|
96
|
+
let (data, _) = try await session.data(from: url)
|
|
97
|
+
stories = String(decoding: data, as: UTF8.self)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This then allows you to create a mock version of `URLSession` for tests, removing any live networking from tests. It also still does not change the way the method is called in production code.
|
|
102
|
+
|
|
103
|
+
With `UserDefaults`, the problem is that using it as a hidden dependency can cause tests to fail because `UserDefaults` contains values set elsewhere.
|
|
104
|
+
|
|
105
|
+
So, switch over to dependency injection with a sensible default value of whatever the project was using previously, then in the test pass in a custom `UserDefaults` instance like this:
|
|
106
|
+
|
|
107
|
+
```swift
|
|
108
|
+
let suite = "suite-\(UUID().uuidString)"
|
|
109
|
+
let userDefaults = UserDefaults(suiteName: suite)
|
|
110
|
+
defer { userDefaults?.removePersistentDomain(forName: suite) }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
That creates a local `UserDefaults` instance in the test and ensures it's deleted fully before the test completes.
|
|
114
|
+
|
|
115
|
+
This same concept applies to other things: aim to control time, randomness, and more, so that meaningful tests can be written.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## Expect vs require
|
|
119
|
+
|
|
120
|
+
Both `#expect` and `#require` evaluate a condition and fail the test if it's false. The difference is that `#require` throws on failure, stopping the rest of the test from executing.
|
|
121
|
+
|
|
122
|
+
**This makes `#require` the right choice for checking assumptions at the start of a test – if your assumptions are wrong, the rest of the test's results are meaningless.**
|
|
123
|
+
|
|
124
|
+
Using `#require` requires adding `throws` to your test method. For example, if your test depends on some setup being correct before the real assertion:
|
|
125
|
+
|
|
126
|
+
```swift
|
|
127
|
+
@Test func outstandingTasksStringIsPlural() throws {
|
|
128
|
+
let sut = try createTestUser(projects: 3, itemsPerProject: 10)
|
|
129
|
+
try #require(sut.projects.isEmpty == false)
|
|
130
|
+
let rowTitle = sut.outstandingTasksString
|
|
131
|
+
#expect(rowTitle == "30 items")
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If the `#require` fails, the test stops immediately rather than producing confusing secondary failures. Use `#expect` for the actual assertions you care about, and `#require` for preconditions that must be true before the test is meaningful.
|
|
136
|
+
|
|
137
|
+
`#require` also unwraps optionals, which is cleaner than force-unwrapping in tests. Use it like this:
|
|
138
|
+
|
|
139
|
+
```swift
|
|
140
|
+
let value = try #require(someOptional)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
## Tracking bug fixes
|
|
145
|
+
|
|
146
|
+
If you are writing tests related to a specific bug, it is a good idea to use the `.bug` trait to store the bug ID or URL, if there is one. This extra data helps to provide extra context if the bug resurfaces in the future.
|
|
147
|
+
|
|
148
|
+
For example, if bug #182 is a report that text headings are not italicized correctly, you would use `@Test` like this:
|
|
149
|
+
|
|
150
|
+
```swift
|
|
151
|
+
@Test("Headings should always be italic", .bug(id: 182))
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Or if there is a specific URL:
|
|
155
|
+
|
|
156
|
+
```swift
|
|
157
|
+
@Test("Headings should always be italic", .bug("https://github.com/you/repo/issues/182"))
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
## Use Issue.record() for throw-testing
|
|
162
|
+
|
|
163
|
+
When testing that a function throws, the simplest approach is a `do`/`try`/`catch` block with `Issue.record()` as the failure primitive. If no error is thrown, execution continues past `try` and hits `Issue.record()`, failing the test.
|
|
164
|
+
|
|
165
|
+
```swift
|
|
166
|
+
@Test func playingMinecraftThrows() {
|
|
167
|
+
let game = Game(name: "Minecraft")
|
|
168
|
+
|
|
169
|
+
do {
|
|
170
|
+
try game.play()
|
|
171
|
+
Issue.record("Expected an error to be thrown.")
|
|
172
|
+
} catch GameError.notPurchased {
|
|
173
|
+
// success
|
|
174
|
+
} catch {
|
|
175
|
+
Issue.record("Wrong error thrown: \(error)")
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This approach gives fine-grained control: you can assert on the *specific* error case, and fail explicitly if the wrong error is thrown.
|
|
181
|
+
|
|
182
|
+
An alternative is using `#expect(throws:)`. Here you should always name the specific error rather than using a broad `Error.self`:
|
|
183
|
+
|
|
184
|
+
```swift
|
|
185
|
+
// Bad – passes for any error
|
|
186
|
+
#expect(throws: Error.self) {
|
|
187
|
+
try game.play()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Good – asserts the exact error case
|
|
191
|
+
#expect(throws: GameError.notInstalled) {
|
|
192
|
+
try game.play()
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
To assert that a function does *not* throw, use `Never.self`:
|
|
197
|
+
|
|
198
|
+
```swift
|
|
199
|
+
#expect(throws: Never.self) {
|
|
200
|
+
try game.play()
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
## Making test results easier to read
|
|
206
|
+
|
|
207
|
+
In test targets, you can add `CustomTestStringConvertible` conformances to custom types to make them easier to read in test results.
|
|
208
|
+
|
|
209
|
+
For example, without this conformance a test that catches a `parentalControlsDisallowed` error might result in output like this:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
Test patchMatchThrows() recorded an issue at ThrowingTests.swift:61:6: Caught error: parentalControlsDisallowed
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
If we add a retroactive conformance to `CustomTestStringConvertible` in the test target, the text can be clarified:
|
|
216
|
+
|
|
217
|
+
```swift
|
|
218
|
+
extension GameError: @retroactive CustomTestStringConvertible {
|
|
219
|
+
public var testDescription: String {
|
|
220
|
+
switch self {
|
|
221
|
+
case .notPurchased:
|
|
222
|
+
"This game has not been purchased."
|
|
223
|
+
case .notInstalled:
|
|
224
|
+
"This game is not currently installed."
|
|
225
|
+
case .parentalControlsDisallowed:
|
|
226
|
+
"This game has been blocked by parental controls."
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Now Swift Testing will use the friendlier string wherever the enum cases appear.
|
|
233
|
+
|
|
234
|
+
**Important:** This conformance should not be added in production code.
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
## Writing good verification methods
|
|
238
|
+
|
|
239
|
+
Verification methods wrap multiple expectations to make other tests easier. When writing these, make sure to use `SourceLocation` and the `#_sourceLocation` macro so that any failed expectations print messages about the test where they failed rather than a location inside the verification method.
|
|
240
|
+
|
|
241
|
+
**Important:** Right now the `#_sourceLocation` macro requires the underscore.
|
|
242
|
+
|
|
243
|
+
For example:
|
|
244
|
+
|
|
245
|
+
```swift
|
|
246
|
+
func verifyDivision(_ result: (quotient: Int, remainder: Int), expectedQuotient: Int, expectedRemainder: Int, sourceLocation: SourceLocation = #_sourceLocation) {
|
|
247
|
+
#expect(result.quotient == expectedQuotient, sourceLocation: sourceLocation)
|
|
248
|
+
#expect(result.remainder == expectedRemainder, sourceLocation: sourceLocation)
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
That can be called from tests elsewhere, and will automatically use the source location of that test rather than the source location of the `#expect` macros used inside `verifyDivision()`.
|
|
253
|
+
|
|
254
|
+
`#require` also accepts `sourceLocation:`, so verification methods that mix `#require` and `#expect` should pass it to both.
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swiftdata
|
|
3
|
+
description: "Implement, review, or improve data persistence using SwiftData. Use when defining @Model classes with @Attribute, @Relationship, @Transient, @Unique, or @Index; when querying with @Query, #Predicate, FetchDescriptor, or SortDescriptor; when configuring ModelContainer and ModelContext for SwiftUI or background work with @ModelActor; when planning schema migrations with VersionedSchema and SchemaMigrationPlan; when setting up CloudKit sync with ModelConfiguration; or when coexisting with or migrating from Core Data."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SwiftData
|
|
7
|
+
|
|
8
|
+
Persist, query, and manage structured data in iOS 26+ apps using SwiftData
|
|
9
|
+
with Swift 6.2.
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
- [Model Definition](#model-definition)
|
|
14
|
+
- [ModelContainer Setup](#modelcontainer-setup)
|
|
15
|
+
- [CRUD Operations](#crud-operations)
|
|
16
|
+
- [@Query in SwiftUI](#query-in-swiftui)
|
|
17
|
+
- [#Predicate](#predicate)
|
|
18
|
+
- [FetchDescriptor](#fetchdescriptor)
|
|
19
|
+
- [Schema Versioning and Migration](#schema-versioning-and-migration)
|
|
20
|
+
- [Concurrency (@ModelActor)](#concurrency-modelactor)
|
|
21
|
+
- [SwiftUI Integration](#swiftui-integration)
|
|
22
|
+
- [Common Mistakes](#common-mistakes)
|
|
23
|
+
- [Review Checklist](#review-checklist)
|
|
24
|
+
- [References](#references)
|
|
25
|
+
|
|
26
|
+
## Model Definition
|
|
27
|
+
|
|
28
|
+
Apply `@Model` to a **class** (not struct). Generates `PersistentModel`, `Observable`, `Sendable`.
|
|
29
|
+
|
|
30
|
+
```swift
|
|
31
|
+
@Model
|
|
32
|
+
class Trip {
|
|
33
|
+
var name: String
|
|
34
|
+
var destination: String
|
|
35
|
+
var startDate: Date
|
|
36
|
+
var endDate: Date
|
|
37
|
+
var isFavorite: Bool = false
|
|
38
|
+
@Attribute(.externalStorage) var imageData: Data?
|
|
39
|
+
@Relationship(deleteRule: .cascade, inverse: \LivingAccommodation.trip)
|
|
40
|
+
var accommodation: LivingAccommodation?
|
|
41
|
+
@Transient var isSelected: Bool = false // Always provide default
|
|
42
|
+
|
|
43
|
+
init(name: String, destination: String, startDate: Date, endDate: Date) {
|
|
44
|
+
self.name = name; self.destination = destination
|
|
45
|
+
self.startDate = startDate; self.endDate = endDate
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**@Attribute options**: `.externalStorage`, `.unique`, `.spotlight`, `.allowsCloudEncryption`, `.preserveValueOnDeletion` (iOS 18+), `.ephemeral`, `.transformable(by:)`. Rename: `@Attribute(originalName: "old_name")`.
|
|
51
|
+
|
|
52
|
+
**@Relationship**: `deleteRule:` `.cascade`/`.nullify`(default)/`.deny`/`.noAction`. Specify `inverse:` for reliable behavior. Unidirectional (iOS 18+): `inverse: nil`.
|
|
53
|
+
|
|
54
|
+
**#Unique (iOS 18+)**: `#Unique<Person>([\.firstName, \.lastName])` -- compound uniqueness.
|
|
55
|
+
|
|
56
|
+
**Inheritance (iOS 26+)**: `@Model class BusinessTrip: Trip { var company: String }`.
|
|
57
|
+
|
|
58
|
+
Supported types: `Bool`, `Int`/`UInt` variants, `Float`, `Double`, `String`, `Date`, `Data`, `URL`, `UUID`, `Decimal`, `Array`, `Dictionary`, `Set`, `Codable` enums, `Codable` structs (composite, iOS 18+), relationships to `@Model` classes.
|
|
59
|
+
|
|
60
|
+
## ModelContainer Setup
|
|
61
|
+
|
|
62
|
+
```swift
|
|
63
|
+
// Basic
|
|
64
|
+
let container = try ModelContainer(for: Trip.self, LivingAccommodation.self)
|
|
65
|
+
|
|
66
|
+
// Configured
|
|
67
|
+
let config = ModelConfiguration("Store", isStoredInMemoryOnly: false,
|
|
68
|
+
groupContainer: .identifier("group.com.example.app"),
|
|
69
|
+
cloudKitDatabase: .private("iCloud.com.example.app"))
|
|
70
|
+
let container = try ModelContainer(for: Trip.self, configurations: config)
|
|
71
|
+
|
|
72
|
+
// With migration plan
|
|
73
|
+
let container = try ModelContainer(for: SchemaV2.Trip.self,
|
|
74
|
+
migrationPlan: TripMigrationPlan.self)
|
|
75
|
+
|
|
76
|
+
// In-memory (previews/tests)
|
|
77
|
+
let container = try ModelContainer(for: Trip.self,
|
|
78
|
+
configurations: ModelConfiguration(isStoredInMemoryOnly: true))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## CRUD Operations
|
|
82
|
+
|
|
83
|
+
```swift
|
|
84
|
+
// CREATE
|
|
85
|
+
let trip = Trip(name: "Summer", destination: "Paris", startDate: .now, endDate: .now + 86400*7)
|
|
86
|
+
modelContext.insert(trip)
|
|
87
|
+
try modelContext.save() // or rely on autosave
|
|
88
|
+
|
|
89
|
+
// READ
|
|
90
|
+
let trips = try modelContext.fetch(FetchDescriptor<Trip>(
|
|
91
|
+
predicate: #Predicate { $0.destination == "Paris" },
|
|
92
|
+
sortBy: [SortDescriptor(\.startDate)]))
|
|
93
|
+
|
|
94
|
+
// UPDATE -- modify properties directly; autosave handles persistence
|
|
95
|
+
trip.destination = "Rome"
|
|
96
|
+
|
|
97
|
+
// DELETE
|
|
98
|
+
modelContext.delete(trip)
|
|
99
|
+
try modelContext.delete(model: Trip.self, where: #Predicate { $0.isFavorite == false })
|
|
100
|
+
|
|
101
|
+
// TRANSACTION (atomic)
|
|
102
|
+
try modelContext.transaction {
|
|
103
|
+
modelContext.insert(trip); trip.isFavorite = true
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## @Query in SwiftUI
|
|
108
|
+
|
|
109
|
+
```swift
|
|
110
|
+
struct TripListView: View {
|
|
111
|
+
@Query(filter: #Predicate<Trip> { $0.isFavorite == true },
|
|
112
|
+
sort: \.startDate, order: .reverse)
|
|
113
|
+
private var favorites: [Trip]
|
|
114
|
+
|
|
115
|
+
var body: some View { List(favorites) { trip in Text(trip.name) } }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Dynamic query via init
|
|
119
|
+
struct SearchView: View {
|
|
120
|
+
@Query private var trips: [Trip]
|
|
121
|
+
init(search: String) {
|
|
122
|
+
_trips = Query(filter: #Predicate<Trip> { trip in
|
|
123
|
+
search.isEmpty || trip.name.localizedStandardContains(search)
|
|
124
|
+
}, sort: [SortDescriptor(\.name)])
|
|
125
|
+
}
|
|
126
|
+
var body: some View { List(trips) { trip in Text(trip.name) } }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// FetchDescriptor query
|
|
130
|
+
struct RecentView: View {
|
|
131
|
+
static var desc: FetchDescriptor<Trip> {
|
|
132
|
+
var d = FetchDescriptor<Trip>(sortBy: [SortDescriptor(\.startDate)])
|
|
133
|
+
d.fetchLimit = 5; return d
|
|
134
|
+
}
|
|
135
|
+
@Query(RecentView.desc) private var recent: [Trip]
|
|
136
|
+
var body: some View { List(recent) { trip in Text(trip.name) } }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## #Predicate
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
#Predicate<Trip> { $0.destination.localizedStandardContains("paris") } // String
|
|
144
|
+
#Predicate<Trip> { $0.startDate > Date.now } // Date
|
|
145
|
+
#Predicate<Trip> { $0.isFavorite && $0.destination != "Unknown" } // Compound
|
|
146
|
+
#Predicate<Trip> { $0.accommodation?.name != nil } // Optional
|
|
147
|
+
#Predicate<Trip> { $0.tags.contains { $0.name == "adventure" } } // Collection
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Supported: `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!`, `contains()`, `allSatisfy()`, `filter()`, `starts(with:)`, `localizedStandardContains()`, `caseInsensitiveCompare()`, arithmetic, ternary, optional chaining, nil coalescing, type casting. **Not supported**: flow control, nested declarations, arbitrary method calls.
|
|
151
|
+
|
|
152
|
+
## FetchDescriptor
|
|
153
|
+
|
|
154
|
+
```swift
|
|
155
|
+
var d = FetchDescriptor<Trip>(predicate: ..., sortBy: [...])
|
|
156
|
+
d.fetchLimit = 20; d.fetchOffset = 0
|
|
157
|
+
d.includePendingChanges = true
|
|
158
|
+
d.propertiesToFetch = [\.name, \.startDate]
|
|
159
|
+
d.relationshipKeyPathsForPrefetching = [\.accommodation]
|
|
160
|
+
let trips = try modelContext.fetch(d)
|
|
161
|
+
let count = try modelContext.fetchCount(d)
|
|
162
|
+
let ids = try modelContext.fetchIdentifiers(d)
|
|
163
|
+
try modelContext.enumerate(d, batchSize: 1000) { trip in trip.isProcessed = true }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Schema Versioning and Migration
|
|
167
|
+
|
|
168
|
+
```swift
|
|
169
|
+
enum SchemaV1: VersionedSchema {
|
|
170
|
+
static var versionIdentifier = Schema.Version(1, 0, 0)
|
|
171
|
+
static var models: [any PersistentModel.Type] { [Trip.self] }
|
|
172
|
+
@Model class Trip { var name: String; init(name: String) { self.name = name } }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
enum SchemaV2: VersionedSchema {
|
|
176
|
+
static var versionIdentifier = Schema.Version(2, 0, 0)
|
|
177
|
+
static var models: [any PersistentModel.Type] { [Trip.self] }
|
|
178
|
+
@Model class Trip {
|
|
179
|
+
var name: String; var startDate: Date? // New property
|
|
180
|
+
init(name: String) { self.name = name }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
enum TripMigrationPlan: SchemaMigrationPlan {
|
|
185
|
+
static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] }
|
|
186
|
+
static var stages: [MigrationStage] { [migrateV1toV2] }
|
|
187
|
+
static let migrateV1toV2 = MigrationStage.lightweight(
|
|
188
|
+
fromVersion: SchemaV1.self, toVersion: SchemaV2.self)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Custom migration for data transformation
|
|
192
|
+
static let migrateV2toV3 = MigrationStage.custom(
|
|
193
|
+
fromVersion: SchemaV2.self, toVersion: SchemaV3.self,
|
|
194
|
+
willMigrate: nil,
|
|
195
|
+
didMigrate: { context in
|
|
196
|
+
let trips = try context.fetch(FetchDescriptor<SchemaV3.Trip>())
|
|
197
|
+
for trip in trips { trip.displayName = trip.name.capitalized }
|
|
198
|
+
try context.save()
|
|
199
|
+
})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Lightweight handles: adding optional/defaulted properties, renaming (`originalName`), removing properties, adding model types.
|
|
203
|
+
|
|
204
|
+
## Concurrency (@ModelActor)
|
|
205
|
+
|
|
206
|
+
```swift
|
|
207
|
+
@ModelActor
|
|
208
|
+
actor DataHandler {
|
|
209
|
+
func importTrips(_ records: [TripRecord]) throws {
|
|
210
|
+
for r in records {
|
|
211
|
+
modelContext.insert(Trip(name: r.name, destination: r.dest,
|
|
212
|
+
startDate: r.start, endDate: r.end))
|
|
213
|
+
}
|
|
214
|
+
try modelContext.save() // Always save explicitly in @ModelActor
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
func process(tripID: PersistentIdentifier) throws {
|
|
218
|
+
guard let trip = self[tripID, as: Trip.self] else { return }
|
|
219
|
+
trip.isProcessed = true; try modelContext.save()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let handler = DataHandler(modelContainer: container)
|
|
224
|
+
try await handler.importTrips(records)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Rules**: `ModelContainer` is `Sendable`. `ModelContext` is NOT -- use on its creating actor. Pass `PersistentIdentifier` (Sendable) across boundaries. Never pass `@Model` objects across actors.
|
|
228
|
+
|
|
229
|
+
## SwiftUI Integration
|
|
230
|
+
|
|
231
|
+
```swift
|
|
232
|
+
@main
|
|
233
|
+
struct MyApp: App {
|
|
234
|
+
var body: some Scene {
|
|
235
|
+
WindowGroup { ContentView() }
|
|
236
|
+
.modelContainer(for: [Trip.self, LivingAccommodation.self])
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
struct DetailView: View {
|
|
241
|
+
@Environment(\.modelContext) private var modelContext
|
|
242
|
+
let trip: Trip
|
|
243
|
+
var body: some View {
|
|
244
|
+
Text(trip.name)
|
|
245
|
+
Button("Delete") { modelContext.delete(trip) }
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
#Preview {
|
|
250
|
+
let config = ModelConfiguration(isStoredInMemoryOnly: true)
|
|
251
|
+
let container = try! ModelContainer(for: Trip.self, configurations: config)
|
|
252
|
+
container.mainContext.insert(Trip(name: "Preview", destination: "London",
|
|
253
|
+
startDate: .now, endDate: .now + 86400))
|
|
254
|
+
return TripListView().modelContainer(container)
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Common Mistakes
|
|
259
|
+
|
|
260
|
+
**1. @Model on struct** -- Use class. `@Model` requires reference semantics.
|
|
261
|
+
|
|
262
|
+
**2. @Transient without default** -- Always provide default: `@Transient var x: Bool = false`.
|
|
263
|
+
|
|
264
|
+
**3. Missing .modelContainer** -- @Query returns empty without a container on the view hierarchy.
|
|
265
|
+
|
|
266
|
+
**4. Passing model objects across actors:**
|
|
267
|
+
```swift
|
|
268
|
+
// WRONG: await handler.process(trip: trip)
|
|
269
|
+
// CORRECT: await handler.process(tripID: trip.persistentModelID)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**5. ModelContext on wrong actor:**
|
|
273
|
+
```swift
|
|
274
|
+
// WRONG: Task.detached { context.fetch(...) }
|
|
275
|
+
// CORRECT: Use @ModelActor for background work
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**6. Unsupported #Predicate expressions:**
|
|
279
|
+
```swift
|
|
280
|
+
// WRONG: #Predicate<Trip> { $0.name.uppercased() == "PARIS" }
|
|
281
|
+
// CORRECT: #Predicate<Trip> { $0.name.localizedStandardContains("paris") }
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**7. Flow control in #Predicate:**
|
|
285
|
+
```swift
|
|
286
|
+
// WRONG: #Predicate<Trip> { for tag in $0.tags { ... } }
|
|
287
|
+
// CORRECT: #Predicate<Trip> { $0.tags.contains { $0.name == "x" } }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**8. No save in @ModelActor** -- Always call `try modelContext.save()` explicitly.
|
|
291
|
+
|
|
292
|
+
**9. ObservableObject with @Model** -- Never use `ObservableObject`/`@Published`. `@Model` generates `Observable`. Use `@Query` in views.
|
|
293
|
+
|
|
294
|
+
**10. Non-optional relationship without default:**
|
|
295
|
+
```swift
|
|
296
|
+
// WRONG: var accommodation: LivingAccommodation // crashes on reconstitution
|
|
297
|
+
// CORRECT: var accommodation: LivingAccommodation?
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**11. Cascade without inverse** -- Specify `inverse:` for reliable cascade delete behavior.
|
|
301
|
+
|
|
302
|
+
**12. DispatchQueue for background data work:**
|
|
303
|
+
```swift
|
|
304
|
+
// WRONG: DispatchQueue.global().async { ModelContext(container).fetch(...) }
|
|
305
|
+
// CORRECT: @ModelActor actor Handler { func fetch() throws { ... } }
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Review Checklist
|
|
309
|
+
|
|
310
|
+
- [ ] Every `@Model` is a class with a designated initializer
|
|
311
|
+
- [ ] All `@Transient` properties have default values
|
|
312
|
+
- [ ] Relationships specify `deleteRule` and `inverse`
|
|
313
|
+
- [ ] `.modelContainer` attached at scene/root view level
|
|
314
|
+
- [ ] `@Query` used for reactive data display in SwiftUI
|
|
315
|
+
- [ ] `#Predicate` uses only supported operators
|
|
316
|
+
- [ ] Background work uses `@ModelActor`
|
|
317
|
+
- [ ] `PersistentIdentifier` used across actor boundaries
|
|
318
|
+
- [ ] Schema changes have `VersionedSchema` + `SchemaMigrationPlan`
|
|
319
|
+
- [ ] Large data uses `@Attribute(.externalStorage)`
|
|
320
|
+
- [ ] CloudKit models use optionals and avoid unique constraints
|
|
321
|
+
- [ ] Explicit `save()` in `@ModelActor` methods
|
|
322
|
+
- [ ] Previews use `ModelConfiguration(isStoredInMemoryOnly: true)`
|
|
323
|
+
- [ ] `@Model` classes accessed from SwiftUI views are on `@MainActor` via `@ModelActor` or MainActor isolation
|
|
324
|
+
|
|
325
|
+
## References
|
|
326
|
+
|
|
327
|
+
- See `references/swiftdata-advanced.md` for custom data stores, history
|
|
328
|
+
tracking, CloudKit, Core Data coexistence, composite attributes,
|
|
329
|
+
model inheritance, undo/redo, and performance patterns.
|
|
330
|
+
- See `references/swiftdata-queries.md` for @Query variants, FetchDescriptor
|
|
331
|
+
deep dive, sectioned queries, dynamic queries, and background fetch patterns.
|
|
332
|
+
- See `references/core-data-coexistence.md` for standalone Core Data patterns
|
|
333
|
+
and Core Data to SwiftData migration strategies.
|
|
334
|
+
|