yarramate 1.14.1 → 1.15.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.
- package/dist/compiler.js +14 -1
- package/dist/design-command.js +48 -0
- package/dist/interrogate-command.d.ts +29 -0
- package/dist/interrogate-command.js +7 -0
- package/dist/subject-references.d.ts +7 -1
- package/dist/subject-references.js +24 -3
- package/dist/visual-app/assets/index-Bhq3K16z.js +394 -0
- package/dist/visual-app/index.html +1 -1
- package/dist/visual-app-lib/editor.js +26923 -26880
- package/dist/visual-app-lib/types/interrogate-command.d.ts +29 -0
- package/dist/visual-app-lib/types/subject-references.d.ts +7 -1
- package/dist/visual-app-lib/types/visual-app/nesting-span.d.ts +13 -0
- package/docs/CONSUMING-YARRAMATE.md +7 -7
- package/docs/INTERROGATION.md +138 -4
- package/docs/MODEL-FLOOR.md +43 -10
- package/package.json +1 -1
- package/schema/yarramate-design-step.schema.json +45 -0
- package/schema/yarramate-interrogation-report.schema.json +45 -0
- package/schema/yarramate-question-catalogue.schema.json +45 -0
- package/dist/visual-app/assets/index-YnxgCmo5.js +0 -394
package/dist/compiler.js
CHANGED
|
@@ -362,9 +362,22 @@ function compileWorkspaceResolved(parsed) {
|
|
|
362
362
|
}
|
|
363
363
|
const alreadyDeclaresPolicy = pendingProfiles.some(({ identity }) => identity === shippedPolicyIdentity);
|
|
364
364
|
if (!alreadyDeclaresPolicy) {
|
|
365
|
+
// This probe runs BEFORE the document gate that rejects a source whose
|
|
366
|
+
// schema check failed, so it has to hold its own precondition: a source
|
|
367
|
+
// that composes to anything but a mapping - an empty file, a comment-only
|
|
368
|
+
// one, a bare scalar - selects no profile at all. It used to read
|
|
369
|
+
// `.profile` through an `as` cast, which is what hid the null from the
|
|
370
|
+
// typechecker, and an empty document crashed the whole compile with a
|
|
371
|
+
// `TypeError` instead of the `YM201 must be object` its schema already
|
|
372
|
+
// produces. Every other consumer of a parsed entry checks its diagnostics
|
|
373
|
+
// first (the profile walk above, the pattern walk below); this one could
|
|
374
|
+
// not, because it runs before that gate exists, so it narrows instead.
|
|
365
375
|
const selected = documentInputs.some(({ entry }) => {
|
|
366
376
|
const value = entry.value;
|
|
367
|
-
return value
|
|
377
|
+
return (typeof value === 'object' &&
|
|
378
|
+
value !== null &&
|
|
379
|
+
value.profile ===
|
|
380
|
+
shippedPolicyIdentity);
|
|
368
381
|
});
|
|
369
382
|
const extended = pendingProfiles.some(({ value }) => value.extends === shippedPolicyIdentity);
|
|
370
383
|
if (selected || extended) {
|
package/dist/design-command.js
CHANGED
|
@@ -81,6 +81,43 @@ const localKind = (qualified) => {
|
|
|
81
81
|
const hash = qualified.lastIndexOf('#');
|
|
82
82
|
return hash === -1 ? qualified : qualified.slice(hash + 1);
|
|
83
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* The `missing-claim` predicates that name a CONCEPT FIELD, and the field
|
|
86
|
+
* each one is written through (#430).
|
|
87
|
+
*
|
|
88
|
+
* `missing-claim` matches a raw predicate, and a predicate is not an
|
|
89
|
+
* authoring gesture: `yarramate/attestation/adequacy` is written by adding an
|
|
90
|
+
* attestation, `yarramate/reference/refers-to` by adding a reference, and a
|
|
91
|
+
* profile may mint predicates this engine has never heard of. So the
|
|
92
|
+
* condition cannot map to one operation in general, which is why an adopter
|
|
93
|
+
* wiring it up got a card with no affordance and filed #430.
|
|
94
|
+
*
|
|
95
|
+
* It maps for these three, and only these three, because each is a named
|
|
96
|
+
* field on a concept that `update-concept` writes directly. They are Core's
|
|
97
|
+
* own, closed, and they are every `missing-claim` the shipped catalogue uses
|
|
98
|
+
* — `owner-missing`, `information-unowned`, `concept-undescribed` and
|
|
99
|
+
* `status-missing`. Anything else returns no skeleton, which is ADR 0110's
|
|
100
|
+
* rule holding: a wrong skeleton is never offered.
|
|
101
|
+
*
|
|
102
|
+
* This is the "one new mapping case" ADR 0110 anticipated, not a remedy DSL.
|
|
103
|
+
* The engine still takes no position on which predicates a HOST should render
|
|
104
|
+
* as editable; the trigger carries the predicate and the host decides. This
|
|
105
|
+
* is the CLI rendering its own affordance, the same as the other two cases.
|
|
106
|
+
*/
|
|
107
|
+
const CONCEPT_FIELD_PREDICATES = {
|
|
108
|
+
'yarramate/ownership/owner': {
|
|
109
|
+
name: 'owner',
|
|
110
|
+
placeholder: '<owning-subject-id>',
|
|
111
|
+
},
|
|
112
|
+
'yarramate/concept/description': {
|
|
113
|
+
name: 'description',
|
|
114
|
+
placeholder: '<one line>',
|
|
115
|
+
},
|
|
116
|
+
'yarramate/lifecycle/status': {
|
|
117
|
+
name: 'status',
|
|
118
|
+
placeholder: '<planned|current|retired>',
|
|
119
|
+
},
|
|
120
|
+
};
|
|
84
121
|
const skeletonHeader = (documentAddress, op) => [
|
|
85
122
|
'',
|
|
86
123
|
'Prefilled skeleton (edit the <placeholders>, save as operations.yaml):',
|
|
@@ -125,6 +162,17 @@ const renderSkeleton = (step, documentAddress) => {
|
|
|
125
162
|
` to: ${to}`,
|
|
126
163
|
];
|
|
127
164
|
}
|
|
165
|
+
if (condition.condition === 'missing-claim' && step.subject !== undefined) {
|
|
166
|
+
const field = CONCEPT_FIELD_PREDICATES[condition.predicate];
|
|
167
|
+
if (field === undefined)
|
|
168
|
+
return [];
|
|
169
|
+
return [
|
|
170
|
+
...skeletonHeader(documentAddress, 'update-concept'),
|
|
171
|
+
' concept:',
|
|
172
|
+
` id: ${step.subject.id.split('#').pop()}`,
|
|
173
|
+
` ${field.name}: ${field.placeholder}`,
|
|
174
|
+
];
|
|
175
|
+
}
|
|
128
176
|
return [];
|
|
129
177
|
};
|
|
130
178
|
export function runDesignCommand(options, cwd) {
|
|
@@ -110,6 +110,35 @@ export type CatalogueCondition = {
|
|
|
110
110
|
readonly direction: 'incoming' | 'outgoing' | 'either';
|
|
111
111
|
readonly counterpartKinds: readonly string[];
|
|
112
112
|
readonly kindMatching?: 'exact' | 'descendants';
|
|
113
|
+
} | {
|
|
114
|
+
/**
|
|
115
|
+
* The negative twin of `exists-linkage`, workspace-scope like it
|
|
116
|
+
* (#436, ADR 0138). Fires while NO concept in the workspace satisfies
|
|
117
|
+
* the linkage, so a question can ask "nothing anywhere links this way".
|
|
118
|
+
*
|
|
119
|
+
* It exists because a **vocabulary question** was asking the wrong
|
|
120
|
+
* thing. `below-subject-count` measures a population, and a vocabulary
|
|
121
|
+
* question means "did anyone survey this" — a proxy that fails in both
|
|
122
|
+
* directions, measured on a live engagement: two throwaway values close
|
|
123
|
+
* it dishonestly, while a truthful single-value estate can never close
|
|
124
|
+
* it at all.
|
|
125
|
+
*
|
|
126
|
+
* `MODEL-FLOOR.md` prescribes the answer's home: a classification axis
|
|
127
|
+
* is a `grouping` that aggregates its members, so a scheme aggregating
|
|
128
|
+
* its classes IS the statement that these are the classes. Asking "no
|
|
129
|
+
* scheme aggregates any class" needs this condition; asking how many
|
|
130
|
+
* classes exist does not reach it.
|
|
131
|
+
*
|
|
132
|
+
* Deliberately NOT `!exists-linkage` in the evaluator, for the reason
|
|
133
|
+
* `has-subject-of-kind` is not `!no-subject-of-kind`: written as its own
|
|
134
|
+
* check, an empty workspace falls out right rather than by double
|
|
135
|
+
* negative.
|
|
136
|
+
*/
|
|
137
|
+
readonly condition: 'no-linkage-exists';
|
|
138
|
+
readonly kinds: readonly string[];
|
|
139
|
+
readonly direction: 'incoming' | 'outgoing' | 'either';
|
|
140
|
+
readonly counterpartKinds: readonly string[];
|
|
141
|
+
readonly kindMatching?: 'exact' | 'descendants';
|
|
113
142
|
} | {
|
|
114
143
|
readonly condition: 'missing-constraint';
|
|
115
144
|
readonly kinds: readonly string[];
|
|
@@ -207,6 +207,7 @@ const namedKinds = (question) => {
|
|
|
207
207
|
case 'missing-linkage':
|
|
208
208
|
case 'has-linkage':
|
|
209
209
|
case 'exists-linkage':
|
|
210
|
+
case 'no-linkage-exists':
|
|
210
211
|
kinds.push(...condition.kinds, ...condition.counterpartKinds);
|
|
211
212
|
break;
|
|
212
213
|
default:
|
|
@@ -267,6 +268,7 @@ const CONDITION_SCOPE = {
|
|
|
267
268
|
'below-subject-count': 'workspace',
|
|
268
269
|
'no-state-defined': 'workspace',
|
|
269
270
|
'exists-linkage': 'workspace',
|
|
271
|
+
'no-linkage-exists': 'workspace',
|
|
270
272
|
'missing-claim': 'subject',
|
|
271
273
|
'missing-relationship': 'subject',
|
|
272
274
|
isolated: 'subject',
|
|
@@ -413,6 +415,11 @@ const conditionHolds = (index, condition, subjectId, profileContext, evidence, m
|
|
|
413
415
|
linkageHits(index, condition, subjectId, profileContext));
|
|
414
416
|
case 'exists-linkage':
|
|
415
417
|
return [...index.concepts].some((id) => linkageHits(index, condition, id, profileContext));
|
|
418
|
+
case 'no-linkage-exists':
|
|
419
|
+
// Its own check rather than `!exists-linkage`, so an empty workspace
|
|
420
|
+
// falls out right: no concepts means no linkage, which is exactly the
|
|
421
|
+
// model a vocabulary question is loudest about.
|
|
422
|
+
return ![...index.concepts].some((id) => linkageHits(index, condition, id, profileContext));
|
|
416
423
|
case 'missing-constraint': {
|
|
417
424
|
const matching = condition.kindMatching ?? 'descendants';
|
|
418
425
|
return !(index.claimsBySubject.get(subjectId) ?? []).some((claim) => claim.predicate === 'yarramate/constraint/requires' &&
|
|
@@ -18,7 +18,13 @@ export type SubjectReferenceGroup = 'document' | 'projection' | 'evidence' | 'ad
|
|
|
18
18
|
export type SubjectReferenceForm = 'declaration' | 'reference' | 'qualified';
|
|
19
19
|
export interface SubjectReferencePosition {
|
|
20
20
|
readonly group: SubjectReferenceGroup;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Key path from the document root; `*` matches every element of a
|
|
23
|
+
* collection, which is every index of a sequence and every value of a
|
|
24
|
+
* mapping whose keys are open. Both spell `*` because the schema, not the
|
|
25
|
+
* position, is what decides which one a path lands on: `items` and
|
|
26
|
+
* `patternProperties` are the same shape of "and now, each of these".
|
|
27
|
+
*/
|
|
22
28
|
readonly path: readonly string[];
|
|
23
29
|
readonly form: SubjectReferenceForm;
|
|
24
30
|
}
|
|
@@ -25,6 +25,15 @@ export const SUBJECT_REFERENCE_POSITIONS = [
|
|
|
25
25
|
path: ['concepts', '*', 'constraints', '*', 'ref'],
|
|
26
26
|
form: 'reference',
|
|
27
27
|
},
|
|
28
|
+
// The subjects bound into a pattern instance's slots (ADR 0123). Keyed by
|
|
29
|
+
// slot name rather than indexed, which is why it went missing: this is the
|
|
30
|
+
// only address in any of the four schemas that lives in a mapping, and the
|
|
31
|
+
// completeness walker below derived only sequences.
|
|
32
|
+
{
|
|
33
|
+
group: 'document',
|
|
34
|
+
path: ['concepts', '*', 'parts', '*'],
|
|
35
|
+
form: 'reference',
|
|
36
|
+
},
|
|
28
37
|
{
|
|
29
38
|
group: 'document',
|
|
30
39
|
path: ['concepts', '*', 'references', '*', 'ref'],
|
|
@@ -150,10 +159,22 @@ const collect = (node, path, pointer, form, documentId, source, hits, aliases) =
|
|
|
150
159
|
}
|
|
151
160
|
const [segment, ...rest] = path;
|
|
152
161
|
if (segment === '*') {
|
|
153
|
-
if (
|
|
162
|
+
if (isSeq(node)) {
|
|
163
|
+
for (const [index, item] of node.items.entries()) {
|
|
164
|
+
collect(item, rest, `${pointer}/${index}`, form, documentId, source, hits, aliases);
|
|
165
|
+
}
|
|
154
166
|
return;
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
}
|
|
168
|
+
// A mapping with open keys, which today is `parts` alone. The pointer
|
|
169
|
+
// segment is the authored key, so a diagnostic names the SLOT ("/parts/
|
|
170
|
+
// interface") rather than a position the reader would have to count out.
|
|
171
|
+
if (isMap(node)) {
|
|
172
|
+
for (const item of node.items) {
|
|
173
|
+
const key = isScalar(item.key) ? String(item.key.value) : undefined;
|
|
174
|
+
if (key === undefined)
|
|
175
|
+
continue;
|
|
176
|
+
collect(item.value, rest, `${pointer}/${key}`, form, documentId, source, hits, aliases);
|
|
177
|
+
}
|
|
157
178
|
}
|
|
158
179
|
return;
|
|
159
180
|
}
|