yarramate 0.18.0 → 0.20.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 (70) hide show
  1. package/README.md +9 -1
  2. package/catalogues/core-enrichment.yaml +52 -1
  3. package/dist/adapters/graphify-cli.js +0 -0
  4. package/dist/adapters/likec4-cli.js +0 -0
  5. package/dist/adapters/likec4-export.d.ts +1 -1
  6. package/dist/adapters/likec4-export.js +32 -0
  7. package/dist/adapters/mcp-cli.js +0 -0
  8. package/dist/adapters/visual/client.js +0 -1
  9. package/dist/adapters/visual/protocol-contract.d.ts +118 -35
  10. package/dist/adapters/visual/protocol-contract.js +1 -11
  11. package/dist/adapters/visual/protocol.d.ts +8 -1
  12. package/dist/adapters/visual/protocol.js +86 -46
  13. package/dist/adapters/visual/request.d.ts +37 -0
  14. package/dist/adapters/visual/request.js +131 -0
  15. package/dist/adapters/visual/session-server.d.ts +12 -14
  16. package/dist/adapters/visual/session-server.js +641 -283
  17. package/dist/adapters/visual/session-store.d.ts +31 -29
  18. package/dist/adapters/visual/session-store.js +94 -94
  19. package/dist/adapters/visual/wire.d.ts +36 -10
  20. package/dist/adapters/visual-cli.d.ts +3 -0
  21. package/dist/adapters/visual-cli.js +66 -1
  22. package/dist/apply-command.d.ts +10 -0
  23. package/dist/apply-command.js +399 -247
  24. package/dist/cli.js +0 -0
  25. package/dist/compiler.d.ts +66 -0
  26. package/dist/compiler.js +209 -89
  27. package/dist/graph-projection.d.ts +62 -0
  28. package/dist/graph-projection.js +222 -0
  29. package/dist/index.d.ts +2 -2
  30. package/dist/index.js +1 -1
  31. package/dist/interrogate-command.d.ts +2 -0
  32. package/dist/interrogate-command.js +22 -0
  33. package/dist/kind-label.d.ts +8 -0
  34. package/dist/kind-label.js +8 -0
  35. package/dist/operations.d.ts +139 -0
  36. package/dist/operations.js +11 -0
  37. package/dist/projection.d.ts +2 -0
  38. package/dist/projection.js +8 -2
  39. package/dist/reconciliation.js +8 -7
  40. package/dist/source-document.d.ts +10 -0
  41. package/dist/source-document.js +21 -1
  42. package/dist/visual-app/assets/index-6oSR9RGM.js +354 -0
  43. package/dist/visual-app/assets/index-dwA9AOr5.css +1 -0
  44. package/dist/visual-app/index.html +2 -2
  45. package/docs/CONSUMING-YARRAMATE.md +25 -19
  46. package/package.json +9 -4
  47. package/schema/yarramate-adapter-mapping.schema.json +1 -1
  48. package/schema/yarramate-apply-result.schema.json +8 -2
  49. package/schema/yarramate-document.schema.json +1 -1
  50. package/schema/yarramate-likec4-diagnostic-result.schema.json +1 -1
  51. package/schema/yarramate-operations.schema.json +203 -1
  52. package/schema/yarramate-projection.schema.json +12 -3
  53. package/schema/yarramate-question-catalogue.schema.json +15 -2
  54. package/schema/yarramate-visual-event.schema.json +143 -0
  55. package/schema/yarramate-visual-graph.schema.json +272 -0
  56. package/schema/yarramate-visual-layout.schema.json +41 -0
  57. package/schema/yarramate-visual-model.schema.json +6 -44
  58. package/schema/yarramate-visual-response.schema.json +170 -19
  59. package/schema/yarramate-visual-session-descriptor.schema.json +1 -1
  60. package/schema/yarramate-visual-session-request.schema.json +0 -70
  61. package/schema/yarramate-visual-session-started.schema.json +2 -9
  62. package/schema/yarramate-visual-status.schema.json +3 -2
  63. package/skills/yarramate-architecture/SKILL.md +20 -13
  64. package/skills/yarramate-architecture/references/journey-checklists.md +4 -2
  65. package/skills/yarramate-architecture/references/native-authoring.md +30 -0
  66. package/skills/yarramate-architecture/references/visual-conversations.md +149 -150
  67. package/dist/adapters/visual/likec4-compiler.d.ts +0 -55
  68. package/dist/adapters/visual/likec4-compiler.js +0 -460
  69. package/dist/visual-app/assets/index-CGvC8e3R.css +0 -1
  70. package/dist/visual-app/assets/index-CXuW1tOj.js +0 -209
@@ -0,0 +1,222 @@
1
+ import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, parseConstraintExpectsValue, } from './compiler.js';
2
+ import { kindLabelOf } from './kind-label.js';
3
+ const CONCEPT_KIND_PREDICATE = 'yarramate/concept/kind';
4
+ const CONCEPT_NAME_PREDICATE = 'yarramate/concept/name';
5
+ const CONCEPT_DESCRIPTION_PREDICATE = 'yarramate/concept/description';
6
+ const CONCEPT_ALIAS_PREDICATE = 'yarramate/concept/alias';
7
+ const LIFECYCLE_STATUS_PREDICATE = 'yarramate/lifecycle/status';
8
+ const IDENTITY_DISTINCT_FROM_PREDICATE = 'yarramate/identity/distinct-from';
9
+ const LINEAGE_SUPERSEDES_PREDICATE = 'yarramate/lineage/supersedes';
10
+ const OWNERSHIP_OWNER_PREDICATE = 'yarramate/ownership/owner';
11
+ const CONSTRAINT_REQUIRES_PREDICATE = 'yarramate/constraint/requires';
12
+ const REFERENCE_REFERS_TO_PREDICATE = 'yarramate/reference/refers-to';
13
+ const STATE_PRESENT_IN_PREDICATE = 'yarramate/state/present-in';
14
+ const RELATIONSHIP_NAME_PREDICATE = 'yarramate/relationship/name';
15
+ const RELATIONSHIP_DESCRIPTION_PREDICATE = 'yarramate/relationship/description';
16
+ const ACCESS_MODE_PREDICATE = 'yarramate/access/mode';
17
+ const FLOW_CONTENT_PREDICATE = 'yarramate/flow/content';
18
+ // This is compiler-internal data: every claim's object narrows deterministically
19
+ // by predicate. A mismatch means a real bug upstream in src/compiler.ts, not
20
+ // malformed input this function should tolerate — so it throws rather than
21
+ // silently coercing to an empty string.
22
+ const claimValue = (claim) => {
23
+ if ('value' in claim.object) {
24
+ return claim.object.value;
25
+ }
26
+ throw new Error(`Expected a value-typed object for claim "${claim.id}" (subject "${claim.subject}", predicate "${claim.predicate}"), got a ref-typed object`);
27
+ };
28
+ const claimRef = (claim) => {
29
+ if ('ref' in claim.object) {
30
+ return claim.object.ref;
31
+ }
32
+ throw new Error(`Expected a ref-typed object for claim "${claim.id}" (subject "${claim.subject}", predicate "${claim.predicate}"), got a value-typed object`);
33
+ };
34
+ // The compiler qualifies every subject id as `<document id>#<authored id>`
35
+ // (`qualifyReference` in src/compiler.ts), and a document's authored ids never
36
+ // contain `#`. An operation addresses a subject by the id someone wrote in the
37
+ // file, not by the qualified identity the compile derived, so the projection
38
+ // carries both rather than leaving the browser to re-derive one from the other.
39
+ const authoredIdOf = (subjectId) => {
40
+ const boundary = subjectId.indexOf('#');
41
+ return boundary === -1 ? subjectId : subjectId.slice(boundary + 1);
42
+ };
43
+ const compareById = (left, right) => left.id.localeCompare(right.id);
44
+ const groupClaimsBySubject = (claims) => {
45
+ const groups = new Map();
46
+ for (const claim of claims) {
47
+ const group = groups.get(claim.subject);
48
+ if (group === undefined) {
49
+ groups.set(claim.subject, [claim]);
50
+ }
51
+ else {
52
+ group.push(claim);
53
+ }
54
+ }
55
+ return groups;
56
+ };
57
+ const projectConcept = (subjectId, claims, profileContext) => {
58
+ const kindClaim = claims.find((claim) => claim.predicate === CONCEPT_KIND_PREDICATE);
59
+ if (kindClaim === undefined) {
60
+ throw new Error(`Concept "${subjectId}" is missing its required yarramate/concept/kind claim`);
61
+ }
62
+ const kind = claimValue(kindClaim);
63
+ const nameClaim = claims.find((claim) => claim.predicate === CONCEPT_NAME_PREDICATE);
64
+ if (nameClaim === undefined) {
65
+ throw new Error(`Concept "${subjectId}" is missing its required yarramate/concept/name claim`);
66
+ }
67
+ const descriptionClaim = claims.find((claim) => claim.predicate === CONCEPT_DESCRIPTION_PREDICATE);
68
+ const statusClaim = claims.find((claim) => claim.predicate === LIFECYCLE_STATUS_PREDICATE);
69
+ const ownerClaim = claims.find((claim) => claim.predicate === OWNERSHIP_OWNER_PREDICATE);
70
+ const constraints = claims
71
+ .filter((claim) => claim.predicate === CONSTRAINT_REQUIRES_PREDICATE)
72
+ .map((requiresClaim) => {
73
+ // The constraint's local id lives between the `~constraint-` marker
74
+ // and the end of the requires claim's own id; the paired expects
75
+ // claim (if the constraint declared one) reuses that same local id
76
+ // under a `~expects-` marker in the same claim group.
77
+ const localId = requiresClaim.id.split('~constraint-')[1];
78
+ if (localId === undefined) {
79
+ throw new Error(`Constraint claim "${requiresClaim.id}" on concept "${subjectId}" does not follow the "<subject>~constraint-<id>" id convention`);
80
+ }
81
+ const expectsClaim = claims.find((claim) => claim.id === `${subjectId}~expects-${localId}`);
82
+ let expects;
83
+ if (expectsClaim === undefined) {
84
+ expects = null;
85
+ }
86
+ else {
87
+ const parsed = parseConstraintExpectsValue(claimValue(expectsClaim));
88
+ if (parsed === undefined) {
89
+ throw new Error(`Constraint expects claim "${expectsClaim.id}" on concept "${subjectId}" does not follow the "provider key value" encoding`);
90
+ }
91
+ expects = parsed;
92
+ }
93
+ return {
94
+ id: localId,
95
+ ref: claimRef(requiresClaim),
96
+ expects,
97
+ };
98
+ });
99
+ const attestations = claims
100
+ .filter((claim) => claim.predicate.startsWith(ATTESTATION_PREDICATE_PREFIX))
101
+ .map((claim) => {
102
+ const parsed = parseAttestationClaimValue(claimValue(claim));
103
+ if (parsed === undefined) {
104
+ throw new Error(`Attestation claim "${claim.id}" on concept "${subjectId}" does not follow the "by on [recordedBy]" encoding`);
105
+ }
106
+ return {
107
+ topic: claim.predicate.slice(ATTESTATION_PREDICATE_PREFIX.length),
108
+ by: parsed.by,
109
+ on: parsed.on,
110
+ recordedBy: parsed.recordedBy ?? null,
111
+ };
112
+ });
113
+ return {
114
+ id: subjectId,
115
+ localId: authoredIdOf(subjectId),
116
+ document: kindClaim.source.path,
117
+ kind,
118
+ kindLabel: kindLabelOf(kind),
119
+ layer: profileContext.conceptKindLayers.get(kind) ?? null,
120
+ aspect: profileContext.conceptKindAspects.get(kind) ?? null,
121
+ name: claimValue(nameClaim),
122
+ description: descriptionClaim === undefined ? null : claimValue(descriptionClaim),
123
+ aka: claims
124
+ .filter((claim) => claim.predicate === CONCEPT_ALIAS_PREDICATE)
125
+ .map(claimValue)
126
+ .sort(),
127
+ status: statusClaim === undefined ? null : claimValue(statusClaim),
128
+ owner: ownerClaim === undefined ? null : claimRef(ownerClaim),
129
+ distinctFrom: claims
130
+ .filter((claim) => claim.predicate === IDENTITY_DISTINCT_FROM_PREDICATE)
131
+ .map(claimRef)
132
+ .sort(),
133
+ supersedes: claims
134
+ .filter((claim) => claim.predicate === LINEAGE_SUPERSEDES_PREDICATE)
135
+ .map(claimRef)
136
+ .sort(),
137
+ constraints,
138
+ references: claims
139
+ .filter((claim) => claim.predicate === REFERENCE_REFERS_TO_PREDICATE)
140
+ .map((referencesClaim) => {
141
+ const localId = referencesClaim.id.split('~reference-')[1];
142
+ if (localId === undefined) {
143
+ throw new Error(`Reference claim "${referencesClaim.id}" on concept "${subjectId}" does not follow the "<subject>~reference-<id>" id convention`);
144
+ }
145
+ return {
146
+ id: localId,
147
+ ref: claimRef(referencesClaim),
148
+ };
149
+ })
150
+ .sort((left, right) => left.ref.localeCompare(right.ref)),
151
+ presentIn: claims
152
+ .filter((claim) => claim.predicate === STATE_PRESENT_IN_PREDICATE)
153
+ .map(claimRef)
154
+ .sort(),
155
+ attestations,
156
+ };
157
+ };
158
+ const projectRelationship = (relationshipId, allClaims, ownClaims, profileContext) => {
159
+ // A relationship's defining claim is asymmetric: its id is the
160
+ // relationship's subject id, but its own `subject` field is the
161
+ // from-concept, so it lives in the from-concept's claim group, not this
162
+ // relationship's — it has to be found by scanning every claim.
163
+ const definingClaim = allClaims.find((claim) => claim.id === relationshipId);
164
+ if (definingClaim === undefined) {
165
+ throw new Error(`Relationship "${relationshipId}" is missing its defining claim`);
166
+ }
167
+ const kind = definingClaim.predicate;
168
+ const nameClaim = ownClaims.find((claim) => claim.predicate === RELATIONSHIP_NAME_PREDICATE);
169
+ const descriptionClaim = ownClaims.find((claim) => claim.predicate === RELATIONSHIP_DESCRIPTION_PREDICATE);
170
+ const modeClaim = ownClaims.find((claim) => claim.predicate === ACCESS_MODE_PREDICATE);
171
+ const contentClaim = ownClaims.find((claim) => claim.predicate === FLOW_CONTENT_PREDICATE);
172
+ const statusClaim = ownClaims.find((claim) => claim.predicate === LIFECYCLE_STATUS_PREDICATE);
173
+ return {
174
+ id: relationshipId,
175
+ localId: authoredIdOf(relationshipId),
176
+ document: definingClaim.source.path,
177
+ kind,
178
+ kindLabel: kindLabelOf(kind),
179
+ coreKindLabel: kindLabelOf(profileContext.relationshipKindLineages.get(kind)?.[0] ?? kind),
180
+ from: definingClaim.subject,
181
+ to: claimRef(definingClaim),
182
+ name: nameClaim === undefined ? null : claimValue(nameClaim),
183
+ description: descriptionClaim === undefined ? null : claimValue(descriptionClaim),
184
+ mode: modeClaim === undefined ? null : claimValue(modeClaim),
185
+ content: contentClaim === undefined ? null : claimValue(contentClaim),
186
+ status: statusClaim === undefined ? null : claimValue(statusClaim),
187
+ references: ownClaims
188
+ .filter((claim) => claim.predicate === REFERENCE_REFERS_TO_PREDICATE)
189
+ .map((referencesClaim) => {
190
+ const localId = referencesClaim.id.split('~reference-')[1];
191
+ if (localId === undefined) {
192
+ throw new Error(`Reference claim "${referencesClaim.id}" on relationship "${relationshipId}" does not follow the "<subject>~reference-<id>" id convention`);
193
+ }
194
+ return {
195
+ id: localId,
196
+ ref: claimRef(referencesClaim),
197
+ };
198
+ })
199
+ .sort((left, right) => left.ref.localeCompare(right.ref)),
200
+ presentIn: ownClaims
201
+ .filter((claim) => claim.predicate === STATE_PRESENT_IN_PREDICATE)
202
+ .map(claimRef)
203
+ .sort(),
204
+ };
205
+ };
206
+ export function projectGraphForCanvas(graph, profileContext) {
207
+ const claimsBySubject = groupClaimsBySubject(graph.claims);
208
+ const nodes = [];
209
+ const edges = [];
210
+ for (const subject of graph.subjects) {
211
+ const ownClaims = claimsBySubject.get(subject.id) ?? [];
212
+ if (subject.type === 'concept') {
213
+ nodes.push(projectConcept(subject.id, ownClaims, profileContext));
214
+ }
215
+ else {
216
+ edges.push(projectRelationship(subject.id, graph.claims, ownClaims, profileContext));
217
+ }
218
+ }
219
+ nodes.sort(compareById);
220
+ edges.sort(compareById);
221
+ return { nodes, edges };
222
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { compileWorkspace, compileWorkspaceWithProfileContext, } from './compiler.js';
1
+ export { compileWorkspace, compileWorkspaceIncremental, compileWorkspaceWithProfileContext, } from './compiler.js';
2
2
  export { serializeSemanticGraph } from './graph.js';
3
3
  export { checkCoreContract, loadCoreContract, type CoreContract, type CoreContractCommand, type CoreContractFormat, type CoreContractLoadResult, type CoreContractSurface, } from './core-contract.js';
4
4
  export { compareArchitectureStates, type StateComparison, type StateComparisonIssue, type StateComparisonResult, } from './architecture-state.js';
@@ -7,7 +7,7 @@ export { evaluateEvidence, evaluateEvidenceWorkspace, loadEvidence, type Evidenc
7
7
  export { constraintExpectsPredicate, reconcileEvidenceReports, type AssertedRelationship, type AttestationStaleness, type DeclaredSource, type EvidenceFinding, type ExpectationComparison, type ReconciliationFinding, type ReconciliationReport, type StaleAttestationFinding, type UnobservedExpectation, } from './reconciliation.js';
8
8
  export { deriveAttestationStaleness } from './attestation-staleness.js';
9
9
  export { buildRtm, renderRtmMarkdown, type RequirementsTraceabilityMatrix, type RtmAttestation, type RtmContextEntry, type RtmDescopedEntry, type RtmEvidenceVerdict, type RtmLineageEntry, type RtmRealizer, type RtmRow, type RtmSource, } from './rtm.js';
10
- export type { CompilationResult, ContextualCompilationResult, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
10
+ export type { CompilationCache, CompilationResult, ContextualCompilationResult, IncrementalCompilationResult, ParsedWorkspaceSource, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
11
11
  export { canonicalProjection, evaluateProjection, loadProjection, renderProjectionMarkdown, } from './projection.js';
12
12
  export { loadAdapterMapping, validateAdapterMapping, validateAdapterMappings, type AdapterMapping, type AdapterMappingLoadResult, type AdapterMappingValidationResult, type AdapterMappingsValidationResult, type AdapterSubjectMapping, } from './adapter-mapping.js';
13
13
  export type { LifecycleStatus, ProjectionDefinition, ProjectionLoadResult, ProjectionResult, } from './projection.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { compileWorkspace, compileWorkspaceWithProfileContext, } from './compiler.js';
1
+ export { compileWorkspace, compileWorkspaceIncremental, compileWorkspaceWithProfileContext, } from './compiler.js';
2
2
  export { serializeSemanticGraph } from './graph.js';
3
3
  export { checkCoreContract, loadCoreContract, } from './core-contract.js';
4
4
  export { compareArchitectureStates, } from './architecture-state.js';
@@ -35,6 +35,8 @@ type CatalogueCondition = {
35
35
  readonly topic: string;
36
36
  } | {
37
37
  readonly condition: 'near-duplicate';
38
+ } | {
39
+ readonly condition: 'unconstrained-kind';
38
40
  };
39
41
  export interface CatalogueQuestion {
40
42
  readonly id: string;
@@ -224,6 +224,28 @@ const conditionHolds = (index, condition, subjectId, profileContext) => {
224
224
  // index, so this reads exactly like every other condition: the claim's
225
225
  // existence is the whole signal (ADR 0056).
226
226
  return (index.nearDuplicates().get(subjectId) ?? []).length > 0;
227
+ case 'unconstrained-kind': {
228
+ // A compiled graph already satisfies every aspect rule it is subject to
229
+ // (YM404), so participation in a kind that pins the aspect at this
230
+ // subject's end is the whole test: reclassify the subject to another
231
+ // aspect and the workspace stops compiling. Where no such claim exists
232
+ // the kind is a label the engine can never contradict (ADR 0083).
233
+ // Without a profile context the rules are unknown, not absent, so the
234
+ // condition reports nothing rather than inventing a finding.
235
+ if (profileContext === undefined)
236
+ return false;
237
+ return !index.relationshipClaims.some((claim) => {
238
+ const endpoints = profileContext.relationshipKindEndpointAspects.get(claim.predicate);
239
+ if (endpoints === undefined)
240
+ return false;
241
+ if (endpoints.source !== undefined && claim.subject === subjectId) {
242
+ return true;
243
+ }
244
+ return (endpoints.target !== undefined &&
245
+ 'ref' in claim.object &&
246
+ claim.object.ref === subjectId);
247
+ });
248
+ }
227
249
  }
228
250
  };
229
251
  // Shared with design: question templates (standard and askPlain alike)
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A kind identity is `<profile>#<local id>`, and its label is that local id.
3
+ *
4
+ * This lives apart from `graph-projection.ts` because the browser needs it:
5
+ * the projection imports the compiler, and the compiler reaches for Ajv
6
+ * through `node:module` at load, which a bundle cannot honour.
7
+ */
8
+ export declare const kindLabelOf: (kind: string) => string;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A kind identity is `<profile>#<local id>`, and its label is that local id.
3
+ *
4
+ * This lives apart from `graph-projection.ts` because the browser needs it:
5
+ * the projection imports the compiler, and the compiler reaches for Ajv
6
+ * through `node:module` at load, which a bundle cannot honour.
7
+ */
8
+ export const kindLabelOf = (kind) => kind.slice(kind.lastIndexOf('#') + 1);
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The `yarramate/operations/v1` document shape, and the result of landing one.
3
+ *
4
+ * These declarations are the typed twin of `schema/yarramate-operations.schema.json`
5
+ * and `schema/yarramate-apply-result.schema.json`. They live apart from
6
+ * `./apply-command.js` - which owns the machinery that validates, splices, and
7
+ * writes a batch - because the visual protocol carries the same documents to a
8
+ * browser that must not pull Ajv, `yaml`, or `node:fs` into its bundle or its
9
+ * typecheck. Types only: this module has no runtime dependency of any kind.
10
+ */
11
+ export interface IdentifiedReference {
12
+ readonly id: string;
13
+ readonly ref: string;
14
+ }
15
+ export interface ConstraintReference extends IdentifiedReference {
16
+ readonly expects?: {
17
+ readonly provider: string;
18
+ readonly key: string;
19
+ readonly value: string;
20
+ };
21
+ }
22
+ export interface ConceptFields {
23
+ readonly id: string;
24
+ readonly kind?: string;
25
+ readonly name?: string;
26
+ readonly description?: string;
27
+ readonly aka?: readonly string[];
28
+ readonly status?: string;
29
+ readonly owner?: string;
30
+ readonly distinctFrom?: readonly string[];
31
+ readonly supersedes?: readonly string[];
32
+ readonly constraints?: readonly ConstraintReference[];
33
+ readonly references?: readonly IdentifiedReference[];
34
+ readonly presentIn?: readonly string[];
35
+ readonly attestations?: ReadonlyArray<{
36
+ readonly topic: string;
37
+ readonly by: string;
38
+ readonly recordedBy: string;
39
+ readonly on: string;
40
+ }>;
41
+ }
42
+ export interface RelationshipFields {
43
+ readonly id: string;
44
+ readonly kind?: string;
45
+ readonly from?: string;
46
+ readonly to?: string;
47
+ readonly name?: string;
48
+ readonly description?: string;
49
+ readonly status?: string;
50
+ readonly mode?: string;
51
+ readonly content?: string;
52
+ readonly references?: readonly IdentifiedReference[];
53
+ readonly presentIn?: readonly string[];
54
+ }
55
+ export interface ObservationTarget {
56
+ readonly subject?: string;
57
+ readonly claim?: string;
58
+ readonly key?: string;
59
+ }
60
+ export interface ObservationFields extends ObservationTarget {
61
+ readonly value?: string;
62
+ readonly result?: 'confirmed' | 'contradicted' | 'unknown' | 'not-observed';
63
+ readonly evidence?: {
64
+ readonly uri: string;
65
+ readonly message?: string;
66
+ };
67
+ }
68
+ export interface ObservationChange extends ObservationTarget {
69
+ readonly value?: string;
70
+ readonly result?: 'confirmed' | 'contradicted' | 'unknown' | 'not-observed';
71
+ readonly evidence?: {
72
+ readonly uri?: string;
73
+ readonly message?: string;
74
+ };
75
+ }
76
+ export type YarramateOperation = {
77
+ readonly op: 'add-concept';
78
+ readonly document: string;
79
+ readonly concept: ConceptFields;
80
+ } | {
81
+ readonly op: 'add-relationship';
82
+ readonly document: string;
83
+ readonly relationship: RelationshipFields;
84
+ } | {
85
+ readonly op: 'update-concept';
86
+ readonly document: string;
87
+ readonly concept: ConceptFields;
88
+ readonly remove?: readonly string[];
89
+ } | {
90
+ readonly op: 'update-relationship';
91
+ readonly document: string;
92
+ readonly relationship: RelationshipFields;
93
+ readonly remove?: readonly string[];
94
+ } | {
95
+ readonly op: 'delete-concept';
96
+ readonly document: string;
97
+ readonly concept: {
98
+ readonly id: string;
99
+ };
100
+ } | {
101
+ readonly op: 'delete-relationship';
102
+ readonly document: string;
103
+ readonly relationship: {
104
+ readonly id: string;
105
+ };
106
+ } | {
107
+ readonly op: 'add-observation';
108
+ readonly document: string;
109
+ readonly observation: ObservationFields;
110
+ } | {
111
+ readonly op: 'update-observation';
112
+ readonly document: string;
113
+ readonly observation: ObservationChange;
114
+ readonly remove?: readonly string[];
115
+ } | {
116
+ readonly op: 'delete-observation';
117
+ readonly document: string;
118
+ readonly observation: ObservationTarget;
119
+ };
120
+ export interface OperationsDocument {
121
+ readonly format: 'yarramate/operations/v1';
122
+ readonly operations: readonly YarramateOperation[];
123
+ }
124
+ export interface YarramateApplyResult {
125
+ readonly format: 'yarramate/apply-result/v1';
126
+ readonly workspace: string;
127
+ readonly applied: {
128
+ readonly addedConcepts: number;
129
+ readonly addedRelationships: number;
130
+ readonly updatedConcepts: number;
131
+ readonly updatedRelationships: number;
132
+ readonly deletedConcepts: number;
133
+ readonly deletedRelationships: number;
134
+ readonly addedObservations: number;
135
+ readonly updatedObservations: number;
136
+ readonly deletedObservations: number;
137
+ };
138
+ readonly documents: readonly string[];
139
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The `yarramate/operations/v1` document shape, and the result of landing one.
3
+ *
4
+ * These declarations are the typed twin of `schema/yarramate-operations.schema.json`
5
+ * and `schema/yarramate-apply-result.schema.json`. They live apart from
6
+ * `./apply-command.js` - which owns the machinery that validates, splices, and
7
+ * writes a batch - because the visual protocol carries the same documents to a
8
+ * browser that must not pull Ajv, `yaml`, or `node:fs` into its bundle or its
9
+ * typecheck. Types only: this module has no runtime dependency of any kind.
10
+ */
11
+ export {};
@@ -28,8 +28,10 @@ export interface ProjectionDefinition {
28
28
  readonly showLifecycle?: boolean;
29
29
  readonly showEvidence?: boolean;
30
30
  readonly showOwnership?: boolean;
31
+ readonly notation?: 'native' | 'archimate';
31
32
  };
32
33
  }
34
+ export type ProjectionQuery = ProjectionDefinition['query'];
33
35
  export interface ProjectionResult {
34
36
  readonly format: 'yarramate/projection-result/v1';
35
37
  readonly projection: string;
@@ -1,9 +1,11 @@
1
- import Ajv2020Module from 'ajv/dist/2020.js';
1
+ import { createRequire } from 'node:module';
2
2
  import { isDeclaredNonGoal } from './brief.js';
3
3
  import { loadSourceDocument } from './source-document.js';
4
4
  import projectionSchema from '../schema/yarramate-projection.schema.json' with { type: 'json'
5
5
  };
6
- const Ajv2020 = Ajv2020Module.default;
6
+ const require = createRequire(import.meta.url);
7
+ const ajv2020Module = require('ajv/dist/2020.js');
8
+ const Ajv2020 = ajv2020Module.default ?? ajv2020Module;
7
9
  const validateProjection = new Ajv2020({ allErrors: true }).compile(projectionSchema);
8
10
  export function loadProjection(source) {
9
11
  const loaded = loadSourceDocument(source, validateProjection, 'Projection');
@@ -45,6 +47,7 @@ export function canonicalProjection(projection) {
45
47
  ...(presentation.showLifecycle === undefined ? {} : { showLifecycle: presentation.showLifecycle }),
46
48
  ...(presentation.showEvidence === undefined ? {} : { showEvidence: presentation.showEvidence }),
47
49
  ...(presentation.showOwnership === undefined ? {} : { showOwnership: presentation.showOwnership }),
50
+ ...(presentation.notation === undefined ? {} : { notation: presentation.notation }),
48
51
  },
49
52
  }),
50
53
  };
@@ -226,6 +229,9 @@ export function evaluateProjection(graph, projection, profileContext) {
226
229
  ...(projection.presentation.showOwnership === undefined
227
230
  ? {}
228
231
  : { showOwnership: projection.presentation.showOwnership }),
232
+ ...(projection.presentation.notation === undefined
233
+ ? {}
234
+ : { notation: projection.presentation.notation }),
229
235
  },
230
236
  }),
231
237
  documents: graph.documents.filter(({ id }) => selectedDocuments.has(id)),
@@ -1,4 +1,4 @@
1
- import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, } from './compiler.js';
1
+ import { ATTESTATION_PREDICATE_PREFIX, parseAttestationClaimValue, parseConstraintExpectsValue, } from './compiler.js';
2
2
  const assertedRelationshipsByClaim = (graph) => {
3
3
  const asserted = new Map();
4
4
  if (graph === undefined)
@@ -30,7 +30,8 @@ export const constraintExpectsPredicate = 'yarramate/constraint/expects';
30
30
  // A finding is now a union (ADR 0074); only the evidence arm can carry an
31
31
  // expectation, so ordering reads it through one narrowing helper.
32
32
  const expectationOf = (finding) => 'expectation' in finding ? finding.expectation : undefined;
33
- // Mirrors the compiler encoding (ADR 0075). Provider and key admit no
33
+ // The compiler's parseConstraintExpectsValue is the sole authority for
34
+ // decoding the encoded value (ADR 0075). Provider and key admit no
34
35
  // whitespace, so the first two spaces delimit them and everything after the
35
36
  // second space is the expected value verbatim, spaces included.
36
37
  const parseExpectation = (claim) => {
@@ -38,15 +39,15 @@ const parseExpectation = (claim) => {
38
39
  !('value' in claim.object)) {
39
40
  return undefined;
40
41
  }
41
- const match = /^(\S+) (\S+) ([\s\S]+)$/.exec(claim.object.value);
42
- if (match === null)
42
+ const parts = parseConstraintExpectsValue(claim.object.value);
43
+ if (parts === undefined)
43
44
  return undefined;
44
45
  return {
45
46
  claim: claim.id,
46
47
  subject: claim.subject,
47
- provider: match[1],
48
- key: match[2],
49
- expected: match[3],
48
+ provider: parts.provider,
49
+ key: parts.key,
50
+ expected: parts.value,
50
51
  declared: {
51
52
  document: claim.source.document,
52
53
  path: claim.source.path,
@@ -21,6 +21,16 @@ export type SourceDocumentResult<T> = {
21
21
  };
22
22
  export declare const diagnosticOrder: (left: Diagnostic, right: Diagnostic) => number;
23
23
  export declare const describeSchemaViolation: (error: Pick<ErrorObject, 'keyword' | 'message' | 'params'>) => string;
24
+ /**
25
+ * The errors worth reading, in the order Ajv found them.
26
+ *
27
+ * A discriminated union raises two errors when the tag itself is missing or
28
+ * mistyped: the `required`/`type` fault that names the property, and a
29
+ * `discriminator` row restating it. The second says nothing the first did not,
30
+ * so it is dropped. A tag that is present but names no branch keeps its row -
31
+ * that one is the only report of the fault.
32
+ */
33
+ export declare const readableSchemaErrors: <T extends Pick<ErrorObject, 'keyword' | 'params'>>(errors: readonly T[]) => readonly T[];
24
34
  export declare const closestCandidate: (value: string, candidates: Iterable<string>) => string | undefined;
25
35
  export declare function locateSourcePath(sourcePath: string, yaml: ReturnType<typeof parseDocument>, lineCounter: LineCounter, yamlPath: readonly (string | number)[], pointer: string): SourceLocation;
26
36
  export declare function loadSourceDocument<T>(source: WorkspaceSource, validate: ValidateFunction, schemaLabel: string): SourceDocumentResult<T>;
@@ -18,8 +18,28 @@ export const describeSchemaViolation = (error) => {
18
18
  .join(', ');
19
19
  return `${base}: ${shown}${allowed.length > 8 ? ', …' : ''}`;
20
20
  }
21
+ // The one pattern in these schemas that is a rule rather than a shape:
22
+ // `must match pattern "\S"` names the regex, not what the author did wrong.
23
+ if (error.keyword === 'pattern' && error.params.pattern === '\\S') {
24
+ return 'must not be blank';
25
+ }
26
+ // A tag naming no branch reads as a search failure ("must be in oneOf").
27
+ // Say what was named instead, since that is the value to correct.
28
+ if (error.keyword === 'discriminator' && error.params.error === 'mapping') {
29
+ return `unknown ${JSON.stringify(error.params.tag)} value ${JSON.stringify(error.params.tagValue)}`;
30
+ }
21
31
  return base;
22
32
  };
33
+ /**
34
+ * The errors worth reading, in the order Ajv found them.
35
+ *
36
+ * A discriminated union raises two errors when the tag itself is missing or
37
+ * mistyped: the `required`/`type` fault that names the property, and a
38
+ * `discriminator` row restating it. The second says nothing the first did not,
39
+ * so it is dropped. A tag that is present but names no branch keeps its row -
40
+ * that one is the only report of the fault.
41
+ */
42
+ export const readableSchemaErrors = (errors) => errors.filter((error) => error.keyword !== 'discriminator' || error.params.error === 'mapping');
23
43
  const editDistance = (left, right) => {
24
44
  const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
25
45
  for (let i = 1; i <= left.length; i += 1) {
@@ -86,7 +106,7 @@ export function loadSourceDocument(source, validate, schemaLabel) {
86
106
  if (!validate(value)) {
87
107
  return {
88
108
  ok: false,
89
- diagnostics: (validate.errors ?? [])
109
+ diagnostics: readableSchemaErrors(validate.errors ?? [])
90
110
  .map((error) => {
91
111
  const property = error.keyword === 'additionalProperties'
92
112
  ? String(error.params.additionalProperty)