yarramate 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # YarraMate
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/yarramate)](https://www.npmjs.com/package/yarramate)
4
+ [![CI](https://github.com/yarrasys/yarramate/actions/workflows/ci.yml/badge.svg)](https://github.com/yarrasys/yarramate/actions/workflows/ci.yml)
5
+ [![CodeQL](https://github.com/yarrasys/yarramate/actions/workflows/codeql.yml/badge.svg)](https://github.com/yarrasys/yarramate/actions/workflows/codeql.yml)
6
+ [![license: MIT](https://img.shields.io/github/license/yarrasys/yarramate)](LICENSE)
7
+
3
8
  YarraMate is a tool-neutral semantic architecture engine and guided
4
9
  methodology. It turns architectural intent into deterministic, testable
5
10
  context shared by people and agents.
@@ -94,8 +99,16 @@ pnpm docs:dev
94
99
 
95
100
  ## CLI
96
101
 
97
- Build the repository, then invoke the same executable surface intended for
98
- published use:
102
+ Install the published executable or invoke it directly with `npx`:
103
+
104
+ ```sh
105
+ npm install --global yarramate
106
+ yarramate --help
107
+
108
+ npx yarramate check .yarramate/workspace.yaml
109
+ ```
110
+
111
+ When developing the repository, build and invoke the same executable surface:
99
112
 
100
113
  ```sh
101
114
  pnpm build
@@ -117,7 +130,7 @@ For a local consumer test, create a package artifact:
117
130
 
118
131
  ```sh
119
132
  pnpm pack --pack-destination /tmp/yarramate-package
120
- npm install --global /tmp/yarramate-package/yarramate-0.1.0.tgz
133
+ npm install --global /tmp/yarramate-package/yarramate-*.tgz
121
134
  yarramate --help
122
135
  ```
123
136
 
@@ -169,6 +182,8 @@ You do not need to provide a solution, formal proposal, or implementation.
169
182
 
170
183
  Read [CONTRIBUTING.md](CONTRIBUTING.md) before proposing changes to native
171
184
  semantics or stable interfaces. Report suspected vulnerabilities according to
172
- [SECURITY.md](SECURITY.md).
185
+ [SECURITY.md](SECURITY.md). Participation is governed by the
186
+ [Code of Conduct](CODE_OF_CONDUCT.md), and help channels are described in
187
+ [SUPPORT.md](SUPPORT.md).
173
188
 
174
189
  YarraMate is available under the [MIT License](LICENSE).
@@ -0,0 +1,500 @@
1
+ // YarraMate semantic profile for LikeC4.
2
+ //
3
+ // This vocabulary is ArchiMate-inspired but independently expressed. The tags
4
+ // encode semantic coordinates; element kinds carry the human-facing notation.
5
+ specification {
6
+ color motivationColor #E8D7F1
7
+ color strategyColor #F3E5AB
8
+ color businessColor #FFF1B8
9
+ color applicationColor #C9E7FF
10
+ color technologyColor #D8F0D2
11
+ color physicalColor #D5E8D4
12
+ color implementationColor #F5D5CB
13
+ color compositeColor #E6E6E6
14
+ color relationColor #5B6470
15
+
16
+ tag layerMotivation
17
+ tag layerStrategy
18
+ tag layerBusiness
19
+ tag layerApplication
20
+ tag layerTechnology
21
+ tag layerPhysical
22
+ tag layerImplementation
23
+ tag layerComposite
24
+
25
+ tag aspectActive
26
+ tag aspectBehavior
27
+ tag aspectPassive
28
+
29
+ // Adapter-owned deployment presentation kinds.
30
+ deploymentNode environment {
31
+ notation 'Environment'
32
+ style { color technologyColor }
33
+ }
34
+
35
+ deploymentNode zone {
36
+ notation 'Zone'
37
+ style { color compositeColor }
38
+ }
39
+
40
+ deploymentNode host {
41
+ notation 'Host'
42
+ style { color technologyColor }
43
+ }
44
+
45
+ deploymentNode runtime {
46
+ notation 'Runtime'
47
+ style { color applicationColor }
48
+ }
49
+
50
+ // ── Motivation ────────────────────────────────────────────────────────────
51
+
52
+ element stakeholder {
53
+ #layerMotivation
54
+ notation 'Stakeholder'
55
+ style { shape person; color motivationColor }
56
+ }
57
+
58
+ element driver {
59
+ #layerMotivation
60
+ notation 'Driver'
61
+ style { color motivationColor }
62
+ }
63
+
64
+ element assessment {
65
+ #layerMotivation
66
+ notation 'Assessment'
67
+ style { color motivationColor }
68
+ }
69
+
70
+ element goal {
71
+ #layerMotivation
72
+ notation 'Goal'
73
+ style { color motivationColor }
74
+ }
75
+
76
+ element outcome {
77
+ #layerMotivation
78
+ notation 'Outcome'
79
+ style { color motivationColor }
80
+ }
81
+
82
+ element principle {
83
+ #layerMotivation
84
+ notation 'Principle'
85
+ style { color motivationColor }
86
+ }
87
+
88
+ element requirement {
89
+ #layerMotivation
90
+ notation 'Requirement'
91
+ style { color motivationColor }
92
+ }
93
+
94
+ element constraint {
95
+ #layerMotivation
96
+ notation 'Constraint'
97
+ style { color motivationColor }
98
+ }
99
+
100
+ element meaning {
101
+ #layerMotivation
102
+ notation 'Meaning'
103
+ style { color motivationColor }
104
+ }
105
+
106
+ element value {
107
+ #layerMotivation
108
+ notation 'Value'
109
+ style { color motivationColor }
110
+ }
111
+
112
+ // ── Strategy ──────────────────────────────────────────────────────────────
113
+
114
+ element resource {
115
+ #layerStrategy #aspectActive
116
+ notation 'Resource'
117
+ style { color strategyColor }
118
+ }
119
+
120
+ element capability {
121
+ #layerStrategy #aspectBehavior
122
+ notation 'Capability'
123
+ style { color strategyColor }
124
+ }
125
+
126
+ element valueStream {
127
+ #layerStrategy #aspectBehavior
128
+ notation 'Value stream'
129
+ style { color strategyColor }
130
+ }
131
+
132
+ element courseOfAction {
133
+ #layerStrategy #aspectBehavior
134
+ notation 'Course of action'
135
+ style { color strategyColor }
136
+ }
137
+
138
+ // ── Business layer ────────────────────────────────────────────────────────
139
+
140
+ element businessActor {
141
+ #layerBusiness #aspectActive
142
+ notation 'Business actor'
143
+ style { shape person; color businessColor }
144
+ }
145
+
146
+ element businessRole {
147
+ #layerBusiness #aspectActive
148
+ notation 'Business role'
149
+ style { color businessColor }
150
+ }
151
+
152
+ element businessCollaboration {
153
+ #layerBusiness #aspectActive
154
+ notation 'Business collaboration'
155
+ style { color businessColor; multiple true }
156
+ }
157
+
158
+ element businessInterface {
159
+ #layerBusiness #aspectActive
160
+ notation 'Business interface'
161
+ style { color businessColor }
162
+ }
163
+
164
+ element businessProcess {
165
+ #layerBusiness #aspectBehavior
166
+ notation 'Business process'
167
+ style { color businessColor }
168
+ }
169
+
170
+ element businessFunction {
171
+ #layerBusiness #aspectBehavior
172
+ notation 'Business function'
173
+ style { color businessColor }
174
+ }
175
+
176
+ element businessInteraction {
177
+ #layerBusiness #aspectBehavior
178
+ notation 'Business interaction'
179
+ style { color businessColor }
180
+ }
181
+
182
+ element businessEvent {
183
+ #layerBusiness #aspectBehavior
184
+ notation 'Business event'
185
+ style { color businessColor }
186
+ }
187
+
188
+ element businessService {
189
+ #layerBusiness #aspectBehavior
190
+ notation 'Business service'
191
+ style { color businessColor }
192
+ }
193
+
194
+ element businessObject {
195
+ #layerBusiness #aspectPassive
196
+ notation 'Business object'
197
+ style { color businessColor }
198
+ }
199
+
200
+ element contract {
201
+ #layerBusiness #aspectPassive
202
+ notation 'Contract'
203
+ style { color businessColor }
204
+ }
205
+
206
+ element representation {
207
+ #layerBusiness #aspectPassive
208
+ notation 'Representation'
209
+ style { color businessColor }
210
+ }
211
+
212
+ element product {
213
+ #layerBusiness #aspectPassive
214
+ notation 'Product'
215
+ style { color businessColor }
216
+ }
217
+
218
+ // ── Application layer ─────────────────────────────────────────────────────
219
+
220
+ element applicationComponent {
221
+ #layerApplication #aspectActive
222
+ notation 'Application component'
223
+ style { shape component; color applicationColor }
224
+ }
225
+
226
+ element applicationCollaboration {
227
+ #layerApplication #aspectActive
228
+ notation 'Application collaboration'
229
+ style { color applicationColor; multiple true }
230
+ }
231
+
232
+ element applicationInterface {
233
+ #layerApplication #aspectActive
234
+ notation 'Application interface'
235
+ style { color applicationColor }
236
+ }
237
+
238
+ element applicationFunction {
239
+ #layerApplication #aspectBehavior
240
+ notation 'Application function'
241
+ style { color applicationColor }
242
+ }
243
+
244
+ element applicationInteraction {
245
+ #layerApplication #aspectBehavior
246
+ notation 'Application interaction'
247
+ style { color applicationColor }
248
+ }
249
+
250
+ element applicationProcess {
251
+ #layerApplication #aspectBehavior
252
+ notation 'Application process'
253
+ style { color applicationColor }
254
+ }
255
+
256
+ element applicationEvent {
257
+ #layerApplication #aspectBehavior
258
+ notation 'Application event'
259
+ style { color applicationColor }
260
+ }
261
+
262
+ element applicationService {
263
+ #layerApplication #aspectBehavior
264
+ notation 'Application service'
265
+ style { color applicationColor }
266
+ }
267
+
268
+ element dataObject {
269
+ #layerApplication #aspectPassive
270
+ notation 'Data object'
271
+ style { shape storage; color applicationColor }
272
+ }
273
+
274
+ // ── Technology layer ──────────────────────────────────────────────────────
275
+
276
+ element node {
277
+ #layerTechnology #aspectActive
278
+ notation 'Node'
279
+ style { color technologyColor }
280
+ }
281
+
282
+ element device {
283
+ #layerTechnology #aspectActive
284
+ notation 'Device'
285
+ style { color technologyColor }
286
+ }
287
+
288
+ element systemSoftware {
289
+ #layerTechnology #aspectActive
290
+ notation 'System software'
291
+ style { color technologyColor }
292
+ }
293
+
294
+ element technologyCollaboration {
295
+ #layerTechnology #aspectActive
296
+ notation 'Technology collaboration'
297
+ style { color technologyColor; multiple true }
298
+ }
299
+
300
+ element technologyInterface {
301
+ #layerTechnology #aspectActive
302
+ notation 'Technology interface'
303
+ style { color technologyColor }
304
+ }
305
+
306
+ element path {
307
+ #layerTechnology #aspectActive
308
+ notation 'Path'
309
+ style { color technologyColor }
310
+ }
311
+
312
+ element communicationNetwork {
313
+ #layerTechnology #aspectActive
314
+ notation 'Communication network'
315
+ style { color technologyColor }
316
+ }
317
+
318
+ element technologyFunction {
319
+ #layerTechnology #aspectBehavior
320
+ notation 'Technology function'
321
+ style { color technologyColor }
322
+ }
323
+
324
+ element technologyProcess {
325
+ #layerTechnology #aspectBehavior
326
+ notation 'Technology process'
327
+ style { color technologyColor }
328
+ }
329
+
330
+ element technologyInteraction {
331
+ #layerTechnology #aspectBehavior
332
+ notation 'Technology interaction'
333
+ style { color technologyColor }
334
+ }
335
+
336
+ element technologyEvent {
337
+ #layerTechnology #aspectBehavior
338
+ notation 'Technology event'
339
+ style { color technologyColor }
340
+ }
341
+
342
+ element technologyService {
343
+ #layerTechnology #aspectBehavior
344
+ notation 'Technology service'
345
+ style { color technologyColor }
346
+ }
347
+
348
+ element artifact {
349
+ #layerTechnology #aspectPassive
350
+ notation 'Artifact'
351
+ style { color technologyColor }
352
+ }
353
+
354
+ // ── Physical layer ────────────────────────────────────────────────────────
355
+
356
+ element equipment {
357
+ #layerPhysical #aspectActive
358
+ notation 'Equipment'
359
+ style { color physicalColor }
360
+ }
361
+
362
+ element facility {
363
+ #layerPhysical #aspectActive
364
+ notation 'Facility'
365
+ style { color physicalColor }
366
+ }
367
+
368
+ element distributionNetwork {
369
+ #layerPhysical #aspectActive
370
+ notation 'Distribution network'
371
+ style { color physicalColor }
372
+ }
373
+
374
+ element material {
375
+ #layerPhysical #aspectPassive
376
+ notation 'Material'
377
+ style { color physicalColor }
378
+ }
379
+
380
+ // ── Implementation and migration ─────────────────────────────────────────
381
+
382
+ element workPackage {
383
+ #layerImplementation #aspectBehavior
384
+ notation 'Work package'
385
+ style { color implementationColor }
386
+ }
387
+
388
+ element deliverable {
389
+ #layerImplementation #aspectPassive
390
+ notation 'Deliverable'
391
+ style { color implementationColor }
392
+ }
393
+
394
+ element implementationEvent {
395
+ #layerImplementation #aspectBehavior
396
+ notation 'Implementation event'
397
+ style { color implementationColor }
398
+ }
399
+
400
+ element plateau {
401
+ #layerImplementation
402
+ notation 'Plateau'
403
+ style { color implementationColor }
404
+ }
405
+
406
+ element gap {
407
+ #layerImplementation
408
+ notation 'Gap'
409
+ style { color implementationColor }
410
+ }
411
+
412
+ // ── Composite and relationship connectors ────────────────────────────────
413
+
414
+ element grouping {
415
+ #layerComposite
416
+ notation 'Grouping'
417
+ style { color compositeColor; border dashed }
418
+ }
419
+
420
+ element location {
421
+ #layerComposite
422
+ notation 'Location'
423
+ style { color compositeColor }
424
+ }
425
+
426
+ element andJunction {
427
+ #layerComposite
428
+ notation 'AND junction'
429
+ style { color compositeColor }
430
+ }
431
+
432
+ element orJunction {
433
+ #layerComposite
434
+ notation 'OR junction'
435
+ style { color compositeColor }
436
+ }
437
+
438
+ // ── Relationships ─────────────────────────────────────────────────────────
439
+
440
+ relationship composition {
441
+ description 'Strong whole-part relationship.'
442
+ color relationColor
443
+ }
444
+
445
+ relationship aggregation {
446
+ description 'Weak whole-part relationship.'
447
+ color relationColor
448
+ line dashed
449
+ }
450
+
451
+ relationship assignment {
452
+ description 'Allocation of responsibility, execution, or use.'
453
+ color relationColor
454
+ }
455
+
456
+ relationship realization {
457
+ description 'Implementation or fulfillment of a more abstract concept.'
458
+ color relationColor
459
+ line dashed
460
+ }
461
+
462
+ relationship serving {
463
+ description 'Functionality made available to another concept.'
464
+ color relationColor
465
+ }
466
+
467
+ relationship access {
468
+ description 'Use, creation, modification, or reading of passive structure.'
469
+ color relationColor
470
+ line dashed
471
+ }
472
+
473
+ relationship influence {
474
+ description 'Effect on a motivation concept.'
475
+ color relationColor
476
+ line dotted
477
+ }
478
+
479
+ relationship association {
480
+ description 'Semantically relevant connection not expressed more specifically.'
481
+ color relationColor
482
+ line dashed
483
+ }
484
+
485
+ relationship triggering {
486
+ description 'Temporal or causal precedence between behaviors or events.'
487
+ color relationColor
488
+ }
489
+
490
+ relationship flow {
491
+ description 'Transfer of information, value, goods, or another object.'
492
+ color relationColor
493
+ line dashed
494
+ }
495
+
496
+ relationship specialization {
497
+ description 'Concept is a more specific form of another concept.'
498
+ color relationColor
499
+ }
500
+ }
@@ -151,7 +151,7 @@ const publishGeneratedProject = (cwd, outputDirectory, input) => {
151
151
  else {
152
152
  mkdirSync(projectPath, { recursive: true });
153
153
  }
154
- const specificationSource = readFileSync(fileURLToPath(new URL('../../.yarramate/integrations/likec4/prototype/specification.likec4', import.meta.url)));
154
+ const specificationSource = readFileSync(fileURLToPath(new URL('../../assets/likec4/specification.likec4', import.meta.url)));
155
155
  const configSource = `${JSON.stringify({
156
156
  $schema: 'https://likec4.dev/schemas/config.json',
157
157
  name: input.projectName,
@@ -133,6 +133,10 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
133
133
  'constraints',
134
134
  referencesFor(projection.claims, concept.id, 'yarramate/constraint/requires'),
135
135
  ],
136
+ [
137
+ 'references',
138
+ referencesFor(projection.claims, concept.id, 'yarramate/reference/refers-to'),
139
+ ],
136
140
  ], ' ');
137
141
  if (description === undefined && metadata.length === 0) {
138
142
  lines.push(` ${external} = ${kind} ${quote(name)}`);
@@ -159,6 +163,7 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
159
163
  if (source === undefined || target === undefined)
160
164
  continue;
161
165
  const name = valueFor(projection.claims, relationship.id, 'yarramate/relationship/name');
166
+ const description = valueFor(projection.claims, relationship.id, 'yarramate/relationship/description');
162
167
  const metadata = metadataLines([
163
168
  ['yarramateId', relationship.id],
164
169
  ['yarramateKind', structural.predicate],
@@ -175,8 +180,16 @@ export function exportLikeC4(projection, mapping, kindMapping, options = {}) {
175
180
  'content',
176
181
  valueFor(projection.claims, relationship.id, 'yarramate/flow/content'),
177
182
  ],
183
+ [
184
+ 'references',
185
+ referencesFor(projection.claims, relationship.id, 'yarramate/reference/refers-to'),
186
+ ],
178
187
  ], ' ');
179
- lines.push(` ${source} -[${externalRelationshipKind.get(structural.predicate) ?? kindId(structural.predicate)}]-> ${target}${name === undefined ? '' : ` ${quote(name)}`}${metadata.length === 0 ? '' : ' {'}`, ...metadata, ...(metadata.length === 0 ? [] : [' }']));
188
+ lines.push(` ${source} -[${externalRelationshipKind.get(structural.predicate) ?? kindId(structural.predicate)}]-> ${target}${name === undefined ? '' : ` ${quote(name)}`}${description === undefined && metadata.length === 0 ? '' : ' {'}`, ...(description === undefined
189
+ ? []
190
+ : [` description ${quote(description)}`]), ...metadata, ...(description === undefined && metadata.length === 0
191
+ ? []
192
+ : [' }']));
180
193
  }
181
194
  const viewId = identifier.test(projection.projection.split('@')[0] ?? '')
182
195
  ? projection.projection.split('@')[0]
@@ -73,7 +73,7 @@ const viewBody = ({ id, prepared, dynamic, deployment, }) => {
73
73
  }
74
74
  if (dynamic !== undefined) {
75
75
  const claimsById = new Map(prepared.projection.claims.map((claim) => [claim.id, claim]));
76
- const lines = dynamic.steps.map((step) => {
76
+ const lines = dynamic.steps.flatMap((step) => {
77
77
  const structural = claimsById.get(step.relationship);
78
78
  const source = externalByNative.get(structural.subject);
79
79
  const target = 'ref' in structural.object
@@ -86,7 +86,21 @@ const viewBody = ({ id, prepared, dynamic, deployment, }) => {
86
86
  (declaredTitle !== undefined && 'value' in declaredTitle.object
87
87
  ? declaredTitle.object.value
88
88
  : undefined);
89
- return ` ${source} -> ${target}${title === undefined ? '' : ` ${quote(title)}`}`;
89
+ const declaredDescription = prepared.projection.claims.find((claim) => claim.subject === step.relationship &&
90
+ claim.predicate === 'yarramate/relationship/description' &&
91
+ 'value' in claim.object);
92
+ const description = declaredDescription !== undefined &&
93
+ 'value' in declaredDescription.object
94
+ ? declaredDescription.object.value
95
+ : undefined;
96
+ const statement = ` ${source} -> ${target}${title === undefined ? '' : ` ${quote(title)}`}`;
97
+ return description === undefined
98
+ ? [statement]
99
+ : [
100
+ `${statement} {`,
101
+ ` description ${quote(description)}`,
102
+ ' }',
103
+ ];
90
104
  });
91
105
  const viewId = id ?? prepared.projection.projection.split('@')[0];
92
106
  return [
@@ -5,7 +5,7 @@ export interface CliResult {
5
5
  readonly stderr: string;
6
6
  }
7
7
  export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
8
- export declare const usage = "Usage:\n yarramate init <directory>\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--status <status>] [--mode <mode>] [--content <text>] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n";
8
+ export declare const usage = "Usage:\n yarramate init <directory>\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n";
9
9
  export declare const diagnosticJson: (diagnostics: unknown) => string;
10
10
  export declare const checkResultJson: (ok: boolean, diagnostics: unknown) => string;
11
11
  export declare const humanDiagnostics: (diagnostics: readonly Pick<Diagnostic, "path" | "line" | "column" | "code" | "message">[]) => string;
@@ -14,7 +14,7 @@ export const isMainModule = (moduleUrl, entrypoint) => {
14
14
  return false;
15
15
  }
16
16
  };
17
- export const usage = 'Usage:\n yarramate init <directory>\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--status <status>] [--mode <mode>] [--content <text>] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n';
17
+ export const usage = 'Usage:\n yarramate init <directory>\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n';
18
18
  export const diagnosticJson = (diagnostics) => `${JSON.stringify({
19
19
  format: 'yarramate/diagnostic-result/v1',
20
20
  diagnostics,
package/dist/cli.js CHANGED
@@ -301,6 +301,7 @@ const oneFlag = (flags, flag) => {
301
301
  };
302
302
  const hasRepeatedSingletonFlag = (flags) => [...flags.entries()].some(([flag, values]) => flag !== '--source' &&
303
303
  flag !== '--constraint' &&
304
+ flag !== '--reference' &&
304
305
  flag !== '--present-in' &&
305
306
  values.length !== 1);
306
307
  const appendBlockItem = (document, collection, item) => {
@@ -310,6 +311,20 @@ const appendBlockItem = (document, collection, item) => {
310
311
  sequence.flow = false;
311
312
  }
312
313
  };
314
+ const identifiedReferences = (values) => {
315
+ const parsed = values.map((value) => {
316
+ const separator = value.indexOf('=');
317
+ return separator <= 0 || separator === value.length - 1
318
+ ? undefined
319
+ : {
320
+ id: value.slice(0, separator),
321
+ ref: value.slice(separator + 1),
322
+ };
323
+ });
324
+ return parsed.some((reference) => reference === undefined)
325
+ ? undefined
326
+ : parsed;
327
+ };
313
328
  const runAdd = (options, cwd) => {
314
329
  const parsed = parseFlags(options);
315
330
  if (parsed === undefined) {
@@ -323,6 +338,7 @@ const runAdd = (options, cwd) => {
323
338
  '--description',
324
339
  '--owner',
325
340
  '--constraint',
341
+ '--reference',
326
342
  '--present-in',
327
343
  '--source',
328
344
  ]);
@@ -336,16 +352,9 @@ const runAdd = (options, cwd) => {
336
352
  if (id === undefined || kind === undefined || name === undefined) {
337
353
  return { exitCode: 2, stdout: '', stderr: usage };
338
354
  }
339
- const constraints = (parsed.flags.get('--constraint') ?? []).map((value) => {
340
- const separator = value.indexOf('=');
341
- return separator <= 0 || separator === value.length - 1
342
- ? undefined
343
- : {
344
- id: value.slice(0, separator),
345
- ref: value.slice(separator + 1),
346
- };
347
- });
348
- if (constraints.some((constraint) => constraint === undefined)) {
355
+ const constraints = identifiedReferences(parsed.flags.get('--constraint') ?? []);
356
+ const references = identifiedReferences(parsed.flags.get('--reference') ?? []);
357
+ if (constraints === undefined || references === undefined) {
349
358
  return { exitCode: 2, stdout: '', stderr: usage };
350
359
  }
351
360
  try {
@@ -365,6 +374,7 @@ const runAdd = (options, cwd) => {
365
374
  ? {}
366
375
  : { owner: oneFlag(parsed.flags, '--owner') }),
367
376
  ...(constraints.length === 0 ? {} : { constraints }),
377
+ ...(references.length === 0 ? {} : { references }),
368
378
  ...(parsed.flags.get('--present-in') === undefined
369
379
  ? {}
370
380
  : { presentIn: parsed.flags.get('--present-in') }),
@@ -417,9 +427,11 @@ const runConnect = (options, cwd) => {
417
427
  '--from',
418
428
  '--to',
419
429
  '--name',
430
+ '--description',
420
431
  '--status',
421
432
  '--mode',
422
433
  '--content',
434
+ '--reference',
423
435
  '--present-in',
424
436
  '--source',
425
437
  ]);
@@ -437,6 +449,10 @@ const runConnect = (options, cwd) => {
437
449
  to === undefined) {
438
450
  return { exitCode: 2, stdout: '', stderr: usage };
439
451
  }
452
+ const references = identifiedReferences(parsed.flags.get('--reference') ?? []);
453
+ if (references === undefined) {
454
+ return { exitCode: 2, stdout: '', stderr: usage };
455
+ }
440
456
  try {
441
457
  const absolutePath = resolve(cwd, parsed.path);
442
458
  const document = parseDocument(readFileSync(absolutePath, 'utf8'));
@@ -448,6 +464,9 @@ const runConnect = (options, cwd) => {
448
464
  ...(oneFlag(parsed.flags, '--name') === undefined
449
465
  ? {}
450
466
  : { name: oneFlag(parsed.flags, '--name') }),
467
+ ...(oneFlag(parsed.flags, '--description') === undefined
468
+ ? {}
469
+ : { description: oneFlag(parsed.flags, '--description') }),
451
470
  ...(oneFlag(parsed.flags, '--status') === undefined
452
471
  ? {}
453
472
  : { status: oneFlag(parsed.flags, '--status') }),
@@ -457,6 +476,7 @@ const runConnect = (options, cwd) => {
457
476
  ...(oneFlag(parsed.flags, '--content') === undefined
458
477
  ? {}
459
478
  : { content: oneFlag(parsed.flags, '--content') }),
479
+ ...(references.length === 0 ? {} : { references }),
460
480
  ...(parsed.flags.get('--present-in') === undefined
461
481
  ? {}
462
482
  : { presentIn: parsed.flags.get('--present-in') }),
@@ -535,6 +555,9 @@ const runCompile = (options, cwd) => {
535
555
  };
536
556
  export function runCli(args, cwd = process.cwd()) {
537
557
  const [command, ...options] = args;
558
+ if (command === '--help' || command === '-h' || command === 'help') {
559
+ return { exitCode: 0, stdout: usage, stderr: '' };
560
+ }
538
561
  if (command === 'init') {
539
562
  return runInit(options, cwd);
540
563
  }
package/dist/compiler.js CHANGED
@@ -376,6 +376,11 @@ function compileWorkspaceResolved(sources) {
376
376
  ])));
377
377
  const qualifyReference = (documentId, reference) => reference.includes('#') ? reference : `${documentId}#${reference}`;
378
378
  const architectureStateIds = new Set(documents.flatMap(({ value }) => (value.states ?? []).map((state) => `${value.id}#${state.id}`)));
379
+ const subjectIds = new Set(documents.flatMap(({ value }) => [
380
+ ...(value.states ?? []).map((state) => `${value.id}#${state.id}`),
381
+ ...value.concepts.map((concept) => `${value.id}#${concept.id}`),
382
+ ...value.relationships.map((relationship) => `${value.id}#${relationship.id}`),
383
+ ]));
379
384
  const architectureStateAfter = new Map(documents.flatMap(({ value }) => (value.states ?? []).flatMap((state) => state.after === undefined
380
385
  ? []
381
386
  : [
@@ -582,6 +587,36 @@ function compileWorkspaceResolved(sources) {
582
587
  });
583
588
  }
584
589
  }
590
+ const seenReferenceIds = new Set();
591
+ for (const [referenceIndex, reference] of (concept.references ?? []).entries()) {
592
+ if (seenReferenceIds.has(reference.id)) {
593
+ const pointer = `/concepts/${index}/references/${referenceIndex}/id`;
594
+ const source = location(['concepts', index, 'references', referenceIndex, 'id'], pointer);
595
+ diagnostics.push({
596
+ severity: 'error',
597
+ code: 'YM309',
598
+ message: `Duplicate reference ID "${reference.id}"`,
599
+ path: input.path,
600
+ pointer,
601
+ line: source.line,
602
+ column: source.column,
603
+ });
604
+ }
605
+ seenReferenceIds.add(reference.id);
606
+ if (!subjectIds.has(qualifyReference(value.id, reference.ref))) {
607
+ const pointer = `/concepts/${index}/references/${referenceIndex}/ref`;
608
+ const source = location(['concepts', index, 'references', referenceIndex, 'ref'], pointer);
609
+ diagnostics.push({
610
+ severity: 'error',
611
+ code: 'YM308',
612
+ message: `Unresolved subject reference "${reference.ref}"`,
613
+ path: input.path,
614
+ pointer,
615
+ line: source.line,
616
+ column: source.column,
617
+ });
618
+ }
619
+ }
585
620
  for (const [stateIndex, state] of (concept.presentIn ?? []).entries()) {
586
621
  if (!architectureStateIds.has(qualifyReference(value.id, state))) {
587
622
  const pointer = `/concepts/${index}/presentIn/${stateIndex}`;
@@ -599,6 +634,48 @@ function compileWorkspaceResolved(sources) {
599
634
  }
600
635
  }
601
636
  for (const [index, relationship] of value.relationships.entries()) {
637
+ const seenReferenceIds = new Set();
638
+ for (const [referenceIndex, reference] of (relationship.references ?? []).entries()) {
639
+ if (seenReferenceIds.has(reference.id)) {
640
+ const pointer = `/relationships/${index}/references/${referenceIndex}/id`;
641
+ const source = location([
642
+ 'relationships',
643
+ index,
644
+ 'references',
645
+ referenceIndex,
646
+ 'id',
647
+ ], pointer);
648
+ diagnostics.push({
649
+ severity: 'error',
650
+ code: 'YM309',
651
+ message: `Duplicate reference ID "${reference.id}"`,
652
+ path: input.path,
653
+ pointer,
654
+ line: source.line,
655
+ column: source.column,
656
+ });
657
+ }
658
+ seenReferenceIds.add(reference.id);
659
+ if (!subjectIds.has(qualifyReference(value.id, reference.ref))) {
660
+ const pointer = `/relationships/${index}/references/${referenceIndex}/ref`;
661
+ const source = location([
662
+ 'relationships',
663
+ index,
664
+ 'references',
665
+ referenceIndex,
666
+ 'ref',
667
+ ], pointer);
668
+ diagnostics.push({
669
+ severity: 'error',
670
+ code: 'YM308',
671
+ message: `Unresolved subject reference "${reference.ref}"`,
672
+ path: input.path,
673
+ pointer,
674
+ line: source.line,
675
+ column: source.column,
676
+ });
677
+ }
678
+ }
602
679
  for (const [stateIndex, state] of (relationship.presentIn ?? []).entries()) {
603
680
  const stateIdentity = qualifyReference(value.id, state);
604
681
  if (!architectureStateIds.has(stateIdentity)) {
@@ -785,6 +862,18 @@ function compileWorkspaceResolved(sources) {
785
862
  source: location(['concepts', index, 'constraints', constraintIndex, 'ref'], `/concepts/${index}/constraints/${constraintIndex}/ref`),
786
863
  });
787
864
  }
865
+ for (const [referenceIndex, reference] of (concept.references ?? []).entries()) {
866
+ claims.push({
867
+ id: `${subject}~reference-${reference.id}`,
868
+ subject,
869
+ predicate: 'yarramate/reference/refers-to',
870
+ object: {
871
+ ref: qualifyReference(value.id, reference.ref),
872
+ },
873
+ origin: 'declared',
874
+ source: location(['concepts', index, 'references', referenceIndex, 'ref'], `/concepts/${index}/references/${referenceIndex}/ref`),
875
+ });
876
+ }
788
877
  for (const [stateIndex, state] of (concept.presentIn ?? []).entries()) {
789
878
  const stateIdentity = qualifyReference(value.id, state);
790
879
  claims.push({
@@ -819,6 +908,16 @@ function compileWorkspaceResolved(sources) {
819
908
  source: location(['relationships', index, 'name'], `/relationships/${index}/name`),
820
909
  });
821
910
  }
911
+ if (relationship.description !== undefined) {
912
+ claims.push({
913
+ id: `${id}~description`,
914
+ subject: id,
915
+ predicate: 'yarramate/relationship/description',
916
+ object: { value: relationship.description },
917
+ origin: 'declared',
918
+ source: location(['relationships', index, 'description'], `/relationships/${index}/description`),
919
+ });
920
+ }
822
921
  if (relationship.mode !== undefined) {
823
922
  claims.push({
824
923
  id: `${id}~mode`,
@@ -849,6 +948,18 @@ function compileWorkspaceResolved(sources) {
849
948
  source: location(['relationships', index, 'status'], `/relationships/${index}/status`),
850
949
  });
851
950
  }
951
+ for (const [referenceIndex, reference] of (relationship.references ?? []).entries()) {
952
+ claims.push({
953
+ id: `${id}~reference-${reference.id}`,
954
+ subject: id,
955
+ predicate: 'yarramate/reference/refers-to',
956
+ object: {
957
+ ref: qualifyReference(value.id, reference.ref),
958
+ },
959
+ origin: 'declared',
960
+ source: location(['relationships', index, 'references', referenceIndex, 'ref'], `/relationships/${index}/references/${referenceIndex}/ref`),
961
+ });
962
+ }
852
963
  for (const [stateIndex, state] of (relationship.presentIn ?? []).entries()) {
853
964
  const stateIdentity = qualifyReference(value.id, state);
854
965
  claims.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yarramate",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Tool-neutral semantic architecture engine and guided methodology",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,6 +23,7 @@
23
23
  ],
24
24
  "files": [
25
25
  "dist",
26
+ "assets/likec4",
26
27
  "schema",
27
28
  "skills/yarramate-architecture",
28
29
  "docs/CONSUMING-YARRAMATE.md"
@@ -78,7 +79,7 @@
78
79
  "build": "tsc -p tsconfig.build.json",
79
80
  "prepack": "pnpm build",
80
81
  "docs:dev": "pnpm self:export:likec4 && likec4 serve .yarramate-out/likec4",
81
- "format": "likec4 format .yarramate/integrations/likec4/prototype",
82
+ "format": "likec4 format assets/likec4",
82
83
  "self:check": "pnpm build && node dist/cli.js check .yarramate/workspace.yaml",
83
84
  "self:check:json": "pnpm build && node dist/cli.js check .yarramate/workspace.yaml --json",
84
85
  "self:compile": "pnpm build && node dist/cli.js compile .yarramate/workspace.yaml",
@@ -92,6 +92,7 @@
92
92
  "deterministic-diagnostic-order",
93
93
  "git-native-governance",
94
94
  "globally-qualified-compiled-identities",
95
+ "identified-reference-integrity",
95
96
  "no-partial-graph-on-error",
96
97
  "portable-projection-selectors",
97
98
  "source-located-diagnostics"
@@ -110,6 +111,7 @@
110
111
  "architectural-quality",
111
112
  "automatic-discovery",
112
113
  "external-language-conformance",
114
+ "formal-workflow-semantics",
113
115
  "state-scoped-claim-values"
114
116
  ]
115
117
  }
@@ -100,6 +100,9 @@
100
100
  "$ref": "#/$defs/constraint"
101
101
  }
102
102
  },
103
+ "references": {
104
+ "$ref": "#/$defs/identifiedReferences"
105
+ },
103
106
  "presentIn": {
104
107
  "$ref": "#/$defs/stateReferences"
105
108
  }
@@ -118,6 +121,26 @@
118
121
  }
119
122
  }
120
123
  },
124
+ "identifiedReference": {
125
+ "type": "object",
126
+ "additionalProperties": false,
127
+ "required": ["id", "ref"],
128
+ "properties": {
129
+ "id": {
130
+ "$ref": "#/$defs/id"
131
+ },
132
+ "ref": {
133
+ "$ref": "#/$defs/reference"
134
+ }
135
+ }
136
+ },
137
+ "identifiedReferences": {
138
+ "type": "array",
139
+ "minItems": 1,
140
+ "items": {
141
+ "$ref": "#/$defs/identifiedReference"
142
+ }
143
+ },
121
144
  "relationship": {
122
145
  "type": "object",
123
146
  "additionalProperties": false,
@@ -139,6 +162,9 @@
139
162
  "name": {
140
163
  "$ref": "#/$defs/nonEmptyText"
141
164
  },
165
+ "description": {
166
+ "$ref": "#/$defs/nonEmptyText"
167
+ },
142
168
  "mode": {
143
169
  "enum": ["read", "write", "read-write", "unspecified"]
144
170
  },
@@ -148,6 +174,9 @@
148
174
  "status": {
149
175
  "$ref": "#/$defs/lifecycleStatus"
150
176
  },
177
+ "references": {
178
+ "$ref": "#/$defs/identifiedReferences"
179
+ },
151
180
  "presentIn": {
152
181
  "$ref": "#/$defs/stateReferences"
153
182
  }
@@ -43,7 +43,11 @@ relationships:
43
43
  kind: access
44
44
  from: delivery-api
45
45
  to: delivery-data
46
+ description: The API uses the governed record without maintaining a copy.
46
47
  mode: read-write
48
+ references:
49
+ - id: residency-policy
50
+ ref: delivery-data
47
51
  ```
48
52
 
49
53
  IDs are document-local and compile to `document-id#subject-id`. Cross-document
@@ -76,6 +80,29 @@ use association when no stronger semantic meaning is justified.
76
80
  Ownership is one accountable reference, not approval workflow. Constraints are
77
81
  identified references, not a policy engine or free-form metadata bag.
78
82
 
83
+ ## Rationale and citations
84
+
85
+ Use `description` on either a concept or relationship for decided narrative
86
+ about that exact subject. Use an identified `references` entry when the
87
+ narrative depends on another concept or relationship and the citation must
88
+ remain checkable:
89
+
90
+ ```yaml
91
+ description: Failure releases the lease and retains partial evidence.
92
+ references:
93
+ - id: failure-destination
94
+ ref: retry-pool
95
+ ```
96
+
97
+ Core checks the explicit target and local reference ID. It does not scan prose
98
+ for IDs or interpret descriptions as formal preconditions, postconditions, or
99
+ workflow rules.
100
+
101
+ For interaction flows, model steps that need identity as behavior concepts and
102
+ model normal or failure transitions as native relationships. A LikeC4 dynamic
103
+ view may order those projected relationships and display their descriptions;
104
+ the view does not become the workflow source of truth.
105
+
79
106
  ## Architecture states
80
107
 
81
108
  ```yaml
@@ -153,7 +180,9 @@ yarramate add .yarramate/architecture/main.yaml \
153
180
  --id delivery-api --kind applicationComponent --name "Delivery API"
154
181
  yarramate connect .yarramate/architecture/main.yaml \
155
182
  --id api-realizes-service --kind realization \
156
- --from delivery-api --to delivery-service
183
+ --from delivery-api --to delivery-service \
184
+ --description "The API implements the agreed delivery boundary" \
185
+ --reference decision-source=delivery-service
157
186
  yarramate check .yarramate/workspace.yaml --json
158
187
  yarramate compile .yarramate/workspace.yaml
159
188
  yarramate context <projection.yaml> .yarramate/workspace.yaml