@sumrco/cli 0.2.1 → 0.3.1

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 (37) hide show
  1. package/ai/modules/kontract/resources/api-generation-standards.rf.md +83 -0
  2. package/ai/modules/kontract/resources/authoring/configuration.rf.md +220 -0
  3. package/ai/modules/kontract/resources/authoring/design-profile/api-styles.rf.md +241 -0
  4. package/ai/modules/kontract/resources/authoring/design-profile/async-styles.rf.md +134 -0
  5. package/ai/modules/kontract/resources/authoring/design-profile/overview.md +64 -0
  6. package/ai/modules/kontract/resources/authoring/design-profile/schema-layout-styles.rf.md +356 -0
  7. package/ai/modules/kontract/resources/authoring/overview.md +56 -0
  8. package/ai/modules/kontract/resources/authoring/schema-reuse.rf.md +492 -0
  9. package/ai/modules/kontract/resources/authoring/scope-and-splitting.rf.md +244 -0
  10. package/ai/modules/kontract/resources/authoring/spec-layout.rf.md +492 -0
  11. package/ai/modules/kontract/resources/authoring/team-members/spec-author.tm.md +77 -0
  12. package/ai/modules/kontract/resources/{workflows/contract-change.wf.md → authoring/workflows/spec-change.wf.md} +24 -16
  13. package/ai/modules/kontract/resources/generated-output.rf.md +185 -17
  14. package/ai/modules/kontract/resources/openapi-sdk-generator-research.rf.md +57 -0
  15. package/ai/modules/kontract/resources/overview.md +130 -44
  16. package/ai/modules/kontract/resources/performance.rf.md +7 -7
  17. package/ai/modules/kontract/sumr.module.yaml +5 -2
  18. package/ai/modules/mission/sumr.module.yaml +6 -0
  19. package/ai/modules/playbook/resources/authoring/content-structure.rf.md +1 -1
  20. package/ai/modules/playbook/resources/authoring/cross-referencing.rf.md +1 -1
  21. package/ai/modules/playbook/resources/authoring/descriptions.rf.md +1 -1
  22. package/ai/modules/playbook/resources/authoring/extraction.rf.md +1 -1
  23. package/ai/modules/playbook/resources/authoring/flows.rf.md +1 -1
  24. package/ai/modules/playbook/resources/authoring/folder-structure.rf.md +1 -1
  25. package/ai/modules/playbook/resources/authoring/frontmatter.rf.md +4 -1
  26. package/ai/modules/playbook/resources/authoring/markdown.rf.md +1 -1
  27. package/ai/modules/playbook/resources/authoring/overview.md +1 -1
  28. package/ai/modules/playbook/resources/team-members/{playbook-technical-writer.tm.md → technical-writer.tm.md} +3 -2
  29. package/ai/modules/playbook/sumr.module.yaml +7 -2
  30. package/index.js +486 -284
  31. package/package.json +1 -1
  32. package/ai/modules/kontract/resources/configuration.rf.md +0 -123
  33. package/ai/modules/kontract/resources/openapi-generator-lessons.rf.md +0 -61
  34. package/ai/modules/kontract/resources/schema-reuse.rf.md +0 -233
  35. package/ai/modules/kontract/resources/scope-and-splitting.rf.md +0 -147
  36. package/ai/modules/kontract/resources/spec-layout.rf.md +0 -69
  37. package/ai/modules/kontract/resources/team-members/contract-author.tm.md +0 -52
@@ -0,0 +1,492 @@
1
+ ---
2
+ category: reference
3
+ name: schema-reuse
4
+ title: Schema Reuse and Shared Components
5
+ description: "How to author API and async schemas with reusable shared components, $ref, Kontract sets, and safe composition so Kontract output stays consistent and avoids duplicated contract shapes."
6
+ label: Schema Reuse
7
+ when: Adding repeated fields, response envelopes, pagination, errors, schedules, config objects, or reviewing generated diffs with duplicated DTO/model shapes
8
+ order: 12
9
+ ---
10
+
11
+ # Schema Reuse and Shared Components
12
+
13
+ Kontract works best when the spec is designed like a product API, not like a
14
+ collection of copied YAML snippets. If the same concept appears in multiple
15
+ operations or services, define it once and reference it with `$ref`.
16
+
17
+ This reduces drift in the source specs immediately. It also gives Kontract a
18
+ clear path to emit more shared generated code as the generator evolves.
19
+
20
+ Before extracting or naming a shared component, choose a name from product
21
+ language. Shared names should stay specific enough for generated DTO/model/SDK
22
+ symbols.
23
+
24
+ ## Local-first promotion model
25
+
26
+ Keep a schema in the smallest owning place until reuse proves it should move:
27
+
28
+ 1. **Surface-local** — define one-off request, response, and model shapes in the
29
+ root file's `components.schemas`.
30
+ 2. **Capability-local shared** — move the shape into the capability's
31
+ `shared/` folder when two or more surfaces in that capability reuse the same
32
+ concept.
33
+ 3. **Repo-level shared** — move the shape into `schema/shared/` only when two or
34
+ more capabilities share the same semantics, lifecycle, and owner.
35
+
36
+ Do not create shared files only because a shape might become reusable later.
37
+ Shared components are a promotion step, not the default folder for every model.
38
+
39
+ ## What to share
40
+
41
+ Prefer shared components for concepts that mean the same thing everywhere:
42
+
43
+ - scalar primitives: UUIDs, ISO dates, slugs, URLs, MIME types, time strings;
44
+ - path/query parameters: `id`, `recipeId`, pagination, date ranges;
45
+ - standard error responses and error body schemas;
46
+ - pagination metadata;
47
+ - common response envelope fields such as `success`, `timestamp`, `path`, and
48
+ optional `message`;
49
+ - weekly schedules and reusable time-slot shapes;
50
+ - repeated configuration objects, such as measurement units, recipe step labels,
51
+ display colors, notification settings, or payment settings when they truly
52
+ share semantics.
53
+
54
+ Example shared scalar and parameter file:
55
+
56
+ ```yaml
57
+ # schema/shared/common.yml
58
+ components:
59
+ schemas:
60
+ UuidString:
61
+ type: string
62
+ format: uuid
63
+ ImageMimeType:
64
+ type: string
65
+ pattern: '^image\/(jpeg|png|webp|gif)$'
66
+ PaginationMeta:
67
+ type: object
68
+ required: [page, pageSize, pageCount, total]
69
+ properties:
70
+ page:
71
+ type: integer
72
+ minimum: 1
73
+ pageSize:
74
+ type: integer
75
+ minimum: 1
76
+ pageCount:
77
+ type: integer
78
+ minimum: 0
79
+ total:
80
+ type: integer
81
+ minimum: 0
82
+ parameters:
83
+ id:
84
+ name: id
85
+ in: path
86
+ required: true
87
+ schema:
88
+ $ref: '#/components/schemas/UuidString'
89
+ ```
90
+
91
+ Use those components from service specs:
92
+
93
+ ```yaml
94
+ parameters:
95
+ - $ref: ../shared/common.yml#/components/parameters/id
96
+ content:
97
+ application/json:
98
+ schema:
99
+ $ref: ../shared/common.yml#/components/schemas/ImageMimeType
100
+ ```
101
+
102
+ ## Protocol scenarios for reuse
103
+
104
+ Pick the protocol scenario before extracting shared components:
105
+
106
+ 1. **API-only** — share HTTP schemas, parameters, responses, envelopes,
107
+ pagination, errors, and scalar constraints through API schema components.
108
+ 2. **async-only** — share message payload schemas, message enums, scalar
109
+ constraints, and value objects through async schema components or
110
+ capability-local model fragments.
111
+ 3. **API + async** — keep protocol layers separate and share only neutral
112
+ components with identical meaning. The safest cross-protocol sharing is
113
+ usually enums, IDs, slugs, scalar value objects, and simple payload shapes.
114
+
115
+ Do not create cross-protocol shared files only because API schema and async schema
116
+ payloads look similar today. Shared components must have the same product
117
+ meaning and owner.
118
+
119
+ ## API-only reuse
120
+
121
+ If the repo only has API schema sources, keep one-off schemas in the owning
122
+ API schema surface. Use a capability-local `shared/` folder only after multiple
123
+ surfaces in that capability reuse the same concept. Use `schema/shared/` only
124
+ after multiple capabilities share the same concept.
125
+
126
+ ```text
127
+ schema/
128
+ bookstore/
129
+ main.api.yml
130
+ catalog.api.yml
131
+ reviews.api.yml
132
+ shared/
133
+ enums.yml # reused by catalog and reviews
134
+ components.yml
135
+ shared/
136
+ errors.yml # reused by multiple capabilities
137
+ pagination.yml
138
+ ```
139
+
140
+ Use API schema `components` for reusable HTTP shapes:
141
+
142
+ ```yaml
143
+ # schema/bookstore/shared/enums.yml
144
+ components:
145
+ schemas:
146
+ BookFormat:
147
+ type: string
148
+ enum: [hardcover, paperback, ebook]
149
+ ```
150
+
151
+ ## async-only reuse
152
+
153
+ If the repo only has async schema sources, keep reusable message payload schemas and
154
+ enums close to the messaging roots or under `schema/shared/` when several
155
+ message families share the same meaning.
156
+
157
+ ```text
158
+ schema/
159
+ shared/
160
+ message-enums.yml
161
+ bookstore/
162
+ bookstore.async.yml
163
+ messages/
164
+ book-commands.yml
165
+ book-events.yml
166
+ models/
167
+ books.yml
168
+ enums.yml
169
+ ```
170
+
171
+ ```yaml
172
+ # schema/bookstore/models/enums.yml
173
+ BookFormat:
174
+ type: string
175
+ enum: [hardcover, paperback, ebook]
176
+ ```
177
+
178
+ ## API + async shared components
179
+
180
+ When one product capability has both HTTP and messaging contracts, keep protocol
181
+ entrypoints separate and share only business schemas.
182
+
183
+ ```text
184
+ schema/
185
+ bookstore/
186
+ bookstore.api.yml
187
+ bookstore.async.yml
188
+ paths/books.yml
189
+ messages/books.yml
190
+ models/enums.yml
191
+ models/books.yml
192
+ ```
193
+
194
+ Good shared fragment:
195
+
196
+ ```yaml
197
+ # schema/bookstore/models/enums.yml
198
+ BookFormat:
199
+ type: string
200
+ enum: [hardcover, paperback, ebook]
201
+
202
+ # schema/bookstore/models/books.yml
203
+ BookSlug:
204
+ type: string
205
+ minLength: 1
206
+ maxLength: 120
207
+ pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
208
+
209
+ PaginationMeta:
210
+ $ref: ../../shared/common.yml#/components/schemas/PaginationMeta
211
+ ```
212
+
213
+ API schema can use it through path and response fragments:
214
+
215
+ ```yaml
216
+ # schema/bookstore/paths/books.yml
217
+ parameters:
218
+ - name: bookSlug
219
+ in: path
220
+ required: true
221
+ schema:
222
+ $ref: ../models/books.yml#/BookSlug
223
+ get:
224
+ operationId: getBook
225
+ responses:
226
+ "200":
227
+ description: Book detail.
228
+ content:
229
+ application/json:
230
+ schema:
231
+ $ref: ../models/books.yml#/Book
232
+ ```
233
+
234
+ async schema can use the same payload schema from a message fragment:
235
+
236
+ ```yaml
237
+ # schema/bookstore/messages/books.yml
238
+ messages:
239
+ GetBookQuery:
240
+ payload:
241
+ $ref: ../models/books.yml#/GetBookQuery
242
+ BookReply:
243
+ payload:
244
+ $ref: ../models/books.yml#/Book
245
+ ```
246
+
247
+ Keep protocol-specific pieces out of `models/`:
248
+
249
+ - API schema paths, parameters, request bodies, responses → `paths/` or
250
+ `components/`.
251
+ - async schema channels, operations, messages → `messages/`.
252
+ - Payload objects, enums, scalars, and reusable business shapes → `models/`.
253
+ - Share enums through `models/enums.yml` when API schema and async schema use the same
254
+ vocabulary and values.
255
+
256
+ This keeps API contracts, async messages, and generated models aligned without
257
+ making either protocol depend on the other's metadata.
258
+
259
+ ## Kontract sets for reusable map fragments
260
+
261
+ Use `kontract.sets` when repeated YAML maps make the source noisy but the
262
+ reusable concept is not a standalone schema. `$sets` can apply reusable maps in
263
+ responses, parameters, schema properties, headers, request bodies, async
264
+ operations, messages, payload schemas, or any other mapping.
265
+
266
+ ```yaml
267
+ # schema/bookings/shared/components.yml
268
+ kontract:
269
+ sets:
270
+ StandardErrors:
271
+ "400":
272
+ $ref: '#/components/responses/BadRequest'
273
+ "401":
274
+ $ref: '#/components/responses/Unauthorized'
275
+ "403":
276
+ $ref: '#/components/responses/Forbidden'
277
+ "404":
278
+ $ref: '#/components/responses/NotFound'
279
+ "500":
280
+ $ref: '#/components/responses/InternalServerError'
281
+ AuditedFields:
282
+ createdAt:
283
+ type: string
284
+ format: date-time
285
+ updatedAt:
286
+ type: string
287
+ format: date-time
288
+ components:
289
+ responses:
290
+ BadRequest:
291
+ description: Bad request.
292
+ Unauthorized:
293
+ description: Unauthorized.
294
+ Forbidden:
295
+ description: Forbidden.
296
+ NotFound:
297
+ description: Not found.
298
+ InternalServerError:
299
+ description: Internal server error.
300
+ ```
301
+
302
+ ```yaml
303
+ # schema/bookings/public.api.yml
304
+ api: 3.0.3
305
+ paths:
306
+ /bookings:
307
+ post:
308
+ responses:
309
+ $sets:
310
+ - $ref: ./shared/components.yml#/kontract/sets/StandardErrors
311
+ "201":
312
+ description: Booking created.
313
+ content:
314
+ application/json:
315
+ schema:
316
+ $ref: '#/components/schemas/CreateBookingResponse'
317
+ components:
318
+ schemas:
319
+ Booking:
320
+ type: object
321
+ properties:
322
+ $sets:
323
+ - $ref: ./shared/components.yml#/kontract/sets/AuditedFields
324
+ id:
325
+ type: string
326
+ ```
327
+
328
+ Set rules:
329
+
330
+ - `$sets` merges maps only; it does not concatenate arrays.
331
+ - Local keys override set keys.
332
+ - Multiple sets that define the same key must use identical values.
333
+ - Kontract rebases copied `$ref` values when a set comes from another file.
334
+ - Normalized files passed to validators and generators do not contain
335
+ `kontract` or `$sets`.
336
+
337
+ ## Response envelopes
338
+
339
+ If every endpoint repeats the same envelope fields, do not retype them in every
340
+ schema. Put the shared part in a component and compose concrete responses with
341
+ `allOf`.
342
+
343
+ ```yaml
344
+ # schema/shared/common.yml
345
+ components:
346
+ schemas:
347
+ SuccessResponseBase:
348
+ type: object
349
+ required: [success, timestamp, path]
350
+ properties:
351
+ success:
352
+ type: boolean
353
+ timestamp:
354
+ type: string
355
+ format: date-time
356
+ path:
357
+ type: string
358
+ message:
359
+ type: string
360
+ ```
361
+
362
+ ```yaml
363
+ # schema/recipes/catalog.api.yml
364
+ components:
365
+ schemas:
366
+ RecipesFindOneResponse:
367
+ allOf:
368
+ - $ref: ../shared/common.yml#/components/schemas/SuccessResponseBase
369
+ - type: object
370
+ required: [data]
371
+ properties:
372
+ data:
373
+ $ref: '#/components/schemas/RecipeResponse'
374
+ ```
375
+
376
+ Use the same pattern for paginated responses:
377
+
378
+ ```yaml
379
+ components:
380
+ schemas:
381
+ RecipesFindAllResponse:
382
+ allOf:
383
+ - $ref: ../shared/common.yml#/components/schemas/SuccessResponseBase
384
+ - type: object
385
+ required: [data]
386
+ properties:
387
+ data:
388
+ type: array
389
+ items:
390
+ $ref: '#/components/schemas/RecipeResponse'
391
+ meta:
392
+ $ref: ../shared/common.yml#/components/schemas/PaginationMeta
393
+ ```
394
+
395
+ Kontract supports object `allOf` composition by flattening supported object refs
396
+ and inline object parts. Today this mainly reduces YAML duplication and contract
397
+ semantic drift. It may still emit concrete generated DTO/model properties per
398
+ response until the generator grows preserved composition or shared-output
399
+ emission.
400
+
401
+ ## Schedules and repeated nested objects
402
+
403
+ If several features define the same day-of-week schedule or time-slot object,
404
+ share the object definitions instead of duplicating each day shape.
405
+
406
+ ```yaml
407
+ # schema/shared/scheduling.yml
408
+ components:
409
+ schemas:
410
+ WeeklyTimeSlot:
411
+ type: object
412
+ required: [from, to]
413
+ properties:
414
+ from:
415
+ type: string
416
+ pattern: '^(0[6-9]|1\d|2[0-3]):[0-5]\d$'
417
+ to:
418
+ type: string
419
+ pattern: '^(0[6-9]|1\d|2[0-3]):[0-5]\d$'
420
+ WeeklySchedule:
421
+ type: object
422
+ properties:
423
+ sunday:
424
+ type: array
425
+ items:
426
+ $ref: '#/components/schemas/WeeklyTimeSlot'
427
+ monday:
428
+ type: array
429
+ items:
430
+ $ref: '#/components/schemas/WeeklyTimeSlot'
431
+ ```
432
+
433
+ Then service-specific schedules can reference or compose those shared pieces.
434
+
435
+ ## Review checklist
436
+
437
+ After writing or updating any `*.api.yml` or `*.async.yml` file, review
438
+ the spec before running generation. Actively look for these smells:
439
+
440
+ - the same `success/timestamp/path/message` fields repeated in many response
441
+ schemas;
442
+ - identical pagination `meta` objects or `PaginationMeta` definitions copied
443
+ across services instead of referencing a shared component;
444
+ - many `id` parameters declared inline instead of referencing a shared
445
+ parameter;
446
+ - inline UUID, date, date-time, URL, slug, or enum-like scalar constraints that
447
+ already exist in a shared component;
448
+ - identical regex patterns copied into multiple schemas;
449
+ - repeated day-of-week schedule structures;
450
+ - error response schemas or problem-details bodies copied instead of referencing
451
+ shared responses;
452
+ - event envelopes or async schema reply wrappers copied with the same product
453
+ meaning;
454
+ - generated diffs adding many near-identical DTO/model files after a small API
455
+ change.
456
+
457
+ When you see a smell, propose a shared component before generating.
458
+
459
+ ## When not to share
460
+
461
+ Do not share only because two shapes currently look alike. Keep separate
462
+ components when:
463
+
464
+ - endpoints have different validation rules or lifecycle/backwards-compatibility
465
+ needs;
466
+ - a request DTO and response DTO intentionally differ, even if fields overlap;
467
+ - names would become vague (`CommonThing`, `BaseData`, `GenericConfig`);
468
+ - the reusable name would hide a real product term, such as using `Collection`
469
+ when collections are a product concept;
470
+ - sharing would force unrelated teams or product areas to coordinate every
471
+ future change.
472
+
473
+ A good shared component has a clear product meaning and a stable owner.
474
+
475
+ ## Safe workflow
476
+
477
+ 1. Keep new schemas local to the root or surface that owns them.
478
+ 2. When reuse appears inside one capability, move the repeated shape into that
479
+ capability's `shared/` folder and replace duplicates with `$ref`.
480
+ 3. When reuse appears across capabilities, promote the shape to
481
+ `schema/shared/` and update `$ref` links deliberately.
482
+ 4. Use supported `allOf` object composition when a concrete response extends a
483
+ shared base.
484
+ 5. Run `sumr kontract validate`.
485
+ 6. Run a scoped generation first, for example:
486
+
487
+ ```bash
488
+ sumr kontract generate --source api --target backend --specific recipes/catalog
489
+ ```
490
+
491
+ 7. Review both the spec diff and generated diff. If generated output changed in
492
+ unrelated product areas, stop and investigate before committing.