@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,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruby-testing
|
|
3
|
+
description: Writes and stabilises Ruby test suites in whichever framework the project already runs, RSpec or Minitest, with FactoryBot or fixtures, verifying doubles and stubs, frozen or travelled time, WebMock and VCR to keep tests off the network, and a seed-and-bisect routine for order-dependent or flaky specs. Use when adding RSpec examples or Minitest tests to a Ruby or Rails codebase, stubbing an HTTP client or the clock in Ruby, or tracking down a spec that fails only in CI or only with some seeds.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs ruby, bundler and the project's test framework (rspec or minitest); webmock, vcr, factory_bot and timecop only if the project uses them.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: testing
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "ruby, bundle"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Ruby testing with RSpec or Minitest
|
|
15
|
+
|
|
16
|
+
Fast, isolated tests that fail for one clear reason.
|
|
17
|
+
|
|
18
|
+
## 0. Find the setup
|
|
19
|
+
|
|
20
|
+
1. Detect the framework: `spec/` + `.rspec` + `rspec` in `Gemfile.lock` means RSpec; `test/` + `minitest` (or Rails `test_helper.rb`) means Minitest. Use that one. Never add the other framework.
|
|
21
|
+
2. Find the command: `bin/rspec`, `bundle exec rspec`, `bin/rails test`, `bundle exec rake test`, or what CI runs. Always go through Bundler.
|
|
22
|
+
3. Read `spec/spec_helper.rb` / `rails_helper.rb` / `spec/support/**` or `test/test_helper.rb`. Reuse existing helpers, shared contexts, factories and fixtures.
|
|
23
|
+
4. Run the suite (or the relevant directory) once. If it already fails, **stop and tell the user** which tests fail before you change anything.
|
|
24
|
+
5. Note what is installed: `factory_bot`, `webmock`, `vcr`, `timecop`, `database_cleaner`, `shoulda-matchers`, `rspec-rails`. Use them; ask before adding a gem.
|
|
25
|
+
|
|
26
|
+
## 1. Writing a test
|
|
27
|
+
|
|
28
|
+
**RSpec**
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
RSpec.describe Invoice do
|
|
32
|
+
describe "#total" do
|
|
33
|
+
it "rejects a negative quantity" do
|
|
34
|
+
invoice = described_class.new(lines: [{ price: 5, quantity: -1 }])
|
|
35
|
+
expect { invoice.total }.to raise_error(Invoice::InvalidLine, /quantity/)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `describe` the unit or method (`"#instance_method"`, `".class_method"`), `context "when ..."` for a condition, `it` for one behaviour.
|
|
42
|
+
- `let` for lazy values, `let!` only when the record must exist before the example. Don't nest `let` overrides more than two levels deep.
|
|
43
|
+
- Floats: `be_within(0.001).of(0.3)`. Collections: `contain_exactly`, `match_array`, `include`.
|
|
44
|
+
|
|
45
|
+
**Minitest**
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
class InvoiceTest < Minitest::Test # ActiveSupport::TestCase in Rails
|
|
49
|
+
def test_total_rejects_negative_quantity
|
|
50
|
+
invoice = Invoice.new(lines: [{ price: 5, quantity: -1 }])
|
|
51
|
+
error = assert_raises(Invoice::InvalidLine) { invoice.total }
|
|
52
|
+
assert_match(/quantity/, error.message)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- `assert_equal expected, actual` (expected first). `assert_in_delta` for floats.
|
|
58
|
+
- Spec-style Minitest: wrap the value, `_(total).must_equal 5`. minitest 6 removed the expectation methods from `Object`, so a bare `total.must_equal 5` raises `NoMethodError`.
|
|
59
|
+
|
|
60
|
+
Both: test through the public interface, one behaviour per test, arrange-act-assert. Keep the `raise_error` / `assert_raises` block to the single call that should raise.
|
|
61
|
+
|
|
62
|
+
## 2. Test data
|
|
63
|
+
|
|
64
|
+
- **FactoryBot:** factories hold the minimum valid attributes; add `trait`s for variants. Prefer `build` or `build_stubbed` (no DB) over `create`; use `create` only when the test reads back from the database. Use `sequence` for unique fields.
|
|
65
|
+
- Avoid deep association chains in factories; they make every `create` insert many rows.
|
|
66
|
+
- **Rails fixtures** (`test/fixtures/*.yml`): fine for stable reference data; reference them by name (`users(:alice)`). Don't mutate shared fixtures in a way later tests rely on.
|
|
67
|
+
- Each test builds its own data. Never depend on records created by another test.
|
|
68
|
+
|
|
69
|
+
## 3. Doubles and stubs
|
|
70
|
+
|
|
71
|
+
- Stub only boundaries: HTTP clients, payment gateways, mailers, the clock, randomness. Never stub the object under test.
|
|
72
|
+
- **RSpec:** prefer verifying doubles so stubs break when the real API changes: `instance_double(PaymentGateway, charge: result)`, `class_double`, `object_double`. Also set `config.mock_with(:rspec) { |m| m.verify_partial_doubles = true }`.
|
|
73
|
+
- `allow(x).to receive(:y).and_return(z)` to stub; `expect(x).to receive(:y).with(args)` only when the call itself is the behaviour.
|
|
74
|
+
- Spies: `allow(...)` then `expect(x).to have_received(:y)` after acting.
|
|
75
|
+
- Avoid `allow_any_instance_of`; inject the collaborator instead.
|
|
76
|
+
- **Minitest:** `Object#stub` and `Minitest::Mock` come from `require "minitest/mock"`. From minitest 6 they live in the separate `minitest-mock` gem; add it to the Gemfile if the require fails. Call `mock.verify` at the end.
|
|
77
|
+
|
|
78
|
+
## 4. Time, randomness and the network
|
|
79
|
+
|
|
80
|
+
- **Time:** in Rails use `ActiveSupport::Testing::TimeHelpers` (`travel_to Time.zone.local(2025, 1, 31) { ... }`, `freeze_time`); include it in RSpec via `config.include ActiveSupport::Testing::TimeHelpers`. Outside Rails use `Timecop.freeze` with `Timecop.return` in an `after` hook, or inject a clock. Test month ends, leap days and DST boundaries explicitly.
|
|
81
|
+
- **Randomness:** RSpec: `config.order = :random` and `Kernel.srand config.seed` so a seed reproduces everything. Minitest randomises order by default; reuse with `--seed N`.
|
|
82
|
+
- **Network:** `require "webmock/rspec"` (or `webmock/minitest`) and `WebMock.disable_net_connect!(allow_localhost: true)` in the helper. Stub with `stub_request(:post, "https://api.example.com/charges").with(body: hash_including(amount: 500)).to_return(status: 200, body: json)`.
|
|
83
|
+
- **VCR** for recorded responses: `filter_sensitive_data("<API_KEY>") { ENV["API_KEY"] }` so secrets never land in cassettes; use `record: :once` and re-record deliberately. Check every new cassette for tokens and personal data before committing.
|
|
84
|
+
- **Filesystem:** `Dir.mktmpdir` with a block, never the repo tree or `/tmp` paths shared between tests.
|
|
85
|
+
|
|
86
|
+
## 5. Fixing a flaky test
|
|
87
|
+
|
|
88
|
+
1. Reproduce with the CI seed: `bundle exec rspec --seed 12345` or `bin/rails test --seed 12345`.
|
|
89
|
+
2. Order-dependent? RSpec: `bundle exec rspec --bisect --seed 12345` finds the minimal pair. Minitest: the `minitest-bisect` gem, if installed.
|
|
90
|
+
3. Run the single test repeatedly to separate timing flakiness from order: loop it 20 times in the shell.
|
|
91
|
+
4. Common causes and fixes:
|
|
92
|
+
- Leaked state: class-level caches, `ENV`, globals, `Rails.cache`, `Current` attributes. Reset in `after`, or avoid the global.
|
|
93
|
+
- Time: `Date.today` vs `Time.zone.today`, tests near midnight or month end. Freeze time.
|
|
94
|
+
- Ordering: queries without `order` compared with `eq`. Use `contain_exactly` or add `order`.
|
|
95
|
+
- Database: data committed outside the transaction (threads, `after_commit` in system tests). Use the project's cleaning strategy.
|
|
96
|
+
- Async: `sleep` in tests. Wait on a condition (Capybara's waiting matchers) or run jobs inline (`perform_enqueued_jobs`).
|
|
97
|
+
5. Fix the cause. Do not add `retry` gems, sleeps, or skips to hide it.
|
|
98
|
+
|
|
99
|
+
## 6. Speed
|
|
100
|
+
|
|
101
|
+
- Profile: `bundle exec rspec --profile 10`. Look for `create` where `build_stubbed` would do, and `before(:all)` / `let!` creating unused data.
|
|
102
|
+
- Parallelise only with the project's tool (`parallelize` in Rails, `parallel_tests`), after tests are isolated.
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
- Never delete, skip (`skip`, `xit`, `pending`) or loosen an existing test to make the suite pass. If a test is wrong, explain why and get agreement first.
|
|
107
|
+
- No real network, no real clock, no sleeps.
|
|
108
|
+
- Don't assert on private methods or `instance_variable_get`.
|
|
109
|
+
- After three focused attempts without green, stop and report what you tried.
|
|
110
|
+
|
|
111
|
+
## Report back
|
|
112
|
+
|
|
113
|
+
End with: tests added or changed (file and description), the exact command and seed you ran, final pass/fail counts, and anything left untested.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Add RSpec tests for the PaymentGateway wrapper, stubbing the HTTP calls",
|
|
4
|
+
"This Minitest test passes locally but fails in CI with some seeds, help me find why",
|
|
5
|
+
"How do I freeze time in an RSpec spec that checks subscription renewal dates?",
|
|
6
|
+
"Our FactoryBot factories make the suite crawl, can you speed up the specs?",
|
|
7
|
+
"Replace allow_any_instance_of in these specs with something less brittle",
|
|
8
|
+
"Write Minitest tests for this Ruby class and keep them off the network with WebMock"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Refactor this Ruby class to use Enumerable instead of manual loops",
|
|
12
|
+
"Review the changed .rb files on my branch for nil errors before merging",
|
|
13
|
+
"bundle exec rspec says cannot load such file -- csv after upgrading Ruby",
|
|
14
|
+
"Write pytest fixtures for the payment client",
|
|
15
|
+
"Set up Playwright end-to-end tests for the checkout flow",
|
|
16
|
+
"Raise the test coverage of this repo to 80 percent"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: runbook-writing
|
|
3
|
+
description: Operational runbook and playbook authoring for on-call engineers, built around the alert or symptom that sends someone to the page, ordered read-only diagnosis checks with expected output, copy-paste remediation commands that each carry a rollback and a stop condition, escalation paths with contacts and time limits, verification that service has recovered, and a schedule for testing the runbook in drills. Use when writing or fixing a runbook for an alert, a recurring operational task such as failover or certificate rotation, or a service handover to an on-call rotation.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Reads the codebase, alert rules and deploy config; runs only read-only commands to check that documented commands exist.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: docs
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Runbook writing
|
|
14
|
+
|
|
15
|
+
A runbook is read at 3 a.m. by someone tired who may not know the service. Every step must be executable as written, in order, without needing the author.
|
|
16
|
+
|
|
17
|
+
## 0. Scope the runbook
|
|
18
|
+
|
|
19
|
+
1. One runbook per alert or per task. If the user wants "the runbook for service X", propose one short page per alert plus an index.
|
|
20
|
+
2. Collect: the alert rule (query, threshold, duration), service ownership, dashboards, deploy method, config locations, dependencies, and past incidents for this alert. Search the repo for alert definitions and existing runbooks; reuse their location and format.
|
|
21
|
+
3. Find who the reader is (any on-call engineer, a specialist team, support) and what access they have. Do not write steps that need access the reader lacks.
|
|
22
|
+
4. If you cannot find how the service is deployed or restarted, **stop and ask the user**. Do not guess commands.
|
|
23
|
+
|
|
24
|
+
## 1. Header
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
# Runbook: <alert name or task>
|
|
28
|
+
Service: <name> Owner: <team> Last tested: <date, or "never"> Severity: <SEV level the alert pages at>
|
|
29
|
+
Alert: <exact alert name as it appears in the pager> Link: <alert rule>
|
|
30
|
+
Dashboards: <links>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 2. Trigger and symptoms
|
|
34
|
+
|
|
35
|
+
- What fired: the exact condition in plain words ("5xx rate on checkout-api above 2% for 5 minutes").
|
|
36
|
+
- What users see when this is real.
|
|
37
|
+
- Known false positives and how to recognise them in under a minute.
|
|
38
|
+
- Impact if ignored, so the reader can judge urgency.
|
|
39
|
+
|
|
40
|
+
## 3. Diagnosis
|
|
41
|
+
|
|
42
|
+
Ordered checks, most likely and cheapest first. Diagnosis steps must be **read-only**.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
1. Is it a recent deploy?
|
|
46
|
+
Run: <command to list recent deploys>
|
|
47
|
+
Expect: last deploy older than 1 hour.
|
|
48
|
+
If a deploy landed in the last hour: go to Remediation A (roll back).
|
|
49
|
+
2. Is the database reachable?
|
|
50
|
+
Run: <read-only check>
|
|
51
|
+
Expect: <exact healthy output>
|
|
52
|
+
If not: go to Remediation B, or escalate to the database team.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Rules for each check: the exact command or dashboard panel, the expected healthy output, and a branch for each abnormal result. Use placeholders like `<pod-name>` in angle brackets and say how to find the value.
|
|
56
|
+
|
|
57
|
+
## 4. Remediation
|
|
58
|
+
|
|
59
|
+
Each remediation is a separate, labelled procedure:
|
|
60
|
+
|
|
61
|
+
- **When to use it**: which diagnosis result leads here.
|
|
62
|
+
- **Risk**: what it affects (drops connections, loses in-flight jobs, customer-visible).
|
|
63
|
+
- **Steps**: numbered, copy-paste commands, one action each. Destructive or customer-visible steps are marked `CAUTION` and say who must approve them.
|
|
64
|
+
- **Rollback**: how to undo this remediation if it makes things worse.
|
|
65
|
+
- **Stop condition**: when to stop trying and escalate ("if error rate is not falling 10 minutes after step 3").
|
|
66
|
+
|
|
67
|
+
Prefer the safest effective action first: roll back, fail over, scale out, or disable a feature flag before restarting stateful systems, editing data or deleting anything. Never put a data-deleting command in a runbook without a backup or snapshot step immediately before it.
|
|
68
|
+
|
|
69
|
+
## 5. Escalation
|
|
70
|
+
|
|
71
|
+
- Who to page next, how (rotation name, not a person's phone number in plain text), and after how long.
|
|
72
|
+
- Which dependency teams own what, with their rotation names.
|
|
73
|
+
- When to declare an incident and who communicates to customers.
|
|
74
|
+
|
|
75
|
+
## 6. Verification
|
|
76
|
+
|
|
77
|
+
How to confirm recovery: the metric and the value it must return to, for how long, plus one user-level check (a synthetic request, a test login). Say when it is safe to resolve the alert.
|
|
78
|
+
|
|
79
|
+
## 7. Afterwards
|
|
80
|
+
|
|
81
|
+
- Record what was done in the incident ticket.
|
|
82
|
+
- If this was a real incident, link the postmortem (the `incident-postmortem` skill covers writing it).
|
|
83
|
+
- Update the runbook if any step was wrong or missing, and bump "Last tested".
|
|
84
|
+
|
|
85
|
+
## Keeping runbooks tested
|
|
86
|
+
|
|
87
|
+
- Check every command against the current code and config: search for the service names, namespaces, flags and script paths you reference. Run read-only commands (`--help`, list or describe commands) if the user allows; never run remediation commands to "test" them on production.
|
|
88
|
+
- Recommend a drill in staging or a game day each quarter, or after any change to the service's deploy or infra, and record the date in the header.
|
|
89
|
+
- Flag a runbook as stale when "Last tested" is over six months old or references removed resources.
|
|
90
|
+
- Keep runbooks in the repo next to the service or in the team's known runbook location, and link each from its alert's annotation so the pager opens it directly.
|
|
91
|
+
|
|
92
|
+
## Reviewing an existing runbook
|
|
93
|
+
|
|
94
|
+
Report, most serious first: commands that no longer match the code, destructive steps without approval or rollback, diagnosis steps that change state, missing expected outputs, missing escalation or stop conditions, no verification, and a stale or missing "Last tested" date.
|
|
95
|
+
|
|
96
|
+
## Never
|
|
97
|
+
|
|
98
|
+
- Never include secrets, passwords or tokens. Point to where the reader gets them (the secrets manager path).
|
|
99
|
+
- Never write "investigate the issue" or "fix the config" as a step; say exactly what to look at and what to change.
|
|
100
|
+
- Never run remediation steps yourself while writing the runbook.
|
|
101
|
+
- Never reference one engineer's laptop, alias or personal script.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Write a runbook for the HighReplicationLag alert on our Postgres cluster so on-call knows what to check and do",
|
|
4
|
+
"Our on-call playbook for the queue backlog alert just says 'restart things'. Rewrite it with diagnosis, rollback and escalation",
|
|
5
|
+
"Document the step-by-step procedure for rotating the TLS certificates on the ingress, with verification",
|
|
6
|
+
"We're handing the billing service to the SRE rotation. Create runbooks for each of its alerts"
|
|
7
|
+
],
|
|
8
|
+
"should_not_trigger": [
|
|
9
|
+
"Write up the postmortem for last night's replication lag outage with action items",
|
|
10
|
+
"Set up a GitHub Actions workflow that deploys the ingress config on merge",
|
|
11
|
+
"Design a blue-green deployment so we can roll back the billing service instantly",
|
|
12
|
+
"Write a how-to guide for developers on running the billing service locally"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust-patterns
|
|
3
|
+
description: Idiomatic Rust design guide covering ownership and borrowing choices, Result-based errors with thiserror in libraries and anyhow in binaries, traits and generics, iterator chains, async with tokio, rules for unsafe, cargo workspaces and clippy settings. Use when writing new Rust code, shaping a crate's public API, choosing between owned and borrowed types, or setting up a Cargo workspace and lint policy.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a Rust toolchain (cargo, rustc, clippy, rustfmt) to check the code it writes.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "cargo"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Rust patterns
|
|
15
|
+
|
|
16
|
+
Write Rust that compiles on the first honest try, says what it owns, and fails with typed errors instead of panics.
|
|
17
|
+
|
|
18
|
+
## 0. Before you write
|
|
19
|
+
|
|
20
|
+
1. Read `Cargo.toml` (and the workspace root's, if any): edition, `rust-version`, features, existing error crate, async runtime. Match them; never add a second error crate or runtime.
|
|
21
|
+
2. Read `clippy.toml`, `rustfmt.toml` and any `[lints]` table. They are the house rules.
|
|
22
|
+
3. Find a neighbouring module that does something similar and copy its shape (error type, constructor style, module layout).
|
|
23
|
+
4. If the task needs a new dependency, **stop and ask the user** before adding it, and name the crate and why.
|
|
24
|
+
|
|
25
|
+
## 1. Ownership and borrowing
|
|
26
|
+
|
|
27
|
+
Decide per parameter, in this order:
|
|
28
|
+
|
|
29
|
+
| Need | Take |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Only read it | `&T`, `&str`, `&[T]`, `&Path` (not `&String`, `&Vec<T>`, `&PathBuf`) |
|
|
32
|
+
| Mutate the caller's value | `&mut T` |
|
|
33
|
+
| Store it or move it to another thread | `T` by value (`String`, `Vec<T>`) |
|
|
34
|
+
| Accept owned or borrowed, store it | `impl Into<String>` / `impl Into<PathBuf>` |
|
|
35
|
+
| Return borrowed-or-owned | `Cow<'_, str>` |
|
|
36
|
+
|
|
37
|
+
- Return owned values from constructors and builders; return references only from accessors (`fn name(&self) -> &str`).
|
|
38
|
+
- When the borrow checker objects, restructure first: shorten the borrow with a block, split the struct into fields borrowed separately, or collect indices before mutating. Use `.clone()` only when the data is small or the copy is the real intent, never just to silence an error.
|
|
39
|
+
- Shared ownership: `Rc<T>` single-threaded, `Arc<T>` across threads. Interior mutability: `Cell`/`RefCell` single-threaded, `Mutex`/`RwLock` or atomics across threads. Reaching for `Rc<RefCell<T>>` everywhere usually means the ownership tree needs redesign; say so.
|
|
40
|
+
- Add explicit lifetimes only when elision can't express the relationship. A struct holding references (`struct Parser<'a>`) is fine for short-lived views; long-lived structs should own their data.
|
|
41
|
+
|
|
42
|
+
## 2. Errors
|
|
43
|
+
|
|
44
|
+
- Library crates: one error enum per crate or module with `thiserror`. Wrap sources with `#[from]` or `#[source]` so the chain survives.
|
|
45
|
+
- Binaries and app glue: `anyhow::Result<T>`, adding context at each boundary with `.context("reading config")` / `.with_context(|| format!("opening {path:?}"))`.
|
|
46
|
+
- Propagate with `?`. Convert at module boundaries, not at every call.
|
|
47
|
+
- `unwrap()` and `expect()` belong in tests, examples and provably-impossible cases. For the last, use `expect("reason this cannot fail")`, never bare `unwrap()`.
|
|
48
|
+
- Never return `Result<T, String>` or `Box<dyn Error>` from a public library API; callers can't match on them.
|
|
49
|
+
- `panic!` is for broken invariants (bugs), not for bad input, missing files or network failures.
|
|
50
|
+
|
|
51
|
+
Patterns and full examples: `references/errors-and-traits.md`.
|
|
52
|
+
|
|
53
|
+
## 3. Traits and generics
|
|
54
|
+
|
|
55
|
+
- Prefer generics (`fn f<R: Read>(r: R)` or `impl Read`) for hot paths and when the caller picks one type. Use `dyn Trait` for heterogeneous collections, plugin points and to cut compile time or binary size.
|
|
56
|
+
- Keep trait bounds on `impl` blocks and functions, not on struct definitions.
|
|
57
|
+
- Implement standard traits instead of ad hoc methods: `From`/`TryFrom` for conversions, `Display` for user-facing text, `Default`, `FromStr`, `Iterator`, `AsRef`.
|
|
58
|
+
- Derive `Debug` on every public type. Derive `Clone`, `PartialEq`, `Eq`, `Hash` only when they make sense for the type.
|
|
59
|
+
- Newtypes (`struct UserId(u64)`) for values that must not be mixed up. Enums instead of boolean flags or stringly-typed states.
|
|
60
|
+
- Don't implement `Deref` to fake inheritance.
|
|
61
|
+
|
|
62
|
+
## 4. Iterators and collections
|
|
63
|
+
|
|
64
|
+
- Chain `iter()`, `map`, `filter`, `filter_map`, `flat_map`, then `collect()`; collect into `Result<Vec<_>, _>` to stop at the first error.
|
|
65
|
+
- Use a plain `for` loop when the body has side effects, early `return`, or `?` that would make the chain unreadable.
|
|
66
|
+
- Pre-size with `Vec::with_capacity` when the length is known. Use the `entry` API on maps instead of a lookup followed by an insert.
|
|
67
|
+
- Don't `collect()` just to iterate again; pass the iterator on (`impl Iterator<Item = T>`).
|
|
68
|
+
|
|
69
|
+
## 5. Async with tokio
|
|
70
|
+
|
|
71
|
+
1. One runtime, created once with `#[tokio::main]` in the binary. Libraries take no runtime dependency they can avoid and never create their own runtime.
|
|
72
|
+
2. Never block inside async code: no `std::thread::sleep`, `std::fs` on hot paths, blocking HTTP clients, or long CPU loops. Use `tokio::time::sleep`, `tokio::fs`, or move the work into `tokio::task::spawn_blocking`.
|
|
73
|
+
3. Never hold a `std::sync::Mutex` guard across `.await`. Drop it before awaiting, or use `tokio::sync::Mutex` when the lock must span an await.
|
|
74
|
+
4. Every `tokio::spawn` gets an owner: keep the `JoinHandle` (or use a `JoinSet`) and handle its result, or the task's panic and error vanish.
|
|
75
|
+
5. Bound concurrency and channels: `tokio::sync::Semaphore`, bounded `mpsc::channel(n)`. Unbounded channels need a written reason.
|
|
76
|
+
6. Add timeouts to network I/O with `tokio::time::timeout`, and support shutdown with a cancellation signal checked in `tokio::select!`.
|
|
77
|
+
7. Spawned futures must be `Send + 'static`: move owned data (or an `Arc`) into them, don't borrow.
|
|
78
|
+
|
|
79
|
+
## 6. Unsafe
|
|
80
|
+
|
|
81
|
+
- Don't write `unsafe` unless the task cannot be done in safe Rust (FFI, a measured hot path, a primitive the standard library lacks). If you think you need it, **stop and tell the user why** before writing it.
|
|
82
|
+
- Every `unsafe` block gets a `// SAFETY:` comment naming the invariant that makes it sound, and every `unsafe fn` gets a `# Safety` doc section stating what callers must uphold.
|
|
83
|
+
- Keep the block as small as possible and wrap it in a safe API that enforces the invariant.
|
|
84
|
+
- Run the tests under Miri (`cargo +nightly miri test`) when unsafe code touches pointers or memory layout, if Miri is available.
|
|
85
|
+
|
|
86
|
+
## 7. Cargo workspaces
|
|
87
|
+
|
|
88
|
+
- Root `Cargo.toml` with `[workspace]`, `members`, and shared versions in `[workspace.dependencies]`; members use `serde = { workspace = true }`.
|
|
89
|
+
- Put shared lint settings in `[workspace.lints]` and `[lints] workspace = true` in each member.
|
|
90
|
+
- Features are additive: a feature must never remove an API or change behaviour incompatibly. Gate optional dependencies with `dep:name`.
|
|
91
|
+
- Commit `Cargo.lock`. Cargo's current guidance is to commit it for libraries too; follow the project if it has a documented reason not to.
|
|
92
|
+
- Layout and a starter manifest: `references/cargo-and-clippy.md`.
|
|
93
|
+
|
|
94
|
+
## 8. Finish: format, lint, test
|
|
95
|
+
|
|
96
|
+
Run, and fix everything before you report:
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
cargo fmt --all
|
|
100
|
+
cargo clippy --all-targets --all-features -- -D warnings
|
|
101
|
+
cargo test --all-features
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If the project has mutually exclusive features, `--all-features` fails by design: use the feature flags CI uses instead.
|
|
105
|
+
|
|
106
|
+
- Fix clippy warnings in the code. Suppress only on the smallest item, with a reason: `#[expect(clippy::lint_name, reason = "...")]` when the project's `rust-version` is 1.81 or newer (it warns once the lint stops firing), otherwise `#[allow(clippy::lint_name)]` plus a comment.
|
|
107
|
+
- If a check fails for reasons outside your change, report it instead of fixing unrelated code.
|
|
108
|
+
|
|
109
|
+
## Never
|
|
110
|
+
|
|
111
|
+
- Never add `unwrap()` on user input, I/O, parsing or network results in non-test code.
|
|
112
|
+
- Never silence the borrow checker with `clone()`, `Rc<RefCell<_>>` or `unsafe` without saying why.
|
|
113
|
+
- Never use `std::mem::transmute` to convert between types that have safe conversions.
|
|
114
|
+
- Never add a dependency, a feature flag or `#![allow(...)]` at crate level without asking.
|
|
115
|
+
|
|
116
|
+
## Report back
|
|
117
|
+
|
|
118
|
+
End with: files changed, the three commands above and their results, any `unsafe`, `allow` or new dependency introduced and why, and anything left undone.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Should this Rust function take a String or a &str? It just looks up a key in a map",
|
|
4
|
+
"Set up a thiserror error enum for my parser crate and use anyhow in the CLI binary",
|
|
5
|
+
"Split my Rust project into a cargo workspace with a core library and a cli crate",
|
|
6
|
+
"Write an async tokio worker that pulls jobs from a channel and processes them with bounded concurrency",
|
|
7
|
+
"What's the idiomatic way to design a trait for pluggable storage backends in Rust?"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Add proptest property tests for my Rust base64 encoder",
|
|
11
|
+
"Review this Rust pull request for unwraps and unsafe blocks before I merge it",
|
|
12
|
+
"cargo build fails with E0502 cannot borrow as mutable because it is also borrowed as immutable",
|
|
13
|
+
"Write idiomatic Go error handling with wrapped errors for this HTTP handler",
|
|
14
|
+
"Design a REST API for the orders service with pagination and versioning"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Cargo workspaces and clippy policy
|
|
2
|
+
|
|
3
|
+
Read this when creating or restructuring a workspace, or setting lint policy.
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
my-project/
|
|
9
|
+
├── Cargo.toml # workspace root, no [package] unless it is also a crate
|
|
10
|
+
├── Cargo.lock
|
|
11
|
+
├── crates/
|
|
12
|
+
│ ├── app-core/ # domain types and logic, no I/O
|
|
13
|
+
│ ├── store/ # persistence, depends on app-core
|
|
14
|
+
│ └── cli/ # binary, depends on app-core and store
|
|
15
|
+
└── clippy.toml # optional
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Keep the dependency graph acyclic and pointing inwards: binaries depend on libraries, never the reverse.
|
|
19
|
+
|
|
20
|
+
## Root manifest
|
|
21
|
+
|
|
22
|
+
```toml
|
|
23
|
+
[workspace]
|
|
24
|
+
resolver = "3" # edition 2024 default; use "2" on edition 2021
|
|
25
|
+
members = ["crates/*"]
|
|
26
|
+
|
|
27
|
+
[workspace.package]
|
|
28
|
+
edition = "2024"
|
|
29
|
+
license = "MIT"
|
|
30
|
+
|
|
31
|
+
[workspace.dependencies]
|
|
32
|
+
anyhow = "1"
|
|
33
|
+
thiserror = "2"
|
|
34
|
+
serde = { version = "1", features = ["derive"] }
|
|
35
|
+
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
|
36
|
+
|
|
37
|
+
[workspace.lints.rust]
|
|
38
|
+
unsafe_code = "forbid"
|
|
39
|
+
|
|
40
|
+
[workspace.lints.clippy]
|
|
41
|
+
unwrap_used = "warn"
|
|
42
|
+
expect_used = "warn"
|
|
43
|
+
dbg_macro = "warn"
|
|
44
|
+
todo = "warn"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Match the edition, resolver and dependency major versions already used by the project rather than copying the numbers above; they are placeholders.
|
|
48
|
+
|
|
49
|
+
`unsafe_code = "forbid"` is a sensible default for crates with no FFI. Relax it per crate (`deny`, then `#[allow(unsafe_code)]` on the one module) only where unsafe is justified.
|
|
50
|
+
|
|
51
|
+
## Member manifest
|
|
52
|
+
|
|
53
|
+
```toml
|
|
54
|
+
[package]
|
|
55
|
+
name = "store"
|
|
56
|
+
version = "0.1.0"
|
|
57
|
+
edition.workspace = true
|
|
58
|
+
license.workspace = true
|
|
59
|
+
|
|
60
|
+
[dependencies]
|
|
61
|
+
app-core = { path = "../app-core" } # don't name a crate `core` or `std`: it shadows the built-in crate
|
|
62
|
+
serde = { workspace = true }
|
|
63
|
+
thiserror = { workspace = true }
|
|
64
|
+
sqlx = { version = "0.8", optional = true }
|
|
65
|
+
|
|
66
|
+
[features]
|
|
67
|
+
default = []
|
|
68
|
+
postgres = ["dep:sqlx"]
|
|
69
|
+
|
|
70
|
+
[lints]
|
|
71
|
+
workspace = true
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Useful cargo commands
|
|
75
|
+
|
|
76
|
+
| Task | Command |
|
|
77
|
+
|---|---|
|
|
78
|
+
| Check fast without codegen | `cargo check --workspace --all-targets` |
|
|
79
|
+
| Build one member | `cargo build -p store` |
|
|
80
|
+
| Why is this crate here? | `cargo tree -i <crate>` |
|
|
81
|
+
| Duplicate versions in the graph | `cargo tree -d` |
|
|
82
|
+
| Features enabled on a crate | `cargo tree -e features -i <crate>` |
|
|
83
|
+
| Update one dependency | `cargo update -p <crate>` |
|
|
84
|
+
| Check every feature combination | `cargo hack check --feature-powerset` (needs `cargo-hack`) |
|
|
85
|
+
|
|
86
|
+
## Clippy
|
|
87
|
+
|
|
88
|
+
Baseline for CI:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- `clippy::all` (the default groups) is on already. `clippy::pedantic` is opt-in and noisy; enable it only if the project already does, or the user asks.
|
|
95
|
+
- `unwrap_used` and `expect_used` are restriction lints. Allow them in tests with `#[cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]` at the crate root, or in `clippy.toml` with `allow-unwrap-in-tests = true` and `allow-expect-in-tests = true`.
|
|
96
|
+
- Suppress a lint on the smallest item, with a reason:
|
|
97
|
+
|
|
98
|
+
```rust
|
|
99
|
+
#[expect(clippy::too_many_arguments, reason = "mirrors the C API one-to-one")] // Rust 1.81+
|
|
100
|
+
fn ffi_call(/* ... */) {}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
On an older `rust-version`, use `#[allow(...)]` with a trailing comment instead.
|
|
104
|
+
|
|
105
|
+
- Don't use `cargo clippy --fix` on a dirty tree without reviewing the diff it produces.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Errors and traits: worked patterns
|
|
2
|
+
|
|
3
|
+
Read this when designing a crate's error type or a public trait.
|
|
4
|
+
|
|
5
|
+
## Library error with thiserror
|
|
6
|
+
|
|
7
|
+
```rust
|
|
8
|
+
use std::path::PathBuf;
|
|
9
|
+
|
|
10
|
+
#[derive(Debug, thiserror::Error)]
|
|
11
|
+
pub enum ConfigError {
|
|
12
|
+
#[error("could not read config file {path:?}")]
|
|
13
|
+
Read {
|
|
14
|
+
path: PathBuf,
|
|
15
|
+
#[source]
|
|
16
|
+
source: std::io::Error,
|
|
17
|
+
},
|
|
18
|
+
#[error("invalid config")]
|
|
19
|
+
Parse(#[from] toml::de::Error),
|
|
20
|
+
#[error("missing required key `{0}`")]
|
|
21
|
+
MissingKey(&'static str),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn load(path: &std::path::Path) -> Result<Config, ConfigError> {
|
|
25
|
+
let text = std::fs::read_to_string(path).map_err(|source| ConfigError::Read {
|
|
26
|
+
path: path.to_owned(),
|
|
27
|
+
source,
|
|
28
|
+
})?;
|
|
29
|
+
let config: Config = toml::from_str(&text)?; // From<toml::de::Error>
|
|
30
|
+
Ok(config)
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Rules:
|
|
35
|
+
|
|
36
|
+
- The `#[error]` message describes this layer only. Don't repeat the source's message in it; reporters walk `source()` and print the chain.
|
|
37
|
+
- Use `#[from]` only when the conversion is unambiguous. If two call sites produce the same source type with different meaning, use explicit variants with `#[source]` and `map_err`.
|
|
38
|
+
- Mark public error enums `#[non_exhaustive]` if you expect to add variants without a major version bump.
|
|
39
|
+
|
|
40
|
+
## Application errors with anyhow
|
|
41
|
+
|
|
42
|
+
```rust
|
|
43
|
+
use anyhow::{bail, Context, Result};
|
|
44
|
+
|
|
45
|
+
fn main() -> Result<()> {
|
|
46
|
+
let path = std::env::args().nth(1).context("usage: tool <config>")?;
|
|
47
|
+
let config = mylib::load(path.as_ref())
|
|
48
|
+
.with_context(|| format!("loading config from {path}"))?;
|
|
49
|
+
if config.workers == 0 {
|
|
50
|
+
bail!("workers must be at least 1");
|
|
51
|
+
}
|
|
52
|
+
Ok(())
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- Returning `anyhow::Result<()>` from `main` prints the error chain on failure and exits non-zero.
|
|
57
|
+
- Use `anyhow::Error::downcast_ref::<T>()` only at the top level, when you must branch on a specific cause.
|
|
58
|
+
|
|
59
|
+
## Option and Result helpers
|
|
60
|
+
|
|
61
|
+
| Want | Use |
|
|
62
|
+
|---|---|
|
|
63
|
+
| Option to Result | `opt.ok_or(Error::Missing)?` / `opt.ok_or_else(\|\| ...)?` |
|
|
64
|
+
| Default on None | `opt.unwrap_or_default()` / `unwrap_or_else(\|\| ...)` |
|
|
65
|
+
| Transform the success value | `res.map(...)` |
|
|
66
|
+
| Chain fallible steps | `res.and_then(...)` |
|
|
67
|
+
| Log and drop an error deliberately | `if let Err(e) = res { tracing::warn!(error = %e, "..."); }` |
|
|
68
|
+
|
|
69
|
+
Never use `let _ = fallible();` for a `Result` whose failure matters; handle or log it.
|
|
70
|
+
|
|
71
|
+
## Trait design
|
|
72
|
+
|
|
73
|
+
```rust
|
|
74
|
+
pub trait Store {
|
|
75
|
+
fn get(&self, key: &str) -> Result<Option<Vec<u8>>, StoreError>;
|
|
76
|
+
fn put(&self, key: &str, value: &[u8]) -> Result<(), StoreError>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
pub struct Cache<S: Store> { inner: S } // avoid: bound on the struct
|
|
80
|
+
pub struct Cache<S> { inner: S } // prefer
|
|
81
|
+
impl<S: Store> Cache<S> { /* ... */ } // bound where it is needed
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Take `&self` in trait methods unless mutation is the point; implementations can use interior mutability.
|
|
85
|
+
- Keep traits small and focused. A trait with ten methods usually wants splitting.
|
|
86
|
+
- To make a trait usable as `dyn Trait`, avoid generic methods and `Self` return types (or gate them with `where Self: Sized`).
|
|
87
|
+
- Sealed traits (a private supertrait) stop downstream crates from implementing a trait you need to extend later.
|
|
88
|
+
|
|
89
|
+
## Async traits
|
|
90
|
+
|
|
91
|
+
`async fn` in traits works on stable Rust for static dispatch. For `dyn` use, or when callers need the returned future to be `Send`, check the project's current approach (the `async-trait` crate, or a method returning `Pin<Box<dyn Future<Output = T> + Send + '_>>`) and follow it rather than mixing styles.
|
|
92
|
+
|
|
93
|
+
## Conversions
|
|
94
|
+
|
|
95
|
+
- `impl From<A> for B` gives you `Into` for free. Never implement `Into` directly.
|
|
96
|
+
- `TryFrom` for conversions that can fail; its `Error` type should be a real error, not `()` or `String`.
|
|
97
|
+
- `AsRef<str>` / `AsRef<Path>` for functions that only need to look at the data.
|