yarramate 1.0.0 → 1.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.
@@ -135,6 +135,23 @@ export type IncrementalCompilationResult = ({
135
135
  readonly incremental: boolean;
136
136
  readonly cache: CompilationCache;
137
137
  };
138
+ /**
139
+ * A succession entry: a bare predecessor id, or one with the respect in which
140
+ * it was superseded.
141
+ *
142
+ * The scope is load-bearing rather than decorative. A model claimed that Zoekt
143
+ * superseded the Elasticsearch indexer, unqualified, while the source it was
144
+ * built from says Zoekt "handles only code search and does not replace
145
+ * Elasticsearch". The prose carried the qualifier and the field could not, and
146
+ * `ask --compare` reads the field, so the declared target architecture became
147
+ * the deletion of a component that is not being deleted (ADR 0109).
148
+ */
149
+ export type NativeSuccession = string | {
150
+ readonly subject: string;
151
+ readonly inRespectOf: string;
152
+ };
153
+ export declare const successionSubject: (entry: NativeSuccession) => string;
154
+ export declare const successionScope: (entry: NativeSuccession) => string | undefined;
138
155
  export { ATTESTATION_PREDICATE_PREFIX, attestationClaimValue, parseAttestationClaimValue, parseConstraintExpectsValue, type AttestationClaimParts, type ConstraintExpectsParts, } from './graph-claims.js';
139
156
  interface ResolvedPosition {
140
157
  readonly line: number;
@@ -8,15 +8,39 @@ export interface EvidenceObservedValue {
8
8
  readonly key: string;
9
9
  readonly value: string;
10
10
  }
11
+ /**
12
+ * One search a provider ran and found nothing at, recorded so a reader can
13
+ * re-run it. yarramate never executes it: the engine has no access to the
14
+ * subject tree, and gaining one would be a different decision (ADR 0107).
15
+ */
16
+ export type SearchProbe = {
17
+ readonly glob: string;
18
+ } | {
19
+ readonly grep: string;
20
+ readonly paths?: readonly string[];
21
+ };
22
+ /**
23
+ * A figure quoted in an evidence message, with how it was produced, so a
24
+ * reader can tell a measured number from a remembered one and re-derive it at
25
+ * a later commit.
26
+ */
27
+ export interface Measurement {
28
+ readonly value: string;
29
+ readonly method: string;
30
+ }
31
+ interface ObservationProvenance {
32
+ readonly searched?: readonly SearchProbe[];
33
+ readonly measured?: readonly Measurement[];
34
+ }
11
35
  export type EvidenceObservation = ({
12
36
  readonly subject: string;
13
37
  readonly result: EvidenceResult;
14
38
  readonly evidence: EvidenceLocator;
15
- } & Partial<EvidenceObservedValue>) | ({
39
+ } & ObservationProvenance & Partial<EvidenceObservedValue>) | ({
16
40
  readonly claim: string;
17
41
  readonly result: EvidenceResult;
18
42
  readonly evidence: EvidenceLocator;
19
- } & Partial<EvidenceObservedValue>);
43
+ } & ObservationProvenance & Partial<EvidenceObservedValue>);
20
44
  export interface EvidenceDocument {
21
45
  readonly format: 'yarramate/evidence/v1';
22
46
  readonly id: string;
@@ -60,3 +84,4 @@ export type EvidenceWorkspaceEvaluationResult = {
60
84
  export declare function loadEvidence(source: WorkspaceSource): EvidenceLoadResult;
61
85
  export declare function evaluateEvidence(graph: SemanticGraph, evidence: EvidenceDocument): EvidenceEvaluationResult;
62
86
  export declare function evaluateEvidenceWorkspace(graph: SemanticGraph, evidenceDocuments: readonly EvidenceDocument[]): EvidenceWorkspaceEvaluationResult;
87
+ export {};
@@ -232,6 +232,53 @@ the notation module is the rendering vocabulary for that mode; the element
232
232
  vocabulary and relationship table themselves are implemented in the core
233
233
  profile (ADR 0097).
234
234
 
235
+ ## Evaluating question catalogues off Node
236
+
237
+ The interrogation engine is public API. A consumer that compiles a workspace
238
+ itself can load a catalogue and evaluate it without the CLI:
239
+
240
+ ```ts
241
+ import {
242
+ evaluateCatalogue,
243
+ loadQuestionCatalogue,
244
+ INTERROGATION_SEMANTICS_VERSION,
245
+ } from 'yarramate/interrogation'
246
+ ```
247
+
248
+ **Import the subpath, not the package entry, wherever the runtime is not
249
+ Node.** The `.` barrel reaches `node:fs`, `node:path` and `node:child_process`
250
+ through workspace loading, the filesystem source store and git-derived
251
+ attestation staleness, so taking the engine from there drags Node in behind it.
252
+ `yarramate/interrogation` carries the engine alone and is pinned free of Node
253
+ built-ins by the same test that guards `yarramate/adapter/visual-graph`. The
254
+ same names are also exported from `.` for Node consumers.
255
+
256
+ `evaluateCatalogue` returns the report without its `workspace`, which the
257
+ caller supplies:
258
+
259
+ ```ts
260
+ const report = { workspace: id, ...evaluateCatalogue(catalogue, graph, profileContext) }
261
+ ```
262
+
263
+ The report carries `semantics`, the version of condition evaluation, which
264
+ changes only when an existing question's answer can change for an unchanged
265
+ model ([ADR 0106](adr/0106-a-report-says-which-engine-answered.md)). A consumer
266
+ that **persists** answers should store it beside them: equal means a flipped
267
+ answer is about the model and belongs in front of a user, different means the
268
+ engine moved and the right response is to re-baseline silently rather than
269
+ reopen someone's queue.
270
+
271
+ Every question also carries `trigger`, the catalogue conditions that opened
272
+ it, verbatim ([ADR 0110](adr/0110-an-open-question-carries-its-answer-shape.md)).
273
+ That is the question's machine-readable answer shape: a host building an
274
+ answering affordance — a concept form with the kind preselected from
275
+ `no-subject-of-kind`, a relationship editor with one endpoint fixed by
276
+ `missing-relationship`'s `direction`, an attestation form on
277
+ `missing-attestation`'s `topic` — maps the conditions directly instead of
278
+ re-deriving the shape from its own catalogue copy. The field is required and
279
+ the published report schema uses `additionalProperties: false`, so upgrade a
280
+ separately pinned schema together with the package.
281
+
235
282
  ### Mount the visual editor
236
283
 
237
284
  Mount the packaged editor when the consuming product owns the sources and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yarramate",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Tool-neutral semantic architecture engine and guided methodology",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -54,6 +54,10 @@
54
54
  "types": "./dist/notation/archimate.d.ts",
55
55
  "import": "./dist/notation/archimate.js"
56
56
  },
57
+ "./interrogation": {
58
+ "types": "./dist/interrogation-entry.d.ts",
59
+ "import": "./dist/interrogation-entry.js"
60
+ },
57
61
  "./visual-app": {
58
62
  "types": "./dist/visual-app-lib/editor.d.ts",
59
63
  "import": "./dist/visual-app-lib/editor.js"
@@ -522,6 +522,7 @@
522
522
  "format",
523
523
  "workspace",
524
524
  "catalogue",
525
+ "semantics",
525
526
  "summary",
526
527
  "waves"
527
528
  ],
@@ -535,6 +536,9 @@
535
536
  "catalogue": {
536
537
  "type": "string"
537
538
  },
539
+ "semantics": {
540
+ "type": "string"
541
+ },
538
542
  "summary": {
539
543
  "type": "object"
540
544
  },
@@ -875,6 +879,10 @@
875
879
  },
876
880
  "notObserved": {
877
881
  "type": "integer"
882
+ },
883
+ "unsupportedAbsences": {
884
+ "type": "integer",
885
+ "minimum": 0
878
886
  }
879
887
  },
880
888
  "additionalProperties": false
@@ -950,6 +958,10 @@
950
958
  "notObserved": {
951
959
  "type": "integer"
952
960
  },
961
+ "unsupportedAbsences": {
962
+ "type": "integer",
963
+ "minimum": 0
964
+ },
953
965
  "subjectsWithoutEvidence": {
954
966
  "type": "integer"
955
967
  },
@@ -83,7 +83,8 @@
83
83
  "authority",
84
84
  "question",
85
85
  "materiality",
86
- "resolution"
86
+ "resolution",
87
+ "trigger"
87
88
  ],
88
89
  "properties": {
89
90
  "questionId": {
@@ -123,6 +124,14 @@
123
124
  "type": "string",
124
125
  "minLength": 1
125
126
  },
127
+ "trigger": {
128
+ "description": "The catalogue trigger, verbatim: the conditions that opened this question, i.e. its machine-readable answer shape.",
129
+ "type": "array",
130
+ "minItems": 1,
131
+ "items": {
132
+ "$ref": "#/$defs/condition"
133
+ }
134
+ },
126
135
  "subject": {
127
136
  "type": "object",
128
137
  "additionalProperties": false,
@@ -163,5 +172,377 @@
163
172
  "type": "string",
164
173
  "minLength": 1
165
174
  }
175
+ },
176
+ "$defs": {
177
+ "condition": {
178
+ "description": "One deterministic trigger condition. All conditions in a trigger must hold (AND) for the question to be open.",
179
+ "oneOf": [
180
+ {
181
+ "type": "object",
182
+ "additionalProperties": false,
183
+ "required": [
184
+ "condition",
185
+ "predicate"
186
+ ],
187
+ "properties": {
188
+ "condition": {
189
+ "const": "missing-claim"
190
+ },
191
+ "predicate": {
192
+ "$ref": "#/$defs/claimPredicate"
193
+ }
194
+ }
195
+ },
196
+ {
197
+ "type": "object",
198
+ "additionalProperties": false,
199
+ "required": [
200
+ "condition",
201
+ "kinds",
202
+ "direction"
203
+ ],
204
+ "properties": {
205
+ "condition": {
206
+ "const": "missing-relationship"
207
+ },
208
+ "kinds": {
209
+ "type": "array",
210
+ "minItems": 1,
211
+ "uniqueItems": true,
212
+ "items": {
213
+ "$ref": "#/$defs/qualifiedKind"
214
+ }
215
+ },
216
+ "direction": {
217
+ "enum": [
218
+ "incoming",
219
+ "outgoing",
220
+ "any"
221
+ ]
222
+ },
223
+ "kindMatching": {
224
+ "enum": [
225
+ "exact",
226
+ "descendants"
227
+ ]
228
+ }
229
+ }
230
+ },
231
+ {
232
+ "type": "object",
233
+ "additionalProperties": false,
234
+ "required": [
235
+ "condition"
236
+ ],
237
+ "properties": {
238
+ "condition": {
239
+ "const": "isolated"
240
+ }
241
+ }
242
+ },
243
+ {
244
+ "type": "object",
245
+ "additionalProperties": false,
246
+ "required": [
247
+ "condition",
248
+ "kinds"
249
+ ],
250
+ "properties": {
251
+ "condition": {
252
+ "const": "no-subject-of-kind"
253
+ },
254
+ "kinds": {
255
+ "type": "array",
256
+ "minItems": 1,
257
+ "uniqueItems": true,
258
+ "items": {
259
+ "$ref": "#/$defs/qualifiedKind"
260
+ }
261
+ },
262
+ "kindMatching": {
263
+ "enum": [
264
+ "exact",
265
+ "descendants"
266
+ ],
267
+ "default": "descendants"
268
+ }
269
+ }
270
+ },
271
+ {
272
+ "type": "object",
273
+ "additionalProperties": false,
274
+ "required": [
275
+ "condition"
276
+ ],
277
+ "properties": {
278
+ "condition": {
279
+ "const": "no-state-defined"
280
+ }
281
+ }
282
+ },
283
+ {
284
+ "type": "object",
285
+ "additionalProperties": false,
286
+ "required": [
287
+ "condition",
288
+ "kinds",
289
+ "direction",
290
+ "counterpartKinds"
291
+ ],
292
+ "properties": {
293
+ "condition": {
294
+ "const": "missing-linkage"
295
+ },
296
+ "kinds": {
297
+ "type": "array",
298
+ "minItems": 1,
299
+ "uniqueItems": true,
300
+ "items": {
301
+ "$ref": "#/$defs/qualifiedKind"
302
+ }
303
+ },
304
+ "direction": {
305
+ "enum": [
306
+ "outgoing",
307
+ "incoming"
308
+ ]
309
+ },
310
+ "counterpartKinds": {
311
+ "type": "array",
312
+ "minItems": 1,
313
+ "uniqueItems": true,
314
+ "items": {
315
+ "$ref": "#/$defs/qualifiedKind"
316
+ }
317
+ },
318
+ "kindMatching": {
319
+ "enum": [
320
+ "exact",
321
+ "descendants"
322
+ ],
323
+ "default": "descendants"
324
+ }
325
+ }
326
+ },
327
+ {
328
+ "type": "object",
329
+ "additionalProperties": false,
330
+ "required": [
331
+ "condition",
332
+ "kinds",
333
+ "direction",
334
+ "counterpartKinds"
335
+ ],
336
+ "properties": {
337
+ "condition": {
338
+ "const": "has-linkage"
339
+ },
340
+ "kinds": {
341
+ "type": "array",
342
+ "minItems": 1,
343
+ "uniqueItems": true,
344
+ "items": {
345
+ "$ref": "#/$defs/qualifiedKind"
346
+ }
347
+ },
348
+ "direction": {
349
+ "enum": [
350
+ "outgoing",
351
+ "incoming",
352
+ "either"
353
+ ]
354
+ },
355
+ "counterpartKinds": {
356
+ "type": "array",
357
+ "minItems": 1,
358
+ "uniqueItems": true,
359
+ "items": {
360
+ "$ref": "#/$defs/qualifiedKind"
361
+ }
362
+ },
363
+ "kindMatching": {
364
+ "enum": [
365
+ "exact",
366
+ "descendants"
367
+ ],
368
+ "default": "descendants"
369
+ }
370
+ }
371
+ },
372
+ {
373
+ "type": "object",
374
+ "additionalProperties": false,
375
+ "required": [
376
+ "condition",
377
+ "kinds",
378
+ "direction",
379
+ "counterpartKinds"
380
+ ],
381
+ "properties": {
382
+ "condition": {
383
+ "const": "exists-linkage"
384
+ },
385
+ "kinds": {
386
+ "type": "array",
387
+ "minItems": 1,
388
+ "uniqueItems": true,
389
+ "items": {
390
+ "$ref": "#/$defs/qualifiedKind"
391
+ }
392
+ },
393
+ "direction": {
394
+ "enum": [
395
+ "outgoing",
396
+ "incoming",
397
+ "either"
398
+ ]
399
+ },
400
+ "counterpartKinds": {
401
+ "type": "array",
402
+ "minItems": 1,
403
+ "uniqueItems": true,
404
+ "items": {
405
+ "$ref": "#/$defs/qualifiedKind"
406
+ }
407
+ },
408
+ "kindMatching": {
409
+ "enum": [
410
+ "exact",
411
+ "descendants"
412
+ ],
413
+ "default": "descendants"
414
+ }
415
+ }
416
+ },
417
+ {
418
+ "type": "object",
419
+ "additionalProperties": false,
420
+ "required": [
421
+ "condition",
422
+ "kinds"
423
+ ],
424
+ "properties": {
425
+ "condition": {
426
+ "const": "missing-constraint"
427
+ },
428
+ "kinds": {
429
+ "type": "array",
430
+ "minItems": 1,
431
+ "uniqueItems": true,
432
+ "items": {
433
+ "$ref": "#/$defs/qualifiedKind"
434
+ }
435
+ },
436
+ "kindMatching": {
437
+ "enum": [
438
+ "exact",
439
+ "descendants"
440
+ ],
441
+ "default": "descendants"
442
+ }
443
+ }
444
+ },
445
+ {
446
+ "type": "object",
447
+ "additionalProperties": false,
448
+ "description": "The subject is an endpoint of at least one flow relationship that has no yarramate/flow/content claim.",
449
+ "required": [
450
+ "condition"
451
+ ],
452
+ "properties": {
453
+ "condition": {
454
+ "const": "missing-flow-content"
455
+ }
456
+ }
457
+ },
458
+ {
459
+ "type": "object",
460
+ "additionalProperties": false,
461
+ "required": [
462
+ "condition",
463
+ "predicate",
464
+ "direction"
465
+ ],
466
+ "properties": {
467
+ "condition": {
468
+ "const": "missing-reference"
469
+ },
470
+ "predicate": {
471
+ "$ref": "#/$defs/claimPredicate"
472
+ },
473
+ "direction": {
474
+ "enum": [
475
+ "outgoing",
476
+ "incoming"
477
+ ]
478
+ }
479
+ }
480
+ },
481
+ {
482
+ "type": "object",
483
+ "additionalProperties": false,
484
+ "required": [
485
+ "condition",
486
+ "topic"
487
+ ],
488
+ "properties": {
489
+ "condition": {
490
+ "const": "missing-attestation"
491
+ },
492
+ "topic": {
493
+ "type": "string",
494
+ "pattern": "^[a-z][a-z0-9-]*$"
495
+ }
496
+ }
497
+ },
498
+ {
499
+ "type": "object",
500
+ "additionalProperties": false,
501
+ "description": "The subject resembles another subject of the same kind closely enough to be the same thing under two names, and no yarramate/identity/distinct-from claim dismisses the pair. Deterministic and lexical; the algorithm and thresholds are stated in ADR 0077. Questions using it may interpolate {counterparts}.",
502
+ "required": [
503
+ "condition"
504
+ ],
505
+ "properties": {
506
+ "condition": {
507
+ "const": "near-duplicate"
508
+ }
509
+ }
510
+ },
511
+ {
512
+ "type": "object",
513
+ "additionalProperties": false,
514
+ "description": "The subject's kind is never tested by anything the compiler can check: it participates in no relationship whose kind pins the aspect at the subject's end, so reclassifying it to any other kind of any other aspect would still compile. The kind is a label rather than a constraint (ADR 0083).",
515
+ "required": [
516
+ "condition"
517
+ ],
518
+ "properties": {
519
+ "condition": {
520
+ "const": "unconstrained-kind"
521
+ }
522
+ }
523
+ },
524
+ {
525
+ "type": "object",
526
+ "additionalProperties": false,
527
+ "description": "The subject supersedes a predecessor that is still current, and does not say in what respect. A succession that replaced its predecessor outright says so by the predecessor being gone; one where both remain is usually partial, and the respect is the part a reader needs (ADR 0109).",
528
+ "required": [
529
+ "condition"
530
+ ],
531
+ "properties": {
532
+ "condition": {
533
+ "const": "unscoped-succession"
534
+ }
535
+ }
536
+ }
537
+ ]
538
+ },
539
+ "claimPredicate": {
540
+ "type": "string",
541
+ "pattern": "^[a-z][a-z0-9/@.#-]*$"
542
+ },
543
+ "qualifiedKind": {
544
+ "type": "string",
545
+ "pattern": "^[a-z][a-z0-9/-]*@[0-9][0-9A-Za-z.-]*#[A-Za-z][A-Za-z0-9-]*$"
546
+ }
166
547
  }
167
548
  }
@@ -127,6 +127,24 @@
127
127
  "supersedes": {
128
128
  "$ref": "#/$defs/successionReferences"
129
129
  },
130
+ "forbids": {
131
+ "description": "Relationship shapes this subject rules out, checked against the graph. A constraint nothing tests is a comment. Deliberately narrow: forbid a relationship kind between named endpoints, with exceptions, which covers \"everything goes through X\" and needs no traversal.",
132
+ "type": "array",
133
+ "minItems": 1,
134
+ "items": {
135
+ "type": "object",
136
+ "additionalProperties": false,
137
+ "required": ["relationship"],
138
+ "anyOf": [{ "required": ["from"] }, { "required": ["to"] }],
139
+ "properties": {
140
+ "relationship": { "type": "string", "minLength": 1 },
141
+ "from": { "type": "string", "minLength": 1 },
142
+ "to": { "type": "string", "minLength": 1 },
143
+ "exceptFrom": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } },
144
+ "exceptTo": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
145
+ }
146
+ }
147
+ },
130
148
  "constraints": {
131
149
  "type": "array",
132
150
  "items": {
@@ -237,7 +255,18 @@
237
255
  "minItems": 1,
238
256
  "uniqueItems": true,
239
257
  "items": {
240
- "$ref": "#/$defs/reference"
258
+ "oneOf": [
259
+ { "$ref": "#/$defs/reference" },
260
+ {
261
+ "type": "object",
262
+ "additionalProperties": false,
263
+ "required": ["subject", "inRespectOf"],
264
+ "properties": {
265
+ "subject": { "$ref": "#/$defs/reference" },
266
+ "inRespectOf": { "$ref": "#/$defs/nonEmptyText" }
267
+ }
268
+ }
269
+ ]
241
270
  }
242
271
  },
243
272
  "relationship": {
@@ -27,6 +27,44 @@
27
27
  }
28
28
  },
29
29
  "$defs": {
30
+ "searchProbe": {
31
+ "description": "One search a provider performed and found nothing at. Recorded so a reader can re-run it; yarramate does not execute it.",
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "oneOf": [{ "required": ["glob"] }, { "required": ["grep"] }],
35
+ "properties": {
36
+ "glob": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "description": "A path glob that matched nothing, e.g. PRAEFECT_*"
40
+ },
41
+ "grep": {
42
+ "type": "string",
43
+ "minLength": 1,
44
+ "description": "A pattern that matched nothing."
45
+ },
46
+ "paths": {
47
+ "type": "array",
48
+ "minItems": 1,
49
+ "items": { "type": "string", "minLength": 1 },
50
+ "description": "Paths the search covered. Absent means the whole tree."
51
+ }
52
+ }
53
+ },
54
+ "measurement": {
55
+ "description": "A figure quoted in an evidence message, with how it was produced. Recorded so a reader can re-derive it rather than trust it; yarramate does not execute the method.",
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": ["value", "method"],
59
+ "properties": {
60
+ "value": { "type": "string", "minLength": 1 },
61
+ "method": {
62
+ "type": "string",
63
+ "minLength": 1,
64
+ "description": "How the value was obtained, precisely enough to reproduce."
65
+ }
66
+ }
67
+ },
30
68
  "id": {
31
69
  "type": "string",
32
70
  "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
@@ -77,6 +115,18 @@
77
115
  "not-observed"
78
116
  ]
79
117
  },
118
+ "searched": {
119
+ "type": "array",
120
+ "minItems": 1,
121
+ "items": { "$ref": "#/$defs/searchProbe" },
122
+ "description": "What was searched and found empty. Meaningful on a not-observed result, which is the one result that asserts a negative about a tree nobody read exhaustively."
123
+ },
124
+ "measured": {
125
+ "type": "array",
126
+ "minItems": 1,
127
+ "items": { "$ref": "#/$defs/measurement" },
128
+ "description": "Figures quoted in the message, with how each was produced."
129
+ },
80
130
  "key": {
81
131
  "$ref": "#/$defs/observationKey"
82
132
  },