yarramate 1.3.0 → 1.4.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 (32) hide show
  1. package/catalogues/core-enrichment.yaml +13 -1
  2. package/dist/adapters/visual/workspace-model.d.ts +14 -2
  3. package/dist/adapters/visual/workspace-model.js +19 -9
  4. package/dist/ask-command.js +8 -1
  5. package/dist/compiler.d.ts +13 -1
  6. package/dist/compiler.js +646 -0
  7. package/dist/design-command.js +14 -3
  8. package/dist/graph-projection.d.ts +10 -0
  9. package/dist/graph-projection.js +1 -0
  10. package/dist/interrogate-command.d.ts +17 -0
  11. package/dist/interrogate-command.js +72 -40
  12. package/dist/relationship-drafting.js +16 -1
  13. package/dist/visual-app/assets/{index-C3i9SxTe.js → index-yCHtmQUH.js} +1 -1
  14. package/dist/visual-app/index.html +1 -1
  15. package/dist/visual-app-lib/editor.js +22875 -22261
  16. package/dist/visual-app-lib/types/adapters/visual/workspace-model.d.ts +14 -2
  17. package/dist/visual-app-lib/types/compiler.d.ts +13 -1
  18. package/dist/visual-app-lib/types/graph-projection.d.ts +10 -0
  19. package/dist/visual-app-lib/types/interrogate-command.d.ts +17 -0
  20. package/dist/visual-app-lib/types/visual-app/local-host.d.ts +42 -0
  21. package/dist/visual-app-lib/types/workspace.d.ts +2 -0
  22. package/dist/workspace.d.ts +2 -0
  23. package/dist/workspace.js +1 -0
  24. package/docs/CONSUMING-YARRAMATE.md +41 -0
  25. package/package.json +2 -1
  26. package/schema/yarramate-design-step.schema.json +13 -0
  27. package/schema/yarramate-document.schema.json +14 -0
  28. package/schema/yarramate-interrogation-report.schema.json +18 -0
  29. package/schema/yarramate-pattern.schema.json +133 -0
  30. package/schema/yarramate-question-catalogue.schema.json +21 -0
  31. package/schema/yarramate-visual-graph.schema.json +9 -0
  32. package/schema/yarramate-workspace.schema.json +4 -0
@@ -49,13 +49,25 @@ export declare const conceptCountOf: (graph: SemanticGraph, query: ProjectionQue
49
49
  * Subject ids come from the same compiled graph as `CanvasNode.id`, so the
50
50
  * join in the browser is a plain lookup.
51
51
  */
52
+ export interface DismissedQuestion {
53
+ readonly questionId: string;
54
+ /** Absent dismisses the question wherever it appears. */
55
+ readonly subject?: string;
56
+ }
52
57
  export declare const interrogationOverlayOf: (compiled: {
53
58
  readonly graph: SemanticGraph;
54
59
  readonly profileContext: ResolvedProfileContext;
55
60
  }, catalogue: {
56
61
  readonly path: string;
57
62
  readonly source: string;
58
- }) => VisualInterrogationOverlay | undefined;
63
+ },
64
+ /**
65
+ * What the host has already dealt with (#328). Evaluation is unchanged and
66
+ * the model is untouched: this decides only what the pane draws, because a
67
+ * question set aside in the host's own product should not be asked again by
68
+ * a pane embedded in it.
69
+ */
70
+ dismissed?: readonly DismissedQuestion[]) => VisualInterrogationOverlay | undefined;
59
71
  /**
60
72
  * Rebuilds the shared editor workspace from one successful compile.
61
73
  *
@@ -71,7 +83,7 @@ export declare const renderedWorkspaceOf: (compiled: {
71
83
  }, views: readonly VisualViewSummary[], metadata: Omit<VisualRenderedModel, "graph" | "vocabulary" | "interrogation">, catalogue?: {
72
84
  readonly path: string;
73
85
  readonly source: string;
74
- }) => {
86
+ }, dismissed?: readonly DismissedQuestion[]) => {
75
87
  readonly model: VisualRenderedModel;
76
88
  readonly views: readonly VisualViewSummary[];
77
89
  };
@@ -74,6 +74,18 @@ export interface ResolvedProfileContext {
74
74
  /** Kind identity -> the core kind it resolves to through lineage. */
75
75
  readonly conceptKindCoreAncestors: ReadonlyMap<string, CoreConceptKindId>;
76
76
  readonly relationshipKindCoreAncestors: ReadonlyMap<string, RelationshipKind>;
77
+ /**
78
+ * The core relationship kinds a pattern PORTS for a concept kind (#268
79
+ * phase 3, ADR 0124), keyed by kind identity. Only kinds that have a
80
+ * pattern with ports appear.
81
+ *
82
+ * A macro edge needs both ends to port its kind, so a consumer offering a
83
+ * palette between two instances intersects the two sets. Two raw groupings
84
+ * permit ten of the eleven kinds, which is no narrowing at all; the ports
85
+ * are what restore the guidance the relationship table gives everywhere
86
+ * else.
87
+ */
88
+ readonly patternPortKinds: ReadonlyMap<string, readonly RelationshipKind[]>;
77
89
  /**
78
90
  * The core relationship kinds the ArchiMate table permits between two
79
91
  * concept kind identities, resolved through lineage; undefined when either
@@ -104,7 +116,7 @@ export type ContextualCompilationResult = {
104
116
  */
105
117
  export interface ParsedWorkspaceSource {
106
118
  readonly source: string;
107
- readonly kind: 'profile' | 'document';
119
+ readonly kind: 'profile' | 'document' | 'pattern';
108
120
  readonly value: unknown;
109
121
  readonly schemaDiagnostics: readonly Diagnostic[];
110
122
  /**
@@ -14,6 +14,16 @@ export interface CanvasNode {
14
14
  * profile kind it was authored as.
15
15
  */
16
16
  readonly coreKindLabel: string;
17
+ /**
18
+ * The core relationship kinds this node's PATTERN ports, or `[]` where its
19
+ * kind has no pattern with ports (#268 phase 3, ADR 0124).
20
+ *
21
+ * A macro edge needs both ends to port its kind, so an editor offering a
22
+ * palette between two instances intersects the two lists. Two raw groupings
23
+ * permit ten of the eleven kinds, which is no guidance at all; this is what
24
+ * restores the narrowing the relationship table gives everywhere else.
25
+ */
26
+ readonly portKinds: readonly string[];
17
27
  readonly layer: Layer | null;
18
28
  readonly aspect: Aspect | null;
19
29
  readonly name: string;
@@ -87,6 +87,8 @@ export type CatalogueCondition = {
87
87
  readonly condition: 'unscoped-succession';
88
88
  } | {
89
89
  readonly condition: 'unchallenged-evidence';
90
+ } | {
91
+ readonly condition: 'has-any-subject';
90
92
  };
91
93
  /**
92
94
  * One observation from the workspace's evidence overlay, reduced to what
@@ -131,6 +133,11 @@ export interface QuestionCatalogue {
131
133
  readonly id: string;
132
134
  readonly name: string;
133
135
  readonly description?: string;
136
+ /**
137
+ * Conditions that must all hold before this wave opens (#334, ADR 0125).
138
+ * Absent means always open, which is what every wave did before this.
139
+ */
140
+ readonly opensWhen?: readonly CatalogueCondition[];
134
141
  }[];
135
142
  readonly questions: readonly CatalogueQuestion[];
136
143
  }
@@ -161,6 +168,16 @@ export interface ReportQuestion {
161
168
  export interface ReportWave {
162
169
  readonly id: string;
163
170
  readonly name: string;
171
+ /**
172
+ * Whether the wave's gate is met (#334, ADR 0125). A wave with no
173
+ * `opensWhen` is always open.
174
+ *
175
+ * A wave reported `false` carries NO questions and contributes nothing to
176
+ * the summary. Its questions are premature rather than answered, and a
177
+ * progress rail that counted them as answered would flatter itself exactly
178
+ * where someone is most likely to trust it.
179
+ */
180
+ readonly opened: boolean;
164
181
  readonly questions: readonly ReportQuestion[];
165
182
  }
166
183
  export interface InterrogationSummary {
@@ -34,5 +34,47 @@ export interface LocalHostOptions {
34
34
  readonly description?: string;
35
35
  /** Told after every landed commit, so a host can persist what it was given. */
36
36
  readonly onCommit?: (documents: readonly string[]) => void;
37
+ /**
38
+ * The question catalogue the questions section evaluates, or the shipped
39
+ * `core-enrichment` one when absent (#328).
40
+ *
41
+ * The engine is yarramate's and so is the UI; the QUESTIONS belong to
42
+ * whoever adopted it. `core-enrichment` is a general modelling interview,
43
+ * right for this repository's own CLI and for a host with no domain of its
44
+ * own, and wrong for a product whose interview is about its own subject
45
+ * matter. Until this existed a host could have the questions UI only by
46
+ * also running yarramate's catalogue, so a product with its own interview
47
+ * had to omit the section and show its questions on a separate surface,
48
+ * away from the model they are about.
49
+ *
50
+ * Bytes rather than a parsed catalogue, matching what the seam beneath
51
+ * already takes: a catalogue that does not load leaves the overlay absent
52
+ * rather than failing the mount, because the overlay is a garnish on the
53
+ * model and a model frame must not be blocked by it.
54
+ */
55
+ readonly catalogue?: {
56
+ readonly path: string;
57
+ readonly source: string;
58
+ };
59
+ /**
60
+ * Questions this host has already dealt with and does not want asked again
61
+ * (#328).
62
+ *
63
+ * A host-supplied catalogue alone does not close this: the editor evaluates
64
+ * the catalogue itself and cannot know that a reviewer set a question aside,
65
+ * with a reason, recorded somewhere the editor cannot see. Without this the
66
+ * pane would go on asking a question its own product had answered.
67
+ *
68
+ * `subject` absent dismisses the question wherever it appears - the
69
+ * workspace-scoped entry and every subject's - which is "stop asking this".
70
+ * `subject` present dismisses it for that subject alone, which is "not for
71
+ * this one". Dismissal hides a question from the pane and changes nothing
72
+ * about the model or about what `ask --open` reports; the interview is not
73
+ * the editor's to settle.
74
+ */
75
+ readonly dismissed?: readonly {
76
+ readonly questionId: string;
77
+ readonly subject?: string;
78
+ }[];
37
79
  }
38
80
  export declare const createLocalHost: (options: LocalHostOptions) => EditorHost;
@@ -6,6 +6,7 @@ export interface WorkspaceManifest {
6
6
  readonly profiles: readonly string[];
7
7
  readonly projections: readonly string[];
8
8
  readonly adapterMappings: readonly string[];
9
+ readonly patterns?: readonly string[];
9
10
  readonly evidence?: readonly string[];
10
11
  readonly contracts?: readonly string[];
11
12
  }
@@ -15,6 +16,7 @@ export interface ResolvedWorkspace {
15
16
  readonly profiles: readonly string[];
16
17
  readonly projections: readonly string[];
17
18
  readonly adapterMappings: readonly string[];
19
+ readonly patterns: readonly string[];
18
20
  readonly evidence: readonly string[];
19
21
  readonly contracts: readonly string[];
20
22
  }
@@ -6,6 +6,7 @@ export interface WorkspaceManifest {
6
6
  readonly profiles: readonly string[];
7
7
  readonly projections: readonly string[];
8
8
  readonly adapterMappings: readonly string[];
9
+ readonly patterns?: readonly string[];
9
10
  readonly evidence?: readonly string[];
10
11
  readonly contracts?: readonly string[];
11
12
  }
@@ -15,6 +16,7 @@ export interface ResolvedWorkspace {
15
16
  readonly profiles: readonly string[];
16
17
  readonly projections: readonly string[];
17
18
  readonly adapterMappings: readonly string[];
19
+ readonly patterns: readonly string[];
18
20
  readonly evidence: readonly string[];
19
21
  readonly contracts: readonly string[];
20
22
  }
package/dist/workspace.js CHANGED
@@ -106,6 +106,7 @@ export function loadWorkspaceManifest(source, cwd) {
106
106
  profiles: expand('profiles', 'profile', value.profiles),
107
107
  projections: expand('projections', 'projection', value.projections),
108
108
  adapterMappings: expand('adapterMappings', 'adapter mapping', value.adapterMappings),
109
+ patterns: expand('patterns', 'pattern', value.patterns ?? []),
109
110
  evidence: expand('evidence', 'evidence', value.evidence ?? []),
110
111
  contracts: expand('contracts', 'Core contract', value.contracts ?? []),
111
112
  };
@@ -298,6 +298,47 @@ const editor = mountEditor(document.querySelector('#editor')!, {
298
298
  editor.unmount()
299
299
  ```
300
300
 
301
+ ### The questions are yours
302
+
303
+ The questions section evaluates the shipped `core-enrichment` catalogue by
304
+ default. A host with its own interrogation supplies its own instead (#328):
305
+
306
+ ```ts
307
+ const editor = mountEditor(element, {
308
+ store,
309
+ workspace,
310
+ sections: ['palette', 'properties', 'questions', 'changes'],
311
+ catalogue: { path: 'catalogues/consulting.yaml', source: catalogueText },
312
+ dismissed: [
313
+ { questionId: 'register-fidelity', subject: 'crm-integration' },
314
+ { questionId: 'engagement-framing' },
315
+ ],
316
+ })
317
+ ```
318
+
319
+ The division this draws is deliberate. **The engine is yarramate's and so is
320
+ the UI; the questions belong to whoever adopted it.** `core-enrichment` is a
321
+ general modelling interview — right for yarramate's own CLI, and right for a
322
+ host with no domain of its own — and wrong for a product whose interview is
323
+ about its own subject matter. Inheriting it would mean asking a consultant
324
+ about modelling hygiene in the pane where they are asking a client about
325
+ sign-off.
326
+
327
+ `catalogue` takes bytes, and a catalogue that does not load leaves the overlay
328
+ absent rather than failing the mount: the overlay is a garnish on the model,
329
+ and a model frame must not be blocked by it.
330
+
331
+ `dismissed` is what the host has already dealt with. A supplied catalogue
332
+ alone does not cover this — the editor evaluates the catalogue itself and
333
+ cannot know that a reviewer set a question aside, with a reason, recorded
334
+ somewhere the editor cannot see, so the pane would go on asking a question the
335
+ host's own product had answered. Naming a `subject` dismisses the question for
336
+ that subject alone; omitting it dismisses the question wherever it appears.
337
+
338
+ Dismissal decides what the **pane draws** and nothing else. The model is
339
+ untouched and `ask --open` still reports the question, because the interview is
340
+ not the editor's to settle.
341
+
301
342
  Pass `readOnly: true` to mount a viewer over the same surface — for a frozen
302
343
  published snapshot, say ([ADR 0117](adr/0117-a-mounted-editor-can-refuse-the-pen.md)).
303
344
  The reviewer still selects, filters, navigates views and reads questions and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yarramate",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Tool-neutral semantic architecture engine and guided methodology",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -65,6 +65,7 @@
65
65
  "./visual-app/styles.css": "./dist/visual-app-lib/styles.css",
66
66
  "./schema/document": "./schema/yarramate-document.schema.json",
67
67
  "./schema/profile": "./schema/yarramate-profile.schema.json",
68
+ "./schema/pattern": "./schema/yarramate-pattern.schema.json",
68
69
  "./schema/projection": "./schema/yarramate-projection.schema.json",
69
70
  "./schema/projection-result": "./schema/yarramate-projection-result.schema.json",
70
71
  "./schema/adapter-mapping": "./schema/yarramate-adapter-mapping.schema.json",
@@ -534,6 +534,19 @@
534
534
  }
535
535
  }
536
536
  },
537
+ {
538
+ "type": "object",
539
+ "additionalProperties": false,
540
+ "description": "The workspace holds at least one concept (#334, ADR 0125). The guard a late wave needs to say \"only once the model has substance\": an empty model is not an architecture at rest, it has not started, and asking it how the planned architecture becomes real greets someone ahead of question one.",
541
+ "required": [
542
+ "condition"
543
+ ],
544
+ "properties": {
545
+ "condition": {
546
+ "const": "has-any-subject"
547
+ }
548
+ }
549
+ },
537
550
  {
538
551
  "type": "object",
539
552
  "additionalProperties": false,
@@ -151,6 +151,20 @@
151
151
  "$ref": "#/$defs/constraint"
152
152
  }
153
153
  },
154
+ "parts": {
155
+ "description": "The subjects that fill this instance's pattern slots, by slot name (ADR 0123). Valid only on a kind that has a pattern; the compiler mints the pattern's wiring between the instance and what these name, and refuses any authored relationship that contradicts it. Binding, not generation: a part keeps its own name, owner, evidence and outside edges.",
156
+ "type": "object",
157
+ "minProperties": 1,
158
+ "additionalProperties": false,
159
+ "propertyNames": {
160
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
161
+ },
162
+ "patternProperties": {
163
+ "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$": {
164
+ "$ref": "#/$defs/reference"
165
+ }
166
+ }
167
+ },
154
168
  "references": {
155
169
  "$ref": "#/$defs/identifiedReferences"
156
170
  },
@@ -66,6 +66,7 @@
66
66
  "required": [
67
67
  "id",
68
68
  "name",
69
+ "opened",
69
70
  "questions"
70
71
  ],
71
72
  "properties": {
@@ -77,6 +78,10 @@
77
78
  "type": "string",
78
79
  "minLength": 1
79
80
  },
81
+ "opened": {
82
+ "description": "Whether this wave's gate is met (#334, ADR 0125). A wave with no `opensWhen` is always true. A wave reported false asks nothing and carries no questions: they are premature rather than answered, which is the third state a progress rail needs so a closed wave is not counted as done. Its questions are excluded from `summary` for the same reason.",
83
+ "type": "boolean"
84
+ },
80
85
  "questions": {
81
86
  "type": "array",
82
87
  "items": {
@@ -533,6 +538,19 @@
533
538
  }
534
539
  }
535
540
  },
541
+ {
542
+ "type": "object",
543
+ "additionalProperties": false,
544
+ "description": "The workspace holds at least one concept (#334, ADR 0125). The guard a late wave needs to say \"only once the model has substance\": an empty model is not an architecture at rest, it has not started, and asking it how the planned architecture becomes real greets someone ahead of question one.",
545
+ "required": [
546
+ "condition"
547
+ ],
548
+ "properties": {
549
+ "condition": {
550
+ "const": "has-any-subject"
551
+ }
552
+ }
553
+ },
536
554
  {
537
555
  "type": "object",
538
556
  "additionalProperties": false,
@@ -0,0 +1,133 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://yarramate.org/schema/pattern/v1",
4
+ "title": "YarraMate structural pattern",
5
+ "description": "The shape a concept kind promises: the parts an instance binds and the wiring the compiler mints between them (ADR 0123). A pattern declares structure; a profile declares vocabulary. Expansion is compile-time and lands in the semantic graph, never in a document.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["format", "id", "version", "patterns"],
9
+ "properties": {
10
+ "format": {
11
+ "const": "yarramate/pattern/v1"
12
+ },
13
+ "id": {
14
+ "type": "string",
15
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
16
+ },
17
+ "version": {
18
+ "type": "string",
19
+ "pattern": "^[0-9]+\\.[0-9]+$"
20
+ },
21
+ "patterns": {
22
+ "type": "array",
23
+ "minItems": 1,
24
+ "items": {
25
+ "$ref": "#/$defs/pattern"
26
+ }
27
+ }
28
+ },
29
+ "$defs": {
30
+ "pattern": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["kind", "parts", "wiring"],
34
+ "properties": {
35
+ "kind": {
36
+ "description": "The globally qualified concept kind this pattern shapes. The kind's anchor is whatever the profile says it specializes; a pattern never restates it.",
37
+ "$ref": "#/$defs/qualifiedKind"
38
+ },
39
+ "parts": {
40
+ "description": "The slots an instance binds, by name. A slot names the kind the bound subject must be, and whether an instance must bind it.",
41
+ "type": "object",
42
+ "minProperties": 1,
43
+ "additionalProperties": false,
44
+ "propertyNames": {
45
+ "$ref": "#/$defs/slotName"
46
+ },
47
+ "patternProperties": {
48
+ "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$": {
49
+ "$ref": "#/$defs/part"
50
+ }
51
+ }
52
+ },
53
+ "ports": {
54
+ "description": "Where a macro-grain edge lands canonically (#268 phase 2, ADR 0124). A relationship authored between two instances whose patterns both declare a port for its kind is expanded: `out` names the slot it leaves from on the source instance, `in` the slot it arrives at on the target. The macro edge itself survives - it is an authored fact, which is what gives a collapsed view edges to draw.",
55
+ "type": "array",
56
+ "minItems": 1,
57
+ "items": {
58
+ "$ref": "#/$defs/port"
59
+ }
60
+ },
61
+ "wiring": {
62
+ "description": "The relationships the compiler mints between an instance and its bound parts. Where an authored relationship already says exactly this, nothing is minted. Any other authored relationship between the same pair contradicts the pattern and fails compilation.",
63
+ "type": "array",
64
+ "minItems": 1,
65
+ "items": {
66
+ "$ref": "#/$defs/wire"
67
+ }
68
+ }
69
+ }
70
+ },
71
+ "part": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "required": ["kind"],
75
+ "properties": {
76
+ "kind": {
77
+ "$ref": "#/$defs/qualifiedKind"
78
+ },
79
+ "required": {
80
+ "description": "An instance that leaves a required slot unbound fails compilation (YM416). Absent means optional.",
81
+ "type": "boolean"
82
+ }
83
+ }
84
+ },
85
+ "port": {
86
+ "type": "object",
87
+ "additionalProperties": false,
88
+ "required": ["kind", "out", "in"],
89
+ "properties": {
90
+ "kind": {
91
+ "$ref": "#/$defs/qualifiedKind"
92
+ },
93
+ "out": {
94
+ "description": "The slot a macro edge of this kind leaves from, on the SOURCE instance. Never `self`: an edge that left from the instance would be the macro edge again.",
95
+ "$ref": "#/$defs/slotName"
96
+ },
97
+ "in": {
98
+ "description": "The slot a macro edge of this kind arrives at, on the TARGET instance.",
99
+ "$ref": "#/$defs/slotName"
100
+ }
101
+ }
102
+ },
103
+ "wire": {
104
+ "type": "object",
105
+ "additionalProperties": false,
106
+ "required": ["from", "kind", "to"],
107
+ "properties": {
108
+ "from": {
109
+ "$ref": "#/$defs/endpoint"
110
+ },
111
+ "kind": {
112
+ "$ref": "#/$defs/qualifiedKind"
113
+ },
114
+ "to": {
115
+ "$ref": "#/$defs/endpoint"
116
+ }
117
+ }
118
+ },
119
+ "endpoint": {
120
+ "description": "A slot name declared in `parts`, or `self` for the instance itself.",
121
+ "type": "string",
122
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
123
+ },
124
+ "slotName": {
125
+ "type": "string",
126
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
127
+ },
128
+ "qualifiedKind": {
129
+ "type": "string",
130
+ "pattern": "^[a-z][a-z0-9-]*(?:/[a-z][a-z0-9-]*)+@[0-9]+\\.[0-9]+#[a-z][A-Za-z0-9-]*$"
131
+ }
132
+ }
133
+ }
@@ -63,6 +63,14 @@
63
63
  },
64
64
  "description": {
65
65
  "$ref": "#/$defs/nonEmptyText"
66
+ },
67
+ "opensWhen": {
68
+ "description": "Conditions that must all hold before this wave opens (#334, ADR 0125). A wave that has not opened asks nothing: its questions are premature rather than answered, and the report says so with `opened: false` and no questions rather than reporting them closed. Written in the same condition vocabulary a question's trigger uses, so a reviewer reads a gate the way they already read a trigger. Absent means the wave is always open, which is what every wave did before this existed.",
69
+ "type": "array",
70
+ "minItems": 1,
71
+ "items": {
72
+ "$ref": "#/$defs/condition"
73
+ }
66
74
  }
67
75
  }
68
76
  }
@@ -503,6 +511,19 @@
503
511
  }
504
512
  }
505
513
  },
514
+ {
515
+ "type": "object",
516
+ "additionalProperties": false,
517
+ "description": "The workspace holds at least one concept (#334, ADR 0125). The guard a late wave needs to say \"only once the model has substance\": an empty model is not an architecture at rest, it has not started, and asking it how the planned architecture becomes real greets someone ahead of question one.",
518
+ "required": [
519
+ "condition"
520
+ ],
521
+ "properties": {
522
+ "condition": {
523
+ "const": "has-any-subject"
524
+ }
525
+ }
526
+ },
506
527
  {
507
528
  "type": "object",
508
529
  "additionalProperties": false,
@@ -31,6 +31,7 @@
31
31
  "kind",
32
32
  "kindLabel",
33
33
  "coreKindLabel",
34
+ "portKinds",
34
35
  "layer",
35
36
  "aspect",
36
37
  "name",
@@ -64,6 +65,14 @@
64
65
  "kindLabel": {
65
66
  "type": "string"
66
67
  },
68
+ "portKinds": {
69
+ "description": "The core relationship kinds this node's pattern ports, or [] where its kind has no pattern with ports (#268 phase 3, ADR 0124). A macro edge needs both ends to port its kind, so an editor offering a palette between two instances intersects the two lists.",
70
+ "type": "array",
71
+ "uniqueItems": true,
72
+ "items": {
73
+ "type": "string"
74
+ }
75
+ },
67
76
  "coreKindLabel": {
68
77
  "type": "string"
69
78
  },
@@ -28,6 +28,10 @@
28
28
  "projections": {
29
29
  "$ref": "#/$defs/patterns"
30
30
  },
31
+ "patterns": {
32
+ "description": "yarramate/pattern/v1 documents: the shape a concept kind promises, and the wiring the compiler mints between an instance and the parts it binds (ADR 0123). Structure, where profiles declare vocabulary. Optional, like evidence and contracts.",
33
+ "$ref": "#/$defs/patterns"
34
+ },
31
35
  "adapterMappings": {
32
36
  "$ref": "#/$defs/patterns"
33
37
  },