@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.
Files changed (656) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +516 -2
  3. package/agents/accessibility-reviewer/AGENT.md +70 -0
  4. package/agents/accessibility-reviewer/evals/triggers.json +15 -0
  5. package/agents/agent-evaluator/AGENT.md +74 -0
  6. package/agents/agent-evaluator/evals/triggers.json +15 -0
  7. package/agents/architect/AGENT.md +82 -0
  8. package/agents/architect/evals/triggers.json +17 -0
  9. package/agents/billing-reviewer/AGENT.md +68 -0
  10. package/agents/billing-reviewer/evals/triggers.json +16 -0
  11. package/agents/code-reviewer/AGENT.md +51 -0
  12. package/agents/code-reviewer/evals/triggers.json +14 -0
  13. package/agents/code-simplifier/AGENT.md +74 -0
  14. package/agents/code-simplifier/evals/triggers.json +15 -0
  15. package/agents/cpp-build-resolver/AGENT.md +73 -0
  16. package/agents/cpp-build-resolver/evals/triggers.json +16 -0
  17. package/agents/cpp-reviewer/AGENT.md +59 -0
  18. package/agents/cpp-reviewer/evals/triggers.json +16 -0
  19. package/agents/csharp-build-resolver/AGENT.md +84 -0
  20. package/agents/csharp-build-resolver/evals/triggers.json +17 -0
  21. package/agents/csharp-reviewer/AGENT.md +88 -0
  22. package/agents/csharp-reviewer/evals/triggers.json +17 -0
  23. package/agents/dart-build-resolver/AGENT.md +80 -0
  24. package/agents/dart-build-resolver/evals/triggers.json +17 -0
  25. package/agents/dart-reviewer/AGENT.md +66 -0
  26. package/agents/dart-reviewer/evals/triggers.json +17 -0
  27. package/agents/database-reviewer/AGENT.md +72 -0
  28. package/agents/database-reviewer/evals/triggers.json +16 -0
  29. package/agents/django-reviewer/AGENT.md +61 -0
  30. package/agents/django-reviewer/evals/triggers.json +15 -0
  31. package/agents/doc-updater/AGENT.md +92 -0
  32. package/agents/doc-updater/evals/triggers.json +14 -0
  33. package/agents/e2e-runner/AGENT.md +88 -0
  34. package/agents/e2e-runner/evals/triggers.json +16 -0
  35. package/agents/fastapi-reviewer/AGENT.md +63 -0
  36. package/agents/fastapi-reviewer/evals/triggers.json +15 -0
  37. package/agents/flutter-reviewer/AGENT.md +70 -0
  38. package/agents/flutter-reviewer/evals/triggers.json +15 -0
  39. package/agents/fsharp-build-resolver/AGENT.md +81 -0
  40. package/agents/fsharp-build-resolver/evals/triggers.json +15 -0
  41. package/agents/fsharp-reviewer/AGENT.md +77 -0
  42. package/agents/fsharp-reviewer/evals/triggers.json +15 -0
  43. package/agents/go-build-resolver/AGENT.md +85 -0
  44. package/agents/go-build-resolver/evals/triggers.json +16 -0
  45. package/agents/go-reviewer/AGENT.md +66 -0
  46. package/agents/go-reviewer/evals/triggers.json +16 -0
  47. package/agents/healthcare-reviewer/AGENT.md +65 -0
  48. package/agents/healthcare-reviewer/evals/triggers.json +16 -0
  49. package/agents/i18n-reviewer/AGENT.md +64 -0
  50. package/agents/i18n-reviewer/evals/triggers.json +16 -0
  51. package/agents/infra-reviewer/AGENT.md +93 -0
  52. package/agents/infra-reviewer/evals/triggers.json +15 -0
  53. package/agents/java-build-resolver/AGENT.md +82 -0
  54. package/agents/java-build-resolver/evals/triggers.json +16 -0
  55. package/agents/java-reviewer/AGENT.md +64 -0
  56. package/agents/java-reviewer/evals/triggers.json +16 -0
  57. package/agents/kotlin-build-resolver/AGENT.md +89 -0
  58. package/agents/kotlin-build-resolver/evals/triggers.json +16 -0
  59. package/agents/kotlin-reviewer/AGENT.md +75 -0
  60. package/agents/kotlin-reviewer/evals/triggers.json +16 -0
  61. package/agents/mle-reviewer/AGENT.md +68 -0
  62. package/agents/mle-reviewer/evals/triggers.json +16 -0
  63. package/agents/network-config-reviewer/AGENT.md +68 -0
  64. package/agents/network-config-reviewer/evals/triggers.json +17 -0
  65. package/agents/payments-reviewer/AGENT.md +64 -0
  66. package/agents/payments-reviewer/evals/triggers.json +16 -0
  67. package/agents/performance-optimizer/AGENT.md +86 -0
  68. package/agents/performance-optimizer/evals/triggers.json +16 -0
  69. package/agents/perl-build-resolver/AGENT.md +84 -0
  70. package/agents/perl-build-resolver/evals/triggers.json +15 -0
  71. package/agents/perl-reviewer/AGENT.md +77 -0
  72. package/agents/perl-reviewer/evals/triggers.json +15 -0
  73. package/agents/php-build-resolver/AGENT.md +88 -0
  74. package/agents/php-build-resolver/evals/triggers.json +16 -0
  75. package/agents/php-reviewer/AGENT.md +61 -0
  76. package/agents/php-reviewer/evals/triggers.json +15 -0
  77. package/agents/planner/AGENT.md +75 -0
  78. package/agents/planner/evals/triggers.json +17 -0
  79. package/agents/pr-test-analyzer/AGENT.md +82 -0
  80. package/agents/pr-test-analyzer/evals/triggers.json +16 -0
  81. package/agents/python-build-resolver/AGENT.md +87 -0
  82. package/agents/python-build-resolver/evals/triggers.json +16 -0
  83. package/agents/python-reviewer/AGENT.md +63 -0
  84. package/agents/python-reviewer/evals/triggers.json +15 -0
  85. package/agents/rag-reviewer/AGENT.md +71 -0
  86. package/agents/rag-reviewer/evals/triggers.json +16 -0
  87. package/agents/react-reviewer/AGENT.md +70 -0
  88. package/agents/react-reviewer/evals/triggers.json +16 -0
  89. package/agents/refactor-cleaner/AGENT.md +82 -0
  90. package/agents/refactor-cleaner/evals/triggers.json +16 -0
  91. package/agents/ruby-build-resolver/AGENT.md +93 -0
  92. package/agents/ruby-build-resolver/evals/triggers.json +16 -0
  93. package/agents/ruby-reviewer/AGENT.md +62 -0
  94. package/agents/ruby-reviewer/evals/triggers.json +16 -0
  95. package/agents/rust-build-resolver/AGENT.md +98 -0
  96. package/agents/rust-build-resolver/evals/triggers.json +16 -0
  97. package/agents/rust-reviewer/AGENT.md +78 -0
  98. package/agents/rust-reviewer/evals/triggers.json +15 -0
  99. package/agents/security-reviewer/AGENT.md +84 -0
  100. package/agents/security-reviewer/evals/triggers.json +16 -0
  101. package/agents/silent-failure-hunter/AGENT.md +66 -0
  102. package/agents/silent-failure-hunter/evals/triggers.json +16 -0
  103. package/agents/solidity-reviewer/AGENT.md +70 -0
  104. package/agents/solidity-reviewer/evals/triggers.json +16 -0
  105. package/agents/swift-build-resolver/AGENT.md +85 -0
  106. package/agents/swift-build-resolver/evals/triggers.json +15 -0
  107. package/agents/swift-reviewer/AGENT.md +75 -0
  108. package/agents/swift-reviewer/evals/triggers.json +15 -0
  109. package/agents/typescript-build-resolver/AGENT.md +76 -0
  110. package/agents/typescript-build-resolver/evals/triggers.json +15 -0
  111. package/agents/typescript-reviewer/AGENT.md +76 -0
  112. package/agents/typescript-reviewer/evals/triggers.json +15 -0
  113. package/agents/vue-reviewer/AGENT.md +75 -0
  114. package/agents/vue-reviewer/evals/triggers.json +15 -0
  115. package/bin/kete-loom.js +683 -0
  116. package/commands/aside.md +19 -0
  117. package/commands/build-fix.md +26 -0
  118. package/commands/campaign.md +19 -0
  119. package/commands/changelog.md +36 -0
  120. package/commands/checkpoint.md +26 -0
  121. package/commands/codemap.md +25 -0
  122. package/commands/commit.md +26 -0
  123. package/commands/deps-audit.md +32 -0
  124. package/commands/epic-decompose.md +21 -0
  125. package/commands/explain.md +37 -0
  126. package/commands/feature-dev.md +22 -0
  127. package/commands/hookify.md +23 -0
  128. package/commands/i18n-check.md +39 -0
  129. package/commands/issue-sync.md +22 -0
  130. package/commands/jira.md +19 -0
  131. package/commands/learn.md +23 -0
  132. package/commands/loom.md +21 -0
  133. package/commands/loop.md +20 -0
  134. package/commands/model-route.md +27 -0
  135. package/commands/onboard.md +35 -0
  136. package/commands/orchestrate.md +21 -0
  137. package/commands/plan-feature.md +36 -0
  138. package/commands/postmortem.md +42 -0
  139. package/commands/pr-describe.md +39 -0
  140. package/commands/pr.md +22 -0
  141. package/commands/prd.md +41 -0
  142. package/commands/project-init.md +21 -0
  143. package/commands/refactor-clean.md +24 -0
  144. package/commands/resume-session.md +22 -0
  145. package/commands/review.md +35 -0
  146. package/commands/save-session.md +37 -0
  147. package/commands/security-scan.md +32 -0
  148. package/commands/sessions.md +21 -0
  149. package/commands/skill-create.md +21 -0
  150. package/commands/tdd.md +36 -0
  151. package/commands/test-coverage.md +24 -0
  152. package/commands/threat-model.md +36 -0
  153. package/commands/update-docs.md +25 -0
  154. package/commands/verify.md +21 -0
  155. package/hooks/block-dangerous-commands/HOOK.md +30 -0
  156. package/hooks/block-dangerous-commands/cases.json +50 -0
  157. package/hooks/block-dangerous-commands/hook.js +75 -0
  158. package/hooks/block-no-verify/HOOK.md +25 -0
  159. package/hooks/block-no-verify/cases.json +30 -0
  160. package/hooks/block-no-verify/hook.js +43 -0
  161. package/hooks/format-on-edit/HOOK.md +24 -0
  162. package/hooks/format-on-edit/hook.js +89 -0
  163. package/hooks/guard-secrets/HOOK.md +19 -0
  164. package/hooks/guard-secrets/cases.json +110 -0
  165. package/hooks/guard-secrets/hook.js +44 -0
  166. package/hooks/load-session-context/HOOK.md +20 -0
  167. package/hooks/load-session-context/hook.js +56 -0
  168. package/hooks/protect-files/HOOK.md +24 -0
  169. package/hooks/protect-files/cases.json +27 -0
  170. package/hooks/protect-files/hook.js +39 -0
  171. package/hooks/run.js +132 -0
  172. package/mcp/atlassian.json +11 -0
  173. package/mcp/chrome-devtools.json +18 -0
  174. package/mcp/cloudflare.json +11 -0
  175. package/mcp/context7.json +11 -0
  176. package/mcp/fetch.json +17 -0
  177. package/mcp/figma.json +11 -0
  178. package/mcp/git.json +17 -0
  179. package/mcp/github.json +11 -0
  180. package/mcp/linear.json +11 -0
  181. package/mcp/memory.json +18 -0
  182. package/mcp/notion.json +11 -0
  183. package/mcp/playwright.json +18 -0
  184. package/mcp/sentry.json +11 -0
  185. package/mcp/stripe.json +11 -0
  186. package/mcp/supabase.json +11 -0
  187. package/mcp/time.json +17 -0
  188. package/package.json +59 -4
  189. package/profiles.json +165 -0
  190. package/rules/common.md +21 -0
  191. package/rules/cpp.md +29 -0
  192. package/rules/csharp.md +25 -0
  193. package/rules/dart.md +29 -0
  194. package/rules/fsharp.md +28 -0
  195. package/rules/git.md +26 -0
  196. package/rules/go.md +29 -0
  197. package/rules/java.md +29 -0
  198. package/rules/kotlin.md +27 -0
  199. package/rules/perl.md +26 -0
  200. package/rules/php.md +28 -0
  201. package/rules/python.md +30 -0
  202. package/rules/ruby.md +27 -0
  203. package/rules/rust.md +26 -0
  204. package/rules/security.md +29 -0
  205. package/rules/swift.md +28 -0
  206. package/rules/testing.md +25 -0
  207. package/rules/typescript.md +29 -0
  208. package/schemas/skill-frontmatter.schema.json +52 -0
  209. package/scripts/lib/audit.js +96 -0
  210. package/scripts/lib/frontmatter.js +65 -0
  211. package/scripts/lib/hooks-install.js +292 -0
  212. package/scripts/lib/i18n.js +51 -0
  213. package/scripts/lib/inventory.js +85 -0
  214. package/scripts/lib/lint.js +158 -0
  215. package/scripts/lib/mcp-install.js +284 -0
  216. package/scripts/lib/profiles.js +128 -0
  217. package/scripts/lib/render.js +86 -0
  218. package/scripts/lib/rules-install.js +118 -0
  219. package/scripts/lib/skills.js +86 -0
  220. package/scripts/lib/targets.js +113 -0
  221. package/skills/accounting-api-integration/SKILL.md +81 -0
  222. package/skills/accounting-api-integration/evals/triggers.json +17 -0
  223. package/skills/accounting-api-integration/references/quickbooks.md +48 -0
  224. package/skills/accounting-api-integration/references/xero.md +45 -0
  225. package/skills/agent-harness-design/SKILL.md +125 -0
  226. package/skills/agent-harness-design/evals/triggers.json +21 -0
  227. package/skills/ai-media-generation/SKILL.md +89 -0
  228. package/skills/ai-media-generation/evals/triggers.json +16 -0
  229. package/skills/ai-media-generation/references/job-pipeline.md +76 -0
  230. package/skills/angular-patterns/SKILL.md +122 -0
  231. package/skills/angular-patterns/evals/triggers.json +16 -0
  232. package/skills/api-design/SKILL.md +96 -0
  233. package/skills/api-design/evals/triggers.json +18 -0
  234. package/skills/api-design/references/conventions.md +133 -0
  235. package/skills/architecture-decision-records/SKILL.md +98 -0
  236. package/skills/architecture-decision-records/evals/triggers.json +17 -0
  237. package/skills/article-writing/SKILL.md +108 -0
  238. package/skills/article-writing/evals/triggers.json +16 -0
  239. package/skills/aspnet-core-patterns/SKILL.md +149 -0
  240. package/skills/aspnet-core-patterns/evals/triggers.json +16 -0
  241. package/skills/auth-patterns/SKILL.md +104 -0
  242. package/skills/auth-patterns/evals/triggers.json +17 -0
  243. package/skills/auth-patterns/references/oauth-oidc.md +64 -0
  244. package/skills/autonomous-loops/SKILL.md +99 -0
  245. package/skills/autonomous-loops/evals/triggers.json +14 -0
  246. package/skills/aws-patterns/SKILL.md +91 -0
  247. package/skills/aws-patterns/evals/triggers.json +16 -0
  248. package/skills/azure-patterns/SKILL.md +86 -0
  249. package/skills/azure-patterns/evals/triggers.json +16 -0
  250. package/skills/benchmark-methodology/SKILL.md +88 -0
  251. package/skills/benchmark-methodology/evals/triggers.json +16 -0
  252. package/skills/benchmark-methodology/references/language-harnesses.md +130 -0
  253. package/skills/bgp-diagnostics/SKILL.md +84 -0
  254. package/skills/bgp-diagnostics/evals/triggers.json +18 -0
  255. package/skills/bgp-diagnostics/references/bgp-reference.md +81 -0
  256. package/skills/bioinformatics-workflows/SKILL.md +108 -0
  257. package/skills/bioinformatics-workflows/evals/triggers.json +16 -0
  258. package/skills/bioinformatics-workflows/references/commands.md +71 -0
  259. package/skills/bioinformatics-workflows/references/formats.md +24 -0
  260. package/skills/bioinformatics-workflows/references/workflow-managers.md +107 -0
  261. package/skills/brand-voice/SKILL.md +112 -0
  262. package/skills/brand-voice/evals/triggers.json +16 -0
  263. package/skills/browser-qa/SKILL.md +99 -0
  264. package/skills/browser-qa/evals/triggers.json +16 -0
  265. package/skills/bun-runtime/SKILL.md +93 -0
  266. package/skills/bun-runtime/evals/triggers.json +16 -0
  267. package/skills/cisco-ios-config/SKILL.md +101 -0
  268. package/skills/cisco-ios-config/evals/triggers.json +18 -0
  269. package/skills/cisco-ios-config/references/verification.md +63 -0
  270. package/skills/clickhouse-patterns/SKILL.md +101 -0
  271. package/skills/clickhouse-patterns/evals/triggers.json +18 -0
  272. package/skills/clickhouse-patterns/references/engines-and-views.md +138 -0
  273. package/skills/clinical-decision-support/SKILL.md +90 -0
  274. package/skills/clinical-decision-support/evals/triggers.json +16 -0
  275. package/skills/clinical-decision-support/references/cds-hooks.md +76 -0
  276. package/skills/clinical-decision-support/references/regulatory-boundaries.md +37 -0
  277. package/skills/code-tour/SKILL.md +97 -0
  278. package/skills/code-tour/evals/triggers.json +14 -0
  279. package/skills/codebase-onboarding/SKILL.md +112 -0
  280. package/skills/codebase-onboarding/evals/triggers.json +14 -0
  281. package/skills/cold-outreach-sequences/SKILL.md +90 -0
  282. package/skills/cold-outreach-sequences/evals/triggers.json +16 -0
  283. package/skills/content-repurposing/SKILL.md +82 -0
  284. package/skills/content-repurposing/evals/triggers.json +16 -0
  285. package/skills/context-budget/SKILL.md +84 -0
  286. package/skills/context-budget/evals/triggers.json +14 -0
  287. package/skills/conventional-commits/SKILL.md +144 -0
  288. package/skills/conventional-commits/evals/triggers.json +16 -0
  289. package/skills/cpp-patterns/SKILL.md +129 -0
  290. package/skills/cpp-patterns/evals/triggers.json +16 -0
  291. package/skills/cpp-patterns/references/cmake-and-tooling.md +88 -0
  292. package/skills/cpp-patterns/references/undefined-behaviour.md +75 -0
  293. package/skills/cpp-testing/SKILL.md +131 -0
  294. package/skills/cpp-testing/evals/triggers.json +16 -0
  295. package/skills/csharp-patterns/SKILL.md +110 -0
  296. package/skills/csharp-patterns/evals/triggers.json +17 -0
  297. package/skills/csharp-patterns/references/analyzers.md +59 -0
  298. package/skills/csharp-testing/SKILL.md +85 -0
  299. package/skills/csharp-testing/evals/triggers.json +17 -0
  300. package/skills/csharp-testing/references/integration-tests.md +77 -0
  301. package/skills/css-architecture/SKILL.md +143 -0
  302. package/skills/css-architecture/evals/triggers.json +16 -0
  303. package/skills/customs-trade-compliance/SKILL.md +95 -0
  304. package/skills/customs-trade-compliance/evals/triggers.json +16 -0
  305. package/skills/customs-trade-compliance/references/incoterms-2020.md +42 -0
  306. package/skills/dart-patterns/SKILL.md +157 -0
  307. package/skills/dart-patterns/evals/triggers.json +18 -0
  308. package/skills/dart-testing/SKILL.md +164 -0
  309. package/skills/dart-testing/evals/triggers.json +18 -0
  310. package/skills/data-analysis-notebooks/SKILL.md +113 -0
  311. package/skills/data-analysis-notebooks/evals/triggers.json +16 -0
  312. package/skills/data-analysis-notebooks/references/dataframe-pitfalls.md +49 -0
  313. package/skills/data-analysis-notebooks/references/statistics-pitfalls.md +24 -0
  314. package/skills/data-pipelines/SKILL.md +90 -0
  315. package/skills/data-pipelines/evals/triggers.json +16 -0
  316. package/skills/data-pipelines/references/orchestrators.md +64 -0
  317. package/skills/database-migrations/SKILL.md +102 -0
  318. package/skills/database-migrations/evals/triggers.json +16 -0
  319. package/skills/database-migrations/references/recipes.md +98 -0
  320. package/skills/database-migrations/references/tools.md +21 -0
  321. package/skills/dbt-patterns/SKILL.md +96 -0
  322. package/skills/dbt-patterns/evals/triggers.json +16 -0
  323. package/skills/dbt-patterns/references/yaml-examples.md +114 -0
  324. package/skills/deep-research/SKILL.md +107 -0
  325. package/skills/deep-research/evals/triggers.json +14 -0
  326. package/skills/dependency-audit/SKILL.md +103 -0
  327. package/skills/dependency-audit/evals/triggers.json +16 -0
  328. package/skills/dependency-audit/references/tools.md +124 -0
  329. package/skills/deployment-strategies/SKILL.md +108 -0
  330. package/skills/deployment-strategies/evals/triggers.json +16 -0
  331. package/skills/django-patterns/SKILL.md +123 -0
  332. package/skills/django-patterns/evals/triggers.json +16 -0
  333. package/skills/docker-patterns/SKILL.md +132 -0
  334. package/skills/docker-patterns/evals/triggers.json +16 -0
  335. package/skills/docker-patterns/references/dockerfiles.md +105 -0
  336. package/skills/e2e-testing-playwright/SKILL.md +94 -0
  337. package/skills/e2e-testing-playwright/evals/triggers.json +16 -0
  338. package/skills/e2e-testing-playwright/references/config-and-ci.md +129 -0
  339. package/skills/ef-core-patterns/SKILL.md +117 -0
  340. package/skills/ef-core-patterns/evals/triggers.json +16 -0
  341. package/skills/email-deliverability/SKILL.md +99 -0
  342. package/skills/email-deliverability/evals/triggers.json +17 -0
  343. package/skills/email-deliverability/references/dns-records.md +85 -0
  344. package/skills/error-handling-patterns/SKILL.md +112 -0
  345. package/skills/error-handling-patterns/evals/triggers.json +18 -0
  346. package/skills/error-handling-patterns/references/language-idioms.md +49 -0
  347. package/skills/estimation-and-scoping/SKILL.md +97 -0
  348. package/skills/estimation-and-scoping/evals/triggers.json +14 -0
  349. package/skills/evm-dapp-integration/SKILL.md +73 -0
  350. package/skills/evm-dapp-integration/evals/triggers.json +16 -0
  351. package/skills/fastapi-patterns/SKILL.md +127 -0
  352. package/skills/fastapi-patterns/evals/triggers.json +16 -0
  353. package/skills/ffmpeg-media-processing/SKILL.md +94 -0
  354. package/skills/ffmpeg-media-processing/evals/triggers.json +16 -0
  355. package/skills/ffmpeg-media-processing/references/recipes.md +112 -0
  356. package/skills/fhir-integration/SKILL.md +81 -0
  357. package/skills/fhir-integration/evals/triggers.json +16 -0
  358. package/skills/fhir-integration/references/search-and-bundles.md +73 -0
  359. package/skills/fhir-integration/references/smart-on-fhir.md +50 -0
  360. package/skills/flask-patterns/SKILL.md +132 -0
  361. package/skills/flask-patterns/evals/triggers.json +16 -0
  362. package/skills/flutter-patterns/SKILL.md +99 -0
  363. package/skills/flutter-patterns/evals/triggers.json +16 -0
  364. package/skills/flutterwave-integration/SKILL.md +75 -0
  365. package/skills/flutterwave-integration/evals/triggers.json +16 -0
  366. package/skills/flutterwave-integration/references/api.md +73 -0
  367. package/skills/flutterwave-integration/references/webhooks.md +49 -0
  368. package/skills/fsharp-patterns/SKILL.md +133 -0
  369. package/skills/fsharp-patterns/evals/triggers.json +16 -0
  370. package/skills/fsharp-testing/SKILL.md +136 -0
  371. package/skills/fsharp-testing/evals/triggers.json +16 -0
  372. package/skills/gcp-patterns/SKILL.md +86 -0
  373. package/skills/gcp-patterns/evals/triggers.json +16 -0
  374. package/skills/git-workflow/SKILL.md +114 -0
  375. package/skills/git-workflow/evals/triggers.json +16 -0
  376. package/skills/github-actions-ci/SKILL.md +117 -0
  377. package/skills/github-actions-ci/evals/triggers.json +16 -0
  378. package/skills/github-api-automation/SKILL.md +101 -0
  379. package/skills/github-api-automation/evals/triggers.json +17 -0
  380. package/skills/go-patterns/SKILL.md +118 -0
  381. package/skills/go-patterns/evals/triggers.json +17 -0
  382. package/skills/go-patterns/references/concurrency.md +162 -0
  383. package/skills/go-testing/SKILL.md +154 -0
  384. package/skills/go-testing/evals/triggers.json +17 -0
  385. package/skills/hexagonal-architecture/SKILL.md +89 -0
  386. package/skills/hexagonal-architecture/evals/triggers.json +17 -0
  387. package/skills/hipaa-compliance/SKILL.md +91 -0
  388. package/skills/hipaa-compliance/evals/triggers.json +18 -0
  389. package/skills/hipaa-compliance/references/safeguards.md +68 -0
  390. package/skills/hl7v2-integration/SKILL.md +69 -0
  391. package/skills/hl7v2-integration/evals/triggers.json +16 -0
  392. package/skills/hl7v2-integration/references/messages-and-ack.md +78 -0
  393. package/skills/homelab-network-setup/SKILL.md +125 -0
  394. package/skills/homelab-network-setup/evals/triggers.json +18 -0
  395. package/skills/homelab-network-setup/references/firewall-and-dns.md +45 -0
  396. package/skills/homelab-network-setup/references/wireguard.md +72 -0
  397. package/skills/hubtel-integration/SKILL.md +77 -0
  398. package/skills/hubtel-integration/evals/triggers.json +16 -0
  399. package/skills/hubtel-integration/references/api.md +92 -0
  400. package/skills/i18n-localization/SKILL.md +114 -0
  401. package/skills/i18n-localization/evals/triggers.json +16 -0
  402. package/skills/i18n-localization/references/libraries.md +52 -0
  403. package/skills/incident-postmortem/SKILL.md +118 -0
  404. package/skills/incident-postmortem/evals/triggers.json +14 -0
  405. package/skills/inventory-demand-planning/SKILL.md +89 -0
  406. package/skills/inventory-demand-planning/evals/triggers.json +16 -0
  407. package/skills/inventory-demand-planning/references/formulas.md +62 -0
  408. package/skills/investor-materials/SKILL.md +127 -0
  409. package/skills/investor-materials/evals/triggers.json +16 -0
  410. package/skills/investor-materials/references/templates.md +74 -0
  411. package/skills/invoicing-and-tax/SKILL.md +84 -0
  412. package/skills/invoicing-and-tax/evals/triggers.json +17 -0
  413. package/skills/invoicing-and-tax/references/e-invoicing.md +24 -0
  414. package/skills/invoicing-and-tax/references/invoice-fields.md +38 -0
  415. package/skills/invoicing-and-tax/references/rounding.md +26 -0
  416. package/skills/java-patterns/SKILL.md +141 -0
  417. package/skills/java-patterns/evals/triggers.json +17 -0
  418. package/skills/java-testing/SKILL.md +142 -0
  419. package/skills/java-testing/evals/triggers.json +16 -0
  420. package/skills/jetpack-compose-patterns/SKILL.md +88 -0
  421. package/skills/jetpack-compose-patterns/evals/triggers.json +16 -0
  422. package/skills/jira-integration/SKILL.md +92 -0
  423. package/skills/jira-integration/evals/triggers.json +17 -0
  424. package/skills/jpa-hibernate/SKILL.md +92 -0
  425. package/skills/jpa-hibernate/evals/triggers.json +18 -0
  426. package/skills/jpa-hibernate/references/mapping.md +99 -0
  427. package/skills/kotlin-multiplatform/SKILL.md +97 -0
  428. package/skills/kotlin-multiplatform/evals/triggers.json +16 -0
  429. package/skills/kotlin-patterns/SKILL.md +128 -0
  430. package/skills/kotlin-patterns/evals/triggers.json +16 -0
  431. package/skills/kotlin-testing/SKILL.md +117 -0
  432. package/skills/kotlin-testing/evals/triggers.json +16 -0
  433. package/skills/ktor-patterns/SKILL.md +135 -0
  434. package/skills/ktor-patterns/evals/triggers.json +18 -0
  435. package/skills/kubernetes-patterns/SKILL.md +106 -0
  436. package/skills/kubernetes-patterns/evals/triggers.json +16 -0
  437. package/skills/kubernetes-patterns/references/manifests.md +115 -0
  438. package/skills/laravel-patterns/SKILL.md +136 -0
  439. package/skills/laravel-patterns/evals/triggers.json +16 -0
  440. package/skills/linear-integration/SKILL.md +98 -0
  441. package/skills/linear-integration/evals/triggers.json +17 -0
  442. package/skills/literature-review/SKILL.md +109 -0
  443. package/skills/literature-review/evals/triggers.json +16 -0
  444. package/skills/literature-review/references/appraisal.md +41 -0
  445. package/skills/literature-review/references/search-sources.md +39 -0
  446. package/skills/llm-app-patterns/SKILL.md +97 -0
  447. package/skills/llm-app-patterns/evals/triggers.json +17 -0
  448. package/skills/llm-evals/SKILL.md +98 -0
  449. package/skills/llm-evals/evals/triggers.json +16 -0
  450. package/skills/llm-evals/references/graders.md +66 -0
  451. package/skills/logistics-exception-management/SKILL.md +90 -0
  452. package/skills/logistics-exception-management/evals/triggers.json +16 -0
  453. package/skills/manim-animations/SKILL.md +106 -0
  454. package/skills/manim-animations/evals/triggers.json +16 -0
  455. package/skills/market-research/SKILL.md +117 -0
  456. package/skills/market-research/evals/triggers.json +16 -0
  457. package/skills/marketing-campaign/SKILL.md +111 -0
  458. package/skills/marketing-campaign/evals/triggers.json +16 -0
  459. package/skills/mcp-server-patterns/SKILL.md +127 -0
  460. package/skills/mcp-server-patterns/evals/triggers.json +18 -0
  461. package/skills/mcp-server-patterns/references/protocol-notes.md +68 -0
  462. package/skills/metrics-dashboards/SKILL.md +113 -0
  463. package/skills/metrics-dashboards/evals/triggers.json +16 -0
  464. package/skills/ml-experiment-workflow/SKILL.md +125 -0
  465. package/skills/ml-experiment-workflow/evals/triggers.json +16 -0
  466. package/skills/mongodb-patterns/SKILL.md +115 -0
  467. package/skills/mongodb-patterns/evals/triggers.json +18 -0
  468. package/skills/mongodb-patterns/references/indexes-and-aggregation.md +94 -0
  469. package/skills/mpesa-daraja-integration/SKILL.md +70 -0
  470. package/skills/mpesa-daraja-integration/evals/triggers.json +16 -0
  471. package/skills/mpesa-daraja-integration/references/api.md +103 -0
  472. package/skills/mpesa-daraja-integration/references/callbacks.md +74 -0
  473. package/skills/mtn-momo-integration/SKILL.md +66 -0
  474. package/skills/mtn-momo-integration/evals/triggers.json +16 -0
  475. package/skills/mtn-momo-integration/references/api.md +153 -0
  476. package/skills/mtn-momo-integration/references/callbacks.md +92 -0
  477. package/skills/mtn-momo-integration/references/sandbox-and-go-live.md +51 -0
  478. package/skills/mtn-momo-integration/scripts/sandbox-setup.js +79 -0
  479. package/skills/mysql-patterns/SKILL.md +96 -0
  480. package/skills/mysql-patterns/evals/triggers.json +16 -0
  481. package/skills/mysql-patterns/references/explain.md +34 -0
  482. package/skills/mysql-patterns/references/online-ddl.md +41 -0
  483. package/skills/nestjs-patterns/SKILL.md +114 -0
  484. package/skills/nestjs-patterns/evals/triggers.json +16 -0
  485. package/skills/nestjs-patterns/references/testing.md +68 -0
  486. package/skills/network-automation/SKILL.md +107 -0
  487. package/skills/network-automation/evals/triggers.json +18 -0
  488. package/skills/network-automation/references/tool-snippets.md +153 -0
  489. package/skills/network-troubleshooting/SKILL.md +152 -0
  490. package/skills/network-troubleshooting/evals/triggers.json +18 -0
  491. package/skills/network-troubleshooting/references/commands.md +33 -0
  492. package/skills/nextjs-patterns/SKILL.md +150 -0
  493. package/skills/nextjs-patterns/evals/triggers.json +18 -0
  494. package/skills/nextjs-patterns/references/caching.md +87 -0
  495. package/skills/node-backend-patterns/SKILL.md +103 -0
  496. package/skills/node-backend-patterns/evals/triggers.json +16 -0
  497. package/skills/node-backend-patterns/references/templates.md +122 -0
  498. package/skills/nuxt-patterns/SKILL.md +103 -0
  499. package/skills/nuxt-patterns/evals/triggers.json +16 -0
  500. package/skills/parallel-agent-orchestration/SKILL.md +106 -0
  501. package/skills/parallel-agent-orchestration/evals/triggers.json +14 -0
  502. package/skills/paystack-integration/SKILL.md +59 -0
  503. package/skills/paystack-integration/evals/triggers.json +14 -0
  504. package/skills/paystack-integration/references/api.md +73 -0
  505. package/skills/paystack-integration/references/mobile-money.md +50 -0
  506. package/skills/paystack-integration/references/webhooks.md +80 -0
  507. package/skills/pci-dss-scope/SKILL.md +103 -0
  508. package/skills/pci-dss-scope/evals/triggers.json +18 -0
  509. package/skills/pci-dss-scope/references/saq-and-data.md +36 -0
  510. package/skills/perl-patterns/SKILL.md +135 -0
  511. package/skills/perl-patterns/evals/triggers.json +18 -0
  512. package/skills/perl-testing/SKILL.md +126 -0
  513. package/skills/perl-testing/evals/triggers.json +17 -0
  514. package/skills/php-patterns/SKILL.md +116 -0
  515. package/skills/php-patterns/evals/triggers.json +16 -0
  516. package/skills/php-testing/SKILL.md +123 -0
  517. package/skills/php-testing/evals/triggers.json +16 -0
  518. package/skills/postgres-patterns/SKILL.md +103 -0
  519. package/skills/postgres-patterns/evals/triggers.json +17 -0
  520. package/skills/postgres-patterns/references/explain.md +59 -0
  521. package/skills/postgres-patterns/references/locks.md +53 -0
  522. package/skills/prd-writing/SKILL.md +124 -0
  523. package/skills/prd-writing/evals/triggers.json +15 -0
  524. package/skills/prisma-patterns/SKILL.md +83 -0
  525. package/skills/prisma-patterns/evals/triggers.json +18 -0
  526. package/skills/prisma-patterns/references/prisma-8.md +46 -0
  527. package/skills/prisma-patterns/references/workflow.md +50 -0
  528. package/skills/privacy-compliance/SKILL.md +121 -0
  529. package/skills/privacy-compliance/evals/triggers.json +18 -0
  530. package/skills/privacy-compliance/references/jurisdictions.md +51 -0
  531. package/skills/production-scheduling/SKILL.md +98 -0
  532. package/skills/production-scheduling/evals/triggers.json +16 -0
  533. package/skills/prompt-engineering/SKILL.md +92 -0
  534. package/skills/prompt-engineering/evals/triggers.json +16 -0
  535. package/skills/python-patterns/SKILL.md +107 -0
  536. package/skills/python-patterns/evals/triggers.json +16 -0
  537. package/skills/python-patterns/references/tooling.md +101 -0
  538. package/skills/python-testing/SKILL.md +126 -0
  539. package/skills/python-testing/evals/triggers.json +16 -0
  540. package/skills/python-testing/references/mocking.md +71 -0
  541. package/skills/pytorch-patterns/SKILL.md +139 -0
  542. package/skills/pytorch-patterns/evals/triggers.json +16 -0
  543. package/skills/pytorch-patterns/references/debugging.md +39 -0
  544. package/skills/quarkus-patterns/SKILL.md +93 -0
  545. package/skills/quarkus-patterns/evals/triggers.json +18 -0
  546. package/skills/rag-pipeline-patterns/SKILL.md +117 -0
  547. package/skills/rag-pipeline-patterns/evals/triggers.json +18 -0
  548. package/skills/rag-pipeline-patterns/references/evaluation.md +37 -0
  549. package/skills/rag-pipeline-patterns/references/retrieval-recipes.md +78 -0
  550. package/skills/rails-patterns/SKILL.md +132 -0
  551. package/skills/rails-patterns/evals/triggers.json +16 -0
  552. package/skills/react-native-patterns/SKILL.md +101 -0
  553. package/skills/react-native-patterns/evals/triggers.json +16 -0
  554. package/skills/react-patterns/SKILL.md +140 -0
  555. package/skills/react-patterns/evals/triggers.json +18 -0
  556. package/skills/react-performance/SKILL.md +107 -0
  557. package/skills/react-performance/evals/triggers.json +18 -0
  558. package/skills/redis-patterns/SKILL.md +109 -0
  559. package/skills/redis-patterns/evals/triggers.json +18 -0
  560. package/skills/redis-patterns/references/operations.md +61 -0
  561. package/skills/refactoring-safely/SKILL.md +107 -0
  562. package/skills/refactoring-safely/evals/triggers.json +17 -0
  563. package/skills/release-management/SKILL.md +141 -0
  564. package/skills/release-management/evals/triggers.json +16 -0
  565. package/skills/remotion-video/SKILL.md +91 -0
  566. package/skills/remotion-video/evals/triggers.json +16 -0
  567. package/skills/remotion-video/references/lambda.md +66 -0
  568. package/skills/returns-reverse-logistics/SKILL.md +97 -0
  569. package/skills/returns-reverse-logistics/evals/triggers.json +16 -0
  570. package/skills/ruby-patterns/SKILL.md +127 -0
  571. package/skills/ruby-patterns/evals/triggers.json +18 -0
  572. package/skills/ruby-testing/SKILL.md +113 -0
  573. package/skills/ruby-testing/evals/triggers.json +18 -0
  574. package/skills/runbook-writing/SKILL.md +101 -0
  575. package/skills/runbook-writing/evals/triggers.json +14 -0
  576. package/skills/rust-patterns/SKILL.md +118 -0
  577. package/skills/rust-patterns/evals/triggers.json +16 -0
  578. package/skills/rust-patterns/references/cargo-and-clippy.md +105 -0
  579. package/skills/rust-patterns/references/errors-and-traits.md +97 -0
  580. package/skills/rust-testing/SKILL.md +148 -0
  581. package/skills/rust-testing/evals/triggers.json +16 -0
  582. package/skills/rust-testing/references/fakes-proptest-async.md +113 -0
  583. package/skills/search-first/SKILL.md +73 -0
  584. package/skills/search-first/evals/triggers.json +14 -0
  585. package/skills/security-review/SKILL.md +58 -0
  586. package/skills/security-review/evals/triggers.json +14 -0
  587. package/skills/security-review/references/checklist.md +54 -0
  588. package/skills/security-review/scripts/scan-secrets.js +79 -0
  589. package/skills/seo-audit/SKILL.md +111 -0
  590. package/skills/seo-audit/evals/triggers.json +16 -0
  591. package/skills/seo-audit/references/checks.md +65 -0
  592. package/skills/session-handoff/SKILL.md +101 -0
  593. package/skills/session-handoff/evals/triggers.json +14 -0
  594. package/skills/skill-authoring/SKILL.md +87 -0
  595. package/skills/skill-authoring/evals/triggers.json +14 -0
  596. package/skills/skill-authoring/references/frontmatter.md +40 -0
  597. package/skills/slack-app-integration/SKILL.md +108 -0
  598. package/skills/slack-app-integration/evals/triggers.json +17 -0
  599. package/skills/smart-contract-security/SKILL.md +110 -0
  600. package/skills/smart-contract-security/evals/triggers.json +16 -0
  601. package/skills/smart-contract-security/references/vulnerability-checklist.md +79 -0
  602. package/skills/social-media-publishing/SKILL.md +84 -0
  603. package/skills/social-media-publishing/evals/triggers.json +17 -0
  604. package/skills/social-media-publishing/references/platform-apis.md +116 -0
  605. package/skills/solidity-patterns/SKILL.md +95 -0
  606. package/skills/solidity-patterns/evals/triggers.json +16 -0
  607. package/skills/spring-boot-patterns/SKILL.md +128 -0
  608. package/skills/spring-boot-patterns/evals/triggers.json +18 -0
  609. package/skills/spring-boot-patterns/references/testing.md +72 -0
  610. package/skills/stripe-integration/SKILL.md +69 -0
  611. package/skills/stripe-integration/evals/triggers.json +16 -0
  612. package/skills/stripe-integration/references/api.md +71 -0
  613. package/skills/stripe-integration/references/billing.md +58 -0
  614. package/skills/stripe-integration/references/webhooks.md +76 -0
  615. package/skills/subscription-billing-ops/SKILL.md +118 -0
  616. package/skills/subscription-billing-ops/evals/triggers.json +17 -0
  617. package/skills/subscription-billing-ops/references/proration.md +47 -0
  618. package/skills/subscription-billing-ops/references/revenue-metrics.md +38 -0
  619. package/skills/svelte-patterns/SKILL.md +122 -0
  620. package/skills/svelte-patterns/evals/triggers.json +16 -0
  621. package/skills/swift-patterns/SKILL.md +113 -0
  622. package/skills/swift-patterns/evals/triggers.json +16 -0
  623. package/skills/swift-patterns/references/concurrency-migration.md +40 -0
  624. package/skills/swift-testing/SKILL.md +121 -0
  625. package/skills/swift-testing/evals/triggers.json +16 -0
  626. package/skills/swift-testing/references/swift-testing-cheatsheet.md +81 -0
  627. package/skills/swiftui-patterns/SKILL.md +102 -0
  628. package/skills/swiftui-patterns/evals/triggers.json +16 -0
  629. package/skills/tdd-workflow/SKILL.md +67 -0
  630. package/skills/tdd-workflow/evals/triggers.json +14 -0
  631. package/skills/technical-writing/SKILL.md +95 -0
  632. package/skills/technical-writing/evals/triggers.json +14 -0
  633. package/skills/terraform-patterns/SKILL.md +122 -0
  634. package/skills/terraform-patterns/evals/triggers.json +16 -0
  635. package/skills/terraform-patterns/references/backends.md +67 -0
  636. package/skills/test-coverage-improvement/SKILL.md +107 -0
  637. package/skills/test-coverage-improvement/evals/triggers.json +16 -0
  638. package/skills/threat-modeling/SKILL.md +113 -0
  639. package/skills/threat-modeling/evals/triggers.json +16 -0
  640. package/skills/typescript-patterns/SKILL.md +122 -0
  641. package/skills/typescript-patterns/evals/triggers.json +16 -0
  642. package/skills/typescript-testing/SKILL.md +101 -0
  643. package/skills/typescript-testing/evals/triggers.json +16 -0
  644. package/skills/verification-loop/SKILL.md +70 -0
  645. package/skills/verification-loop/evals/triggers.json +14 -0
  646. package/skills/verification-loop/scripts/detect-checks.js +102 -0
  647. package/skills/vite-patterns/SKILL.md +117 -0
  648. package/skills/vite-patterns/evals/triggers.json +16 -0
  649. package/skills/vue-patterns/SKILL.md +104 -0
  650. package/skills/vue-patterns/evals/triggers.json +16 -0
  651. package/skills/web-accessibility/SKILL.md +119 -0
  652. package/skills/web-accessibility/evals/triggers.json +16 -0
  653. package/skills/web-performance/SKILL.md +107 -0
  654. package/skills/web-performance/evals/triggers.json +16 -0
  655. package/skills/web-scraping/SKILL.md +80 -0
  656. package/skills/web-scraping/evals/triggers.json +16 -0
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: spring-boot-patterns
3
+ description: Spring Boot application structure and conventions, covering controller-service-repository layering, constructor injection, typed @ConfigurationProperties, @Transactional boundaries, Bean Validation, ProblemDetail error responses, SecurityFilterChain setup, Actuator exposure and focused test slices such as @WebMvcTest and @DataJpaTest. Use when building or changing a Spring Boot service, adding an endpoint or configuration, securing routes, wiring health checks, or writing Spring Boot tests.
4
+ license: MIT
5
+ compatibility: Any Agent Skills host. Needs a JDK and the project's Maven or Gradle wrapper. Written for Spring Boot 3.x and 4.x; version-specific differences are called out.
6
+ metadata:
7
+ kete-loom/version: "0.1.0"
8
+ kete-loom/category: coding
9
+ kete-loom/risk: runs-commands
10
+ kete-loom/requires: "java"
11
+ kete-loom/owner: "Kete-org"
12
+ ---
13
+
14
+ # Spring Boot patterns
15
+
16
+ Write Spring Boot code that is easy to test, fails loudly on bad configuration, and keeps transactions and security explicit. For entity mapping, fetch plans and N+1 problems, use the `jpa-hibernate` skill alongside this one.
17
+
18
+ ## 0. Before you change anything
19
+
20
+ 1. Find the Boot version in `pom.xml` (`spring-boot-starter-parent`) or `build.gradle(.kts)` (`org.springframework.boot` plugin). Boot 3 and 4 differ in test setup and Jackson (see `references/testing.md`).
21
+ 2. Find the build command: `./mvnw verify` or `./gradlew build`. Run it once and record the result. If it already fails, **stop and tell the user** before editing.
22
+ 3. Read one existing controller, service and test, and follow their package layout, naming and style. Don't introduce a second style.
23
+
24
+ ## 1. Structure
25
+
26
+ - Package **by feature** (`orders/`, `billing/`), not by layer, unless the project already does otherwise. Keep the `@SpringBootApplication` class in the root package so component scanning finds everything.
27
+ - Layers inside a feature:
28
+ - **Controller**: HTTP only. Parse, validate, call one service method, map to a response DTO. No repository calls, no business rules.
29
+ - **Service**: business rules and the transaction boundary.
30
+ - **Repository**: Spring Data interfaces. No business logic.
31
+ - Never return JPA entities from controllers. Map to records (`record OrderResponse(...)`) so lazy fields, internal columns and cycles never leak into JSON.
32
+
33
+ ## 2. Dependency injection
34
+
35
+ - Use **constructor injection** with `final` fields. One constructor needs no `@Autowired`. Never use field injection in new code.
36
+ - Prefer one implementation per interface. When there are several, choose with `@Qualifier` or a `Map<String, T>` injection, not `@Primary` scattered around.
37
+ - A circular dependency is a design bug. Split the class; don't enable `spring.main.allow-circular-references`.
38
+
39
+ ## 3. Configuration
40
+
41
+ - Bind related settings to a typed record and validate it at startup:
42
+
43
+ ```java
44
+ @ConfigurationProperties(prefix = "payments")
45
+ @Validated
46
+ public record PaymentsProperties(@NotBlank String baseUrl, @NotNull Duration timeout) {}
47
+ ```
48
+
49
+ Register it with `@ConfigurationPropertiesScan` (or `@EnableConfigurationProperties`). Prefer this over many `@Value` fields.
50
+ - Secrets come from environment variables or a secret store, never committed `application.yml`. Relaxed binding maps `PAYMENTS_BASEURL` to `payments.base-url`.
51
+ - Profiles (`application-dev.yml`, `spring.profiles.active`) change environment details, not behaviour. Never gate business logic on a profile name.
52
+ - Set `spring.jpa.open-in-view=false` in new services so lazy loading can't run during view rendering (Boot logs a warning while it is left on).
53
+
54
+ ## 4. Transactions
55
+
56
+ - Put `@Transactional` on **public service methods**, not controllers or repositories. Use `@Transactional(readOnly = true)` for queries.
57
+ - The annotation works through a proxy, so it is **ignored** on a call from another method in the same class (self-invocation) and on private methods. Move the method to another bean if it needs its own transaction.
58
+ - By default only unchecked exceptions roll back. If a checked exception must roll back, set `rollbackFor`.
59
+ - Never call a slow remote API (payment provider, email) inside a database transaction. Commit first, then call out, or use an outbox table or `@TransactionalEventListener(phase = AFTER_COMMIT)`.
60
+
61
+ ## 5. Validation and errors
62
+
63
+ - Annotate request DTOs with Jakarta Bean Validation (`@NotBlank`, `@Size`, `@Email`) and the controller parameter with `@Valid`. Needs `spring-boot-starter-validation`.
64
+ - Return RFC 9457 problem details. Set `spring.mvc.problemdetails.enabled=true` or add one `@RestControllerAdvice` (extending `ResponseEntityExceptionHandler`) that maps domain exceptions to `ProblemDetail` with the right status:
65
+
66
+ ```java
67
+ @ExceptionHandler(OrderNotFoundException.class)
68
+ ProblemDetail notFound(OrderNotFoundException e) {
69
+ return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage());
70
+ }
71
+ ```
72
+
73
+ - Never return stack traces, SQL or exception class names to clients. Log them server-side with a correlation id.
74
+ - Don't catch an exception just to log and rethrow it; let the advice handle it once.
75
+
76
+ ## 6. Security basics
77
+
78
+ - Configure Spring Security with a `SecurityFilterChain` bean (the old `WebSecurityConfigurerAdapter` is gone):
79
+
80
+ ```java
81
+ @Bean
82
+ SecurityFilterChain api(HttpSecurity http) throws Exception {
83
+ return http
84
+ .authorizeHttpRequests(a -> a
85
+ .requestMatchers("/actuator/health/**").permitAll()
86
+ .anyRequest().authenticated())
87
+ .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
88
+ .build();
89
+ }
90
+ ```
91
+
92
+ - Deny by default: end with `.anyRequest().authenticated()` (or `denyAll()`), and permit only named paths.
93
+ - Disable CSRF only for stateless token-authenticated APIs; keep it for cookie sessions.
94
+ - Hash passwords with `PasswordEncoderFactories.createDelegatingPasswordEncoder()` (bcrypt by default). Never store or log raw passwords or tokens.
95
+ - Check ownership in the service ("does this order belong to the caller?"), not only the role. `@PreAuthorize` needs `@EnableMethodSecurity`.
96
+ - **Stop and ask the user** before changing who can reach an endpoint, the auth mechanism, or CORS origins.
97
+
98
+ ## 7. Actuator
99
+
100
+ - Add `spring-boot-starter-actuator`. Only `health` is exposed over HTTP by default; keep it that way and expose more by name (`management.endpoints.web.exposure.include=health,info,prometheus`), never `*` on a public port.
101
+ - Use the liveness and readiness health groups (`/actuator/health/liveness`, `/actuator/health/readiness`, enabled by default) for container probes. Readiness may check dependencies; liveness must not, or a database outage restarts every pod.
102
+ - Put management on a separate port (`management.server.port`) when the platform allows, and protect anything beyond health.
103
+
104
+ ## 8. Testing
105
+
106
+ Use the narrowest slice that covers the behaviour. Details, annotations and Boot 3 versus 4 differences are in `references/testing.md`.
107
+
108
+ | What you test | Use |
109
+ |---|---|
110
+ | Pure business logic | Plain JUnit, no Spring context |
111
+ | Controller: routing, validation, status codes, JSON | `@WebMvcTest(OrderController.class)` with `@MockitoBean` services; `@Import` your security config |
112
+ | Repository queries and mappings | `@DataJpaTest` with `@AutoConfigureTestDatabase(replace = NONE)` and a Testcontainers database |
113
+ | Full wiring, security and config | `@SpringBootTest` sparingly, with `@ServiceConnection` containers |
114
+
115
+ - Assert status, body and problem details for error paths, not only the happy path.
116
+ - Test security rules: an unauthenticated request gets 401, the wrong user gets 403 or 404.
117
+ - Don't use H2 to stand in for PostgreSQL or MySQL in repository tests; dialect differences hide real bugs.
118
+
119
+ ## Never
120
+
121
+ - Field injection, entities in API responses, or `@Transactional` on private or self-invoked methods.
122
+ - `permitAll()` on everything "for now", or `exposure.include=*` in production.
123
+ - `spring.jpa.hibernate.ddl-auto=update` or `create` outside local dev; use migrations.
124
+ - Catching `Exception` in controllers and returning 200 with an error message.
125
+
126
+ ## Before you finish
127
+
128
+ Run the full build (`./mvnw verify` or `./gradlew build`). Report: what changed (files), new config keys and their defaults, security rules touched, tests added, and the final test counts.
@@ -0,0 +1,18 @@
1
+ {
2
+ "should_trigger": [
3
+ "Add a new REST endpoint to our Spring Boot order service with request validation and proper 404 responses",
4
+ "Bind the payments.* settings in application.yml to a typed config class in Spring Boot",
5
+ "My @Transactional method in the Spring service doesn't roll back when called from the same class, why?",
6
+ "Lock down our Spring Boot API with a SecurityFilterChain and JWT resource server, keeping actuator health public",
7
+ "Write a @WebMvcTest for the invoice controller using MockitoBean instead of MockBean",
8
+ "Which actuator endpoints should a Spring Boot app expose for Kubernetes liveness and readiness probes?"
9
+ ],
10
+ "should_not_trigger": [
11
+ "Our Hibernate entities trigger hundreds of selects when loading the order list, fix the N+1",
12
+ "Build a Quarkus REST endpoint backed by Panache and make it compile to a native image",
13
+ "Add JWT authentication and StatusPages error handling to my Ktor server",
14
+ "Review this Java pull request for null handling and resource leaks",
15
+ "Fix the Maven build: cannot find symbol after upgrading the JDK to 21",
16
+ "Write JUnit 5 parameterized tests for this plain Java date parsing utility"
17
+ ]
18
+ }
@@ -0,0 +1,72 @@
1
+ # Spring Boot testing reference
2
+
3
+ ## Slice annotations
4
+
5
+ | Annotation | Loads | Typical collaborators |
6
+ |---|---|---|
7
+ | `@WebMvcTest(X.class)` | MVC infrastructure, controller `X`, advice, filters (not `@Configuration` or `@Component` beans) | `MockMvc` or `MockMvcTester`, `@MockitoBean` services |
8
+ | `@WebFluxTest(X.class)` | WebFlux infrastructure for controller `X` | `WebTestClient` |
9
+ | `@DataJpaTest` | JPA, repositories, `TestEntityManager`; each test rolls back | Testcontainers database |
10
+ | `@JsonTest` | Jackson configuration | `JacksonTester` |
11
+ | `@RestClientTest` | `RestClient` / `RestTemplate` builders | `MockRestServiceServer` |
12
+ | `@SpringBootTest` | The whole application | `@AutoConfigureMockMvc`, real containers |
13
+
14
+ `@DataJpaTest` replaces the datasource with an embedded one by default. To test against a container, add `@AutoConfigureTestDatabase(replace = Replace.NONE)` and import the container configuration below.
15
+
16
+ `@WebMvcTest` does not pick up a `SecurityFilterChain` defined in your own `@Configuration` class, so the slice runs Boot's default security instead of your rules. `@Import(SecurityConfig.class)` into the test; with a JWT resource server, also add `@MockitoBean JwtDecoder` so the context starts without an issuer.
17
+
18
+ ## Mocks
19
+
20
+ - `@MockitoBean` and `@MockitoSpyBean` (Spring Framework 6.2+) replace `@MockBean` and `@SpyBean`. `@MockBean` is deprecated in Boot 3.4 and removed in Boot 4.
21
+ - `@MockitoBean` goes on test class fields, not in `@Configuration` classes.
22
+ - Every distinct set of mocks creates a new application context. Keep mock sets consistent across test classes so the context cache is reused.
23
+
24
+ ## Example: controller slice
25
+
26
+ ```java
27
+ @WebMvcTest(OrderController.class)
28
+ @Import(SecurityConfig.class)
29
+ class OrderControllerTest {
30
+ @Autowired MockMvc mvc;
31
+ @MockitoBean OrderService orders;
32
+ @MockitoBean JwtDecoder jwtDecoder;
33
+
34
+ @Test
35
+ void returns404ProblemWhenMissing() throws Exception {
36
+ when(orders.find(42L)).thenThrow(new OrderNotFoundException(42L));
37
+ mvc.perform(get("/orders/42").with(jwt()))
38
+ .andExpect(status().isNotFound())
39
+ .andExpect(jsonPath("$.status").value(404));
40
+ }
41
+
42
+ @Test
43
+ void rejectsAnonymous() throws Exception {
44
+ mvc.perform(get("/orders/42")).andExpect(status().isUnauthorized());
45
+ }
46
+ }
47
+ ```
48
+
49
+ `jwt()` comes from `spring-security-test` (`SecurityMockMvcRequestPostProcessors`).
50
+
51
+ ## Example: Testcontainers with service connections
52
+
53
+ ```java
54
+ @TestConfiguration(proxyBeanMethods = false)
55
+ class TestcontainersConfig {
56
+ @Bean @ServiceConnection
57
+ PostgreSQLContainer<?> postgres() { return new PostgreSQLContainer<>("postgres:16-alpine"); }
58
+ }
59
+ ```
60
+
61
+ Import it with `@Import(TestcontainersConfig.class)` in `@SpringBootTest` classes, or in `@DataJpaTest` classes together with `@AutoConfigureTestDatabase(replace = Replace.NONE)`. `@ServiceConnection` (Boot 3.1+) sets the datasource URL and credentials, so no `@DynamicPropertySource` is needed.
62
+
63
+ ## Boot 4 differences
64
+
65
+ From the Spring Boot 4.0 migration guide:
66
+
67
+ - Test support is split per technology. Add the matching test starter, for example `spring-boot-starter-webmvc-test` or `spring-boot-starter-data-jpa-test`; these bring in `spring-boot-starter-test`. Slice annotations move to the matching `spring-boot-<technology>-test` module, so imports change.
68
+ - `@SpringBootTest` no longer configures MockMvc on its own. Add `@AutoConfigureMockMvc`.
69
+ - `TestRestTemplate` needs `@AutoConfigureTestRestTemplate`; the newer `RestTestClient` uses `@AutoConfigureRestTestClient`.
70
+ - Jackson 3 is the default (`tools.jackson` packages; `@JsonComponent` becomes `@JacksonComponent`). The auto-configured mapper is a `JsonMapper`; to replace it, define a `JsonMapper` bean, because a plain `ObjectMapper` bean no longer makes it back off.
71
+
72
+ When upgrading, follow the official migration guide rather than this summary.
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: stripe-integration
3
+ description: Builds and hardens Stripe payments and billing, from Checkout Sessions and Payment Intents with SCA and 3D Secure handling to Stripe-Signature webhook verification on the unparsed request body, Idempotency-Key headers on every POST, subscriptions driven by invoice events, the Billing customer portal, and test clocks for simulating renewals and trial ends. Use when adding Stripe Checkout or Elements, setting up Stripe subscriptions or the customer portal, fixing a Stripe webhook signature error or duplicate fulfilment, or testing billing cycles with the Stripe CLI.
4
+ license: MIT
5
+ compatibility: Any Agent Skills host. Live calls need network access to api.stripe.com; local webhook testing uses the Stripe CLI. Examples are TypeScript with the official stripe-node library; the rules apply to any Stripe SDK.
6
+ metadata:
7
+ kete-loom/version: "0.1.0"
8
+ kete-loom/category: integrations
9
+ kete-loom/risk: network
10
+ kete-loom/requires: "stripe"
11
+ kete-loom/owner: "Kete-org"
12
+ ---
13
+
14
+ # Stripe integration
15
+
16
+ Build a Stripe flow where the price comes from the server, a webhook is trusted only after its signature checks out, retries never create a second charge, and subscription access follows what Stripe says, not what the browser says.
17
+
18
+ ## Ground rules
19
+
20
+ 1. **Secret key on the server only.** `STRIPE_SECRET_KEY` (`sk_...`, or a restricted `rk_...` key with only the permissions the service needs) and the webhook secret (`whsec_...`) come from the environment. Only the publishable key (`pk_...`) reaches the browser.
21
+ 2. **Prices come from Stripe or your database.** Pass a `price` ID or compute `amount` server-side. Never accept an amount, currency or price ID from the client without checking it against an allow-list.
22
+ 3. **Amounts are integers in the smallest unit** (cents for USD, pence for GBP; zero-decimal currencies such as JPY are whole units). No float maths.
23
+ 4. **Fulfil from webhooks.** `checkout.session.completed` (plus `checkout.session.async_payment_succeeded` for delayed methods) or `payment_intent.succeeded`. The success page only shows status; it may never load.
24
+ 5. **Verify every webhook** with the SDK on the **raw request bytes**, the `Stripe-Signature` header and that endpoint's `whsec_` secret. Keep the default 5-minute tolerance; never set it to 0.
25
+ 6. **Idempotent both ways.** Send an `Idempotency-Key` (a UUID v4 stored with your order) on every create/update POST so a network retry can't double-charge. Record processed `event.id`s and guard fulfilment with `UPDATE ... WHERE status = 'pending'`, because Stripe delivers events at least once and out of order.
26
+ 7. **Test mode or a sandbox first**, with the Stripe CLI forwarding events, before any live key is configured.
27
+
28
+ ## Choose the flow
29
+
30
+ | Need | Use |
31
+ |---|---|
32
+ | One-off or subscription payment, least code, SCA handled for you | Checkout Session (`mode: 'payment'` or `'subscription'`), hosted or embedded |
33
+ | Your own payment form | Payment Intent + Payment Element; confirm on the client with `stripe.confirmPayment` |
34
+ | Save a card for later charges | `setup_future_usage: 'off_session'` on the Payment Intent, or a Setup Intent |
35
+ | Let customers manage plans, cards and invoices | Billing customer portal session |
36
+
37
+ ## Implementation steps
38
+
39
+ 1. **Create a Checkout Session** on the server with `line_items: [{ price, quantity }]`, `mode`, `success_url` (include `{CHECKOUT_SESSION_ID}`), `cancel_url`, `client_reference_id` or `metadata.order_id`, and an idempotency key. Redirect to `session.url`. Shapes in `references/api.md`.
40
+ 2. **Or create a Payment Intent** with `amount`, `currency`, `automatic_payment_methods: { enabled: true }`, `metadata.order_id`; send only `client_secret` to the browser.
41
+ 3. **Webhook endpoint**: follow `references/webhooks.md`. Mount it before any JSON body parser, verify with `stripe.webhooks.constructEvent`, return `2xx` fast, process in a queue.
42
+ 4. **Handle SCA**: a Payment Intent in `requires_action` needs the customer to authenticate; the client SDK does this when confirming. For off-session charges (renewals, saved cards) handle `authentication_required` by emailing the customer a link back on-session. Never mark paid on `processing`.
43
+ 5. **Subscriptions**: create through Checkout with `mode: 'subscription'`, store `customer` and `subscription` IDs, and drive access from events: `invoice.paid` (extend access; from API `2025-03-31.basil` the period end is on `items.data[].current_period_end`), `invoice.payment_failed` (dunning, keep or restrict per policy), `customer.subscription.updated` / `.deleted` (plan changes, cancellation). Details in `references/billing.md`.
44
+ 6. **Customer portal**: configure it in the Dashboard, then create `billingPortal.sessions.create({ customer, return_url })` from an authenticated route and redirect. Never create a portal session for a customer ID taken from the request; look it up from the logged-in user.
45
+ 7. **Test clocks** for billing: create a clock, attach a new customer to it, subscribe, then advance time to see trials end, renewals and failed payments fire their webhooks (`references/billing.md`).
46
+ 8. **Persist**: order/payment row with expected amount and currency, Checkout Session or Payment Intent ID, idempotency key, status; a `stripe_events` table of processed event IDs; customer and subscription IDs per user.
47
+
48
+ ## Reviewing an existing integration
49
+
50
+ Check each ground rule, then look for:
51
+
52
+ - Webhook route behind `express.json()` / framework body parsing, so `constructEvent` fails or was "fixed" by skipping verification.
53
+ - One `whsec_` reused across endpoints or between test and live (each endpoint and mode has its own).
54
+ - Fulfilment in the success page handler, or on `payment_intent.created` / `processing`.
55
+ - Event order assumed (`invoice.paid` before `customer.subscription.created`); missing objects not fetched from the API.
56
+ - No idempotency key on PaymentIntent, refund or subscription creation; or a new key generated on each retry, which defeats it.
57
+ - Subscription access granted from `checkout.session.completed` alone and never revoked on `customer.subscription.deleted`.
58
+ - Prices or amounts sent from the client; `price` IDs hard-coded from test mode in live config.
59
+ - Secret key in front-end code or a `NEXT_PUBLIC_`/`VITE_` variable.
60
+
61
+ ## Stop and ask the user
62
+
63
+ - Before switching any configuration to live keys.
64
+ - Whether to keep or cut access on a failed renewal (business decision), and what the dunning and retry settings should be.
65
+ - Before building Connect (marketplaces, payouts to others); it is a different model with onboarding and liability decisions.
66
+
67
+ ## Before you finish
68
+
69
+ Stripe's docs at docs.stripe.com and the API version pinned on your account are the final word on field names; this skill gives the rules and the shape. Test with `stripe listen --forward-to localhost:4242/webhook` and `stripe trigger`: card `4242 4242 4242 4242` succeeds, `4000 0025 0000 3155` requires 3D Secure, `4000 0000 0000 9995` declines for insufficient funds. Also test a replayed event (processed once), a bad signature (rejected with 400), a retried create call with the same idempotency key (one object), and a test-clock renewal that fails.
@@ -0,0 +1,16 @@
1
+ {
2
+ "should_trigger": [
3
+ "Integrate Stripe subscriptions into the billing page",
4
+ "Stripe webhook keeps failing with No signatures found matching the expected signature",
5
+ "Add a Stripe customer portal so users can cancel or change their plan",
6
+ "How do I use Stripe test clocks to check what happens when a trial ends?",
7
+ "Handle 3D Secure when charging a saved card off-session with Payment Intents"
8
+ ],
9
+ "should_not_trigger": [
10
+ "Add Flutterwave checkout to our Nigerian e-commerce site",
11
+ "Add Paystack checkout to our Next.js shop so Ghanaian customers can pay",
12
+ "Review the payment module for secret keys leaked into front-end code",
13
+ "Work out which parts of our card checkout fall inside PCI DSS scope",
14
+ "Design the REST API for our invoicing service"
15
+ ]
16
+ }
@@ -0,0 +1,71 @@
1
+ # Stripe API shapes used by this skill
2
+
3
+ Use the official SDK for your language and pin the API version your account uses. Every create/update call takes an idempotency key in the request options (`Idempotency-Key` header over HTTP).
4
+
5
+ Idempotency facts (API v1): keys up to 255 characters, UUID v4 recommended, don't put personal data in them. Stripe saves the first response for a key (including a `500`) and returns it on retries; reusing a key with different parameters is an error; keys can be pruned after 24 hours. Don't send keys on `GET` or `DELETE`.
6
+
7
+ ## Checkout Session (TypeScript, stripe-node)
8
+
9
+ ```ts
10
+ import Stripe from 'stripe';
11
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
12
+
13
+ export async function startCheckout(order: { id: string; priceId: string; quantity: number; idemKey: string }) {
14
+ const session = await stripe.checkout.sessions.create(
15
+ {
16
+ mode: 'payment',
17
+ line_items: [{ price: order.priceId, quantity: order.quantity }], // priceId from your DB, not the client
18
+ success_url: 'https://app.example.com/checkout/success?session_id={CHECKOUT_SESSION_ID}',
19
+ cancel_url: 'https://app.example.com/cart',
20
+ client_reference_id: order.id,
21
+ metadata: { order_id: order.id },
22
+ },
23
+ { idempotencyKey: order.idemKey }, // stored with the order, reused on retry
24
+ );
25
+ return session.url!;
26
+ }
27
+ ```
28
+
29
+ The success page may call `stripe.checkout.sessions.retrieve(session_id)` to show status, but fulfilment stays in the webhook. Check `session.payment_status === 'paid'` (or `'no_payment_required'`) before fulfilling; for delayed methods it is `'unpaid'` at completion and you wait for `checkout.session.async_payment_succeeded`.
30
+
31
+ ## Payment Intent
32
+
33
+ ```ts
34
+ const pi = await stripe.paymentIntents.create(
35
+ { amount: 2550, currency: 'usd', automatic_payment_methods: { enabled: true }, metadata: { order_id: order.id } },
36
+ { idempotencyKey: order.idemKey },
37
+ );
38
+ // send pi.client_secret to the browser; confirm there with stripe.confirmPayment({ elements, confirmParams: { return_url } })
39
+ ```
40
+
41
+ Statuses: `requires_payment_method` → `requires_confirmation` → `requires_action` (SCA / 3DS) → `processing` → `succeeded`, or `canceled`. Only `succeeded` (seen in `payment_intent.succeeded`) means paid. On `payment_intent.payment_failed`, read `last_payment_error` and let the customer try another method.
42
+
43
+ ## Off-session charge with a saved method
44
+
45
+ ```ts
46
+ try {
47
+ await stripe.paymentIntents.create(
48
+ { amount, currency, customer, payment_method, off_session: true, confirm: true },
49
+ { idempotencyKey },
50
+ );
51
+ } catch (err: any) {
52
+ if (err.code === 'authentication_required') {
53
+ // SCA needed: email the customer a link to an on-session page that confirms err.raw.payment_intent
54
+ } else throw err;
55
+ }
56
+ ```
57
+
58
+ ## Refund
59
+
60
+ ```ts
61
+ await stripe.refunds.create({ payment_intent: piId, amount: partialMinor }, { idempotencyKey: refundKey });
62
+ ```
63
+
64
+ ## Test cards
65
+
66
+ | Number | Behaviour |
67
+ |---|---|
68
+ | 4242 4242 4242 4242 | Succeeds |
69
+ | 4000 0025 0000 3155 | Requires 3D Secure authentication |
70
+ | 4000 0000 0000 9995 | Declined, insufficient funds |
71
+ | 4000 0000 0000 0002 | Declined, generic |
@@ -0,0 +1,58 @@
1
+ # Stripe subscriptions, customer portal and test clocks
2
+
3
+ ## Subscription via Checkout
4
+
5
+ ```ts
6
+ const session = await stripe.checkout.sessions.create(
7
+ {
8
+ mode: 'subscription',
9
+ customer: user.stripeCustomerId, // create the Customer once per user and store it
10
+ line_items: [{ price: plan.priceId, quantity: 1 }],
11
+ subscription_data: { trial_period_days: 14, metadata: { user_id: user.id } },
12
+ success_url: 'https://app.example.com/billing/success?session_id={CHECKOUT_SESSION_ID}',
13
+ cancel_url: 'https://app.example.com/billing',
14
+ },
15
+ { idempotencyKey },
16
+ );
17
+ ```
18
+
19
+ Store `session.subscription` from `checkout.session.completed`. From then on, the subscription `status` is the source of truth for access:
20
+
21
+ | `status` | Typical access |
22
+ |---|---|
23
+ | `trialing`, `active` | Full |
24
+ | `past_due` | Your policy: grace period while Stripe retries |
25
+ | `unpaid`, `canceled`, `incomplete_expired`, `paused` | None |
26
+ | `incomplete` | None until the first invoice is paid (often SCA pending) |
27
+
28
+ Sync it on `customer.subscription.created`, `.updated` and `.deleted`, and extend access to the period end on `invoice.paid`. On API version `2025-03-31.basil` and later, the period fields are on subscription items (`items.data[].current_period_end`), not on the subscription; older versions have `subscription.current_period_end`. Read the field for the API version you pinned. If events arrive out of order, retrieve the subscription from the API and store what it says.
29
+
30
+ Plan changes: `stripe.subscriptions.update(subId, { items: [{ id: itemId, price: newPriceId }], proration_behavior: 'create_prorations' })` with an idempotency key, or let the portal do it.
31
+
32
+ ## Customer portal
33
+
34
+ 1. Configure features (cancel, switch plan, update payment method, invoice history) in the Dashboard's customer portal settings, or with `stripe.billingPortal.configurations.create`.
35
+ 2. From an authenticated route:
36
+
37
+ ```ts
38
+ const portal = await stripe.billingPortal.sessions.create({
39
+ customer: currentUser.stripeCustomerId, // from your session, never from the request body
40
+ return_url: 'https://app.example.com/billing',
41
+ });
42
+ redirect(portal.url);
43
+ ```
44
+
45
+ Changes made in the portal arrive as the same subscription and invoice webhooks; no extra handling.
46
+
47
+ ## Test clocks
48
+
49
+ Test clocks simulate time for billing objects in test mode or a sandbox.
50
+
51
+ ```ts
52
+ const clock = await stripe.testHelpers.testClocks.create({ frozen_time: Math.floor(Date.now() / 1000) });
53
+ const customer = await stripe.customers.create({ email: 'trial@example.com', test_clock: clock.id });
54
+ // attach a test payment method, create the subscription, then:
55
+ await stripe.testHelpers.testClocks.advance(clock.id, { frozen_time: clock.frozen_time + 15 * 24 * 3600 });
56
+ ```
57
+
58
+ Advancing is asynchronous: wait for the clock's status to return to `ready` (or the `test_helpers.test_clock.ready` event) before asserting. Scenarios worth running: trial converts to paid, renewal succeeds, renewal fails with `4000 0000 0000 0341` attached (card attaches but charges fail), cancellation at period end takes effect. Only customers created with the clock move with it; delete the clock when done.
@@ -0,0 +1,76 @@
1
+ # Stripe webhooks
2
+
3
+ ## What Stripe sends
4
+
5
+ - `POST` with a JSON `Event` (snapshot payload) or a thin event notification, depending on how the event destination was created. Most existing integrations use snapshot events and read `event.data.object`.
6
+ - Header `Stripe-Signature: t=<timestamp>,v1=<hmac-sha256>[,v1=...]`. The signature is HMAC-SHA256 of `"<t>.<raw body>"` with the endpoint's `whsec_` secret. Only `v1` is valid; ignore other schemes.
7
+ - Live mode retries with exponential backoff for up to three days; sandboxes retry three times over a few hours. `3xx` counts as failure.
8
+ - Delivery is at least once and order is not guaranteed. Don't use `created` to order or dedupe; track `event.id`.
9
+ - Each endpoint, and test vs live, has its own signing secret. During a secret roll, two secrets may be active for up to 24 hours.
10
+
11
+ ## Handler rules
12
+
13
+ 1. Read the **raw body bytes**. Any body parser that runs first breaks verification.
14
+ 2. `stripe.webhooks.constructEvent(rawBody, signatureHeader, endpointSecret)`; on error respond `400` and stop. Keep the default 5-minute tolerance and keep server clocks synced.
15
+ 3. Respond `2xx` immediately, then process in a queue.
16
+ 4. In the worker: skip if `event.id` is already recorded; otherwise handle the type, fetch fresh objects from the API when you need current state, and record `event.id` in the same database transaction as the state change.
17
+ 5. Subscribe the endpoint only to the event types you handle.
18
+ 6. Exempt the route from CSRF middleware (Rails, Django, Laravel).
19
+
20
+ ## Express (TypeScript)
21
+
22
+ ```ts
23
+ import express from 'express';
24
+ import Stripe from 'stripe';
25
+
26
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
27
+ const app = express();
28
+
29
+ // Must be registered before app.use(express.json())
30
+ app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
31
+ let event: Stripe.Event;
32
+ try {
33
+ event = stripe.webhooks.constructEvent(req.body, req.header('stripe-signature') ?? '', process.env.STRIPE_WEBHOOK_SECRET!);
34
+ } catch {
35
+ return res.sendStatus(400);
36
+ }
37
+ res.sendStatus(200);
38
+ queue.add('stripe-event', { id: event.id, type: event.type, objectId: (event.data.object as { id: string }).id });
39
+ });
40
+ ```
41
+
42
+ ## Next.js route handler
43
+
44
+ ```ts
45
+ export async function POST(req: Request) {
46
+ const raw = await req.text(); // unparsed body
47
+ try {
48
+ const event = stripe.webhooks.constructEvent(raw, req.headers.get('stripe-signature') ?? '', process.env.STRIPE_WEBHOOK_SECRET!);
49
+ await enqueue(event.id, event.type);
50
+ } catch {
51
+ return new Response('invalid signature', { status: 400 });
52
+ }
53
+ return new Response('ok');
54
+ }
55
+ ```
56
+
57
+ ## Events to handle
58
+
59
+ | Event | Do |
60
+ |---|---|
61
+ | `checkout.session.completed` | If `payment_status` is `paid`, fulfil the order in `client_reference_id` / `metadata.order_id` once |
62
+ | `checkout.session.async_payment_succeeded` / `_failed` | Fulfil or fail delayed-method payments |
63
+ | `payment_intent.succeeded` / `payment_intent.payment_failed` | Custom Payment Element flows |
64
+ | `invoice.paid` | Extend subscription access to the new period |
65
+ | `invoice.payment_failed` | Start dunning; notify the customer |
66
+ | `customer.subscription.updated` / `customer.subscription.deleted` | Sync plan, status and cancellation |
67
+ | `charge.refunded`, `charge.dispute.created` | Reverse or flag fulfilment |
68
+
69
+ ## Local testing
70
+
71
+ ```bash
72
+ stripe login
73
+ stripe listen --forward-to localhost:4242/webhooks/stripe # prints a whsec_ for this session
74
+ stripe trigger checkout.session.completed
75
+ stripe events resend <event_id> --webhook-endpoint=<endpoint_id>
76
+ ```
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: subscription-billing-ops
3
+ description: Runs the day-to-day mechanics of recurring revenue in any billing system, whether a provider's billing product or your own ledger, covering plan and price changes with grandfathering, proration on upgrades and downgrades, trials and trial conversion, failed-payment dunning and retry schedules, invoice and credit note lifecycles, cancellation and refund policy, and basic MRR, churn and deferred revenue reporting. Use when designing or changing how plans are billed, migrating customers to a new price, deciding what a mid-cycle upgrade should cost, building a dunning flow, issuing credit notes, or reconciling MRR numbers that don't add up.
4
+ license: MIT
5
+ compatibility: Any Agent Skills host. Provider-neutral; for Stripe-specific API calls also load stripe-integration. Examples use SQL and TypeScript-like pseudocode.
6
+ metadata:
7
+ kete-loom/version: "0.1.0"
8
+ kete-loom/category: business
9
+ kete-loom/risk: writes-files
10
+ kete-loom/owner: "Kete-org"
11
+ ---
12
+
13
+ # Subscription billing operations
14
+
15
+ Make every recurring charge explainable: a customer, finance or an auditor should be able to take any invoice and see which plan, price version, period and proration rule produced each line. This skill covers billing behaviour and data, not a provider's API. For Stripe calls use `stripe-integration`; for African payment providers use their own skills; for invoice legal fields and tax use `invoicing-and-tax`.
16
+
17
+ ## Ground rules
18
+
19
+ 1. **Money is integer minor units plus an ISO 4217 currency.** Never floats. Round once, at a defined step, with a documented rule (see `references/proration.md`).
20
+ 2. **Prices are versioned and immutable.** A price change creates a new price row; existing subscriptions keep pointing at the old one until you move them deliberately. Never edit an amount in place.
21
+ 3. **Issued invoices are immutable.** Fix a finalised invoice with a credit note (full or partial) and, if needed, a new invoice. Never edit or delete it. Drafts can change; finalised cannot.
22
+ 4. **Periods are half-open instants in UTC**: `[period_start, period_end)`. Store the customer's billing anchor and timezone separately and only use them to compute the next boundary and to display dates.
23
+ 5. **One subscription state machine, one source of truth.** If a provider runs billing, its subscription status drives access; your copy is a cache refreshed from its events. If you run billing, every state change is a row in an append-only event log.
24
+ 6. **Every charge attempt is idempotent**, keyed on `(invoice_id, attempt_number)`, so a retried job never charges twice.
25
+ 7. **Revenue reporting reads from invoices and credit notes, not from the plan table.** MRR from list prices lies whenever there are discounts, credits or failed payments.
26
+
27
+ ## Workflow
28
+
29
+ 1. **Map what exists.** Search the codebase for the plan/price, subscription, invoice, credit note and payment tables or provider objects, the renewal job or webhook handlers, and where access is granted or revoked. Write a one-line flow: `signup → trial → first invoice → renewal → dunning → cancel`.
30
+ 2. **Identify who computes what.** Decide per concern whether the provider or your code owns it: proration, tax, dunning retries, invoice numbering, emails. Two owners for one concern is the most common source of double charges and mismatched totals. Stop and ask the user if it is unclear.
31
+ 3. **Make the change using the rules for that area below.**
32
+ 4. **Write tests with a fake clock** for each boundary you touched: trial end, renewal on the 31st, leap day, DST change in the customer's timezone, upgrade one second before renewal, downgrade, cancel at period end, failed payment then recovery.
33
+ 5. **Backfill or migrate with a dry run first** (see "Migrating prices"). Print the before and after per customer, get approval, then run.
34
+
35
+ ## Plans and price changes
36
+
37
+ - New customers get the new price immediately. Existing customers: choose **grandfather** (stay on old price), **migrate at next renewal** (with notice), or **migrate now** (prorated). This is a business and sometimes legal decision: stop and ask the user, and ask what notice period applies.
38
+ - Keep a `price_version` or price ID on every subscription item and invoice line.
39
+ - Plan limits and features live in an entitlements table keyed by price, not hard-coded per plan name.
40
+
41
+ ## Proration
42
+
43
+ Decide and document per change type. Defaults that rarely surprise customers:
44
+
45
+ | Change | Default |
46
+ |---|---|
47
+ | Upgrade mid-cycle | Charge the prorated difference now (credit unused old time, charge remaining new time), keep the anchor |
48
+ | Downgrade mid-cycle | Apply at period end; no refund |
49
+ | Quantity increase (seats) | Prorate now, or true-up at next invoice; pick one |
50
+ | Quantity decrease | Apply at period end |
51
+ | Billing interval change (monthly → annual) | Credit unused time, start a new period now, reset anchor |
52
+
53
+ Compute proration by seconds (or days, if you document it) of the half-open period. Worked examples, rounding rules and edge cases in `references/proration.md`.
54
+
55
+ ## Trials
56
+
57
+ - Store `trial_end` as an instant. At `trial_end`, either convert (first invoice, charge) or end (no access), never both by different jobs.
58
+ - Collect a payment method up front or not: it changes conversion and dunning. Ask the user if not decided.
59
+ - Send a reminder before conversion where the law or card network rules require it; check local rules for the markets you sell in and do not assume.
60
+ - A trial extension moves `trial_end`; it never creates a zero-amount invoice that later looks like revenue.
61
+
62
+ ## Dunning and retries
63
+
64
+ 1. On a failed charge, keep the invoice `open` (unpaid), move the subscription to `past_due` (or the provider's equivalent), and schedule retries. A common schedule is 3 to 4 retries over 1 to 3 weeks; let the provider's smart retries run if it has them, and don't add your own on top.
65
+ 2. Notify the customer on the first failure with a link to update the payment method; retry immediately when they update it.
66
+ 3. Classify declines: hard declines (stolen card, closed account) stop retrying and ask for a new method; soft declines (insufficient funds, issuer unavailable) retry.
67
+ 4. Decide the end state after the last retry: cancel, pause, or leave unpaid with access cut. Stop and ask the user; it is a policy decision.
68
+ 5. Grace period: access during dunning is a policy setting, not an accident of which webhook arrived first.
69
+ 6. When a past-due invoice is finally paid, restore access and do not reset the billing anchor unless the policy says so.
70
+
71
+ ## Invoices and credit notes
72
+
73
+ - Lifecycle: `draft → open (finalised, numbered) → paid | void | uncollectible`. Number on finalisation, from a gap-free sequence per legal entity if the jurisdiction requires it.
74
+ - Credit notes reference the original invoice, carry their own number, and reverse tax in the same proportion as the lines they credit.
75
+ - Refund and credit are different things: a credit note reduces what is owed; a refund moves money back. A refund of a paid invoice usually needs both.
76
+ - Void only an invoice that was never sent or paid, if your jurisdiction allows it; otherwise credit it.
77
+
78
+ ## Cancellations
79
+
80
+ - Offer `cancel at period end` (default) and `cancel now`. Cancel now needs a refund rule: none, prorated credit, or prorated refund. Ask the user.
81
+ - Cancellation stops future invoices; it does not void open ones. Decide whether open past-due invoices are still collected.
82
+ - Make cancellation as easy as signup; several jurisdictions have rules on this. Do not build dark patterns.
83
+
84
+ ## Revenue reporting basics
85
+
86
+ - **MRR**: normalise each active, paid subscription's recurring invoice amount after discounts to a month (annual ÷ 12). Exclude one-off charges, tax and usage unless you report them separately.
87
+ - **MRR movements** per month: new, expansion, contraction, churn, reactivation. They must sum from opening to closing MRR; if they don't, there is a bug.
88
+ - **Churn**: logo churn (customers lost ÷ customers at start) and revenue churn (MRR lost ÷ MRR at start), each defined in writing.
89
+ - **Deferred revenue**: an annual prepayment is earned monthly. Reporting it as revenue on the day paid is wrong under accrual accounting. Recognition policy (ASC 606, IFRS 15) belongs to the finance team; give them the invoice and period data, don't decide policy in code.
90
+
91
+ Queries for these in `references/revenue-metrics.md`.
92
+
93
+ ## Migrating prices
94
+
95
+ 1. Select affected subscriptions; write the old and new amount, next invoice date and proration per customer to a file.
96
+ 2. Show totals and a sample to the user; stop and wait for approval.
97
+ 3. Send required notice before the change takes effect.
98
+ 4. Run in batches with idempotency keys, logging each subscription ID and result; make the script resumable.
99
+ 5. Reconcile: next-cycle invoice totals must match the dry-run file.
100
+
101
+ ## Never
102
+
103
+ - Never charge, refund or migrate live customers without explicit approval for that run.
104
+ - Never recompute an issued invoice from current prices.
105
+ - Never let two systems (yours and the provider's) both send dunning emails or both compute proration.
106
+ - Never use the server's local timezone for period maths.
107
+
108
+ ## Report format
109
+
110
+ When asked to design or review a billing change, return:
111
+
112
+ ```
113
+ Change: <one line>
114
+ Owner per concern: proration=<provider|app>, tax=..., dunning=..., numbering=...
115
+ Customer impact: <who pays what, when; one example invoice>
116
+ Edge cases tested: <list>
117
+ Decisions needed from the user: <list, or none>
118
+ ```