vanara 0.1.0 → 0.3.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 (195) hide show
  1. package/README.md +14 -0
  2. package/bin/vanara.js +66 -2
  3. package/catalog/agents/api-designer/AGENT.md +156 -0
  4. package/catalog/agents/api-designer/examples/openapi-snippet.yaml +130 -0
  5. package/catalog/agents/api-designer/examples/review-notes.md +61 -0
  6. package/catalog/agents/api-designer/references/contract-and-openapi.md +82 -0
  7. package/catalog/agents/api-designer/references/design-checklist.md +68 -0
  8. package/catalog/agents/api-designer/references/versioning-and-evolution.md +63 -0
  9. package/catalog/agents/api-designer/scripts/lint-openapi.mjs +180 -0
  10. package/catalog/agents/code-reviewer/AGENT.md +184 -0
  11. package/catalog/agents/code-reviewer/examples/pr-comment-template.md +48 -0
  12. package/catalog/agents/code-reviewer/examples/sample-review-output.md +38 -0
  13. package/catalog/agents/code-reviewer/references/review-checklist.md +46 -0
  14. package/catalog/agents/code-reviewer/references/security-review.md +62 -0
  15. package/catalog/agents/code-reviewer/references/severity-rubric.md +63 -0
  16. package/catalog/agents/code-reviewer/scripts/review-guard.mjs +102 -0
  17. package/catalog/agents/debugger/AGENT.md +128 -0
  18. package/catalog/agents/debugger/examples/postmortem-template.md +59 -0
  19. package/catalog/agents/debugger/examples/repro-template.md +50 -0
  20. package/catalog/agents/debugger/references/bisection.md +63 -0
  21. package/catalog/agents/debugger/references/debugging-method.md +57 -0
  22. package/catalog/agents/debugger/references/observability.md +61 -0
  23. package/catalog/agents/debugger/scripts/parse-stacktrace.mjs +109 -0
  24. package/catalog/agents/pr-summarizer/AGENT.md +172 -0
  25. package/catalog/agents/pr-summarizer/examples/sample-pr-summary.md +46 -0
  26. package/catalog/agents/pr-summarizer/examples/weak-vs-strong-summary.md +68 -0
  27. package/catalog/agents/pr-summarizer/references/identifying-risk.md +68 -0
  28. package/catalog/agents/pr-summarizer/references/reading-the-diff.md +68 -0
  29. package/catalog/agents/pr-summarizer/references/test-plan-and-missing-tests.md +64 -0
  30. package/catalog/agents/pr-summarizer/references/writing-the-summary.md +63 -0
  31. package/catalog/agents/pr-summarizer/scripts/diff-risk.mjs +111 -0
  32. package/catalog/agents/refactoring-specialist/AGENT.md +201 -0
  33. package/catalog/agents/refactoring-specialist/examples/before-after.md +91 -0
  34. package/catalog/agents/refactoring-specialist/examples/refactor-plan.md +97 -0
  35. package/catalog/agents/refactoring-specialist/references/large-scale-refactor.md +50 -0
  36. package/catalog/agents/refactoring-specialist/references/safe-workflow.md +75 -0
  37. package/catalog/agents/refactoring-specialist/references/smell-catalog.md +76 -0
  38. package/catalog/agents/refactoring-specialist/scripts/detect-smells.mjs +150 -0
  39. package/catalog/agents/security-auditor/AGENT.md +184 -0
  40. package/catalog/agents/security-auditor/examples/audit-report.md +68 -0
  41. package/catalog/agents/security-auditor/examples/finding-template.md +31 -0
  42. package/catalog/agents/security-auditor/references/audit-workflow.md +116 -0
  43. package/catalog/agents/security-auditor/references/severity-and-reporting.md +56 -0
  44. package/catalog/agents/security-auditor/references/vuln-classes.md +129 -0
  45. package/catalog/agents/security-auditor/scripts/scan-secrets.mjs +86 -0
  46. package/catalog/agents/technical-writer/AGENT.md +179 -0
  47. package/catalog/agents/technical-writer/examples/how-to-example.md +76 -0
  48. package/catalog/agents/technical-writer/examples/reference-example.md +75 -0
  49. package/catalog/agents/technical-writer/references/clarity-and-style.md +52 -0
  50. package/catalog/agents/technical-writer/references/diataxis-and-doc-types.md +61 -0
  51. package/catalog/agents/technical-writer/references/structure-and-maintenance.md +59 -0
  52. package/catalog/agents/technical-writer/scripts/readability.mjs +103 -0
  53. package/catalog/agents/test-author/AGENT.md +193 -0
  54. package/catalog/agents/test-author/examples/test-plan.md +54 -0
  55. package/catalog/agents/test-author/examples/unit-test-example.ts +55 -0
  56. package/catalog/agents/test-author/references/aaa-and-naming.md +75 -0
  57. package/catalog/agents/test-author/references/mocking-and-fixtures.md +94 -0
  58. package/catalog/agents/test-author/references/test-types-and-pyramid.md +60 -0
  59. package/catalog/agents/test-author/scripts/check-coverage.mjs +145 -0
  60. package/catalog/agents/threat-modeler/AGENT.md +179 -0
  61. package/catalog/agents/threat-modeler/examples/dfd-example.md +55 -0
  62. package/catalog/agents/threat-modeler/examples/threat-model.md +81 -0
  63. package/catalog/agents/threat-modeler/references/dfd-and-trust-boundaries.md +76 -0
  64. package/catalog/agents/threat-modeler/references/mitigation-catalog.md +77 -0
  65. package/catalog/agents/threat-modeler/references/stride-method.md +68 -0
  66. package/catalog/agents/threat-modeler/scripts/stride-checklist.mjs +183 -0
  67. package/catalog/agents/vuln-scanner/AGENT.md +162 -0
  68. package/catalog/agents/vuln-scanner/examples/finding-template.md +57 -0
  69. package/catalog/agents/vuln-scanner/examples/scan-report.md +49 -0
  70. package/catalog/agents/vuln-scanner/references/remediation-and-severity.md +68 -0
  71. package/catalog/agents/vuln-scanner/references/scan-types-and-tools.md +83 -0
  72. package/catalog/agents/vuln-scanner/references/triage-and-false-positives.md +73 -0
  73. package/catalog/agents/vuln-scanner/scripts/parse-scan-results.mjs +133 -0
  74. package/catalog/packs/code-review-pack/PACK.md +100 -0
  75. package/catalog/packs/code-review-pack/examples/pr-flow-example.md +50 -0
  76. package/catalog/packs/code-review-pack/references/rollout-guide.md +40 -0
  77. package/catalog/packs/security-pack/PACK.md +128 -0
  78. package/catalog/packs/security-pack/examples/owasp-vuln-triage-and-fix.md +66 -0
  79. package/catalog/packs/security-pack/examples/threat-model-then-audit.md +77 -0
  80. package/catalog/packs/security-pack/references/choosing-the-right-tool.md +37 -0
  81. package/catalog/packs/security-pack/references/threat-model-to-remediation.md +69 -0
  82. package/catalog/packs/security-pack/references/workflow.md +56 -0
  83. package/catalog/skills/api-pagination/SKILL.md +83 -0
  84. package/catalog/skills/api-pagination/examples/keyset-queries.sql +38 -0
  85. package/catalog/skills/api-pagination/examples/response-shapes.md +51 -0
  86. package/catalog/skills/api-pagination/references/consuming-pages.md +39 -0
  87. package/catalog/skills/api-pagination/references/counting-and-totals.md +41 -0
  88. package/catalog/skills/api-pagination/references/cursor-encoding.md +34 -0
  89. package/catalog/skills/api-pagination/scripts/check-cursor.mjs +51 -0
  90. package/catalog/skills/caching-strategies/SKILL.md +180 -0
  91. package/catalog/skills/caching-strategies/examples/http-cache-headers.md +82 -0
  92. package/catalog/skills/caching-strategies/examples/redis-cache-aside.js +110 -0
  93. package/catalog/skills/caching-strategies/references/cache-patterns.md +93 -0
  94. package/catalog/skills/caching-strategies/references/eviction-and-ttl.md +67 -0
  95. package/catalog/skills/caching-strategies/references/invalidation-and-stampede.md +99 -0
  96. package/catalog/skills/caching-strategies/scripts/ttl-jitter.mjs +99 -0
  97. package/catalog/skills/conventional-commits/SKILL.md +135 -0
  98. package/catalog/skills/conventional-commits/examples/commit-examples.md +60 -0
  99. package/catalog/skills/conventional-commits/examples/commitlint.config.js +31 -0
  100. package/catalog/skills/conventional-commits/references/breaking-changes-semver.md +50 -0
  101. package/catalog/skills/conventional-commits/references/scoping-commits.md +42 -0
  102. package/catalog/skills/conventional-commits/references/spec.md +78 -0
  103. package/catalog/skills/conventional-commits/scripts/lint-commit.mjs +97 -0
  104. package/catalog/skills/database-migrations/SKILL.md +137 -0
  105. package/catalog/skills/database-migrations/examples/add-column-safe.sql +34 -0
  106. package/catalog/skills/database-migrations/examples/migration-config.example.json +27 -0
  107. package/catalog/skills/database-migrations/references/expand-contract.md +74 -0
  108. package/catalog/skills/database-migrations/references/rollback-and-safety.md +71 -0
  109. package/catalog/skills/database-migrations/references/zero-downtime-changes.md +84 -0
  110. package/catalog/skills/database-migrations/scripts/check-migration-reversible.mjs +139 -0
  111. package/catalog/skills/error-handling-patterns/SKILL.md +136 -0
  112. package/catalog/skills/error-handling-patterns/examples/express-error-middleware.js +93 -0
  113. package/catalog/skills/error-handling-patterns/examples/result-pattern.ts +81 -0
  114. package/catalog/skills/error-handling-patterns/references/error-taxonomy.md +91 -0
  115. package/catalog/skills/error-handling-patterns/references/language-patterns.md +102 -0
  116. package/catalog/skills/error-handling-patterns/references/retry-and-backoff.md +76 -0
  117. package/catalog/skills/error-handling-patterns/scripts/lint-empty-catch.mjs +113 -0
  118. package/catalog/skills/git-collaboration-workflows/SKILL.md +104 -0
  119. package/catalog/skills/git-collaboration-workflows/examples/branch-protection.md +48 -0
  120. package/catalog/skills/git-collaboration-workflows/examples/pr-review-playbook.md +48 -0
  121. package/catalog/skills/git-collaboration-workflows/references/branching-models.md +52 -0
  122. package/catalog/skills/git-collaboration-workflows/references/history-hygiene.md +44 -0
  123. package/catalog/skills/git-collaboration-workflows/scripts/check-branch-hygiene.mjs +62 -0
  124. package/catalog/skills/owasp-top10/SKILL.md +112 -0
  125. package/catalog/skills/owasp-top10/examples/sql-injection-fix.md +33 -0
  126. package/catalog/skills/owasp-top10/examples/xss-fix.md +27 -0
  127. package/catalog/skills/owasp-top10/references/access-control.md +36 -0
  128. package/catalog/skills/owasp-top10/references/crypto-failures.md +29 -0
  129. package/catalog/skills/owasp-top10/references/injection.md +39 -0
  130. package/catalog/skills/owasp-top10/references/ssrf-and-design.md +35 -0
  131. package/catalog/skills/owasp-top10/scripts/scan-injection.mjs +73 -0
  132. package/catalog/skills/prompt-engineering/SKILL.md +100 -0
  133. package/catalog/skills/prompt-engineering/examples/classification-prompt.md +29 -0
  134. package/catalog/skills/prompt-engineering/examples/extraction-prompt.md +25 -0
  135. package/catalog/skills/prompt-engineering/references/prompt-injection.md +34 -0
  136. package/catalog/skills/prompt-engineering/references/structured-output.md +34 -0
  137. package/catalog/skills/prompt-engineering/references/techniques.md +33 -0
  138. package/catalog/skills/prompt-engineering/scripts/validate-output.mjs +71 -0
  139. package/catalog/skills/readme-writing/SKILL.md +176 -0
  140. package/catalog/skills/readme-writing/examples/README.template.md +73 -0
  141. package/catalog/skills/readme-writing/examples/good-readme-example.md +95 -0
  142. package/catalog/skills/readme-writing/references/anatomy.md +107 -0
  143. package/catalog/skills/readme-writing/references/maintenance.md +68 -0
  144. package/catalog/skills/readme-writing/references/writing-style.md +79 -0
  145. package/catalog/skills/readme-writing/scripts/lint-readme.mjs +117 -0
  146. package/catalog/skills/refactoring-patterns/SKILL.md +178 -0
  147. package/catalog/skills/refactoring-patterns/examples/extract-function.before-after.js +57 -0
  148. package/catalog/skills/refactoring-patterns/examples/replace-conditional-with-polymorphism.before-after.ts +63 -0
  149. package/catalog/skills/refactoring-patterns/references/code-smells.md +92 -0
  150. package/catalog/skills/refactoring-patterns/references/refactoring-catalog.md +78 -0
  151. package/catalog/skills/refactoring-patterns/references/safe-workflow.md +74 -0
  152. package/catalog/skills/refactoring-patterns/scripts/detect-smells.mjs +137 -0
  153. package/catalog/skills/rest-api-design/SKILL.md +147 -0
  154. package/catalog/skills/rest-api-design/examples/error-envelope.json +26 -0
  155. package/catalog/skills/rest-api-design/examples/orders-api.openapi.yaml +106 -0
  156. package/catalog/skills/rest-api-design/references/error-handling.md +43 -0
  157. package/catalog/skills/rest-api-design/references/pagination.md +41 -0
  158. package/catalog/skills/rest-api-design/references/status-codes.md +32 -0
  159. package/catalog/skills/rest-api-design/references/versioning.md +31 -0
  160. package/catalog/skills/rest-api-design/scripts/check-envelope.mjs +62 -0
  161. package/catalog/skills/secrets-management/SKILL.md +134 -0
  162. package/catalog/skills/secrets-management/examples/env-example.md +41 -0
  163. package/catalog/skills/secrets-management/examples/rotation-runbook.md +50 -0
  164. package/catalog/skills/secrets-management/references/leak-response.md +47 -0
  165. package/catalog/skills/secrets-management/references/rotation.md +46 -0
  166. package/catalog/skills/secrets-management/references/secret-managers.md +52 -0
  167. package/catalog/skills/secrets-management/scripts/detect-hardcoded.mjs +96 -0
  168. package/catalog/skills/secure-auth/SKILL.md +135 -0
  169. package/catalog/skills/secure-auth/examples/auth-flow.md +60 -0
  170. package/catalog/skills/secure-auth/examples/jwt-verify.ts +89 -0
  171. package/catalog/skills/secure-auth/references/auth-attacks.md +76 -0
  172. package/catalog/skills/secure-auth/references/oauth2-oidc.md +65 -0
  173. package/catalog/skills/secure-auth/references/password-hashing.md +70 -0
  174. package/catalog/skills/secure-auth/references/sessions-vs-jwt.md +69 -0
  175. package/catalog/skills/secure-auth/scripts/check-password-policy.mjs +114 -0
  176. package/catalog/skills/sql-index-tuning/SKILL.md +171 -0
  177. package/catalog/skills/sql-index-tuning/examples/explain-walkthrough.sql +91 -0
  178. package/catalog/skills/sql-index-tuning/examples/index-ddl.sql +67 -0
  179. package/catalog/skills/sql-index-tuning/references/btree-internals.md +58 -0
  180. package/catalog/skills/sql-index-tuning/references/composite-and-covering.md +60 -0
  181. package/catalog/skills/sql-index-tuning/references/reading-explain.md +54 -0
  182. package/catalog/skills/sql-index-tuning/references/selectivity-and-cardinality.md +62 -0
  183. package/catalog/skills/sql-index-tuning/scripts/suggest-index.mjs +213 -0
  184. package/catalog/skills/test-plan-design/SKILL.md +154 -0
  185. package/catalog/skills/test-plan-design/examples/test-case-template.md +45 -0
  186. package/catalog/skills/test-plan-design/examples/test-plan.md +57 -0
  187. package/catalog/skills/test-plan-design/references/case-design.md +79 -0
  188. package/catalog/skills/test-plan-design/references/risk-prioritization.md +58 -0
  189. package/catalog/skills/test-plan-design/references/test-pyramid.md +58 -0
  190. package/catalog/skills/test-plan-design/scripts/coverage-gaps.mjs +109 -0
  191. package/free-tier.json +15 -0
  192. package/package.json +15 -4
  193. package/premium-index.json +1 -0
  194. package/src/config.js +31 -1
  195. package/src/license.js +143 -0
package/README.md CHANGED
@@ -13,6 +13,20 @@ vanara update --apply # pull newer catalog versions
13
13
  vanara uninstall security-pack --members
14
14
  ```
15
15
 
16
+ ## Free vs Pro — no API keys, ever
17
+
18
+ The catalog ships **inside the package**: `npx vanara` works for anyone, offline, with zero
19
+ configuration and zero API keys. Tiers:
20
+
21
+ - **Free** — 27 items (code-review-pack, security-pack, and their members plus 15 core skills
22
+ and 5 more agents). Install freely, forever.
23
+ - **Pro ($10/mo)** — all 200+ items. Subscribe via Polar.sh (they handle payment + tax), get a
24
+ license key, then: `vanara unlock <license-key>`. The key is validated against Polar's public
25
+ API (no secrets in this CLI), re-checked weekly, with a 7-day offline grace period.
26
+ `vanara account` shows your tier; `vanara unlock --remove` returns to free.
27
+
28
+ The one key in the system is the Pro license key — never an LLM/API key.
29
+
16
30
  ## How it works
17
31
 
18
32
  - **Catalog source** — a directory with `agents/ skills/ packs/` (the Forge layout: flat
package/bin/vanara.js CHANGED
@@ -6,6 +6,8 @@ import { parseArgs } from 'node:util';
6
6
  import { resolveSource } from '../src/config.js';
7
7
  import { loadCatalog, resolveInstallSet } from '../src/catalog.js';
8
8
  import { applyInstall, applyUninstall, readLock, planUpdates } from '../src/installer.js';
9
+ import { loadFreeTier, POLAR } from '../src/config.js';
10
+ import { unlock, isUnlocked, readLicense, clearLicense, syncPremium } from '../src/license.js';
9
11
 
10
12
  const HELP = `vanara — agent packs, agents, and skills for your project.
11
13
 
@@ -18,6 +20,9 @@ Commands:
18
20
  uninstall <name> [--members] Remove an item; --members also removes a pack's members.
19
21
  installed Show what is installed here (from ${'.vanara.json'}).
20
22
  update [--apply] Show (or apply) available catalog updates.
23
+ unlock <license-key> Activate Pro (all items). Subscribe: ${POLAR.checkoutUrl}
24
+ account Show your tier and license status.
25
+ sync Re-download the premium catalog (Pro).
21
26
  help This message.
22
27
 
23
28
  Options:
@@ -35,6 +40,14 @@ function fmtItem(i) {
35
40
  return ` [${i.kind}] ${i.name}${version} — ${String(i.data.description ?? '').slice(0, 100)}`;
36
41
  }
37
42
 
43
+ const FREE = loadFreeTier();
44
+ import { readFileSync } from 'node:fs';
45
+ import { fileURLToPath } from 'node:url';
46
+ import path from 'node:path';
47
+ let PREMIUM_INDEX = [];
48
+ try { PREMIUM_INDEX = JSON.parse(readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'premium-index.json'), 'utf8')); } catch {}
49
+ const isFree = (item) => (FREE[item.kind + 's'] ?? []).includes(item.name);
50
+
38
51
  async function main() {
39
52
  const { values, positionals } = parseArgs({
40
53
  args: process.argv.slice(2),
@@ -78,15 +91,19 @@ async function main() {
78
91
  return;
79
92
  }
80
93
 
94
+ const unlockedState = await isUnlocked();
81
95
  if (command === 'list') {
82
- const items = catalog.items.filter((i) => !values.kind || i.kind === values.kind);
96
+ const present = new Set(catalog.items.map((i) => i.name));
97
+ const virtual = PREMIUM_INDEX.filter((p) => !present.has(p.name)).map((p) => ({ ...p, data: { version: p.version, description: p.description }, supportFiles: [], virtual: true }));
98
+ const items = [...catalog.items, ...virtual].filter((i) => !values.kind || i.kind === values.kind);
83
99
  for (const kind of ['pack', 'agent', 'skill']) {
84
100
  const group = items.filter((i) => i.kind === kind);
85
101
  if (group.length === 0) continue;
86
102
  console.log(`\n${kind.toUpperCase()}S (${group.length})`);
87
- for (const i of group) console.log(fmtItem(i));
103
+ for (const i of group) console.log(fmtItem(i) + (i.virtual || !(unlockedState.unlocked || isFree(i)) ? ' [Pro]' : ''));
88
104
  }
89
105
  console.log(`\n${items.length} item(s) from ${source}`);
106
+ if (!unlockedState.unlocked) console.log(`Free items install now; [Pro] items need a subscription ($10/mo): ${POLAR.checkoutUrl}`);
90
107
  return;
91
108
  }
92
109
 
@@ -114,7 +131,23 @@ async function main() {
114
131
  return;
115
132
  }
116
133
  const resolved = resolveInstallSet(catalog, names);
134
+ if (!unlockedState.unlocked) {
135
+ const locked = [...resolved.packs, ...resolved.toInstall].filter((i) => !isFree(i));
136
+ if (locked.length) {
137
+ console.error('Pro items: ' + locked.map((i) => i.name).join(', '));
138
+ console.error(`Subscribe ($10/mo): ${POLAR.checkoutUrl} then run: vanara unlock <license-key>`);
139
+ process.exitCode = 1;
140
+ return;
141
+ }
142
+ }
117
143
  if (resolved.missing.length) {
144
+ const inIndex = resolved.missing.filter((m) => PREMIUM_INDEX.some((p) => m.startsWith(p.name)));
145
+ if (inIndex.length) {
146
+ console.error('Pro items (not in the free tier): ' + inIndex.join(', '));
147
+ console.error(`Subscribe ($10/mo): ${POLAR.checkoutUrl} then: vanara unlock <license-key> (then vanara sync)`);
148
+ process.exitCode = 1;
149
+ return;
150
+ }
118
151
  console.error(`Cannot install — missing from catalog:\n ${resolved.missing.join('\n ')}`);
119
152
  process.exitCode = 1;
120
153
  return;
@@ -154,6 +187,37 @@ async function main() {
154
187
  return;
155
188
  }
156
189
 
190
+ if (command === 'unlock') {
191
+ if (!names[0]) { console.error('Usage: vanara unlock <license-key> (or: vanara unlock --remove)'); process.exitCode = 1; return; }
192
+ if (names[0] === '--remove') { await clearLicense(); console.log('License removed - back to the free tier.'); return; }
193
+ const res = await unlock(names[0]);
194
+ if (!res.valid) { console.error('Could not activate: ' + res.error); process.exitCode = 1; return; }
195
+ console.log('Pro unlocked. Fetching the premium catalog...');
196
+ const dl = await syncPremium();
197
+ console.log(dl.ok ? `Premium catalog installed to ${dl.dir}. Everything is installable now.` : `Note: ${dl.error}`);
198
+ return;
199
+ }
200
+
201
+ if (command === 'sync') {
202
+ const dl = await syncPremium();
203
+ if (!dl.ok) { console.error(dl.error); process.exitCode = 1; return; }
204
+ console.log(`Premium catalog refreshed at ${dl.dir}.`);
205
+ return;
206
+ }
207
+
208
+ if (command === 'account') {
209
+ const lic = await readLicense();
210
+ if (!lic) {
211
+ const freeCount = (FREE.agents?.length ?? 0) + (FREE.skills?.length ?? 0);
212
+ console.log(`Tier: Free (${freeCount} items + ${FREE.packs?.length ?? 0} packs). Pro: ${POLAR.checkoutUrl}`);
213
+ return;
214
+ }
215
+ const state = await isUnlocked();
216
+ console.log(`Tier: ${state.unlocked ? 'Pro' + (state.grace ? ' (offline grace)' : '') : 'Free - license inactive: ' + (state.reason ?? 'unknown')}`);
217
+ console.log(`Key: ${lic.key.slice(0, 8)}... validated: ${lic.validatedAt}`);
218
+ return;
219
+ }
220
+
157
221
  console.error(`Unknown command: ${command}\n`);
158
222
  console.log(HELP);
159
223
  process.exitCode = 1;
@@ -0,0 +1,156 @@
1
+ ---
2
+ name: api-designer
3
+ description: Use when designing a new HTTP/GraphQL API or changing an existing one — modeling resources, defining endpoint contracts, choosing status codes, pagination, filtering, error envelopes, versioning, and idempotency. Produces a reviewable API contract plus an OpenAPI snippet, not production code.
4
+ tools: Read, Grep, Glob, Write
5
+ model: claude-sonnet-4-6
6
+ type: agent
7
+ version: 2.0.0
8
+ updated: 2026-06-29
9
+ ---
10
+ # API Designer
11
+
12
+ You design APIs that are **predictable, evolvable, and pleasant to consume**. A good API is *guessable*:
13
+ once a consumer learns one endpoint they can predict the rest. Consistency beats cleverness — a boring,
14
+ uniform contract is worth more than an elegant-but-surprising one, because every inconsistency becomes a
15
+ special case in every client forever.
16
+
17
+ You are read-only by design (`Read`, `Grep`, `Glob`): you investigate the existing API surface and
18
+ produce a **contract as text** — resource models, an endpoint table, the response/error envelope, the
19
+ versioning rule, and an OpenAPI snippet. You do not write production handlers; you hand a justified,
20
+ self-consistent contract to the implementer.
21
+
22
+ ## Operating principle
23
+
24
+ > The contract is the product. Servers and clients are implementations of it. Design the contract so a
25
+ > new client can be written against the docs alone, with no tribal knowledge.
26
+
27
+ Two forces dominate every decision: **consistency** (does this match the rest of the API?) and
28
+ **evolvability** (can I add to this later without breaking existing consumers?). When a local choice
29
+ conflicts with the API-wide pattern, the pattern wins — even if the local choice is marginally nicer.
30
+
31
+ ## Workflow
32
+
33
+ Follow these steps in order. Do not jump to endpoints before the resource model is settled.
34
+
35
+ 1. **Discover existing conventions first.** Use `Grep`/`Glob` to read the current routes, schemas, and
36
+ error shapes in the repo. A new endpoint must match the established envelope, casing, auth, and
37
+ pagination style. Inconsistency is the most expensive bug you can ship into an API.
38
+ 2. **Model resources, not actions.** Identify the nouns and their relationships. Name collections as
39
+ plural nouns (`/orders`), nest one level deep at most to show ownership (`/users/{id}/orders`), and
40
+ express non-CRUD actions as sub-resources (`POST /orders/{id}/refunds`, never `POST /refundOrder`).
41
+ 3. **Define each endpoint precisely.** For every endpoint specify: method, path, request schema,
42
+ response schema, **and the full set of status codes** (success *and* failure). Map each HTTP verb to
43
+ its correct semantics (GET safe+idempotent, PUT idempotent, POST not).
44
+ 4. **Apply cross-cutting rules uniformly.** One response envelope, one error shape, pagination on *every*
45
+ collection, consistent filtering/sorting query params, and documented auth + rate-limit per endpoint.
46
+ See `references/design-checklist.md`.
47
+ 5. **Plan for change.** Choose an explicit versioning strategy and state the backward-compatibility
48
+ rules (what is additive vs breaking). See `references/versioning-and-evolution.md`.
49
+ 6. **Emit the OpenAPI snippet.** Produce a minimal but valid OpenAPI 3.1 fragment for the new/changed
50
+ endpoints so the contract is machine-checkable. See `references/contract-and-openapi.md`.
51
+ 7. **Self-check.** Re-read your contract against the checklist and the existing API. Run
52
+ `scripts/lint-openapi.mjs` on the emitted spec (as JSON) to catch missing required fields.
53
+
54
+ ## Output format
55
+
56
+ Produce, in order:
57
+
58
+ 1. **Resource model** — the nouns, their relationships, and identifier strategy.
59
+ 2. **Endpoint table** — method, path, purpose, success code, error codes, auth, pagination.
60
+ 3. **Response & error envelope** — the single shape used everywhere (success and error).
61
+ 4. **Versioning rule** — the strategy and the additive-vs-breaking policy.
62
+ 5. **OpenAPI snippet** — a valid fragment for the endpoints (see example below).
63
+ 6. **Open questions / risks** — ambiguities the implementer or product owner must resolve.
64
+
65
+ ### Envelope and OpenAPI example
66
+
67
+ Standardize one envelope across every endpoint. On error, the same shape with `data: null`:
68
+
69
+ ```json
70
+ {
71
+ "data": [ { "id": "ord_101", "status": "open" } ],
72
+ "meta": { "nextCursor": "eyJpZCI6MTIwfQ", "limit": 20, "hasMore": true },
73
+ "error": null
74
+ }
75
+ ```
76
+
77
+ The OpenAPI fragment makes it checkable. A minimal, valid shape:
78
+
79
+ ```yaml
80
+ openapi: 3.1.0
81
+ info: { title: Orders API, version: "1.0.0" }
82
+ paths:
83
+ /orders:
84
+ get:
85
+ summary: List orders
86
+ parameters:
87
+ - { name: limit, in: query, schema: { type: integer, default: 20, maximum: 100 } }
88
+ - { name: cursor, in: query, schema: { type: string } }
89
+ responses:
90
+ "200": { description: A page of orders }
91
+ "401": { description: Unauthenticated }
92
+ ```
93
+
94
+ See `examples/openapi-snippet.yaml` for a complete worked example and `examples/review-notes.md` for how
95
+ this agent critiques a draft contract.
96
+
97
+ ## Common pitfalls (failure modes)
98
+
99
+ - **`200 OK` with `{"success": false}`** — returning a success status with an error body breaks every
100
+ client's error handling. Use the accurate status code (4xx/5xx); never tunnel errors through 200.
101
+ - **Verbs in URLs** (`/createUser`, `/getOrders`) — the HTTP method *is* the verb. Paths are nouns.
102
+ - **Unbounded list endpoints** — a `/users` that returns 2M rows is a DoS you inflicted on yourself.
103
+ Every collection paginates, with a server-enforced max `limit`.
104
+ - **Inconsistent shapes** — one endpoint returns a bare array, another an object; clients can't
105
+ generalize. Pick one envelope and use it everywhere, including errors.
106
+ - **Leaking existence via 404-vs-403** — returning 403 for resources an unauthorized user shouldn't even
107
+ know exist tells them it exists. Be deliberate (often 404 is the safer signal).
108
+ - **Breaking changes without a version bump** — renaming or removing a field, tightening validation, or
109
+ changing a type silently breaks consumers. Those require a new version; only additive changes are safe.
110
+ - **Over-nesting** (`/users/{id}/orders/{id}/items/{id}/...`) — past one level, link by ID instead.
111
+ - **Designing endpoints before resources** — leads to RPC-over-HTTP, not a resource model.
112
+
113
+ ## When NOT to use / boundaries
114
+
115
+ - **Not for trivial, local additions.** Adding one field to an existing, well-established response — just
116
+ follow the existing pattern; a full contract design is ceremony.
117
+ - **Not a coder.** This agent designs and documents the contract; it does not implement handlers,
118
+ middleware, or persistence. Hand the contract to the implementer.
119
+ - **Not the right tool when REST is the wrong paradigm.** Prefer **GraphQL** for flexible nested
120
+ client-driven selections, **gRPC** for low-latency internal service-to-service calls, and
121
+ **webhooks/event streams** for server push. Recommend the right paradigm rather than forcing REST.
122
+ - **Not a security review.** It documents auth and rate-limit expectations per endpoint, but a real
123
+ authz/threat review (IDOR, token handling, abuse) belongs to the `security-auditor` agent.
124
+
125
+ ## Files in this package
126
+
127
+ - `references/design-checklist.md` — the full pre-ship checklist: resources, methods, status codes,
128
+ envelope, pagination, filtering, auth, idempotency, concurrency.
129
+ - `references/contract-and-openapi.md` — how to write the OpenAPI 3.1 contract, components/$ref reuse,
130
+ and what `scripts/lint-openapi.mjs` enforces.
131
+ - `references/versioning-and-evolution.md` — versioning strategies and the additive-vs-breaking change
132
+ policy with a deprecation playbook.
133
+ - `examples/openapi-snippet.yaml` — a complete, valid OpenAPI fragment using the conventions here.
134
+ - `examples/review-notes.md` — a worked review of a flawed draft contract with severity-ranked findings.
135
+ - `scripts/lint-openapi.mjs` — runnable Node check that an OpenAPI doc (as JSON) has the required fields
136
+ (`openapi`, `info.title/version`, `paths`, response schemas); supports `--selftest` (exit 0/1).
137
+
138
+ Pairs with the [`rest-api-design`](../../skills/rest-api-design/SKILL.md) skill (the deep reference for
139
+ the conventions applied here), the `api-documenter` agent (turns this contract into published docs), and
140
+ the `security-auditor` agent (for the authz/threat review this agent deliberately leaves out).
141
+
142
+
143
+ ## Memory — learn across sessions
144
+
145
+ You keep a persistent, per-project memory at `.claude/memory/api-designer.md`. It is
146
+ how you get sharper on *this* codebase over time instead of starting cold every run.
147
+
148
+ - **Before you start:** read `.claude/memory/api-designer.md` if it exists and apply what
149
+ it holds — corrections you were given before, this project's conventions, decisions
150
+ and their rationale, and recurring pitfalls. If it is missing, continue without it.
151
+ - **After you finish:** if this task taught you something durable — a correction from
152
+ the user, a project-specific convention, a mistake worth not repeating — append it as
153
+ a short dated bullet under a relevant heading, and prune anything now stale or wrong.
154
+ Keep entries terse and general.
155
+ - **Never record** secrets, credentials, tokens, personal data, or one-off trivia, and
156
+ never write anywhere except your own `.claude/memory/` file.
@@ -0,0 +1,130 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Orders API
4
+ version: "1.0.0"
5
+ description: >-
6
+ Worked example of the api-designer conventions — plural-noun resources, accurate
7
+ status codes, cursor pagination, a single response/error envelope, and idempotent creates.
8
+ servers:
9
+ - url: https://api.example.com/v1
10
+ security:
11
+ - bearerAuth: []
12
+ paths:
13
+ /orders:
14
+ get:
15
+ summary: List orders
16
+ description: Cursor-paginated; limit is capped server-side at 100.
17
+ parameters:
18
+ - { name: status, in: query, schema: { type: string, enum: [open, paid, refunded] } }
19
+ - { name: sort, in: query, schema: { type: string, example: "-createdAt" } }
20
+ - { name: limit, in: query, schema: { type: integer, default: 20, maximum: 100 } }
21
+ - { name: cursor, in: query, schema: { type: string } }
22
+ responses:
23
+ "200":
24
+ description: A page of orders
25
+ content:
26
+ application/json:
27
+ schema: { $ref: "#/components/schemas/OrderList" }
28
+ "401": { $ref: "#/components/responses/Error" }
29
+ "429": { $ref: "#/components/responses/Error" }
30
+ post:
31
+ summary: Create an order
32
+ description: Accepts an Idempotency-Key so retries do not double-create.
33
+ parameters:
34
+ - name: Idempotency-Key
35
+ in: header
36
+ required: true
37
+ schema: { type: string }
38
+ requestBody:
39
+ required: true
40
+ content:
41
+ application/json:
42
+ schema: { $ref: "#/components/schemas/OrderInput" }
43
+ responses:
44
+ "201":
45
+ description: Created
46
+ headers:
47
+ Location: { schema: { type: string }, description: URL of the new order }
48
+ content:
49
+ application/json:
50
+ schema: { $ref: "#/components/schemas/OrderEnvelope" }
51
+ "422": { $ref: "#/components/responses/Error" }
52
+ /orders/{id}:
53
+ get:
54
+ summary: Get one order
55
+ parameters:
56
+ - { name: id, in: path, required: true, schema: { type: string } }
57
+ responses:
58
+ "200":
59
+ description: The order
60
+ content:
61
+ application/json:
62
+ schema: { $ref: "#/components/schemas/OrderEnvelope" }
63
+ "404": { $ref: "#/components/responses/Error" }
64
+ /orders/{id}/refunds:
65
+ post:
66
+ summary: Refund an order
67
+ description: Non-CRUD action modeled as a sub-resource, not POST /refundOrder.
68
+ parameters:
69
+ - { name: id, in: path, required: true, schema: { type: string } }
70
+ responses:
71
+ "201": { description: Refund created }
72
+ "409": { $ref: "#/components/responses/Error" }
73
+ components:
74
+ securitySchemes:
75
+ bearerAuth:
76
+ type: http
77
+ scheme: bearer
78
+ responses:
79
+ Error:
80
+ description: Standard error envelope
81
+ content:
82
+ application/json:
83
+ schema: { $ref: "#/components/schemas/ErrorEnvelope" }
84
+ schemas:
85
+ Order:
86
+ type: object
87
+ properties:
88
+ id: { type: string, example: "ord_01H..." }
89
+ status: { type: string, enum: [open, paid, refunded] }
90
+ total: { type: number }
91
+ createdAt: { type: string, format: date-time }
92
+ OrderInput:
93
+ type: object
94
+ required: [items]
95
+ properties:
96
+ items:
97
+ type: array
98
+ items:
99
+ type: object
100
+ properties:
101
+ productId: { type: string }
102
+ quantity: { type: integer }
103
+ Meta:
104
+ type: object
105
+ properties:
106
+ nextCursor: { type: string, nullable: true }
107
+ limit: { type: integer }
108
+ hasMore: { type: boolean }
109
+ OrderList:
110
+ type: object
111
+ properties:
112
+ data: { type: array, items: { $ref: "#/components/schemas/Order" } }
113
+ meta: { $ref: "#/components/schemas/Meta" }
114
+ error: { type: object, nullable: true }
115
+ OrderEnvelope:
116
+ type: object
117
+ properties:
118
+ data: { $ref: "#/components/schemas/Order" }
119
+ error: { type: object, nullable: true }
120
+ ErrorEnvelope:
121
+ type: object
122
+ properties:
123
+ data: { nullable: true }
124
+ error:
125
+ type: object
126
+ properties:
127
+ code: { type: string }
128
+ message: { type: string }
129
+ details: { type: array, items: { type: object } }
130
+ requestId: { type: string }
@@ -0,0 +1,61 @@
1
+ # Review Notes — Critiquing a Draft Contract
2
+
3
+ This shows how the api-designer agent reviews a flawed draft. Findings are severity-ranked so the
4
+ implementer fixes the contract-breakers first. Severity legend: **CRITICAL** (breaks clients / data
5
+ risk), **HIGH** (real bug or inconsistency), **MEDIUM** (maintainability), **LOW** (style).
6
+
7
+ ## The draft under review
8
+
9
+ ```http
10
+ POST /createOrder → 200 { "success": true, "order": {...} }
11
+ GET /getOrders → 200 [ {...}, {...}, ... ] # entire table
12
+ POST /refundOrder → 200 { "success": false, "msg": "already refunded" }
13
+ GET /orders/{id} → 200 { "id": 41, "status": "open" }
14
+ DELETE /orders/{id} → 200 { "success": true }
15
+ ```
16
+
17
+ ## Findings
18
+
19
+ ### CRITICAL
20
+
21
+ 1. **`200 OK` with `{"success": false}` on refund.** A failed refund returns HTTP 200, so every client
22
+ that checks the status code treats a failure as success. Use a real code — `409 Conflict` for "already
23
+ refunded" — with the standard error envelope.
24
+ *Fix:* `POST /orders/{id}/refunds → 201` on success, `409` + `{ "data": null, "error": { "code":
25
+ "already_refunded", ... } }` on conflict.
26
+
27
+ 2. **Unbounded `GET /getOrders` returns the entire table.** No pagination and no `limit` cap is a
28
+ self-inflicted DoS as the table grows.
29
+ *Fix:* `GET /orders?limit=20&cursor=...` with `limit` capped server-side at 100, returning the list
30
+ envelope with `meta.nextCursor`.
31
+
32
+ ### HIGH
33
+
34
+ 3. **Verbs in URLs** (`/createOrder`, `/getOrders`, `/refundOrder`). The HTTP method is the verb; paths
35
+ are nouns.
36
+ *Fix:* `POST /orders`, `GET /orders`, `POST /orders/{id}/refunds`.
37
+
38
+ 4. **Inconsistent response shapes.** `GET /getOrders` returns a bare array; other endpoints return
39
+ objects with ad-hoc keys (`order`, `success`, `msg`). Clients can't generalize.
40
+ *Fix:* one envelope everywhere — `{ data, meta, error }`.
41
+
42
+ 5. **Wrong success codes.** `POST /createOrder` returns 200 with no `Location`; it creates a resource so
43
+ it should be `201 Created` + `Location: /orders/{id}`. `DELETE` should be `204 No Content`.
44
+
45
+ ### MEDIUM
46
+
47
+ 6. **Enumerable integer IDs** (`"id": 41`). Sequential IDs leak volume and invite enumeration attacks.
48
+ *Fix:* opaque IDs (`ord_01H...`, UUID/ULID).
49
+
50
+ 7. **No idempotency on create.** A retried `POST /orders` will double-create.
51
+ *Fix:* accept an `Idempotency-Key` header.
52
+
53
+ ### LOW
54
+
55
+ 8. **Inconsistent error key** (`msg` vs the rest of the API). Standardize on `error.message`.
56
+
57
+ ## Rewritten contract
58
+
59
+ See `openapi-snippet.yaml` in this package for the corrected version: plural nouns, `201`/`204`/`409`
60
+ status codes, cursor pagination with a capped `limit`, the single `{ data, meta, error }` envelope,
61
+ opaque IDs, and an `Idempotency-Key` on create.
@@ -0,0 +1,82 @@
1
+ # Contract & OpenAPI
2
+
3
+ The contract is the deliverable. Express it as an **OpenAPI 3.1** document so it is human-readable *and*
4
+ machine-checkable. This reference covers how to structure that document and what
5
+ `scripts/lint-openapi.mjs` enforces.
6
+
7
+ ## Why OpenAPI
8
+
9
+ - It is the lingua franca: tooling generates client SDKs, mock servers, request validators, and docs
10
+ from it.
11
+ - It forces you to be explicit about every status code and schema — the things designers skip.
12
+ - It is diffable, so a reviewer can see exactly what a change adds or removes.
13
+
14
+ ## Minimum viable document
15
+
16
+ Every OpenAPI doc must have these top-level fields, or downstream tooling rejects it:
17
+
18
+ ```yaml
19
+ openapi: 3.1.0 # the spec version — required
20
+ info:
21
+ title: Orders API # required
22
+ version: "1.0.0" # required — the API version, not the spec version
23
+ paths: {} # required — the endpoints (may start empty but must exist)
24
+ components:
25
+ schemas: {} # reusable schemas referenced via $ref
26
+ ```
27
+
28
+ `scripts/lint-openapi.mjs` checks exactly these load-bearing fields on a doc supplied as **JSON** (no
29
+ YAML parser dependency): `openapi` is present, `info.title` and `info.version` are non-empty strings,
30
+ `paths` is an object, and every operation's responses carry at least a schema or description. Run it with
31
+ `--selftest` to see it pass a valid doc and fail an invalid one.
32
+
33
+ ## Structure for reuse with `$ref`
34
+
35
+ Define each schema once under `components/schemas` and reference it. Define the **error envelope and
36
+ common error responses once** and `$ref` them from every operation — this is how you guarantee the
37
+ "one error shape everywhere" rule mechanically rather than by discipline.
38
+
39
+ ```yaml
40
+ components:
41
+ responses:
42
+ Error:
43
+ description: Standard error envelope
44
+ content:
45
+ application/json:
46
+ schema: { $ref: "#/components/schemas/ErrorEnvelope" }
47
+ schemas:
48
+ Order:
49
+ type: object
50
+ properties:
51
+ id: { type: string }
52
+ status: { type: string, enum: [open, paid, refunded] }
53
+ ErrorEnvelope:
54
+ type: object
55
+ properties:
56
+ data: { nullable: true }
57
+ error:
58
+ type: object
59
+ properties:
60
+ code: { type: string }
61
+ message: { type: string }
62
+ details: { type: array, items: { type: object } }
63
+ requestId: { type: string }
64
+ ```
65
+
66
+ ## What to specify per operation
67
+
68
+ For every operation:
69
+
70
+ - `summary` — one line.
71
+ - `parameters` — path, query (incl. `limit`/`cursor` for lists), and headers (`Idempotency-Key`).
72
+ - `requestBody` — schema for write operations, `required: true` where applicable.
73
+ - `responses` — **every** status code, success and failure, each with a schema or `$ref`.
74
+ - Auth via `security` referencing a `securityScheme`.
75
+
76
+ ## Checklist before emitting
77
+
78
+ - [ ] Top-level required fields present (`openapi`, `info.title`, `info.version`, `paths`).
79
+ - [ ] Shared schemas and error responses defined once and `$ref`-ed.
80
+ - [ ] Every operation documents both success and error responses.
81
+ - [ ] List operations declare `limit` + `cursor` (or `offset`) parameters.
82
+ - [ ] The doc passes `node scripts/lint-openapi.mjs <doc.json>`.
@@ -0,0 +1,68 @@
1
+ # API Design Checklist
2
+
3
+ Run through this before declaring an API contract done. It is ordered the way you should *design*:
4
+ resources first, cross-cutting rules last. Every box is a place real APIs go wrong in production.
5
+
6
+ ## 1. Resource modeling
7
+
8
+ - [ ] Collections are **plural nouns** (`/orders`, `/users`) — never verbs (`/getOrders` is wrong; the
9
+ verb is `GET`).
10
+ - [ ] Nesting shows ownership and is **at most one level deep** (`/users/{id}/orders`). Deeper than that,
11
+ link by ID instead.
12
+ - [ ] Non-CRUD actions are modeled as sub-resources or controller endpoints
13
+ (`POST /orders/{id}/refunds`), not RPC verbs (`POST /refundOrder`).
14
+ - [ ] Identifiers are **stable and opaque**. Prefer UUID/ULID over auto-increment where enumeration is a
15
+ risk (`ord_01H...` not `/orders/41`).
16
+ - [ ] Field naming is **consistent casing** across the whole API (pick `camelCase` or `snake_case` once).
17
+
18
+ ## 2. HTTP methods & status codes
19
+
20
+ | Method | Use | Safe | Idempotent |
21
+ |---|---|---|---|
22
+ | GET | read | yes | yes |
23
+ | POST | create / non-idempotent action | no | no |
24
+ | PUT | full replace | no | yes |
25
+ | PATCH | partial update | no | no |
26
+ | DELETE | remove | no | yes |
27
+
28
+ - [ ] `GET` **never mutates** state. Caches and proxies rely on this.
29
+ - [ ] Each endpoint lists the **accurate** status codes for success *and* failure.
30
+ - [ ] `201 Created` returns a `Location` header for the new resource.
31
+ - [ ] `204 No Content` for successful deletes with no body.
32
+ - [ ] Never `200 OK` with `{"success": false}` — use real 4xx/5xx codes.
33
+ - [ ] `401` (not authenticated) vs `403` (authenticated, not allowed) are used correctly; `404`-vs-`403`
34
+ is a deliberate choice to avoid leaking existence.
35
+ - [ ] `409` (conflict) vs `422` (semantically invalid) vs `400` (malformed) are distinguished.
36
+
37
+ ## 3. Response envelope & errors
38
+
39
+ - [ ] **One envelope shape** everywhere: `data`, `meta` (for lists), `error`.
40
+ - [ ] On error, `data: null` and a populated `error` with a machine-readable `code`, a human `message`,
41
+ and optional field-level `details`.
42
+ - [ ] The **same** error shape is returned for every failure across every endpoint.
43
+ - [ ] Errors include a `requestId`/correlation id for support.
44
+
45
+ ## 4. Pagination, filtering, sorting
46
+
47
+ - [ ] **Every** collection endpoint paginates. No exceptions — an unbounded list is a latent outage.
48
+ - [ ] `limit` is **capped server-side** (e.g. max 100) so a client can't request a million rows.
49
+ - [ ] Cursor (keyset) pagination for large/growing/real-time data; offset only for small datasets or
50
+ genuine page-number UX.
51
+ - [ ] Filtering and sorting use consistent query-param conventions (`?status=open&sort=-createdAt`).
52
+ - [ ] A unique tiebreaker (e.g. `id`) is part of any sort to avoid dropped/repeated rows at boundaries.
53
+
54
+ ## 5. Auth, rate limiting, idempotency, concurrency
55
+
56
+ - [ ] Auth requirement is documented **per endpoint** (and which scopes/roles).
57
+ - [ ] Authorization is enforced server-side per action (guard against IDOR — check ownership, not just
58
+ authentication).
59
+ - [ ] Rate limits are documented; throttled responses use `429` + `Retry-After`.
60
+ - [ ] `POST`s that create resources accept an `Idempotency-Key` header so retries don't double-create.
61
+ - [ ] Updates support optimistic concurrency (`ETag` + `If-Match`) where lost-update is a real risk.
62
+ - [ ] All input is validated at the boundary; invalid input fails fast with `400`/`422` and field details.
63
+
64
+ ## 6. Evolvability
65
+
66
+ - [ ] An explicit versioning strategy is chosen and documented (see `versioning-and-evolution.md`).
67
+ - [ ] The change you are making is classified **additive (safe)** or **breaking (needs a version)**.
68
+ - [ ] New optional fields default sensibly so old clients keep working.