@ketecode/loom 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +516 -2
- package/agents/accessibility-reviewer/AGENT.md +70 -0
- package/agents/accessibility-reviewer/evals/triggers.json +15 -0
- package/agents/agent-evaluator/AGENT.md +74 -0
- package/agents/agent-evaluator/evals/triggers.json +15 -0
- package/agents/architect/AGENT.md +82 -0
- package/agents/architect/evals/triggers.json +17 -0
- package/agents/billing-reviewer/AGENT.md +68 -0
- package/agents/billing-reviewer/evals/triggers.json +16 -0
- package/agents/code-reviewer/AGENT.md +51 -0
- package/agents/code-reviewer/evals/triggers.json +14 -0
- package/agents/code-simplifier/AGENT.md +74 -0
- package/agents/code-simplifier/evals/triggers.json +15 -0
- package/agents/cpp-build-resolver/AGENT.md +73 -0
- package/agents/cpp-build-resolver/evals/triggers.json +16 -0
- package/agents/cpp-reviewer/AGENT.md +59 -0
- package/agents/cpp-reviewer/evals/triggers.json +16 -0
- package/agents/csharp-build-resolver/AGENT.md +84 -0
- package/agents/csharp-build-resolver/evals/triggers.json +17 -0
- package/agents/csharp-reviewer/AGENT.md +88 -0
- package/agents/csharp-reviewer/evals/triggers.json +17 -0
- package/agents/dart-build-resolver/AGENT.md +80 -0
- package/agents/dart-build-resolver/evals/triggers.json +17 -0
- package/agents/dart-reviewer/AGENT.md +66 -0
- package/agents/dart-reviewer/evals/triggers.json +17 -0
- package/agents/database-reviewer/AGENT.md +72 -0
- package/agents/database-reviewer/evals/triggers.json +16 -0
- package/agents/django-reviewer/AGENT.md +61 -0
- package/agents/django-reviewer/evals/triggers.json +15 -0
- package/agents/doc-updater/AGENT.md +92 -0
- package/agents/doc-updater/evals/triggers.json +14 -0
- package/agents/e2e-runner/AGENT.md +88 -0
- package/agents/e2e-runner/evals/triggers.json +16 -0
- package/agents/fastapi-reviewer/AGENT.md +63 -0
- package/agents/fastapi-reviewer/evals/triggers.json +15 -0
- package/agents/flutter-reviewer/AGENT.md +70 -0
- package/agents/flutter-reviewer/evals/triggers.json +15 -0
- package/agents/fsharp-build-resolver/AGENT.md +81 -0
- package/agents/fsharp-build-resolver/evals/triggers.json +15 -0
- package/agents/fsharp-reviewer/AGENT.md +77 -0
- package/agents/fsharp-reviewer/evals/triggers.json +15 -0
- package/agents/go-build-resolver/AGENT.md +85 -0
- package/agents/go-build-resolver/evals/triggers.json +16 -0
- package/agents/go-reviewer/AGENT.md +66 -0
- package/agents/go-reviewer/evals/triggers.json +16 -0
- package/agents/healthcare-reviewer/AGENT.md +65 -0
- package/agents/healthcare-reviewer/evals/triggers.json +16 -0
- package/agents/i18n-reviewer/AGENT.md +64 -0
- package/agents/i18n-reviewer/evals/triggers.json +16 -0
- package/agents/infra-reviewer/AGENT.md +93 -0
- package/agents/infra-reviewer/evals/triggers.json +15 -0
- package/agents/java-build-resolver/AGENT.md +82 -0
- package/agents/java-build-resolver/evals/triggers.json +16 -0
- package/agents/java-reviewer/AGENT.md +64 -0
- package/agents/java-reviewer/evals/triggers.json +16 -0
- package/agents/kotlin-build-resolver/AGENT.md +89 -0
- package/agents/kotlin-build-resolver/evals/triggers.json +16 -0
- package/agents/kotlin-reviewer/AGENT.md +75 -0
- package/agents/kotlin-reviewer/evals/triggers.json +16 -0
- package/agents/mle-reviewer/AGENT.md +68 -0
- package/agents/mle-reviewer/evals/triggers.json +16 -0
- package/agents/network-config-reviewer/AGENT.md +68 -0
- package/agents/network-config-reviewer/evals/triggers.json +17 -0
- package/agents/payments-reviewer/AGENT.md +64 -0
- package/agents/payments-reviewer/evals/triggers.json +16 -0
- package/agents/performance-optimizer/AGENT.md +86 -0
- package/agents/performance-optimizer/evals/triggers.json +16 -0
- package/agents/perl-build-resolver/AGENT.md +84 -0
- package/agents/perl-build-resolver/evals/triggers.json +15 -0
- package/agents/perl-reviewer/AGENT.md +77 -0
- package/agents/perl-reviewer/evals/triggers.json +15 -0
- package/agents/php-build-resolver/AGENT.md +88 -0
- package/agents/php-build-resolver/evals/triggers.json +16 -0
- package/agents/php-reviewer/AGENT.md +61 -0
- package/agents/php-reviewer/evals/triggers.json +15 -0
- package/agents/planner/AGENT.md +75 -0
- package/agents/planner/evals/triggers.json +17 -0
- package/agents/pr-test-analyzer/AGENT.md +82 -0
- package/agents/pr-test-analyzer/evals/triggers.json +16 -0
- package/agents/python-build-resolver/AGENT.md +87 -0
- package/agents/python-build-resolver/evals/triggers.json +16 -0
- package/agents/python-reviewer/AGENT.md +63 -0
- package/agents/python-reviewer/evals/triggers.json +15 -0
- package/agents/rag-reviewer/AGENT.md +71 -0
- package/agents/rag-reviewer/evals/triggers.json +16 -0
- package/agents/react-reviewer/AGENT.md +70 -0
- package/agents/react-reviewer/evals/triggers.json +16 -0
- package/agents/refactor-cleaner/AGENT.md +82 -0
- package/agents/refactor-cleaner/evals/triggers.json +16 -0
- package/agents/ruby-build-resolver/AGENT.md +93 -0
- package/agents/ruby-build-resolver/evals/triggers.json +16 -0
- package/agents/ruby-reviewer/AGENT.md +62 -0
- package/agents/ruby-reviewer/evals/triggers.json +16 -0
- package/agents/rust-build-resolver/AGENT.md +98 -0
- package/agents/rust-build-resolver/evals/triggers.json +16 -0
- package/agents/rust-reviewer/AGENT.md +78 -0
- package/agents/rust-reviewer/evals/triggers.json +15 -0
- package/agents/security-reviewer/AGENT.md +84 -0
- package/agents/security-reviewer/evals/triggers.json +16 -0
- package/agents/silent-failure-hunter/AGENT.md +66 -0
- package/agents/silent-failure-hunter/evals/triggers.json +16 -0
- package/agents/solidity-reviewer/AGENT.md +70 -0
- package/agents/solidity-reviewer/evals/triggers.json +16 -0
- package/agents/swift-build-resolver/AGENT.md +85 -0
- package/agents/swift-build-resolver/evals/triggers.json +15 -0
- package/agents/swift-reviewer/AGENT.md +75 -0
- package/agents/swift-reviewer/evals/triggers.json +15 -0
- package/agents/typescript-build-resolver/AGENT.md +76 -0
- package/agents/typescript-build-resolver/evals/triggers.json +15 -0
- package/agents/typescript-reviewer/AGENT.md +76 -0
- package/agents/typescript-reviewer/evals/triggers.json +15 -0
- package/agents/vue-reviewer/AGENT.md +75 -0
- package/agents/vue-reviewer/evals/triggers.json +15 -0
- package/bin/kete-loom.js +683 -0
- package/commands/aside.md +19 -0
- package/commands/build-fix.md +26 -0
- package/commands/campaign.md +19 -0
- package/commands/changelog.md +36 -0
- package/commands/checkpoint.md +26 -0
- package/commands/codemap.md +25 -0
- package/commands/commit.md +26 -0
- package/commands/deps-audit.md +32 -0
- package/commands/epic-decompose.md +21 -0
- package/commands/explain.md +37 -0
- package/commands/feature-dev.md +22 -0
- package/commands/hookify.md +23 -0
- package/commands/i18n-check.md +39 -0
- package/commands/issue-sync.md +22 -0
- package/commands/jira.md +19 -0
- package/commands/learn.md +23 -0
- package/commands/loom.md +21 -0
- package/commands/loop.md +20 -0
- package/commands/model-route.md +27 -0
- package/commands/onboard.md +35 -0
- package/commands/orchestrate.md +21 -0
- package/commands/plan-feature.md +36 -0
- package/commands/postmortem.md +42 -0
- package/commands/pr-describe.md +39 -0
- package/commands/pr.md +22 -0
- package/commands/prd.md +41 -0
- package/commands/project-init.md +21 -0
- package/commands/refactor-clean.md +24 -0
- package/commands/resume-session.md +22 -0
- package/commands/review.md +35 -0
- package/commands/save-session.md +37 -0
- package/commands/security-scan.md +32 -0
- package/commands/sessions.md +21 -0
- package/commands/skill-create.md +21 -0
- package/commands/tdd.md +36 -0
- package/commands/test-coverage.md +24 -0
- package/commands/threat-model.md +36 -0
- package/commands/update-docs.md +25 -0
- package/commands/verify.md +21 -0
- package/hooks/block-dangerous-commands/HOOK.md +30 -0
- package/hooks/block-dangerous-commands/cases.json +50 -0
- package/hooks/block-dangerous-commands/hook.js +75 -0
- package/hooks/block-no-verify/HOOK.md +25 -0
- package/hooks/block-no-verify/cases.json +30 -0
- package/hooks/block-no-verify/hook.js +43 -0
- package/hooks/format-on-edit/HOOK.md +24 -0
- package/hooks/format-on-edit/hook.js +89 -0
- package/hooks/guard-secrets/HOOK.md +19 -0
- package/hooks/guard-secrets/cases.json +110 -0
- package/hooks/guard-secrets/hook.js +44 -0
- package/hooks/load-session-context/HOOK.md +20 -0
- package/hooks/load-session-context/hook.js +56 -0
- package/hooks/protect-files/HOOK.md +24 -0
- package/hooks/protect-files/cases.json +27 -0
- package/hooks/protect-files/hook.js +39 -0
- package/hooks/run.js +132 -0
- package/mcp/atlassian.json +11 -0
- package/mcp/chrome-devtools.json +18 -0
- package/mcp/cloudflare.json +11 -0
- package/mcp/context7.json +11 -0
- package/mcp/fetch.json +17 -0
- package/mcp/figma.json +11 -0
- package/mcp/git.json +17 -0
- package/mcp/github.json +11 -0
- package/mcp/linear.json +11 -0
- package/mcp/memory.json +18 -0
- package/mcp/notion.json +11 -0
- package/mcp/playwright.json +18 -0
- package/mcp/sentry.json +11 -0
- package/mcp/stripe.json +11 -0
- package/mcp/supabase.json +11 -0
- package/mcp/time.json +17 -0
- package/package.json +59 -4
- package/profiles.json +165 -0
- package/rules/common.md +21 -0
- package/rules/cpp.md +29 -0
- package/rules/csharp.md +25 -0
- package/rules/dart.md +29 -0
- package/rules/fsharp.md +28 -0
- package/rules/git.md +26 -0
- package/rules/go.md +29 -0
- package/rules/java.md +29 -0
- package/rules/kotlin.md +27 -0
- package/rules/perl.md +26 -0
- package/rules/php.md +28 -0
- package/rules/python.md +30 -0
- package/rules/ruby.md +27 -0
- package/rules/rust.md +26 -0
- package/rules/security.md +29 -0
- package/rules/swift.md +28 -0
- package/rules/testing.md +25 -0
- package/rules/typescript.md +29 -0
- package/schemas/skill-frontmatter.schema.json +52 -0
- package/scripts/lib/audit.js +96 -0
- package/scripts/lib/frontmatter.js +65 -0
- package/scripts/lib/hooks-install.js +292 -0
- package/scripts/lib/i18n.js +51 -0
- package/scripts/lib/inventory.js +85 -0
- package/scripts/lib/lint.js +158 -0
- package/scripts/lib/mcp-install.js +284 -0
- package/scripts/lib/profiles.js +128 -0
- package/scripts/lib/render.js +86 -0
- package/scripts/lib/rules-install.js +118 -0
- package/scripts/lib/skills.js +86 -0
- package/scripts/lib/targets.js +113 -0
- package/skills/accounting-api-integration/SKILL.md +81 -0
- package/skills/accounting-api-integration/evals/triggers.json +17 -0
- package/skills/accounting-api-integration/references/quickbooks.md +48 -0
- package/skills/accounting-api-integration/references/xero.md +45 -0
- package/skills/agent-harness-design/SKILL.md +125 -0
- package/skills/agent-harness-design/evals/triggers.json +21 -0
- package/skills/ai-media-generation/SKILL.md +89 -0
- package/skills/ai-media-generation/evals/triggers.json +16 -0
- package/skills/ai-media-generation/references/job-pipeline.md +76 -0
- package/skills/angular-patterns/SKILL.md +122 -0
- package/skills/angular-patterns/evals/triggers.json +16 -0
- package/skills/api-design/SKILL.md +96 -0
- package/skills/api-design/evals/triggers.json +18 -0
- package/skills/api-design/references/conventions.md +133 -0
- package/skills/architecture-decision-records/SKILL.md +98 -0
- package/skills/architecture-decision-records/evals/triggers.json +17 -0
- package/skills/article-writing/SKILL.md +108 -0
- package/skills/article-writing/evals/triggers.json +16 -0
- package/skills/aspnet-core-patterns/SKILL.md +149 -0
- package/skills/aspnet-core-patterns/evals/triggers.json +16 -0
- package/skills/auth-patterns/SKILL.md +104 -0
- package/skills/auth-patterns/evals/triggers.json +17 -0
- package/skills/auth-patterns/references/oauth-oidc.md +64 -0
- package/skills/autonomous-loops/SKILL.md +99 -0
- package/skills/autonomous-loops/evals/triggers.json +14 -0
- package/skills/aws-patterns/SKILL.md +91 -0
- package/skills/aws-patterns/evals/triggers.json +16 -0
- package/skills/azure-patterns/SKILL.md +86 -0
- package/skills/azure-patterns/evals/triggers.json +16 -0
- package/skills/benchmark-methodology/SKILL.md +88 -0
- package/skills/benchmark-methodology/evals/triggers.json +16 -0
- package/skills/benchmark-methodology/references/language-harnesses.md +130 -0
- package/skills/bgp-diagnostics/SKILL.md +84 -0
- package/skills/bgp-diagnostics/evals/triggers.json +18 -0
- package/skills/bgp-diagnostics/references/bgp-reference.md +81 -0
- package/skills/bioinformatics-workflows/SKILL.md +108 -0
- package/skills/bioinformatics-workflows/evals/triggers.json +16 -0
- package/skills/bioinformatics-workflows/references/commands.md +71 -0
- package/skills/bioinformatics-workflows/references/formats.md +24 -0
- package/skills/bioinformatics-workflows/references/workflow-managers.md +107 -0
- package/skills/brand-voice/SKILL.md +112 -0
- package/skills/brand-voice/evals/triggers.json +16 -0
- package/skills/browser-qa/SKILL.md +99 -0
- package/skills/browser-qa/evals/triggers.json +16 -0
- package/skills/bun-runtime/SKILL.md +93 -0
- package/skills/bun-runtime/evals/triggers.json +16 -0
- package/skills/cisco-ios-config/SKILL.md +101 -0
- package/skills/cisco-ios-config/evals/triggers.json +18 -0
- package/skills/cisco-ios-config/references/verification.md +63 -0
- package/skills/clickhouse-patterns/SKILL.md +101 -0
- package/skills/clickhouse-patterns/evals/triggers.json +18 -0
- package/skills/clickhouse-patterns/references/engines-and-views.md +138 -0
- package/skills/clinical-decision-support/SKILL.md +90 -0
- package/skills/clinical-decision-support/evals/triggers.json +16 -0
- package/skills/clinical-decision-support/references/cds-hooks.md +76 -0
- package/skills/clinical-decision-support/references/regulatory-boundaries.md +37 -0
- package/skills/code-tour/SKILL.md +97 -0
- package/skills/code-tour/evals/triggers.json +14 -0
- package/skills/codebase-onboarding/SKILL.md +112 -0
- package/skills/codebase-onboarding/evals/triggers.json +14 -0
- package/skills/cold-outreach-sequences/SKILL.md +90 -0
- package/skills/cold-outreach-sequences/evals/triggers.json +16 -0
- package/skills/content-repurposing/SKILL.md +82 -0
- package/skills/content-repurposing/evals/triggers.json +16 -0
- package/skills/context-budget/SKILL.md +84 -0
- package/skills/context-budget/evals/triggers.json +14 -0
- package/skills/conventional-commits/SKILL.md +144 -0
- package/skills/conventional-commits/evals/triggers.json +16 -0
- package/skills/cpp-patterns/SKILL.md +129 -0
- package/skills/cpp-patterns/evals/triggers.json +16 -0
- package/skills/cpp-patterns/references/cmake-and-tooling.md +88 -0
- package/skills/cpp-patterns/references/undefined-behaviour.md +75 -0
- package/skills/cpp-testing/SKILL.md +131 -0
- package/skills/cpp-testing/evals/triggers.json +16 -0
- package/skills/csharp-patterns/SKILL.md +110 -0
- package/skills/csharp-patterns/evals/triggers.json +17 -0
- package/skills/csharp-patterns/references/analyzers.md +59 -0
- package/skills/csharp-testing/SKILL.md +85 -0
- package/skills/csharp-testing/evals/triggers.json +17 -0
- package/skills/csharp-testing/references/integration-tests.md +77 -0
- package/skills/css-architecture/SKILL.md +143 -0
- package/skills/css-architecture/evals/triggers.json +16 -0
- package/skills/customs-trade-compliance/SKILL.md +95 -0
- package/skills/customs-trade-compliance/evals/triggers.json +16 -0
- package/skills/customs-trade-compliance/references/incoterms-2020.md +42 -0
- package/skills/dart-patterns/SKILL.md +157 -0
- package/skills/dart-patterns/evals/triggers.json +18 -0
- package/skills/dart-testing/SKILL.md +164 -0
- package/skills/dart-testing/evals/triggers.json +18 -0
- package/skills/data-analysis-notebooks/SKILL.md +113 -0
- package/skills/data-analysis-notebooks/evals/triggers.json +16 -0
- package/skills/data-analysis-notebooks/references/dataframe-pitfalls.md +49 -0
- package/skills/data-analysis-notebooks/references/statistics-pitfalls.md +24 -0
- package/skills/data-pipelines/SKILL.md +90 -0
- package/skills/data-pipelines/evals/triggers.json +16 -0
- package/skills/data-pipelines/references/orchestrators.md +64 -0
- package/skills/database-migrations/SKILL.md +102 -0
- package/skills/database-migrations/evals/triggers.json +16 -0
- package/skills/database-migrations/references/recipes.md +98 -0
- package/skills/database-migrations/references/tools.md +21 -0
- package/skills/dbt-patterns/SKILL.md +96 -0
- package/skills/dbt-patterns/evals/triggers.json +16 -0
- package/skills/dbt-patterns/references/yaml-examples.md +114 -0
- package/skills/deep-research/SKILL.md +107 -0
- package/skills/deep-research/evals/triggers.json +14 -0
- package/skills/dependency-audit/SKILL.md +103 -0
- package/skills/dependency-audit/evals/triggers.json +16 -0
- package/skills/dependency-audit/references/tools.md +124 -0
- package/skills/deployment-strategies/SKILL.md +108 -0
- package/skills/deployment-strategies/evals/triggers.json +16 -0
- package/skills/django-patterns/SKILL.md +123 -0
- package/skills/django-patterns/evals/triggers.json +16 -0
- package/skills/docker-patterns/SKILL.md +132 -0
- package/skills/docker-patterns/evals/triggers.json +16 -0
- package/skills/docker-patterns/references/dockerfiles.md +105 -0
- package/skills/e2e-testing-playwright/SKILL.md +94 -0
- package/skills/e2e-testing-playwright/evals/triggers.json +16 -0
- package/skills/e2e-testing-playwright/references/config-and-ci.md +129 -0
- package/skills/ef-core-patterns/SKILL.md +117 -0
- package/skills/ef-core-patterns/evals/triggers.json +16 -0
- package/skills/email-deliverability/SKILL.md +99 -0
- package/skills/email-deliverability/evals/triggers.json +17 -0
- package/skills/email-deliverability/references/dns-records.md +85 -0
- package/skills/error-handling-patterns/SKILL.md +112 -0
- package/skills/error-handling-patterns/evals/triggers.json +18 -0
- package/skills/error-handling-patterns/references/language-idioms.md +49 -0
- package/skills/estimation-and-scoping/SKILL.md +97 -0
- package/skills/estimation-and-scoping/evals/triggers.json +14 -0
- package/skills/evm-dapp-integration/SKILL.md +73 -0
- package/skills/evm-dapp-integration/evals/triggers.json +16 -0
- package/skills/fastapi-patterns/SKILL.md +127 -0
- package/skills/fastapi-patterns/evals/triggers.json +16 -0
- package/skills/ffmpeg-media-processing/SKILL.md +94 -0
- package/skills/ffmpeg-media-processing/evals/triggers.json +16 -0
- package/skills/ffmpeg-media-processing/references/recipes.md +112 -0
- package/skills/fhir-integration/SKILL.md +81 -0
- package/skills/fhir-integration/evals/triggers.json +16 -0
- package/skills/fhir-integration/references/search-and-bundles.md +73 -0
- package/skills/fhir-integration/references/smart-on-fhir.md +50 -0
- package/skills/flask-patterns/SKILL.md +132 -0
- package/skills/flask-patterns/evals/triggers.json +16 -0
- package/skills/flutter-patterns/SKILL.md +99 -0
- package/skills/flutter-patterns/evals/triggers.json +16 -0
- package/skills/flutterwave-integration/SKILL.md +75 -0
- package/skills/flutterwave-integration/evals/triggers.json +16 -0
- package/skills/flutterwave-integration/references/api.md +73 -0
- package/skills/flutterwave-integration/references/webhooks.md +49 -0
- package/skills/fsharp-patterns/SKILL.md +133 -0
- package/skills/fsharp-patterns/evals/triggers.json +16 -0
- package/skills/fsharp-testing/SKILL.md +136 -0
- package/skills/fsharp-testing/evals/triggers.json +16 -0
- package/skills/gcp-patterns/SKILL.md +86 -0
- package/skills/gcp-patterns/evals/triggers.json +16 -0
- package/skills/git-workflow/SKILL.md +114 -0
- package/skills/git-workflow/evals/triggers.json +16 -0
- package/skills/github-actions-ci/SKILL.md +117 -0
- package/skills/github-actions-ci/evals/triggers.json +16 -0
- package/skills/github-api-automation/SKILL.md +101 -0
- package/skills/github-api-automation/evals/triggers.json +17 -0
- package/skills/go-patterns/SKILL.md +118 -0
- package/skills/go-patterns/evals/triggers.json +17 -0
- package/skills/go-patterns/references/concurrency.md +162 -0
- package/skills/go-testing/SKILL.md +154 -0
- package/skills/go-testing/evals/triggers.json +17 -0
- package/skills/hexagonal-architecture/SKILL.md +89 -0
- package/skills/hexagonal-architecture/evals/triggers.json +17 -0
- package/skills/hipaa-compliance/SKILL.md +91 -0
- package/skills/hipaa-compliance/evals/triggers.json +18 -0
- package/skills/hipaa-compliance/references/safeguards.md +68 -0
- package/skills/hl7v2-integration/SKILL.md +69 -0
- package/skills/hl7v2-integration/evals/triggers.json +16 -0
- package/skills/hl7v2-integration/references/messages-and-ack.md +78 -0
- package/skills/homelab-network-setup/SKILL.md +125 -0
- package/skills/homelab-network-setup/evals/triggers.json +18 -0
- package/skills/homelab-network-setup/references/firewall-and-dns.md +45 -0
- package/skills/homelab-network-setup/references/wireguard.md +72 -0
- package/skills/hubtel-integration/SKILL.md +77 -0
- package/skills/hubtel-integration/evals/triggers.json +16 -0
- package/skills/hubtel-integration/references/api.md +92 -0
- package/skills/i18n-localization/SKILL.md +114 -0
- package/skills/i18n-localization/evals/triggers.json +16 -0
- package/skills/i18n-localization/references/libraries.md +52 -0
- package/skills/incident-postmortem/SKILL.md +118 -0
- package/skills/incident-postmortem/evals/triggers.json +14 -0
- package/skills/inventory-demand-planning/SKILL.md +89 -0
- package/skills/inventory-demand-planning/evals/triggers.json +16 -0
- package/skills/inventory-demand-planning/references/formulas.md +62 -0
- package/skills/investor-materials/SKILL.md +127 -0
- package/skills/investor-materials/evals/triggers.json +16 -0
- package/skills/investor-materials/references/templates.md +74 -0
- package/skills/invoicing-and-tax/SKILL.md +84 -0
- package/skills/invoicing-and-tax/evals/triggers.json +17 -0
- package/skills/invoicing-and-tax/references/e-invoicing.md +24 -0
- package/skills/invoicing-and-tax/references/invoice-fields.md +38 -0
- package/skills/invoicing-and-tax/references/rounding.md +26 -0
- package/skills/java-patterns/SKILL.md +141 -0
- package/skills/java-patterns/evals/triggers.json +17 -0
- package/skills/java-testing/SKILL.md +142 -0
- package/skills/java-testing/evals/triggers.json +16 -0
- package/skills/jetpack-compose-patterns/SKILL.md +88 -0
- package/skills/jetpack-compose-patterns/evals/triggers.json +16 -0
- package/skills/jira-integration/SKILL.md +92 -0
- package/skills/jira-integration/evals/triggers.json +17 -0
- package/skills/jpa-hibernate/SKILL.md +92 -0
- package/skills/jpa-hibernate/evals/triggers.json +18 -0
- package/skills/jpa-hibernate/references/mapping.md +99 -0
- package/skills/kotlin-multiplatform/SKILL.md +97 -0
- package/skills/kotlin-multiplatform/evals/triggers.json +16 -0
- package/skills/kotlin-patterns/SKILL.md +128 -0
- package/skills/kotlin-patterns/evals/triggers.json +16 -0
- package/skills/kotlin-testing/SKILL.md +117 -0
- package/skills/kotlin-testing/evals/triggers.json +16 -0
- package/skills/ktor-patterns/SKILL.md +135 -0
- package/skills/ktor-patterns/evals/triggers.json +18 -0
- package/skills/kubernetes-patterns/SKILL.md +106 -0
- package/skills/kubernetes-patterns/evals/triggers.json +16 -0
- package/skills/kubernetes-patterns/references/manifests.md +115 -0
- package/skills/laravel-patterns/SKILL.md +136 -0
- package/skills/laravel-patterns/evals/triggers.json +16 -0
- package/skills/linear-integration/SKILL.md +98 -0
- package/skills/linear-integration/evals/triggers.json +17 -0
- package/skills/literature-review/SKILL.md +109 -0
- package/skills/literature-review/evals/triggers.json +16 -0
- package/skills/literature-review/references/appraisal.md +41 -0
- package/skills/literature-review/references/search-sources.md +39 -0
- package/skills/llm-app-patterns/SKILL.md +97 -0
- package/skills/llm-app-patterns/evals/triggers.json +17 -0
- package/skills/llm-evals/SKILL.md +98 -0
- package/skills/llm-evals/evals/triggers.json +16 -0
- package/skills/llm-evals/references/graders.md +66 -0
- package/skills/logistics-exception-management/SKILL.md +90 -0
- package/skills/logistics-exception-management/evals/triggers.json +16 -0
- package/skills/manim-animations/SKILL.md +106 -0
- package/skills/manim-animations/evals/triggers.json +16 -0
- package/skills/market-research/SKILL.md +117 -0
- package/skills/market-research/evals/triggers.json +16 -0
- package/skills/marketing-campaign/SKILL.md +111 -0
- package/skills/marketing-campaign/evals/triggers.json +16 -0
- package/skills/mcp-server-patterns/SKILL.md +127 -0
- package/skills/mcp-server-patterns/evals/triggers.json +18 -0
- package/skills/mcp-server-patterns/references/protocol-notes.md +68 -0
- package/skills/metrics-dashboards/SKILL.md +113 -0
- package/skills/metrics-dashboards/evals/triggers.json +16 -0
- package/skills/ml-experiment-workflow/SKILL.md +125 -0
- package/skills/ml-experiment-workflow/evals/triggers.json +16 -0
- package/skills/mongodb-patterns/SKILL.md +115 -0
- package/skills/mongodb-patterns/evals/triggers.json +18 -0
- package/skills/mongodb-patterns/references/indexes-and-aggregation.md +94 -0
- package/skills/mpesa-daraja-integration/SKILL.md +70 -0
- package/skills/mpesa-daraja-integration/evals/triggers.json +16 -0
- package/skills/mpesa-daraja-integration/references/api.md +103 -0
- package/skills/mpesa-daraja-integration/references/callbacks.md +74 -0
- package/skills/mtn-momo-integration/SKILL.md +66 -0
- package/skills/mtn-momo-integration/evals/triggers.json +16 -0
- package/skills/mtn-momo-integration/references/api.md +153 -0
- package/skills/mtn-momo-integration/references/callbacks.md +92 -0
- package/skills/mtn-momo-integration/references/sandbox-and-go-live.md +51 -0
- package/skills/mtn-momo-integration/scripts/sandbox-setup.js +79 -0
- package/skills/mysql-patterns/SKILL.md +96 -0
- package/skills/mysql-patterns/evals/triggers.json +16 -0
- package/skills/mysql-patterns/references/explain.md +34 -0
- package/skills/mysql-patterns/references/online-ddl.md +41 -0
- package/skills/nestjs-patterns/SKILL.md +114 -0
- package/skills/nestjs-patterns/evals/triggers.json +16 -0
- package/skills/nestjs-patterns/references/testing.md +68 -0
- package/skills/network-automation/SKILL.md +107 -0
- package/skills/network-automation/evals/triggers.json +18 -0
- package/skills/network-automation/references/tool-snippets.md +153 -0
- package/skills/network-troubleshooting/SKILL.md +152 -0
- package/skills/network-troubleshooting/evals/triggers.json +18 -0
- package/skills/network-troubleshooting/references/commands.md +33 -0
- package/skills/nextjs-patterns/SKILL.md +150 -0
- package/skills/nextjs-patterns/evals/triggers.json +18 -0
- package/skills/nextjs-patterns/references/caching.md +87 -0
- package/skills/node-backend-patterns/SKILL.md +103 -0
- package/skills/node-backend-patterns/evals/triggers.json +16 -0
- package/skills/node-backend-patterns/references/templates.md +122 -0
- package/skills/nuxt-patterns/SKILL.md +103 -0
- package/skills/nuxt-patterns/evals/triggers.json +16 -0
- package/skills/parallel-agent-orchestration/SKILL.md +106 -0
- package/skills/parallel-agent-orchestration/evals/triggers.json +14 -0
- package/skills/paystack-integration/SKILL.md +59 -0
- package/skills/paystack-integration/evals/triggers.json +14 -0
- package/skills/paystack-integration/references/api.md +73 -0
- package/skills/paystack-integration/references/mobile-money.md +50 -0
- package/skills/paystack-integration/references/webhooks.md +80 -0
- package/skills/pci-dss-scope/SKILL.md +103 -0
- package/skills/pci-dss-scope/evals/triggers.json +18 -0
- package/skills/pci-dss-scope/references/saq-and-data.md +36 -0
- package/skills/perl-patterns/SKILL.md +135 -0
- package/skills/perl-patterns/evals/triggers.json +18 -0
- package/skills/perl-testing/SKILL.md +126 -0
- package/skills/perl-testing/evals/triggers.json +17 -0
- package/skills/php-patterns/SKILL.md +116 -0
- package/skills/php-patterns/evals/triggers.json +16 -0
- package/skills/php-testing/SKILL.md +123 -0
- package/skills/php-testing/evals/triggers.json +16 -0
- package/skills/postgres-patterns/SKILL.md +103 -0
- package/skills/postgres-patterns/evals/triggers.json +17 -0
- package/skills/postgres-patterns/references/explain.md +59 -0
- package/skills/postgres-patterns/references/locks.md +53 -0
- package/skills/prd-writing/SKILL.md +124 -0
- package/skills/prd-writing/evals/triggers.json +15 -0
- package/skills/prisma-patterns/SKILL.md +83 -0
- package/skills/prisma-patterns/evals/triggers.json +18 -0
- package/skills/prisma-patterns/references/prisma-8.md +46 -0
- package/skills/prisma-patterns/references/workflow.md +50 -0
- package/skills/privacy-compliance/SKILL.md +121 -0
- package/skills/privacy-compliance/evals/triggers.json +18 -0
- package/skills/privacy-compliance/references/jurisdictions.md +51 -0
- package/skills/production-scheduling/SKILL.md +98 -0
- package/skills/production-scheduling/evals/triggers.json +16 -0
- package/skills/prompt-engineering/SKILL.md +92 -0
- package/skills/prompt-engineering/evals/triggers.json +16 -0
- package/skills/python-patterns/SKILL.md +107 -0
- package/skills/python-patterns/evals/triggers.json +16 -0
- package/skills/python-patterns/references/tooling.md +101 -0
- package/skills/python-testing/SKILL.md +126 -0
- package/skills/python-testing/evals/triggers.json +16 -0
- package/skills/python-testing/references/mocking.md +71 -0
- package/skills/pytorch-patterns/SKILL.md +139 -0
- package/skills/pytorch-patterns/evals/triggers.json +16 -0
- package/skills/pytorch-patterns/references/debugging.md +39 -0
- package/skills/quarkus-patterns/SKILL.md +93 -0
- package/skills/quarkus-patterns/evals/triggers.json +18 -0
- package/skills/rag-pipeline-patterns/SKILL.md +117 -0
- package/skills/rag-pipeline-patterns/evals/triggers.json +18 -0
- package/skills/rag-pipeline-patterns/references/evaluation.md +37 -0
- package/skills/rag-pipeline-patterns/references/retrieval-recipes.md +78 -0
- package/skills/rails-patterns/SKILL.md +132 -0
- package/skills/rails-patterns/evals/triggers.json +16 -0
- package/skills/react-native-patterns/SKILL.md +101 -0
- package/skills/react-native-patterns/evals/triggers.json +16 -0
- package/skills/react-patterns/SKILL.md +140 -0
- package/skills/react-patterns/evals/triggers.json +18 -0
- package/skills/react-performance/SKILL.md +107 -0
- package/skills/react-performance/evals/triggers.json +18 -0
- package/skills/redis-patterns/SKILL.md +109 -0
- package/skills/redis-patterns/evals/triggers.json +18 -0
- package/skills/redis-patterns/references/operations.md +61 -0
- package/skills/refactoring-safely/SKILL.md +107 -0
- package/skills/refactoring-safely/evals/triggers.json +17 -0
- package/skills/release-management/SKILL.md +141 -0
- package/skills/release-management/evals/triggers.json +16 -0
- package/skills/remotion-video/SKILL.md +91 -0
- package/skills/remotion-video/evals/triggers.json +16 -0
- package/skills/remotion-video/references/lambda.md +66 -0
- package/skills/returns-reverse-logistics/SKILL.md +97 -0
- package/skills/returns-reverse-logistics/evals/triggers.json +16 -0
- package/skills/ruby-patterns/SKILL.md +127 -0
- package/skills/ruby-patterns/evals/triggers.json +18 -0
- package/skills/ruby-testing/SKILL.md +113 -0
- package/skills/ruby-testing/evals/triggers.json +18 -0
- package/skills/runbook-writing/SKILL.md +101 -0
- package/skills/runbook-writing/evals/triggers.json +14 -0
- package/skills/rust-patterns/SKILL.md +118 -0
- package/skills/rust-patterns/evals/triggers.json +16 -0
- package/skills/rust-patterns/references/cargo-and-clippy.md +105 -0
- package/skills/rust-patterns/references/errors-and-traits.md +97 -0
- package/skills/rust-testing/SKILL.md +148 -0
- package/skills/rust-testing/evals/triggers.json +16 -0
- package/skills/rust-testing/references/fakes-proptest-async.md +113 -0
- package/skills/search-first/SKILL.md +73 -0
- package/skills/search-first/evals/triggers.json +14 -0
- package/skills/security-review/SKILL.md +58 -0
- package/skills/security-review/evals/triggers.json +14 -0
- package/skills/security-review/references/checklist.md +54 -0
- package/skills/security-review/scripts/scan-secrets.js +79 -0
- package/skills/seo-audit/SKILL.md +111 -0
- package/skills/seo-audit/evals/triggers.json +16 -0
- package/skills/seo-audit/references/checks.md +65 -0
- package/skills/session-handoff/SKILL.md +101 -0
- package/skills/session-handoff/evals/triggers.json +14 -0
- package/skills/skill-authoring/SKILL.md +87 -0
- package/skills/skill-authoring/evals/triggers.json +14 -0
- package/skills/skill-authoring/references/frontmatter.md +40 -0
- package/skills/slack-app-integration/SKILL.md +108 -0
- package/skills/slack-app-integration/evals/triggers.json +17 -0
- package/skills/smart-contract-security/SKILL.md +110 -0
- package/skills/smart-contract-security/evals/triggers.json +16 -0
- package/skills/smart-contract-security/references/vulnerability-checklist.md +79 -0
- package/skills/social-media-publishing/SKILL.md +84 -0
- package/skills/social-media-publishing/evals/triggers.json +17 -0
- package/skills/social-media-publishing/references/platform-apis.md +116 -0
- package/skills/solidity-patterns/SKILL.md +95 -0
- package/skills/solidity-patterns/evals/triggers.json +16 -0
- package/skills/spring-boot-patterns/SKILL.md +128 -0
- package/skills/spring-boot-patterns/evals/triggers.json +18 -0
- package/skills/spring-boot-patterns/references/testing.md +72 -0
- package/skills/stripe-integration/SKILL.md +69 -0
- package/skills/stripe-integration/evals/triggers.json +16 -0
- package/skills/stripe-integration/references/api.md +71 -0
- package/skills/stripe-integration/references/billing.md +58 -0
- package/skills/stripe-integration/references/webhooks.md +76 -0
- package/skills/subscription-billing-ops/SKILL.md +118 -0
- package/skills/subscription-billing-ops/evals/triggers.json +17 -0
- package/skills/subscription-billing-ops/references/proration.md +47 -0
- package/skills/subscription-billing-ops/references/revenue-metrics.md +38 -0
- package/skills/svelte-patterns/SKILL.md +122 -0
- package/skills/svelte-patterns/evals/triggers.json +16 -0
- package/skills/swift-patterns/SKILL.md +113 -0
- package/skills/swift-patterns/evals/triggers.json +16 -0
- package/skills/swift-patterns/references/concurrency-migration.md +40 -0
- package/skills/swift-testing/SKILL.md +121 -0
- package/skills/swift-testing/evals/triggers.json +16 -0
- package/skills/swift-testing/references/swift-testing-cheatsheet.md +81 -0
- package/skills/swiftui-patterns/SKILL.md +102 -0
- package/skills/swiftui-patterns/evals/triggers.json +16 -0
- package/skills/tdd-workflow/SKILL.md +67 -0
- package/skills/tdd-workflow/evals/triggers.json +14 -0
- package/skills/technical-writing/SKILL.md +95 -0
- package/skills/technical-writing/evals/triggers.json +14 -0
- package/skills/terraform-patterns/SKILL.md +122 -0
- package/skills/terraform-patterns/evals/triggers.json +16 -0
- package/skills/terraform-patterns/references/backends.md +67 -0
- package/skills/test-coverage-improvement/SKILL.md +107 -0
- package/skills/test-coverage-improvement/evals/triggers.json +16 -0
- package/skills/threat-modeling/SKILL.md +113 -0
- package/skills/threat-modeling/evals/triggers.json +16 -0
- package/skills/typescript-patterns/SKILL.md +122 -0
- package/skills/typescript-patterns/evals/triggers.json +16 -0
- package/skills/typescript-testing/SKILL.md +101 -0
- package/skills/typescript-testing/evals/triggers.json +16 -0
- package/skills/verification-loop/SKILL.md +70 -0
- package/skills/verification-loop/evals/triggers.json +14 -0
- package/skills/verification-loop/scripts/detect-checks.js +102 -0
- package/skills/vite-patterns/SKILL.md +117 -0
- package/skills/vite-patterns/evals/triggers.json +16 -0
- package/skills/vue-patterns/SKILL.md +104 -0
- package/skills/vue-patterns/evals/triggers.json +16 -0
- package/skills/web-accessibility/SKILL.md +119 -0
- package/skills/web-accessibility/evals/triggers.json +16 -0
- package/skills/web-performance/SKILL.md +107 -0
- package/skills/web-performance/evals/triggers.json +16 -0
- package/skills/web-scraping/SKILL.md +80 -0
- package/skills/web-scraping/evals/triggers.json +16 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"We're raising prices next quarter. How do we move existing subscribers to the new price without breaking their renewals?",
|
|
4
|
+
"What should a customer pay if they upgrade from Basic to Pro halfway through their billing month?",
|
|
5
|
+
"Design a dunning flow for failed renewal payments: how many retries, which emails, and when do we cut access?",
|
|
6
|
+
"Our MRR movements don't add up from opening to closing balance each month, help me find out why",
|
|
7
|
+
"Customers on annual plans who cancel mid-year: should we issue a credit note, a refund, or neither?",
|
|
8
|
+
"Free trial users get charged twice when the trial ends; the conversion job and the renewal job both fire"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Integrate Stripe Checkout in subscription mode and handle the invoice.paid webhook",
|
|
12
|
+
"What fields does a VAT invoice need for a B2B customer in Germany?",
|
|
13
|
+
"Push our paid invoices into QuickBooks Online as Payment entities",
|
|
14
|
+
"Review the billing module diff for rounding bugs and double charges before we merge",
|
|
15
|
+
"Write a pricing page with three tiers and a monthly/annual toggle"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Proration: formulas, rounding and edge cases
|
|
2
|
+
|
|
3
|
+
## Formula
|
|
4
|
+
|
|
5
|
+
For a period `[start, end)` and a change at instant `t` (`start <= t < end`):
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
remaining_fraction = (end - t) / (end - start) # seconds, exact rational
|
|
9
|
+
credit_old = round(old_amount_minor * remaining_fraction)
|
|
10
|
+
charge_new = round(new_amount_minor * remaining_fraction)
|
|
11
|
+
proration = charge_new - credit_old # may be negative on a downgrade
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Keep the fraction exact (integers for numerator and denominator, or a decimal library) and round each line once. Use a decimal type (`numeric` in SQL, `Decimal` in Python, `decimal.js` or `big.js` in JS, `BigDecimal` in Java), never binary floats.
|
|
15
|
+
|
|
16
|
+
## Rounding rules
|
|
17
|
+
|
|
18
|
+
- Pick one rule and write it down: half-up (common for invoices) or half-even (banker's). Many tax authorities specify rounding for tax; use theirs for tax lines.
|
|
19
|
+
- Round per line, then sum. Don't round the total separately, or lines won't add up to it.
|
|
20
|
+
- Zero-decimal currencies (JPY, KRW) have no minor unit below 1; three-decimal currencies (BHD, IQD, JOD, KWD, LYD, OMR, TND in the [ISO 4217 list](https://www.six-group.com/en/products-services/financial-information/data-standards.html) as of Sep 2026) have 1000 minor units. Take minor-unit exponents from ISO 4217 data, not a hard-coded 100.
|
|
21
|
+
|
|
22
|
+
## Worked example
|
|
23
|
+
|
|
24
|
+
Monthly plan, 30-day period `2026-04-01T00:00Z` to `2026-05-01T00:00Z` (2,592,000 s). Upgrade from 2,000 to 5,000 (minor units) at `2026-04-16T00:00Z`.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
remaining = 15 days = 1,296,000 s → fraction 1/2
|
|
28
|
+
credit_old = round(2000 * 1/2) = 1000
|
|
29
|
+
charge_new = round(5000 * 1/2) = 2500
|
|
30
|
+
proration = 1500 charged now; renewal on 2026-05-01 is 5000
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Same change in a 31-day month at the same calendar date gives `16/31` remaining: credit `round(2000*16/31)=round(1032.26)=1032`, charge `round(5000*16/31)=round(2580.65)=2581`, proration `1549`. Customers notice that months differ; say in the docs whether you prorate by seconds of the actual period or by a fixed 30-day month.
|
|
34
|
+
|
|
35
|
+
## Edge cases to test
|
|
36
|
+
|
|
37
|
+
| Case | Expected |
|
|
38
|
+
|---|---|
|
|
39
|
+
| Anchor on the 31st | Next boundary is the last day of shorter months, then back to the 31st when it exists; never drift to the 28th permanently |
|
|
40
|
+
| Anchor on 29 Feb (annual) | Pick 28 Feb or 1 Mar in non-leap years; document it |
|
|
41
|
+
| Change exactly at `end` | Belongs to the next period; no proration on the old one |
|
|
42
|
+
| Two changes in one period | Each prorates from its own `t` against the price then in force; sum of lines equals the net change |
|
|
43
|
+
| Upgrade then downgrade same day | Net proration is small but non-zero; credits must not exceed what was charged |
|
|
44
|
+
| DST change in customer timezone | Period length in seconds differs by an hour; compute from instants, display in local time |
|
|
45
|
+
| Proration below the minimum charge amount | Carry it to the next invoice as a pending line instead of charging (cards have minimums per currency) |
|
|
46
|
+
| Discount or coupon active | Prorate the discounted amount, and state whether the coupon itself prorates |
|
|
47
|
+
| Tax-inclusive prices | Prorate the gross, then split tax using the same rule as normal invoices |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Revenue metrics queries
|
|
2
|
+
|
|
3
|
+
Shapes for a schema with `subscriptions`, `invoices`, `invoice_lines (invoice_id, subscription_id, kind, amount_minor, currency, period_start, period_end)` and `credit_notes`. Adapt names; give each currency its own figures or convert with a dated rate table, never with today's rate for past months.
|
|
4
|
+
|
|
5
|
+
## MRR at a date
|
|
6
|
+
|
|
7
|
+
```sql
|
|
8
|
+
-- Recurring lines whose service period covers :as_of, net of discounts, normalised to a month.
|
|
9
|
+
SELECT l.currency,
|
|
10
|
+
SUM(l.amount_minor * 2629746.0 -- average seconds per month (365.2425 days / 12)
|
|
11
|
+
/ EXTRACT(EPOCH FROM (l.period_end - l.period_start))) AS mrr_minor
|
|
12
|
+
FROM invoice_lines l
|
|
13
|
+
JOIN invoices i ON i.id = l.invoice_id
|
|
14
|
+
WHERE l.kind = 'recurring'
|
|
15
|
+
AND i.status IN ('open', 'paid')
|
|
16
|
+
AND l.period_start <= :as_of AND l.period_end > :as_of
|
|
17
|
+
GROUP BY l.currency;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Subtract credit note lines that credit recurring lines in the same period. Decide whether `open` (unpaid, in dunning) counts; many teams exclude invoices past the final dunning attempt.
|
|
21
|
+
|
|
22
|
+
## MRR movements for a month
|
|
23
|
+
|
|
24
|
+
For each customer, compare MRR at the start (`m0`) and end (`m1`) of the month:
|
|
25
|
+
|
|
26
|
+
| Bucket | Rule |
|
|
27
|
+
|---|---|
|
|
28
|
+
| new | `m0 = 0`, `m1 > 0`, first ever paid subscription for the customer |
|
|
29
|
+
| reactivation | `m0 = 0`, `m1 > 0`, customer had MRR before |
|
|
30
|
+
| expansion | `m1 > m0 > 0` |
|
|
31
|
+
| contraction | `0 < m1 < m0` |
|
|
32
|
+
| churn | `m0 > 0`, `m1 = 0` |
|
|
33
|
+
|
|
34
|
+
Check: `opening + new + reactivation + expansion - contraction - churn = closing`. Group by customer, not subscription, if a plan change creates a new subscription; otherwise every plan change shows as churn plus new.
|
|
35
|
+
|
|
36
|
+
## Deferred revenue (straight-line)
|
|
37
|
+
|
|
38
|
+
For each paid recurring line, revenue earned in month `M` is `amount * overlap_seconds(line period, M) / line_period_seconds`. Deferred balance at a date is paid amount minus earned to date. Hand the per-line schedule to finance; recognition policy, contract modifications and multi-element arrangements are their call.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: svelte-patterns
|
|
3
|
+
description: Svelte 5 runes and SvelteKit 2 guidance for $state, $derived, $effect and $props reactivity, snippets instead of slots, universal versus server load functions, form actions with use:enhance and fail(), $lib/server and $env/static/private for server-only code, and SSR traps such as shared module state leaking between users. Use when writing or migrating Svelte components to runes, adding SvelteKit routes, loads or actions, or debugging an effect loop or a value that differs between server and browser.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Targets Svelte 5 and SvelteKit 2; Svelte 4 syntax is mentioned only for migration. Needs the project's package manager to run svelte-check and tests.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Svelte 5 and SvelteKit patterns
|
|
14
|
+
|
|
15
|
+
Write runes-based Svelte components and SvelteKit routes that load data on the right side of the network, keep secrets on the server, and render the same on server and client.
|
|
16
|
+
|
|
17
|
+
## 0. Orient first
|
|
18
|
+
|
|
19
|
+
1. Read `package.json` for `svelte` and `@sveltejs/kit` versions and the adapter. Svelte 4 projects (`export let`, `$:`, `on:click`, `<slot>`) need different syntax; do not mix styles inside one component.
|
|
20
|
+
2. To migrate, run `npx sv migrate svelte-5` (and `npx sv migrate app-state` for `$app/stores`), then review the diff.
|
|
21
|
+
3. Run `npx svelte-check` before and after your change; it type-checks `.svelte` files and catches rune misuse.
|
|
22
|
+
|
|
23
|
+
## 1. Runes
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import type { Snippet } from 'svelte';
|
|
28
|
+
let { items, onselect, children }: { items: Item[]; onselect: (id: string) => void; children?: Snippet } = $props();
|
|
29
|
+
let query = $state('');
|
|
30
|
+
let filtered = $derived(items.filter((i) => i.name.includes(query)));
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<input bind:value={query} />
|
|
34
|
+
{#each filtered as item (item.id)}
|
|
35
|
+
<button onclick={() => onselect(item.id)}>{item.name}</button>
|
|
36
|
+
{/each}
|
|
37
|
+
{@render children?.()}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- `$state` for anything that changes. Objects and arrays become deep proxies, so `list.push(x)` works. Use `$state.raw` for large data you replace wholesale.
|
|
41
|
+
- `$derived(expr)` or `$derived.by(() => ...)` for computed values. Never compute derived state with `$effect` writing to another `$state`; that causes extra renders and loops.
|
|
42
|
+
- `$effect` only for side effects that talk to the outside (DOM APIs, timers, analytics, a non-Svelte library). Return a cleanup function for anything it starts. It does not run during SSR.
|
|
43
|
+
- An effect that reads and writes the same state loops forever. Read with `untrack(() => x)` (imported from `svelte`) when you must, or restructure into `$derived`.
|
|
44
|
+
- `$props()` replaces `export let`. Props are read-only by default; to bind two-way, mark the prop `$bindable()` and the parent uses `bind:value`.
|
|
45
|
+
- Destructuring a `$state` object into locals takes a snapshot; read `obj.field` where you need it to stay live. Use `$state.snapshot(obj)` when passing state to code that does not expect a proxy (`structuredClone`, external libs).
|
|
46
|
+
- Shared reactive state lives in `.svelte.ts` / `.svelte.js` modules. Export an object or class with `$state` fields, not a reassigned `let`, because importers cannot see a reassignment.
|
|
47
|
+
|
|
48
|
+
## 2. Components
|
|
49
|
+
|
|
50
|
+
- Events are props: `onclick={...}`, and components accept callback props (`onselect`) instead of `createEventDispatcher`.
|
|
51
|
+
- Use snippets (`{#snippet row(item)}...{/snippet}` and `{@render row(item)}`) instead of slots in new code. `children` is the default snippet.
|
|
52
|
+
- Key every `{#each}` that can reorder: `{#each items as item (item.id)}`.
|
|
53
|
+
- `{@html}` with user content is XSS. Sanitise first or render as text.
|
|
54
|
+
|
|
55
|
+
## 3. SvelteKit load functions
|
|
56
|
+
|
|
57
|
+
| File | Runs | Use for |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `+page.ts` / `+layout.ts` | server on first load, then browser | public APIs, data safe to fetch from the browser |
|
|
60
|
+
| `+page.server.ts` / `+layout.server.ts` | server only | database, secrets, private APIs, cookies |
|
|
61
|
+
|
|
62
|
+
1. Use the `fetch` passed to `load`, not the global one: it forwards cookies, works with relative URLs on the server, and inlines the response so the browser does not refetch.
|
|
63
|
+
2. Return plain serialisable data from server loads (Svelte's `devalue` handles Date, Map, Set; not class instances or functions).
|
|
64
|
+
3. Start independent requests in parallel; do not `await` them one after another. Avoid waterfalls caused by `await parent()` before your own fetches.
|
|
65
|
+
4. Throw expected failures with `error(404, 'Not found')` and send users elsewhere with `redirect(303, '/login')` from `@sveltejs/kit`. In SvelteKit 2 these throw for you; do not wrap them in a `try` that swallows them.
|
|
66
|
+
5. Read the result in the page with `let { data } = $props();`.
|
|
67
|
+
6. Re-run loads with `invalidate(url)` / `invalidateAll()`; declare custom dependencies with `depends('app:cart')`.
|
|
68
|
+
7. Read page and navigation state from `$app/state` (`page.url`, `page.params`, `navigating`), not the deprecated `$app/stores`.
|
|
69
|
+
|
|
70
|
+
## 4. Form actions
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// +page.server.ts
|
|
74
|
+
export const actions = {
|
|
75
|
+
default: async ({ request, locals }) => {
|
|
76
|
+
const data = await request.formData();
|
|
77
|
+
const email = String(data.get('email') ?? '');
|
|
78
|
+
if (!email.includes('@')) return fail(400, { email, missing: true });
|
|
79
|
+
await locals.db.subscribe(email);
|
|
80
|
+
redirect(303, '/thanks');
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- Validate every field on the server, return `fail(status, data)` with the values to refill the form, and read them in the page through `let { form } = $props();`.
|
|
86
|
+
- Add `use:enhance` from `$app/forms` for progressive enhancement; the form must still work without JavaScript.
|
|
87
|
+
- Check authorization in the action itself (`locals.user`), not only in the page load.
|
|
88
|
+
- Use named actions (`?/delete`) when one page has several forms.
|
|
89
|
+
|
|
90
|
+
## 5. Server-only code and secrets
|
|
91
|
+
|
|
92
|
+
- Put server modules under `src/lib/server/` or name them `*.server.ts`. SvelteKit refuses to bundle them into client code, which turns a leak into a build error.
|
|
93
|
+
- Secrets come from `$env/static/private` (build-time) or `$env/dynamic/private` (runtime). Only `PUBLIC_`-prefixed values belong in `$env/static/public` / `$env/dynamic/public`.
|
|
94
|
+
- Put request-scoped data (current user, db handle) on `event.locals` in `hooks.server.ts`, and type it in `src/app.d.ts`.
|
|
95
|
+
|
|
96
|
+
## 6. SSR pitfalls
|
|
97
|
+
|
|
98
|
+
1. **Module-level state is shared by every user on the server.** A `let user = $state(null)` in a module, or a writable store, set during a request leaks to the next visitor. Keep per-request data in `locals` and load data, or create state in a component and share it with `setContext`/`getContext`.
|
|
99
|
+
2. Do not touch `window`, `document` or `localStorage` during render. Use `$effect` or `onMount`, or guard with `browser` from `$app/environment`.
|
|
100
|
+
3. Values that differ by environment (time, random ids, locale) cause hydration mismatches; compute them on the server and pass them through load data.
|
|
101
|
+
4. Turn off SSR per route with `export const ssr = false` only for genuinely browser-only pages; set `export const prerender = true` for static pages.
|
|
102
|
+
|
|
103
|
+
## 7. Tests
|
|
104
|
+
|
|
105
|
+
- Unit-test logic in `.svelte.ts` and server modules with Vitest. Test components with `@testing-library/svelte` (`render`, `screen`, user-event) and assert on visible output.
|
|
106
|
+
- Test actions and loads by calling the exported functions with a minimal event object, or end to end with Playwright against `vite preview`.
|
|
107
|
+
|
|
108
|
+
## Stop and ask the user
|
|
109
|
+
|
|
110
|
+
- Migrating a large Svelte 4 codebase to runes in one go.
|
|
111
|
+
- Changing the adapter, or the `ssr`/`prerender` setting of a production route.
|
|
112
|
+
- A secret is in a public env var or a client-reachable module.
|
|
113
|
+
|
|
114
|
+
## Never
|
|
115
|
+
|
|
116
|
+
- Never import from `$lib/server` or `$env/*/private` in a `+page.ts`, `+layout.ts` or component.
|
|
117
|
+
- Never derive state with `$effect`; use `$derived`.
|
|
118
|
+
- Never keep per-user data in module-level state.
|
|
119
|
+
|
|
120
|
+
## Report back
|
|
121
|
+
|
|
122
|
+
List the files changed, which side (server, universal, client) each new load or action runs on, the `svelte-check` and test results, and anything not checked in a browser.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Migrate this Svelte component from export let and $: to runes",
|
|
4
|
+
"My $effect keeps re-running forever when I update the counter",
|
|
5
|
+
"Add a SvelteKit form action for the newsletter signup with server validation",
|
|
6
|
+
"Should this SvelteKit load go in +page.ts or +page.server.ts? It reads the database",
|
|
7
|
+
"One user's cart is showing up for another user in our SvelteKit app"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Add a Nuxt server route that returns the product list",
|
|
11
|
+
"Write a Vue 3 Pinia store for the shopping cart",
|
|
12
|
+
"Set up an Angular reactive form with typed controls",
|
|
13
|
+
"Write Playwright tests that click through the signup flow in the browser",
|
|
14
|
+
"Audit the signup page for screen reader accessibility"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swift-patterns
|
|
3
|
+
description: Idiomatic Swift 5.x and 6 for writing or restructuring code, covering structs and enums over classes, protocols with associated types and some/any, optional handling without force unwraps, typed and untyped throws, async/await, actors, Sendable and strict concurrency checking, plus Package.swift layout for SwiftPM. Use when writing new Swift code, choosing between a struct, class or actor, adopting async/await, migrating a module to the Swift 6 language mode, or setting up a Swift package.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a Swift 5.9+ toolchain (swift or xcodebuild) to build and check the code. Swift 6.2 features are marked as such.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: writes-files
|
|
10
|
+
kete-loom/requires: "swift"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Swift patterns
|
|
15
|
+
|
|
16
|
+
Write Swift that the compiler can check: value semantics by default, isolation stated explicitly, failure in the type system. This skill is for language-level code. SwiftUI view structure belongs to `swiftui-patterns`; tests belong to `swift-testing`.
|
|
17
|
+
|
|
18
|
+
## 0. Before you write
|
|
19
|
+
|
|
20
|
+
1. Find the toolchain and mode: `swift --version`, the `// swift-tools-version:` line in `Package.swift`, or `SWIFT_VERSION` in the Xcode build settings. Tools version 6.0+ defaults to the Swift 6 language mode unless `swiftLanguageModes: [.v5]` (or a target's `.swiftLanguageMode(.v5)`) says otherwise.
|
|
21
|
+
2. Check the concurrency settings: `SWIFT_STRICT_CONCURRENCY`, `SWIFT_DEFAULT_ACTOR_ISOLATION` and `SWIFT_APPROACHABLE_CONCURRENCY` in Xcode, or `swiftSettings` in `Package.swift`. Code that compiles in one setting may not in another, so match the project.
|
|
22
|
+
3. Read two or three neighbouring files and follow their conventions (naming, error types, logging, file layout).
|
|
23
|
+
|
|
24
|
+
## 1. Pick the type
|
|
25
|
+
|
|
26
|
+
| Need | Use |
|
|
27
|
+
|---|---|
|
|
28
|
+
| Data with no identity, copied freely | `struct` |
|
|
29
|
+
| A closed set of states or results | `enum` with associated values |
|
|
30
|
+
| Shared mutable state accessed from several tasks | `actor` |
|
|
31
|
+
| UI state or anything touching UIKit/AppKit | a `@MainActor` class or the main-actor default |
|
|
32
|
+
| Identity, inheritance, or Objective-C interop | `final class` (drop `final` only when you need subclassing) |
|
|
33
|
+
|
|
34
|
+
- Prefer `let`. Make a stored property `var` only when something mutates it.
|
|
35
|
+
- Model impossible states out: `enum LoadState { case idle, loading, loaded(User), failed(Error) }` beats three optionals and a Bool.
|
|
36
|
+
- Large value types with copy-on-write storage are fine; don't convert a struct to a class "for performance" without a measurement.
|
|
37
|
+
|
|
38
|
+
## 2. Protocols and generics
|
|
39
|
+
|
|
40
|
+
- Write protocols for capabilities your code needs (`protocol UserStore { func user(id: User.ID) async throws -> User }`), not mirrors of one concrete class.
|
|
41
|
+
- Use `some Protocol` for parameters and return types when one concrete type flows through. Use `any Protocol` only when you need heterogeneous storage or runtime swapping; it costs an existential box.
|
|
42
|
+
- Protocols with `associatedtype` work with `some`, generics and primary associated types (`some Collection<Int>`). Reach for a hand-written type eraser only when `any P<T>` cannot express it.
|
|
43
|
+
- Put shared behaviour in protocol extensions, but remember extension-only methods are statically dispatched: a conforming type cannot override them unless the method is also a requirement.
|
|
44
|
+
|
|
45
|
+
## 3. Optionals
|
|
46
|
+
|
|
47
|
+
- Unwrap with `guard let x else { return }` (Swift 5.7 shorthand), `if let`, `??` with a real default, or optional chaining.
|
|
48
|
+
- Never write `!` on a value that comes from input, the network, a dictionary lookup, `first`, a cast or a file. Force unwrap only an invariant visible on the same line, such as `URL(string: "https://example.com")!` on a string literal.
|
|
49
|
+
- No implicitly unwrapped optionals (`var x: T!`) outside `@IBOutlet` and two-phase init you cannot restructure.
|
|
50
|
+
- `try?` throws the error away. Use it only when "no value" is genuinely the same outcome as "failed".
|
|
51
|
+
|
|
52
|
+
## 4. Errors
|
|
53
|
+
|
|
54
|
+
- Throw a domain `enum` conforming to `Error` (and `LocalizedError` if users see it). Include the associated data a caller needs to react.
|
|
55
|
+
- Swift 6 typed throws (`func parse() throws(ParseError)`) suit closed, internal error sets and embedded code. For public APIs that will grow new failures, keep plain `throws`.
|
|
56
|
+
- Catch where you can do something: retry, map to a user message, or fall back. Otherwise let it propagate. An empty `catch {}` is a bug.
|
|
57
|
+
- Use `Result` for stored or callback-delivered outcomes; for async code, `async throws` replaces it.
|
|
58
|
+
- `fatalError`, `precondition` and `!` crash release builds. Reserve them for programmer errors, never for bad input.
|
|
59
|
+
|
|
60
|
+
## 5. Concurrency
|
|
61
|
+
|
|
62
|
+
1. Make functions `async` instead of taking completion handlers. Wrap a legacy callback API once with `withCheckedThrowingContinuation` and resume exactly once on every path.
|
|
63
|
+
2. Use structured concurrency first: `async let` for a fixed number of child tasks, `withThrowingTaskGroup` for a dynamic number. They cancel children and propagate errors.
|
|
64
|
+
3. Use `Task { }` only at the boundary where sync code starts async work (a button action, `viewDidLoad`). Keep the handle if the work must be cancelled. `Task.detached` is almost never right; it drops priority, task-locals and actor context.
|
|
65
|
+
4. Protect shared mutable state with an `actor`. Remember actor reentrancy: state can change across every `await` inside an actor method, so re-check invariants after each suspension.
|
|
66
|
+
5. Mark UI-facing types `@MainActor`. Don't sprinkle `DispatchQueue.main.async` in new async code.
|
|
67
|
+
6. Check `Task.isCancelled` or call `try Task.checkCancellation()` in long loops.
|
|
68
|
+
7. In Swift 6.2 with `NonisolatedNonsendingByDefault`, a `nonisolated async` function runs on the caller's actor. Mark CPU-heavy work `@concurrent` to move it off the caller.
|
|
69
|
+
|
|
70
|
+
### Sendable rules
|
|
71
|
+
|
|
72
|
+
- A type crossing an isolation boundary must be `Sendable`. Structs and enums whose stored properties are all `Sendable` get it implicitly when not public; state it explicitly on public types.
|
|
73
|
+
- A class is `Sendable` only if it is `final` with immutable `let` stored properties of `Sendable` types, or it is actor-isolated.
|
|
74
|
+
- `@unchecked Sendable` is a promise you protect the state yourself (for example with a `Mutex` from the `Synchronization` module, Swift 6 and macOS 15 / iOS 18 or later, or a lock such as `OSAllocatedUnfairLock` or `NSLock`). Write the reason in a comment next to it.
|
|
75
|
+
- `nonisolated(unsafe)` and `@preconcurrency import` are migration tools. Each use needs a comment saying why and when it goes away.
|
|
76
|
+
|
|
77
|
+
For migrating a module to complete checking or the Swift 6 mode, read `references/concurrency-migration.md`.
|
|
78
|
+
|
|
79
|
+
## 6. SwiftPM
|
|
80
|
+
|
|
81
|
+
- One `Package.swift` with explicit `products`, `targets` and `dependencies`. Pin remote packages with `from:` (semver) and commit `Package.resolved` for apps and executables. A library's `Package.resolved` is ignored by packages that depend on it, so its version ranges must be correct on their own.
|
|
82
|
+
- Keep the core logic in a library target with no UIKit/SwiftUI imports so it builds and tests on any platform with `swift build` and `swift test`.
|
|
83
|
+
- Put per-target flags in `swiftSettings`, for example:
|
|
84
|
+
|
|
85
|
+
```swift
|
|
86
|
+
.target(
|
|
87
|
+
name: "Core",
|
|
88
|
+
swiftSettings: [
|
|
89
|
+
.enableUpcomingFeature("ExistentialAny"),
|
|
90
|
+
// Swift 5 mode only; Swift 6 mode already enforces complete checking.
|
|
91
|
+
.enableUpcomingFeature("StrictConcurrency"),
|
|
92
|
+
]
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Use `package` access (Swift 5.9+) for symbols shared between targets of one package, and access levels on imports (`internal import Foo`, Swift 6) to stop a dependency leaking into your public API.
|
|
97
|
+
|
|
98
|
+
## 7. Check your work
|
|
99
|
+
|
|
100
|
+
1. Build with the project's command (`swift build`, or `xcodebuild -scheme <Scheme> build`). Zero new warnings, not just zero errors.
|
|
101
|
+
2. If the project is still in Swift 5 mode, also build once with `swift build -Xswiftc -strict-concurrency=complete` and report new concurrency warnings rather than hiding them.
|
|
102
|
+
3. Run the tests (`swift test` or `xcodebuild test`).
|
|
103
|
+
|
|
104
|
+
## Never
|
|
105
|
+
|
|
106
|
+
- Never silence a concurrency diagnostic with `@unchecked Sendable`, `nonisolated(unsafe)` or `@preconcurrency` without the comment described above. If the honest fix is a redesign, **stop and ask the user**.
|
|
107
|
+
- Never lower the language mode or strict-concurrency level to make code compile without telling the user.
|
|
108
|
+
- Never block a thread waiting on async work (semaphores, `DispatchGroup.wait`) inside an async context; it can deadlock the cooperative pool.
|
|
109
|
+
- Never capture `self` strongly in a long-lived escaping closure stored on `self` (timers, observers, stored callbacks). Use `[weak self]` there.
|
|
110
|
+
|
|
111
|
+
## Report back
|
|
112
|
+
|
|
113
|
+
End with: the files changed, the build and test commands you ran with their result, any new `@unchecked Sendable`, `nonisolated(unsafe)` or force unwraps you added and why, and anything you left for the user to decide.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Should this cache be a class, a struct or an actor in Swift?",
|
|
4
|
+
"Convert these completion-handler network calls in my Swift package to async/await",
|
|
5
|
+
"Help me design a Swift protocol with an associated type for our repository layer",
|
|
6
|
+
"We want to move our iOS app's core module to the Swift 6 language mode, where do we start?",
|
|
7
|
+
"Set up a new Package.swift with a library target and an executable target"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Write Swift Testing tests with #expect for the PriceCalculator struct",
|
|
11
|
+
"Review this Swift pull request for retain cycles and force unwraps",
|
|
12
|
+
"Xcode build fails with 'sending value of non-Sendable type' after upgrading, fix it",
|
|
13
|
+
"Build a SwiftUI settings screen with a NavigationStack and a Form",
|
|
14
|
+
"Write idiomatic Kotlin coroutines for this Android repository class"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Migrating a module to complete concurrency checking
|
|
2
|
+
|
|
3
|
+
Move one target at a time, leaf modules first. Each step should build cleanly before the next.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
1. **Measure.** Build once with complete checking and count the diagnostics:
|
|
8
|
+
- SwiftPM: `swift build -Xswiftc -strict-concurrency=complete 2>&1 | grep -c "warning:"`
|
|
9
|
+
- Xcode: set `SWIFT_STRICT_CONCURRENCY = complete` for the target, build, read the Issue navigator.
|
|
10
|
+
2. **Turn it on per target in Swift 5 mode** so diagnostics are warnings, not errors:
|
|
11
|
+
- SwiftPM: `swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]` (on 5.9 toolchains use `.enableExperimentalFeature("StrictConcurrency")`).
|
|
12
|
+
- Xcode: `SWIFT_STRICT_CONCURRENCY = complete`.
|
|
13
|
+
3. **Fix in this order**, because each step removes many downstream warnings:
|
|
14
|
+
1. Isolate UI code: add `@MainActor` to view controllers, view models and delegates that touch UIKit/AppKit.
|
|
15
|
+
2. Make plain data types `Sendable` (usually just adding the conformance to structs and enums).
|
|
16
|
+
3. Turn classes that guard state with a queue or lock into actors, or keep the lock and mark them `@unchecked Sendable` with a comment.
|
|
17
|
+
4. Replace global and static `var`s: make them `let`, isolate them to `@MainActor`, or move them into an actor.
|
|
18
|
+
5. Handle third-party modules that are not yet annotated with `@preconcurrency import Module`, and note it for removal.
|
|
19
|
+
4. **Switch the language mode** once the target has no concurrency warnings:
|
|
20
|
+
- SwiftPM: `// swift-tools-version:6.0` (or later) and drop any `.swiftLanguageMode(.v5)` on that target.
|
|
21
|
+
- Xcode: `SWIFT_VERSION = 6.0`.
|
|
22
|
+
5. Run the full test suite. Data-race fixes can change ordering; flaky tests after migration usually point at a real race.
|
|
23
|
+
|
|
24
|
+
## Common diagnostics and the usual fix
|
|
25
|
+
|
|
26
|
+
| Diagnostic (paraphrased) | Usual fix |
|
|
27
|
+
|---|---|
|
|
28
|
+
| Static property is not concurrency-safe because it is nonisolated global shared mutable state | Make it `let` of a `Sendable` type, or isolate it with `@MainActor` |
|
|
29
|
+
| Sending value of non-Sendable type risks causing data races | Make the type `Sendable`, or stop sending it across the boundary (create it inside the task) |
|
|
30
|
+
| Capture of non-Sendable type in a `@Sendable` closure | Capture a `Sendable` copy (a value or an ID) instead of the object |
|
|
31
|
+
| Call to main actor-isolated method in a synchronous nonisolated context | Mark the caller `@MainActor`, or make it `async` and `await` the call |
|
|
32
|
+
| Main actor-isolated property cannot be used to satisfy nonisolated protocol requirement | Isolate the conformance (`extension V: @MainActor P`, Swift 6.2) or make the member `nonisolated` if it touches no isolated state |
|
|
33
|
+
| Passing closure as a 'sending' parameter risks causing data races | Avoid capturing mutable non-Sendable state; copy what the closure needs first |
|
|
34
|
+
|
|
35
|
+
## Swift 6.2 options
|
|
36
|
+
|
|
37
|
+
- `.defaultIsolation(MainActor.self)` in a target's `swiftSettings` (Xcode: `SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor`) makes unannotated code main-actor isolated. Good for app targets; usually wrong for libraries and networking or model packages. Mark background-safe types `nonisolated`.
|
|
38
|
+
- `.enableUpcomingFeature("NonisolatedNonsendingByDefault")` makes `nonisolated async` functions run on the caller's actor. Use `@concurrent` for work that must leave it.
|
|
39
|
+
- `.enableUpcomingFeature("InferIsolatedConformances")` lets a main-actor type's protocol conformance be inferred as main-actor isolated.
|
|
40
|
+
- Xcode's `SWIFT_APPROACHABLE_CONCURRENCY = YES` turns on a bundle of these upcoming features. New Xcode 26 app templates may enable it and main-actor default isolation; SwiftPM packages do not, so the same code can behave differently in an app target and a package. Check each target.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swift-testing
|
|
3
|
+
description: Writes and fixes Swift unit, async and UI tests using Apple's Swift Testing framework (@Test, @Suite, #expect, #require, parameterized arguments, confirmation) alongside XCTest, with protocol-based fakes injected through initializers instead of mocking frameworks, and XCUITest basics for UI flows. Use when adding tests to a Swift package or Xcode project, testing async or actor code, replacing a live network or database dependency with a fake, converting XCTestCase classes to Swift Testing, or fixing a flaky Swift test.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Swift Testing needs a Swift 6 toolchain or Xcode 16+; XCTest works with any Xcode. UI tests need Xcode and a simulator on macOS.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: testing
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "swift"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Swift testing
|
|
15
|
+
|
|
16
|
+
Write Swift tests that are fast, deterministic and run in parallel. Use Swift Testing for new unit and integration tests, XCTest where the project or the feature requires it (UI tests, performance tests, older toolchains).
|
|
17
|
+
|
|
18
|
+
## 0. Before you start
|
|
19
|
+
|
|
20
|
+
1. Find how tests run here: `swift test` for a package, or `xcodebuild test -scheme <Scheme> -destination '<destination>'` for an Xcode project (`xcodebuild -list` shows schemes; `xcrun simctl list devices available` shows simulators). Check CI config for the exact command.
|
|
21
|
+
2. Run the existing suite once and record the result. If it already fails, **stop and tell the user** before changing anything.
|
|
22
|
+
3. Check which framework the target already uses (`import Testing` or `import XCTest`). Both can live in one test target; don't convert existing XCTest files unless asked.
|
|
23
|
+
|
|
24
|
+
## 1. Choose the framework
|
|
25
|
+
|
|
26
|
+
| Test | Use |
|
|
27
|
+
|---|---|
|
|
28
|
+
| New unit or integration test, Swift 6 toolchain | Swift Testing |
|
|
29
|
+
| UI automation (`XCUIApplication`) | XCTest only |
|
|
30
|
+
| Performance (`measure { }`, `XCTMetric`) | XCTest only |
|
|
31
|
+
| Toolchain older than Swift 6 / Xcode 16 | XCTest |
|
|
32
|
+
| Existing XCTest file you are extending | Match the file |
|
|
33
|
+
|
|
34
|
+
## 2. Swift Testing essentials
|
|
35
|
+
|
|
36
|
+
```swift
|
|
37
|
+
import Testing
|
|
38
|
+
@testable import Core
|
|
39
|
+
|
|
40
|
+
@Suite struct PriceCalculatorTests {
|
|
41
|
+
let calculator = PriceCalculator(taxRate: 0.15) // fresh instance per test
|
|
42
|
+
|
|
43
|
+
@Test func addsTax() {
|
|
44
|
+
#expect(calculator.total(for: 100) == 115)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Test(arguments: [0, -1, .max])
|
|
48
|
+
func rejectsInvalidAmounts(_ amount: Int) {
|
|
49
|
+
#expect(throws: PriceError.self) { try calculator.validate(amount) }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Test func parsesFirstLine() throws {
|
|
53
|
+
let line = try #require(calculator.lines.first) // stops the test if nil
|
|
54
|
+
#expect(line.amount > 0)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- `#expect` records a failure and continues; `#require` throws and stops. Use `#require` to unwrap optionals instead of `!`.
|
|
60
|
+
- Each `@Test` gets a new suite instance, so `init` replaces `setUp` and `deinit` (in a class or actor suite) replaces `tearDown`.
|
|
61
|
+
- Prefer `@Test(arguments:)` over loops: each argument is reported and re-runnable on its own. Arguments must be `Sendable`.
|
|
62
|
+
- Tests run in parallel by default. Add `.serialized` to a suite only when tests share an external resource you cannot isolate, and say why.
|
|
63
|
+
- Use traits instead of comments: `.disabled("reason")`, `.enabled(if: ...)`, `.bug("url")`, `.tags(.networking)`, `.timeLimit(.minutes(1))`. For a known bug, wrap the failing check in `withKnownIssue { }` rather than deleting it.
|
|
64
|
+
|
|
65
|
+
For an XCTest-to-Swift-Testing mapping table and more examples, read `references/swift-testing-cheatsheet.md`.
|
|
66
|
+
|
|
67
|
+
## 3. Async and actor code
|
|
68
|
+
|
|
69
|
+
1. Mark the test `async throws` and `await` the call directly. Never sleep to wait for work.
|
|
70
|
+
2. For callbacks or delegate events, use `await confirmation(expectedCount: n) { confirm in ... }` in Swift Testing, or `XCTestExpectation` plus `await fulfillment(of: [exp], timeout: 2)` in async XCTest (not `wait(for:)`, which blocks).
|
|
71
|
+
3. For code isolated to the main actor, mark the test or suite `@MainActor`.
|
|
72
|
+
4. Control time: inject a clock (`any Clock<Duration>`) or a `now: () -> Date` closure instead of reading `Date()` or calling `Task.sleep` directly in production code.
|
|
73
|
+
5. Test cancellation explicitly when the code promises to honour it: start a `Task`, cancel it, assert the `CancellationError` or the cleanup.
|
|
74
|
+
|
|
75
|
+
## 4. Fakes through protocols
|
|
76
|
+
|
|
77
|
+
1. Find the dependency that makes the test slow or non-deterministic: network, database, file system, clock, randomness, keychain.
|
|
78
|
+
2. Define a small protocol for what the code under test needs, if one does not exist.
|
|
79
|
+
3. Inject it through the initializer with the live type as default only if the codebase already does that.
|
|
80
|
+
4. Write a fake in the test target: a struct or actor that returns canned values and records calls.
|
|
81
|
+
|
|
82
|
+
```swift
|
|
83
|
+
protocol UserStore: Sendable { func user(id: String) async throws -> User }
|
|
84
|
+
|
|
85
|
+
actor FakeUserStore: UserStore {
|
|
86
|
+
var result: Result<User, Error>
|
|
87
|
+
private(set) var requestedIDs: [String] = []
|
|
88
|
+
init(_ result: Result<User, Error>) { self.result = result }
|
|
89
|
+
func user(id: String) async throws -> User {
|
|
90
|
+
requestedIDs.append(id)
|
|
91
|
+
return try result.get()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Fake only boundaries. Never fake the type under test, and don't fake value types; construct real ones.
|
|
97
|
+
- For `URLSession`, prefer injecting a protocol over subclassing; a custom `URLProtocol` registered on a `URLSessionConfiguration` is the fallback when you must test real request building.
|
|
98
|
+
|
|
99
|
+
## 5. UI test basics (XCTest)
|
|
100
|
+
|
|
101
|
+
1. Give elements stable `accessibilityIdentifier`s in the app; query by identifier, not by label text.
|
|
102
|
+
2. Launch with test configuration: `app.launchArguments += ["-ui-testing"]` and `app.launchEnvironment["API_BASE_URL"] = ...`, and have the app swap in fakes when it sees them.
|
|
103
|
+
3. Wait with `element.waitForExistence(timeout: 5)`; never `sleep`.
|
|
104
|
+
4. Keep UI tests to a few critical journeys. Logic belongs in unit tests.
|
|
105
|
+
|
|
106
|
+
## 6. Run and check
|
|
107
|
+
|
|
108
|
+
1. Run the single test first: `swift test --filter PriceCalculatorTests/addsTax` or `xcodebuild test -only-testing:<Target>/<Suite>/<test>`.
|
|
109
|
+
2. Then the whole target. Then the full suite.
|
|
110
|
+
3. For a flaky test, run it repeatedly (`swift test --filter <name>` in a loop, or Xcode's run-repeatedly option) and fix the cause (shared state, real time, ordering) rather than adding retries.
|
|
111
|
+
|
|
112
|
+
## Never
|
|
113
|
+
|
|
114
|
+
- Never delete, skip or weaken an existing assertion to get green. If a test is wrong, explain why and get the user's agreement.
|
|
115
|
+
- Never hit the real network, real keychain or a shared simulator state in unit tests.
|
|
116
|
+
- Never use `sleep`, `Task.sleep` or fixed delays to wait for asynchronous results.
|
|
117
|
+
- Never add `.serialized` or disable parallel testing to hide a data race.
|
|
118
|
+
|
|
119
|
+
## Report back
|
|
120
|
+
|
|
121
|
+
End with: tests added or changed (names), the exact command you ran, pass/fail counts, any fakes or protocols you introduced, and anything left untested.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Add Swift Testing tests with #expect for the CartTotals struct",
|
|
4
|
+
"How do I test an actor method that's async in Swift?",
|
|
5
|
+
"Replace the real API client with a fake so my XCTest cases stop hitting the network",
|
|
6
|
+
"Convert our XCTestCase classes to the new @Test macro style",
|
|
7
|
+
"My iOS unit test is flaky because of a Task.sleep, how do I make it deterministic?"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Refactor this Swift networking layer to use async/await and actors",
|
|
11
|
+
"Review my Swift changes for main-thread UI violations",
|
|
12
|
+
"swift test won't even compile, SwiftPM can't resolve the package graph",
|
|
13
|
+
"Write Playwright end-to-end tests for the web checkout flow",
|
|
14
|
+
"Write Kotlin unit tests with JUnit 5 and MockK for the Android repository"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Swift Testing cheatsheet
|
|
2
|
+
|
|
3
|
+
## XCTest to Swift Testing
|
|
4
|
+
|
|
5
|
+
| XCTest | Swift Testing |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `class FooTests: XCTestCase` | `@Suite struct FooTests` (the `@Suite` attribute is optional) |
|
|
8
|
+
| `func testBar()` | `@Test func bar()` |
|
|
9
|
+
| `setUp()` / `tearDown()` | `init()` / `deinit` (class or actor suite) |
|
|
10
|
+
| `XCTAssertEqual(a, b)` | `#expect(a == b)` |
|
|
11
|
+
| `XCTAssertTrue(x)` / `XCTAssertFalse(x)` | `#expect(x)` / `#expect(!x)` |
|
|
12
|
+
| `XCTAssertNil(x)` | `#expect(x == nil)` |
|
|
13
|
+
| `let y = try XCTUnwrap(x)` | `let y = try #require(x)` |
|
|
14
|
+
| `XCTAssertThrowsError(try f())` | `#expect(throws: (any Error).self) { try f() }` |
|
|
15
|
+
| `XCTAssertThrowsError` with a specific error | `#expect(throws: MyError.notFound) { try f() }` (error must be `Equatable`) |
|
|
16
|
+
| `XCTAssertNoThrow(try f())` | `#expect(throws: Never.self) { try f() }` |
|
|
17
|
+
| `XCTFail("msg")` | `Issue.record("msg")` |
|
|
18
|
+
| `XCTSkip` | `.enabled(if:)` / `.disabled(_:)` trait |
|
|
19
|
+
| `XCTExpectFailure` | `withKnownIssue { }` |
|
|
20
|
+
| `XCTestExpectation` + `fulfillment(of:)` | `await confirmation { confirm in ... }` (the event must happen before the closure returns) |
|
|
21
|
+
|
|
22
|
+
Keep these in XCTest: `XCUIApplication` UI tests, `measure { }` performance tests, and anything that needs `XCTestCase` subclass hooks.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
Parameterized over two collections, paired rather than a cartesian product:
|
|
27
|
+
|
|
28
|
+
```swift
|
|
29
|
+
@Test(arguments: zip(["1", "42", "-7"], [1, 42, -7]))
|
|
30
|
+
func parsesIntegers(input: String, expected: Int) throws {
|
|
31
|
+
#expect(try Parser.int(input) == expected)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Counting events:
|
|
36
|
+
|
|
37
|
+
```swift
|
|
38
|
+
@Test func notifiesOncePerSave() async throws {
|
|
39
|
+
let store = DocumentStore()
|
|
40
|
+
try await confirmation(expectedCount: 2) { saved in
|
|
41
|
+
store.onSave = { _ in saved() }
|
|
42
|
+
try await store.save("a")
|
|
43
|
+
try await store.save("b")
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Tags for filtering:
|
|
49
|
+
|
|
50
|
+
```swift
|
|
51
|
+
extension Tag { @Tag static var integration: Self }
|
|
52
|
+
|
|
53
|
+
@Test(.tags(.integration)) func syncsWithServer() async throws { ... }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run a subset: `swift test --filter <regex>` matches suite and test names. Swift Testing and XCTest tests in one package both run under `swift test`.
|
|
57
|
+
|
|
58
|
+
## Main-actor tests
|
|
59
|
+
|
|
60
|
+
```swift
|
|
61
|
+
@MainActor @Suite struct ProfileViewModelTests {
|
|
62
|
+
@Test func loadsProfile() async throws {
|
|
63
|
+
let model = ProfileViewModel(store: FakeUserStore(.success(.fixture)))
|
|
64
|
+
await model.load()
|
|
65
|
+
#expect(model.state == .loaded(.fixture))
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Async XCTest
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
final class LegacyTests: XCTestCase {
|
|
74
|
+
func testDelegateCalled() async {
|
|
75
|
+
let exp = expectation(description: "delegate")
|
|
76
|
+
let sut = Uploader(onFinish: { exp.fulfill() })
|
|
77
|
+
sut.start()
|
|
78
|
+
await fulfillment(of: [exp], timeout: 2)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|