@rse/specbook 1.5.2 → 1.6.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 (63) hide show
  1. package/LICENSE.txt +201 -0
  2. package/README.md +288 -43
  3. package/dst/specbook-api.d.ts +23 -3
  4. package/dst/specbook-api.js +133 -59
  5. package/dst/specbook-cli.js +93 -26
  6. package/dst/specbook-cmd-describe.js +4 -4
  7. package/dst/specbook-cmd-export-schema.d.ts +8 -0
  8. package/dst/specbook-cmd-export-schema.js +441 -0
  9. package/dst/specbook-cmd-export.js +23 -6
  10. package/dst/specbook-cmd-init-sample.d.ts +4 -0
  11. package/dst/specbook-cmd-init-sample.js +322 -0
  12. package/dst/specbook-cmd-init.d.ts +1 -0
  13. package/dst/specbook-cmd-init.js +42 -10
  14. package/dst/specbook-cmd-lint.js +46 -48
  15. package/dst/specbook-cmd-query.d.ts +30 -0
  16. package/dst/specbook-cmd-query.js +165 -0
  17. package/dst/specbook-config.d.ts +9 -1
  18. package/dst/specbook-config.js +135 -10
  19. package/dst/specbook-coverage.js +2 -2
  20. package/dst/specbook-diagram.d.ts +7 -5
  21. package/dst/specbook-diagram.js +546 -143
  22. package/dst/specbook-export-ast.d.ts +1 -1
  23. package/dst/specbook-export-ast.js +11 -8
  24. package/dst/specbook-export-code.js +3 -3
  25. package/dst/specbook-export-common.d.ts +1 -1
  26. package/dst/specbook-export-common.js +3 -2
  27. package/dst/specbook-export-diagram.d.ts +1 -1
  28. package/dst/specbook-export-diagram.js +25 -7
  29. package/dst/specbook-export-html-fold.d.ts +16 -6
  30. package/dst/specbook-export-html-fold.js +65 -44
  31. package/dst/specbook-export-html-render.js +107 -26
  32. package/dst/specbook-export-html-search.js +18 -20
  33. package/dst/specbook-export-html-template.js +38 -9
  34. package/dst/specbook-export-html-widget.d.ts +2 -0
  35. package/dst/specbook-export-html-widget.js +219 -19
  36. package/dst/specbook-export-html.css +208 -38
  37. package/dst/specbook-export-html.js +96 -53
  38. package/dst/specbook-export-md.d.ts +2 -1
  39. package/dst/specbook-export-md.js +7 -8
  40. package/dst/specbook-export-pdf.js +1 -1
  41. package/dst/specbook-files.d.ts +2 -0
  42. package/dst/specbook-files.js +41 -0
  43. package/dst/specbook-format-meta.yaml +401 -0
  44. package/dst/specbook-format-pm.yaml +1240 -0
  45. package/dst/specbook-format-schema.d.ts +33 -3
  46. package/dst/specbook-format-schema.js +49 -3
  47. package/dst/specbook-format.md +427 -47
  48. package/dst/specbook-format.yaml +524 -228
  49. package/dst/specbook-ignore.js +2 -2
  50. package/dst/specbook-link.d.ts +4 -0
  51. package/dst/specbook-link.js +61 -6
  52. package/dst/specbook-mcp.js +103 -19
  53. package/dst/specbook-parse-semantic.d.ts +1 -0
  54. package/dst/specbook-parse-semantic.js +11 -4
  55. package/dst/specbook-parse-syntax.d.ts +1 -0
  56. package/dst/specbook-parse-syntax.js +1 -1
  57. package/dst/specbook-parse.d.ts +1 -1
  58. package/dst/specbook-parse.js +3 -3
  59. package/dst/specbook-project.d.ts +2 -0
  60. package/dst/specbook-project.js +15 -5
  61. package/dst/specbook-remote.d.ts +28 -0
  62. package/dst/specbook-remote.js +429 -0
  63. package/package.json +32 -20
@@ -76,11 +76,26 @@ type SchemaObject = {
76
76
  referenced?: string[]
77
77
  coverage?: string[]
78
78
  automaton?: SchemaAutomaton
79
- diagram?: SchemaDiagram
79
+ badge?: SchemaBadge
80
+ multiple?: SchemaMultiple
81
+ diagram?: SchemaDiagram | SchemaDiagram[]
80
82
  format?: SchemaFormat
81
83
  props?: SchemaProperty[]
82
84
  children?: SchemaObject[]
83
85
  }
86
+ type SchemaBadge = BadgeIcon | {
87
+ property: string
88
+ map: Record<string, BadgeIcon>
89
+ default?: BadgeIcon
90
+ }
91
+ type SchemaMultiple = {
92
+ property: string
93
+ pattern: string
94
+ }
95
+ type BadgeIcon =
96
+ "person" | "team" | "bot" | "device" | "mobile" | "browser" | "service" |
97
+ "function" | "api" | "code" | "package" | "database" | "cache" | "queue" |
98
+ "document" | "mail" | "communication" | "clock" | "security" | "cloud" | "network"
84
99
  type SchemaAutomaton = {
85
100
  nodes: string
86
101
  edges: string
@@ -103,24 +118,48 @@ type SchemaDiagramCenterEdges = {
103
118
  labeled?: string
104
119
  via?: string
105
120
  }
121
+ type SchemaDiagramNest = {
122
+ properties?: string[]
123
+ parent?: boolean
124
+ kind?: boolean
125
+ layout?: "graph" | "hub" | "grid"
126
+ crossing?: "nodes" | "target" | "both"
127
+ }
106
128
  type SchemaDiagram = {
107
- type?: "graph" | "hub" | "grid"
129
+ type?: "graph" | "hub" | "grid" | "state" | "layer" |
130
+ "slice" | "tree" | "matrix" | "sequence" | "petri" |
131
+ "gantt"
108
132
  nodes?: string
109
133
  edges?: string
134
+ local?: boolean
110
135
  center?: string | SchemaDiagramCenter
111
136
  centerEdges?: SchemaDiagramCenterEdges
112
- links?: "props" | "all"
137
+ links?: "props" | "all" | string[]
113
138
  labeled?: boolean
114
139
  edgeSource?: string
115
140
  edgeTarget?: string
116
141
  edgeArity?: string
142
+ edgeGuard?: string
143
+ edgeKind?: string
144
+ edgeDuration?: string
145
+ edgeTime?: string
146
+ places?: string
147
+ tokens?: string
148
+ capacity?: string
149
+ duration?: string
150
+ start?: string
117
151
  hierarchy?: boolean
118
152
  deep?: boolean
153
+ descend?: string[]
154
+ reverse?: string[]
119
155
  onlyConnected?: boolean
120
156
  collapse?: boolean
121
157
  qualified?: boolean
158
+ ordered?: boolean
159
+ markUncovered?: "none" | "referenced" | "coverage" | "all"
160
+ nest?: SchemaDiagramNest
122
161
  properties?: string[]
123
- config?: Partial<GradiaConfig>
162
+ config?: SchemaGradiaConfig
124
163
  }
125
164
  type SchemaFormat = {
126
165
  type?: "auto" | "complex" | "compact" | "concise"
@@ -154,13 +193,31 @@ type SchemaGradiaConfig = Partial<{
154
193
  "color-node-self-name": string
155
194
  "color-node-self-box": string
156
195
  "color-node-self-border": string
196
+ "color-node-lifeline": string
157
197
  "color-group-name": string
158
198
  "color-group-box": string
159
199
  "color-group-border": string
200
+ "color-container-name": string
201
+ "color-container-box": string
202
+ "color-container-border": string
203
+ "color-composite-border": string
204
+ "color-node-pseudo-name": string
205
+ "color-node-pseudo-box": string
206
+ "color-node-pseudo-border": string
207
+ "color-node-place-name": string
208
+ "color-node-place-box": string
209
+ "color-node-place-border": string
160
210
  "color-edge-line": string
161
211
  "color-edge-name": string
162
212
  "color-edge-arity": string
163
213
  "color-edge-halo": string
214
+ "color-mark-violation": string
215
+ "color-mark-critical": string
216
+ "size-font-node": number
217
+ "size-font-type": number
218
+ "size-font-prop": number
219
+ "size-font-edge": number
220
+ "size-font-arity": number
164
221
  "size-canvas-margin": number
165
222
  "size-node-width-min": number
166
223
  "size-node-width-max": number
@@ -171,6 +228,7 @@ type SchemaGradiaConfig = Partial<{
171
228
  "size-edge-port-gap": number
172
229
  "group-box-padding": number
173
230
  "group-box-gap": number
231
+ "container-box-padding": number
174
232
  "graph-columns-max": number
175
233
  "graph-channel-width-max": number
176
234
  "graph-channel-width-min": number
@@ -183,12 +241,28 @@ type SchemaGradiaConfig = Partial<{
183
241
  "hub-channel-width-min": number
184
242
  "hub-node-gap": number
185
243
  "hub-node-degree-max": number
244
+ "hub-node-count-max": number
186
245
  "grid-columns-max": number
187
246
  "grid-columns-min": number
188
247
  "grid-gap-horizontal": number
189
248
  "grid-gap-vertical": number
190
249
  "grid-node-width-equal": boolean
191
250
  "grid-node-height-equal": boolean
251
+ "sequence-column-gap": number
252
+ "sequence-message-gap": number
253
+ "tree-level-gap": number
254
+ "tree-sibling-gap": number
255
+ "tree-row-max": number
256
+ "tree-stack-min": number
257
+ "tree-stack-double": boolean
258
+ "tree-node-width-equal": boolean
259
+ "tree-horizontal": boolean
260
+ "gantt-unit-width": number
261
+ "gantt-unit-name": string
262
+ "gantt-critical": boolean
263
+ "mark-violation": boolean
264
+ "layer-numbered": boolean
265
+ "slice-numbered": boolean
192
266
  }>
193
267
  ```
194
268
 
@@ -206,17 +280,22 @@ type SchemaGradiaConfig = Partial<{
206
280
  BECAUSE it discriminates objects and anchors `[[xxx]]` references
207
281
 
208
282
  - `SchemaObject.name?: string`:
209
- exact artifact name (level 1) or name regex (deeper levels),
210
- BECAUSE artifacts are fixed documents, deeper names need a rule only
283
+ exact artifact name (level 1) or name regex (deeper levels), where a
284
+ level 1 object kind with neither `name` nor `id` is a *recurring*
285
+ artifact, which any number (also none) of level 1 objects of its
286
+ kind match, all in its `file`,
287
+ BECAUSE artifacts are fixed documents, deeper names need a rule only,
288
+ while a catalog of equally shaped artifacts knows no fixed names
211
289
 
212
290
  - `SchemaObject.id?: string`:
213
291
  short locally-unique identifier the object explicitly carries in its heading,
214
292
  BECAUSE stable ids keep `[[xxx]]` references free of prose names
215
293
 
216
294
  - `SchemaObject.file?: string`:
217
- Markdown file the level 1 artifact is generated into and resides in,
295
+ Markdown file the level 1 artifact is generated into (by `init`),
218
296
  optionally shared with the other artifacts carrying the same file,
219
- BECAUSE artifacts must map deterministically onto files
297
+ BECAUSE artifacts must map deterministically onto files when created,
298
+ while every `.md` file below the base directory is parsed afterwards
220
299
 
221
300
  - `SchemaObject.desc?: string`:
222
301
  prose description of the object kind,
@@ -253,10 +332,38 @@ type SchemaGradiaConfig = Partial<{
253
332
  BECAUSE a lifecycle is only trustworthy when its dead-ends and
254
333
  livelocks are ruled out
255
334
 
256
- - `SchemaObject.diagram?: SchemaDiagram`:
257
- diagram generated for every object of this kind (left out when the
258
- configuration is emitted compressed by level 2 or higher),
259
- BECAUSE relations buried in references are graspable only when drawn
335
+ - `SchemaObject.badge?: SchemaBadge`:
336
+ badge icon drawn inside the node box of every object of this kind in
337
+ every diagram (left out when the configuration is emitted compressed
338
+ by level 2 or higher),
339
+ BECAUSE the nature of a node is recognized at a glance from an icon,
340
+ while its type label has to be read
341
+
342
+ - `SchemaBadge`:
343
+ a fixed icon, or the icon the value of the `property` of an object
344
+ maps onto through `map` (a mapped value of an `enum(...)` valued
345
+ property has to be one of its members), else the `default` icon,
346
+ else none,
347
+ BECAUSE the kind alone does not always tell the nature of an object
348
+ -- a `Server` node and a `Function` node deserve different icons
349
+
350
+ - `SchemaObject.multiple?: SchemaMultiple`:
351
+ the several instances an object of this kind stands for, drawn as a
352
+ stack of node boxes in every diagram: whenever the value of the
353
+ `property` matches the (unanchored) regular expression `pattern`,
354
+ e.g. `0..n per environment` matched by a pattern for a range with an
355
+ upper bound beyond 1 (left out when the configuration is emitted
356
+ compressed by level 2 or higher),
357
+ BECAUSE a replicated node is a different operational beast than a
358
+ single one, yet both are specified by one object
359
+
360
+ - `SchemaObject.diagram?: SchemaDiagram | SchemaDiagram[]`:
361
+ diagram, or list of diagrams (rendered in their order), generated for
362
+ every object of this kind (left out when the configuration is emitted
363
+ compressed by level 2 or higher), where merged configuration files
364
+ match the diagrams of a list by their `type`,
365
+ BECAUSE relations buried in references are graspable only when drawn,
366
+ and the same relations often tell more than one story
260
367
 
261
368
  - `SchemaObject.format?: SchemaFormat`:
262
369
  HTML/PDF rendering format of the objects of this kind (left out when
@@ -308,8 +415,19 @@ type SchemaGradiaConfig = Partial<{
308
415
  diagram derived for every object of an object kind,
309
416
  BECAUSE the relations of a specification are graspable only when drawn
310
417
 
311
- - `SchemaDiagram.type?: "graph" | "hub" | "grid"`:
418
+ - `SchemaDiagram.type?: "graph" | "hub" | "grid" | "state" | "layer" |
419
+ "slice" | "tree" | "matrix" | "sequence" | "petri" | "gantt"`:
312
420
  diagram shape: `graph` (free), `hub` (centered), `grid` (edge-less),
421
+ `state` (state machine, with the `automaton` flags as pseudo-states),
422
+ `layer` (stacked bands, with the `nest` containers as the layers),
423
+ `slice` (side-by-side bands, with the `nest` containers as the
424
+ slices), `tree` (tidy tree, with the `nest` containers as the
425
+ parents), `matrix` (dependency structure matrix, with the `nest`
426
+ containers as the partitions), `sequence` (sequence diagram, with
427
+ the edge objects as the messages), `petri` (Petri net, with the
428
+ `places` objects as the places and all other nodes as the transitions),
429
+ `gantt` (Gantt chart, with the nodes as the tasks, the edges as their
430
+ dependencies, and the `nest` containers as the partitions),
313
431
  BECAUSE one object set tells different stories as different shapes
314
432
 
315
433
  - `SchemaDiagram.nodes?: string`:
@@ -320,6 +438,12 @@ type SchemaGradiaConfig = Partial<{
320
438
  comma-separated `[[xxx]]` patterns of the objects acting as edges, not nodes,
321
439
  BECAUSE some relations are objects, yet still connect two nodes
322
440
 
441
+ - `SchemaDiagram.local?: boolean`:
442
+ whether the `nodes` and `edges` patterns match only the object
443
+ itself and its descendants (default: `false`),
444
+ BECAUSE the patterns match corpus-wide, while a per-object diagram
445
+ of a recurring kind shows the own subtree only
446
+
323
447
  - `SchemaDiagram.center?: string | SchemaDiagramCenter`:
324
448
  object a `hub` diagram is projected onto (default: `self`), or a
325
449
  synthetic center node, labeled from a referenced `source` object (or
@@ -341,9 +465,12 @@ type SchemaGradiaConfig = Partial<{
341
465
  synthetic center, so their relation to it lives in a direction
342
466
  property instead
343
467
 
344
- - `SchemaDiagram.links?: "props" | "all"`:
345
- edge source: property values (`props`) or also texts (`all`),
346
- BECAUSE prose references are incidental and need an opt-in
468
+ - `SchemaDiagram.links?: "props" | "all" | string[]`:
469
+ edge source: property values (`props`), also texts (`all`), or the
470
+ values of the listed properties only (e.g. `[ DEPENDS-ON ]`, where
471
+ an empty list derives no reference edges at all),
472
+ BECAUSE prose references are incidental and need an opt-in, and a
473
+ diagram about one relation must not mix in the others
347
474
 
348
475
  - `SchemaDiagram.labeled?: boolean`:
349
476
  whether the edges derived from property values carry the lower-cased
@@ -356,13 +483,35 @@ type SchemaGradiaConfig = Partial<{
356
483
 
357
484
  - `SchemaDiagram.edgeTarget?: string`:
358
485
  edge property naming the target node (default: the first non-source
359
- property whose value is a single reference),
486
+ property whose value is a single reference), where a `list(...)`
487
+ of references yields one edge per target,
360
488
  BECAUSE the convention breaks on multiple references per object
361
489
 
362
490
  - `SchemaDiagram.edgeArity?: string`:
363
491
  edge property carrying the edge arity (default: `ARITY`),
364
492
  BECAUSE cardinality labels are named domain-specifically
365
493
 
494
+ - `SchemaDiagram.edgeGuard?: string`:
495
+ edge property whose value follows the edge name in brackets
496
+ (e.g. `submit [complete]`), rendered italic in a `state` diagram,
497
+ BECAUSE a transition is only understood together with its condition
498
+
499
+ - `SchemaDiagram.edgeKind?: string`:
500
+ edge property whose value is the message kind of a `sequence`
501
+ diagram: `Call` (the default), `Signal`, `Return`, `Create`, or
502
+ `Destroy` (case-insensitive, plus the Gradia names `sync` and `async`),
503
+ BECAUSE a call, a notification, and a response read differently
504
+
505
+ - `SchemaDiagram.edgeDuration?: string`:
506
+ edge property whose value is the duration constraint of a call of a
507
+ `sequence` diagram up to its return (e.g. `< 200ms`),
508
+ BECAUSE response time budgets belong to the interaction they bound
509
+
510
+ - `SchemaDiagram.edgeTime?: string`:
511
+ edge property whose value is the time constraint or observation at
512
+ the start of a message of a `sequence` diagram (e.g. `t = now`),
513
+ BECAUSE some interactions are defined by their timing
514
+
366
515
  - `SchemaDiagram.hierarchy?: boolean`:
367
516
  whether containment edges derive from the nesting (default: `false`),
368
517
  BECAUSE the nesting carries no reference and would stay invisible
@@ -372,8 +521,54 @@ type SchemaGradiaConfig = Partial<{
372
521
  counted as the arity (default: `false`),
373
522
  BECAUSE the relations between coarse objects live in their fine parts
374
523
 
524
+ - `SchemaDiagram.descend?: string[]`:
525
+ child object kinds whose references (and those of their descendants
526
+ of the listed kinds) count as the uncounted own references of their
527
+ node, labeled with the lower-cased kind under `labeled` (e.g.
528
+ `[ MESSAGE ]`, so an interaction references the components its
529
+ messages involve),
530
+ BECAUSE a relation can live in a part of an object without the
531
+ whole object's references being worth counting
532
+
533
+ - `SchemaDiagram.reverse?: string[]`:
534
+ properties whose reference edges point back from the referenced
535
+ object onto the referencing node (e.g. `[ CONSUMES ]`, so a unit
536
+ consuming a resource yields the arc from the resource to the unit),
537
+ BECAUSE a relation drawn against the direction of its reference
538
+ keeps the references of the specification pointing backward
539
+
540
+ - `SchemaDiagram.places?: string`:
541
+ comma-separated `[[xxx]]` patterns of the node objects acting as the
542
+ places of a `petri` diagram, all other nodes acting as its
543
+ transitions (without it, Gradia infers them from the arcs),
544
+ BECAUSE the kind of an object, not its position in the net, tells
545
+ whether it holds items or does work
546
+
547
+ - `SchemaDiagram.tokens?: string`:
548
+ property of a place carrying its initial tokens (an integer of at
549
+ least 0) in a `petri` diagram,
550
+ BECAUSE the analysis of the net starts from its initial marking
551
+
552
+ - `SchemaDiagram.capacity?: string`:
553
+ property of a place carrying its capacity (an integer of at least 1)
554
+ in a `petri` diagram, an absent one leaving the place unbounded,
555
+ BECAUSE a bound decides whether a producer has to wait
556
+
557
+ - `SchemaDiagram.duration?: string`:
558
+ property of a task carrying its duration (a non-negative number of
559
+ time units) in a `gantt` diagram, an absent one making the task a
560
+ milestone,
561
+ BECAUSE the length of a bar is the one fact a schedule rests on
562
+
563
+ - `SchemaDiagram.start?: string`:
564
+ property of a task carrying its earliest start (a non-negative number
565
+ of time units, default: `0`) in a `gantt` diagram,
566
+ BECAUSE some tasks start at an agreed time, not when their
567
+ dependencies allow
568
+
375
569
  - `SchemaDiagram.onlyConnected?: boolean`:
376
- whether a `graph` drops its edge-less nodes (default: `false`),
570
+ whether a diagram (except a `hub` or `grid`) drops its edge-less
571
+ nodes (default: `false`),
377
572
  BECAUSE isolated nodes dilute a diagram about relationships
378
573
 
379
574
  - `SchemaDiagram.collapse?: boolean`:
@@ -391,12 +586,25 @@ type SchemaGradiaConfig = Partial<{
391
586
  BECAUSE in a coarse-to-fine specification the references then all
392
587
  point backward, i.e. upward
393
588
 
589
+ - `SchemaDiagram.markUncovered?: "none" | "referenced" | "coverage" | "all"`:
590
+ which uncovered nodes are marked as incomplete, i.e. drawn with a
591
+ border in the violation (signal) color: none (`none`, the default),
592
+ the ones lacking the reference coverage their `referenced` kind
593
+ demands (`referenced`), the ones the `coverage` reports count
594
+ as unreferenced while no report counts them as referenced
595
+ (`coverage`, e.g. the requirements no test case references), or
596
+ both (`all`),
597
+ BECAUSE a coverage lapse spotted in the overview is fixed sooner
598
+ than one listed among the lint warnings or coverage ratios only
599
+
394
600
  - `SchemaDiagram.nest?: SchemaDiagramNest`:
395
601
  nesting of the nodes into container nodes,
396
602
  BECAUSE a containment drawn as a box beats one drawn as an edge
397
603
 
398
604
  - `SchemaDiagramNest.properties?: string[]`:
399
- properties whose single referenced object is the container of a node,
605
+ properties whose single referenced object is the container of a node
606
+ (in a `tree` diagram the first of several referenced objects, the
607
+ others becoming further parents),
400
608
  BECAUSE a `PART-OF` or `TIER` reference is a containment, not a relation
401
609
 
402
610
  - `SchemaDiagramNest.parent?: boolean`:
@@ -419,7 +627,11 @@ type SchemaGradiaConfig = Partial<{
419
627
  BECAUSE a bundle of references into a container reads as one edge onto it
420
628
 
421
629
  - `SchemaDiagram.properties?: string[]`:
422
- property names attached to the nodes as key/value annotations,
630
+ property names attached to the nodes as key/value annotations, none
631
+ of which may be a node attribute Gradia reserves (`url`, `type`,
632
+ `primary`, `group`, `cluster`, `parent`, `container`, `order`,
633
+ `badge`, `multiple`, `mark`, `layer`, `slice`, `state`, `region`,
634
+ `petri`, `tokens`, `capacity`, `duration`, or `start`),
423
635
  BECAUSE the node name alone rarely makes the nodes comparable
424
636
 
425
637
  - `SchemaDiagram.config?: SchemaGradiaConfig`:
@@ -506,15 +718,24 @@ type SchemaGradiaConfig = Partial<{
506
718
 
507
719
  - `SchemaGradiaConfig."color-node-*": string`:
508
720
  colors of the node boxes in their regular, primary, ghost, and self
509
- role,
721
+ role, of the pseudo-states of a `state` diagram, of the places of a
722
+ `petri` diagram, and of the lifelines of a `sequence` diagram,
510
723
  BECAUSE the role of a node has to be readable at a glance
511
724
 
512
- - `SchemaGradiaConfig."color-group-*", "color-edge-*": string`:
513
- colors of the group boxes and of the edges and their labels,
725
+ - `SchemaGradiaConfig."color-{group,container,composite,edge}-*": string`:
726
+ colors of the group, container, and composite boxes and of the
727
+ edges and their labels,
514
728
  BECAUSE both have to stay visible yet recede behind the nodes
515
729
 
516
- - `SchemaGradiaConfig."size-*", "group-box-*": number`:
517
- geometry of the canvas, the node boxes, and the edge routing,
730
+ - `SchemaGradiaConfig."color-mark-*": string`:
731
+ colors of the violation marks and of the critical path of a `gantt`
732
+ diagram,
733
+ BECAUSE a flaw or a bottleneck has to stand out from the regular drawing
734
+
735
+ - `SchemaGradiaConfig."size-*", "group-box-*", "container-box-*": number`:
736
+ font sizes of the node names, types, properties, edge names, and
737
+ arities, and geometry of the canvas, the node, group, and container
738
+ boxes, and the edge routing,
518
739
  BECAUSE the geometry has to fit both the page and the text lengths
519
740
 
520
741
  - `SchemaGradiaConfig."graph-*": number`:
@@ -522,32 +743,62 @@ type SchemaGradiaConfig = Partial<{
522
743
  BECAUSE a free network needs room for its edges, not just its nodes
523
744
 
524
745
  - `SchemaGradiaConfig."hub-*": number`:
525
- layout of a `hub`: channel widths, node gap, and node degree,
746
+ layout of a `hub`: channel widths, node gap, node degree, and the
747
+ node count of a column before it wraps into two staggered sub-columns,
526
748
  BECAUSE a hub grows sideways and has to be bounded per side
527
749
 
528
750
  - `SchemaGradiaConfig."grid-*": number | boolean`:
529
751
  layout of a `grid`: column bounds, tile gaps, and tile width,
530
752
  BECAUSE a catalog reads best as evenly sized tiles in stable columns
531
753
 
754
+ - `SchemaGradiaConfig."sequence-*": number`:
755
+ layout of a `sequence`: the gap between the lifelines and between
756
+ the messages,
757
+ BECAUSE an interaction reads top-down only with room between its steps
758
+
759
+ - `SchemaGradiaConfig."tree-*": number | boolean`:
760
+ layout of a `tree`: level and sibling gaps, the nodes per row, the
761
+ stacking of leaf siblings, equal node widths, and a horizontal
762
+ orientation,
763
+ BECAUSE a wide or deep hierarchy has to fit the page width
764
+
765
+ - `SchemaGradiaConfig."gantt-*": number | string | boolean`:
766
+ layout of a `gantt`: the width and the name of a time unit and the
767
+ marking of the critical path,
768
+ BECAUSE the time scale of a schedule is domain-specific
769
+
770
+ - `SchemaGradiaConfig."mark-violation", "layer-numbered", "slice-numbered": boolean`:
771
+ whether violations are marked at all (e.g. the cycles of a `matrix`
772
+ or the cross links of a `tree`) and whether the bands of a `layer`
773
+ or `slice` diagram are numbered,
774
+ BECAUSE a structure which is legitimate in one domain is a flaw in another
775
+
532
776
  ### Links
533
777
 
534
778
  The edges are derived from the `[[xxx]]` references of the node objects
535
779
  -- from their property values only, or, with `links: all`, from their
536
- descriptions as well. Every referenced object is lifted to its nearest
780
+ descriptions as well, or, with a list of property names, from the
781
+ values of these properties only. Every referenced object is lifted to its nearest
537
782
  ancestor-or-self within the node set, so a reference onto the scenario
538
783
  of a use case yields an edge onto the use case. With `deep: true` the
539
784
  references of all descendants of a node object count as its own and the
540
785
  number of references behind an edge becomes its arity, so a diagram of
541
786
  coarse objects (like the artifacts of a whole specification) shows the
542
- relations living in their fine-grained parts. With `labeled: true` an
543
- edge derived from a property value carries the lower-cased property key
544
- as its label, telling the edges of different properties apart, while
545
- the edges derived from descriptions stay unlabeled.
787
+ relations living in their fine-grained parts. With `descend` the
788
+ references of the descendants of the listed kinds alone count as the
789
+ node's own, uncounted, so one edge per target remains. With `labeled:
790
+ true` an edge derived from a property value carries the lower-cased
791
+ property key as its label (or, for a `descend` descendant, its
792
+ lower-cased kind), telling the edges of different properties apart,
793
+ while the edges derived from descriptions stay unlabeled.
546
794
 
547
795
  An object acting as an edge (`edges`) connects its parent object to the
548
796
  object its target property references. Where an edge object is a sibling
549
797
  of its own source instead of a child of it, `edgeSource` names the
550
- property carrying that source.
798
+ property carrying that source, and where the target property named by
799
+ `edgeTarget` carries a `list(...)` of references, the edge object yields
800
+ one edge per resolvable target. With `edgeGuard`, the value of the named
801
+ property follows the name of every such edge in brackets.
551
802
 
552
803
  As the *nesting* of the objects carries no such reference, `hierarchy:
553
804
  true` additionally derives a containment edge from every node object to
@@ -561,14 +812,120 @@ This is what turns a parent object into the hub of its own children. A
561
812
 
562
813
  An object kind can additionally carry a `diagram` field, which derives
563
814
  a [Gradia](https://github.com/rse/gradia) diagram for every object of
564
- that kind. Its `type` selects the diagram shape (`graph`, `hub`, or
565
- `grid`), `nodes` and `edges` select the participating objects through
815
+ that kind -- or a list of such diagram configurations, which derives
816
+ all of them, in their order. Its `type` selects the diagram shape
817
+ (`graph`, `hub`, `grid`, `state`, `layer`, `slice`, `tree`,
818
+ `matrix`, `sequence`, `petri`, or `gantt`), `nodes` and
819
+ `edges` select the participating objects through
566
820
  comma-separated `[[xxx]]` reference patterns (`nodes` defaults to the
567
- object itself plus all objects below it), and `center` names the object
821
+ object itself plus all objects below it, and `local: true` restricts the
822
+ matches of both patterns to the object itself plus all objects below it,
823
+ as the patterns match corpus-wide), and `center` names the object
568
824
  a `hub` is projected onto (default: `self`). An edge object of the
569
825
  center targeting the center itself (a self-reference) places the center
570
826
  a second time among its outputs, rendered as a dashed "self" node.
571
827
 
828
+ A `state` diagram is laid out like a `graph`, but draws a state machine:
829
+ where its object kind carries an `automaton`, a single initial
830
+ pseudo-state (a dot) leads into every node flagged `initial`, and every
831
+ node flagged `final` leads into a single final pseudo-state (a
832
+ bullseye), so a final node may still have outgoing edges. A `state`
833
+ diagram accepts no `nest`, as every container would be a composite
834
+ state needing an initial state of its own.
835
+
836
+ A `layer` diagram draws a layered architecture: the `nest` container of
837
+ a node names the band it sits in, the bands stack top-down in the
838
+ document order of their first node and are numbered bottom-up (unless
839
+ `config` sets `layer-numbered: false`), and the containers enclosing a
840
+ container group its band into a cluster column. The containers are no
841
+ nodes there, so the edges ending at a container are dropped, an edge
842
+ pointing upwards against the layering is marked as a violation, and
843
+ neither `nest.layout`, a lifting `nest.crossing`, nor `ordered` apply.
844
+ A `slice` diagram is its side-by-side companion: the bands line up
845
+ left-to-right (numbered only with `config` setting `slice-numbered:
846
+ true`), the nodes of a band stack top-down, the enclosing containers
847
+ group the bands into cluster rows, and an edge pointing back to a left
848
+ band closing a direct cycle is marked as a violation.
849
+
850
+ A `tree` diagram draws a tidy tree (or a forest of them): the `nest`
851
+ container of a node becomes its parent, so a child-to-parent reference
852
+ like `BROADER` or `PART-OF`, named by `nest.properties`, yields the
853
+ tree, while the containers stay nodes. Where such a property carries
854
+ several references (like a multiple `INHERITS`), the first one names
855
+ the parent and the others further parents. Every other edge (the ones
856
+ from the further parents included) is drawn as a dashed cross link and
857
+ marked as a violation (unless `config` sets `mark-violation: false`, as
858
+ for a legitimate multiple inheritance), so `links: []` derives a pure
859
+ tree (plus its further parents). With
860
+ `ordered: true` the children follow the document order. A `tree`
861
+ diagram accepts neither `nest.layout` nor a lifting `nest.crossing`.
862
+
863
+ A `matrix` diagram draws a dependency structure matrix: the nodes are
864
+ both its rows and its columns, the cell of a row and a column counts the
865
+ edges from the one to the other (a numeric arity counting as its
866
+ number), and the nodes are ordered topologically (with `ordered: true`
867
+ in document order instead), so all entries lie above the diagonal
868
+ except the ones of the cycles, which are outlined and marked as
869
+ violations (unless `config` sets `mark-violation: false`, as for
870
+ relations where mutual references are regular). A `nest` container
871
+ partitions its members, and the partitions are outlined on the
872
+ diagonal: a container taking part in the relation itself stays a row
873
+ and a column of its own, heading its partition, while a mere grouping
874
+ object (without any edge) names the partition only. `qualified: true`
875
+ puts the shared object kind into the corner. A `matrix` diagram accepts
876
+ no `nest.layout`. It usually restricts the `links` onto the very
877
+ relation it is about, e.g. `links: [ DEPENDS-ON ]`.
878
+
879
+ A `sequence` diagram draws the edge objects (`edges`) as its messages,
880
+ top-down in document order and never merged, so a repeated message
881
+ stays: `edgeSource` and `edgeTarget` name the sender and the receivers,
882
+ `edgeKind` the message kind (a `Return` answering the latest unanswered
883
+ `Call` of its sender, which activates the callee until then),
884
+ `edgeDuration` a duration constraint of a call, and `edgeTime` a time
885
+ constraint at a message. Its participants are the senders and receivers
886
+ of its messages, lined up in the order the messages first involve
887
+ them (unless `nodes` restricts them), so `local: true` usually
888
+ restricts the messages onto the own subtree. A `sequence` diagram
889
+ derives no reference edges and hence accepts neither `links`,
890
+ `hierarchy`, `deep`, `labeled`, `nest`, nor `ordered`, while `edgeKind`,
891
+ `edgeDuration`, and `edgeTime` apply to it alone. Its messages are
892
+ validated, each violation reported on its edge object: a participant is
893
+ created only by the first message it takes part in, takes part in no
894
+ message after its destruction, and a duration constraint belongs to a
895
+ call answered by a return.
896
+
897
+ A `petri` diagram draws a Petri net from the reference edges: the node
898
+ objects matching `places` are its places (drawn as circles holding their
899
+ `tokens` and showing their `capacity`), all other nodes its
900
+ transitions, and every edge an arc, where `reverse` turns the edges of
901
+ a consuming property into the arcs from the place to the transition, so
902
+ both arc directions stem from references of the transitions. Gradia
903
+ analyzes the net from its initial marking (within a bounded
904
+ exploration budget) and marks the unbounded places (missing
905
+ backpressure), the places holding a token in a deadlock, and the dead
906
+ and non-live transitions. The net is validated, each violation reported
907
+ on its object: the tokens are an integer of at least 0, the capacity one
908
+ of at least 1 holding them, and an arc connects a place with a
909
+ transition. A `petri` diagram accepts no `nest`, `places`, `tokens`,
910
+ and `capacity` apply to it alone, and `tokens` and `capacity` require
911
+ `places`.
912
+
913
+ A `gantt` diagram draws a Gantt chart: the node objects are its tasks
914
+ (one row each, a task without a `duration` being a milestone), the
915
+ edges its finish-to-start dependencies (so a property referencing the
916
+ *preceding* tasks is listed under `reverse`, as a dependency arrow runs
917
+ from the predecessor), and the `nest` containers the partitions of its
918
+ rows (like in a `matrix`, the containers themselves being no tasks).
919
+ The `duration` and `start` options name the properties of the tasks
920
+ carrying their duration and earliest start in abstract time units (a
921
+ `gantt-unit-name` in `config` labels them), from which Gradia schedules
922
+ the tasks as early as possible, draws the slack of every task, and
923
+ marks the critical path. The schedule is validated, each violation
924
+ reported on its object: the duration and the start are non-negative
925
+ numbers, and the dependencies form no cycle. A `gantt` diagram accepts
926
+ no `nest` layout or crossing, and `duration` and `start` apply to it
927
+ alone.
928
+
572
929
  Instead of naming an existing object, `center` can also declare a
573
930
  *synthetic* center node, which represents something no specification
574
931
  object models -- like the solution itself in the middle of a context
@@ -592,6 +949,11 @@ rendering options (e.g. `grid-columns-max: 2`) through to the diagram.
592
949
  As the Gradia defaults are dimensioned for a stand-alone canvas,
593
950
  SpecBook presets a compact geometry for every diagram, which `config`
594
951
  overrides: `size-canvas-margin: 8`, `size-node-height-scale: 2`,
952
+ `size-font-type`, `size-font-edge`, and `size-font-arity: 19` (about
953
+ 20% over the Gradia defaults, as the diagrams are embedded at half
954
+ their coordinate scale), `size-edge-track-gap: 20` (the parallel edges
955
+ spread further apart), `graph-channel-width-max: 1000` (a capped
956
+ channel squeezes its many edges into an unreadable bundle),
595
957
  `hub-node-gap: 10`, `hub-node-count-max: 6` (a `hub` column of more
596
958
  nodes wraps into two staggered sub-columns), `grid-gap-horizontal: 24`,
597
959
  `grid-gap-vertical: 12`, and `grid-columns-min: 4` (the square
@@ -670,7 +1032,12 @@ one ignores it), `maxCellHeight`
670
1032
  the height of every other non-empty cell of its row before the HTML
671
1033
  export folds its remaining text away, and `withUnusedProps`
672
1034
  (default: `false`) unconditionally renders the defined but unused
673
- properties of that kind, as property table lines or table columns. In
1035
+ properties of that kind, as property table lines or table columns.
1036
+ Within a `concise` table, the name column takes a fixed fifth of the
1037
+ width, the description column two shares of the rest, and every property
1038
+ column one to two and a half shares by its longest word (its header
1039
+ included), so a property name or a reference kind label never breaks
1040
+ inside a narrow column. In
674
1041
  the HTML/PDF export, a property absent from an object shows an "empty
675
1042
  set" marker in place of its value, which tells it apart from a property
676
1043
  given with an empty value.
@@ -725,7 +1092,7 @@ type SpecProperty = {
725
1092
  BECAUSE a specification is one corpus of separately authored files
726
1093
 
727
1094
  - `Spec.artifacts: SpecArtifact[]`:
728
- all artifacts parsed from the Markdown files the SCHEMA references,
1095
+ all artifacts parsed from the Markdown files below the base directory,
729
1096
  BECAUSE the corpus is parsed as a whole to resolve references
730
1097
 
731
1098
  - `SpecArtifact`:
@@ -837,16 +1204,20 @@ several such files, merged in order into one schema (the later files
837
1204
  into the earlier ones, where the objects merge deeply and the elements
838
1205
  of the lists are matched by their identity). If no particular schema is
839
1206
  given, the standard schema configuration bundled with **SpecBook**
840
- applies.
1207
+ applies. Beside it (named `std`), **SpecBook** bundles the meta schema
1208
+ configuration (named `meta`), which describes a schema configuration
1209
+ itself, and the project management schema configuration (named `pm`),
1210
+ which plans the delivery of the solution a specification describes.
841
1211
 
842
1212
  SpecBook SPEC Model
843
1213
  -------------------
844
1214
 
845
1215
  ### Artifacts
846
1216
 
847
- A specification consists of Markdown *artifact* files, which the SCHEMA
848
- Model references by their `file` fields and which are resolved against
849
- a base directory. Every file has to start with
1217
+ A specification consists of Markdown *artifact* files: every `.md` file
1218
+ below a base directory (recursively, except below `.`-prefixed entries),
1219
+ whatever its name, while the `file` fields of the SCHEMA Model only tell
1220
+ where the generated files are created. Every file has to start with
850
1221
  a "frontmatter" block carrying the `Created:` and `Modified:` timestamps
851
1222
  (format `yyyy-LL-dd HH:mm`):
852
1223
 
@@ -859,10 +1230,11 @@ Modified: 2026-06-18 10:18
859
1230
 
860
1231
  Every level 1 heading opens a new *artifact*, so usually each file
861
1232
  carries exactly one. Several artifacts nevertheless reside in one file
862
- whenever the schema configures the same `file` for them: they then
863
- follow each other on level 1, below the single frontmatter block of that
864
- file. Both ATX headings (`# <text/>`, `## <text/>`, ...) and Setext
865
- headings (`<text/>` underlined with `===` or `---`) are supported.
1233
+ whenever the schema configures the same `file` for them (or the author
1234
+ places them so): they then follow each other on level 1, below the
1235
+ single frontmatter block of that file. Both ATX headings (`# <text/>`,
1236
+ `## <text/>`, ...) and Setext headings (`<text/>` underlined with `===`
1237
+ or `---`) are supported.
866
1238
 
867
1239
  As the timestamps are a property of the *file* and not of the individual
868
1240
  artifact, the normalized Markdown export -- which merges the entire
@@ -1027,6 +1399,13 @@ The event is created and configured but not visible to attendees,
1027
1399
  BECAUSE an event needs a private setup phase.
1028
1400
  ```
1029
1401
 
1402
+ The `, BECAUSE ` keyword is the source syntax and stays English in every
1403
+ document language, while the HTML/PDF export follows the bold `BECAUSE`
1404
+ with its non-bold, lower-case translation in parentheses into the
1405
+ language of the `LANG` property of the title object (`BECAUSE (weil)`
1406
+ for `de`, `BECAUSE (parce que)` for `fr`, and so on, while `en` and an
1407
+ unknown language render the bare `BECAUSE`).
1408
+
1030
1409
  The rationale ends with its paragraph: all blocks following it (further
1031
1410
  paragraphs, embedded images, and diagrams) form the *elaboration*, which
1032
1411
  is rendered below the statement and its rationale, so the description
@@ -1043,8 +1422,9 @@ BECAUSE an event needs a private setup phase.
1043
1422
  ### Wiki-Style References
1044
1423
 
1045
1424
  A `[[<reference/>]]` anywhere in a name, property value, description, or
1046
- rationale (except inside a fenced code block, whose code stays verbatim,
1047
- so e.g. a shell test `[[ -f x ]]` is no reference) is a Wiki-style
1425
+ rationale (except inside a fenced code block or an inline code span,
1426
+ whose code stays verbatim, so e.g. a shell test `[[ -f x ]]` is no
1427
+ reference) is a Wiki-style
1048
1428
  reference to another object, which is checked
1049
1429
  for unique resolvability and rendered as a hyperlink in the HTML/PDF
1050
1430
  exports. A reference is a `.`-separated path of segments, where each