skilldrop-cli 0.16.34 → 0.16.36

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 (212) hide show
  1. package/README.md +1 -0
  2. package/agents/README.md +4 -1
  3. package/agents/docs-reviewer.md +47 -0
  4. package/agents/finding-adjudicator.md +49 -0
  5. package/agents/test-reviewer.md +52 -0
  6. package/catalogue.json +17 -7
  7. package/model-routing.json +40 -0
  8. package/package.json +1 -1
  9. package/packs/ai-engineering/skills/agent-config-review/SKILL.md +116 -0
  10. package/packs/ai-engineering/skills/agent-config-review/evals/eval_queries.json +12 -0
  11. package/packs/ai-engineering/skills/agent-config-review/evals/evals.json +75 -0
  12. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/.claude/agents/db-migrator.md +8 -0
  13. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/.claude/agents/reviewer.md +9 -0
  14. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/.claude/settings.json +48 -0
  15. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/.claude/settings.local.json +5 -0
  16. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/.mcp.json +22 -0
  17. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/package.json +12 -0
  18. package/packs/ai-engineering/skills/agent-config-review/evals/files/2/.cursor/mcp.json +17 -0
  19. package/packs/ai-engineering/skills/agent-config-review/evals/files/2/config.toml +21 -0
  20. package/packs/ai-engineering/skills/agent-config-review/evals/files/4/my-user-settings.json +11 -0
  21. package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/.claude/agents/release-notes.md +13 -0
  22. package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/.claude/settings.json +39 -0
  23. package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/.mcp.json +18 -0
  24. package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/package.json +14 -0
  25. package/packs/ai-engineering/skills/agent-config-review/evals/files/5/.github/workflows/codex-triage.yml +29 -0
  26. package/packs/ai-engineering/skills/agent-config-review/evals/files/5/AGENTS.md +16 -0
  27. package/packs/ai-engineering/skills/agent-config-review/evals/files/5/codex/config.toml +34 -0
  28. package/packs/ai-engineering/skills/agent-config-review/examples/inputs/tidewater-ingest/.claude/settings.json +22 -0
  29. package/packs/ai-engineering/skills/agent-config-review/examples/inputs/tidewater-ingest/.mcp.json +9 -0
  30. package/packs/ai-engineering/skills/agent-config-review/examples/python-repo-tighten.md +81 -0
  31. package/packs/ai-engineering/skills/agent-config-review/manifest.json +60 -0
  32. package/packs/ai-engineering/skills/agent-config-review/reference.md +205 -0
  33. package/packs/ai-engineering/skills/agent-config-review/scripts/agent_config_scan.py +989 -0
  34. package/packs/ai-engineering/skills/agent-incident-response/SKILL.md +175 -0
  35. package/packs/ai-engineering/skills/agent-incident-response/evals/eval_queries.json +14 -0
  36. package/packs/ai-engineering/skills/agent-incident-response/evals/evals.json +107 -0
  37. package/packs/ai-engineering/skills/agent-incident-response/evals/files/1/agent-session-excerpt.txt +11 -0
  38. package/packs/ai-engineering/skills/agent-incident-response/evals/files/1/ci-build-log.txt +24 -0
  39. package/packs/ai-engineering/skills/agent-incident-response/evals/files/1/incident-notes.md +19 -0
  40. package/packs/ai-engineering/skills/agent-incident-response/evals/files/2/mcp-config.json +20 -0
  41. package/packs/ai-engineering/skills/agent-incident-response/evals/files/2/session-transcript.txt +8 -0
  42. package/packs/ai-engineering/skills/agent-incident-response/evals/files/4/agent-session.log +70 -0
  43. package/packs/ai-engineering/skills/agent-incident-response/evals/files/4/infra/staging-db.tf +25 -0
  44. package/packs/ai-engineering/skills/agent-incident-response/evals/files/5/jobs/nightly-triage/job.yaml +19 -0
  45. package/packs/ai-engineering/skills/agent-incident-response/evals/files/5/jobs/nightly-triage/run.log +13 -0
  46. package/packs/ai-engineering/skills/agent-incident-response/evals/files/5/usage-export-2026-10-20.csv +13 -0
  47. package/packs/ai-engineering/skills/agent-incident-response/evals/files/6/.mcp.json +20 -0
  48. package/packs/ai-engineering/skills/agent-incident-response/evals/files/6/git-log.txt +12 -0
  49. package/packs/ai-engineering/skills/agent-incident-response/evals/files/6/notesync-excerpt.txt +30 -0
  50. package/packs/ai-engineering/skills/agent-incident-response/evals/files/6/notesync.log +15 -0
  51. package/packs/ai-engineering/skills/agent-incident-response/evals/files/6/ssh-config.txt +16 -0
  52. package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/.github/workflows/ci.yml +28 -0
  53. package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/SECURITY.md +8 -0
  54. package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/issue-479.md +35 -0
  55. package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/package.json +15 -0
  56. package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/pr-482.diff +20 -0
  57. package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/run-log.txt +30 -0
  58. package/packs/ai-engineering/skills/agent-incident-response/examples/injected-issue-comment.md +57 -0
  59. package/packs/ai-engineering/skills/agent-incident-response/manifest.json +55 -0
  60. package/packs/ai-engineering/skills/agent-incident-response/reference.md +152 -0
  61. package/packs/ai-engineering/skills/agent-incident-response/scripts/find_secret_exposure.py +239 -0
  62. package/packs/ai-engineering/skills/agent-incident-response/scripts/usage_split.py +194 -0
  63. package/packs/converters/skills/file-to-markdown/SKILL.md +3 -2
  64. package/packs/converters/skills/file-to-markdown/evals/eval_queries.json +4 -0
  65. package/packs/converters/skills/file-to-markdown/manifest.json +2 -2
  66. package/packs/converters/skills/file-to-markdown/scripts/to_markdown.py +340 -7
  67. package/packs/converters/skills/md-to-confluence/SKILL.md +139 -0
  68. package/packs/converters/skills/md-to-confluence/evals/eval_queries.json +42 -0
  69. package/packs/converters/skills/md-to-confluence/evals/evals.json +89 -0
  70. package/packs/converters/skills/md-to-confluence/evals/files/1/img/latency-dashboard.png +0 -0
  71. package/packs/converters/skills/md-to-confluence/evals/files/1/payments-runbook.md +60 -0
  72. package/packs/converters/skills/md-to-confluence/evals/files/2/deploy-checklist.md +33 -0
  73. package/packs/converters/skills/md-to-confluence/evals/files/3/img/after/screen.png +0 -0
  74. package/packs/converters/skills/md-to-confluence/evals/files/3/img/before/screen.png +0 -0
  75. package/packs/converters/skills/md-to-confluence/evals/files/3/release-notes-4.2.md +49 -0
  76. package/packs/converters/skills/md-to-confluence/evals/files/4/docs/img/topology.png +0 -0
  77. package/packs/converters/skills/md-to-confluence/evals/files/4/docs/ingest-pipeline-design.md +65 -0
  78. package/packs/converters/skills/md-to-confluence/evals/files/5/support/troubleshooting-sso.md +57 -0
  79. package/packs/converters/skills/md-to-confluence/examples/inputs/img/queue-depth.png +0 -0
  80. package/packs/converters/skills/md-to-confluence/examples/inputs/queue-runbook.fixed.md +21 -0
  81. package/packs/converters/skills/md-to-confluence/examples/inputs/queue-runbook.md +21 -0
  82. package/packs/converters/skills/md-to-confluence/examples/queue-runbook.md +57 -0
  83. package/packs/converters/skills/md-to-confluence/manifest.json +52 -0
  84. package/packs/converters/skills/md-to-confluence/reference.md +92 -0
  85. package/packs/converters/skills/md-to-confluence/scripts/md_to_confluence.py +821 -0
  86. package/packs/converters/skills/md-to-pptx/SKILL.md +131 -0
  87. package/packs/converters/skills/md-to-pptx/evals/eval_queries.json +12 -0
  88. package/packs/converters/skills/md-to-pptx/evals/evals.json +84 -0
  89. package/packs/converters/skills/md-to-pptx/evals/files/1/img/defect-burndown.png +0 -0
  90. package/packs/converters/skills/md-to-pptx/evals/files/1/status-report.md +75 -0
  91. package/packs/converters/skills/md-to-pptx/evals/files/3/brand.json +12 -0
  92. package/packs/converters/skills/md-to-pptx/evals/files/3/night-shift-induction.md +53 -0
  93. package/packs/converters/skills/md-to-pptx/evals/files/4/charts/ontime.png +0 -0
  94. package/packs/converters/skills/md-to-pptx/evals/files/4/qbr-q3.md +85 -0
  95. package/packs/converters/skills/md-to-pptx/evals/files/5/vendor-sync-notes.md +28 -0
  96. package/packs/converters/skills/md-to-pptx/examples/support-weekly-input.md +48 -0
  97. package/packs/converters/skills/md-to-pptx/examples/support-weekly.md +75 -0
  98. package/packs/converters/skills/md-to-pptx/examples/tickets-by-week.png +0 -0
  99. package/packs/converters/skills/md-to-pptx/manifest.json +49 -0
  100. package/packs/converters/skills/md-to-pptx/reference.md +139 -0
  101. package/packs/converters/skills/md-to-pptx/scripts/md_to_pptx.py +1841 -0
  102. package/packs/dev-team/loops/modernize/LOOP.md +124 -0
  103. package/packs/dev-team/loops/modernize/evals/eval_queries.json +9 -0
  104. package/packs/dev-team/loops/modernize/evals/evals.json +27 -0
  105. package/packs/dev-team/loops/modernize/loop.json +60 -0
  106. package/packs/dev-team/skills/behaviour-equivalence/SKILL.md +155 -0
  107. package/packs/dev-team/skills/behaviour-equivalence/evals/eval_queries.json +11 -0
  108. package/packs/dev-team/skills/behaviour-equivalence/evals/evals.json +76 -0
  109. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/cases.jsonl +12 -0
  110. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/new.jsonl +12 -0
  111. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/notes.md +15 -0
  112. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/old.jsonl +12 -0
  113. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/old_rerun.jsonl +12 -0
  114. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/pricing_v1.py +45 -0
  115. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/pricing_v2.py +65 -0
  116. package/packs/dev-team/skills/behaviour-equivalence/evals/files/1/rules.json +10 -0
  117. package/packs/dev-team/skills/behaviour-equivalence/evals/files/3/diff-summary.txt +16 -0
  118. package/packs/dev-team/skills/behaviour-equivalence/evals/files/4/diff.txt +13 -0
  119. package/packs/dev-team/skills/behaviour-equivalence/evals/files/4/fee_calc_py2.py +16 -0
  120. package/packs/dev-team/skills/behaviour-equivalence/evals/files/4/fee_calc_py3.py +24 -0
  121. package/packs/dev-team/skills/behaviour-equivalence/evals/files/4/input.csv +13 -0
  122. package/packs/dev-team/skills/behaviour-equivalence/evals/files/4/new_output.csv +13 -0
  123. package/packs/dev-team/skills/behaviour-equivalence/evals/files/4/old_output.csv +13 -0
  124. package/packs/dev-team/skills/behaviour-equivalence/evals/files/5/TEAM_NOTES.md +19 -0
  125. package/packs/dev-team/skills/behaviour-equivalence/evals/files/5/normalisation.yaml +12 -0
  126. package/packs/dev-team/skills/behaviour-equivalence/evals/files/5/recordings/new_run.jsonl +8 -0
  127. package/packs/dev-team/skills/behaviour-equivalence/evals/files/5/recordings/old_run1.jsonl +8 -0
  128. package/packs/dev-team/skills/behaviour-equivalence/evals/files/5/recordings/old_run2.jsonl +8 -0
  129. package/packs/dev-team/skills/behaviour-equivalence/examples/delivery-date.md +78 -0
  130. package/packs/dev-team/skills/behaviour-equivalence/manifest.json +33 -0
  131. package/packs/dev-team/skills/behaviour-equivalence/reference.md +97 -0
  132. package/packs/dev-team/skills/behaviour-equivalence/scripts/golden_diff.py +524 -0
  133. package/packs/dev-team/skills/behaviour-equivalence/templates/accepted.json +10 -0
  134. package/packs/dev-team/skills/behaviour-equivalence/templates/rules.json +7 -0
  135. package/packs/frontend/pack.json +55 -0
  136. package/packs/frontend/skills/rendering-strategy/SKILL.md +149 -0
  137. package/packs/frontend/skills/rendering-strategy/evals/eval_queries.json +38 -0
  138. package/packs/frontend/skills/rendering-strategy/evals/evals.json +77 -0
  139. package/packs/frontend/skills/rendering-strategy/evals/files/1/fernhill-app.md +40 -0
  140. package/packs/frontend/skills/rendering-strategy/evals/files/3/routes.md +33 -0
  141. package/packs/frontend/skills/rendering-strategy/evals/files/4/build-notes.txt +5 -0
  142. package/packs/frontend/skills/rendering-strategy/evals/files/4/nuxt.config.ts +19 -0
  143. package/packs/frontend/skills/rendering-strategy/evals/files/4/pages/recipes/[slug].vue +37 -0
  144. package/packs/frontend/skills/rendering-strategy/evals/files/4/routes.md +15 -0
  145. package/packs/frontend/skills/rendering-strategy/evals/files/5/routes.txt +18 -0
  146. package/packs/frontend/skills/rendering-strategy/evals/files/5/src/hooks.server.ts +14 -0
  147. package/packs/frontend/skills/rendering-strategy/evals/files/5/src/routes/(marketing)/pricing/+page.server.ts +10 -0
  148. package/packs/frontend/skills/rendering-strategy/evals/files/5/src/routes/+layout.server.ts +10 -0
  149. package/packs/frontend/skills/rendering-strategy/evals/files/5/svelte.config.js +10 -0
  150. package/packs/frontend/skills/rendering-strategy/evals/files/5/traffic.md +18 -0
  151. package/packs/frontend/skills/rendering-strategy/examples/saas-site.md +42 -0
  152. package/packs/frontend/skills/rendering-strategy/manifest.json +50 -0
  153. package/packs/frontend/skills/rendering-strategy/reference.md +116 -0
  154. package/packs/frontend/skills/rendering-strategy/scripts/render_load.py +181 -0
  155. package/packs/frontend/skills/web-performance-review/SKILL.md +106 -0
  156. package/packs/frontend/skills/web-performance-review/evals/eval_queries.json +38 -0
  157. package/packs/frontend/skills/web-performance-review/evals/evals.json +76 -0
  158. package/packs/frontend/skills/web-performance-review/evals/files/1/psi.json +1065 -0
  159. package/packs/frontend/skills/web-performance-review/evals/files/2/after.json +359 -0
  160. package/packs/frontend/skills/web-performance-review/evals/files/2/before.json +375 -0
  161. package/packs/frontend/skills/web-performance-review/evals/files/4/psi-pdp-mobile-2026-10-19.json +171 -0
  162. package/packs/frontend/skills/web-performance-review/evals/files/4/rum-inp-attribution-oct14-19.csv +6 -0
  163. package/packs/frontend/skills/web-performance-review/evals/files/4/rum-inp-daily.csv +15 -0
  164. package/packs/frontend/skills/web-performance-review/evals/files/5/crux-home-phone.json +35 -0
  165. package/packs/frontend/skills/web-performance-review/evals/files/5/lighthouse-home-mobile.json +173 -0
  166. package/packs/frontend/skills/web-performance-review/examples/quayside-article-lhr.json +480 -0
  167. package/packs/frontend/skills/web-performance-review/examples/quayside-article.md +103 -0
  168. package/packs/frontend/skills/web-performance-review/manifest.json +48 -0
  169. package/packs/frontend/skills/web-performance-review/reference.md +105 -0
  170. package/packs/frontend/skills/web-performance-review/scripts/lh_summary.py +743 -0
  171. package/packs/product-manager/skills/lean-canvas/SKILL.md +162 -0
  172. package/packs/product-manager/skills/lean-canvas/evals/eval_queries.json +42 -0
  173. package/packs/product-manager/skills/lean-canvas/evals/evals.json +82 -0
  174. package/packs/product-manager/skills/lean-canvas/evals/files/1/pawpass-notes.md +54 -0
  175. package/packs/product-manager/skills/lean-canvas/evals/files/3/lunchloop-pitch.md +30 -0
  176. package/packs/product-manager/skills/lean-canvas/evals/files/4/clubkit-notes.md +44 -0
  177. package/packs/product-manager/skills/lean-canvas/evals/files/5/cost-model.csv +5 -0
  178. package/packs/product-manager/skills/lean-canvas/evals/files/5/proposal.md +40 -0
  179. package/packs/product-manager/skills/lean-canvas/evals/files/5/stakeholder-emails.txt +18 -0
  180. package/packs/product-manager/skills/lean-canvas/examples/bike-repair.md +72 -0
  181. package/packs/product-manager/skills/lean-canvas/manifest.json +34 -0
  182. package/packs/product-manager/skills/lean-canvas/reference.md +102 -0
  183. package/packs/product-manager/skills/lean-canvas/scripts/unit_economics.py +211 -0
  184. package/packs/solution-architect/skills/api-contract-draft/SKILL.md +2 -2
  185. package/packs/solution-architect/skills/api-contract-draft/manifest.json +28 -6
  186. package/packs/solution-architect/skills/event-contract-draft/SKILL.md +167 -0
  187. package/packs/solution-architect/skills/event-contract-draft/evals/eval_queries.json +38 -0
  188. package/packs/solution-architect/skills/event-contract-draft/evals/evals.json +76 -0
  189. package/packs/solution-architect/skills/event-contract-draft/evals/files/1/order-events-brief.md +45 -0
  190. package/packs/solution-architect/skills/event-contract-draft/evals/files/4/NOTES.md +23 -0
  191. package/packs/solution-architect/skills/event-contract-draft/evals/files/4/schemas/shipment-update.json +40 -0
  192. package/packs/solution-architect/skills/event-contract-draft/evals/files/5/invoice.example.json +25 -0
  193. package/packs/solution-architect/skills/event-contract-draft/examples/order-service.asyncapi.json +246 -0
  194. package/packs/solution-architect/skills/event-contract-draft/examples/order-service.md +92 -0
  195. package/packs/solution-architect/skills/event-contract-draft/manifest.json +25 -0
  196. package/packs/solution-architect/skills/event-contract-draft/reference.md +113 -0
  197. package/packs/solution-architect/skills/event-contract-draft/scripts/check_asyncapi.py +502 -0
  198. package/packs/solution-architect/skills/event-contract-draft/templates/asyncapi-skeleton.json +144 -0
  199. package/packs/sre-oncall/skills/slo-spec/SKILL.md +161 -0
  200. package/packs/sre-oncall/skills/slo-spec/evals/eval_queries.json +38 -0
  201. package/packs/sre-oncall/skills/slo-spec/evals/evals.json +75 -0
  202. package/packs/sre-oncall/skills/slo-spec/evals/files/1/order-service.md +51 -0
  203. package/packs/sre-oncall/skills/slo-spec/evals/files/3/search-slo-status.md +30 -0
  204. package/packs/sre-oncall/skills/slo-spec/evals/files/4/alerts/payments-api.rules.yaml +16 -0
  205. package/packs/sre-oncall/skills/slo-spec/evals/files/4/docs/dependencies.md +8 -0
  206. package/packs/sre-oncall/skills/slo-spec/evals/files/4/metrics/summary-90d.md +51 -0
  207. package/packs/sre-oncall/skills/slo-spec/evals/files/5/ops-notes.md +34 -0
  208. package/packs/sre-oncall/skills/slo-spec/examples/ticket-booking.md +88 -0
  209. package/packs/sre-oncall/skills/slo-spec/manifest.json +34 -0
  210. package/packs/sre-oncall/skills/slo-spec/reference.md +113 -0
  211. package/packs/sre-oncall/skills/slo-spec/scripts/slo_math.py +449 -0
  212. package/packs/sre-oncall/skills/slo-spec/templates/slo-spec.md +35 -0
@@ -0,0 +1,102 @@
1
+ # lean-canvas reference
2
+
3
+ Long-form material for the steps in `SKILL.md`. The canvas is Ash Maurya's adaptation of the
4
+ Business Model Canvas (*Running Lean*); the interview rules follow Rob Fitzpatrick's *The Mom
5
+ Test*.
6
+
7
+ ## The nine boxes, in the order to fill them
8
+
9
+ | Box | What goes in | Common wrong entry |
10
+ |---|---|---|
11
+ | Problem | The top 1–3 problems, and how the early adopter copes today (existing alternatives) | A solution phrased as a problem: "no app for X" |
12
+ | Customer segments | Who has the problem; the **early adopter** named separately, narrow enough to find by name | "Small businesses", "everyone who…" |
13
+ | Unique value proposition | One sentence: why this, for the early adopter, instead of what they do today | A feature list |
14
+ | Solution | The smallest thing that addresses each problem | A roadmap |
15
+ | Channels | The paths to the early adopter, free and paid | Channels that reach the later segment |
16
+ | Revenue streams | Price, who pays, how often | "Freemium" with no paid tier defined |
17
+ | Cost structure | Cost to serve one customer, fixed costs, acquisition cost | Fixed costs only |
18
+ | Key metrics | The 1–3 numbers that show the model works (activation, retention, paid conversion) | Downloads, page views |
19
+ | Unfair advantage | What a funded competitor can't copy or buy within a year | A feature, "AI", "first mover", "passion" |
20
+
21
+ **Internal products.** Revenue streams = the budget line or measured saving that keeps it
22
+ funded. Customer = the team that has to change how it works; early adopter = the one team
23
+ with the worst version of the problem and a lead who asked for help. Channel = how that team
24
+ hears about and gets onto the tool (a mandate, a team lead, an existing workflow it plugs
25
+ into). Unfair advantage can be real here: access to internal data or a mandate no vendor
26
+ has. Still test whether the team will change its habits.
27
+
28
+ ## Grading evidence
29
+
30
+ | Signal | Grade |
31
+ |---|---|
32
+ | Paid, pre-ordered, signed a letter of intent, or switched from a current tool | Behaviour — strongest |
33
+ | Describes a recent, specific instance of the problem unprompted, and what it cost | Behaviour about the past — strong |
34
+ | Built a workaround (spreadsheet, hired someone) | Behaviour — strong |
35
+ | "I'd definitely use it", "great idea", survey interest | Opinion — weak, counts as assumption |
36
+ | Industry statistic or report | Sizes the market; says nothing about this segment's willingness to pay |
37
+ | Any of the above from a segment other than the early adopter | Counts as assumption for the early adopter |
38
+
39
+ ## Scoring risk
40
+
41
+ | | Fatal (3) | Pivot (2) | Tune (1) |
42
+ |---|---|---|---|
43
+ | **High (3)** — no evidence, wrong segment, opinion only | 9 | 6 | 3 |
44
+ | **Medium (2)** — right segment, few people or indirect | 6 | 4 | 2 |
45
+ | **Low (1)** — observed behaviour, right segment | 3 | 2 | 1 |
46
+
47
+ Ties: rank the assumption whose test is cheaper first; it clears the way for the rest.
48
+
49
+ Why problem, customer and channel usually top the list: a wrong solution is fixed by
50
+ rebuilding it, which a team can do in weeks. A wrong problem or an unreachable customer means
51
+ the next build lands on nobody, and no amount of engineering moves it.
52
+
53
+ ## Test menu, cheapest first
54
+
55
+ | Test | Proves wrong | Typical sample | Typical cost and time |
56
+ |---|---|---|---|
57
+ | Problem interview | The problem, the segment | 10–20 early adopters | $0–$200, 1–2 weeks |
58
+ | Channel smoke test | The channel and its cost | $100–$500 of spend or one week of outreach on one channel | the spend, 1 week |
59
+ | Landing page with a price | The UVP and the price | 100+ visitors from the stated channel | $100–$500, 1–2 weeks |
60
+ | Concierge run (deliver it by hand) | The solution and retention | 3–10 customers | founder time, 2–4 weeks |
61
+ | Pre-sale or letter of intent | Willingness to pay | 5–20 asks | $0, 2–4 weeks |
62
+ | Wizard of Oz (looks automated, done by hand) | The solution at small volume | 10–30 uses | founder time, 2–4 weeks |
63
+
64
+ A survey and "build an MVP and see" are not on the menu: the first measures opinion, the
65
+ second costs months to learn what an interview learns in a week.
66
+
67
+ ## Setting the pass/fail line
68
+
69
+ - **Write it before the test runs**, as a count: "fewer than 4 of 10". A line set after the
70
+ results arrive moves to fit them.
71
+ - **Take it from the numbers when they exist.** Channel test fail line = the most a customer
72
+ can cost to acquire (lifetime margin ÷ 3 for a business that has to cover fixed costs;
73
+ lifetime margin at break-even). Landing-page fail line = the conversion that would
74
+ produce that cost per customer at the channel's cost per visitor.
75
+ - **No numbers yet:** a problem interview passes when at least 4 in 10 describe a recent
76
+ instance unprompted; a pre-sale passes when at least 1 in 5 pays or signs.
77
+ - **Say what failure changes:** which box is rewritten, and what the next test is.
78
+
79
+ ## Problem-interview rules
80
+
81
+ 1. Ask about the past, never the future. ✅ "When did that last happen? What did you do?"
82
+ ❌ "Would you use an app that…?"
83
+ 2. Don't pitch until the end, if at all. The moment the idea is described, people are polite.
84
+ 3. Ask what it cost them: time, money, a lost customer.
85
+ 4. Ask what they do about it today and what that costs. No workaround is a warning sign.
86
+ 5. Count who raised the problem **before** it was named, separately from who agreed once asked.
87
+
88
+ Five-question script: "Walk me through your last [day/week/job]." / "What was the hardest
89
+ part?" / "When did [problem area] last go wrong? What happened?" / "What did you do about
90
+ it, and what did that cost?" / "What have you tried to fix it?"
91
+
92
+ ## What counts as an unfair advantage
93
+
94
+ Counts: insider information, an existing audience or customer base, a network effect,
95
+ a community, exclusive contracts or access, regulatory permission others lack, a team
96
+ member whose name opens doors in this market, internal data or a mandate (internal products).
97
+
98
+ Doesn't count: a feature, an algorithm, "AI", a patent pending on a feature, first-mover,
99
+ better UX, lower price, hard work, passion. These go in Solution or UVP.
100
+
101
+ "None yet" is the most common correct answer at this stage. Name a candidate the idea
102
+ could grow into, if the input supports one.
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env python3
2
+ """Unit-economics sanity check for a lean canvas.
3
+
4
+ Stdlib only; runs on Python 3.9+. Computes, never estimates:
5
+
6
+ * contribution (gross margin) per customer per period, and as a % of price;
7
+ * customer lifetime in periods (given directly, or 1 / churn);
8
+ * lifetime gross margin (LTV) and LTV:CAC;
9
+ * CAC payback in periods, and whether it is longer than the lifetime;
10
+ * the most a customer can cost to acquire: at break-even (LTV) and at 3:1 (LTV / 3);
11
+ * break-even customers for the fixed costs, and the customers a stated
12
+ market can supply, when those are given.
13
+
14
+ All money figures share one currency and one period (default "month"). Convert
15
+ an annual price to monthly before passing it, or pass --period year with
16
+ annual figures throughout.
17
+
18
+ Usage:
19
+ python3 unit_economics.py --price 29 --variable-cost 6 --cac 400 \
20
+ --lifetime 9 --fixed 4300
21
+ python3 unit_economics.py --price 29 --variable-cost 6 --cac 400 --churn 0.11
22
+ python3 unit_economics.py --json inputs.json [-o out.md]
23
+ echo '{"price": 29, "variable_cost": 6, "cac": 400, "lifetime": 9}' | \
24
+ python3 unit_economics.py --json -
25
+
26
+ JSON keys mirror the flags: price, variable_cost, cac, lifetime, churn,
27
+ fixed, market_customers, period. Flags given on the command line override
28
+ the JSON.
29
+
30
+ Exit codes: 0 ok, 2 bad input.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import argparse
36
+ import json
37
+ import sys
38
+
39
+ KEYS = ("price", "variable_cost", "cac", "lifetime", "churn", "fixed", "market_customers")
40
+
41
+
42
+ def fail(msg: str) -> None:
43
+ print(f"error: {msg}", file=sys.stderr)
44
+ sys.exit(2)
45
+
46
+
47
+ def load_json(path: str) -> dict:
48
+ try:
49
+ if path == "-":
50
+ data = json.load(sys.stdin)
51
+ else:
52
+ with open(path, encoding="utf-8") as fh:
53
+ data = json.load(fh)
54
+ except FileNotFoundError:
55
+ fail(f"no such file: {path}")
56
+ except json.JSONDecodeError as exc:
57
+ fail(f"not valid JSON ({exc})")
58
+ if not isinstance(data, dict):
59
+ fail("JSON input must be one object")
60
+ return data
61
+
62
+
63
+ def number(name: str, value, minimum: float = 0.0, allow_equal: bool = True):
64
+ if value is None:
65
+ return None
66
+ try:
67
+ val = float(value)
68
+ except (TypeError, ValueError):
69
+ fail(f"{name} must be a number, got {value!r}")
70
+ if val < minimum or (not allow_equal and val == minimum):
71
+ op = ">=" if allow_equal else ">"
72
+ fail(f"{name} must be {op} {minimum:g}, got {val:g}")
73
+ return val
74
+
75
+
76
+ def money(x: float) -> str:
77
+ return f"{x:,.2f}"
78
+
79
+
80
+ def compute(inp: dict) -> tuple[list[str], list[str]]:
81
+ period = str(inp.get("period") or "month")
82
+ price = number("price", inp.get("price"), 0.0, allow_equal=False)
83
+ if price is None:
84
+ fail("price is required (per customer per period)")
85
+ vcost = number("variable_cost", inp.get("variable_cost")) or 0.0
86
+ cac = number("cac", inp.get("cac"))
87
+ lifetime = number("lifetime", inp.get("lifetime"), 0.0, allow_equal=False)
88
+ churn = inp.get("churn")
89
+ fixed = number("fixed", inp.get("fixed"))
90
+ market = number("market_customers", inp.get("market_customers"))
91
+
92
+ if churn is not None:
93
+ churn = number("churn", churn, 0.0, allow_equal=False)
94
+ if churn > 1:
95
+ if churn <= 100:
96
+ churn = churn / 100.0 # "11" meaning 11%
97
+ else:
98
+ fail("churn must be a fraction (0.11) or a percent (11)")
99
+ if lifetime is None:
100
+ lifetime = 1.0 / churn
101
+
102
+ lines: list[str] = []
103
+ flags: list[str] = []
104
+
105
+ margin = price - vcost
106
+ margin_pct = margin / price * 100
107
+ lines.append(f"Price per customer per {period}: {money(price)}")
108
+ if inp.get("variable_cost") is None:
109
+ lines.append(f"Variable cost per customer per {period}: not given, treated as 0")
110
+ flags.append("No cost to serve was given, so every margin figure here is an upper bound.")
111
+ else:
112
+ lines.append(f"Variable cost per customer per {period}: {money(vcost)}")
113
+ lines.append(f"Contribution per customer per {period}: {money(margin)} ({margin_pct:.1f}% gross margin)")
114
+ if margin <= 0:
115
+ flags.append(f"Each customer costs more to serve than they pay ({money(margin)} per {period}). No volume fixes this.")
116
+
117
+ ltv = None
118
+ if lifetime is not None:
119
+ src = f"1 / churn of {churn * 100:.1f}%" if churn is not None and inp.get("lifetime") is None else "as given"
120
+ lines.append(f"Customer lifetime: {lifetime:.1f} {period}s ({src})")
121
+ ltv = margin * lifetime
122
+ lines.append(f"Lifetime contribution (LTV): {money(ltv)}")
123
+
124
+ if cac is not None:
125
+ lines.append(f"Customer acquisition cost (CAC): {money(cac)}")
126
+ if margin > 0:
127
+ payback = cac / margin
128
+ lines.append(f"CAC payback: {payback:.1f} {period}s")
129
+ if lifetime is not None and payback > lifetime:
130
+ flags.append(
131
+ f"CAC payback ({payback:.1f} {period}s) is longer than the customer lifetime "
132
+ f"({lifetime:.1f} {period}s): every acquired customer loses {money(cac - ltv)}."
133
+ )
134
+ if ltv is not None:
135
+ ratio = ltv / cac if cac > 0 else float("inf")
136
+ lines.append(f"LTV:CAC: {ratio:.2f}" if cac > 0 else "LTV:CAC: no acquisition cost given as 0")
137
+ if cac > 0 and ratio < 1:
138
+ flags.append(f"LTV:CAC is {ratio:.2f}: acquisition destroys money before fixed costs.")
139
+ elif cac > 0 and ratio < 3:
140
+ flags.append(f"LTV:CAC is {ratio:.2f}, under the 3:1 most subscription businesses need to cover fixed costs.")
141
+
142
+ if ltv is not None and ltv > 0:
143
+ lines.append(f"Most a customer can cost to acquire: {money(ltv)} at break-even, {money(ltv / 3)} at 3:1")
144
+
145
+ if fixed is not None:
146
+ lines.append(f"Fixed costs per {period}: {money(fixed)}")
147
+ if margin > 0:
148
+ be = fixed / margin
149
+ be_whole = int(be) if be == int(be) else int(be) + 1
150
+ lines.append(f"Break-even customers (active at once): {be_whole:,}")
151
+ if market is not None:
152
+ lines.append(f"Customers the stated market holds: {int(market):,}")
153
+ if be_whole > market:
154
+ flags.append(
155
+ f"Break-even needs {be_whole:,} active customers but the stated market holds {int(market):,}."
156
+ )
157
+ else:
158
+ share = be_whole / market * 100
159
+ lines.append(f"Break-even share of the stated market: {share:.1f}%")
160
+ if share >= 25:
161
+ flags.append(
162
+ f"Break-even needs {share:.0f}% of the stated market as paying customers at the same time."
163
+ )
164
+ if cac is not None and ltv is not None and ltv <= cac:
165
+ flags.append("Break-even customers cannot be reached through paid acquisition at this CAC: each one adds a loss.")
166
+
167
+ missing = [k for k in ("variable_cost", "cac", "lifetime/churn", "fixed")
168
+ if (k == "lifetime/churn" and lifetime is None)
169
+ or (k != "lifetime/churn" and inp.get(k) is None)]
170
+ if missing:
171
+ lines.append("Not computed (input missing): " + ", ".join(missing))
172
+ return lines, flags
173
+
174
+
175
+ def main() -> None:
176
+ ap = argparse.ArgumentParser(description="Unit-economics sanity check for a lean canvas.")
177
+ ap.add_argument("--json", metavar="PATH", help="JSON object of inputs, or - for stdin")
178
+ ap.add_argument("--price", help="price per customer per period")
179
+ ap.add_argument("--variable-cost", dest="variable_cost", help="cost to serve one customer per period")
180
+ ap.add_argument("--cac", help="cost to acquire one paying customer")
181
+ ap.add_argument("--lifetime", help="average customer lifetime in periods")
182
+ ap.add_argument("--churn", help="churn per period, as 0.11 or 11")
183
+ ap.add_argument("--fixed", help="fixed costs per period")
184
+ ap.add_argument("--market-customers", dest="market_customers", help="customers the stated market holds")
185
+ ap.add_argument("--period", help='period label, default "month"')
186
+ ap.add_argument("-o", "--output", help="write the report to this path instead of stdout")
187
+ args = ap.parse_args()
188
+
189
+ inp: dict = load_json(args.json) if args.json else {}
190
+ unknown = set(inp) - set(KEYS) - {"period"}
191
+ if unknown:
192
+ fail("unknown JSON keys: " + ", ".join(sorted(unknown)))
193
+ for key in KEYS + ("period",):
194
+ val = getattr(args, key)
195
+ if val is not None:
196
+ inp[key] = val
197
+
198
+ lines, flags = compute(inp)
199
+ out = ["Unit economics", ""] + [f"- {ln}" for ln in lines]
200
+ out += ["", "Where the numbers don't close:" if flags else "Where the numbers don't close: none found."]
201
+ out += [f"- {f}" for f in flags]
202
+ text = "\n".join(out) + "\n"
203
+ if args.output:
204
+ with open(args.output, "w", encoding="utf-8") as fh:
205
+ fh.write(text)
206
+ else:
207
+ sys.stdout.write(text)
208
+
209
+
210
+ if __name__ == "__main__":
211
+ main()
@@ -29,7 +29,7 @@ Produces the contract *before* the code — a valid OpenAPI 3.1 document two tea
29
29
  3. **Open questions** — the unknowns that block freezing the contract, each with who can answer it
30
30
  Start from [`templates/openapi-skeleton.yaml`](templates/openapi-skeleton.yaml).
31
31
 
32
- 6. **For event/queue contracts** (the brief says "publish", "consume", "webhook out"): same discipline, AsyncAPI-shaped — channel name, payload schema with the same typing rigor, delivery semantics stated (at-least-once is the default; say it), and the consumer-retry/DLQ expectation in the channel description.
32
+ 6. **For event/queue contracts** (the brief says "publish", "consume", "webhook out"): hand the event side to `event-contract-draft`, which owns event naming, the envelope, ordering, delivery and versioning. When the request mixes both, draft the HTTP side here and say which events it leaves to that contract.
33
33
 
34
34
  ## Useful references in this skill
35
35
 
@@ -50,12 +50,12 @@ Produces the contract *before* the code — a valid OpenAPI 3.1 document two tea
50
50
  - ✅ Designing a new API or new resource before implementation
51
51
  - ✅ Frontend and backend (or two teams) need a contract to build against in parallel
52
52
  - ✅ A story from `user-story-splitter` whose slice is "expose X over the API"
53
- - ✅ Drafting the webhook/event contract a partner will consume
54
53
 
55
54
  ## When NOT to use this skill
56
55
 
57
56
  - ❌ Documenting an API that already exists — that's `guide-builder` (reference archetype)
58
57
  - ❌ Choosing REST vs GraphQL vs gRPC — this skill starts after that choice is made
58
+ - ❌ An event, message or webhook contract — that's `event-contract-draft`
59
59
  - ❌ Reviewing a contract someone else drafted — `doc-critique` or `council-review`
60
60
  - ❌ Generating server/client code from the spec — hand the frozen contract to the codegen tool
61
61
 
@@ -1,11 +1,33 @@
1
1
  {
2
2
  "name": "api-contract-draft",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Draft an OpenAPI 3.1 contract from a feature brief or story — resource-oriented paths, a full error catalog (RFC 9457), pagination/idempotency/versioning decided not deferred, examples on every operation, plus a decisions log so review argues choices instead of YAML. Use when the user wants to design an API, draft an OpenAPI/Swagger spec, define request/response shapes, or agree a contract between frontend/backend or two teams before code.",
5
5
  "entrypoint": "SKILL.md",
6
- "deps": { "npm": [], "pip": [] },
7
- "env": { "required": [], "optional": [] },
8
- "related": ["council-review", "doc-critique", "guide-builder", "user-story-splitter"],
9
- "tags": ["api-design", "openapi", "contract-first", "rest", "swagger", "asyncapi"],
10
- "model": { "tier": "standard", "rationale": "Schema synthesis against fixed conventions. The resource modeling needs judgment; the conventions bound it. Escalate via ambiguous-input on contested multi-team contracts." }
6
+ "deps": {
7
+ "npm": [],
8
+ "pip": []
9
+ },
10
+ "env": {
11
+ "required": [],
12
+ "optional": []
13
+ },
14
+ "related": [
15
+ "council-review",
16
+ "doc-critique",
17
+ "guide-builder",
18
+ "user-story-splitter",
19
+ "event-contract-draft"
20
+ ],
21
+ "tags": [
22
+ "api-design",
23
+ "openapi",
24
+ "contract-first",
25
+ "rest",
26
+ "swagger",
27
+ "asyncapi"
28
+ ],
29
+ "model": {
30
+ "tier": "standard",
31
+ "rationale": "Schema synthesis against fixed conventions. The resource modeling needs judgment; the conventions bound it. Escalate via ambiguous-input on contested multi-team contracts."
32
+ }
11
33
  }
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: event-contract-draft
3
+ description: Draft the contract for an event or message interface (Kafka topics, SNS/SQS, EventBridge, Pub/Sub, RabbitMQ, webhooks) as an AsyncAPI 3.0 document plus a short decisions page — past-tense event names, a required envelope with event id for deduplication, the partition key and the ordering it does and doesn't give, at-least-once delivery and how consumers stay idempotent, data minimisation with PII marked, additive-only versioning with dated deprecation, and retention, replay and dead-letter handling. Use when the user wants to define events, design a Kafka topic or message schema, write an AsyncAPI spec, agree what a producer publishes and what consumers must do, or version an event safely.
4
+ ---
5
+
6
+ # event-contract-draft
7
+
8
+ Produces the contract for an asynchronous interface before producer and consumers build
9
+ against it: an AsyncAPI 3.0 document and a one-page decisions record. Request/response HTTP
10
+ APIs are `api-contract-draft`; a dataset's schema and quality SLAs for analytics readers are
11
+ `data-contract`; the system design that decides events are the right tool is `design-doc`.
12
+
13
+ ## How to respond
14
+
15
+ **Four rules come before the steps and outrank them:**
16
+
17
+ - **Answer what was asked, first.** Open with the answer, the decision or the artifact, in plain words. Scores, matrices, frameworks and tags come after it, and anything that doesn't change the answer is cut.
18
+ - **Use only what you were given.** Don't add facts, names, numbers, incidents, history, steps or sections the input doesn't contain. What you need and don't have is left out of the artifact and listed once at the end under "To confirm".
19
+ - **Deliver from what you have.** When the request gives you something to work on, state your assumptions in a line and produce the result. When it gives you nothing to work on, ask for it in one or two plain sentences and say what you will do once you have it.
20
+ - **Write for someone who has never heard of this skill.** No skill names, no paths or scripts from this folder, no internal terms, and nothing about how the run was set up. A next step is one plain sentence at the end that describes the work.
21
+ - **For this skill:** When the request asks for something an event contract can't promise (global order, exactly-once, "change it in place", "include it just in case"), the first lines say no, why, and what the contract offers instead. Building the unsafe version quietly is the failure this skill exists to prevent.
22
+
23
+ 1. **Size the reply to the ask.** A question about one rule ("is ordering guaranteed if we key
24
+ by paymentId?") gets a direct answer in a few lines: yes or no, why, the fix. No AsyncAPI
25
+ document. A request to define events gets the full contract (step 11).
26
+
27
+ 2. **List the facts, the producer and the consumers.** For each consumer, write what it does
28
+ with each event; that line decides payload size and justifies every field. Ask at most 2
29
+ questions, spent on who consumes and what they do with it, and which broker. Missing broker
30
+ → write broker-neutral with a Kafka binding, tagged `[assumption]`.
31
+
32
+ 3. **Name events as facts, past tense, in the producer's domain.** One event type per message
33
+ schema.
34
+ - ✅ `OrderPaid` — ❌ `CreateShipment`. A command tells another team what to do and couples
35
+ the producer to the warehouse's process; the warehouse decides to pick when it sees
36
+ `OrderPaid`. If one named receiver must act, that is a command on the receiver's queue,
37
+ outside this contract; say so.
38
+ - ✅ `OrderPlaced`, `OrderCancelled` — ❌ `OrderEvent` with a `status` field. A generic
39
+ event makes every consumer parse a union and breaks them all when a status is added.
40
+ - ✅ `OrderDeliveryAddressChanged` — ❌ `OrderUpdated` when the business fact has a name.
41
+
42
+ 4. **Decide notification or event-carried state per event, with the reason.** Notification =
43
+ ids plus a link; consumers call back. Event-carried state = the fields named consumers act
44
+ on, so they never call back. ✅ *"`OrderPaid` carries lines and address: fulfilment picks
45
+ without calling the order API."* ❌ The whole order aggregate on every event "for
46
+ flexibility". Table in [`reference.md`](reference.md).
47
+
48
+ 5. **Put the envelope on every message, all required:** event id, event type, event version,
49
+ occurred-at (UTC, RFC 3339, `format: date-time`), producer, correlation id; causation id
50
+ too. Domain fields go under `data`, with a per-entity revision (`orderRevision`) that
51
+ increases on each change. ❌ `"placedAt": "2026-10-19 14:03:11"` — no zone, not RFC 3339.
52
+ Money is integer minor units plus currency: ❌ `"total": 42.50`.
53
+
54
+ 6. **State delivery, ordering and duplicates per channel.**
55
+ - **Delivery is at-least-once** unless the broker and the consumer prove otherwise.
56
+ Consumers deduplicate on the event id or apply only a higher revision; name which, per
57
+ consumer. ✅ *"Loyalty awards points keyed on (orderId, reason); a second OrderPaid is a
58
+ no-op."* ❌ "Exactly-once delivery" in the description.
59
+ - **Ordering exists only per partition key.** Name the key, what it orders, and what it
60
+ doesn't. ✅ *"Keyed by orderId: one order's events arrive in order. No order across
61
+ orders, no global order."* Events whose relative order matters (`OrderPaid`,
62
+ `OrderCancelled`) share one channel and one key; on two topics they race.
63
+ - **A global-order request is declined.** One partition means one consumer at a time, and
64
+ retries and dead-letter redrives reorder it anyway. Offer what the reader needs:
65
+ per-entity order plus `occurredAt` and the revision, sorted at read time.
66
+ - **The producer publishes each entity's events in commit order** (an outbox written in
67
+ the same transaction), or the per-key promise is false.
68
+ - **Webhooks** have no ordering: receivers use the revision. Sign the raw body (HMAC) with
69
+ a timestamp inside the signature so receivers reject replays; state the retry schedule,
70
+ its cap, and where deliveries that keep failing go.
71
+
72
+ 7. **Minimise the data.** Every field names the consumer that uses it; no consumer, no field.
73
+ ❌ `customerEmail` "just in case": it sits in every consumer's log and replay for the whole
74
+ retention window and cannot be recalled. Personal data a consumer does need stays and gets
75
+ `x-pii` naming that consumer (✅ `deliveryAddress` for fulfilment). Card numbers never go on
76
+ an event, marked or not; send a payment reference or the last four digits.
77
+
78
+ 8. **Version additively; break only with a new version and a date.** Within a version only
79
+ optional fields are added; consumers ignore unknown fields, so payload schemas stay open
80
+ (no `additionalProperties: false`). A rename, type or unit change, removal, new required
81
+ field or key change is breaking. ❌ Renaming `total` in place while a consumer reads it in
82
+ production. ✅ Add `totalMinor` beside `total`, publish both, mark `total` deprecated with a
83
+ sunset date; or publish `v2` on a new channel alongside `v1` for a dual-publish window. The
84
+ sunset date comes from the input or is "to agree with <consumer>"; never invent one. New
85
+ event types on a live topic whose consumers assume one type break those consumers: put
86
+ them on a new channel or confirm the consumers skip unknown types.
87
+
88
+ 9. **State retention, replay and dead-letter on every channel.** How long messages are kept
89
+ (from the input or `[assumption]`), whether consumers can replay and from where, what goes
90
+ to the DLQ after how many attempts, and who watches it. An unnamed DLQ owner goes under "To
91
+ confirm", not a made-up team. Broker specifics in [`reference.md`](reference.md).
92
+
93
+ 10. **Write the contract.** Start from
94
+ [`templates/asyncapi-skeleton.json`](templates/asyncapi-skeleton.json); emit JSON (the
95
+ check below reads JSON only). Channel facts go in `x-partition-key`, `x-ordering`,
96
+ `x-delivery`, `x-retention`, `x-replay`, `x-dead-letter`; consumer duties in
97
+ `info.description`. Then run the check and fix every FAIL:
98
+
99
+ ```bash
100
+ # Claude Code
101
+ python3 "${CLAUDE_SKILL_DIR}/scripts/check_asyncapi.py" asyncapi.json
102
+ # Other IDEs (from the skill folder)
103
+ python3 scripts/check_asyncapi.py asyncapi.json
104
+ ```
105
+
106
+ It checks AsyncAPI 3.x structure, `$ref`s, past-tense names, the envelope in every message,
107
+ one type per schema, unmarked personal data, closed schemas, deprecation dates, and each
108
+ channel's key, ordering, delivery, retention, replay and DLQ notes. When it can't run, say
109
+ the document is unchecked. Never claim a check that didn't run.
110
+
111
+ 11. **Reply in this order:**
112
+ 1. **What the contract decides**, 3–6 lines, leading with every pushback from steps 3–9.
113
+ 2. **Decisions**: a table of decision, choice, why (shape in [`reference.md`](reference.md)).
114
+ 3. **What consumers must do**: numbered, at most 6: dedupe, ignore unknown fields, revision
115
+ check, no cross-key order, DLQ duty, the migration they owe and by when.
116
+ 4. **The AsyncAPI document**, saved as `asyncapi.json` when files can be written, inline
117
+ otherwise; then one line on the check result.
118
+ 5. **To confirm**: each open item with who can answer it.
119
+
120
+ **Non-interactive runs** (subagent, CI, headless): a missing broker, retention, consumer
121
+ detail or sunset date becomes a tagged `[assumption]` or a "To confirm" line, and the contract
122
+ ships. No producer or no event at all emits `BLOCKED: need the producing service and what
123
+ happens in it that others react to`.
124
+
125
+ ## Useful references in this skill
126
+
127
+ - [`reference.md`](reference.md) — envelope fields and CloudEvents mapping, notification vs state, safe vs breaking changes, broker ordering/delivery/retention facts, consumer idempotency patterns, the `x-` extensions, decisions page shape
128
+ - [`templates/asyncapi-skeleton.json`](templates/asyncapi-skeleton.json) — valid AsyncAPI 3.0 starting point with envelope, two messages and every channel extension
129
+ - [`scripts/check_asyncapi.py`](scripts/check_asyncapi.py) — the structural and rule check (stdlib only; JSON input; exit 1 on failures)
130
+ - [`examples/order-service.md`](examples/order-service.md) — worked example: an order service brief with four traps, and the contract that answers it
131
+
132
+ ## Quality bar
133
+
134
+ - **Every event name is a past-tense fact** in the producer's domain, one type per schema.
135
+ - **Every message has the full envelope, required**, with a stable event id for deduplication.
136
+ - **Every channel names its key, the order it gives and the order it doesn't.** No global order.
137
+ - **Each consumer's idempotency is stated**: dedupe on id, revision check, or a natural key.
138
+ - **Every field has a consumer**; personal data that stays is marked; nothing "just in case".
139
+ - **Breaking changes go through a new field or version** with a deprecation date that came from the input or is listed as open.
140
+ - **Retention, replay and dead-letter are stated per channel**, with the DLQ owner named or open.
141
+ - **The check result is reported only if it ran**, and the document has no FAIL.
142
+ - **Nothing invented**: no team names, dates, volumes or retention the input didn't give, except as `[assumption]`.
143
+
144
+ ## When to use this skill
145
+
146
+ - ✅ Defining the events a service publishes, before consumers build against them
147
+ - ✅ Designing a Kafka topic, SNS/SQS, EventBridge, Pub/Sub or RabbitMQ message contract
148
+ - ✅ Changing a live event's schema without breaking its consumers
149
+ - ✅ A webhook contract partners will receive
150
+ - ✅ "Is ordering / exactly-once / dedup guaranteed if we do X?" about a message interface
151
+
152
+ ## When NOT to use this skill
153
+
154
+ - ❌ A request/response HTTP API — `api-contract-draft`
155
+ - ❌ A table, dataset or analytics stream's schema, freshness and quality SLAs — `data-contract`
156
+ - ❌ Deciding whether the system should be event-driven at all, or its components — `design-doc`
157
+
158
+ ## Anti-patterns to avoid
159
+
160
+ - ❌ **Commands dressed as events.** `CreateShipment` on a topic makes the order service own the warehouse's workflow.
161
+ - ❌ **Promising global order** or exactly-once because someone asked. Brokers give per-key order and at-least-once.
162
+ - ❌ **No event id**, so consumers dedupe on a hash of the payload and double-count the legitimate repeat.
163
+ - ❌ **Related events on separate topics** whose order then races: `OrderCancelled` overtakes `OrderPaid`.
164
+ - ❌ **The "just in case" field.** Personal data with no consumer, kept in every log for the retention window.
165
+ - ❌ **In-place breaking change** to a live event, "so there's one version to maintain". Fulfilment breaks at deploy.
166
+ - ❌ **`additionalProperties: false`** on payloads, which turns every additive change into a breaking one.
167
+ - ❌ **A DLQ nobody owns.** Messages land there and the business notices weeks later.
@@ -0,0 +1,38 @@
1
+ [
2
+ {
3
+ "query": "Write the AsyncAPI spec for the order events our service publishes to Kafka",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "Define the message schema, envelope and partition key for our payment events topic",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "We need to rename a field on a live SNS/SQS event without breaking the consumers. How do we version it?",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "Draft the EventBridge event contract for InvoicePaid, including what consumers have to do about duplicates",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "If we key our Kafka topic by customerId, is ordering guaranteed for each order?",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "Draft the OpenAPI spec for the orders REST API: create, fetch and list orders",
24
+ "should_trigger": false
25
+ },
26
+ {
27
+ "query": "Write a data contract for the orders table the analysts query in the warehouse, with freshness and completeness SLAs",
28
+ "should_trigger": false
29
+ },
30
+ {
31
+ "query": "Define the data contract for the clickstream topic that feeds the lakehouse, with null-rate and volume checks",
32
+ "should_trigger": false
33
+ },
34
+ {
35
+ "query": "Write a design doc for moving the order service from polling to an event-driven architecture",
36
+ "should_trigger": false
37
+ }
38
+ ]