@elevasis/sdk 1.27.1 → 1.28.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.
package/dist/cli.cjs CHANGED
@@ -37592,6 +37592,26 @@ var SystemUiSchema = external_exports.object({
37592
37592
  icon: IconNameSchema.optional(),
37593
37593
  order: external_exports.number().int().optional()
37594
37594
  });
37595
+ var SystemInterfaceKeySchema = ModelIdSchema;
37596
+ var SystemInterfaceLifecycleSchema = external_exports.enum(["draft", "active", "disabled", "deprecated", "archived"]).meta({ label: "System interface lifecycle", color: "teal" });
37597
+ var SystemInterfaceReadinessProfileSchema = external_exports.string().trim().min(1).max(200).regex(
37598
+ /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*(?:\.[a-z0-9][a-z0-9-]*)?$/,
37599
+ 'Readiness profiles must use dotted lowercase identifiers (e.g. "sales.lead-gen.api")'
37600
+ );
37601
+ var SystemInterfaceResourceScopeSchema = external_exports.array(ModelIdSchema).default([]);
37602
+ var SystemApiInterfaceSchema = external_exports.object({
37603
+ lifecycle: SystemInterfaceLifecycleSchema.default("active"),
37604
+ readinessProfile: SystemInterfaceReadinessProfileSchema.optional(),
37605
+ /**
37606
+ * Resource ids that participate in this API interface. This scopes readiness
37607
+ * derivation without duplicating authored required/provided contract refs.
37608
+ */
37609
+ resourceIds: SystemInterfaceResourceScopeSchema.optional()
37610
+ }).strict();
37611
+ var SystemInterfaceRefSchema = external_exports.object({
37612
+ systemPath: SystemPathSchema,
37613
+ interfaceKey: SystemInterfaceKeySchema
37614
+ }).strict();
37595
37615
  var JsonValueSchema = external_exports.lazy(
37596
37616
  () => external_exports.union([
37597
37617
  external_exports.string(),
@@ -37630,6 +37650,8 @@ var SystemEntrySchema = external_exports.object({
37630
37650
  policies: external_exports.array(ModelIdSchema.meta({ ref: "policy" })).default([]).optional(),
37631
37651
  /** Optional goals this system contributes to. */
37632
37652
  drivesGoals: external_exports.array(ModelIdSchema.meta({ ref: "goal" })).default([]).optional(),
37653
+ /** Thin API runtime-boundary marker. Readiness is derived from scoped resources and topology. */
37654
+ apiInterface: SystemApiInterfaceSchema.optional(),
37633
37655
  /** @deprecated Use lifecycle. Accepted for one publish cycle. */
37634
37656
  status: SystemStatusSchema.optional(),
37635
37657
  /** @deprecated Use ui.path. Kept for one-cycle Feature compatibility. */
@@ -38066,6 +38088,15 @@ var OmTopologyMetadataSchema = external_exports.record(external_exports.string()
38066
38088
  }
38067
38089
  visit(metadata, []);
38068
38090
  });
38091
+ var OmTopologySystemInterfaceGrantSchema = external_exports.object({
38092
+ consumer: SystemInterfaceRefSchema,
38093
+ provider: SystemInterfaceRefSchema,
38094
+ resourceIds: external_exports.array(ResourceIdSchema).default([]),
38095
+ ontologyIds: external_exports.array(OntologyIdSchema).default([])
38096
+ }).strict();
38097
+ var OmTopologySystemInterfaceGrantMetadataSchema = external_exports.object({
38098
+ systemInterfaceGrant: OmTopologySystemInterfaceGrantSchema
38099
+ }).strict();
38069
38100
  var OmTopologyRelationshipSchema = external_exports.object({
38070
38101
  from: OmTopologyNodeRefSchema,
38071
38102
  kind: OmTopologyRelationshipKindSchema,
@@ -38617,6 +38648,26 @@ function refineOrganizationModel(model, ctx) {
38617
38648
  );
38618
38649
  }
38619
38650
  });
38651
+ allSystems.forEach(({ path: systemPath, schemaPath, system }) => {
38652
+ system.apiInterface?.resourceIds?.forEach((resourceId, resourceIndex) => {
38653
+ const resource = resourcesById.get(resourceId);
38654
+ if (resource === void 0) {
38655
+ addIssue(
38656
+ ctx,
38657
+ [...schemaPath, "apiInterface", "resourceIds", resourceIndex],
38658
+ `System Interface "${systemPath}/api" scopes unknown resource "${resourceId}"`
38659
+ );
38660
+ return;
38661
+ }
38662
+ if (resource.systemPath !== systemPath) {
38663
+ addIssue(
38664
+ ctx,
38665
+ [...schemaPath, "apiInterface", "resourceIds", resourceIndex],
38666
+ `System Interface "${systemPath}/api" scopes resource "${resourceId}" from system "${resource.systemPath}"`
38667
+ );
38668
+ }
38669
+ });
38670
+ });
38620
38671
  function validateResourceOntologyBinding(resourceId, bindingKey, expectedKind, ids) {
38621
38672
  const ontologyIds2 = ids === void 0 ? [] : Array.isArray(ids) ? ids : [ids];
38622
38673
  ontologyIds2.forEach((ontologyId, ontologyIndex) => {
@@ -40215,6 +40266,458 @@ function validateModelConfig(config3) {
40215
40266
  }
40216
40267
  }
40217
40268
 
40269
+ // ../core/src/business/acquisition/ontology-validation.ts
40270
+ var LEAD_GEN_API_INTERFACE = {
40271
+ systemPath: "sales.lead-gen",
40272
+ interfaceKey: "api",
40273
+ readinessProfile: "sales.lead-gen.api"
40274
+ };
40275
+ var CRM_API_INTERFACE = {
40276
+ systemPath: "sales.crm",
40277
+ interfaceKey: "api",
40278
+ readinessProfile: "sales.crm.api"
40279
+ };
40280
+ var LEAD_GEN_CRM_HANDOFF_INTERFACE = {
40281
+ systemPath: "sales.lead-gen",
40282
+ interfaceKey: "crm-handoff",
40283
+ readinessProfile: "sales.lead-gen.crm-handoff"
40284
+ };
40285
+ var LEAD_GEN_API_READINESS_LABEL = LEAD_GEN_API_INTERFACE.readinessProfile;
40286
+ var CRM_API_READINESS_LABEL = CRM_API_INTERFACE.readinessProfile;
40287
+ var LEAD_GEN_CRM_HANDOFF_READINESS_LABEL = LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile;
40288
+ var LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID = formatOntologyId({
40289
+ scope: "sales.lead-gen",
40290
+ kind: "object",
40291
+ localId: "list"
40292
+ });
40293
+ var LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID = formatOntologyId({
40294
+ scope: "sales.lead-gen",
40295
+ kind: "object",
40296
+ localId: "company"
40297
+ });
40298
+ var LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID = formatOntologyId({
40299
+ scope: "sales.lead-gen",
40300
+ kind: "object",
40301
+ localId: "contact"
40302
+ });
40303
+ var LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID = formatOntologyId({
40304
+ scope: "sales.lead-gen",
40305
+ kind: "catalog",
40306
+ localId: "build-template"
40307
+ });
40308
+ var LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
40309
+ scope: "sales.lead-gen",
40310
+ kind: "catalog",
40311
+ localId: "company-stage"
40312
+ });
40313
+ var LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
40314
+ scope: "sales.lead-gen",
40315
+ kind: "catalog",
40316
+ localId: "contact-stage"
40317
+ });
40318
+ var CRM_PIPELINE_CATALOG_ONTOLOGY_ID = formatOntologyId({
40319
+ scope: "sales.crm",
40320
+ kind: "catalog",
40321
+ localId: "crm.pipeline"
40322
+ });
40323
+ var LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
40324
+ scope: "sales.lead-gen",
40325
+ kind: "catalog",
40326
+ localId: "lead-gen.stage-catalog"
40327
+ });
40328
+ function createLeadGenStageCatalog(model) {
40329
+ return {
40330
+ id: LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID,
40331
+ label: "Lead Gen Processing Stages",
40332
+ ownerSystemId: "sales.lead-gen",
40333
+ kind: "processing-stage-catalog",
40334
+ entries: Object.fromEntries(
40335
+ Object.entries(getLeadGenStageCatalog(model)).map(([key, entry]) => [
40336
+ key,
40337
+ {
40338
+ ...entry
40339
+ }
40340
+ ])
40341
+ ),
40342
+ legacyCatalogKey: "LEAD_GEN_STAGE_CATALOG"
40343
+ };
40344
+ }
40345
+ function isPlainRecord(value) {
40346
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
40347
+ }
40348
+ function addReadinessIssue(issues, family, code, message, details = {}) {
40349
+ issues.push({
40350
+ family,
40351
+ code,
40352
+ message,
40353
+ ...details
40354
+ });
40355
+ }
40356
+ function formatInterfaceIdentity(systemPath, interfaceKey) {
40357
+ return `${systemPath}/${interfaceKey}`;
40358
+ }
40359
+ function profileForInterface(systemPath, interfaceKey, readinessProfile) {
40360
+ return readinessProfile ?? `${systemPath}.${interfaceKey}`;
40361
+ }
40362
+ function readinessMarkerPath(context) {
40363
+ return context.interfaceKey === "api" ? `systems.${context.systemPath}.apiInterface` : `systems.${context.systemPath}.derivedCrmHandoffReadiness`;
40364
+ }
40365
+ function getActiveScopedResources(model, resourceIds, issues, context) {
40366
+ const resources = [];
40367
+ for (const [index, resourceId] of resourceIds.entries()) {
40368
+ const resource = model.resources?.[resourceId];
40369
+ const path3 = `${readinessMarkerPath(context)}.resourceIds.${index}`;
40370
+ if (resource === void 0) {
40371
+ addReadinessIssue(
40372
+ issues,
40373
+ "SYSTEM_INTERFACE_NOT_READY",
40374
+ "missing-resource",
40375
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes missing resource "${resourceId}".`,
40376
+ { path: path3, ref: resourceId }
40377
+ );
40378
+ continue;
40379
+ }
40380
+ if (resource.systemPath !== context.systemPath) {
40381
+ addReadinessIssue(
40382
+ issues,
40383
+ "SYSTEM_INTERFACE_INVALID",
40384
+ "resource-system-mismatch",
40385
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes resource "${resourceId}" from system "${resource.systemPath}".`,
40386
+ { path: path3, ref: resourceId }
40387
+ );
40388
+ continue;
40389
+ }
40390
+ if (resource.status !== "active") {
40391
+ addReadinessIssue(
40392
+ issues,
40393
+ "SYSTEM_INTERFACE_NOT_READY",
40394
+ "inactive-resource",
40395
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes inactive resource "${resourceId}".`,
40396
+ { path: path3, ref: resourceId }
40397
+ );
40398
+ continue;
40399
+ }
40400
+ resources.push(resource);
40401
+ }
40402
+ return resources;
40403
+ }
40404
+ function resourceBindingIds(resources, key) {
40405
+ return new Set(resources.flatMap((resource) => resource.ontology?.[key] ?? []));
40406
+ }
40407
+ function requireScopedBinding(issues, boundIds, bindingKey, ontologyId, context) {
40408
+ if (boundIds.has(ontologyId)) return;
40409
+ addReadinessIssue(
40410
+ issues,
40411
+ "SYSTEM_INTERFACE_NOT_READY",
40412
+ "missing-resource-binding",
40413
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" has no active scoped resource with ontology.${bindingKey} binding "${ontologyId}".`,
40414
+ { path: `${readinessMarkerPath(context)}.resourceIds`, ref: ontologyId }
40415
+ );
40416
+ }
40417
+ function ontologyOwnerMatches(ontologyId, expectedSystemPath, catalog) {
40418
+ if (catalog?.ownerSystemId !== void 0) return catalog.ownerSystemId === expectedSystemPath;
40419
+ return parseOntologyId(ontologyId).scope === expectedSystemPath;
40420
+ }
40421
+ function requireObjectReadiness(issues, index, objectId, context) {
40422
+ const object2 = index.ontology.objectTypes[objectId];
40423
+ if (object2 === void 0) {
40424
+ addReadinessIssue(
40425
+ issues,
40426
+ "SYSTEM_INTERFACE_NOT_READY",
40427
+ "missing-object",
40428
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing required object type "${objectId}".`,
40429
+ { ref: objectId }
40430
+ );
40431
+ return;
40432
+ }
40433
+ const ownerSystemId = object2.ownerSystemId ?? parseOntologyId(objectId).scope;
40434
+ if (ownerSystemId !== context.systemPath) {
40435
+ addReadinessIssue(
40436
+ issues,
40437
+ "SYSTEM_INTERFACE_INVALID",
40438
+ "foreign-object",
40439
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires object "${objectId}" owned by "${ownerSystemId}".`,
40440
+ { ref: objectId }
40441
+ );
40442
+ }
40443
+ }
40444
+ function requireCatalogReadiness(issues, index, catalogId, context) {
40445
+ const catalog = index.ontology.catalogTypes[catalogId];
40446
+ if (catalog === void 0) {
40447
+ addReadinessIssue(
40448
+ issues,
40449
+ "SYSTEM_INTERFACE_NOT_READY",
40450
+ "missing-catalog",
40451
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing required catalog "${catalogId}".`,
40452
+ { ref: catalogId }
40453
+ );
40454
+ return void 0;
40455
+ }
40456
+ if (context.allowForeignOwner !== true && !ontologyOwnerMatches(catalogId, context.systemPath, catalog)) {
40457
+ addReadinessIssue(
40458
+ issues,
40459
+ "SYSTEM_INTERFACE_INVALID",
40460
+ "foreign-catalog",
40461
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires catalog "${catalogId}" owned by "${catalog.ownerSystemId ?? parseOntologyId(catalogId).scope}".`,
40462
+ { ref: catalogId }
40463
+ );
40464
+ }
40465
+ if (Object.keys(getCatalogEntries(catalog)).length === 0) {
40466
+ addReadinessIssue(
40467
+ issues,
40468
+ "SYSTEM_INTERFACE_NOT_READY",
40469
+ "empty-catalog",
40470
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires catalog entries for "${catalogId}".`,
40471
+ { ref: catalogId }
40472
+ );
40473
+ }
40474
+ return catalog;
40475
+ }
40476
+ function requireLeadGenInterfaceReadiness(issues, index, resources, context) {
40477
+ const reads = resourceBindingIds(resources, "reads");
40478
+ const catalogs = resourceBindingIds(resources, "usesCatalogs");
40479
+ for (const objectId of [
40480
+ LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID,
40481
+ LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID,
40482
+ LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID
40483
+ ]) {
40484
+ requireObjectReadiness(issues, index, objectId, context);
40485
+ requireScopedBinding(issues, reads, "reads", objectId, context);
40486
+ }
40487
+ for (const catalogId of [
40488
+ LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID,
40489
+ LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID,
40490
+ LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID
40491
+ ]) {
40492
+ requireCatalogReadiness(issues, index, catalogId, context);
40493
+ requireScopedBinding(issues, catalogs, "usesCatalogs", catalogId, context);
40494
+ }
40495
+ const templateStepCatalog = findLeadGenTemplateStepCatalog(index);
40496
+ if (templateStepCatalog === void 0) {
40497
+ addReadinessIssue(
40498
+ issues,
40499
+ "SYSTEM_INTERFACE_NOT_READY",
40500
+ "missing-template-step-catalog",
40501
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing a lead-gen template-step catalog.`
40502
+ );
40503
+ } else {
40504
+ requireCatalogReadiness(issues, index, templateStepCatalog.id, context);
40505
+ requireScopedBinding(issues, catalogs, "usesCatalogs", templateStepCatalog.id, context);
40506
+ }
40507
+ const leadGenStageCatalog = requireCatalogReadiness(issues, index, LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID, context);
40508
+ return leadGenStageCatalog;
40509
+ }
40510
+ function requireCrmInterfaceReadiness(issues, index, resources, context) {
40511
+ const catalogs = resourceBindingIds(resources, "usesCatalogs");
40512
+ const crmPipelineCatalog = requireCatalogReadiness(issues, index, CRM_PIPELINE_CATALOG_ONTOLOGY_ID, context);
40513
+ requireScopedBinding(issues, catalogs, "usesCatalogs", CRM_PIPELINE_CATALOG_ONTOLOGY_ID, context);
40514
+ return crmPipelineCatalog;
40515
+ }
40516
+ function findSystemInterfaceGrant(model, consumer, provider) {
40517
+ return Object.values(model.topology?.relationships ?? {}).find((relationship) => {
40518
+ const grant = relationship.metadata?.["systemInterfaceGrant"];
40519
+ if (!isPlainRecord(grant) || !isPlainRecord(grant.consumer) || !isPlainRecord(grant.provider)) return false;
40520
+ return grant.consumer.systemPath === consumer.systemPath && grant.consumer.interfaceKey === consumer.interfaceKey && grant.provider.systemPath === provider.systemPath && grant.provider.interfaceKey === provider.interfaceKey;
40521
+ });
40522
+ }
40523
+ function requireHandoffBridgeReadiness(issues, model, context) {
40524
+ const crmResult = computeInterfaceReadiness(model, CRM_API_INTERFACE);
40525
+ if (!crmResult.ready) {
40526
+ for (const issue2 of crmResult.issues) {
40527
+ addReadinessIssue(
40528
+ issues,
40529
+ "SYSTEM_BRIDGE_NOT_READY",
40530
+ issue2.code,
40531
+ `Provider interface ${formatInterfaceIdentity(CRM_API_INTERFACE.systemPath, CRM_API_INTERFACE.interfaceKey)} is not ready: ${issue2.message}`,
40532
+ { path: issue2.path, ref: issue2.ref }
40533
+ );
40534
+ }
40535
+ }
40536
+ const bridgeGrant = findSystemInterfaceGrant(model, context, CRM_API_INTERFACE);
40537
+ if (bridgeGrant === void 0) {
40538
+ addReadinessIssue(
40539
+ issues,
40540
+ "SYSTEM_BRIDGE_NOT_READY",
40541
+ "missing-topology-grant",
40542
+ `System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires a scoped topology grant to "${formatInterfaceIdentity(CRM_API_INTERFACE.systemPath, CRM_API_INTERFACE.interfaceKey)}".`
40543
+ );
40544
+ }
40545
+ return bridgeGrant;
40546
+ }
40547
+ function getLeadGenCrmHandoffResourceIds(model) {
40548
+ return Object.values(model.resources ?? {}).filter(
40549
+ (resource) => resource.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && resource.ontology?.usesCatalogs?.includes(CRM_PIPELINE_CATALOG_ONTOLOGY_ID) === true
40550
+ ).map((resource) => resource.id);
40551
+ }
40552
+ function getSystemInterfaceReadinessMarker(model, request) {
40553
+ const system = getSystem(model, request.systemPath);
40554
+ if (system === void 0) return void 0;
40555
+ if (request.interfaceKey === LEAD_GEN_API_INTERFACE.interfaceKey) {
40556
+ return system.apiInterface;
40557
+ }
40558
+ if (request.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && request.interfaceKey === LEAD_GEN_CRM_HANDOFF_INTERFACE.interfaceKey) {
40559
+ return {
40560
+ lifecycle: "active",
40561
+ readinessProfile: LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile,
40562
+ resourceIds: getLeadGenCrmHandoffResourceIds(model)
40563
+ };
40564
+ }
40565
+ return void 0;
40566
+ }
40567
+ function mergeLeadGenDerivedCatalogs(model) {
40568
+ const baseCatalogTypes = model.ontology?.catalogTypes ?? {};
40569
+ const derivedCatalogTypes = {};
40570
+ if (baseCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] === void 0) {
40571
+ derivedCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] = createLeadGenStageCatalog(model);
40572
+ }
40573
+ if (Object.keys(derivedCatalogTypes).length === 0) return model;
40574
+ return {
40575
+ ...model,
40576
+ ontology: {
40577
+ ...model.ontology ?? {},
40578
+ catalogTypes: {
40579
+ ...baseCatalogTypes,
40580
+ ...derivedCatalogTypes
40581
+ }
40582
+ }
40583
+ };
40584
+ }
40585
+ function compileBusinessOntology(model, contractId) {
40586
+ const compilation = compileOrganizationOntology(mergeLeadGenDerivedCatalogs(model));
40587
+ if (compilation.diagnostics.length > 0) {
40588
+ const summary = compilation.diagnostics.map((diagnostic) => diagnostic.message).join("; ");
40589
+ throw new Error(`${contractId} ontology validation index failed to compile: ${summary}`);
40590
+ }
40591
+ return {
40592
+ ontology: compilation.ontology,
40593
+ actionTypesByLegacyId: indexActionTypesByLegacyId(compilation.ontology.actionTypes)
40594
+ };
40595
+ }
40596
+ function tryCompileBusinessOntology(model, readinessProfile, issues) {
40597
+ try {
40598
+ return compileBusinessOntology(model, readinessProfile);
40599
+ } catch (error46) {
40600
+ addReadinessIssue(
40601
+ issues,
40602
+ "SYSTEM_INTERFACE_INVALID",
40603
+ "ontology-compile-failed",
40604
+ error46 instanceof Error ? error46.message : String(error46)
40605
+ );
40606
+ return void 0;
40607
+ }
40608
+ }
40609
+ function computeInterfaceReadiness(model, request) {
40610
+ const issues = [];
40611
+ const system = getSystem(model, request.systemPath);
40612
+ const systemInterface = getSystemInterfaceReadinessMarker(model, request);
40613
+ const readinessProfile = systemInterface === void 0 ? void 0 : profileForInterface(request.systemPath, request.interfaceKey, systemInterface.readinessProfile);
40614
+ const scopedResourceIds = systemInterface?.resourceIds ?? [];
40615
+ if (system === void 0) {
40616
+ addReadinessIssue(
40617
+ issues,
40618
+ "SYSTEM_INTERFACE_MISSING",
40619
+ "missing-system",
40620
+ `System "${request.systemPath}" is missing.`,
40621
+ { ref: request.systemPath }
40622
+ );
40623
+ return { ready: false, systemPath: request.systemPath, interfaceKey: request.interfaceKey, scopedResourceIds, issues };
40624
+ }
40625
+ if (systemInterface === void 0) {
40626
+ addReadinessIssue(
40627
+ issues,
40628
+ "SYSTEM_INTERFACE_MISSING",
40629
+ "missing-interface",
40630
+ `System "${request.systemPath}" does not declare interface "${request.interfaceKey}".`,
40631
+ { path: readinessMarkerPath(request) }
40632
+ );
40633
+ return { ready: false, systemPath: request.systemPath, interfaceKey: request.interfaceKey, scopedResourceIds, issues };
40634
+ }
40635
+ if (systemInterface.lifecycle !== "active") {
40636
+ addReadinessIssue(
40637
+ issues,
40638
+ "SYSTEM_INTERFACE_DISABLED",
40639
+ "inactive-interface",
40640
+ `System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" lifecycle is "${systemInterface.lifecycle}".`,
40641
+ { path: `${readinessMarkerPath(request)}.lifecycle` }
40642
+ );
40643
+ }
40644
+ const supportedProfiles = [
40645
+ LEAD_GEN_API_INTERFACE.readinessProfile,
40646
+ CRM_API_INTERFACE.readinessProfile,
40647
+ LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile
40648
+ ];
40649
+ const supportedProfile = readinessProfile !== void 0 && supportedProfiles.some((profile) => profile === readinessProfile);
40650
+ if (!supportedProfile) {
40651
+ addReadinessIssue(
40652
+ issues,
40653
+ "SYSTEM_INTERFACE_INVALID",
40654
+ "unknown-readiness-profile",
40655
+ `System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" references unknown readiness profile "${readinessProfile}".`,
40656
+ { path: `${readinessMarkerPath(request)}.readinessProfile`, ref: readinessProfile }
40657
+ );
40658
+ return {
40659
+ ready: false,
40660
+ systemPath: request.systemPath,
40661
+ interfaceKey: request.interfaceKey,
40662
+ readinessProfile,
40663
+ scopedResourceIds,
40664
+ issues
40665
+ };
40666
+ }
40667
+ if (scopedResourceIds.length === 0) {
40668
+ addReadinessIssue(
40669
+ issues,
40670
+ "SYSTEM_INTERFACE_NOT_READY",
40671
+ "missing-scoped-resources",
40672
+ `System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" must scope at least one active resource.`,
40673
+ { path: `${readinessMarkerPath(request)}.resourceIds` }
40674
+ );
40675
+ }
40676
+ const checkedReadinessProfile = readinessProfile;
40677
+ if (checkedReadinessProfile === void 0) {
40678
+ throw new Error("Supported readiness profile unexpectedly resolved to undefined");
40679
+ }
40680
+ const resources = getActiveScopedResources(model, scopedResourceIds, issues, request);
40681
+ const index = tryCompileBusinessOntology(model, checkedReadinessProfile, issues);
40682
+ if (index !== void 0) {
40683
+ if (checkedReadinessProfile === LEAD_GEN_API_INTERFACE.readinessProfile) {
40684
+ requireLeadGenInterfaceReadiness(issues, index, resources, request);
40685
+ } else if (checkedReadinessProfile === CRM_API_INTERFACE.readinessProfile) {
40686
+ requireCrmInterfaceReadiness(issues, index, resources, request);
40687
+ } else if (checkedReadinessProfile === LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile) {
40688
+ requireLeadGenInterfaceReadiness(issues, index, resources, request);
40689
+ requireCrmInterfaceReadiness(issues, index, resources, { ...request, allowForeignOwner: true });
40690
+ requireHandoffBridgeReadiness(issues, model, request);
40691
+ }
40692
+ }
40693
+ return {
40694
+ ready: issues.length === 0,
40695
+ systemPath: request.systemPath,
40696
+ interfaceKey: request.interfaceKey,
40697
+ readinessProfile,
40698
+ scopedResourceIds,
40699
+ issues
40700
+ };
40701
+ }
40702
+ function findLeadGenTemplateStepCatalog(index) {
40703
+ return Object.values(index.ontology.catalogTypes).find(
40704
+ (catalog) => catalog.ownerSystemId === "sales.lead-gen" && catalog.kind === "template-step" && catalog.appliesTo === LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID
40705
+ );
40706
+ }
40707
+ function indexActionTypesByLegacyId(actionTypes) {
40708
+ const byLegacyId = {};
40709
+ for (const actionType of Object.values(actionTypes)) {
40710
+ const legacyActionId = actionType["legacyActionId"];
40711
+ if (typeof legacyActionId === "string") {
40712
+ byLegacyId[legacyActionId] = actionType;
40713
+ }
40714
+ }
40715
+ return byLegacyId;
40716
+ }
40717
+ function getCatalogEntries(catalog) {
40718
+ return isPlainRecord(catalog.entries) ? catalog.entries : {};
40719
+ }
40720
+
40218
40721
  // ../core/src/platform/registry/validation.ts
40219
40722
  var RegistryValidationError = class extends Error {
40220
40723
  constructor(orgName, resourceId, field2, message) {
@@ -40305,7 +40808,7 @@ function ontologyIndexForKind(index, kind) {
40305
40808
  function sameJson(left, right) {
40306
40809
  return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
40307
40810
  }
40308
- function addOntologyBindingIssues(issues, orgName, resource, ontologyIndex) {
40811
+ function addOntologyBindingIssues(issues, orgName, resource, ontologyIndex, organizationModel) {
40309
40812
  const binding = resource.ontology;
40310
40813
  if (binding === void 0) return;
40311
40814
  const hasOperationalOntologyBinding = binding.primaryAction !== void 0 || (binding.reads?.length ?? 0) > 0 || (binding.writes?.length ?? 0) > 0 || (binding.usesCatalogs?.length ?? 0) > 0 || (binding.emits?.length ?? 0) > 0;
@@ -40348,6 +40851,45 @@ function addOntologyBindingIssues(issues, orgName, resource, ontologyIndex) {
40348
40851
  validateRefs("writes", "object", binding.writes);
40349
40852
  validateRefs("usesCatalogs", "catalog", binding.usesCatalogs);
40350
40853
  validateRefs("emits", "event", binding.emits);
40854
+ addOntologyTopologyGrantIssues(issues, orgName, resource, organizationModel);
40855
+ }
40856
+ function addOntologyTopologyGrantIssues(issues, orgName, resource, organizationModel) {
40857
+ const binding = resource.ontology;
40858
+ if (binding === void 0) return;
40859
+ const refs = [
40860
+ ...binding.actions ?? [],
40861
+ ...binding.primaryAction !== void 0 ? [binding.primaryAction] : [],
40862
+ ...binding.reads ?? [],
40863
+ ...binding.writes ?? [],
40864
+ ...binding.usesCatalogs ?? [],
40865
+ ...binding.emits ?? []
40866
+ ];
40867
+ for (const ontologyId of new Set(refs)) {
40868
+ const parsed = parseOntologyId(ontologyId);
40869
+ if (parsed.isGlobal || parsed.scope === resource.systemPath) continue;
40870
+ if (hasScopedTopologyGrant(organizationModel, resource, parsed.scope, ontologyId)) continue;
40871
+ const missingGrant = `systemInterfaceGrant(${resource.systemPath} -> ${parsed.scope}, resourceIds: ["${resource.id}"], ontologyIds: ["${ontologyId}"])`;
40872
+ addGovernanceIssue(
40873
+ issues,
40874
+ "ontology-topology-grant-missing",
40875
+ orgName,
40876
+ resource.id,
40877
+ `[${orgName}] Resource '${resource.id}' in system '${resource.systemPath}' references ontology '${ontologyId}' from system '${parsed.scope}' without scoped topology grant '${missingGrant}'.`
40878
+ );
40879
+ }
40880
+ }
40881
+ function hasScopedTopologyGrant(organizationModel, resource, targetSystemPath, ontologyId) {
40882
+ return Object.values(organizationModel.topology?.relationships ?? {}).some((relationship) => {
40883
+ if (relationship.kind !== "uses") return false;
40884
+ const grant = relationship.metadata?.["systemInterfaceGrant"];
40885
+ if (!isSystemInterfaceGrantRecord(grant)) return false;
40886
+ return grant.consumer.systemPath === resource.systemPath && grant.provider.systemPath === targetSystemPath && grant.resourceIds.includes(resource.id) && grant.ontologyIds.includes(ontologyId);
40887
+ });
40888
+ }
40889
+ function isSystemInterfaceGrantRecord(value) {
40890
+ if (typeof value !== "object" || value === null) return false;
40891
+ const grant = value;
40892
+ return typeof grant.consumer?.systemPath === "string" && typeof grant.consumer.interfaceKey === "string" && typeof grant.provider?.systemPath === "string" && typeof grant.provider.interfaceKey === "string" && Array.isArray(grant.resourceIds) && grant.resourceIds.every((id) => typeof id === "string") && Array.isArray(grant.ontologyIds) && grant.ontologyIds.every((id) => typeof id === "string");
40351
40893
  }
40352
40894
  function addTopologyIssues(issues, orgName, deployment, organizationModel, systemsById, omResourcesById, ontologyIndex) {
40353
40895
  const relationships = organizationModel.topology?.relationships;
@@ -40463,7 +41005,7 @@ function validateResourceGovernance(orgName, deployment, organizationModel = dep
40463
41005
  `[${orgName}] Resource '${resource.id}' ontology descriptor mismatch between code and OM.`
40464
41006
  );
40465
41007
  }
40466
- addOntologyBindingIssues(issues, orgName, resource, ontologyIndex);
41008
+ addOntologyBindingIssues(issues, orgName, resource, ontologyIndex, organizationModel);
40467
41009
  }
40468
41010
  for (const runtimeResource of runtimeResources) {
40469
41011
  const omResource = omResourcesById.get(runtimeResource.resourceId);
@@ -40513,6 +41055,41 @@ function validateResourceGovernance(orgName, deployment, organizationModel = dep
40513
41055
  issues
40514
41056
  };
40515
41057
  }
41058
+ function addSystemInterfaceIssue(issues, orgName, systemPath, interfaceKey, issue2) {
41059
+ issues.push({
41060
+ type: issue2.family,
41061
+ orgName,
41062
+ systemPath,
41063
+ interfaceKey,
41064
+ code: issue2.code,
41065
+ path: issue2.path,
41066
+ ref: issue2.ref,
41067
+ message: `[${orgName}] ${issue2.family}:${issue2.code}: ${issue2.message}`
41068
+ });
41069
+ }
41070
+ function validateDeclaredSystemInterfaceReadiness(orgName, organizationModel) {
41071
+ const issues = [];
41072
+ if (organizationModel === void 0) return { valid: true, issues };
41073
+ const model = organizationModel;
41074
+ for (const { path: path3, system } of listAllSystems(model)) {
41075
+ if (system.apiInterface === void 0) continue;
41076
+ const interfaceKey = "api";
41077
+ const result = computeInterfaceReadiness(model, { systemPath: path3, interfaceKey });
41078
+ for (const issue2 of result.issues) {
41079
+ addSystemInterfaceIssue(issues, orgName, path3, interfaceKey, issue2);
41080
+ }
41081
+ }
41082
+ if (issues.length > 0) {
41083
+ const first = issues[0];
41084
+ throw new RegistryValidationError(
41085
+ first.orgName,
41086
+ `${first.systemPath}/${first.interfaceKey}`,
41087
+ first.path ?? "organizationModel.systems.apiInterface",
41088
+ first.message
41089
+ );
41090
+ }
41091
+ return { valid: true, issues };
41092
+ }
40516
41093
  function validateDeploymentSpec(orgName, resources) {
40517
41094
  const seenIds = /* @__PURE__ */ new Set();
40518
41095
  resources.workflows?.forEach((workflow) => {
@@ -40550,6 +41127,7 @@ function validateDeploymentSpec(orgName, resources) {
40550
41127
  }
40551
41128
  });
40552
41129
  validateResourceGovernance(orgName, resources);
41130
+ validateDeclaredSystemInterfaceReadiness(orgName, resources.organizationModel);
40553
41131
  }
40554
41132
  function validateResourceModelConfig(orgName, resourceId, modelConfig) {
40555
41133
  try {
@@ -44130,7 +44708,7 @@ function wrapAction(commandName, fn) {
44130
44708
  // package.json
44131
44709
  var package_default = {
44132
44710
  name: "@elevasis/sdk",
44133
- version: "1.27.1",
44711
+ version: "1.28.0",
44134
44712
  description: "SDK for building Elevasis organization resources",
44135
44713
  type: "module",
44136
44714
  bin: {
@@ -44382,6 +44960,7 @@ function validateResourceGovernancePreflight(orgName, org, contractRegistry) {
44382
44960
  if (org.organizationModel !== void 0) {
44383
44961
  OrganizationModelSchema.parse(org.organizationModel);
44384
44962
  }
44963
+ validateDeclaredSystemInterfaceReadiness(orgName, org.organizationModel);
44385
44964
  if (process.env.ELEVASIS_RESOURCE_VALIDATOR === "warn-only") return;
44386
44965
  validateResourceGovernance(orgName, org, org.organizationModel, { mode: "strict" });
44387
44966
  const contractRefs = listOrganizationModelContractRefs(org);