cfsa-antigravity 2.0.0 → 2.2.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 (116) hide show
  1. package/README.md +14 -0
  2. package/package.json +1 -1
  3. package/template/.agent/instructions/commands.md +8 -32
  4. package/template/.agent/instructions/example.md +21 -0
  5. package/template/.agent/instructions/patterns.md +3 -3
  6. package/template/.agent/instructions/tech-stack.md +71 -23
  7. package/template/.agent/instructions/workflow.md +12 -1
  8. package/template/.agent/rules/completion-checklist.md +6 -0
  9. package/template/.agent/rules/security-first.md +3 -3
  10. package/template/.agent/rules/vertical-slices.md +1 -1
  11. package/template/.agent/skill-library/MANIFEST.md +6 -0
  12. package/template/.agent/skill-library/stack/devops/git-advanced/SKILL.md +972 -0
  13. package/template/.agent/skill-library/stack/devops/git-workflow/SKILL.md +420 -0
  14. package/template/.agent/skills/api-versioning/SKILL.md +44 -298
  15. package/template/.agent/skills/api-versioning/references/typescript.md +157 -0
  16. package/template/.agent/skills/architecture-mapping/SKILL.md +13 -13
  17. package/template/.agent/skills/bootstrap-agents/SKILL.md +151 -152
  18. package/template/.agent/skills/clean-code/SKILL.md +64 -118
  19. package/template/.agent/skills/clean-code/references/typescript.md +126 -0
  20. package/template/.agent/skills/database-schema-design/SKILL.md +93 -317
  21. package/template/.agent/skills/database-schema-design/references/relational.md +228 -0
  22. package/template/.agent/skills/error-handling-patterns/SKILL.md +62 -557
  23. package/template/.agent/skills/error-handling-patterns/references/go.md +162 -0
  24. package/template/.agent/skills/error-handling-patterns/references/python.md +262 -0
  25. package/template/.agent/skills/error-handling-patterns/references/rust.md +112 -0
  26. package/template/.agent/skills/error-handling-patterns/references/typescript.md +178 -0
  27. package/template/.agent/skills/idea-extraction/SKILL.md +322 -224
  28. package/template/.agent/skills/logging-best-practices/SKILL.md +108 -767
  29. package/template/.agent/skills/logging-best-practices/references/go.md +49 -0
  30. package/template/.agent/skills/logging-best-practices/references/python.md +52 -0
  31. package/template/.agent/skills/logging-best-practices/references/typescript.md +215 -0
  32. package/template/.agent/skills/migration-management/SKILL.md +127 -311
  33. package/template/.agent/skills/migration-management/references/relational.md +214 -0
  34. package/template/.agent/skills/parallel-feature-development/SKILL.md +34 -43
  35. package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +1 -1
  36. package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +2 -2
  37. package/template/.agent/skills/pipeline-rubrics/references/scoring.md +1 -1
  38. package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +2 -1
  39. package/template/.agent/skills/prd-templates/SKILL.md +23 -6
  40. package/template/.agent/skills/prd-templates/references/be-spec-template.md +2 -2
  41. package/template/.agent/skills/prd-templates/references/decomposition-templates.md +2 -2
  42. package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +2 -0
  43. package/template/.agent/skills/prd-templates/references/fe-spec-template.md +1 -1
  44. package/template/.agent/skills/prd-templates/references/fractal-cx-template.md +58 -0
  45. package/template/.agent/skills/prd-templates/references/fractal-feature-template.md +93 -0
  46. package/template/.agent/skills/prd-templates/references/fractal-node-index-template.md +55 -0
  47. package/template/.agent/skills/prd-templates/references/ideation-crosscut-template.md +26 -47
  48. package/template/.agent/skills/prd-templates/references/ideation-index-template.md +47 -31
  49. package/template/.agent/skills/prd-templates/references/operational-templates.md +1 -1
  50. package/template/.agent/skills/prd-templates/references/placeholder-workflow-mapping.md +50 -21
  51. package/template/.agent/skills/prd-templates/references/skill-loading-protocol.md +32 -0
  52. package/template/.agent/skills/prd-templates/references/slice-completion-gates.md +29 -0
  53. package/template/.agent/skills/prd-templates/references/spec-coverage-sweep.md +3 -3
  54. package/template/.agent/skills/prd-templates/references/tdd-testing-policy.md +39 -0
  55. package/template/.agent/skills/prd-templates/references/vision-template.md +8 -8
  56. package/template/.agent/skills/regex-patterns/SKILL.md +122 -540
  57. package/template/.agent/skills/regex-patterns/references/go.md +44 -0
  58. package/template/.agent/skills/regex-patterns/references/javascript.md +63 -0
  59. package/template/.agent/skills/regex-patterns/references/python.md +77 -0
  60. package/template/.agent/skills/regex-patterns/references/rust.md +43 -0
  61. package/template/.agent/skills/resolve-ambiguity/SKILL.md +1 -1
  62. package/template/.agent/skills/session-continuity/SKILL.md +11 -9
  63. package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +2 -2
  64. package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +1 -1
  65. package/template/.agent/skills/session-continuity/protocols/05-session-close.md +1 -1
  66. package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +1 -1
  67. package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +57 -78
  68. package/template/.agent/skills/session-continuity/protocols/11-parallel-synthesis.md +1 -1
  69. package/template/.agent/skills/spec-writing/SKILL.md +1 -1
  70. package/template/.agent/skills/tdd-workflow/SKILL.md +94 -317
  71. package/template/.agent/skills/tdd-workflow/references/typescript.md +231 -0
  72. package/template/.agent/skills/testing-strategist/SKILL.md +74 -687
  73. package/template/.agent/skills/testing-strategist/references/typescript.md +328 -0
  74. package/template/.agent/skills/workflow-automation/SKILL.md +62 -154
  75. package/template/.agent/skills/workflow-automation/references/inngest.md +88 -0
  76. package/template/.agent/skills/workflow-automation/references/temporal.md +64 -0
  77. package/template/.agent/workflows/bootstrap-agents-fill.md +85 -143
  78. package/template/.agent/workflows/bootstrap-agents-provision.md +90 -107
  79. package/template/.agent/workflows/create-prd-architecture.md +23 -16
  80. package/template/.agent/workflows/create-prd-compile.md +11 -12
  81. package/template/.agent/workflows/create-prd-design-system.md +1 -1
  82. package/template/.agent/workflows/create-prd-security.md +9 -11
  83. package/template/.agent/workflows/create-prd-stack.md +10 -4
  84. package/template/.agent/workflows/create-prd.md +9 -9
  85. package/template/.agent/workflows/decompose-architecture-structure.md +4 -6
  86. package/template/.agent/workflows/decompose-architecture-validate.md +18 -1
  87. package/template/.agent/workflows/decompose-architecture.md +18 -3
  88. package/template/.agent/workflows/evolve-contract.md +11 -11
  89. package/template/.agent/workflows/evolve-feature-classify.md +14 -6
  90. package/template/.agent/workflows/ideate-discover.md +72 -107
  91. package/template/.agent/workflows/ideate-extract.md +84 -63
  92. package/template/.agent/workflows/ideate-validate.md +26 -22
  93. package/template/.agent/workflows/ideate.md +9 -9
  94. package/template/.agent/workflows/implement-slice-setup.md +25 -23
  95. package/template/.agent/workflows/implement-slice-tdd.md +73 -89
  96. package/template/.agent/workflows/implement-slice.md +4 -4
  97. package/template/.agent/workflows/plan-phase-preflight.md +6 -2
  98. package/template/.agent/workflows/plan-phase-write.md +6 -8
  99. package/template/.agent/workflows/remediate-pipeline-assess.md +2 -1
  100. package/template/.agent/workflows/resolve-ambiguity.md +2 -2
  101. package/template/.agent/workflows/update-architecture-map.md +22 -5
  102. package/template/.agent/workflows/validate-phase-quality.md +155 -0
  103. package/template/.agent/workflows/validate-phase-readiness.md +167 -0
  104. package/template/.agent/workflows/validate-phase.md +19 -157
  105. package/template/.agent/workflows/verify-infrastructure.md +10 -10
  106. package/template/.agent/workflows/write-architecture-spec-design.md +23 -14
  107. package/template/.agent/workflows/write-be-spec-classify.md +25 -21
  108. package/template/.agent/workflows/write-be-spec.md +1 -1
  109. package/template/.agent/workflows/write-fe-spec-classify.md +6 -12
  110. package/template/.agent/workflows/write-fe-spec-write.md +1 -1
  111. package/template/AGENTS.md +6 -2
  112. package/template/GEMINI.md +5 -3
  113. package/template/docs/README.md +10 -10
  114. package/template/docs/kit-architecture.md +126 -33
  115. package/template/docs/plans/ideation/README.md +8 -3
  116. package/template/.agent/skills/prd-templates/references/ideation-domain-template.md +0 -55
@@ -1,16 +1,24 @@
1
1
  ---
2
2
  name: api-versioning
3
3
  description: "Manage API versioning and evolution with URL/header/query strategies, deprecation workflows, breaking change classification, sunset headers, and consumer-driven contract testing. Use when designing versioning strategy, deprecating endpoints, or evolving API contracts."
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  ---
6
6
 
7
7
  # API Versioning & Evolution
8
8
 
9
9
  APIs are contracts with consumers. Breaking that contract destroys trust. This skill covers how to version, evolve, and deprecate APIs without breaking clients.
10
10
 
11
- ## Versioning Strategies
11
+ ## Stack-Specific References
12
+
13
+ After reading the methodology below, read the reference matching your surface's framework:
14
+
15
+ | Framework | Reference |
16
+ |-----------|-----------|
17
+ | TypeScript / Node.js | `references/typescript.md` |
12
18
 
13
- ### Strategy Comparison
19
+ ---
20
+
21
+ ## Versioning Strategies
14
22
 
15
23
  | Strategy | Example | Pros | Cons |
16
24
  |----------|---------|------|------|
@@ -23,94 +31,16 @@ APIs are contracts with consumers. Breaking that contract destroys trust. This s
23
31
 
24
32
  Use **URL path versioning** for public APIs (simplicity and discoverability). Use **header versioning** for internal/partner APIs (cleaner resource model).
25
33
 
26
- ### URL Path Versioning
27
-
28
- ```typescript
29
- // src/pages/api/v1/models/[id].ts
30
- export async function GET({ params }: APIContext) {
31
- // V1 response shape
32
- return new Response(JSON.stringify({
33
- id: params.id,
34
- name: model.name,
35
- provider: model.provider,
36
- // V1 had a flat pricing field
37
- price_per_token: model.pricePerToken,
38
- }));
39
- }
40
-
41
- // src/pages/api/v2/models/[id].ts
42
- export async function GET({ params }: APIContext) {
43
- // V2 response shape (nested pricing)
44
- return new Response(JSON.stringify({
45
- id: params.id,
46
- name: model.name,
47
- provider: model.provider,
48
- // V2 has structured pricing
49
- pricing: {
50
- input: model.inputPrice,
51
- output: model.outputPrice,
52
- currency: 'USD',
53
- unit: 'per_million_tokens',
54
- },
55
- }));
56
- }
57
- ```
58
-
59
- ### Header Versioning
60
-
61
- ```typescript
62
- // Version routing middleware
63
- function versionRouter(handlers: Record<string, Handler>): Handler {
64
- return async (context) => {
65
- const version = context.request.headers.get('API-Version')
66
- ?? context.url.searchParams.get('version')
67
- ?? DEFAULT_VERSION;
68
-
69
- const handler = handlers[version];
70
- if (!handler) {
71
- return new Response(JSON.stringify({
72
- type: 'https://api.example.com/problems/unsupported-version',
73
- title: 'Unsupported API Version',
74
- status: 400,
75
- detail: `API version '${version}' is not supported. Supported versions: ${Object.keys(handlers).join(', ')}`,
76
- }), { status: 400, headers: { 'Content-Type': 'application/problem+json' } });
77
- }
78
-
79
- const response = await handler(context);
80
- response.headers.set('API-Version', version);
81
- return response;
82
- };
83
- }
84
-
85
- // Usage
86
- export const GET = versionRouter({
87
- '1': handleV1,
88
- '2': handleV2,
89
- });
90
- ```
91
-
92
34
  ---
93
35
 
94
36
  ## Default Version Behavior
95
37
 
96
- When a client does not specify a version, the API must behave predictably.
97
-
98
38
  | Strategy | Behavior | When to Use |
99
39
  |----------|----------|-------------|
100
40
  | **Default to latest** | Unversioned requests get the newest version | Internal APIs with controlled consumers |
101
41
  | **Default to oldest supported** | Unversioned requests get V1 | Public APIs (avoid surprise breakage) |
102
42
  | **Require explicit version** | Return 400 if no version specified | Strict APIs where ambiguity is unacceptable |
103
43
 
104
- ```typescript
105
- const DEFAULT_VERSION = '1'; // Conservative default for public APIs
106
-
107
- function resolveVersion(request: Request): string {
108
- return request.headers.get('API-Version')
109
- ?? request.url.searchParams.get('version')
110
- ?? DEFAULT_VERSION;
111
- }
112
- ```
113
-
114
44
  ---
115
45
 
116
46
  ## Breaking vs Non-Breaking Changes
@@ -119,25 +49,25 @@ function resolveVersion(request: Request): string {
119
49
 
120
50
  | Change | Example | Why It Is Safe |
121
51
  |--------|---------|---------------|
122
- | Add optional field to response | `"avatar_url": "..."` added to user response | Clients ignore unknown fields |
52
+ | Add optional field to response | `"avatar_url": "..."` added | Clients ignore unknown fields |
123
53
  | Add optional query parameter | `?sort=name` now supported | Existing queries still work |
124
54
  | Add new endpoint | `POST /api/v1/webhooks` | Does not affect existing endpoints |
125
- | Widen accepted input types | Field accepts `string \| number` instead of just `string` | Existing valid inputs remain valid |
55
+ | Widen accepted input types | Field accepts `string | number` | Existing valid inputs remain valid |
126
56
  | Add optional request field | `"metadata": {}` now accepted | Existing requests without it still work |
127
- | Relax validation | Max length changed from 100 to 200 | Previously valid inputs still valid |
57
+ | Relax validation | Max length 100 200 | Previously valid inputs still valid |
128
58
 
129
59
  ### Breaking (Requires New Version)
130
60
 
131
61
  | Change | Example | Why It Breaks |
132
62
  |--------|---------|---------------|
133
63
  | Remove field from response | `price_per_token` removed | Clients reading this field break |
134
- | Rename field | `price_per_token` renamed to `pricing` | Clients reading old name break |
64
+ | Rename field | `price_per_token` `pricing` | Clients reading old name break |
135
65
  | Change field type | `price` from number to object | Parsing breaks |
136
66
  | Remove endpoint | `DELETE /api/v1/legacy` | Clients calling it get 404 |
137
67
  | Add required request field | `"region"` now required | Existing requests missing it fail |
138
- | Tighten validation | Max length changed from 200 to 100 | Previously valid inputs now rejected |
68
+ | Tighten validation | Max length 200 100 | Previously valid inputs rejected |
139
69
  | Change error response format | Different error shape | Client error handling breaks |
140
- | Change authentication scheme | Bearer token to API key | Auth headers break |
70
+ | Change authentication scheme | Bearer token API key | Auth headers break |
141
71
 
142
72
  ---
143
73
 
@@ -145,29 +75,16 @@ function resolveVersion(request: Request): string {
145
75
 
146
76
  The safest evolution strategy: never remove or rename, only add.
147
77
 
148
- ```typescript
149
- // V1 response (original)
150
- interface ModelV1 {
151
- id: string;
152
- name: string;
153
- price_per_token: number; // original flat field
154
- }
155
-
156
- // V1.1 response (additive evolution --- no new version needed)
157
- interface ModelV1_1 {
158
- id: string;
159
- name: string;
160
- price_per_token: number; // KEPT for backward compatibility
161
- pricing: { // ADDED new structured field
162
- input: number;
163
- output: number;
164
- currency: string;
165
- unit: string;
166
- };
167
- }
168
- // Clients reading price_per_token still work.
169
- // New clients use pricing object.
170
- // Remove price_per_token only in V2.
78
+ ```
79
+ V1 response:
80
+ { id, name, price_per_token }
81
+
82
+ V1.1 response (additive — no new version needed):
83
+ { id, name, price_per_token, pricing: { input, output, currency, unit } }
84
+
85
+ Clients reading price_per_token still work.
86
+ New clients use pricing object.
87
+ Remove price_per_token only in V2.
171
88
  ```
172
89
 
173
90
  ---
@@ -176,101 +93,32 @@ interface ModelV1_1 {
176
93
 
177
94
  ### Sunset Header (RFC 8594)
178
95
 
179
- ```typescript
180
- function addDeprecationHeaders(response: Response, sunsetDate: string, docUrl: string): Response {
181
- response.headers.set('Deprecation', 'true');
182
- response.headers.set('Sunset', sunsetDate); // HTTP date format
183
- response.headers.set('Link', `<${docUrl}>; rel="sunset"`);
184
- return response;
185
- }
186
-
187
- // Usage
188
- const response = await handleV1Request(context);
189
- addDeprecationHeaders(
190
- response,
191
- 'Sat, 01 Mar 2026 00:00:00 GMT',
192
- 'https://docs.example.com/migration/v1-to-v2'
193
- );
194
- ```
96
+ Every deprecated endpoint/version must include these headers:
97
+ - `Deprecation: true`
98
+ - `Sunset: <HTTP date>` — when this version will stop working
99
+ - `Link: <migration-url>; rel="sunset"`
195
100
 
196
101
  ### Deprecation Timeline
197
102
 
198
103
  | Phase | Duration | Actions |
199
104
  |-------|----------|---------|
200
105
  | **Announce** | T-6 months | Add `Deprecation: true` header, publish migration guide |
201
- | **Warn** | T-3 months | Add `Sunset` header with date, send email to consumers |
106
+ | **Warn** | T-3 months | Add `Sunset` header with date, email consumers |
202
107
  | **Monitor** | T-1 month | Track usage, notify active consumers directly |
203
108
  | **Sunset** | T-0 | Return 410 Gone with migration link |
204
109
  | **Remove** | T+3 months | Remove code (keep tests to prevent regression) |
205
110
 
206
- ### Sunset Response
207
-
208
- ```typescript
209
- function sunsetResponse(migrationUrl: string): Response {
210
- return new Response(JSON.stringify({
211
- type: 'https://api.example.com/problems/gone',
212
- title: 'API Version Removed',
213
- status: 410,
214
- detail: `This API version has been sunset. Please migrate to the latest version.`,
215
- migrationGuide: migrationUrl,
216
- }), {
217
- status: 410,
218
- headers: {
219
- 'Content-Type': 'application/problem+json',
220
- 'Link': `<${migrationUrl}>; rel="successor-version"`,
221
- },
222
- });
223
- }
224
- ```
225
-
226
111
  ---
227
112
 
228
113
  ## Consumer-Driven Contract Testing
229
114
 
230
- Consumers define the contract they depend on. The provider runs these contracts in CI to ensure backward compatibility.
231
-
232
- ```typescript
233
- // consumer-contracts/model-service.contract.test.ts
234
- import { describe, it, expect } from 'vitest';
235
-
236
- describe('Model API Contract (Consumer: Dashboard)', () => {
237
- it('GET /api/v1/models/:id returns expected shape', async () => {
238
- const response = await fetch(`${API_URL}/api/v1/models/gpt-4`);
239
- const data = await response.json();
240
-
241
- // Consumer only depends on these fields --- adding fields is fine
242
- expect(data).toMatchObject({
243
- id: expect.any(String),
244
- name: expect.any(String),
245
- provider: expect.any(String),
246
- });
247
-
248
- // These fields must exist with these types
249
- expect(typeof data.id).toBe('string');
250
- expect(typeof data.name).toBe('string');
251
- });
252
-
253
- it('returns 404 for unknown model', async () => {
254
- const response = await fetch(`${API_URL}/api/v1/models/nonexistent`);
255
- expect(response.status).toBe(404);
256
- });
257
- });
258
- ```
115
+ Consumers define the contract they depend on. The provider runs these contracts in CI.
259
116
 
260
- **Provider CI pipeline runs all consumer contracts before deploy:**
261
- ```yaml
262
- # .github/workflows/api-deploy.yml
263
- jobs:
264
- contract-tests:
265
- runs-on: ubuntu-latest
266
- steps:
267
- - name: Run consumer contracts
268
- run: pnpm test:contracts
269
- # Deploy only if contracts pass
270
- deploy:
271
- needs: contract-tests
272
- # ...
273
- ```
117
+ **Concept:**
118
+ 1. Each API consumer writes contract tests specifying fields they depend on
119
+ 2. Provider runs ALL consumer contracts in CI before deploy
120
+ 3. If a consumer contract breaks, the deploy is blocked
121
+ 4. Adding new fields never breaks contracts (consumers ignore unknown fields)
274
122
 
275
123
  ---
276
124
 
@@ -285,118 +133,16 @@ deprecate(api): mark /api/v1/legacy/search as deprecated
285
133
  breaking(api): remove price_per_token field from /api/v2/models response
286
134
  ```
287
135
 
288
- ### Generated Changelog
289
-
290
- ```markdown
291
- ## API Changelog
292
-
293
- ### v2.3.0 (2026-02-15)
294
-
295
- #### Added
296
- - `GET /api/v1/webhooks` - List registered webhooks
297
- - `POST /api/v1/webhooks` - Register a new webhook
298
-
299
- #### Deprecated
300
- - `GET /api/v1/legacy/search` - Use `GET /api/v1/search` instead. Sunset: 2026-08-01.
301
-
302
- #### Fixed
303
- - Pagination cursor encoding now handles special characters correctly
304
-
305
- ### v2.0.0 (2026-01-01)
306
-
307
- #### Breaking Changes
308
- - Removed `price_per_token` field from model responses. Use `pricing` object instead.
309
- - See [migration guide](https://docs.example.com/migration/v1-to-v2).
310
- ```
311
-
312
136
  ---
313
137
 
314
138
  ## Migration Guides
315
139
 
316
- Every version bump must include a migration guide that covers:
317
-
318
- ```markdown
319
- # Migrating from API v1 to v2
320
-
321
- ## Timeline
322
- - **v1 deprecated:** January 1, 2026
323
- - **v1 sunset:** July 1, 2026
324
- - **v1 removed:** October 1, 2026
140
+ Every version bump must include a migration guide:
325
141
 
326
- ## Breaking Changes
327
-
328
- ### Model pricing field restructured
329
-
330
- **Before (v1):**
331
- ```json
332
- { "price_per_token": 0.00003 }
333
- ```
334
-
335
- **After (v2):**
336
- ```json
337
- { "pricing": { "input": 0.00001, "output": 0.00003, "currency": "USD", "unit": "per_million_tokens" } }
338
- ```
339
-
340
- **Migration steps:**
341
- 1. Update your response type definitions
342
- 2. Replace `model.price_per_token` with `model.pricing.output`
343
- 3. Test with v2 endpoint
344
- 4. Update API-Version header to `2`
345
- ```
346
-
347
- ---
348
-
349
- ## Version Routing Middleware
350
-
351
- ```typescript
352
- // src/middleware/api-version.ts
353
- type VersionedHandlers = Record<string, Handler>;
354
-
355
- export function createVersionedRoute(
356
- handlers: VersionedHandlers,
357
- options: {
358
- defaultVersion?: string;
359
- deprecated?: Record<string, { sunset: string; migrationUrl: string }>;
360
- } = {}
361
- ): Handler {
362
- const { defaultVersion, deprecated = {} } = options;
363
-
364
- return async (context) => {
365
- const version = resolveVersion(context.request) ?? defaultVersion;
366
-
367
- if (!version) {
368
- return problemResponse({
369
- type: 'https://api.example.com/problems/version-required',
370
- title: 'API Version Required',
371
- status: 400,
372
- detail: 'Please specify an API version via the API-Version header.',
373
- });
374
- }
375
-
376
- if (!(version in handlers)) {
377
- return problemResponse({
378
- type: 'https://api.example.com/problems/unsupported-version',
379
- title: 'Unsupported API Version',
380
- status: 400,
381
- detail: `Version '${version}' is not supported.`,
382
- });
383
- }
384
-
385
- const response = await handlers[version](context);
386
- response.headers.set('API-Version', version);
387
-
388
- // Add deprecation headers if applicable
389
- if (version in deprecated) {
390
- const { sunset, migrationUrl } = deprecated[version];
391
- response.headers.set('Deprecation', 'true');
392
- response.headers.set('Sunset', sunset);
393
- response.headers.set('Link', `<${migrationUrl}>; rel="sunset"`);
394
- }
395
-
396
- return response;
397
- };
398
- }
399
- ```
142
+ 1. **Timeline** — deprecation date, sunset date, removal date
143
+ 2. **Breaking changes** — before/after for each changed field or endpoint
144
+ 3. **Migration steps** numbered steps to update client code
145
+ 4. **Testing instructions** — how to verify migration works
400
146
 
401
147
  ---
402
148
 
@@ -407,7 +153,7 @@ export function createVersionedRoute(
407
153
  | Increment version for every change | Version only on breaking changes |
408
154
  | Remove old version without notice | Follow deprecation timeline (6+ months) |
409
155
  | Different versioning per endpoint | Consistent strategy across the entire API |
410
- | Version in the response body only | Use URL path or headers --- visible and consistent |
156
+ | Version in the response body only | Use URL path or headers visible and consistent |
411
157
  | No default version behavior | Define and document the default |
412
158
  | Breaking change without migration guide | Every breaking change needs a guide |
413
159
  | No consumer notification | Email, changelog, and headers all used together |
@@ -0,0 +1,157 @@
1
+ # TypeScript API Versioning Patterns
2
+
3
+ Language-specific patterns for the `api-versioning` skill. Read `SKILL.md` first for universal methodology.
4
+
5
+ ---
6
+
7
+ ## URL Path Versioning
8
+
9
+ ```typescript
10
+ // src/pages/api/v1/models/[id].ts
11
+ export async function GET({ params }: APIContext) {
12
+ return new Response(JSON.stringify({
13
+ id: params.id,
14
+ name: model.name,
15
+ provider: model.provider,
16
+ price_per_token: model.pricePerToken, // V1 flat field
17
+ }));
18
+ }
19
+
20
+ // src/pages/api/v2/models/[id].ts
21
+ export async function GET({ params }: APIContext) {
22
+ return new Response(JSON.stringify({
23
+ id: params.id,
24
+ name: model.name,
25
+ provider: model.provider,
26
+ pricing: { // V2 structured pricing
27
+ input: model.inputPrice,
28
+ output: model.outputPrice,
29
+ currency: 'USD',
30
+ unit: 'per_million_tokens',
31
+ },
32
+ }));
33
+ }
34
+ ```
35
+
36
+ ## Header Versioning Middleware
37
+
38
+ ```typescript
39
+ function versionRouter(handlers: Record<string, Handler>): Handler {
40
+ return async (context) => {
41
+ const version = context.request.headers.get('API-Version')
42
+ ?? context.url.searchParams.get('version')
43
+ ?? DEFAULT_VERSION;
44
+
45
+ const handler = handlers[version];
46
+ if (!handler) {
47
+ return new Response(JSON.stringify({
48
+ type: 'https://api.example.com/problems/unsupported-version',
49
+ title: 'Unsupported API Version',
50
+ status: 400,
51
+ detail: `API version '${version}' is not supported.`,
52
+ }), { status: 400, headers: { 'Content-Type': 'application/problem+json' } });
53
+ }
54
+
55
+ const response = await handler(context);
56
+ response.headers.set('API-Version', version);
57
+ return response;
58
+ };
59
+ }
60
+ ```
61
+
62
+ ## Sunset Headers
63
+
64
+ ```typescript
65
+ function addDeprecationHeaders(response: Response, sunsetDate: string, docUrl: string): Response {
66
+ response.headers.set('Deprecation', 'true');
67
+ response.headers.set('Sunset', sunsetDate);
68
+ response.headers.set('Link', `<${docUrl}>; rel="sunset"`);
69
+ return response;
70
+ }
71
+
72
+ function sunsetResponse(migrationUrl: string): Response {
73
+ return new Response(JSON.stringify({
74
+ type: 'https://api.example.com/problems/gone',
75
+ title: 'API Version Removed',
76
+ status: 410,
77
+ detail: 'This API version has been sunset. Please migrate.',
78
+ migrationGuide: migrationUrl,
79
+ }), {
80
+ status: 410,
81
+ headers: {
82
+ 'Content-Type': 'application/problem+json',
83
+ 'Link': `<${migrationUrl}>; rel="successor-version"`,
84
+ },
85
+ });
86
+ }
87
+ ```
88
+
89
+ ## Consumer-Driven Contract Tests (Vitest)
90
+
91
+ ```typescript
92
+ import { describe, it, expect } from 'vitest';
93
+
94
+ describe('Model API Contract (Consumer: Dashboard)', () => {
95
+ it('GET /api/v1/models/:id returns expected shape', async () => {
96
+ const response = await fetch(`${API_URL}/api/v1/models/gpt-4`);
97
+ const data = await response.json();
98
+
99
+ expect(data).toMatchObject({
100
+ id: expect.any(String),
101
+ name: expect.any(String),
102
+ provider: expect.any(String),
103
+ });
104
+ });
105
+
106
+ it('returns 404 for unknown model', async () => {
107
+ const response = await fetch(`${API_URL}/api/v1/models/nonexistent`);
108
+ expect(response.status).toBe(404);
109
+ });
110
+ });
111
+ ```
112
+
113
+ ## Full Version Routing Middleware
114
+
115
+ ```typescript
116
+ export function createVersionedRoute(
117
+ handlers: Record<string, Handler>,
118
+ options: {
119
+ defaultVersion?: string;
120
+ deprecated?: Record<string, { sunset: string; migrationUrl: string }>;
121
+ } = {}
122
+ ): Handler {
123
+ const { defaultVersion, deprecated = {} } = options;
124
+
125
+ return async (context) => {
126
+ const version = resolveVersion(context.request) ?? defaultVersion;
127
+
128
+ if (!version) {
129
+ return problemResponse({
130
+ type: 'https://api.example.com/problems/version-required',
131
+ title: 'API Version Required', status: 400,
132
+ detail: 'Please specify an API version via the API-Version header.',
133
+ });
134
+ }
135
+
136
+ if (!(version in handlers)) {
137
+ return problemResponse({
138
+ type: 'https://api.example.com/problems/unsupported-version',
139
+ title: 'Unsupported API Version', status: 400,
140
+ detail: `Version '${version}' is not supported.`,
141
+ });
142
+ }
143
+
144
+ const response = await handlers[version](context);
145
+ response.headers.set('API-Version', version);
146
+
147
+ if (version in deprecated) {
148
+ const { sunset, migrationUrl } = deprecated[version];
149
+ response.headers.set('Deprecation', 'true');
150
+ response.headers.set('Sunset', sunset);
151
+ response.headers.set('Link', `<${migrationUrl}>; rel="sunset"`);
152
+ }
153
+
154
+ return response;
155
+ };
156
+ }
157
+ ```