@synap-core/workspace-templates 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -863,6 +863,65 @@ interface LayerTemplateGraphInput {
863
863
  declare function bundledEdgesOf(slug: string): readonly TemplateDependency[];
864
864
  declare function layerTemplateGraph(input: LayerTemplateGraphInput): CompositionGraph;
865
865
 
866
+ /**
867
+ * Cross-template CONCEPT guard — catches vocabulary forks between templates.
868
+ *
869
+ * WHY THIS EXISTS (read before weakening or deleting it):
870
+ * `business-developer.yaml` introduced workspace-scoped `crm-lead`/`crm-client`
871
+ * for the same real-world concept that `foundation.yaml`/`crm.yaml` declare as
872
+ * pod-wide shared `lead`/`client`. Nothing failed. Three reasons:
873
+ *
874
+ * 1. `build.mjs`'s `validate()` runs PER FILE, inside the read loop, so it
875
+ * structurally cannot see that another template already owns `lead`.
876
+ * 2. The only vocabulary rule that exists (`template-shared-role-ssot.test.ts`
877
+ * in synap-backend) polices a HARDCODED five-slug set — and it FORBIDS
878
+ * declaring one of those slugs workspace-scoped, which is exactly what the
879
+ * two-tier design legitimately needed. So RENAMING was the path of least
880
+ * resistance: `crm-lead` is outside the set and passes silently. That rule
881
+ * polices SPELLING, not MEANING.
882
+ * 3. That test also cannot run in CI — it resolves this package across a
883
+ * sibling-repo relative path that CI never checks out. It is green on a dev
884
+ * machine and mute everywhere else.
885
+ *
886
+ * This guard lives HERE, in the repo that owns the template source, and runs in
887
+ * this repo's CI (`pnpm test`) and in `pnpm build` / `prepublishOnly` via
888
+ * `scripts/check-concepts.mjs`. It is deliberately NOT a hardcoded slug list —
889
+ * that is the pattern that failed.
890
+ *
891
+ * WHAT IT DOES NOT DO: it does not guess. There is no fuzzy name matching, no
892
+ * edit distance, no synonym table. A guesser nobody trusts is worse than no
893
+ * guard. It uses two exact, falsifiable signals only — see the rules below.
894
+ */
895
+
896
+ interface ConceptViolation {
897
+ rule: "concept-collision" | "alias-shape-divergence" | "stale-allowlist";
898
+ /** The concept term or semantic slug the finding is about. */
899
+ concept: string;
900
+ message: string;
901
+ }
902
+ /**
903
+ * DECLARED, INTENTIONAL divergences — the escape hatch, and deliberately the
904
+ * SECONDARY mechanism. An entry here is an author saying "yes, two spellings of
905
+ * one concept, on purpose, and here is why". Keep it tiny; every entry must
906
+ * carry its WHY. A key that stops matching anything FAILS the guard, so this
907
+ * list cannot rot into a graveyard.
908
+ *
909
+ * Key = the two colliding profile slugs, sorted, joined by "|".
910
+ */
911
+ declare const DECLARED_DIVERGENCES: Record<string, string>;
912
+ /**
913
+ * The bare noun a concept identifier ends in: `crm.lead` → `lead`,
914
+ * `crm-lead` → `lead`, `lead` → `lead`.
915
+ *
916
+ * This is EXACT terminal-token equality on the author's own qualified name —
917
+ * not a similarity heuristic. `crm-lead` and `lead` collide because the author
918
+ * literally wrote the same noun; `prospect` and `lead` do not collide, and that
919
+ * is honest: no guard can know they mean the same thing.
920
+ */
921
+ declare function conceptTerm(semanticSlug: string): string;
922
+ declare function findConceptViolations(templates?: Record<string, WorkspaceYaml>): ConceptViolation[];
923
+ declare function formatConceptViolations(violations: ConceptViolation[]): string;
924
+
866
925
  /**
867
926
  * @synap-core/workspace-templates — Definition adapter
868
927
  *
@@ -1067,4 +1126,4 @@ interface PackageDefinitionOutput {
1067
1126
  */
1068
1127
  declare function toPackageDefinition(slugOrYaml: string | WorkspaceYaml): PackageDefinitionOutput;
1069
1128
 
1070
- export { type AssembleCatalogInput, type BentoViewBlockWire, type BentoWidgetBlockWire, type BundledCatalogEntry, type BundledEntry, type CatalogEntry, type CompositionEdge, type CompositionGraph, type CompositionInstalledEntry, type CompositionNode, type CompositionOverlay, type CpPackageRow, type InstalledTemplate, type LayerTemplateGraphInput, type MergeCatalogInput, type MergeCatalogResult, PACKAGE_DEPENDENCY_KINDS, type PackageDefinitionOutput, type PackageDependencyKind, type RemoteCatalogEntry, type RemoteEntry, type RemoteSourceStatus, type SplitBentoBlocks, type TemplateActionPlacement, type TemplateAppKind, type TemplateAppRef, type TemplateAutomation, type TemplateBentoBlock, type TemplateDefaultSource, type TemplateDependency, type TemplateDisplayTemplate, type TemplateEntityLink, type TemplateExtendsRef, type TemplateIntegration, type TemplateLayoutConfig, type TemplateMeta, type TemplateOnboarding, type TemplateOnboardingCollectTarget, type TemplatePlaybook, type TemplatePlaybookParam, type TemplateProfile, type TemplateProfileEntityBentoTemplates, type TemplateProperty, type TemplateSeedEntity, type TemplateSidebarItem, type TemplateSidebarSurface, type TemplateSourceRole, type TemplateSuggestedRelation, type TemplateUpdateCheck, type TemplateView, type ToDefinitionResult, WORKSPACE_TEMPLATES, type WorkspaceDefinitionInput, type WorkspaceTemplateSlug, type WorkspaceYaml, assembleCatalog, bundledEdgesOf, computeTemplateUpdates, getWorkspaceTemplate, isHashVersion, layerTemplateGraph, listPublicTemplates, listWorkspaceTemplates, mergeCatalog, rowToRemoteEntry, splitBentoBlocks, toPackageDefinition, toWorkspaceDefinition };
1129
+ export { type AssembleCatalogInput, type BentoViewBlockWire, type BentoWidgetBlockWire, type BundledCatalogEntry, type BundledEntry, type CatalogEntry, type CompositionEdge, type CompositionGraph, type CompositionInstalledEntry, type CompositionNode, type CompositionOverlay, type ConceptViolation, type CpPackageRow, DECLARED_DIVERGENCES, type InstalledTemplate, type LayerTemplateGraphInput, type MergeCatalogInput, type MergeCatalogResult, PACKAGE_DEPENDENCY_KINDS, type PackageDefinitionOutput, type PackageDependencyKind, type RemoteCatalogEntry, type RemoteEntry, type RemoteSourceStatus, type SplitBentoBlocks, type TemplateActionPlacement, type TemplateAppKind, type TemplateAppRef, type TemplateAutomation, type TemplateBentoBlock, type TemplateDefaultSource, type TemplateDependency, type TemplateDisplayTemplate, type TemplateEntityLink, type TemplateExtendsRef, type TemplateIntegration, type TemplateLayoutConfig, type TemplateMeta, type TemplateOnboarding, type TemplateOnboardingCollectTarget, type TemplatePlaybook, type TemplatePlaybookParam, type TemplateProfile, type TemplateProfileEntityBentoTemplates, type TemplateProperty, type TemplateSeedEntity, type TemplateSidebarItem, type TemplateSidebarSurface, type TemplateSourceRole, type TemplateSuggestedRelation, type TemplateUpdateCheck, type TemplateView, type ToDefinitionResult, WORKSPACE_TEMPLATES, type WorkspaceDefinitionInput, type WorkspaceTemplateSlug, type WorkspaceYaml, assembleCatalog, bundledEdgesOf, computeTemplateUpdates, conceptTerm, findConceptViolations, formatConceptViolations, getWorkspaceTemplate, isHashVersion, layerTemplateGraph, listPublicTemplates, listWorkspaceTemplates, mergeCatalog, rowToRemoteEntry, splitBentoBlocks, toPackageDefinition, toWorkspaceDefinition };
package/dist/index.js CHANGED
@@ -6798,6 +6798,7 @@ var DATA = {
6798
6798
  "description": "A person or company actively being qualified or contacted by this CRM workspace.",
6799
6799
  "scope": "WORKSPACE",
6800
6800
  "profileKind": "role",
6801
+ "semanticSlug": "crm.lead",
6801
6802
  "applicableKinds": [
6802
6803
  "person",
6803
6804
  "company"
@@ -6905,6 +6906,7 @@ var DATA = {
6905
6906
  "description": "A CRM lead converted by a won commercial proposal and ready for handoff.",
6906
6907
  "scope": "WORKSPACE",
6907
6908
  "profileKind": "role",
6909
+ "semanticSlug": "crm.client",
6908
6910
  "applicableKinds": [
6909
6911
  "person",
6910
6912
  "company"
@@ -13260,7 +13262,7 @@ var DATA = {
13260
13262
  ]
13261
13263
  },
13262
13264
  "bento": {
13263
- "viewName": "Ecosystem Map",
13265
+ "viewName": "Ecosystem Home",
13264
13266
  "blocks": [
13265
13267
  {
13266
13268
  "kind": "view",
@@ -22507,18 +22509,92 @@ var DATA = {
22507
22509
  },
22508
22510
  "profiles": [
22509
22511
  {
22510
- "slug": "meeting",
22511
- "displayName": "Meeting",
22512
- "icon": "video",
22512
+ "slug": "person",
22513
+ "displayName": "Person",
22514
+ "icon": "user",
22515
+ "color": "amber",
22516
+ "description": "A meeting attendee.",
22517
+ "properties": [
22518
+ {
22519
+ "slug": "email",
22520
+ "label": "Email",
22521
+ "valueType": "string",
22522
+ "inputType": "email"
22523
+ }
22524
+ ]
22525
+ },
22526
+ {
22527
+ "slug": "task",
22528
+ "displayName": "Task",
22529
+ "icon": "check-square",
22530
+ "color": "blue",
22531
+ "description": "An action item that came out of a meeting.",
22532
+ "properties": [
22533
+ {
22534
+ "slug": "status",
22535
+ "label": "Status",
22536
+ "valueType": "string",
22537
+ "inputType": "select",
22538
+ "enumValues": [
22539
+ "todo",
22540
+ "in-progress",
22541
+ "done",
22542
+ "cancelled"
22543
+ ],
22544
+ "constraints": {
22545
+ "defaultValue": "todo"
22546
+ }
22547
+ },
22548
+ {
22549
+ "slug": "dueDate",
22550
+ "label": "Due",
22551
+ "valueType": "date",
22552
+ "inputType": "date"
22553
+ }
22554
+ ]
22555
+ },
22556
+ {
22557
+ "slug": "note",
22558
+ "displayName": "Note",
22559
+ "icon": "file-text",
22513
22560
  "color": "violet",
22514
- "description": "A meeting \u2014 its date, attendees, and what came out of it.",
22561
+ "description": "A note or summary captured from a meeting.",
22515
22562
  "properties": [
22516
22563
  {
22517
- "slug": "meetingDate",
22518
- "label": "Date",
22564
+ "slug": "content",
22565
+ "label": "Content",
22566
+ "valueType": "string",
22567
+ "inputType": "textarea"
22568
+ }
22569
+ ]
22570
+ },
22571
+ {
22572
+ "slug": "event",
22573
+ "displayName": "Event",
22574
+ "icon": "calendar",
22575
+ "color": "sky",
22576
+ "description": "A calendar event \u2014 the thing a meeting actually is.",
22577
+ "properties": [
22578
+ {
22579
+ "slug": "startDate",
22580
+ "label": "Start",
22519
22581
  "valueType": "date",
22520
22582
  "inputType": "date"
22521
- },
22583
+ }
22584
+ ]
22585
+ },
22586
+ {
22587
+ "slug": "meeting",
22588
+ "displayName": "Meeting",
22589
+ "icon": "video",
22590
+ "color": "violet",
22591
+ "scope": "shared",
22592
+ "profileKind": "role",
22593
+ "applicableKinds": [
22594
+ "event"
22595
+ ],
22596
+ "description": "The meeting hat an event wears \u2014 platform, transcript, and what came out of it.",
22597
+ "properties": [
22522
22598
  {
22523
22599
  "slug": "platform",
22524
22600
  "label": "Platform",
@@ -22532,12 +22608,6 @@ var DATA = {
22532
22608
  "other"
22533
22609
  ]
22534
22610
  },
22535
- {
22536
- "slug": "attendees",
22537
- "label": "Attendees",
22538
- "valueType": "array",
22539
- "inputType": "text"
22540
- },
22541
22611
  {
22542
22612
  "slug": "status",
22543
22613
  "label": "Status",
@@ -22577,19 +22647,44 @@ var DATA = {
22577
22647
  "config": {
22578
22648
  "columns": [
22579
22649
  "title",
22580
- "meetingDate",
22650
+ "startDate",
22581
22651
  "platform",
22582
22652
  "status"
22583
22653
  ]
22654
+ }
22655
+ },
22656
+ {
22657
+ "name": "Needs Processing",
22658
+ "slug": "meetings-unprocessed",
22659
+ "type": "list",
22660
+ "scopeProfileSlug": "meeting",
22661
+ "description": "Meetings that happened but haven't been imported and summarized yet.",
22662
+ "config": {
22663
+ "filterBy": {
22664
+ "field": "status",
22665
+ "operator": "is",
22666
+ "value": "completed"
22667
+ }
22584
22668
  },
22585
22669
  "defaultView": true
22586
22670
  },
22671
+ {
22672
+ "name": "Meeting Notes",
22673
+ "slug": "meeting-notes",
22674
+ "type": "list",
22675
+ "scopeProfileSlug": "note",
22676
+ "description": "Summaries and notes produced from meetings."
22677
+ },
22587
22678
  {
22588
22679
  "name": "Meetings Calendar",
22589
22680
  "slug": "meetings-calendar",
22590
22681
  "type": "calendar",
22591
22682
  "scopeProfileSlug": "meeting",
22592
- "description": "Meetings on a calendar, keyed on their date."
22683
+ "description": "Meetings on a calendar, keyed on their date.",
22684
+ "config": {
22685
+ "dateField": "properties.startDate",
22686
+ "colorBy": "status"
22687
+ }
22593
22688
  },
22594
22689
  {
22595
22690
  "name": "Action Items",
@@ -22673,27 +22768,29 @@ var DATA = {
22673
22768
  ],
22674
22769
  "blindSpots": [
22675
22770
  `Recurring meetings (standups, 1:1s) get skipped because they feel routine \u2014 but their action items are exactly what falls through the cracks. Don't let "it's just a standup" excuse skipping capture.
22676
- `
22771
+ `,
22772
+ `"Attendees" as a list of names is not attendee capture \u2014 it's a string. If the summary says "Sarah will send the deck" and Sarah isn't a tracked person linked to that task, the next meeting with Sarah starts from zero again. Resolve every attendee to a real person, or say out loud that you're choosing not to.
22773
+ `,
22774
+ "A summary nobody re-reads is theatre. The test for a good meeting note is whether someone who missed the call could act from it alone \u2014 if it needs the transcript to make sense, it isn't a summary yet.\n"
22677
22775
  ],
22678
- "bar": "A handful of recent meetings are logged with a real summary and at least one action item each, so the Import & Summarize playbook has something concrete to run against.\n"
22776
+ "bar": 'The PIPE is live, not the rows: transcript source and calendar connected, the Import & Summarize playbook run once end-to-end on a real recent meeting, and the output inspected together \u2014 a note a human would actually keep, tasks that are genuinely actionable, and attendees resolved to tracked people rather than name strings. If the first import produces vague tasks ("follow up"), fix the extraction bar now, not after fifty meetings.\n'
22679
22777
  },
22680
22778
  "collect": [
22681
22779
  {
22682
22780
  "profileSlug": "meeting",
22683
- "what": "Recent or upcoming meetings worth tracking.",
22781
+ "what": "Which recurring meetings are worth importing every time \u2014 the standing 1:1s, the weekly ops call. A watchlist, not a backlog.\n",
22684
22782
  "cardinality": "few",
22685
22783
  "keyFields": [
22686
- "meetingDate",
22687
22784
  "platform"
22688
22785
  ]
22689
22786
  }
22690
22787
  ],
22691
22788
  "openingQuestions": [
22692
- "What meetings do you want tracked \u2014 recurring ones, or just the important ones?",
22693
- "Where do your meetings live \u2014 Zoom, Google Meet, somewhere else?",
22694
- "Do you already record/transcribe them, or should that start now?"
22789
+ "Name a meeting from last week whose outcome you can't remember. What did that cost you?",
22790
+ "When a meeting produces an action item, where does it go today \u2014 and does it survive the week?",
22791
+ "Which recurring meeting would you cancel if you could see its action-item completion rate?"
22695
22792
  ],
22696
- "doneWhen": "A few meetings captured with a date and platform, at least one with a summary."
22793
+ "doneWhen": "Transcript source connected and one real meeting imported end-to-end \u2014 summary note, at least one action item, and attendees resolved to people.\n"
22697
22794
  },
22698
22795
  "app": {
22699
22796
  "kind": "stored",
@@ -22704,7 +22801,8 @@ var DATA = {
22704
22801
  "defaultApp": null,
22705
22802
  "pinnedApps": [
22706
22803
  "dashboard",
22707
- "data"
22804
+ "data",
22805
+ "chat"
22708
22806
  ],
22709
22807
  "sidebarItems": [
22710
22808
  {
@@ -22734,7 +22832,7 @@ var DATA = {
22734
22832
  ]
22735
22833
  },
22736
22834
  "bento": {
22737
- "viewName": "Meetings",
22835
+ "viewName": "Meetings Home",
22738
22836
  "blocks": [
22739
22837
  {
22740
22838
  "kind": "view",
@@ -23458,7 +23556,8 @@ var DATA = {
23458
23556
  "defaultApp": null,
23459
23557
  "pinnedApps": [
23460
23558
  "dashboard",
23461
- "data"
23559
+ "data",
23560
+ "chat"
23462
23561
  ],
23463
23562
  "sidebarItems": [
23464
23563
  {
@@ -23482,7 +23581,7 @@ var DATA = {
23482
23581
  ]
23483
23582
  },
23484
23583
  "bento": {
23485
- "viewName": "Campaigns",
23584
+ "viewName": "Outreach Home",
23486
23585
  "blocks": [
23487
23586
  {
23488
23587
  "kind": "view",
@@ -27010,6 +27109,109 @@ function layerTemplateGraph(input) {
27010
27109
  return { nodes, edges, layers, cycles };
27011
27110
  }
27012
27111
 
27112
+ // src/concept-guard.ts
27113
+ var DECLARED_DIVERGENCES = {
27114
+ // Two-tier lead vocabulary, documented at business-developer.yaml:1-16.
27115
+ // Foundation's `lead` is a SHARED pod-wide role: being a lead is true
27116
+ // everywhere. `crm-lead` is a WORKSPACE-scoped role so a person can be a lead
27117
+ // inside one CRM workspace without becoming a lead across the whole pod. The
27118
+ // tiers are intentionally separate rows; `semanticSlug: crm.lead` records the
27119
+ // kinship so the two are discoverable as one concept via getBySemanticSlug.
27120
+ "crm-lead|lead": "business-developer: workspace-tier lead, deliberately NOT pod-wide foundation `lead` (two-tier CRM design)",
27121
+ // Same reasoning for the post-sale side of the same lifecycle.
27122
+ "client|crm-client": "business-developer: workspace-tier client, deliberately NOT pod-wide foundation `client` (two-tier CRM design)"
27123
+ };
27124
+ function effectiveSemanticSlug(profile) {
27125
+ return profile.semanticSlug ?? profile.slug;
27126
+ }
27127
+ function conceptTerm(semanticSlug) {
27128
+ const tokens = semanticSlug.split(/[.\-_]/).filter(Boolean);
27129
+ return (tokens[tokens.length - 1] ?? semanticSlug).toLowerCase();
27130
+ }
27131
+ function declaredValue(profile, field) {
27132
+ const raw = profile[field];
27133
+ if (raw === void 0 || raw === null) return null;
27134
+ if (field === "applicableKinds") {
27135
+ return Array.isArray(raw) ? JSON.stringify([...raw].sort()) : null;
27136
+ }
27137
+ return JSON.stringify(String(raw).toLowerCase());
27138
+ }
27139
+ function pairKey(a, b) {
27140
+ return [a, b].sort().join("|");
27141
+ }
27142
+ function findConceptViolations(templates = WORKSPACE_TEMPLATES) {
27143
+ const violations = [];
27144
+ const all = [];
27145
+ for (const [templateSlug, template] of Object.entries(templates)) {
27146
+ for (const profile of template.profiles ?? []) {
27147
+ all.push({ template: templateSlug, profile });
27148
+ }
27149
+ }
27150
+ const roles = all.filter((r) => r.profile.profileKind === "role");
27151
+ const usedAllowlistKeys = /* @__PURE__ */ new Set();
27152
+ const reportedPairs = /* @__PURE__ */ new Set();
27153
+ for (let i = 0; i < roles.length; i++) {
27154
+ for (let j = i + 1; j < roles.length; j++) {
27155
+ const a = roles[i];
27156
+ const b = roles[j];
27157
+ if (a.template === b.template) continue;
27158
+ if (a.profile.slug === b.profile.slug) continue;
27159
+ const term = conceptTerm(effectiveSemanticSlug(a.profile));
27160
+ if (term !== conceptTerm(effectiveSemanticSlug(b.profile))) continue;
27161
+ const key = pairKey(a.profile.slug, b.profile.slug);
27162
+ if (key in DECLARED_DIVERGENCES) {
27163
+ usedAllowlistKeys.add(key);
27164
+ continue;
27165
+ }
27166
+ if (reportedPairs.has(key)) continue;
27167
+ reportedPairs.add(key);
27168
+ violations.push({
27169
+ rule: "concept-collision",
27170
+ concept: term,
27171
+ message: `two role profiles denote the concept "${term}" under different slugs: "${a.profile.slug}" (${a.template}) vs "${b.profile.slug}" (${b.template}). Either REUSE the existing slug, or \u2014 if the divergence is intentional \u2014 declare it: set an explicit \`semanticSlug\` on the new profile and add "${key}" to DECLARED_DIVERGENCES in src/concept-guard.ts with the reason.`
27172
+ });
27173
+ }
27174
+ }
27175
+ const bySemantic = /* @__PURE__ */ new Map();
27176
+ for (const ref of all) {
27177
+ const key = effectiveSemanticSlug(ref.profile);
27178
+ const bucket = bySemantic.get(key);
27179
+ if (bucket) bucket.push(ref);
27180
+ else bySemantic.set(key, [ref]);
27181
+ }
27182
+ for (const [semantic, group] of bySemantic) {
27183
+ if (new Set(group.map((r) => r.profile.slug)).size < 2) continue;
27184
+ for (const field of ["profileKind", "scope", "applicableKinds"]) {
27185
+ const claims = /* @__PURE__ */ new Map();
27186
+ for (const ref of group) {
27187
+ const value = declaredValue(ref.profile, field);
27188
+ if (value === null) continue;
27189
+ const existing = claims.get(value);
27190
+ if (!existing) claims.set(value, `${ref.template}:${ref.profile.slug}`);
27191
+ }
27192
+ if (claims.size > 1) {
27193
+ violations.push({
27194
+ rule: "alias-shape-divergence",
27195
+ concept: semantic,
27196
+ message: `profiles aliased to semanticSlug "${semantic}" disagree on \`${field}\`: ` + [...claims].map(([v, who]) => `${who} declares ${v}`).join(", ") + `. Aliased profiles assert one concept \u2014 make the shapes agree, or drop the alias.`
27197
+ });
27198
+ }
27199
+ }
27200
+ }
27201
+ for (const key of Object.keys(DECLARED_DIVERGENCES)) {
27202
+ if (usedAllowlistKeys.has(key)) continue;
27203
+ violations.push({
27204
+ rule: "stale-allowlist",
27205
+ concept: key,
27206
+ message: `DECLARED_DIVERGENCES entry "${key}" no longer matches any collision \u2014 the divergence was resolved or the profiles were renamed. Delete the entry.`
27207
+ });
27208
+ }
27209
+ return violations;
27210
+ }
27211
+ function formatConceptViolations(violations) {
27212
+ return violations.map((v) => ` \u2022 [${v.rule}] ${v.message}`).join("\n");
27213
+ }
27214
+
27013
27215
  // src/define.ts
27014
27216
  function toAutomationWire(automations) {
27015
27217
  return automations?.map((a) => ({
@@ -27165,11 +27367,15 @@ function toPackageDefinition(slugOrYaml) {
27165
27367
  };
27166
27368
  }
27167
27369
  export {
27370
+ DECLARED_DIVERGENCES,
27168
27371
  PACKAGE_DEPENDENCY_KINDS,
27169
27372
  WORKSPACE_TEMPLATES,
27170
27373
  assembleCatalog,
27171
27374
  bundledEdgesOf,
27172
27375
  computeTemplateUpdates,
27376
+ conceptTerm,
27377
+ findConceptViolations,
27378
+ formatConceptViolations,
27173
27379
  getWorkspaceTemplate,
27174
27380
  isHashVersion,
27175
27381
  layerTemplateGraph,