start-vibing-stacks 2.7.4 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-vibing-stacks",
3
- "version": "2.7.4",
3
+ "version": "2.8.0",
4
4
  "description": "AI-powered multi-stack dev workflow for Claude Code. Supports PHP, Node.js, Python and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,409 @@
1
+ ---
2
+ name: openapi-design
3
+ version: 1.0.0
4
+ description: OpenAPI 3.1 spec design — resource naming, status codes, problem+json (RFC 9457) errors, pagination, versioning, security schemes, idempotency, deprecation. Stack-agnostic. Invoke when designing or documenting any HTTP/REST API that publishes an OpenAPI spec, regardless of framework (Fastify, Hono, FastAPI, Laravel, Express, etc.).
5
+ ---
6
+
7
+ # OpenAPI Design — Universal API Contract
8
+
9
+ **Invoke before adding a new endpoint, before publishing a public API, and during code review of any route handler.**
10
+
11
+ > The OpenAPI document is the contract. Code that drifts from it lies to clients. Generate the spec from code (Zod / Pydantic / DTO classes) — never write it by hand for a real service.
12
+
13
+ This skill covers **what the spec should look like**. For **how to generate it** in your stack, see the per-framework skill (`fastify-api`, `hono-api`, `fastapi-patterns`, `laravel-patterns`, etc.).
14
+
15
+ ---
16
+
17
+ ## 1. Resource Naming
18
+
19
+ | Rule | Good | Bad |
20
+ |---|---|---|
21
+ | Plural nouns | `/users`, `/orders` | `/user`, `/getUser` |
22
+ | No verbs in path | `POST /users` | `/createUser` |
23
+ | Kebab-case multi-word | `/payment-methods` | `/paymentMethods`, `/payment_methods` |
24
+ | Hierarchy via path | `/users/{id}/orders` | `/orders?userId=...` (only for filters) |
25
+ | Verbs only for actions | `POST /orders/{id}/cancel` | `PATCH /orders/{id}` with `action=cancel` |
26
+
27
+ The action sub-resource pattern (`/orders/{id}/cancel`) is the escape hatch when an operation is not a clean CRUD on a resource. Use sparingly.
28
+
29
+ ---
30
+
31
+ ## 2. HTTP Status Codes
32
+
33
+ | Code | Use For | Body |
34
+ |---|---|---|
35
+ | `200 OK` | Successful GET / PUT / PATCH | Resource representation |
36
+ | `201 Created` | Successful POST that creates | New resource + `Location` header |
37
+ | `202 Accepted` | Async work queued | Job ID + status URL |
38
+ | `204 No Content` | DELETE, or PUT/PATCH with no body | Empty |
39
+ | `400 Bad Request` | Malformed request | problem+json |
40
+ | `401 Unauthorized` | Missing / invalid credentials | problem+json + `WWW-Authenticate` header |
41
+ | `403 Forbidden` | Authenticated but not allowed | problem+json |
42
+ | `404 Not Found` | Resource not found OR not visible to caller | problem+json |
43
+ | `409 Conflict` | Version conflict, duplicate key, state mismatch | problem+json |
44
+ | `410 Gone` | Resource permanently removed | problem+json |
45
+ | `422 Unprocessable Entity` | Body parses but fails business validation | problem+json with field errors |
46
+ | `429 Too Many Requests` | Rate limited | problem+json + `Retry-After` |
47
+ | `5xx` | Server fault | problem+json (no internals) |
48
+
49
+ **Never** return `200` with `{ "success": false, "error": ... }`. That breaks every reverse proxy, CDN cache, and HTTP client retry policy. Use the right code.
50
+
51
+ `401` vs `403`: 401 = "I don't know who you are". 403 = "I know who you are and you can't do this". Resources you don't want to leak the existence of: return `404` even if authenticated (with a security review note in the spec).
52
+
53
+ ---
54
+
55
+ ## 3. Error Envelope — `application/problem+json` (RFC 9457)
56
+
57
+ One shape for every error response in the entire API. Codegen clients can match a single discriminator.
58
+
59
+ ```json
60
+ {
61
+ "type": "https://api.example.com/problems/validation-error",
62
+ "title": "Validation failed",
63
+ "status": 422,
64
+ "detail": "The request body did not pass schema validation.",
65
+ "instance": "/orders/01HZ.../validate",
66
+ "errors": [
67
+ { "field": "email", "code": "invalid_format", "message": "Must be a valid email." },
68
+ { "field": "items[0].quantity", "code": "out_of_range", "message": "Must be ≥ 1." }
69
+ ],
70
+ "trace_id": "0af7651916cd43dd8448eb211c80319c"
71
+ }
72
+ ```
73
+
74
+ | Field | Source | Notes |
75
+ |---|---|---|
76
+ | `type` | RFC | URI identifying the error class. Stable across versions; humans can read at the URL |
77
+ | `title` | RFC | Short, human, generic per `type` |
78
+ | `status` | RFC | Mirrors HTTP status — useful when only the body is logged |
79
+ | `detail` | RFC | Specific to **this occurrence**; safe to show to the user |
80
+ | `instance` | RFC | URI/path of the failing operation |
81
+ | `errors[]` | extension | Field-level validation errors (422 only) |
82
+ | `trace_id` | extension | Correlate with logs / observability |
83
+
84
+ **Never** put stack traces, raw SQL, file paths, or PII (email, name) in `detail`. Log those server-side, hand the user the `trace_id`.
85
+
86
+ OpenAPI declaration of the schema (reuse via `$ref` everywhere):
87
+
88
+ ```yaml
89
+ components:
90
+ schemas:
91
+ Problem:
92
+ type: object
93
+ required: [type, title, status]
94
+ properties:
95
+ type: { type: string, format: uri }
96
+ title: { type: string }
97
+ status: { type: integer, minimum: 100, maximum: 599 }
98
+ detail: { type: string }
99
+ instance: { type: string, format: uri-reference }
100
+ trace_id: { type: string }
101
+ ValidationProblem:
102
+ allOf:
103
+ - $ref: '#/components/schemas/Problem'
104
+ - type: object
105
+ properties:
106
+ errors:
107
+ type: array
108
+ items:
109
+ type: object
110
+ required: [field, code, message]
111
+ properties:
112
+ field: { type: string }
113
+ code: { type: string }
114
+ message: { type: string }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 4. Pagination
120
+
121
+ Pick **one** strategy per endpoint and document it. Mixing breaks clients.
122
+
123
+ ### Cursor-based (preferred for large/realtime sets)
124
+
125
+ ```http
126
+ GET /events?limit=50&cursor=eyJpZCI6IjAxSFoifQ
127
+ ```
128
+ ```json
129
+ {
130
+ "data": [ ... ],
131
+ "next_cursor": "eyJpZCI6IjAxSFAifQ",
132
+ "has_more": true
133
+ }
134
+ ```
135
+
136
+ Pros: stable under writes (no skipped/duplicated rows when items are inserted), unbounded depth. Cons: no random access, no total count.
137
+
138
+ ### Offset-based (only for small, finite sets — admin, archives)
139
+
140
+ ```http
141
+ GET /users?page=2&page_size=50
142
+ ```
143
+ ```json
144
+ { "data": [ ... ], "page": 2, "page_size": 50, "total": 1247 }
145
+ ```
146
+
147
+ Cap `page_size` at 100. Cap `page * page_size` (deep pagination is expensive on every database).
148
+
149
+ ### Link header alternative (RFC 8288)
150
+
151
+ ```http
152
+ Link: </users?cursor=eyJ...>; rel="next", </users?cursor=eyI...>; rel="prev"
153
+ ```
154
+
155
+ Useful when the body is a bare array. Most teams prefer the JSON envelope above.
156
+
157
+ ---
158
+
159
+ ## 5. Versioning
160
+
161
+ Pick one strategy at the API level. Don't mix.
162
+
163
+ | Strategy | Example | When |
164
+ |---|---|---|
165
+ | URI segment | `/v1/users` | Public APIs, mobile clients (most common, simplest) |
166
+ | Header | `Accept: application/vnd.example.v1+json` | Hypermedia APIs, content negotiation purists |
167
+ | Query param | `/users?api_version=2024-10-01` | Date-based versioning (Stripe-style) |
168
+
169
+ Date-based (`api_version=2024-10-01`) lets you ship breaking changes more gracefully than `/v2`: clients pin a date, you accumulate changes, deprecate per change. Higher cognitive load to operate.
170
+
171
+ **Never** version internal microservice contracts the same way as public ones. Internal: SemVer the package, deploy in lockstep. Public: dated or `/vN`, kept stable for years.
172
+
173
+ ---
174
+
175
+ ## 6. Deprecation Lifecycle (RFC 8594, RFC 9745)
176
+
177
+ ```http
178
+ HTTP/1.1 200 OK
179
+ Deprecation: @1735689600
180
+ Sunset: Wed, 01 Jan 2026 00:00:00 GMT
181
+ Link: <https://docs.example.com/migration/v2>; rel="deprecation"
182
+ ```
183
+
184
+ | Stage | Action | Communicate via |
185
+ |---|---|---|
186
+ | Announce | Mark `deprecated: true` in OpenAPI; document replacement | spec, docs, CHANGELOG |
187
+ | Headers | Add `Deprecation` + `Sunset` headers on every response | runtime |
188
+ | Monitor | Track usage by client (User-Agent, API key) | observability |
189
+ | Sunset | Return `410 Gone` after the published date | runtime |
190
+
191
+ Rule of thumb: announce at least 6 months for paid B2B, 12 months for free public APIs.
192
+
193
+ ---
194
+
195
+ ## 7. Idempotency
196
+
197
+ Required for any unsafe retry-able operation: payments, signups, write webhooks.
198
+
199
+ ```http
200
+ POST /orders
201
+ Idempotency-Key: 7f9c2bd6-1f3a-4b9e-8a4d-1e2d3a4b5c6d
202
+ Content-Type: application/json
203
+ ```
204
+
205
+ Server contract:
206
+ 1. Hash the request (method + path + body) and store it under the key for ≥ 24h
207
+ 2. Same key + same hash → return the original response
208
+ 3. Same key + **different** hash → `409 Conflict` (client bug)
209
+ 4. Reject keys longer than 255 chars; require client-generated UUIDs
210
+
211
+ Document in the spec which endpoints require it (the IETF `Idempotency-Key` draft is widely adopted: Stripe, Square, PayPal).
212
+
213
+ ---
214
+
215
+ ## 8. Security Schemes
216
+
217
+ Declare every auth method. Multiple are fine — clients pick.
218
+
219
+ ```yaml
220
+ components:
221
+ securitySchemes:
222
+ bearerAuth:
223
+ type: http
224
+ scheme: bearer
225
+ bearerFormat: JWT
226
+ apiKey:
227
+ type: apiKey
228
+ in: header
229
+ name: X-API-Key
230
+ oauth2:
231
+ type: oauth2
232
+ flows:
233
+ authorizationCode:
234
+ authorizationUrl: https://auth.example.com/authorize
235
+ tokenUrl: https://auth.example.com/token
236
+ scopes:
237
+ read:orders: Read user's orders
238
+ write:orders: Create and update orders
239
+ cookieSession:
240
+ type: apiKey
241
+ in: cookie
242
+ name: session
243
+
244
+ security:
245
+ - bearerAuth: []
246
+ # OR per-operation:
247
+ # security:
248
+ # - oauth2: [read:orders]
249
+ ```
250
+
251
+ | Scheme | Use For | Caveats |
252
+ |---|---|---|
253
+ | `bearer` JWT | Service-to-service, mobile/SPA | Short expiry (≤15min), refresh flow |
254
+ | `apiKey` (header) | Server-to-server with rotation | Never in query string (logs leak) |
255
+ | `oauth2` | Third-party access on user's behalf | PKCE for public clients |
256
+ | `cookieSession` | First-party browser session | `HttpOnly`, `Secure`, `SameSite` |
257
+
258
+ **Never** declare `security: []` globally and forget to re-add per operation. Easier to declare globally and override `security: []` only on truly public endpoints (health, login, public docs).
259
+
260
+ ---
261
+
262
+ ## 9. Document Hygiene
263
+
264
+ ### `operationId` matters
265
+
266
+ Codegen uses it as the function name. Make it stable, snake_case or camelCase, descriptive:
267
+
268
+ ```yaml
269
+ paths:
270
+ /users/{id}/orders:
271
+ get:
272
+ operationId: listUserOrders # → client.listUserOrders(...)
273
+ ```
274
+
275
+ Renaming an `operationId` is a breaking change for SDK users. Treat it like a public symbol.
276
+
277
+ ### Reuse via `$ref`
278
+
279
+ Define schemas once, reference everywhere. Avoid repeating field shapes inline — codegen produces ugly anonymous types.
280
+
281
+ ```yaml
282
+ components:
283
+ schemas:
284
+ User: { ... }
285
+ UserList:
286
+ type: object
287
+ properties:
288
+ data: { type: array, items: { $ref: '#/components/schemas/User' } }
289
+ ```
290
+
291
+ ### Examples — always
292
+
293
+ Every schema and parameter should have at least one realistic `example`. Tools like Swagger UI, Redoc, Stoplight render them; mock servers serve them. Empty examples → empty SDK fixtures → no testing.
294
+
295
+ Use **placeholder data only**. Never copy production payloads with real emails, names, IDs.
296
+
297
+ ### Tags
298
+
299
+ Group by resource, not by handler file. ≤ 10 tags total — more becomes navigational soup.
300
+
301
+ ```yaml
302
+ tags:
303
+ - { name: Users, description: User accounts and profiles }
304
+ - { name: Orders, description: Order lifecycle }
305
+ ```
306
+
307
+ ### Servers
308
+
309
+ List every environment so the spec is usable as-is in tooling:
310
+
311
+ ```yaml
312
+ servers:
313
+ - url: https://api.example.com/v1
314
+ description: Production
315
+ - url: https://api.staging.example.com/v1
316
+ description: Staging
317
+ - url: http://localhost:3000/v1
318
+ description: Local dev
319
+ ```
320
+
321
+ ---
322
+
323
+ ## 10. Content Types & Negotiation
324
+
325
+ | Content | Type |
326
+ |---|---|
327
+ | Standard JSON request/response | `application/json` |
328
+ | Errors | `application/problem+json` |
329
+ | File upload | `multipart/form-data` |
330
+ | Streaming events | `text/event-stream` (SSE) or `application/x-ndjson` |
331
+ | Large file download | `application/octet-stream` with `Content-Disposition` |
332
+ | Patch | `application/merge-patch+json` (RFC 7396) or `application/json-patch+json` (RFC 6902) |
333
+
334
+ Don't invent new content types unless you genuinely need to. `application/json` covers 95% of APIs.
335
+
336
+ ---
337
+
338
+ ## 11. CORS
339
+
340
+ Document expected `Access-Control-Allow-Origin`/`Methods`/`Headers` in the OpenAPI description **and** enforce them in middleware. Mismatch between spec and reality is a top integration bug.
341
+
342
+ For browser clients: every endpoint must have CORS configured. Don't rely on a wildcard reverse-proxy default — origin allowlists must be explicit.
343
+
344
+ ---
345
+
346
+ ## 12. What NOT to Put in OpenAPI
347
+
348
+ | Don't | Why | Where instead |
349
+ |---|---|---|
350
+ | Internal/admin endpoints | Spec is shipped to SDK consumers | Separate `internal-openapi.yaml`, not deployed |
351
+ | Experimental endpoints | Tagged `x-internal` is brittle; consumers see them anyway | Feature flag + private spec |
352
+ | Secret schemas | Auth payloads / signed tokens internals | Document only the contract, not the implementation |
353
+ | Real production data in examples | PII leak | Synthetic fixtures |
354
+ | Implementation details in `description` | Rot when you refactor | API behavior only — not the database schema |
355
+
356
+ ---
357
+
358
+ ## 13. Spec-First or Code-First?
359
+
360
+ Both are valid; pick one and be consistent.
361
+
362
+ | Approach | Pro | Con | Tooling |
363
+ |---|---|---|---|
364
+ | **Code-first** (generate spec from Zod / Pydantic / DTO) | Spec never drifts; types are the source of truth | Coupled to framework; spec PRs noisy in diff | `fastify-type-provider-zod`, `@hono/zod-openapi`, FastAPI built-in, drf-spectacular |
365
+ | **Spec-first** (write YAML, generate stubs) | Design conversation in PR; multiple impls | Discipline required; easy for code to drift | OpenAPI Generator, Stoplight, Redocly |
366
+
367
+ For most product APIs in 2025, code-first with Zod/Pydantic wins on velocity. Spec-first is better for: cross-team contracts, public APIs with multiple SDK targets, regulated environments.
368
+
369
+ **Mandatory for code-first:** lock the generated spec into version control and CI-diff it. A breaking change to a route should produce a spec diff in the PR — that's your review checkpoint.
370
+
371
+ ---
372
+
373
+ ## 14. Pre-Merge Checklist
374
+
375
+ - [ ] Every operation has `operationId`, `summary`, response schema, error responses (at least 400, 401/403 if auth, 404 if path param)
376
+ - [ ] All 4xx responses use `application/problem+json` schema
377
+ - [ ] Schemas defined in `components/schemas` and `$ref`'d
378
+ - [ ] Examples present and synthetic (no real PII)
379
+ - [ ] Pagination strategy documented
380
+ - [ ] Security scheme set (global or per-op)
381
+ - [ ] Spec checked into git; CI fails on uncommitted regeneration
382
+ - [ ] Linted with Spectral or Redocly (`oas-3.1-*` rules)
383
+
384
+ ---
385
+
386
+ ## FORBIDDEN
387
+
388
+ | Pattern | Reason |
389
+ |---|---|
390
+ | `200 OK` with `{ "error": ... }` | Breaks every HTTP-aware tool in the chain |
391
+ | Plain-text error bodies on 4xx/5xx | Codegen can't model it; client retries break |
392
+ | Hand-written OpenAPI for a real service | Drifts within one sprint |
393
+ | Renaming `operationId` without major version bump | Breaks every generated SDK |
394
+ | Mixing pagination styles in one API | Client code has to special-case |
395
+ | Real PII in examples | GDPR violation, PR review surface area |
396
+ | Stack traces / SQL / file paths in `detail` | Information disclosure |
397
+ | `security: []` global + forgotten per-op | Endpoints ship publicly unauthenticated |
398
+ | Versioning by both URI and header | Cache fragmentation, doc confusion |
399
+ | Deprecating without `Sunset` header | Clients can't plan migration |
400
+
401
+ ---
402
+
403
+ ## See Also
404
+
405
+ - `security-baseline` — auth patterns, problem+json complements `A03 Injection` defense
406
+ - `fastify-api` (Node) — generate this spec via `@fastify/swagger` + `fastify-type-provider-zod`
407
+ - `fastapi-patterns` (Python) — FastAPI generates 3.1 spec automatically; this skill says **what shape** it should have
408
+ - `laravel-patterns` / `api-design` (PHP) — Laravel + L5-Swagger / Scribe
409
+ - `database-migrations` — coordinate spec deprecation with backwards-compat schema migrations