yarramate 1.22.0 → 1.23.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 (36) hide show
  1. package/dist/adapters/visual/protocol-contract.d.ts +54 -0
  2. package/dist/adapters/visual/wire.d.ts +10 -1
  3. package/dist/adapters/visual/workspace-model.d.ts +18 -2
  4. package/dist/adapters/visual/workspace-model.js +72 -2
  5. package/dist/compiler.d.ts +63 -0
  6. package/dist/compiler.js +30 -0
  7. package/dist/concept-drafting.d.ts +70 -0
  8. package/dist/concept-drafting.js +134 -0
  9. package/dist/index.d.ts +1 -1
  10. package/dist/projection.d.ts +0 -11
  11. package/dist/projection.js +0 -4
  12. package/dist/schema-validators.generated.js +22 -34
  13. package/dist/visual-app/assets/index-B6SK3bM5.js +394 -0
  14. package/dist/visual-app/assets/index-CnldeUHL.css +1 -0
  15. package/dist/visual-app/index.html +2 -2
  16. package/dist/visual-app-lib/editor.js +28330 -27975
  17. package/dist/visual-app-lib/styles.css +1 -1
  18. package/dist/visual-app-lib/types/adapters/visual/protocol-contract.d.ts +54 -0
  19. package/dist/visual-app-lib/types/adapters/visual/wire.d.ts +10 -1
  20. package/dist/visual-app-lib/types/adapters/visual/workspace-model.d.ts +18 -2
  21. package/dist/visual-app-lib/types/compiler.d.ts +63 -0
  22. package/dist/visual-app-lib/types/concept-drafting.d.ts +70 -0
  23. package/dist/visual-app-lib/types/projection.d.ts +0 -11
  24. package/dist/visual-app-lib/types/visual-app/graph-canvas.d.ts +2 -13
  25. package/dist/visual-app-lib/types/visual-app/instance-draft-panel.d.ts +30 -0
  26. package/dist/visual-app-lib/types/visual-app/kind-palette.d.ts +29 -3
  27. package/dist/visual-app-lib/types/visual-app/query-fields.d.ts +2 -3
  28. package/dist/visual-app-lib/types/visual-app/query-panel.d.ts +3 -10
  29. package/dist/visual-app-lib/types/visual-app/workspace-state.d.ts +1 -8
  30. package/docs/CONSUMING-YARRAMATE.md +34 -0
  31. package/package.json +1 -1
  32. package/schema/yarramate-projection.schema.json +0 -4
  33. package/schema/yarramate-visual-event.schema.json +6 -0
  34. package/dist/visual-app/assets/index-C0nYrzna.js +0 -395
  35. package/dist/visual-app/assets/index-DF3anVfS.css +0 -1
  36. package/dist/visual-app-lib/types/visual-app/constraint-rows.d.ts +0 -70
@@ -147,9 +147,63 @@ export interface VisualViewSummary {
147
147
  */
148
148
  readonly subjectCount: number;
149
149
  }
150
+ /**
151
+ * One pattern a browser may offer, with everything a form needs to ask for its
152
+ * parts (#473 phase 4, ADR 0146).
153
+ *
154
+ * Built in `workspace-model.ts` so both hosts agree, for the reason the
155
+ * containment tree is: two derivations of what a pattern holds would be two
156
+ * answers to one question.
157
+ */
158
+ export interface VisualPatternOption {
159
+ /** The concept kind that IS this pattern. */
160
+ readonly kind: string;
161
+ readonly label: string;
162
+ /** The core kind the pattern's own kind descends from. */
163
+ readonly coreLabel: string;
164
+ /** The pattern document that declared it, by path. */
165
+ readonly document: string;
166
+ /** The profile's display name for the kind, where it authored one. */
167
+ readonly name?: string;
168
+ readonly slots: readonly {
169
+ readonly name: string;
170
+ readonly required: boolean;
171
+ readonly wiring: "owned" | "context" | "unwired";
172
+ /**
173
+ * The kind LABELS this slot accepts, descendants already resolved.
174
+ *
175
+ * Resolved here rather than in the browser because it needs the lineage
176
+ * map: a slot declaring `kindMatching: descendants` admits a family, and a
177
+ * picker that offered only the declared kind would refuse subjects the
178
+ * compiler accepts.
179
+ */
180
+ readonly admits: readonly string[];
181
+ }[];
182
+ readonly wiring: readonly {
183
+ readonly from: string;
184
+ readonly kind: string;
185
+ readonly to: string;
186
+ }[];
187
+ readonly ports: readonly {
188
+ readonly kind: string;
189
+ readonly out: string;
190
+ readonly in: string;
191
+ }[];
192
+ }
150
193
  export interface VisualKindOption {
151
194
  readonly id: string;
152
195
  readonly label: string;
196
+ /**
197
+ * The pattern this kind IS, which for a pattern kind is its own id (#473
198
+ * phase 4). Absent on a kind no pattern declares, which is most of them.
199
+ */
200
+ readonly pattern?: string;
201
+ /**
202
+ * The display name the profile authored, where it authored one. `label`
203
+ * stays the local id, because that is what a drag payload and an operation
204
+ * carry and the two must not drift.
205
+ */
206
+ readonly name?: string;
153
207
  /**
154
208
  * The nearest core-profile kind this one descends from, as a label — the
155
209
  * same resolution `CanvasNode.coreKindLabel` and `CanvasEdge.coreKindLabel`
@@ -1,6 +1,6 @@
1
1
  import type { CanvasGraph } from '../../graph-projection.js';
2
2
  import type { PatternMembership, PatternVacancy } from '../../compiler.js';
3
- import type { VISUAL_PROTOCOL_VERSION, VisualApplyResultPayload, VisualAuthority, VisualBrowserInput, VisualCapabilities, VisualChoicePresentPayload, VisualDiagnostic, VisualFilterResultPayload, VisualFreezeReason, VisualKindOption, VisualLayoutPositions, VisualLayoutSaveResultPayload, VisualResponse, VisualTerminationReason, VisualViewSummary } from './protocol-contract.js';
3
+ import type { VISUAL_PROTOCOL_VERSION, VisualApplyResultPayload, VisualAuthority, VisualBrowserInput, VisualCapabilities, VisualChoicePresentPayload, VisualDiagnostic, VisualFilterResultPayload, VisualFreezeReason, VisualKindOption, VisualPatternOption, VisualLayoutPositions, VisualLayoutSaveResultPayload, VisualResponse, VisualTerminationReason, VisualViewSummary } from './protocol-contract.js';
4
4
  /**
5
5
  * Transport shapes the session server and the browser application both speak.
6
6
  *
@@ -51,6 +51,15 @@ export interface VisualRenderedModel {
51
51
  readonly vocabulary: {
52
52
  readonly conceptKinds: readonly VisualKindOption[];
53
53
  readonly relationshipKinds: readonly VisualKindOption[];
54
+ /**
55
+ * The patterns this workspace declares, with their slots resolved to what
56
+ * each admits (#473 phase 4, ADR 0146).
57
+ *
58
+ * Optional, and ABSENT rather than empty where the workspace declares none:
59
+ * an empty list is a workspace with no patterns, while absence is a host
60
+ * that never looked, and a palette should be able to tell those apart.
61
+ */
62
+ readonly patterns?: readonly VisualPatternOption[];
54
63
  };
55
64
  /** Every saved layout sidecar, keyed by projection id (Plan-level decision 1). */
56
65
  readonly layouts: {
@@ -2,10 +2,11 @@ import type { Diagnostic } from "../../compiler.js";
2
2
  import type { PendingWrite, SourceStore } from "../../source-store.js";
3
3
  import type { ResolvedWorkspace } from "../../workspace.js";
4
4
  import type { VisualDiagnostic, VisualViewOperation } from "./protocol-contract.js";
5
+ import type { PatternShape } from "../../compiler.js";
5
6
  import type { NestingKind, ProjectionDefinition, ProjectionExclusion, ProjectionQuery } from "../../projection.js";
6
7
  import type { ResolvedProfileContext, SemanticGraph } from "../../compiler.js";
7
8
  import { type CataloguePatternMembership, type CataloguePatternVacancy } from "../../interrogate-command.js";
8
- import type { VisualKindOption, VisualViewSummary } from "./protocol-contract.js";
9
+ import type { VisualKindOption, VisualPatternOption, VisualViewSummary } from "./protocol-contract.js";
9
10
  import type { VisualInterrogationOverlay, VisualRenderedModel } from "./wire.js";
10
11
  /**
11
12
  * What a workspace looks like to the editor, however the editor is being run
@@ -31,7 +32,22 @@ import type { VisualInterrogationOverlay, VisualRenderedModel } from "./wire.js"
31
32
  * keyed on core kinds, so offering an extension kind unchecked puts a `YM404`
32
33
  * one click away.
33
34
  */
34
- export declare const kindOptionsOf: (lineages: ReadonlyMap<string, readonly string[]>) => readonly VisualKindOption[];
35
+ export declare const kindOptionsOf: (lineages: ReadonlyMap<string, readonly string[]>,
36
+ /** Which kinds ARE patterns, so a palette can group them (#473 phase 4). */
37
+ patterns?: ReadonlyMap<string, PatternShape>,
38
+ /** The profile's display names, where it authored any. */
39
+ names?: ReadonlyMap<string, string>) => readonly VisualKindOption[];
40
+ /**
41
+ * Every pattern a browser may offer, with its slots resolved to what they
42
+ * admit.
43
+ *
44
+ * `admits` is the expensive part and the reason this lives here rather than in
45
+ * the browser: a slot declaring `kindMatching: descendants` accepts a whole
46
+ * family, and resolving that needs the lineage map the frame does not carry.
47
+ * A picker built from the declared kind alone would refuse subjects the
48
+ * compiler accepts.
49
+ */
50
+ export declare const patternOptionsOf: (patterns: readonly PatternShape[], lineages: ReadonlyMap<string, readonly string[]>, names?: ReadonlyMap<string, string>) => readonly VisualPatternOption[];
35
51
  /**
36
52
  * How many SUBJECTS a query matches, which is not the size of its match set.
37
53
  *
@@ -31,11 +31,70 @@ import { evaluateCatalogue, composeCatalogues, } from "../../interrogate-command
31
31
  * keyed on core kinds, so offering an extension kind unchecked puts a `YM404`
32
32
  * one click away.
33
33
  */
34
- export const kindOptionsOf = (lineages) => [...lineages.keys()].map((id) => ({
34
+ export const kindOptionsOf = (lineages,
35
+ /** Which kinds ARE patterns, so a palette can group them (#473 phase 4). */
36
+ patterns = new Map(),
37
+ /** The profile's display names, where it authored any. */
38
+ names = new Map()) => [...lineages.keys()].map((id) => ({
35
39
  id,
36
40
  label: kindLabelOf(id),
37
41
  coreLabel: kindLabelOf(lineages.get(id)?.[0] ?? id),
42
+ ...(patterns.has(id) ? { pattern: id } : {}),
43
+ ...(names.has(id) ? { name: names.get(id) } : {}),
38
44
  }));
45
+ /**
46
+ * Every pattern a browser may offer, with its slots resolved to what they
47
+ * admit.
48
+ *
49
+ * `admits` is the expensive part and the reason this lives here rather than in
50
+ * the browser: a slot declaring `kindMatching: descendants` accepts a whole
51
+ * family, and resolving that needs the lineage map the frame does not carry.
52
+ * A picker built from the declared kind alone would refuse subjects the
53
+ * compiler accepts.
54
+ */
55
+ export const patternOptionsOf = (patterns, lineages, names = new Map()) => patterns.map((pattern) => ({
56
+ kind: pattern.kindIdentity,
57
+ label: kindLabelOf(pattern.kindIdentity),
58
+ coreLabel: kindLabelOf(lineages.get(pattern.kindIdentity)?.[0] ?? pattern.kindIdentity),
59
+ document: pattern.declaredBy,
60
+ ...(names.has(pattern.kindIdentity)
61
+ ? { name: names.get(pattern.kindIdentity) }
62
+ : {}),
63
+ slots: pattern.slots.map((slot) => ({
64
+ name: slot.name,
65
+ required: slot.required,
66
+ wiring: wiringOf(pattern, slot.name),
67
+ admits: slot.kindMatching === "exact"
68
+ ? [kindLabelOf(slot.kindIdentity)]
69
+ : [...lineages]
70
+ .filter(([, lineage]) => lineage.includes(slot.kindIdentity))
71
+ .map(([id]) => kindLabelOf(id))
72
+ .sort(),
73
+ })),
74
+ wiring: pattern.wiring.map((wire) => ({
75
+ from: wire.from,
76
+ kind: kindLabelOf(wire.kindIdentity),
77
+ to: wire.to,
78
+ })),
79
+ ports: pattern.ports.map((port) => ({
80
+ kind: kindLabelOf(port.kindIdentity),
81
+ out: port.out,
82
+ in: port.in,
83
+ })),
84
+ }));
85
+ /**
86
+ * How a pattern's wiring relates a slot to the instance, by the same rule the
87
+ * compiler applies (ADR 0143): `self -> slot` is owned, `slot -> self` is
88
+ * context, neither is unwired, and BOTH is owned, because the instance holding
89
+ * something out is the stronger statement.
90
+ */
91
+ const wiringOf = (pattern, slot) => {
92
+ const out = pattern.wiring.some((wire) => wire.from === "self" && wire.to === slot);
93
+ const back = pattern.wiring.some((wire) => wire.from === slot && wire.to === "self");
94
+ if (out)
95
+ return "owned";
96
+ return back ? "context" : "unwired";
97
+ };
39
98
  /**
40
99
  * How many SUBJECTS a query matches, which is not the size of its match set.
41
100
  *
@@ -135,8 +194,19 @@ export const renderedWorkspaceOf = (compiled, views, metadata, catalogue, dismis
135
194
  ...metadata,
136
195
  graph: projectGraphForCanvas(compiled.graph, compiled.profileContext),
137
196
  vocabulary: {
138
- conceptKinds: kindOptionsOf(compiled.profileContext.conceptKindLineages),
197
+ conceptKinds: kindOptionsOf(compiled.profileContext.conceptKindLineages, new Map((compiled.profileContext.patterns ?? []).map((pattern) => [
198
+ pattern.kindIdentity,
199
+ pattern,
200
+ ])), compiled.profileContext.conceptKindNames ?? new Map()),
139
201
  relationshipKinds: kindOptionsOf(compiled.profileContext.relationshipKindLineages),
202
+ // Only where the workspace HAS patterns. An empty list would say the
203
+ // workspace declares none, which is a different claim from a host that
204
+ // never looked (rule 2), and the field is optional for exactly that.
205
+ ...((compiled.profileContext.patterns ?? []).length === 0
206
+ ? {}
207
+ : {
208
+ patterns: patternOptionsOf(compiled.profileContext.patterns ?? [], compiled.profileContext.conceptKindLineages, compiled.profileContext.conceptKindNames ?? new Map()),
209
+ }),
140
210
  },
141
211
  ...(interrogation === undefined ? {} : { interrogation }),
142
212
  // Containment context, forwarded rather than re-derived (#473). Both
@@ -29,6 +29,52 @@ export interface Diagnostic {
29
29
  */
30
30
  readonly subjects?: readonly string[];
31
31
  }
32
+ /**
33
+ * One slot of a pattern, as a CONSUMER reads it (#473 phase 4, ADR 0146).
34
+ *
35
+ * The resolved shape rather than the authored one: `kindIdentity` is qualified
36
+ * and `kindMatching` is explicit, so a caller deciding what a slot admits does
37
+ * not have to re-resolve the document it came from.
38
+ */
39
+ export interface PatternSlotShape {
40
+ readonly name: string;
41
+ readonly kindIdentity: string;
42
+ readonly required: boolean;
43
+ readonly kindMatching: 'exact' | 'descendants';
44
+ }
45
+ /** A wire the pattern declares, `self` naming the instance. */
46
+ export interface PatternWireShape {
47
+ readonly from: string;
48
+ readonly to: string;
49
+ readonly kindIdentity: string;
50
+ }
51
+ /** Where a macro edge of one kind leaves and arrives (ADR 0124). */
52
+ export interface PatternPortShape {
53
+ readonly kindIdentity: string;
54
+ readonly out: string;
55
+ readonly in: string;
56
+ }
57
+ /**
58
+ * A pattern as the workspace resolved it.
59
+ *
60
+ * The pattern DOCUMENT has had a JSON schema since ADR 0123 and never a public
61
+ * TypeScript type, so a host that wanted to offer patterns had to re-read the
62
+ * YAML or guess. Slots come as an ordered array rather than a map because the
63
+ * order a pattern DECLARES them is the order a form should ask for them, and a
64
+ * map's iteration order is not something a consumer should have to trust.
65
+ */
66
+ export interface PatternShape {
67
+ readonly kindIdentity: string;
68
+ /**
69
+ * The PATH of the pattern document that declared it, not that document's id.
70
+ * It is the same string a duplicate-declaration diagnostic names, so a host
71
+ * grouping patterns by document groups them by the thing a reader can open.
72
+ */
73
+ readonly declaredBy: string;
74
+ readonly slots: readonly PatternSlotShape[];
75
+ readonly wiring: readonly PatternWireShape[];
76
+ readonly ports: readonly PatternPortShape[];
77
+ }
32
78
  export interface GraphSource {
33
79
  readonly document: string;
34
80
  readonly path: string;
@@ -86,6 +132,23 @@ export interface ResolvedProfileContext {
86
132
  * else.
87
133
  */
88
134
  readonly patternPortKinds: ReadonlyMap<string, readonly RelationshipKind[]>;
135
+ /**
136
+ * Every pattern the workspace resolved, in kind order (#473 phase 4).
137
+ *
138
+ * Optional for the reason `patternMemberships` is: this shape is published
139
+ * and a required addition is free for readers and a break for CONSTRUCTORS.
140
+ * Read it as `?? []`. Absent, a host offers no patterns rather than offering
141
+ * an empty list of them.
142
+ */
143
+ readonly patterns?: readonly PatternShape[];
144
+ /**
145
+ * Kind identity -> the display name its PROFILE authored (#473 phase 4).
146
+ *
147
+ * Only profile-declared kinds appear: a core kind's name is the vocabulary's
148
+ * own and a consumer already has it. Optional for the reason `patterns` is,
149
+ * and read as `?? new Map()`.
150
+ */
151
+ readonly conceptKindNames?: ReadonlyMap<string, string>;
89
152
  /**
90
153
  * The core relationship kinds the ArchiMate table permits between two
91
154
  * concept kind identities, resolved through lineage; undefined when either
package/dist/compiler.js CHANGED
@@ -296,6 +296,10 @@ function compileWorkspaceResolved(parsed) {
296
296
  }
297
297
  }
298
298
  const profiles = new Map();
299
+ // Kind identity -> the display name the profile authored for it (#473 phase
300
+ // 4). Resolution keeps identity, lineage, layer and aspect and drops the
301
+ // name, so a palette had nothing to show but the local id.
302
+ const conceptKindNames = new Map();
299
303
  const conceptKindByIdentity = new Map();
300
304
  const relationshipKindByIdentity = new Map();
301
305
  const coreConceptKinds = new Map();
@@ -509,6 +513,7 @@ function compileWorkspaceResolved(parsed) {
509
513
  };
510
514
  resolvedConceptKinds.set(kind.id, resolved);
511
515
  conceptKindByIdentity.set(resolved.identity, resolved);
516
+ conceptKindNames.set(resolved.identity, kind.name);
512
517
  }
513
518
  // No round can help what the last one could not.
514
519
  if (deferred.length === 0 || deferred.length === pendingKinds.length) {
@@ -2578,6 +2583,31 @@ function compileWorkspaceResolved(parsed) {
2578
2583
  relationshipKindCoreAncestors: immutableMap([...relationshipKindByIdentity]
2579
2584
  .sort(([left], [right]) => left.localeCompare(right))
2580
2585
  .map(([identity, kind]) => [identity, kind.coreKind])),
2586
+ // Ordered by kind, and each pattern's slots in the order the document
2587
+ // DECLARED them, because that is the order a form should ask for them.
2588
+ conceptKindNames: immutableMap([...conceptKindNames].sort(([left], [right]) => left.localeCompare(right))),
2589
+ patterns: Object.freeze([...patternsByKind]
2590
+ .sort(([left], [right]) => left.localeCompare(right))
2591
+ .map(([identity, pattern]) => ({
2592
+ kindIdentity: identity,
2593
+ declaredBy: pattern.declaredBy,
2594
+ slots: Object.freeze([...pattern.slots.values()].map((slot) => ({
2595
+ name: slot.name,
2596
+ kindIdentity: slot.kindIdentity,
2597
+ required: slot.required,
2598
+ kindMatching: slot.kindMatching,
2599
+ }))),
2600
+ wiring: Object.freeze(pattern.wiring.map((wire) => ({
2601
+ from: wire.from,
2602
+ to: wire.to,
2603
+ kindIdentity: wire.kindIdentity,
2604
+ }))),
2605
+ ports: Object.freeze([...pattern.ports].map(([kindIdentity, port]) => ({
2606
+ kindIdentity,
2607
+ out: port.out,
2608
+ in: port.in,
2609
+ }))),
2610
+ }))),
2581
2611
  patternPortKinds: immutableMap([...patternsByKind]
2582
2612
  .sort(([left], [right]) => left.localeCompare(right))
2583
2613
  .flatMap(([identity, pattern]) => pattern.ports.size === 0
@@ -36,3 +36,73 @@ export declare const draftConcept: (graph: CanvasGraph, input: {
36
36
  readonly kind: string;
37
37
  readonly document: string;
38
38
  }, kinds: readonly string[], reserved?: Iterable<string>) => YarramateOperation | null;
39
+ /**
40
+ * What a reviewer chose for one slot of a pattern instance (#473 phase 4).
41
+ *
42
+ * `null` is a slot left alone, which is legal for an optional slot and is what
43
+ * the interview later asks about (ADR 0140). A required slot left null is
44
+ * refused here rather than staged and refused by the compiler, because a
45
+ * changeset that cannot land is worse than a form that will not submit.
46
+ */
47
+ export type SlotBinding = {
48
+ readonly mode: 'existing';
49
+ readonly subject: string;
50
+ } | {
51
+ readonly mode: 'new';
52
+ readonly name: string;
53
+ readonly kind: string;
54
+ } | null;
55
+ export interface InstanceDraft {
56
+ readonly name: string;
57
+ readonly document: string;
58
+ /** The pattern's own kind, as a label, the same spelling an operation uses. */
59
+ readonly kind: string;
60
+ readonly slots: readonly {
61
+ readonly name: string;
62
+ readonly required: boolean;
63
+ /** Kind labels this slot accepts, descendants already resolved. */
64
+ readonly admits: readonly string[];
65
+ }[];
66
+ }
67
+ /**
68
+ * A pattern instance and every child it mints, as ONE batch.
69
+ *
70
+ * Children first and the instance last, though `apply` compiles the whole
71
+ * candidate workspace atomically and would take either order: item 4.1 proved
72
+ * both, and emitting the order a reader would write by hand costs nothing and
73
+ * makes the diff read forwards.
74
+ *
75
+ * Every minted id is reserved as it is proposed, so two slots filled with the
76
+ * same name do not both slug to `payload` and have the second silently replace
77
+ * the first (#315, the defect that made `reservedIds` required).
78
+ *
79
+ * `null` rather than a partial batch when anything is wrong: a form that stages
80
+ * half an instance leaves the reviewer to discover the rest from a compile
81
+ * error.
82
+ */
83
+ export declare const draftInstance: (graph: CanvasGraph, input: InstanceDraft, bindings: ReadonlyMap<string, SlotBinding>, reserved?: Iterable<string>) => readonly YarramateOperation[] | null;
84
+ /**
85
+ * Filling ONE slot of an instance that already exists (#473 phase 4).
86
+ *
87
+ * The other half of {@link draftInstance}: that one mints an instance and its
88
+ * parts together, this one answers a slot left open, which is what a
89
+ * `missing-part` card asks about (ADR 0140, #447). Both stage the same shape,
90
+ * so the model cannot tell which surface a binding came from.
91
+ *
92
+ * Merges BY SLOT, per ADR 0062's recorded convention: the operation names only
93
+ * the slot being filled, and the slots it does not mention are left alone
94
+ * (#448). There is no null idiom for retraction here, because retraction is
95
+ * coarse `remove: ['parts']` and is a different gesture from filling one.
96
+ *
97
+ * `null` rather than a partial batch: a child minted without the binding that
98
+ * uses it is a subject nobody asked for.
99
+ */
100
+ export declare const draftSlotBinding: (graph: CanvasGraph, input: {
101
+ /** The instance whose slot is being filled. */
102
+ readonly instance: string;
103
+ readonly slot: string;
104
+ /** The document the operation writes to, and any minted child with it. */
105
+ readonly document: string;
106
+ /** Kind labels the slot accepts, descendants already resolved. */
107
+ readonly admits: readonly string[];
108
+ }, binding: SlotBinding, reserved?: Iterable<string>) => readonly YarramateOperation[] | null;
@@ -85,3 +85,137 @@ export const draftConcept = (graph, input, kinds, reserved = []) => {
85
85
  concept: { id, kind: input.kind, name },
86
86
  };
87
87
  };
88
+ /**
89
+ * A pattern instance and every child it mints, as ONE batch.
90
+ *
91
+ * Children first and the instance last, though `apply` compiles the whole
92
+ * candidate workspace atomically and would take either order: item 4.1 proved
93
+ * both, and emitting the order a reader would write by hand costs nothing and
94
+ * makes the diff read forwards.
95
+ *
96
+ * Every minted id is reserved as it is proposed, so two slots filled with the
97
+ * same name do not both slug to `payload` and have the second silently replace
98
+ * the first (#315, the defect that made `reservedIds` required).
99
+ *
100
+ * `null` rather than a partial batch when anything is wrong: a form that stages
101
+ * half an instance leaves the reviewer to discover the rest from a compile
102
+ * error.
103
+ */
104
+ export const draftInstance = (graph, input, bindings, reserved = []) => {
105
+ if (input.document === '')
106
+ return null;
107
+ const name = input.name.trim();
108
+ if (name === '')
109
+ return null;
110
+ const claimed = new Set(reserved);
111
+ const children = [];
112
+ const parts = {};
113
+ for (const slot of input.slots) {
114
+ const binding = bindings.get(slot.name) ?? null;
115
+ if (binding === null) {
116
+ // The model does not stand up without it, so the form cannot stage it.
117
+ if (slot.required)
118
+ return null;
119
+ continue;
120
+ }
121
+ if (binding.mode === 'existing') {
122
+ if (binding.subject === '')
123
+ return null;
124
+ parts[slot.name] = binding.subject;
125
+ continue;
126
+ }
127
+ // A minted child: its kind has to be one the slot admits, or the compiler
128
+ // refuses the binding and the reviewer learns it from a diagnostic.
129
+ if (!slot.admits.includes(binding.kind))
130
+ return null;
131
+ const childName = binding.name.trim();
132
+ if (childName === '')
133
+ return null;
134
+ const childId = proposeConceptId(graph, childName, claimed);
135
+ if (childId === null)
136
+ return null;
137
+ claimed.add(childId);
138
+ children.push({
139
+ op: 'add-concept',
140
+ // The instance's document, not a choice: a part authored somewhere else
141
+ // is a part the reader has to go looking for.
142
+ document: input.document,
143
+ concept: { id: childId, kind: binding.kind, name: childName },
144
+ });
145
+ parts[slot.name] = childId;
146
+ }
147
+ const instanceId = proposeConceptId(graph, name, claimed);
148
+ if (instanceId === null)
149
+ return null;
150
+ return [
151
+ ...children,
152
+ {
153
+ op: 'add-concept',
154
+ document: input.document,
155
+ concept: {
156
+ id: instanceId,
157
+ kind: input.kind,
158
+ name,
159
+ // Omitted rather than empty where nothing was bound: an empty `parts`
160
+ // says the instance binds nothing, and the compiler reads the two the
161
+ // same way but a reader does not.
162
+ ...(Object.keys(parts).length === 0 ? {} : { parts }),
163
+ },
164
+ },
165
+ ];
166
+ };
167
+ /**
168
+ * Filling ONE slot of an instance that already exists (#473 phase 4).
169
+ *
170
+ * The other half of {@link draftInstance}: that one mints an instance and its
171
+ * parts together, this one answers a slot left open, which is what a
172
+ * `missing-part` card asks about (ADR 0140, #447). Both stage the same shape,
173
+ * so the model cannot tell which surface a binding came from.
174
+ *
175
+ * Merges BY SLOT, per ADR 0062's recorded convention: the operation names only
176
+ * the slot being filled, and the slots it does not mention are left alone
177
+ * (#448). There is no null idiom for retraction here, because retraction is
178
+ * coarse `remove: ['parts']` and is a different gesture from filling one.
179
+ *
180
+ * `null` rather than a partial batch: a child minted without the binding that
181
+ * uses it is a subject nobody asked for.
182
+ */
183
+ export const draftSlotBinding = (graph, input, binding, reserved = []) => {
184
+ if (binding === null)
185
+ return null;
186
+ if (input.document === '')
187
+ return null;
188
+ if (input.slot === '')
189
+ return null;
190
+ if (binding.mode === 'existing') {
191
+ if (binding.subject === '')
192
+ return null;
193
+ return [
194
+ {
195
+ op: 'update-concept',
196
+ document: input.document,
197
+ concept: { id: input.instance, parts: { [input.slot]: binding.subject } },
198
+ },
199
+ ];
200
+ }
201
+ if (!input.admits.includes(binding.kind))
202
+ return null;
203
+ const childName = binding.name.trim();
204
+ if (childName === '')
205
+ return null;
206
+ const childId = proposeConceptId(graph, childName, reserved);
207
+ if (childId === null)
208
+ return null;
209
+ return [
210
+ {
211
+ op: 'add-concept',
212
+ document: input.document,
213
+ concept: { id: childId, kind: binding.kind, name: childName },
214
+ },
215
+ {
216
+ op: 'update-concept',
217
+ document: input.document,
218
+ concept: { id: input.instance, parts: { [input.slot]: childId } },
219
+ },
220
+ ];
221
+ };
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { constraintExpectsPredicate, reconcileEvidenceReports, type AssertedRela
10
10
  export { deriveAttestationStaleness } from './attestation-staleness.js';
11
11
  export { deriveArtifactCoverage, type ArtifactCoverage, type CoverageScopePattern, } from './artifact-coverage.js';
12
12
  export { buildRtm, renderRtmMarkdown, type RequirementsTraceabilityMatrix, type RtmAttestation, type RtmContextEntry, type RtmDescopedEntry, type RtmEvidenceVerdict, type RtmLineageEntry, type RtmRealizer, type RtmRow, type RtmSource, } from './rtm.js';
13
- export type { CompilationCache, CompilationResult, ContextualCompilationResult, IncrementalCompilationResult, ParsedWorkspaceSource, PatternMembership, PatternVacancy, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
13
+ export type { CompilationCache, CompilationResult, ContextualCompilationResult, IncrementalCompilationResult, ParsedWorkspaceSource, PatternMembership, PatternPortShape, PatternShape, PatternSlotShape, PatternVacancy, PatternWireShape, Diagnostic, GraphClaim, GraphSource, SemanticGraph, ResolvedProfileContext, WorkspaceSource, } from './compiler.js';
14
14
  export { canonicalProjection, evaluateProjection, explainProjection, instanceClosureOf, loadProjection, renderProjectionMarkdown, type ConceptFacet, type ProjectionExclusion, } from './projection.js';
15
15
  export { loadAdapterMapping, validateAdapterMapping, validateAdapterMappings, type AdapterMapping, type AdapterMappingLoadResult, type AdapterMappingValidationResult, type AdapterMappingsValidationResult, type AdapterSubjectMapping, } from './adapter-mapping.js';
16
16
  export type { LifecycleStatus, ProjectionDefinition, ProjectionLoadResult, ProjectionResult, } from './projection.js';
@@ -78,17 +78,6 @@ export interface ProjectionDefinition {
78
78
  readonly showLifecycle?: boolean;
79
79
  readonly showEvidence?: boolean;
80
80
  readonly showOwnership?: boolean;
81
- /**
82
- * Whether a bound RULING draws as a row inside its holder rather than as a
83
- * node of its own (#473 phase 3, ADR 0145).
84
- *
85
- * On the reference this takes the whole model from 173 boxes to 91, because
86
- * 82 of its rulings are bound into slots and every one of them was drawing
87
- * as a box with a single association edge. Presentation only: the model,
88
- * the query and the selected set do not move, and turning it off restores
89
- * the boxes.
90
- */
91
- readonly showConstraints?: boolean;
92
81
  /**
93
82
  * The folder this view files itself under in an editor's rail: a label the
94
83
  * author declares, nested with `/`, never the directory the projection
@@ -66,7 +66,6 @@ export function canonicalProjection(projection) {
66
66
  ...(presentation.showLifecycle === undefined ? {} : { showLifecycle: presentation.showLifecycle }),
67
67
  ...(presentation.showEvidence === undefined ? {} : { showEvidence: presentation.showEvidence }),
68
68
  ...(presentation.showOwnership === undefined ? {} : { showOwnership: presentation.showOwnership }),
69
- ...(presentation.showConstraints === undefined ? {} : { showConstraints: presentation.showConstraints }),
70
69
  ...(presentation.notation === undefined ? {} : { notation: presentation.notation }),
71
70
  },
72
71
  }),
@@ -632,9 +631,6 @@ export function evaluateProjection(graph, projection, profileContext, membership
632
631
  ...(projection.presentation.showOwnership === undefined
633
632
  ? {}
634
633
  : { showOwnership: projection.presentation.showOwnership }),
635
- ...(projection.presentation.showConstraints === undefined
636
- ? {}
637
- : { showConstraints: projection.presentation.showConstraints }),
638
634
  ...(projection.presentation.notation === undefined
639
635
  ? {}
640
636
  : { notation: projection.presentation.notation }),