yarramate 1.2.0 → 1.4.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 (84) hide show
  1. package/catalogues/core-enrichment.yaml +237 -2
  2. package/dist/adapters/visual/session-server.js +16 -1
  3. package/dist/adapters/visual/view-identity.d.ts +1 -1
  4. package/dist/adapters/visual/view-identity.js +61 -3
  5. package/dist/adapters/visual/wire.d.ts +30 -0
  6. package/dist/adapters/visual/workspace-model.d.ts +35 -3
  7. package/dist/adapters/visual/workspace-model.js +62 -2
  8. package/dist/ask-command.js +37 -14
  9. package/dist/cli-support.d.ts +1 -1
  10. package/dist/cli-support.js +1 -1
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +26 -5
  13. package/dist/compiler.d.ts +13 -1
  14. package/dist/compiler.js +646 -0
  15. package/dist/concept-drafting.d.ts +13 -2
  16. package/dist/concept-drafting.js +15 -3
  17. package/dist/design-command.js +30 -4
  18. package/dist/evidence.js +25 -0
  19. package/dist/graph-projection.d.ts +10 -0
  20. package/dist/graph-projection.js +1 -0
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +1 -1
  23. package/dist/interrogate-command.d.ts +37 -1
  24. package/dist/interrogate-command.js +93 -42
  25. package/dist/interrogation-entry.d.ts +1 -1
  26. package/dist/layout-direction.d.ts +14 -0
  27. package/dist/layout-direction.js +5 -0
  28. package/dist/projection.d.ts +22 -6
  29. package/dist/projection.js +23 -2
  30. package/dist/relationship-drafting.d.ts +21 -2
  31. package/dist/relationship-drafting.js +49 -4
  32. package/dist/subject-references.js +5 -0
  33. package/dist/visual-app/assets/index-Rkq6smL2.css +1 -0
  34. package/dist/visual-app/assets/index-yCHtmQUH.js +394 -0
  35. package/dist/visual-app/index.html +2 -2
  36. package/dist/visual-app-lib/editor.js +29639 -27514
  37. package/dist/visual-app-lib/styles.css +1 -1
  38. package/dist/visual-app-lib/types/adapters/visual/view-identity.d.ts +1 -1
  39. package/dist/visual-app-lib/types/adapters/visual/wire.d.ts +30 -0
  40. package/dist/visual-app-lib/types/adapters/visual/workspace-model.d.ts +35 -3
  41. package/dist/visual-app-lib/types/compiler.d.ts +13 -1
  42. package/dist/visual-app-lib/types/concept-drafting.d.ts +13 -2
  43. package/dist/visual-app-lib/types/graph-projection.d.ts +10 -0
  44. package/dist/visual-app-lib/types/interrogate-command.d.ts +207 -0
  45. package/dist/visual-app-lib/types/layout-direction.d.ts +14 -0
  46. package/dist/visual-app-lib/types/projection.d.ts +22 -6
  47. package/dist/visual-app-lib/types/relationship-drafting.d.ts +21 -2
  48. package/dist/visual-app-lib/types/subject-identity.d.ts +34 -0
  49. package/dist/visual-app-lib/types/visual-app/App.d.ts +27 -2
  50. package/dist/visual-app-lib/types/visual-app/badges.d.ts +1 -0
  51. package/dist/visual-app-lib/types/visual-app/connection-panel.d.ts +9 -1
  52. package/dist/visual-app-lib/types/visual-app/context-menu-model.d.ts +15 -6
  53. package/dist/visual-app-lib/types/visual-app/graph-canvas.d.ts +77 -6
  54. package/dist/visual-app-lib/types/visual-app/kind-palette.d.ts +37 -0
  55. package/dist/visual-app-lib/types/visual-app/local-host.d.ts +42 -0
  56. package/dist/visual-app-lib/types/visual-app/mount.d.ts +73 -2
  57. package/dist/visual-app-lib/types/visual-app/open-questions.d.ts +15 -0
  58. package/dist/visual-app-lib/types/visual-app/query-fields.d.ts +3 -2
  59. package/dist/visual-app-lib/types/visual-app/query-panel.d.ts +8 -1
  60. package/dist/visual-app-lib/types/visual-app/save-view.d.ts +11 -5
  61. package/dist/visual-app-lib/types/visual-app/shipped-catalogue.d.ts +10 -0
  62. package/dist/visual-app-lib/types/visual-app/state.d.ts +15 -4
  63. package/dist/visual-app-lib/types/visual-app/subject-draft-panel.d.ts +13 -1
  64. package/dist/visual-app-lib/types/visual-app/subject-filter.d.ts +35 -0
  65. package/dist/visual-app-lib/types/visual-app/subject-form.d.ts +8 -0
  66. package/dist/visual-app-lib/types/visual-app/view-tree-model.d.ts +42 -9
  67. package/dist/visual-app-lib/types/visual-app/view-tree.d.ts +11 -2
  68. package/dist/visual-app-lib/types/visual-app/workspace-state.d.ts +81 -11
  69. package/dist/visual-app-lib/types/workspace.d.ts +2 -0
  70. package/dist/workspace.d.ts +2 -0
  71. package/dist/workspace.js +1 -0
  72. package/docs/CONSUMING-YARRAMATE.md +81 -0
  73. package/package.json +2 -1
  74. package/schema/yarramate-design-step.schema.json +26 -0
  75. package/schema/yarramate-document.schema.json +14 -0
  76. package/schema/yarramate-interrogation-report.schema.json +31 -0
  77. package/schema/yarramate-pattern.schema.json +133 -0
  78. package/schema/yarramate-projection.schema.json +9 -0
  79. package/schema/yarramate-question-catalogue.schema.json +34 -0
  80. package/schema/yarramate-visual-graph.schema.json +9 -0
  81. package/schema/yarramate-workspace.schema.json +4 -0
  82. package/skills/yarramate-architecture/SKILL.md +5 -0
  83. package/dist/visual-app/assets/index-CcmfL3oY.js +0 -394
  84. package/dist/visual-app/assets/index-DLutIWES.css +0 -1
@@ -12,11 +12,23 @@ import type { VisualKindOption } from '../adapters/visual/protocol-contract.js';
12
12
  * than a transliteration of the name does. It is shown before landing, because
13
13
  * a derived address the author never saw is one nobody agreed to.
14
14
  */
15
- export declare const SubjectDraftPanel: ({ graph, kinds, documents, defaultDocument, onStage, onCancel, }: {
15
+ export declare const SubjectDraftPanel: ({ graph, kinds, documents, defaultDocument, initialKind, reservedIds, onStage, onCancel, }: {
16
16
  readonly graph: CanvasGraph;
17
17
  readonly kinds: readonly VisualKindOption[];
18
18
  readonly documents: readonly string[];
19
19
  readonly defaultDocument: string;
20
+ /** A kind the reviewer picked up on the way in - dragged from the palette
21
+ * or clicked there (#295). Their own choice arriving with the gesture, not
22
+ * a default this form chose for them, so the no-default rule below stands. */
23
+ readonly initialKind?: string;
24
+ /**
25
+ * Ids the pending changeset already claims. The graph only knows what has
26
+ * landed, so without these a second subject slugging to the same id
27
+ * proposed it again and replace-by-target staging swallowed the first
28
+ * silently (#315). Required rather than defaulted: a caller has to say
29
+ * what is staged, even when the answer is nothing.
30
+ */
31
+ readonly reservedIds: readonly string[];
20
32
  readonly onStage: (operation: YarramateOperation) => void;
21
33
  readonly onCancel: () => void;
22
34
  }) => React.ReactElement;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The one substring judgement every surface that filters subjects by typed
3
+ * text shares.
4
+ *
5
+ * #307 extracted the predicate inside `graph-canvas.tsx` so the canvas pass
6
+ * and the shell's empty-state honesty could not drift; #317 lifts it here so
7
+ * the rail's tree filter can share it too. It cannot simply be imported from
8
+ * `graph-canvas.tsx`: the rail's tree model (`view-tree-model.ts`) keeps a
9
+ * zero-dependency discipline — no React, no cytoscape — and merely loading
10
+ * the canvas module registers cytoscape-elk. So the predicate lives in this
11
+ * small pure module and both surfaces import it from here.
12
+ */
13
+ import type { CanvasNode } from "../graph-projection.js";
14
+ /**
15
+ * The slice of a subject the predicate reads — the render data both the
16
+ * canvas and the rail already hold.
17
+ */
18
+ export type FilterableSubject = Pick<CanvasNode, "id" | "name" | "kindLabel">;
19
+ /**
20
+ * Typed filter text made comparable: trimmed and lowercased, once, so every
21
+ * surface trims the same way before matching.
22
+ */
23
+ export declare const normalizeFilterText: (text: string) => string;
24
+ /**
25
+ * Whether a subject survives the typed text: case-insensitive, against its
26
+ * id, name, and kind label. `name` and `kindLabel` are `unknown` because the
27
+ * canvas reads them out of cytoscape data, which types nothing.
28
+ */
29
+ export declare function subjectMatchesQuickFilter(trimmedLowerFilter: string, id: string, name: unknown, kindLabel: unknown): boolean;
30
+ /**
31
+ * How many of these subjects survive the typed text — the number a tree row
32
+ * shows while the filter narrows (#317). With no text every subject
33
+ * survives, so this is also the plain length.
34
+ */
35
+ export declare const countMatchingSubjects: (subjects: readonly FilterableSubject[], filterText: string) => number;
@@ -32,6 +32,14 @@ export interface ConceptFormProps {
32
32
  readonly onStageChange: (operation: YarramateOperation) => void;
33
33
  }
34
34
  export declare const ConceptForm: ({ node, model, operations, onStageChange }: ConceptFormProps) => import("react").JSX.Element;
35
+ export declare const ConceptFacts: ({ node, model, }: {
36
+ readonly node: CanvasNode;
37
+ readonly model: VisualRenderedModel;
38
+ }) => import("react").JSX.Element;
39
+ export declare const RelationshipFacts: ({ edge, model, }: {
40
+ readonly edge: CanvasEdge;
41
+ readonly model: VisualRenderedModel;
42
+ }) => import("react").JSX.Element;
35
43
  export interface RelationshipFormProps {
36
44
  readonly edge: CanvasEdge;
37
45
  readonly model: VisualRenderedModel;
@@ -2,7 +2,8 @@
2
2
  * What the left rail shows, as data.
3
3
  *
4
4
  * The rail is two trees over things the session already has — the saved views
5
- * and the subjects the model declares — so everything it decides is a pure
5
+ * with the reviewer's staged view operations merged over them (ADR 0114), and
6
+ * the subjects the model declares — so everything it decides is a pure
6
7
  * function of those plus what the reviewer typed. It lives outside the
7
8
  * component for the reason `filterToReresolve` does: this repo renders React
8
9
  * with `renderToStaticMarkup` and has no DOM test environment, so logic that
@@ -13,7 +14,8 @@
13
14
  */
14
15
  import type { CanvasNode } from "../graph-projection.js";
15
16
  import type { Layer } from "../profile.js";
16
- import type { VisualViewSummary } from "../adapters/visual/protocol-contract.js";
17
+ import type { VisualViewOperation, VisualViewSummary } from "../adapters/visual/protocol-contract.js";
18
+ import { type FilterableSubject } from "./subject-filter.js";
17
19
  export declare const VIEWS_ROOT_KEY = "views";
18
20
  export declare const MODEL_ROOT_KEY = "model";
19
21
  /** Collapse keys are strings so one set holds every branch of both trees. */
@@ -24,12 +26,30 @@ export declare const layerKey: (layer: string) => string;
24
26
  export declare const modelFolderKey: (folder: string) => string;
25
27
  /** Where a subject with no resolved layer is grouped, last. */
26
28
  export declare const UNLAYERED = "unlayered";
29
+ /**
30
+ * How a row relates to the pending changeset (ADR 0114). `"new"` is a staged
31
+ * `write-view` at a path nothing has landed; `"overwrite"` is one at a path a
32
+ * landed view occupies; `"delete"` is a staged `delete-view`. A landed row
33
+ * nothing pending touches carries `null`.
34
+ */
35
+ export type ViewRowStaging = "new" | "overwrite" | "delete";
27
36
  export interface ViewTreeRow {
28
37
  readonly id: string;
29
38
  readonly title: string;
30
39
  readonly path: string;
31
- readonly subjectCount: number;
40
+ /**
41
+ * `null` when nothing has measured it: a staged NEW view has no landed
42
+ * document, and resolving its query needs the semantic graph the browser
43
+ * does not hold — and, while the tree filter narrows, every landed view
44
+ * but the active one, whose subjects live only in that same graph (#317).
45
+ * A number is either the server's measure of what LANDED (a staged
46
+ * overwrite keeps the landed count, the same staleness story
47
+ * `VisualViewSummary.subjectCount` already tells) or, on the active row,
48
+ * a count of the drawn subjects that survive the typed filter.
49
+ */
50
+ readonly subjectCount: number | null;
32
51
  readonly active: boolean;
52
+ readonly staged: ViewRowStaging | null;
33
53
  }
34
54
  export interface ViewTreeFolder {
35
55
  readonly key: string;
@@ -99,17 +119,30 @@ export declare const matchesFilter: (label: string, filterText: string) => boole
99
119
  export declare const folderOf: (view: VisualViewSummary) => string;
100
120
  export interface ViewTreeInput {
101
121
  readonly views: readonly VisualViewSummary[];
122
+ /**
123
+ * The pending changeset's view operations, merged over `views` so the rail
124
+ * shows the reviewer's own staged intent beside landed truth (ADR 0114).
125
+ * A staged `write-view` at a new path becomes a row; one at a landed path
126
+ * marks that row and shows what WILL land; a staged `delete-view` marks the
127
+ * row rather than hiding it. Discarding an operation removes it from this
128
+ * list, which is the whole revert — the tree derives, it never remembers.
129
+ */
130
+ readonly stagedOperations: readonly VisualViewOperation[];
102
131
  readonly activeViewId: string;
103
132
  /**
104
- * How many subjects the active view is drawing right now, from the standing
105
- * filter's match set. The server's `subjectCount` was true when the frame
106
- * carrying it was sent; this one is true of the canvas beside it, which is
107
- * the number the reviewer can check by looking.
133
+ * The subjects the active view is drawing right now, from the standing
134
+ * filter's match set, or `null` when nothing is filtering the canvas. The
135
+ * subjects themselves rather than their count (#317): the active row's
136
+ * number is how many of them survive the typed filter, which a
137
+ * pre-computed count could not answer. Unfiltered it is their plain
138
+ * length — the number the reviewer can check by looking, where the
139
+ * server's `subjectCount` was only true when the frame carrying it was
140
+ * sent.
108
141
  */
109
- readonly activeSubjectCount: number | null;
142
+ readonly activeSubjects: readonly FilterableSubject[] | null;
110
143
  readonly filterText: string;
111
144
  }
112
- export declare const buildViewTree: ({ views, activeViewId, activeSubjectCount, filterText, }: ViewTreeInput) => ViewTree;
145
+ export declare const buildViewTree: ({ views, stagedOperations, activeViewId, activeSubjects, filterText, }: ViewTreeInput) => ViewTree;
113
146
  export interface ModelTreeInput {
114
147
  readonly nodes: readonly CanvasNode[];
115
148
  /**
@@ -1,4 +1,4 @@
1
- import type { VisualViewSummary } from "../adapters/visual/protocol-contract.js";
1
+ import type { VisualViewOperation, VisualViewSummary } from "../adapters/visual/protocol-contract.js";
2
2
  import type { CanvasNode } from "../graph-projection.js";
3
3
  /**
4
4
  * A right-click on a row, in viewport coordinates. The rail reports which row
@@ -14,6 +14,13 @@ export type TreeRowMenu = (row: {
14
14
  }) => void;
15
15
  export interface ViewTreeProps {
16
16
  readonly views: readonly VisualViewSummary[];
17
+ /**
18
+ * The pending changeset's view operations, merged over `views` by
19
+ * `buildViewTree` so staged intent is visible beside landed truth
20
+ * (ADR 0114). Derived from the changeset at the call site, never stored:
21
+ * discarding the operation is the revert.
22
+ */
23
+ readonly stagedViewOperations: readonly VisualViewOperation[];
17
24
  readonly activeViewId: string;
18
25
  /** Every subject the workspace declares, filtered or not. */
19
26
  readonly nodes: readonly CanvasNode[];
@@ -28,5 +35,7 @@ export interface ViewTreeProps {
28
35
  readonly onNewView: () => void;
29
36
  readonly onSelectSubject: (id: string) => void;
30
37
  readonly onRowMenu: TreeRowMenu;
38
+ /** A viewer, not an author (#298): the new-view affordance is absent. */
39
+ readonly readOnly?: boolean;
31
40
  }
32
- export declare function ViewTree({ views, activeViewId, nodes, inViewIds, filterText, collapsed, onFilterChange, onToggle, onSelectView, onClearView, onNewView, onSelectSubject, onRowMenu, }: ViewTreeProps): import("react").JSX.Element;
41
+ export declare function ViewTree({ views, stagedViewOperations, activeViewId, nodes, inViewIds, filterText, collapsed, onFilterChange, onToggle, onSelectView, onClearView, onNewView, onSelectSubject, onRowMenu, readOnly, }: ViewTreeProps): import("react").JSX.Element;
@@ -1,5 +1,7 @@
1
1
  import type { CanvasEdge, CanvasGraph, CanvasNode } from "../graph-projection.js";
2
2
  import { type NestingKind } from "../nesting.js";
3
+ import { type LayoutDirection } from "../layout-direction.js";
4
+ import type { DecorationMap } from "./graph-canvas.js";
3
5
  import type { ContextMenuTarget } from "./context-menu-model.js";
4
6
  import type { BottomPanelTabId } from "./query-panel.js";
5
7
  export interface SelectedElement {
@@ -37,15 +39,59 @@ export interface ConnectionDraft {
37
39
  }
38
40
  export declare const normalizeSelectedElement: (node: CanvasNode) => SelectedElement;
39
41
  export declare const normalizeSelectedRelationship: (edge: CanvasEdge, nodeTitles: ReadonlyMap<string, string>) => SelectedRelationship;
42
+ /**
43
+ * What the imperative methods read at the moment they are called (#297,
44
+ * ADR 0118). A getter rather than a captured value, because the pointer is
45
+ * handed up once and the graph changes under it with every commit - a method
46
+ * bound to the model that existed at mount time would answer for a model
47
+ * that is gone.
48
+ */
49
+ export interface EditorPointerContext {
50
+ readonly graph: CanvasGraph | null;
51
+ readonly readOnly: boolean;
52
+ }
53
+ /**
54
+ * The host's way of pointing at the canvas (#297, ADR 0118): three methods,
55
+ * each the programmatic twin of a gesture the surface already has - a canvas
56
+ * tap, a palette pick, the inspector's Connect. Every method answers with
57
+ * whether it acted; false means nothing moved - the id named nothing, the
58
+ * model has not arrived, or the mount is a viewer (#298) and the gesture
59
+ * would have reached for the pen.
60
+ */
61
+ export interface EditorPointer {
62
+ readonly select: (subjectId: string) => boolean;
63
+ readonly openDraft: (options?: {
64
+ readonly kind?: string;
65
+ }) => boolean;
66
+ readonly startConnection: (fromSubjectId: string) => boolean;
67
+ /**
68
+ * Replaces the host's per-subject marks wholesale (#314, ADR 0119) - the
69
+ * map is the unit of exchange, never merged into what stood. Unlike its
70
+ * siblings this one needs no model and no pen: the marks are client state
71
+ * drawn when the graph (or its successor) is on screen, and decorating is
72
+ * reading, so a viewer accepts them too. It answers false only where every
73
+ * method does - a handle before the shell's first render or after disposal.
74
+ */
75
+ readonly setDecorations: (decorations: DecorationMap) => boolean;
76
+ }
77
+ /**
78
+ * Builds the pointer over the shell's own dispatchers. Nothing here is a
79
+ * second write path: every method dispatches the same workspace action its
80
+ * on-screen twin dispatches, so what a host can reach is exactly what a
81
+ * reviewer can reach, and anything staged still lands through the changeset.
82
+ */
83
+ export declare const editorPointerFor: (context: () => EditorPointerContext, dispatch: (action: VisualWorkspaceAction) => void, seedDraftKind: (kind: string | undefined) => void, replaceDecorations: (decorations: DecorationMap) => void) => EditorPointer;
40
84
  /**
41
85
  * The right column's sections, in the order they stack (#249, ADR-free: the
42
86
  * design settles the order and nothing derives it).
43
87
  *
44
- * Chat is last because it is pinned at the foot and owns the session's own
45
- * control - the reviewer ends the conversation beside the conversation, not
46
- * from a strip that carries identity and nothing else.
88
+ * The palette leads (#295): it is the tool that makes subjects, and tools
89
+ * read above inspection - a reviewer reaches for what to add before what is
90
+ * selected. Chat is last because it is pinned at the foot and owns the
91
+ * session's own control - the reviewer ends the conversation beside the
92
+ * conversation, not from a strip that carries identity and nothing else.
47
93
  */
48
- export declare const RIGHT_SECTIONS: readonly ["properties", "changes", "chat"];
94
+ export declare const RIGHT_SECTIONS: readonly ["palette", "properties", "questions", "changes", "chat"];
49
95
  export type RightSectionId = (typeof RIGHT_SECTIONS)[number];
50
96
  /** How short a section may be dragged before it is just a header. */
51
97
  export declare const SECTION_MIN_HEIGHT = 96;
@@ -60,16 +106,26 @@ export declare const CONVERSATION_MIN_WIDTH = 320;
60
106
  export declare const CONVERSATION_MAX_WIDTH = 640;
61
107
  export interface VisualWorkspaceState {
62
108
  /**
63
- * The right column: one width, and the sections stacked inside it.
109
+ * The right column: one width, whether the reviewer has put the whole
110
+ * column away, and the sections stacked inside it.
64
111
  *
65
- * There is no longer an open/closed mode. The column is always drawn,
66
- * because the sections are collapsible one by one and a reviewer who wants
67
- * the canvas back shuts the sections rather than the column - which leaves
68
- * three headers saying what is behind them instead of a strip button
69
- * saying nothing (#249).
112
+ * The sections are collapsible one by one, and a reviewer who wants room
113
+ * shuts sections rather than the column - shut headers say what is behind
114
+ * them (#249). `hidden` is the further step for the moments that want the
115
+ * whole canvas - presenting, a projector, a laptop - and the reopen strip
116
+ * that stands in for the column carries what a shut header would have
117
+ * said: the unread count and a waiting choice (#294).
70
118
  */
71
119
  readonly conversation: {
72
120
  readonly width: number;
121
+ /**
122
+ * Whether the reviewer has put the whole column away (#294). A mode
123
+ * beside the width rather than a zero width, so the width stays what it
124
+ * was and reopening restores it - sections and splitters intact. The
125
+ * presenting moments are exactly when this is used, and presentation
126
+ * must not cost the reviewer their layout.
127
+ */
128
+ readonly hidden: boolean;
73
129
  readonly unread: number;
74
130
  /** The sections the reviewer has shut. Held as what is CLOSED so a
75
131
  * section added later arrives open rather than hidden behind a default
@@ -158,10 +214,18 @@ export interface VisualWorkspaceState {
158
214
  * alone - the behaviour that shipped before a view could say.
159
215
  */
160
216
  readonly nesting: readonly NestingKind[];
217
+ /**
218
+ * Which way the active view runs its layers (#274, ADR 0121). A view that
219
+ * says nothing runs `DEFAULT_DIRECTION`, the same rule `nesting` follows:
220
+ * silence restores the default rather than carrying the previous view's
221
+ * answer across.
222
+ */
223
+ readonly direction: LayoutDirection;
161
224
  readonly layout: "layered";
162
225
  readonly showLifecycle: boolean;
163
226
  readonly showEvidence: boolean;
164
227
  readonly showOwnership: boolean;
228
+ readonly showNudges: boolean;
165
229
  }
166
230
  export type VisualWorkspaceAction = {
167
231
  readonly type: "section.toggled";
@@ -173,6 +237,8 @@ export type VisualWorkspaceAction = {
173
237
  } | {
174
238
  readonly type: "conversation.resized";
175
239
  readonly width: number;
240
+ } | {
241
+ readonly type: "conversation.toggled";
176
242
  } | {
177
243
  readonly type: "viewport.resized";
178
244
  readonly viewportWidth: number;
@@ -236,12 +302,15 @@ export type VisualWorkspaceAction = {
236
302
  } | {
237
303
  readonly type: "nesting.set";
238
304
  readonly nesting: readonly NestingKind[];
305
+ } | {
306
+ readonly type: "direction.set";
307
+ readonly direction: LayoutDirection;
239
308
  } | {
240
309
  readonly type: "layout.set";
241
310
  readonly layout: "layered";
242
311
  } | {
243
312
  readonly type: "presentation.toggled";
244
- readonly flag: "showLifecycle" | "showEvidence" | "showOwnership";
313
+ readonly flag: "showLifecycle" | "showEvidence" | "showOwnership" | "showNudges";
245
314
  readonly value: boolean;
246
315
  } | {
247
316
  readonly type: "model.replaced";
@@ -252,6 +321,7 @@ export type VisualWorkspaceAction = {
252
321
  export declare const presentationActionsFor: (presentation: {
253
322
  readonly layout?: "layered";
254
323
  readonly nesting?: readonly NestingKind[];
324
+ readonly direction?: LayoutDirection;
255
325
  readonly showLifecycle?: boolean;
256
326
  readonly showEvidence?: boolean;
257
327
  readonly showOwnership?: boolean;
@@ -6,6 +6,7 @@ export interface WorkspaceManifest {
6
6
  readonly profiles: readonly string[];
7
7
  readonly projections: readonly string[];
8
8
  readonly adapterMappings: readonly string[];
9
+ readonly patterns?: readonly string[];
9
10
  readonly evidence?: readonly string[];
10
11
  readonly contracts?: readonly string[];
11
12
  }
@@ -15,6 +16,7 @@ export interface ResolvedWorkspace {
15
16
  readonly profiles: readonly string[];
16
17
  readonly projections: readonly string[];
17
18
  readonly adapterMappings: readonly string[];
19
+ readonly patterns: readonly string[];
18
20
  readonly evidence: readonly string[];
19
21
  readonly contracts: readonly string[];
20
22
  }
@@ -6,6 +6,7 @@ export interface WorkspaceManifest {
6
6
  readonly profiles: readonly string[];
7
7
  readonly projections: readonly string[];
8
8
  readonly adapterMappings: readonly string[];
9
+ readonly patterns?: readonly string[];
9
10
  readonly evidence?: readonly string[];
10
11
  readonly contracts?: readonly string[];
11
12
  }
@@ -15,6 +16,7 @@ export interface ResolvedWorkspace {
15
16
  readonly profiles: readonly string[];
16
17
  readonly projections: readonly string[];
17
18
  readonly adapterMappings: readonly string[];
19
+ readonly patterns: readonly string[];
18
20
  readonly evidence: readonly string[];
19
21
  readonly contracts: readonly string[];
20
22
  }
package/dist/workspace.js CHANGED
@@ -106,6 +106,7 @@ export function loadWorkspaceManifest(source, cwd) {
106
106
  profiles: expand('profiles', 'profile', value.profiles),
107
107
  projections: expand('projections', 'projection', value.projections),
108
108
  adapterMappings: expand('adapterMappings', 'adapter mapping', value.adapterMappings),
109
+ patterns: expand('patterns', 'pattern', value.patterns ?? []),
109
110
  evidence: expand('evidence', 'evidence', value.evidence ?? []),
110
111
  contracts: expand('contracts', 'Core contract', value.contracts ?? []),
111
112
  };
@@ -298,6 +298,87 @@ const editor = mountEditor(document.querySelector('#editor')!, {
298
298
  editor.unmount()
299
299
  ```
300
300
 
301
+ ### The questions are yours
302
+
303
+ The questions section evaluates the shipped `core-enrichment` catalogue by
304
+ default. A host with its own interrogation supplies its own instead (#328):
305
+
306
+ ```ts
307
+ const editor = mountEditor(element, {
308
+ store,
309
+ workspace,
310
+ sections: ['palette', 'properties', 'questions', 'changes'],
311
+ catalogue: { path: 'catalogues/consulting.yaml', source: catalogueText },
312
+ dismissed: [
313
+ { questionId: 'register-fidelity', subject: 'crm-integration' },
314
+ { questionId: 'engagement-framing' },
315
+ ],
316
+ })
317
+ ```
318
+
319
+ The division this draws is deliberate. **The engine is yarramate's and so is
320
+ the UI; the questions belong to whoever adopted it.** `core-enrichment` is a
321
+ general modelling interview — right for yarramate's own CLI, and right for a
322
+ host with no domain of its own — and wrong for a product whose interview is
323
+ about its own subject matter. Inheriting it would mean asking a consultant
324
+ about modelling hygiene in the pane where they are asking a client about
325
+ sign-off.
326
+
327
+ `catalogue` takes bytes, and a catalogue that does not load leaves the overlay
328
+ absent rather than failing the mount: the overlay is a garnish on the model,
329
+ and a model frame must not be blocked by it.
330
+
331
+ `dismissed` is what the host has already dealt with. A supplied catalogue
332
+ alone does not cover this — the editor evaluates the catalogue itself and
333
+ cannot know that a reviewer set a question aside, with a reason, recorded
334
+ somewhere the editor cannot see, so the pane would go on asking a question the
335
+ host's own product had answered. Naming a `subject` dismisses the question for
336
+ that subject alone; omitting it dismisses the question wherever it appears.
337
+
338
+ Dismissal decides what the **pane draws** and nothing else. The model is
339
+ untouched and `ask --open` still reports the question, because the interview is
340
+ not the editor's to settle.
341
+
342
+ Pass `readOnly: true` to mount a viewer over the same surface — for a frozen
343
+ published snapshot, say ([ADR 0117](adr/0117-a-mounted-editor-can-refuse-the-pen.md)).
344
+ The reviewer still selects, filters, navigates views and reads questions and
345
+ properties (as values, not forms), but every affordance that stages or commits
346
+ is absent rather than disabled: no Add subject, no palette or changes
347
+ sections, no Connect/Delete, no view create/rename/delete, and drags move
348
+ nodes without writing layouts. With no `sections` named, a read-only mount
349
+ defaults to `['properties', 'questions']`. This is a UI posture only — pair it
350
+ with a store that refuses writes; the two defenses are independent.
351
+ `mountEditorWith` takes the same flag as its trailing parameter.
352
+
353
+ The returned handle can also point at the canvas
354
+ ([ADR 0118](adr/0118-the-host-can-point-at-the-canvas.md)):
355
+ `select(subjectId)` selects a concept or relationship exactly as a canvas tap
356
+ would, which also scopes the Open questions section to it;
357
+ `openDraft({ kind })` opens the Add-subject dialog with the kind preselected
358
+ the way a palette pick seeds it (omit `kind` for the plain no-default form);
359
+ and `startConnection(fromSubjectId)` arms the Connect flow from that subject —
360
+ the relationship-with-one-endpoint-fixed affordance an interrogation trigger
361
+ describes. Each returns `false`, rather than throwing, when it moved nothing:
362
+ an id the current model does not name, a model that has not arrived yet, or —
363
+ for `openDraft` and `startConnection` — a read-only mount. These are the
364
+ programmatic twins of tap, palette and Connect, never a second write path:
365
+ anything they lead to still stages through the changeset and commits through
366
+ the same validated batch.
367
+
368
+ The viewer also accepts per-subject marks
369
+ ([ADR 0119](adr/0119-the-viewer-accepts-the-hosts-marks.md)): pass
370
+ `decorations: { [subjectId]: 'added' | 'removed' | 'changed' }` — concepts and
371
+ relationships alike — and the canvas renders them as visual treatments (added
372
+ an eucalyptus border, removed a quiet dashed one, changed ochre; a fault still
373
+ outranks any mark). Comparison semantics stay on your side of the seam: the
374
+ viewer never diffs, it draws the map it is handed, so what a mark means — and
375
+ any legend saying so — is yours. The option is the initial map; the handle's
376
+ `setDecorations(decorations)` replaces it wholesale (never a merge, `{}`
377
+ clears) for a live comparison, works under `readOnly` and before the first
378
+ model frame, and ids the model does not name are silently inert. It shares the
379
+ pointer methods' one false window: before the shell's first render, or after
380
+ unmount.
381
+
301
382
  `store` is the caller's synchronous `SourceStore`; `workspace` is the caller's
302
383
  pre-resolved `ResolvedWorkspace`. The local host compiles, projects, filters,
303
384
  commits and saves layouts over that store. A changeset's model and view writes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yarramate",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Tool-neutral semantic architecture engine and guided methodology",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -65,6 +65,7 @@
65
65
  "./visual-app/styles.css": "./dist/visual-app-lib/styles.css",
66
66
  "./schema/document": "./schema/yarramate-document.schema.json",
67
67
  "./schema/profile": "./schema/yarramate-profile.schema.json",
68
+ "./schema/pattern": "./schema/yarramate-pattern.schema.json",
68
69
  "./schema/projection": "./schema/yarramate-projection.schema.json",
69
70
  "./schema/projection-result": "./schema/yarramate-projection-result.schema.json",
70
71
  "./schema/adapter-mapping": "./schema/yarramate-adapter-mapping.schema.json",
@@ -533,6 +533,32 @@
533
533
  "const": "unscoped-succession"
534
534
  }
535
535
  }
536
+ },
537
+ {
538
+ "type": "object",
539
+ "additionalProperties": false,
540
+ "description": "The workspace holds at least one concept (#334, ADR 0125). The guard a late wave needs to say \"only once the model has substance\": an empty model is not an architecture at rest, it has not started, and asking it how the planned architecture becomes real greets someone ahead of question one.",
541
+ "required": [
542
+ "condition"
543
+ ],
544
+ "properties": {
545
+ "condition": {
546
+ "const": "has-any-subject"
547
+ }
548
+ }
549
+ },
550
+ {
551
+ "type": "object",
552
+ "additionalProperties": false,
553
+ "description": "The workspace's evidence overlay records observations and every one is a frictionless confirmation: no contradicted, unknown, or not-observed result, and no recorded search (ADR 0107). A discovery that never records anything but success never tested a claim it might fail. The one condition that reads the evidence overlay rather than the compiled graph; where the evaluating consumer supplies no overlay it stays quiet (ADR 0120).",
554
+ "required": [
555
+ "condition"
556
+ ],
557
+ "properties": {
558
+ "condition": {
559
+ "const": "unchallenged-evidence"
560
+ }
561
+ }
536
562
  }
537
563
  ]
538
564
  },
@@ -151,6 +151,20 @@
151
151
  "$ref": "#/$defs/constraint"
152
152
  }
153
153
  },
154
+ "parts": {
155
+ "description": "The subjects that fill this instance's pattern slots, by slot name (ADR 0123). Valid only on a kind that has a pattern; the compiler mints the pattern's wiring between the instance and what these name, and refuses any authored relationship that contradicts it. Binding, not generation: a part keeps its own name, owner, evidence and outside edges.",
156
+ "type": "object",
157
+ "minProperties": 1,
158
+ "additionalProperties": false,
159
+ "propertyNames": {
160
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
161
+ },
162
+ "patternProperties": {
163
+ "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$": {
164
+ "$ref": "#/$defs/reference"
165
+ }
166
+ }
167
+ },
154
168
  "references": {
155
169
  "$ref": "#/$defs/identifiedReferences"
156
170
  },
@@ -66,6 +66,7 @@
66
66
  "required": [
67
67
  "id",
68
68
  "name",
69
+ "opened",
69
70
  "questions"
70
71
  ],
71
72
  "properties": {
@@ -77,6 +78,10 @@
77
78
  "type": "string",
78
79
  "minLength": 1
79
80
  },
81
+ "opened": {
82
+ "description": "Whether this wave's gate is met (#334, ADR 0125). A wave with no `opensWhen` is always true. A wave reported false asks nothing and carries no questions: they are premature rather than answered, which is the third state a progress rail needs so a closed wave is not counted as done. Its questions are excluded from `summary` for the same reason.",
83
+ "type": "boolean"
84
+ },
80
85
  "questions": {
81
86
  "type": "array",
82
87
  "items": {
@@ -532,6 +537,32 @@
532
537
  "const": "unscoped-succession"
533
538
  }
534
539
  }
540
+ },
541
+ {
542
+ "type": "object",
543
+ "additionalProperties": false,
544
+ "description": "The workspace holds at least one concept (#334, ADR 0125). The guard a late wave needs to say \"only once the model has substance\": an empty model is not an architecture at rest, it has not started, and asking it how the planned architecture becomes real greets someone ahead of question one.",
545
+ "required": [
546
+ "condition"
547
+ ],
548
+ "properties": {
549
+ "condition": {
550
+ "const": "has-any-subject"
551
+ }
552
+ }
553
+ },
554
+ {
555
+ "type": "object",
556
+ "additionalProperties": false,
557
+ "description": "The workspace's evidence overlay records observations and every one is a frictionless confirmation: no contradicted, unknown, or not-observed result, and no recorded search (ADR 0107). A discovery that never records anything but success never tested a claim it might fail. The one condition that reads the evidence overlay rather than the compiled graph; where the evaluating consumer supplies no overlay it stays quiet (ADR 0120).",
558
+ "required": [
559
+ "condition"
560
+ ],
561
+ "properties": {
562
+ "condition": {
563
+ "const": "unchallenged-evidence"
564
+ }
565
+ }
535
566
  }
536
567
  ]
537
568
  },