@archinsight/cli 3.0.2 → 3.0.3

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 (3) hide show
  1. package/README.md +95 -8
  2. package/build/index.js +1396 -311
  3. package/package.json +3 -2
package/build/index.js CHANGED
@@ -33442,7 +33442,7 @@ var coreSources = [
33442
33442
  },
33443
33443
  {
33444
33444
  "sourceName": "core_deployment.ai",
33445
- "source": "define type DeploymentElement of BoundaryElement\n\nextend type Element\n List of Edge deployment\n InfrastructureComponent runsOn\n List of InfrastructureComponent uses\n\ndefine type InfrastructureComponent of DeploymentElement\n constructor infrastructureComponent\n\n Text name\n Text technology\n Text description\n InfrastructureComponent runsOn\n List of PhysicalWire projection\n List of InfrastructureComponent _\n\ndefine type ProjectionTerm\n\ndefine type DeploymentProfile of BoundaryElement\n constructor deploymentProfile\n\n required List of Deployment appliesTo\n\ndefine type Storage of InfrastructureComponent\n constructor storage\n\ndefine presentation Storage\n graphviz\n shape = cylinder\n\ndefine type Broker of InfrastructureComponent\n constructor broker\n\n Text address\n\ndefine presentation Broker\n graphviz\n shape = box\n style = filled,dashed\n\ndefine type Compute of InfrastructureComponent\n constructor compute\n\n Text address\n List of InfrastructureComponent components\n\ndefine type NetworkConnection of InfrastructureComponent\n constructor networkConnection\n\ndefine type Environment of BoundaryElement\n constructor environment\n\n required Text name\n Text region\n List of DeploymentElement _\n\ndefine type Deployment of DeploymentElement\n constructor deployment\n\n Text name\n List of DeploymentElement _\n\ndefine presentation DeploymentProfile\n graphviz\n visible = false\n\ndefine presentation NetworkConnection\n graphviz\n visible = false\n"
33445
+ "source": "define type DeploymentElement of BoundaryElement\n\nextend type Element\n List of Edge deployment\n InfrastructureComponent runsOn\n List of InfrastructureComponent uses\n\ndefine type InfrastructureComponent of DeploymentElement\n constructor infrastructureComponent\n\n Text name\n Text technology\n Text description\n InfrastructureComponent runsOn\n List of PhysicalWire projection\n List of InfrastructureComponent _\n\ndefine type ProjectionTerm\n\ndefine type DeploymentProfile of BoundaryElement\n constructor deploymentProfile\n\n required List of Deployment appliesTo\n\ndefine type Storage of InfrastructureComponent\n constructor storage\n\ndefine presentation Storage\n graphviz\n shape = cylinder\n\ndefine type Broker of NetworkConnection\n constructor broker\n\n Text address\n\ndefine presentation Broker\n graphviz\n visible = true\n shape = box\n style = filled,dashed\n\ndefine type Compute of InfrastructureComponent\n constructor compute\n\n Text address\n List of InfrastructureComponent components\n\ndefine type NetworkConnection of InfrastructureComponent\n constructor networkConnection\n\ndefine type Environment of BoundaryElement\n constructor environment\n\n required Text name\n Text region\n List of DeploymentElement _\n\ndefine type Deployment of DeploymentElement\n constructor deployment\n\n Text name\n List of DeploymentElement _\n\ndefine presentation DeploymentProfile\n graphviz\n visible = false\n\ndefine presentation NetworkConnection\n graphviz\n visible = false\n"
33446
33446
  },
33447
33447
  {
33448
33448
  "sourceName": "core_operator.ai",
@@ -35171,6 +35171,7 @@ var SYSTEM_TYPE = "System";
35171
35171
  var ACTOR_TYPE = "Actor";
35172
35172
  var ORIGINAL_LINK_OPERATOR = "originalLink";
35173
35173
  var DEPLOYMENT_PROFILE_TYPE = "DeploymentProfile";
35174
+ var ENVIRONMENT_TYPE = "Environment";
35174
35175
  var INFRASTRUCTURE_COMPONENT_TYPE = "InfrastructureComponent";
35175
35176
  var NETWORK_CONNECTION_TYPE = "NetworkConnection";
35176
35177
  var DEPLOYMENT_TYPE = "Deployment";
@@ -35249,8 +35250,8 @@ function linkProject(request) {
35249
35250
  element.id,
35250
35251
  mergeResolvedReferenceAttributes(resolvedElementAttributes.get(element.id) ?? {}, {
35251
35252
  [APPLIES_TO_ATTRIBUTE]: effectiveDeployments.map((deployment) => resolvedValueForElement(deployment, deployment)),
35252
- [RUNS_ON_ATTRIBUTE]: application.runsOn.map((item) => resolvedValueForElement(item.element, item.action)),
35253
- [USES_ATTRIBUTE]: application.uses.map((item) => resolvedValueForElement(item.element, item.action))
35253
+ [RUNS_ON_ATTRIBUTE]: application.runsOn.map(resolvedValueForDeploymentUse),
35254
+ [USES_ATTRIBUTE]: application.uses.map(resolvedValueForDeploymentUse)
35254
35255
  })
35255
35256
  );
35256
35257
  }
@@ -35306,6 +35307,12 @@ function linkProject(request) {
35306
35307
  attributes: effectiveEdgeAttributes,
35307
35308
  projectedAttributes: materialized.edge.attributes,
35308
35309
  projectedOperator: materialized.edge.operator,
35310
+ projectionOrigin: {
35311
+ source: edge.source,
35312
+ target: target.id,
35313
+ sourceIdentity: materialized.edge.sourceIdentity,
35314
+ ...materialized.edge.declaration === void 0 ? {} : { declaration: materialized.edge.declaration }
35315
+ },
35309
35316
  ...materialized.edge.annotations === void 0 ? {} : { annotations: materialized.edge.annotations },
35310
35317
  ...coverage === void 0 ? {} : { coverage }
35311
35318
  });
@@ -35313,7 +35320,7 @@ function linkProject(request) {
35313
35320
  }
35314
35321
  }
35315
35322
  for (const projection of pendingProjections) {
35316
- addProjectedEdges(linkedEdges, projection.sourceIdentity, projection.fromId, projection.toId, projection.attributes, linkedElementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, void 0, projection.annotations, projection.projectedAttributes, projection.projectedOperator, /* @__PURE__ */ new Set(), projection.coverage);
35323
+ addProjectedEdges(linkedEdges, projection.sourceIdentity, projection.fromId, projection.toId, projection.attributes, linkedElementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, void 0, projection.annotations, projection.projectedAttributes, projection.projectedOperator, /* @__PURE__ */ new Set(), projection.coverage, projection.projectionOrigin);
35317
35324
  }
35318
35325
  const slotDomainTypes = typeSystem.slotDomainTypes();
35319
35326
  for (const element of elements) {
@@ -35597,7 +35604,7 @@ function collectBodyItems(body, ownerType, owner, document, typeSystem) {
35597
35604
  collectBodyItem(item, ownerType, owner, document, typeSystem);
35598
35605
  }
35599
35606
  }
35600
- function collectBodyItem(item, ownerType, owner, document, typeSystem) {
35607
+ function collectBodyItem(item, ownerType, owner, document, typeSystem, expectedObjectType) {
35601
35608
  const assignment = firstChild2(item, "assignment");
35602
35609
  if (assignment !== void 0) {
35603
35610
  if (owner !== void 0) {
@@ -35633,7 +35640,15 @@ function collectBodyItem(item, ownerType, owner, document, typeSystem) {
35633
35640
  }
35634
35641
  }
35635
35642
  if (object !== void 0) {
35636
- const element = collectObject(object, ownerType, owner, document, typeSystem, annotations(annotatedObject, document));
35643
+ const element = collectObject(
35644
+ object,
35645
+ ownerType,
35646
+ owner,
35647
+ document,
35648
+ typeSystem,
35649
+ annotations(annotatedObject, document),
35650
+ expectedObjectType ?? expectedNestedType(typeSystem, ownerType)
35651
+ );
35637
35652
  if (owner !== void 0 && element !== void 0 && typeSystem.anonymousListAttribute(ownerType) !== void 0) {
35638
35653
  addAttributeValue(owner.attributes, "_", elementReference(element));
35639
35654
  }
@@ -35773,11 +35788,12 @@ function collectGroupingList(list, ownerType, owner, document, typeSystem) {
35773
35788
  if (collectImplicitDeploymentGroupObject(list, listName, owner, document, typeSystem)) {
35774
35789
  return true;
35775
35790
  }
35791
+ const expectedObjectType = deploymentGroupAttributeType(owner, listName, document, typeSystem);
35776
35792
  for (const item of children(list, "listBodyItem")) {
35777
35793
  const bodyItem = firstChild2(item, "architectureBodyItem");
35778
35794
  if (bodyItem !== void 0) {
35779
35795
  const before = document.elements.length;
35780
- collectBodyItem(bodyItem, ownerType, owner, document, typeSystem);
35796
+ collectBodyItem(bodyItem, ownerType, owner, document, typeSystem, expectedObjectType);
35781
35797
  for (const element of document.elements.slice(before)) {
35782
35798
  if (element.parent === owner.id && element.slotName === void 0) {
35783
35799
  element.slotName = listName;
@@ -35802,9 +35818,7 @@ function collectImplicitDeploymentGroupObject(list, listName, owner, document, t
35802
35818
  return true;
35803
35819
  }
35804
35820
  function resolveDeploymentGroupConstructor(listName, listNameNode, owner, document, typeSystem) {
35805
- const parent = owner.parent === void 0 ? void 0 : document.elements.find((element) => element.id === owner.parent);
35806
- const parentAttribute = parent === void 0 ? void 0 : typeSystem.attribute(parent.type, listName);
35807
- const attributeType2 = parentAttribute === void 0 ? void 0 : parentAttribute.list === true ? parentAttribute.listElementType ?? parentAttribute.type : parentAttribute.type;
35821
+ const attributeType2 = deploymentGroupAttributeType(owner, listName, document, typeSystem);
35808
35822
  if (attributeType2 !== void 0) {
35809
35823
  return resolveImplicitObjectConstructor(attributeType2, listName, listNameNode, document, typeSystem);
35810
35824
  }
@@ -35823,6 +35837,14 @@ function resolveDeploymentGroupConstructor(listName, listNameNode, owner, docume
35823
35837
  }
35824
35838
  return constructors[0];
35825
35839
  }
35840
+ function deploymentGroupAttributeType(owner, listName, document, typeSystem) {
35841
+ const parent = owner.parent === void 0 ? void 0 : document.elements.find((element) => element.id === owner.parent);
35842
+ const attribute = parent === void 0 ? void 0 : typeSystem.attribute(parent.type, listName);
35843
+ if (attribute === void 0) {
35844
+ return void 0;
35845
+ }
35846
+ return attribute.list === true ? attribute.listElementType ?? attribute.type : attribute.type;
35847
+ }
35826
35848
  function collectImplicitObjectAttribute(list, attributeName, attributeNameNode, ownerType, owner, document, typeSystem) {
35827
35849
  if (owner === void 0 || attributeName.length === 0) {
35828
35850
  return false;
@@ -35864,16 +35886,18 @@ function resolveImplicitObjectConstructor(attributeType2, attributeName, attribu
35864
35886
  });
35865
35887
  return void 0;
35866
35888
  }
35867
- if (constructors.length > 1) {
35889
+ const exactConstructors = constructors.filter((constructor) => constructor.ownerType === attributeType2);
35890
+ const candidates = exactConstructors.length > 0 ? exactConstructors : constructors;
35891
+ if (candidates.length > 1) {
35868
35892
  document.diagnostics.push({
35869
35893
  code: "CONSTRUCTOR_AMBIGUOUS",
35870
- message: `Type '${attributeType2}' has multiple constructors for implicit attribute '${attributeName}': ${constructors.map((constructor) => `'${constructor.spelling}'`).join(", ")}`,
35894
+ message: `Type '${attributeType2}' has multiple constructors for implicit attribute '${attributeName}': ${candidates.map((constructor) => `'${constructor.spelling}'`).join(", ")}`,
35871
35895
  sourceName: document.sourceName,
35872
35896
  ...position2(attributeNameNode, document.sourceName)
35873
35897
  });
35874
35898
  return void 0;
35875
35899
  }
35876
- return constructors[0];
35900
+ return candidates[0];
35877
35901
  }
35878
35902
  function collectImplicitObjectElement(list, attributeNameNode, constructor, parent, document, slotName) {
35879
35903
  const localId = nextAnonymousLocalId(document);
@@ -36750,7 +36774,7 @@ function deploymentAttributesForWire(edge, target, context) {
36750
36774
  );
36751
36775
  const application = resolveDeploymentApplication(source, edge.deploymentActions, true, context, deployments);
36752
36776
  return {
36753
- [USES_ATTRIBUTE]: application.uses.map((item) => resolvedValueForElement(item.element, item.action))
36777
+ [USES_ATTRIBUTE]: application.uses.map(resolvedValueForDeploymentUse)
36754
36778
  };
36755
36779
  }
36756
36780
  function resolveDeploymentApplication(owner, actions, wire, context, initialDeployments = []) {
@@ -36896,6 +36920,15 @@ function resolveDeploymentInfrastructure(owner, action, target, deployments, inh
36896
36920
  });
36897
36921
  return [];
36898
36922
  }
36923
+ if (!isDeclaredDeploymentInfrastructureSlot(action.targetId, context.typeSystem)) {
36924
+ context.diagnostics.push({
36925
+ code: "UNDECLARED_IDENTIFIER",
36926
+ message: `Deployment slot '${action.targetId}' is not declared by any Environment type`,
36927
+ sourceName: action.sourceName,
36928
+ ...diagnosticPosition2(action)
36929
+ });
36930
+ return [];
36931
+ }
36899
36932
  if (deployments.length === 0) {
36900
36933
  if (wire) {
36901
36934
  return [];
@@ -36931,6 +36964,22 @@ function resolveDeploymentInfrastructure(owner, action, target, deployments, inh
36931
36964
  }
36932
36965
  return result;
36933
36966
  }
36967
+ function isDeclaredDeploymentInfrastructureSlot(slotName, typeSystem) {
36968
+ for (const type of typeSystem.declaredTypes()) {
36969
+ if (!typeSystem.isAssignable(type, ENVIRONMENT_TYPE)) {
36970
+ continue;
36971
+ }
36972
+ const attribute = typeSystem.attribute(type, slotName);
36973
+ if (attribute === void 0) {
36974
+ continue;
36975
+ }
36976
+ const valueType = attribute.list === true ? attribute.listElementType ?? attribute.type : attribute.type;
36977
+ if (typeSystem.isAssignable(valueType, INFRASTRUCTURE_COMPONENT_TYPE)) {
36978
+ return true;
36979
+ }
36980
+ }
36981
+ return false;
36982
+ }
36934
36983
  function materializeInfrastructureUse(owner, source, action, deploymentId, context, inherited) {
36935
36984
  if (!deploymentActionHasOverrides(action)) {
36936
36985
  return {
@@ -37052,6 +37101,12 @@ function resolvedValueForElement(element, source) {
37052
37101
  ...source.endColumn === void 0 ? {} : { endColumn: source.endColumn }
37053
37102
  };
37054
37103
  }
37104
+ function resolvedValueForDeploymentUse(use) {
37105
+ return {
37106
+ ...resolvedValueForElement(use.element, use.action),
37107
+ ...use.deploymentId === void 0 ? {} : { deploymentId: use.deploymentId }
37108
+ };
37109
+ }
37055
37110
  function mergeResolvedReferenceAttributes(base, override) {
37056
37111
  const result = Object.fromEntries(
37057
37112
  Object.entries(base).map(([name, values]) => [name, [...values]])
@@ -37086,7 +37141,7 @@ function mergeUniqueElements(left, right) {
37086
37141
  function sanitizeLocalId(value) {
37087
37142
  return value.replace(/[^A-Za-z0-9_]/g, "_");
37088
37143
  }
37089
- function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes, elementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, projectionScope, annotations2 = [], projectedAttributes, projectedOperator, visitedProjectionElements = /* @__PURE__ */ new Set(), coverage) {
37144
+ function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes, elementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, projectionScope, annotations2 = [], projectedAttributes, projectedOperator, visitedProjectionElements = /* @__PURE__ */ new Set(), coverage, projectionOrigin = { source: fromId, target: toId }) {
37090
37145
  for (const [attributeName, values] of Object.entries(attributes)) {
37091
37146
  if (attributeName === "_") {
37092
37147
  continue;
@@ -37098,7 +37153,7 @@ function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes
37098
37153
  if (!isGraphElement(value.element)) {
37099
37154
  continue;
37100
37155
  }
37101
- const visitKey = `${fromId}\0${toId}\0${value.element.id}`;
37156
+ const visitKey = `${fromId}\0${toId}\0${value.element.id}\0${value.deploymentId ?? ""}`;
37102
37157
  if (visitedProjectionElements.has(visitKey)) {
37103
37158
  continue;
37104
37159
  }
@@ -37106,6 +37161,13 @@ function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes
37106
37161
  if (isDirectSlotReferenceSelfProjection(value.element, fromId, toId)) {
37107
37162
  continue;
37108
37163
  }
37164
+ const effectiveProjectionScope = projectionScope ?? projectionScopeFor(
37165
+ fromId,
37166
+ toId,
37167
+ value,
37168
+ elementsById,
37169
+ resolvedElementAttributes
37170
+ );
37109
37171
  const rules = value.element.projectionRules;
37110
37172
  if (fromId === toId && rules.some(projectionRuleUsesTo)) {
37111
37173
  const position3 = value.line === void 0 || value.column === void 0 ? value.element : {
@@ -37123,7 +37185,7 @@ function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes
37123
37185
  continue;
37124
37186
  }
37125
37187
  for (const rule3 of rules) {
37126
- addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, value.element, rule3, elementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, projectionScope, annotations2, projectedAttributes, projectedOperator, coverage);
37188
+ addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, value.element, rule3, elementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, effectiveProjectionScope, annotations2, projectedAttributes, projectedOperator, coverage, projectionOrigin);
37127
37189
  }
37128
37190
  addProjectedEdges(
37129
37191
  linkedEdges,
@@ -37136,12 +37198,13 @@ function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes
37136
37198
  ownerIndependentProjectionKeys,
37137
37199
  typeSystem,
37138
37200
  diagnostics,
37139
- projectionScope,
37201
+ effectiveProjectionScope,
37140
37202
  annotations2,
37141
37203
  void 0,
37142
37204
  projectedOperator,
37143
37205
  visitedProjectionElements,
37144
- coverage
37206
+ coverage,
37207
+ projectionOrigin
37145
37208
  );
37146
37209
  }
37147
37210
  }
@@ -37149,7 +37212,7 @@ function addProjectedEdges(linkedEdges, sourceIdentity, fromId, toId, attributes
37149
37212
  function isDirectSlotReferenceSelfProjection(element, fromId, toId) {
37150
37213
  return fromId === toId && element.parent === fromId && element.scalarAttributes.parentType !== void 0 && element.scalarAttributes.attributeName !== void 0;
37151
37214
  }
37152
- function addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, projectionElement, rule3, elementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, projectionScope, annotations2 = [], projectedAttributes, projectedOperator, coverage) {
37215
+ function addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, projectionElement, rule3, elementsById, resolvedElementAttributes, ownerIndependentProjectionKeys, typeSystem, diagnostics, projectionScope, annotations2 = [], projectedAttributes, projectedOperator, coverage, projectionOrigin = { source: fromId, target: toId }) {
37153
37216
  if (!validateConcreteProjectionRule(rule3, typeSystem, diagnostics)) {
37154
37217
  return;
37155
37218
  }
@@ -37158,19 +37221,22 @@ function addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, project
37158
37221
  const effectiveOperator = rule3.operator === ORIGINAL_LINK_OPERATOR ? projectedOperator ?? "->" : rule3.operator;
37159
37222
  const edgeOwnerPlacement = projectionRuleOwnerPlacement(rule3);
37160
37223
  const edgeProjectionScope = projectionPlacementScope(edgeOwnerPlacement, projectionScope);
37224
+ const sourcePlacement = projectionPlacementScope(rule3.source.placement, projectionScope);
37225
+ const targetPlacement = projectionPlacementScope(rule3.target.placement, projectionScope);
37161
37226
  const edgeSourceIdentity = projectionPlacementSourceIdentity(edgeOwnerPlacement, fromId, toId, elementsById) ?? sourceIdentity;
37162
37227
  const carriedAttributes = mergeAttributeValues(
37163
37228
  rule3.operator === ORIGINAL_LINK_OPERATOR ? projectedAttributes : void 0,
37164
37229
  rule3.attributes ?? {}
37165
37230
  );
37166
- const projectionOrigin = { source: fromId, target: toId };
37231
+ const preservesLogicalRelationship = rule3.operator === ORIGINAL_LINK_OPERATOR;
37232
+ const originKey = projectionOriginKey(projectionOrigin);
37167
37233
  for (const source of sources) {
37168
37234
  for (const target of targets) {
37169
37235
  if (coverage !== void 0) {
37170
37236
  coverage.projected = true;
37171
37237
  }
37172
37238
  if (!projectionRuleUsesOwner(rule3)) {
37173
- const key = `${projectionElement.id}\0${source}\0${effectiveOperator}\0${target}`;
37239
+ const key = `${projectionElement.id}\0${source}\0${effectiveOperator}\0${target}\0${preservesLogicalRelationship ? originKey : ""}`;
37174
37240
  if (ownerIndependentProjectionKeys.has(key)) {
37175
37241
  mergeProjectedEdge(
37176
37242
  linkedEdges,
@@ -37190,7 +37256,7 @@ function addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, project
37190
37256
  const targetElement = elementsById.get(target);
37191
37257
  const operator = sourceElement === void 0 || targetElement === void 0 ? void 0 : typeSystem.operatorConstructor(effectiveOperator, sourceElement.type, targetElement.type);
37192
37258
  const type = operator?.ownerType ?? effectiveOperator;
37193
- const existingIndex = linkedEdges.findIndex((edge) => edge.projected === true && edge.source === source && edge.operator === effectiveOperator && edge.target === target && edge.projectionRoot === projectionElement.id && edge.projectionScope === edgeProjectionScope);
37259
+ const existingIndex = linkedEdges.findIndex((edge) => edge.projected === true && edge.source === source && edge.operator === effectiveOperator && edge.target === target && edge.projectionRoot === projectionElement.id && edge.projectionScope === edgeProjectionScope && edge.sourcePlacement === sourcePlacement && edge.targetPlacement === targetPlacement && (!preservesLogicalRelationship || edge.projectionOrigins?.some((origin) => projectionOriginKey(origin) === originKey) === true));
37194
37260
  if (existingIndex >= 0) {
37195
37261
  const edge = linkedEdges[existingIndex];
37196
37262
  if (edge !== void 0) {
@@ -37213,10 +37279,13 @@ function addProjectedRuleEdge(linkedEdges, sourceIdentity, fromId, toId, project
37213
37279
  operator: effectiveOperator,
37214
37280
  type,
37215
37281
  sourceIdentity: edgeSourceIdentity,
37282
+ ...preservesLogicalRelationship && projectionOrigin.declaration !== void 0 ? { declaration: projectionOrigin.declaration } : {},
37216
37283
  attributes: carriedAttributes ?? {},
37217
37284
  ...listAttributesProperty(typeSystem, type),
37218
37285
  projected: true,
37219
37286
  ...edgeProjectionScope === void 0 ? {} : { projectionScope: edgeProjectionScope },
37287
+ ...sourcePlacement === void 0 ? {} : { sourcePlacement },
37288
+ ...targetPlacement === void 0 ? {} : { targetPlacement },
37220
37289
  ...annotations2.length === 0 ? {} : { annotations: annotations2 }
37221
37290
  });
37222
37291
  }
@@ -37265,7 +37334,7 @@ function uniqueProjectionOrigins(origins) {
37265
37334
  const seen = /* @__PURE__ */ new Set();
37266
37335
  const result = [];
37267
37336
  for (const origin of origins) {
37268
- const key = `${origin.source}\0${origin.target}`;
37337
+ const key = projectionOriginKey(origin);
37269
37338
  if (seen.has(key)) {
37270
37339
  continue;
37271
37340
  }
@@ -37274,6 +37343,19 @@ function uniqueProjectionOrigins(origins) {
37274
37343
  }
37275
37344
  return result;
37276
37345
  }
37346
+ function projectionOriginKey(origin) {
37347
+ const declaration = origin.declaration;
37348
+ return [
37349
+ origin.source,
37350
+ origin.target,
37351
+ origin.sourceIdentity ?? "",
37352
+ declaration?.sourceName ?? "",
37353
+ declaration?.line ?? "",
37354
+ declaration?.column ?? "",
37355
+ declaration?.endLine ?? "",
37356
+ declaration?.endColumn ?? ""
37357
+ ].join("\0");
37358
+ }
37277
37359
  function mergeAttributeValues(base, override) {
37278
37360
  if (base === void 0 || Object.keys(base).length === 0) {
37279
37361
  return override;
@@ -37356,8 +37438,58 @@ function uniqueIds(values) {
37356
37438
  function projectionRuleOwnerPlacement(rule3) {
37357
37439
  return rule3.operator === ORIGINAL_LINK_OPERATOR ? rule3.target.placement : rule3.source.placement;
37358
37440
  }
37359
- function projectionPlacementScope(_placement, _projectionScope) {
37360
- return void 0;
37441
+ function projectionScopeFor(fromId, toId, projectionValue, elementsById, resolvedElementAttributes) {
37442
+ const sourcePlacement = endpointPlacement(
37443
+ fromId,
37444
+ projectionValue.deploymentId,
37445
+ projectionValue.element?.context,
37446
+ elementsById,
37447
+ resolvedElementAttributes
37448
+ );
37449
+ const targetPlacement = endpointPlacement(
37450
+ toId,
37451
+ projectionValue.deploymentId,
37452
+ projectionValue.element?.context,
37453
+ elementsById,
37454
+ resolvedElementAttributes
37455
+ );
37456
+ return sourcePlacement === void 0 && targetPlacement === void 0 ? void 0 : {
37457
+ ...sourcePlacement === void 0 ? {} : { sourcePlacement },
37458
+ ...targetPlacement === void 0 ? {} : { targetPlacement }
37459
+ };
37460
+ }
37461
+ function endpointPlacement(endpointId, deploymentId, projectionContext, elementsById, resolvedElementAttributes) {
37462
+ const placements = endpointPlacementValues(endpointId, elementsById, resolvedElementAttributes);
37463
+ if (deploymentId !== void 0) {
37464
+ const inDeployment = uniqueIds(placements.filter((placement) => placement.deploymentId === deploymentId).map((placement) => placement.id));
37465
+ if (inDeployment.length === 1) {
37466
+ return inDeployment[0];
37467
+ }
37468
+ }
37469
+ if (projectionContext !== void 0) {
37470
+ const inContext = uniqueIds(placements.filter((placement) => placement.element?.context === projectionContext).map((placement) => placement.id));
37471
+ if (inContext.length === 1) {
37472
+ return inContext[0];
37473
+ }
37474
+ }
37475
+ const uniquePlacements = uniqueIds(placements.map((placement) => placement.id));
37476
+ return uniquePlacements.length === 1 ? uniquePlacements[0] : void 0;
37477
+ }
37478
+ function endpointPlacementValues(endpointId, elementsById, resolvedElementAttributes) {
37479
+ const visited = /* @__PURE__ */ new Set();
37480
+ let current = elementsById.get(endpointId);
37481
+ while (current !== void 0 && !visited.has(current.id)) {
37482
+ visited.add(current.id);
37483
+ const placements = resolvedElementAttributes.get(current.id)?.[RUNS_ON_ATTRIBUTE] ?? [];
37484
+ if (placements.length > 0) {
37485
+ return placements;
37486
+ }
37487
+ current = current.parent === void 0 ? void 0 : elementsById.get(current.parent);
37488
+ }
37489
+ return [];
37490
+ }
37491
+ function projectionPlacementScope(placement, projectionScope) {
37492
+ return placement === "source" ? projectionScope?.sourcePlacement : projectionScope?.targetPlacement;
37361
37493
  }
37362
37494
  function projectionPlacementSourceIdentity(placement, fromId, toId, elementsById) {
37363
37495
  return elementsById.get(placement === "target" ? toId : fromId)?.sourceName;
@@ -38263,6 +38395,8 @@ function selectGraph(result, scope, query) {
38263
38395
  const selectedElements = /* @__PURE__ */ new Map();
38264
38396
  const selectedEdges = [];
38265
38397
  const selectedEdgeIdentities = /* @__PURE__ */ new Map();
38398
+ const returnedRelationshipPatterns = relationshipPatternsReturnedBy(parsed);
38399
+ const selectedStructuralRelationships = /* @__PURE__ */ new Set();
38266
38400
  const groups = /* @__PURE__ */ new Map();
38267
38401
  const nodeById = queryNodeIndex(result);
38268
38402
  for (const row of rows) {
@@ -38292,6 +38426,8 @@ function selectGraph(result, scope, query) {
38292
38426
  derived: edge.derived,
38293
38427
  projected: edge.projected
38294
38428
  }, edge.edge, selectedEdgeIdentities);
38429
+ } else if (returnedRelationshipPatterns.has(alias)) {
38430
+ selectedStructuralRelationships.add(alias);
38295
38431
  }
38296
38432
  }
38297
38433
  }
@@ -38301,7 +38437,7 @@ function selectGraph(result, scope, query) {
38301
38437
  }
38302
38438
  let groupedSelectedElements = /* @__PURE__ */ new Set();
38303
38439
  if (parsed.groupBy !== void 0) {
38304
- groupedSelectedElements = collectSelectedReferenceGroups(selectedElements, groups, parsed.groupBy);
38440
+ groupedSelectedElements = collectSelectedReferenceGroups(selectedElements, selectedEdges, groups, parsed.groupBy);
38305
38441
  for (const owner of completeReferenceGroupClosure(result, groups, parsed.groupBy)) {
38306
38442
  const element = linkedElementForNode(nodeById.get(owner));
38307
38443
  if (element !== void 0) {
@@ -38309,7 +38445,7 @@ function selectGraph(result, scope, query) {
38309
38445
  }
38310
38446
  }
38311
38447
  }
38312
- const completedEdges = selectedEdges.length > 0 ? selectedEdges : result.edges.filter((edge) => edge.projected !== true && selectedElements.has(edge.source) && selectedElements.has(edge.target)).map((edge) => ({ edge, source: edge.source, target: edge.target, derived: false, projected: false }));
38448
+ const completedEdges = selectedEdges.length > 0 ? selectedEdges : hasAuthoritativeEdgeSelection(returnedRelationshipPatterns, selectedStructuralRelationships) ? [] : result.edges.filter((edge) => edge.projected !== true && selectedElements.has(edge.source) && selectedElements.has(edge.target)).map((edge) => ({ edge, source: edge.source, target: edge.target, derived: false, projected: false }));
38313
38449
  const internalElementIds = new Set([...internalElements(result, rows, parsed)].filter((id) => selectedElements.has(id)));
38314
38450
  const externalElements = [...selectedElements.keys()].filter((id) => !internalElementIds.has(id) && !groupedSelectedElements.has(id));
38315
38451
  const selectedGraph2 = {
@@ -38322,7 +38458,113 @@ function selectGraph(result, scope, query) {
38322
38458
  })).filter((group) => group.elements.length > 0),
38323
38459
  externalElements
38324
38460
  };
38325
- return materializeGroupedView(applyViewBoundary(result, selectedGraph2, scope));
38461
+ const bounded = applyViewBoundary(result, selectedGraph2, scope);
38462
+ const systemSeedFiltered = scope.view === "deployment-container" || scope.view === "deployment-system" ? removeDescendantProjectionsCapturedBySystemSeeds(result, bounded, scope) : bounded;
38463
+ const materialized = materializeGroupedView(
38464
+ systemSeedFiltered,
38465
+ scope.view === "deployment" || scope.view === "deployment-container" || scope.view === "deployment-system"
38466
+ );
38467
+ if (scope.view === "deployment-container") {
38468
+ return applyDeploymentEnvironmentScope(result, materialized, scope);
38469
+ }
38470
+ if (scope.view === "deployment-system") {
38471
+ return simplifyDeploymentSystemInfrastructure(result, rollUpDeploymentSystems(result, materialized, scope));
38472
+ }
38473
+ return materialized;
38474
+ }
38475
+ function removeDescendantProjectionsCapturedBySystemSeeds(result, graph, scope) {
38476
+ const sourceSystems = new Set(result.elements.filter((element) => element.sourceIdentity === scope.tab && elementHasType(element, "SystemElement")).map((element) => element.id));
38477
+ const edges = graph.edges.filter((edge) => {
38478
+ const originSource = edge.edge.originSource ?? edge.edge.source;
38479
+ const originTarget = edge.edge.originTarget ?? edge.edge.target;
38480
+ return ![edge.source, edge.target].some((endpoint) => {
38481
+ const system = baseOccurrenceId(endpoint);
38482
+ return sourceSystems.has(system) && originSource !== system && originTarget !== system;
38483
+ });
38484
+ });
38485
+ const referenced = new Set(edges.flatMap((edge) => [edge.source, edge.target]));
38486
+ const grouped = new Set(graph.groups.flatMap((group) => group.elements));
38487
+ const retained = new Set(Object.keys(graph.elements).filter(
38488
+ (id) => referenced.has(id) || grouped.has(id) || !sourceSystems.has(baseOccurrenceId(id))
38489
+ ));
38490
+ return {
38491
+ ...graph,
38492
+ elements: Object.fromEntries(Object.entries(graph.elements).filter(([id]) => retained.has(id))),
38493
+ edges,
38494
+ groups: graph.groups.map((group) => ({
38495
+ ...group,
38496
+ elements: group.elements.filter((id) => retained.has(id))
38497
+ })).filter((group) => group.elements.length > 0),
38498
+ externalElements: graph.externalElements.filter((id) => retained.has(id))
38499
+ };
38500
+ }
38501
+ function discoverDeploymentEnvironments(result, scope) {
38502
+ const closure = tabClosure(result, scope.tab);
38503
+ const elementsById = new Map(result.elements.map((element) => [element.id, element]));
38504
+ const environmentIds = /* @__PURE__ */ new Set();
38505
+ const wireEnvironmentIds = /* @__PURE__ */ new Set();
38506
+ for (const element of result.elements) {
38507
+ if (!closure.has(element.id) || element.deployed !== true) {
38508
+ continue;
38509
+ }
38510
+ for (const targetId of [...element.attributes.runsOn ?? [], ...element.attributes.uses ?? []]) {
38511
+ const target = elementsById.get(targetId);
38512
+ if (target !== void 0 && target.context !== element.context) {
38513
+ environmentIds.add(target.context);
38514
+ }
38515
+ }
38516
+ }
38517
+ for (const edge of result.edges) {
38518
+ if (edge.projected === true || edge.sourceIdentity !== scope.tab) {
38519
+ continue;
38520
+ }
38521
+ for (const targetId of edge.attributes.uses ?? []) {
38522
+ const target = elementsById.get(targetId);
38523
+ if (target !== void 0 && target.context !== elementsById.get(edge.source)?.context) {
38524
+ wireEnvironmentIds.add(target.context);
38525
+ }
38526
+ }
38527
+ }
38528
+ for (const context of result.contexts) {
38529
+ const ownsConcreteDeployment = result.elements.some(
38530
+ (element) => element.context === context.id && elementHasType(element, "Deployment")
38531
+ );
38532
+ if (context.synthetic !== true && context.sourceIdentity === scope.tab && (context.type === "Environment" || ownsConcreteDeployment)) {
38533
+ environmentIds.add(context.id);
38534
+ }
38535
+ }
38536
+ if (environmentIds.size === 0) {
38537
+ for (const environment of wireEnvironmentIds) {
38538
+ environmentIds.add(environment);
38539
+ }
38540
+ }
38541
+ const contextsById = new Map(result.contexts.map((context) => [context.id, context]));
38542
+ const environmentNamesByContext = /* @__PURE__ */ new Map();
38543
+ for (const element of result.elements) {
38544
+ if (element.synthetic === true || element.parent !== void 0 || !elementHasType(element, "Environment")) {
38545
+ continue;
38546
+ }
38547
+ const name = element.attributes.name?.[0];
38548
+ if (name !== void 0) {
38549
+ environmentNamesByContext.set(element.context, name);
38550
+ }
38551
+ }
38552
+ return [...environmentIds].map((id) => {
38553
+ const name = environmentNamesByContext.get(id) ?? contextsById.get(id)?.attributes.name?.[0];
38554
+ return { id, ...name === void 0 ? {} : { name } };
38555
+ }).sort((left, right) => (left.name ?? left.id).localeCompare(right.name ?? right.id) || left.id.localeCompare(right.id));
38556
+ }
38557
+ function relationshipPatternsReturnedBy(query) {
38558
+ const returned = new Set(query.returns);
38559
+ return new Map(query.matches.flatMap((match) => {
38560
+ const relationship = match.pattern.relationship;
38561
+ return relationship?.alias !== void 0 && returned.has(relationship.alias) ? [[relationship.alias, relationship]] : [];
38562
+ }));
38563
+ }
38564
+ function hasAuthoritativeEdgeSelection(returned, selectedStructuralRelationships) {
38565
+ return [...returned].some(
38566
+ ([alias, pattern]) => pattern.type === "REFERENCES" || pattern.type === void 0 && !selectedStructuralRelationships.has(alias)
38567
+ );
38326
38568
  }
38327
38569
  function addSelectedEdge(edges, next, identity, identities) {
38328
38570
  const endpointKey = `${next.source}\0${next.target}`;
@@ -38347,16 +38589,27 @@ function contextualLinkedEdge(relationship) {
38347
38589
  ...originTarget === void 0 ? {} : { originTarget }
38348
38590
  };
38349
38591
  }
38350
- function materializeGroupedView(graph) {
38592
+ function materializeGroupedView(graph, materializeDeploymentPlacements = true) {
38351
38593
  const groupsByElement = /* @__PURE__ */ new Map();
38352
38594
  for (const group of graph.groups) {
38353
38595
  for (const element of group.elements) {
38354
38596
  groupsByElement.set(element, [...groupsByElement.get(element) ?? [], group]);
38355
38597
  }
38356
38598
  }
38599
+ const placementMaterializationRequired = materializeDeploymentPlacements && [...groupsByElement].some(([elementId]) => {
38600
+ const element = graph.elements[elementId];
38601
+ return element?.deployed === true && (element.attributes.runsOn?.length ?? 0) > 1;
38602
+ });
38603
+ const clonedElementIds = new Set([...groupsByElement].flatMap(([elementId, memberships]) => {
38604
+ const element = graph.elements[elementId];
38605
+ return memberships.length > 1 || placementMaterializationRequired && element?.deployed === true ? [elementId] : [];
38606
+ }));
38607
+ if (clonedElementIds.size === 0) {
38608
+ return graph;
38609
+ }
38357
38610
  const cloneIdsByElementAndGroup = /* @__PURE__ */ new Map();
38358
38611
  for (const [elementId, memberships] of groupsByElement) {
38359
- if (memberships.length < 2) {
38612
+ if (!clonedElementIds.has(elementId)) {
38360
38613
  continue;
38361
38614
  }
38362
38615
  for (const group of memberships) {
@@ -38368,11 +38621,8 @@ function materializeGroupedView(graph) {
38368
38621
  }
38369
38622
  const elements = { ...graph.elements };
38370
38623
  for (const [elementId, memberships] of groupsByElement) {
38371
- if (memberships.length < 2) {
38372
- continue;
38373
- }
38374
38624
  const element = graph.elements[elementId];
38375
- if (element === void 0) {
38625
+ if (element === void 0 || !clonedElementIds.has(elementId)) {
38376
38626
  continue;
38377
38627
  }
38378
38628
  for (const group of memberships) {
@@ -38393,15 +38643,15 @@ function materializeGroupedView(graph) {
38393
38643
  }));
38394
38644
  const edges = graph.edges.map((edge) => ({
38395
38645
  ...edge,
38396
- source: cloneEndpoint(edge.source, edge.edge.projectionScope, cloneIdsByElementAndGroup),
38397
- target: cloneEndpoint(edge.target, edge.edge.projectionScope, cloneIdsByElementAndGroup)
38646
+ source: cloneEndpoint(edge.source, edge.edge.sourcePlacement, cloneIdsByElementAndGroup),
38647
+ target: cloneEndpoint(edge.target, edge.edge.targetPlacement, cloneIdsByElementAndGroup)
38398
38648
  }));
38399
38649
  const referenced = /* @__PURE__ */ new Set([
38400
38650
  ...groups.flatMap((group) => group.elements),
38401
38651
  ...edges.flatMap((edge) => [edge.source, edge.target])
38402
38652
  ]);
38403
- for (const [elementId, memberships] of groupsByElement) {
38404
- if (memberships.length >= 2 && !referenced.has(elementId)) {
38653
+ for (const elementId of clonedElementIds) {
38654
+ if (!referenced.has(elementId)) {
38405
38655
  delete elements[elementId];
38406
38656
  }
38407
38657
  }
@@ -38412,7 +38662,11 @@ function materializeGroupedView(graph) {
38412
38662
  groups,
38413
38663
  externalElements: graph.externalElements.flatMap((element) => {
38414
38664
  const memberships = groupsByElement.get(element) ?? [];
38415
- return memberships.length < 2 ? [element] : memberships.map((group) => groupedCloneId(element, group.owner));
38665
+ const clones = memberships.flatMap((group) => {
38666
+ const clone = cloneIdsByElementAndGroup.get(groupedCloneKey(element, group.owner));
38667
+ return clone === void 0 ? [] : [clone];
38668
+ });
38669
+ return elements[element] === void 0 ? clones : [element, ...clones];
38416
38670
  })
38417
38671
  };
38418
38672
  }
@@ -38425,6 +38679,338 @@ function groupedCloneKey(elementId, groupOwner) {
38425
38679
  function groupedCloneId(elementId, groupOwner) {
38426
38680
  return `${elementId}@@${groupOwner}`;
38427
38681
  }
38682
+ function applyDeploymentEnvironmentScope(result, graph, scope) {
38683
+ const environments = discoverDeploymentEnvironments(result, scope);
38684
+ const environment = scope.environment ?? (environments.length === 1 ? environments[0]?.id : void 0);
38685
+ if (environment === void 0) {
38686
+ return emptyScopedGraph(graph);
38687
+ }
38688
+ if (!environments.some((candidate) => candidate.id === environment)) {
38689
+ return emptyScopedGraph(graph);
38690
+ }
38691
+ const elementsById = new Map(result.elements.map((element) => [element.id, element]));
38692
+ const sourceElements = tabClosure(result, scope.tab);
38693
+ const groups = graph.groups.filter((group) => elementEnvironment(group.owner, elementsById) === environment);
38694
+ const selectedIds = new Set(groups.flatMap((group) => [group.owner, ...group.elements]));
38695
+ const candidateEdges = graph.edges.filter((edge) => deploymentEdgeInEnvironment(edge, environment, elementsById));
38696
+ const edges = candidateEdges.filter((edge) => {
38697
+ const sourceAllowed = deploymentEndpointAllowed(edge.source, environment, elementsById);
38698
+ const targetAllowed = deploymentEndpointAllowed(edge.target, environment, elementsById);
38699
+ return sourceAllowed && targetAllowed;
38700
+ });
38701
+ for (const edge of edges) {
38702
+ selectedIds.add(edge.source);
38703
+ selectedIds.add(edge.target);
38704
+ }
38705
+ const elements = Object.fromEntries(Object.entries(graph.elements).filter(([id]) => selectedIds.has(id)));
38706
+ const externalElements = new Set(graph.externalElements.filter((id) => selectedIds.has(id)));
38707
+ for (const id of selectedIds) {
38708
+ const occurrenceEnvironment = deploymentOccurrenceEnvironment(id, elementsById);
38709
+ if (occurrenceEnvironment !== void 0 && occurrenceEnvironment !== environment && isLogicalDeploymentEndpoint(id, elementsById)) {
38710
+ externalElements.add(id);
38711
+ }
38712
+ if (isLogicalDeploymentEndpoint(id, elementsById) && !sourceElements.has(baseOccurrenceId(id))) {
38713
+ externalElements.add(id);
38714
+ }
38715
+ }
38716
+ return {
38717
+ ...graph,
38718
+ elements,
38719
+ edges,
38720
+ groups: groups.map((group) => ({ ...group, elements: group.elements.filter((id) => selectedIds.has(id)) })),
38721
+ externalElements: [...externalElements]
38722
+ };
38723
+ }
38724
+ function emptyScopedGraph(graph) {
38725
+ return { ...graph, elements: {}, edges: [], groups: [], externalElements: [] };
38726
+ }
38727
+ function deploymentEdgeInEnvironment(edge, environment, elementsById) {
38728
+ const scopes = [edge.edge.projectionRoot, edge.edge.sourcePlacement, edge.edge.targetPlacement].filter((id) => id !== void 0);
38729
+ if (scopes.some((id) => elementEnvironment(id, elementsById) === environment)) {
38730
+ return true;
38731
+ }
38732
+ return [edge.source, edge.target].some((id) => elementEnvironment(id, elementsById) === environment);
38733
+ }
38734
+ function deploymentEndpointAllowed(id, environment, elementsById) {
38735
+ return isLogicalDeploymentEndpoint(id, elementsById) || elementEnvironment(id, elementsById) === environment;
38736
+ }
38737
+ function isLogicalDeploymentEndpoint(id, elementsById) {
38738
+ const element = elementsById.get(baseOccurrenceId(id));
38739
+ return element !== void 0 && !elementHasType(element, "InfrastructureComponent");
38740
+ }
38741
+ function deploymentOccurrenceEnvironment(id, elementsById) {
38742
+ const separator = id.indexOf("@@");
38743
+ if (separator >= 0) {
38744
+ return elementEnvironment(id.slice(separator + 2), elementsById);
38745
+ }
38746
+ const runsOn = elementsById.get(id)?.attributes.runsOn ?? [];
38747
+ return runsOn.length === 1 ? elementEnvironment(runsOn[0], elementsById) : void 0;
38748
+ }
38749
+ function elementEnvironment(id, elementsById) {
38750
+ return elementsById.get(baseOccurrenceId(id))?.context;
38751
+ }
38752
+ function baseOccurrenceId(id) {
38753
+ const separator = id.indexOf("@@");
38754
+ return separator < 0 ? id : id.slice(0, separator);
38755
+ }
38756
+ function rollUpDeploymentSystems(result, graph, scope) {
38757
+ const elementsById = new Map(result.elements.map((element) => [element.id, element]));
38758
+ const parentByChild = new Map(result.elements.flatMap(
38759
+ (element) => element.parent === void 0 ? [] : [[element.id, element.parent]]
38760
+ ));
38761
+ const systemFor = (id) => lineage(baseOccurrenceId(id), parentByChild).find((candidate) => elementHasType(elementsById.get(candidate), "SystemElement"));
38762
+ const fold = (id) => {
38763
+ const system = systemFor(id);
38764
+ if (system === void 0) {
38765
+ return id;
38766
+ }
38767
+ const separator = id.indexOf("@@");
38768
+ return separator < 0 ? system : `${system}${id.slice(separator)}`;
38769
+ };
38770
+ const foldedElement = (id) => {
38771
+ const folded = fold(id);
38772
+ const base = elementsById.get(baseOccurrenceId(folded));
38773
+ if (base === void 0) {
38774
+ return graph.elements[id];
38775
+ }
38776
+ return folded === base.id ? base : { ...base, id: folded, attributes: { ...base.attributes, projectedFrom: [base.id] } };
38777
+ };
38778
+ const groups = graph.groups.map((group) => ({
38779
+ ...group,
38780
+ elements: [...new Set(group.elements.map(fold))]
38781
+ })).filter((group) => group.elements.length > 0);
38782
+ const edges = [];
38783
+ for (const edge of graph.edges) {
38784
+ const originSource = edge.edge.originSource ?? edge.edge.source;
38785
+ const originTarget = edge.edge.originTarget ?? edge.edge.target;
38786
+ const originSourceSystem = systemFor(originSource);
38787
+ const originTargetSystem = systemFor(originTarget);
38788
+ if (originSource !== originTarget && originSourceSystem !== void 0 && originSourceSystem === originTargetSystem) {
38789
+ continue;
38790
+ }
38791
+ const next = { ...edge, source: fold(edge.source), target: fold(edge.target) };
38792
+ if (next.source !== next.target) {
38793
+ addFoldedViewEdge(edges, next);
38794
+ }
38795
+ }
38796
+ const referenced = /* @__PURE__ */ new Set([
38797
+ ...groups.flatMap((group) => [group.owner, ...group.elements]),
38798
+ ...edges.flatMap((edge) => [edge.source, edge.target])
38799
+ ]);
38800
+ const elements = {};
38801
+ for (const id of referenced) {
38802
+ const direct = graph.elements[id];
38803
+ const element = direct ?? [...Object.keys(graph.elements)].filter((candidate) => fold(candidate) === id).map(foldedElement).find((candidate) => candidate !== void 0);
38804
+ if (element !== void 0) {
38805
+ elements[id] = element.id === id ? element : { ...element, id };
38806
+ }
38807
+ }
38808
+ const sourceElements = tabClosure(result, scope.tab);
38809
+ const externalElements = new Set(graph.externalElements.map(fold).filter((id) => referenced.has(id)));
38810
+ for (const id of referenced) {
38811
+ const system = systemFor(id);
38812
+ if (system !== void 0 && !sourceElements.has(system)) {
38813
+ externalElements.add(id);
38814
+ }
38815
+ }
38816
+ return {
38817
+ ...graph,
38818
+ elements,
38819
+ edges,
38820
+ groups,
38821
+ externalElements: [...externalElements]
38822
+ };
38823
+ }
38824
+ function simplifyDeploymentSystemInfrastructure(result, graph) {
38825
+ const elementsById = new Map(result.elements.map((element) => [element.id, element]));
38826
+ const parentByChild = new Map(result.elements.flatMap(
38827
+ (element) => element.parent === void 0 ? [] : [[element.id, element.parent]]
38828
+ ));
38829
+ const externalElements = new Set(graph.externalElements);
38830
+ const placementGroupOwners = new Set(graph.groups.map((group) => group.owner));
38831
+ const retained = new Set(Object.keys(graph.elements).filter((id) => {
38832
+ const element = elementsById.get(baseOccurrenceId(id)) ?? graph.elements[id];
38833
+ return !elementHasType(element, "InfrastructureComponent") || externalElements.has(id) && !placementGroupOwners.has(id);
38834
+ }));
38835
+ const outgoing = /* @__PURE__ */ new Map();
38836
+ const incoming = /* @__PURE__ */ new Map();
38837
+ for (const edge of graph.edges) {
38838
+ const sourceEdges = outgoing.get(edge.source) ?? [];
38839
+ sourceEdges.push(edge);
38840
+ outgoing.set(edge.source, sourceEdges);
38841
+ const targetEdges = incoming.get(edge.target) ?? [];
38842
+ targetEdges.push(edge);
38843
+ incoming.set(edge.target, targetEdges);
38844
+ }
38845
+ const edges = [];
38846
+ const trace = (source, first) => {
38847
+ const visited = /* @__PURE__ */ new Set();
38848
+ const follow = (edge, carrier) => {
38849
+ const target = edge.target;
38850
+ const nextCarrier = logicalRelationshipCarrier(carrier, edge);
38851
+ if (target === source) {
38852
+ return;
38853
+ }
38854
+ if (retained.has(target)) {
38855
+ addFoldedViewEdge(edges, { ...nextCarrier, source, target });
38856
+ return;
38857
+ }
38858
+ if (visited.has(target)) {
38859
+ return;
38860
+ }
38861
+ visited.add(target);
38862
+ for (const next of outgoing.get(target) ?? []) {
38863
+ follow(next, nextCarrier);
38864
+ }
38865
+ };
38866
+ follow(first, first);
38867
+ };
38868
+ for (const source of retained) {
38869
+ for (const edge of outgoing.get(source) ?? []) {
38870
+ trace(source, edge);
38871
+ }
38872
+ }
38873
+ const systemFor = (id) => lineage(baseOccurrenceId(id), parentByChild).find((candidate) => elementHasType(elementsById.get(candidate), "SystemElement"));
38874
+ for (const source of Object.keys(graph.elements)) {
38875
+ if (retained.has(source) || (incoming.get(source)?.length ?? 0) > 0) {
38876
+ continue;
38877
+ }
38878
+ for (const edge of outgoing.get(source) ?? []) {
38879
+ const originSource = edge.edge.originSource ?? edge.edge.source;
38880
+ const system = systemFor(originSource);
38881
+ if (system === void 0) {
38882
+ continue;
38883
+ }
38884
+ const placement = edge.edge.sourcePlacement ?? edge.edge.targetPlacement ?? edge.edge.projectionScope;
38885
+ const occurrence = placement === void 0 ? system : `${system}@@${placement}`;
38886
+ const logicalSource = retained.has(occurrence) ? occurrence : retained.has(system) ? system : void 0;
38887
+ if (logicalSource !== void 0) {
38888
+ trace(logicalSource, edge);
38889
+ }
38890
+ }
38891
+ }
38892
+ const projectedByLogicalEdge = /* @__PURE__ */ new Map();
38893
+ for (const edge of graph.edges) {
38894
+ const originSource = edge.edge.originSource;
38895
+ const originTarget = edge.edge.originTarget;
38896
+ if (edge.projected !== true || originSource === void 0 || originTarget === void 0) {
38897
+ continue;
38898
+ }
38899
+ const projectionScope = edge.edge.projectionRoot ?? edge.edge.projectionScope ?? edge.edge.sourcePlacement ?? edge.edge.targetPlacement ?? "";
38900
+ const key = `${originSource}\0${originTarget}\0${projectionScope}`;
38901
+ const related = projectedByLogicalEdge.get(key) ?? [];
38902
+ related.push(edge);
38903
+ projectedByLogicalEdge.set(key, related);
38904
+ }
38905
+ for (const related of projectedByLogicalEdge.values()) {
38906
+ const first = related[0];
38907
+ const originSource = first.edge.originSource;
38908
+ const originTarget = first.edge.originTarget;
38909
+ const sourceSystem = systemFor(originSource);
38910
+ const targetSystem = systemFor(originTarget);
38911
+ if (sourceSystem === void 0 || targetSystem === void 0 || sourceSystem === targetSystem) {
38912
+ continue;
38913
+ }
38914
+ const projectionEnvironment = first.edge.projectionRoot === void 0 ? void 0 : elementEnvironment(first.edge.projectionRoot, elementsById);
38915
+ const occurrence = (system) => {
38916
+ const relatedCandidates = related.flatMap((edge) => [edge.source, edge.target]).filter((id) => retained.has(id) && systemFor(id) === system);
38917
+ const retainedCandidates = [...retained].filter((id) => systemFor(id) === system);
38918
+ const candidates = [.../* @__PURE__ */ new Set([...relatedCandidates, ...retainedCandidates])];
38919
+ if (projectionEnvironment !== void 0) {
38920
+ return candidates.find((id) => deploymentOccurrenceEnvironment(id, elementsById) === projectionEnvironment) ?? candidates.find((id) => id === system);
38921
+ }
38922
+ return candidates[0];
38923
+ };
38924
+ const source = occurrence(sourceSystem);
38925
+ const target = occurrence(targetSystem);
38926
+ const relatedSimplifiedEdges = edges.filter(
38927
+ (edge) => linkedEdgeHasProjectionOrigin(edge.edge, originSource, originTarget) && (edge.edge.projectionRoot ?? edge.edge.projectionScope ?? edge.edge.sourcePlacement ?? edge.edge.targetPlacement ?? "") === (first.edge.projectionRoot ?? first.edge.projectionScope ?? first.edge.sourcePlacement ?? first.edge.targetPlacement ?? "")
38928
+ );
38929
+ if (source === void 0 || target === void 0 || source === target || endpointsConnected(source, target, relatedSimplifiedEdges)) {
38930
+ continue;
38931
+ }
38932
+ for (const carrier of related.filter((edge) => (edge.edge.attributes.model?.length ?? 0) > 0)) {
38933
+ addFoldedViewEdge(edges, { ...carrier, source, target });
38934
+ }
38935
+ }
38936
+ const environmentRootByContext = new Map(result.elements.filter((element) => element.synthetic !== true && element.parent === void 0 && elementHasType(element, "Environment")).map((element) => [element.context, element.id]));
38937
+ const groupedEnvironmentsByElement = /* @__PURE__ */ new Map();
38938
+ for (const group of graph.groups) {
38939
+ const environment = elementEnvironment(group.owner, elementsById);
38940
+ if (environment === void 0 || !environmentRootByContext.has(environment)) {
38941
+ continue;
38942
+ }
38943
+ for (const id of group.elements) {
38944
+ if (!retained.has(id)) {
38945
+ continue;
38946
+ }
38947
+ const environments = groupedEnvironmentsByElement.get(id) ?? /* @__PURE__ */ new Set();
38948
+ environments.add(environment);
38949
+ groupedEnvironmentsByElement.set(id, environments);
38950
+ }
38951
+ }
38952
+ const groupMembersByEnvironment = /* @__PURE__ */ new Map();
38953
+ for (const id of retained) {
38954
+ const element = elementsById.get(baseOccurrenceId(id)) ?? graph.elements[id];
38955
+ const environments = new Set(groupedEnvironmentsByElement.get(id) ?? []);
38956
+ const occurrenceEnvironment = deploymentOccurrenceEnvironment(id, elementsById);
38957
+ if (occurrenceEnvironment !== void 0) {
38958
+ environments.add(occurrenceEnvironment);
38959
+ }
38960
+ if (environments.size === 0 && elementHasType(element, "InfrastructureComponent") && environmentRootByContext.has(element?.context ?? "")) {
38961
+ environments.add(element.context);
38962
+ }
38963
+ for (const environment of environments) {
38964
+ const members = groupMembersByEnvironment.get(environment) ?? [];
38965
+ members.push(id);
38966
+ groupMembersByEnvironment.set(environment, members);
38967
+ }
38968
+ }
38969
+ const groups = [...groupMembersByEnvironment].map(([environment, members]) => ({
38970
+ owner: environmentRootByContext.get(environment) ?? environment,
38971
+ elements: members
38972
+ }));
38973
+ return {
38974
+ ...graph,
38975
+ elements: Object.fromEntries(Object.entries(graph.elements).filter(([id]) => retained.has(id))),
38976
+ edges,
38977
+ groups,
38978
+ externalElements: graph.externalElements.filter((id) => retained.has(id))
38979
+ };
38980
+ }
38981
+ function linkedEdgeHasProjectionOrigin(edge, source, target) {
38982
+ return edge.originSource === source && edge.originTarget === target || edge.projectionOrigins?.some((origin) => origin.source === source && origin.target === target) === true;
38983
+ }
38984
+ function endpointsConnected(source, target, edges) {
38985
+ const neighbors = /* @__PURE__ */ new Map();
38986
+ for (const edge of edges) {
38987
+ const sourceNeighbors = neighbors.get(edge.source) ?? /* @__PURE__ */ new Set();
38988
+ sourceNeighbors.add(edge.target);
38989
+ neighbors.set(edge.source, sourceNeighbors);
38990
+ const targetNeighbors = neighbors.get(edge.target) ?? /* @__PURE__ */ new Set();
38991
+ targetNeighbors.add(edge.source);
38992
+ neighbors.set(edge.target, targetNeighbors);
38993
+ }
38994
+ const pending = [source];
38995
+ const visited = /* @__PURE__ */ new Set();
38996
+ while (pending.length > 0) {
38997
+ const current = pending.pop();
38998
+ if (current === target) {
38999
+ return true;
39000
+ }
39001
+ if (visited.has(current)) {
39002
+ continue;
39003
+ }
39004
+ visited.add(current);
39005
+ pending.push(...neighbors.get(current) ?? []);
39006
+ }
39007
+ return false;
39008
+ }
39009
+ function logicalRelationshipCarrier(current, candidate) {
39010
+ const currentModel = current.edge.attributes.model;
39011
+ const candidateModel = candidate.edge.attributes.model;
39012
+ return (currentModel === void 0 || currentModel.length === 0) && candidateModel !== void 0 && candidateModel.length > 0 ? candidate : current;
39013
+ }
38428
39014
  function internalElements(result, rows, query) {
38429
39015
  const aliases = new Set(query.matches.flatMap(
38430
39016
  (match) => match.optional ? [] : patternNodeAliases(match.pattern)
@@ -39323,6 +39909,9 @@ function edgePropertyValue(relationship, name) {
39323
39909
  if (name === "context") {
39324
39910
  return relationship.context;
39325
39911
  }
39912
+ if (name === "projectionRoot") {
39913
+ return edge?.projectionRoot;
39914
+ }
39326
39915
  const values = edge?.attributes[name];
39327
39916
  if (edge?.listAttributes?.includes(name) === true) {
39328
39917
  return values;
@@ -39364,11 +39953,16 @@ function collectGroupValue(groups, node, owner, label) {
39364
39953
  elements: [...elements]
39365
39954
  });
39366
39955
  }
39367
- function collectSelectedReferenceGroups(selectedElements, groups, expression) {
39956
+ function collectSelectedReferenceGroups(selectedElements, selectedEdges, groups, expression) {
39368
39957
  const grouped = /* @__PURE__ */ new Set();
39369
39958
  if (expression.kind !== "property") {
39370
39959
  return grouped;
39371
39960
  }
39961
+ for (const group of groups.values()) {
39962
+ for (const element of group.elements) {
39963
+ grouped.add(element);
39964
+ }
39965
+ }
39372
39966
  for (const element of selectedElements.values()) {
39373
39967
  if (element.referenceAttributes?.includes(expression.property) !== true) {
39374
39968
  continue;
@@ -39376,13 +39970,29 @@ function collectSelectedReferenceGroups(selectedElements, groups, expression) {
39376
39970
  const node = { kind: "element", id: element.id, element };
39377
39971
  const value = propertyValue(node, expression.property);
39378
39972
  const owners = Array.isArray(value) ? value : isQueryNode(value) ? [value.id] : [];
39379
- for (const owner of owners) {
39973
+ const selectedOwners = expression.property === "runsOn" && owners.length > 1 ? [] : owners;
39974
+ for (const owner of selectedOwners) {
39380
39975
  collectGroupValue(groups, node, owner, void 0);
39381
39976
  grouped.add(element.id);
39382
39977
  }
39383
39978
  }
39979
+ for (const edge of selectedEdges) {
39980
+ collectSelectedEdgePlacementGroup(selectedElements, groups, grouped, expression.property, edge.source, edge.edge.sourcePlacement);
39981
+ collectSelectedEdgePlacementGroup(selectedElements, groups, grouped, expression.property, edge.target, edge.edge.targetPlacement);
39982
+ }
39384
39983
  return grouped;
39385
39984
  }
39985
+ function collectSelectedEdgePlacementGroup(selectedElements, groups, grouped, propertyName, elementId, placement) {
39986
+ if (placement === void 0) {
39987
+ return;
39988
+ }
39989
+ const element = selectedElements.get(elementId);
39990
+ if (element?.referenceAttributes?.includes(propertyName) !== true || element.attributes[propertyName]?.includes(placement) !== true) {
39991
+ return;
39992
+ }
39993
+ collectGroupValue(groups, { kind: "element", id: element.id, element }, placement, void 0);
39994
+ grouped.add(element.id);
39995
+ }
39386
39996
  function completeReferenceGroupClosure(result, groups, expression) {
39387
39997
  if (expression.kind !== "property") {
39388
39998
  return /* @__PURE__ */ new Set();
@@ -39800,11 +40410,13 @@ var BUILTIN_VIEW_QUERIES = {
39800
40410
  "c2": "MATCH (container:ContainerElement)\nWHERE container.sourceIdentity = $tab\nOPTIONAL MATCH (container)-[containerLink:REFERENCES {withDerived}]-(related:Element)\nWHERE related IS ContainerElement OR related IS SystemElement\nMATCH (boundaryContainer:ContainerElement)\nWHERE boundaryContainer = container OR boundaryContainer = related\nGROUP BY boundaryContainer.parent\nRETURN boundaryContainer, containerLink, related",
39801
40411
  "c3": "MATCH (container:ContainerElement)-[contains:CONTAINS]->(component:ComponentElement)\nWHERE container.sourceIdentity = $tab\nOPTIONAL MATCH (component)-[componentLink:REFERENCES {withDerived}]-(related:Element)\nWHERE related IS ComponentElement OR related IS ContainerElement OR related IS SystemElement\nGROUP BY component.parent\nRETURN component, componentLink, related",
39802
40412
  "c4": "MATCH (code:CodeElement)\nWHERE code.sourceIdentity = $tab\nOPTIONAL MATCH (code)-[link:REFERENCES]-(related:Element)\nWHERE related IS CodeElement OR related IS ComponentElement OR related IS ContainerElement OR related IS SystemElement\nGROUP BY code.parent\nRETURN code, link, related",
39803
- "deployment": "MATCH (node:Element)\nWHERE node.sourceIdentity = $tab\n AND (node IS InfrastructureComponent\n OR ((node IS ContainerElement OR node IS External) AND node.deployed = true))\nOPTIONAL MATCH (deploymentTarget:InfrastructureComponent)\nWHERE deploymentTarget IN node.uses OR deploymentTarget = node.runsOn\nOPTIONAL MATCH ROLLUP (node)-[projectedLink:REFERENCES {projected}]-(projectedPeer:Element)\nWHERE (projectedPeer IS InfrastructureComponent\n OR (projectedPeer IS ContainerElement AND projectedPeer.deployed = true)\n OR projectedPeer IS External)\n AND projectedPeer.id <> node.id\nOPTIONAL MATCH (node)-[directDeploymentLink:REFERENCES]-(directDeploymentTarget:Element)\nWHERE node IS InfrastructureComponent\n AND (directDeploymentTarget IS InfrastructureComponent OR directDeploymentTarget IS External)\nGROUP BY node.runsOn\nRETURN node, deploymentTarget, projectedLink, projectedPeer, directDeploymentLink, directDeploymentTarget"
40413
+ "deployment-system": "MATCH (node:Element)\nWHERE node.sourceIdentity = $tab\n AND (node IS InfrastructureComponent\n OR node IS SystemElement\n OR ((node IS ContainerElement OR node IS External) AND node.deployed = true))\n AND node.id = node.id\nOPTIONAL MATCH (deploymentTarget:InfrastructureComponent)\nWHERE deploymentTarget IN node.uses OR deploymentTarget IN node.runsOn\nOPTIONAL MATCH ROLLUP (node)-[projectedLink:REFERENCES {projected}]-(projectedPeer:Element)\nWHERE (projectedPeer IS InfrastructureComponent\n OR (projectedPeer IS ContainerElement AND projectedPeer.deployed = true)\n OR projectedPeer IS SystemElement\n OR projectedPeer IS External)\n AND projectedPeer.id <> node.id\nOPTIONAL MATCH (node)-[directDeploymentLink:REFERENCES]-(directDeploymentTarget:Element)\nWHERE node IS InfrastructureComponent\n AND (directDeploymentTarget IS InfrastructureComponent OR directDeploymentTarget IS External)\nGROUP BY node.runsOn\nRETURN node, deploymentTarget, projectedLink, projectedPeer, directDeploymentLink, directDeploymentTarget",
40414
+ "deployment-container": "MATCH (node:Element)\nWHERE node.sourceIdentity = $tab\n AND (node IS InfrastructureComponent\n OR node IS SystemElement\n OR ((node IS ContainerElement OR node IS External) AND node.deployed = true))\n AND node.context = node.context\nOPTIONAL MATCH (deploymentTarget:InfrastructureComponent)\nWHERE deploymentTarget IN node.uses OR deploymentTarget IN node.runsOn\nOPTIONAL MATCH ROLLUP (node)-[projectedLink:REFERENCES {projected}]-(projectedPeer:Element)\nWHERE (projectedPeer IS InfrastructureComponent\n OR (projectedPeer IS ContainerElement AND projectedPeer.deployed = true)\n OR projectedPeer IS SystemElement\n OR projectedPeer IS External)\n AND projectedPeer.id <> node.id\nOPTIONAL MATCH (node)-[directDeploymentLink:REFERENCES]-(directDeploymentTarget:Element)\nWHERE node IS InfrastructureComponent\n AND (directDeploymentTarget IS InfrastructureComponent OR directDeploymentTarget IS External)\nGROUP BY node.runsOn\nRETURN node, deploymentTarget, projectedLink, projectedPeer, directDeploymentLink, directDeploymentTarget",
40415
+ "deployment": "MATCH (node:Element)\nWHERE node.sourceIdentity = $tab\n AND (node IS InfrastructureComponent\n OR ((node IS ContainerElement OR node IS External) AND node.deployed = true))\nOPTIONAL MATCH (deploymentTarget:InfrastructureComponent)\nWHERE deploymentTarget IN node.uses OR deploymentTarget IN node.runsOn\nOPTIONAL MATCH ROLLUP (node)-[projectedLink:REFERENCES {projected}]-(projectedPeer:Element)\nWHERE (projectedPeer IS InfrastructureComponent\n OR (projectedPeer IS ContainerElement AND projectedPeer.deployed = true)\n OR projectedPeer IS External)\n AND projectedPeer.id <> node.id\nOPTIONAL MATCH (node)-[directDeploymentLink:REFERENCES]-(directDeploymentTarget:Element)\nWHERE node IS InfrastructureComponent\n AND (directDeploymentTarget IS InfrastructureComponent OR directDeploymentTarget IS External)\nGROUP BY node.runsOn\nRETURN node, deploymentTarget, projectedLink, projectedPeer, directDeploymentLink, directDeploymentTarget"
39804
40416
  };
39805
40417
 
39806
40418
  // src/version.ts
39807
- var version = "3.0.2";
40419
+ var version = "3.0.3";
39808
40420
 
39809
40421
  // src/index.ts
39810
40422
  var hiddenStructureTypes = /* @__PURE__ */ new Set(["List", "Nothing", "Text"]);
@@ -39860,6 +40472,9 @@ async function main() {
39860
40472
  case "structure":
39861
40473
  await runStructure(args);
39862
40474
  return;
40475
+ case "environments":
40476
+ await runEnvironments(args);
40477
+ return;
39863
40478
  case "skill":
39864
40479
  await runSkill(args);
39865
40480
  return;
@@ -39945,8 +40560,38 @@ async function runStructure(args) {
39945
40560
  }
39946
40561
  await writeOutput(args.output, formatStructure(structure));
39947
40562
  }
39948
- async function runSkill(args) {
39949
- if (args.skillAction !== "init") {
40563
+ async function runEnvironments(args) {
40564
+ const project = await loadProject(projectPath(args));
40565
+ if (hasErrors(project.diagnostics)) {
40566
+ process.stderr.write(formatDiagnostics(project.diagnostics));
40567
+ process.exitCode = 1;
40568
+ return;
40569
+ }
40570
+ const source = args.tab === void 0 ? void 0 : selectedSource(project, args.tab, false, void 0);
40571
+ const declared = declaredDeploymentEnvironments(project.result);
40572
+ const declaredById = new Map(declared.map((environment) => [environment.id, environment]));
40573
+ const environments = source === void 0 ? declared : discoverDeploymentEnvironments(project.result, { tab: source }).map((environment) => {
40574
+ const declaration = declaredById.get(environment.id);
40575
+ if (declaration === void 0) {
40576
+ throw new CliError(`Environment '${environment.id}' is relevant to '${source}' but has no environment declaration`);
40577
+ }
40578
+ const name = environment.name ?? declaration.name;
40579
+ return {
40580
+ id: environment.id,
40581
+ ...name === void 0 ? {} : { name },
40582
+ source: declaration.source
40583
+ };
40584
+ });
40585
+ const result = {
40586
+ schemaVersion: "deployment-environments.v1",
40587
+ source: source ?? null,
40588
+ environments
40589
+ };
40590
+ const format = outputFormat(args.format, "text");
40591
+ await writeOutput(args.output, format === "json" ? JSON.stringify(result, null, 2) : formatDeploymentEnvironments(result.environments));
40592
+ }
40593
+ async function runSkill(args) {
40594
+ if (args.skillAction !== "init") {
39950
40595
  throw new CliError("Usage: archinsight skill init [project-dir] [--target generic|codex|claude] [--out dir] [--force]");
39951
40596
  }
39952
40597
  const target = skillTarget(args.target);
@@ -40046,13 +40691,70 @@ async function loadProject(input) {
40046
40691
  };
40047
40692
  }
40048
40693
  async function selectedGraph(project, args) {
40049
- const context = args.context ?? firstContext(project);
40050
- const tab = selectedSource(project, args.tab);
40051
- const view = args.view ?? (args.queryFile === void 0 ? "c1" : void 0);
40052
- const scope = { context, tab, ...view === void 0 ? {} : { view } };
40694
+ const view = args.queryFile === void 0 ? args.view ?? "c1" : void 0;
40053
40695
  const query = args.queryFile === void 0 ? viewQueries[view ?? "c1"] : await readQueryFile(project.root, args.queryFile);
40696
+ const queryNeedsTab = queryUsesVariable(query, "tab");
40697
+ const queryNeedsContext = queryUsesVariable(query, "context");
40698
+ const sourceRequired = view !== void 0 && view !== "c1" && view !== "no-filter" || queryNeedsTab;
40699
+ const tab = selectedSource(project, args.tab, sourceRequired, view);
40700
+ const context = selectedContext(project, args.context, tab, view !== void 0 || queryNeedsContext, view);
40701
+ const environment = deploymentEnvironmentOption(project, args, tab, view);
40702
+ const scope = {
40703
+ ...context === void 0 ? {} : { context },
40704
+ ...tab === void 0 ? {} : { tab },
40705
+ ...view === void 0 ? {} : { view },
40706
+ ...environment === void 0 ? {} : { environment }
40707
+ };
40054
40708
  return selectGraph(project.result, scope, query);
40055
40709
  }
40710
+ function deploymentEnvironmentOption(project, args, tab, view) {
40711
+ if (view !== "deployment-container") {
40712
+ if (args.environment !== void 0) {
40713
+ throw new CliError("Option '--environment' is supported only by the 'deployment-container' view");
40714
+ }
40715
+ return void 0;
40716
+ }
40717
+ if (tab === void 0) {
40718
+ throw new CliError("View 'deployment-container' requires --source");
40719
+ }
40720
+ const environments = discoverDeploymentEnvironments(project.result, { tab });
40721
+ if (args.environment !== void 0) {
40722
+ if (!environments.some((candidate) => candidate.id === args.environment)) {
40723
+ throw new CliError(`Environment '${args.environment}' is not relevant to '${tab}'. Available environments: ${environmentList(environments)}`);
40724
+ }
40725
+ return args.environment;
40726
+ }
40727
+ if (environments.length === 1) {
40728
+ return environments[0].id;
40729
+ }
40730
+ if (environments.length === 0) {
40731
+ throw new CliError(`No deployment environments are relevant to '${tab}'`);
40732
+ }
40733
+ throw new CliError(`View 'deployment-container' requires --environment. Available environments: ${environmentList(environments)}`);
40734
+ }
40735
+ function environmentList(environments) {
40736
+ return environments.map(
40737
+ (environment) => environment.name === void 0 || environment.name === environment.id ? environment.id : `${environment.id} (${environment.name})`
40738
+ ).join(", ") || "none";
40739
+ }
40740
+ function declaredDeploymentEnvironments(result) {
40741
+ return result.elements.filter((element) => element.synthetic !== true && element.parent === void 0 && (element.type === "Environment" || element.baseTypes.includes("Environment"))).map((element) => ({
40742
+ id: element.context,
40743
+ ...element.attributes.name?.[0] === void 0 ? {} : { name: element.attributes.name[0] },
40744
+ source: element.sourceIdentity
40745
+ })).sort(
40746
+ (left, right) => (left.name ?? left.id).localeCompare(right.name ?? right.id) || left.id.localeCompare(right.id)
40747
+ );
40748
+ }
40749
+ function formatDeploymentEnvironments(environments) {
40750
+ if (environments.length === 0) {
40751
+ return "";
40752
+ }
40753
+ return `${environments.map(
40754
+ (environment) => `${environment.id} ${environment.name ?? ""} ${environment.source}`
40755
+ ).join("\n")}
40756
+ `;
40757
+ }
40056
40758
  async function readQueryFile(projectRoot, queryFile) {
40057
40759
  const file = path.isAbsolute(queryFile) ? queryFile : path.resolve(projectRoot, queryFile);
40058
40760
  return readFile(file, "utf8");
@@ -40060,19 +40762,16 @@ async function readQueryFile(projectRoot, queryFile) {
40060
40762
  function projectPath(args) {
40061
40763
  return args.input ?? ".";
40062
40764
  }
40063
- function firstContext(project) {
40064
- const context = project.result.contexts[0]?.id;
40065
- if (context === void 0) {
40066
- throw new CliError("No context found; pass --context explicitly after fixing diagnostics");
40765
+ function selectedSource(project, source, required, view) {
40766
+ if (source === void 0 && project.sources.length === 1) {
40767
+ return project.sources[0].sourceName;
40067
40768
  }
40068
- return context;
40069
- }
40070
- function firstTab(project) {
40071
- return project.sources[0]?.sourceName ?? "";
40072
- }
40073
- function selectedSource(project, source) {
40074
40769
  if (source === void 0) {
40075
- return firstTab(project);
40770
+ if (required) {
40771
+ const subject = view === void 0 ? "This query" : `View '${view}'`;
40772
+ throw new CliError(`${subject} requires --source because it is scoped to a model file`);
40773
+ }
40774
+ return void 0;
40076
40775
  }
40077
40776
  const normalized = normalizeSourceName(project.root, source);
40078
40777
  if (project.sources.some((item) => item.sourceName === normalized)) {
@@ -40080,6 +40779,41 @@ function selectedSource(project, source) {
40080
40779
  }
40081
40780
  throw new CliError(`Source '${source}' is not part of project '${project.root}'`);
40082
40781
  }
40782
+ function selectedContext(project, explicitContext, source, required, view) {
40783
+ const sourceContext = source === void 0 ? void 0 : project.result.contexts.find((context) => context.sourceIdentity === source);
40784
+ if (sourceContext?.synthetic === true && required) {
40785
+ throw new CliError(`Source '${source}' contains definitions and does not declare a renderable context or environment`);
40786
+ }
40787
+ if (view === "c1" && sourceContext !== void 0 && sourceContext.type !== "Context") {
40788
+ throw new CliError(`Source '${source}' does not declare a logical context for the C1 view`);
40789
+ }
40790
+ const available = availableContextIds(project, view);
40791
+ if (explicitContext !== void 0 && !available.includes(explicitContext)) {
40792
+ throw new CliError(`Context '${explicitContext}' is not declared. Available contexts: ${available.join(", ") || "none"}`);
40793
+ }
40794
+ if (explicitContext !== void 0 && sourceContext?.synthetic !== true && sourceContext !== void 0 && sourceContext.id !== explicitContext) {
40795
+ throw new CliError(`Context '${explicitContext}' conflicts with source '${source}', which declares context '${sourceContext.id}'`);
40796
+ }
40797
+ if (explicitContext !== void 0) {
40798
+ return explicitContext;
40799
+ }
40800
+ if (sourceContext?.synthetic !== true && sourceContext !== void 0) {
40801
+ return sourceContext.id;
40802
+ }
40803
+ if (!required) {
40804
+ return void 0;
40805
+ }
40806
+ if (available.length === 1) {
40807
+ return available[0];
40808
+ }
40809
+ throw new CliError(`Cannot infer context. Pass --source or --context. Available contexts: ${available.join(", ") || "none"}`);
40810
+ }
40811
+ function availableContextIds(project, view) {
40812
+ return [...new Set(project.result.contexts.filter((context) => context.synthetic !== true && (view !== "c1" || context.type === "Context")).map((context) => context.id))].sort();
40813
+ }
40814
+ function queryUsesVariable(query, variable) {
40815
+ return new RegExp(`\\$${variable}\\b`).test(query);
40816
+ }
40083
40817
  function normalizeSourceName(root, source) {
40084
40818
  const value = path.isAbsolute(source) ? path.relative(root, source) : source;
40085
40819
  return value.replaceAll("\\", "/").replace(/^\.\//, "");
@@ -40358,6 +41092,7 @@ function parseArgs(argv) {
40358
41092
  context: stringOption(options.context),
40359
41093
  tab: stringOption(options.tab),
40360
41094
  view: viewOption(options.view),
41095
+ environment: stringOption(options.environment),
40361
41096
  queryFile: stringOption(options.query),
40362
41097
  output: stringOption(options.output),
40363
41098
  format: stringOption(options.format),
@@ -40377,6 +41112,8 @@ function optionKey(arg) {
40377
41112
  "-s": "tab",
40378
41113
  "--view": "view",
40379
41114
  "-v": "view",
41115
+ "--environment": "environment",
41116
+ "-e": "environment",
40380
41117
  "--query": "query",
40381
41118
  "-q": "query",
40382
41119
  "--out": "output",
@@ -40389,7 +41126,7 @@ function optionKey(arg) {
40389
41126
  }[arg];
40390
41127
  }
40391
41128
  function command(value) {
40392
- if (value === "link" || value === "render" || value === "query" || value === "structure" || value === "skill") {
41129
+ if (value === "link" || value === "render" || value === "query" || value === "structure" || value === "environments" || value === "skill") {
40393
41130
  return value;
40394
41131
  }
40395
41132
  if (value === void 0) {
@@ -40425,7 +41162,7 @@ function viewOption(value) {
40425
41162
  if (value === void 0) {
40426
41163
  return void 0;
40427
41164
  }
40428
- if (value === "c1" || value === "c2" || value === "c3" || value === "c4" || value === "deployment" || value === "no-filter") {
41165
+ if (value === "c1" || value === "c2" || value === "c3" || value === "c4" || value === "deployment" || value === "deployment-system" || value === "deployment-container" || value === "no-filter") {
40429
41166
  return value;
40430
41167
  }
40431
41168
  throw new CliError(`Unknown view '${String(value)}'`);
@@ -40463,17 +41200,19 @@ function helpText() {
40463
41200
 
40464
41201
  Usage:
40465
41202
  archinsight link [project-dir] [--format text|json] [--out file]
40466
- archinsight render [project-dir] -c <context> [-s <source>] [-v c1|c2|c3|c4|deployment|no-filter] [-q query.aiq] [-f dot|svg|json] [-o file]
40467
- archinsight query [project-dir] -c <context> [-s <source>] [-v c1|c2|c3|c4|deployment|no-filter] [-q query.aiq] [-f text|json] [-o file]
41203
+ archinsight render [project-dir] [-s <source>] [-c <context>] [-v c1|c2|c3|c4|deployment-system|deployment-container|deployment|no-filter] [-e <environment>] [-q query.aiq] [-f dot|svg|json] [-o file]
41204
+ archinsight query [project-dir] [-s <source>] [-c <context>] [-v c1|c2|c3|c4|deployment-system|deployment-container|deployment|no-filter] [-e <environment>] [-q query.aiq] [-f text|json] [-o file]
40468
41205
  archinsight structure [project-dir] [--format text|json] [--out file]
41206
+ archinsight environments [project-dir] [-s <source>] [--format text|json] [--out file]
40469
41207
  archinsight skill init [project-dir] [--target generic|codex|claude] [--out dir] [--force]
40470
41208
 
40471
41209
  Options:
40472
41210
  project-dir Project directory to scan recursively, default: current directory.
40473
- -c, --context <id> Context id for query/render.
40474
- -s, --source <file> Selected project file for queries using $tab.
41211
+ -s, --source <file> Selected model file. Supplies $tab and infers its context.
41212
+ -c, --context <id> Context for context-wide execution without --source; must match it when both are passed.
40475
41213
  --tab <source> Backward-compatible alias for --source.
40476
- -v, --view <name> Built-in view: c1, c2, c3, c4, deployment, no-filter.
41214
+ -v, --view <name> Built-in view: c1, c2, c3, c4, deployment-system, deployment-container, deployment, no-filter.
41215
+ -e, --environment <id> Environment scope for deployment-container; optional when exactly one is relevant.
40477
41216
  -q, --query <file> Query file; overrides --view.
40478
41217
  -f, --format <format> Output format.
40479
41218
  -o, --out <file> Write output to file instead of stdout; for skill init, write the guide directory.
@@ -40483,6 +41222,16 @@ Options:
40483
41222
  -V, --version Print version.
40484
41223
  -h, --help Show help.
40485
41224
 
41225
+ Scope:
41226
+ C1 and no-filter accept either --source or --context. C2-C4 and Deployment
41227
+ views require --source unless project-dir is one .ai file. Source-scoped
41228
+ commands infer context from that file. D2 additionally requires --environment
41229
+ when more than one environment is relevant.
41230
+
41231
+ Environment discovery:
41232
+ environments lists every declared environment. With --source, it returns only
41233
+ the environments relevant to that source for the D2 view.
41234
+
40486
41235
  Diagnostics text format is TSV:
40487
41236
  level<TAB>code<TAB>source<TAB>line<TAB>column<TAB>message
40488
41237
  `;
@@ -40622,10 +41371,12 @@ function sharedSkillFiles() {
40622
41371
  path: "examples/c2-containers.ai",
40623
41372
  content: genericC2ContainersExample()
40624
41373
  },
41374
+ ...genericC2FileSplitExampleFiles(),
40625
41375
  {
40626
41376
  path: "examples/c3-components.ai",
40627
41377
  content: genericC3ComponentsExample()
40628
41378
  },
41379
+ ...genericC3FileSplitExampleFiles(),
40629
41380
  ...genericC4CodeExampleFiles(),
40630
41381
  {
40631
41382
  path: "examples/deployment-framework.ai",
@@ -40665,6 +41416,14 @@ function sharedSkillFiles() {
40665
41416
  path: "examples/builtin-views/c4.aiq",
40666
41417
  content: textFileContent(viewQueries.c4)
40667
41418
  },
41419
+ {
41420
+ path: "examples/builtin-views/deployment-system.aiq",
41421
+ content: textFileContent(viewQueries["deployment-system"])
41422
+ },
41423
+ {
41424
+ path: "examples/builtin-views/deployment-container.aiq",
41425
+ content: textFileContent(viewQueries["deployment-container"])
41426
+ },
40668
41427
  {
40669
41428
  path: "examples/builtin-views/deployment.aiq",
40670
41429
  content: textFileContent(viewQueries.deployment)
@@ -40677,6 +41436,14 @@ function sharedSkillFiles() {
40677
41436
  path: "examples/queries/direct-service-dependencies.aiq",
40678
41437
  content: genericDirectServiceDependenciesQuery()
40679
41438
  },
41439
+ {
41440
+ path: "examples/queries/direct-authored-dependencies.aiq",
41441
+ content: genericDirectAuthoredDependenciesQuery()
41442
+ },
41443
+ {
41444
+ path: "examples/queries/async-topic-dependencies.aiq",
41445
+ content: genericAsyncTopicDependenciesQuery()
41446
+ },
40680
41447
  {
40681
41448
  path: "examples/queries/kafka-service-dependencies.aiq",
40682
41449
  content: genericKafkaServiceDependenciesQuery()
@@ -40986,11 +41753,11 @@ archinsight link . --format text
40986
41753
  archinsight structure . --format text
40987
41754
  \`\`\`
40988
41755
 
40989
- If rendering is needed, ask for the context id and source file when they are not
40990
- obvious:
41756
+ If rendering is needed, ask for the source file when it is not obvious. The CLI
41757
+ infers the context declared by that source:
40991
41758
 
40992
41759
  \`\`\`shell
40993
- archinsight render . -c <context-id> -s <source.ai> -v c2 -f svg -o diagram.svg
41760
+ archinsight render . -s <source.ai> -v c2 -f svg -o diagram.svg
40994
41761
  \`\`\`
40995
41762
 
40996
41763
  Report diagnostics by source, line, column, and message. Avoid rewriting large
@@ -41086,6 +41853,50 @@ Never update a generated skill by running plain \`skill init\` repeatedly or by
41086
41853
  copying only the files that happen to be missing. Regenerate the entire package
41087
41854
  with the installed CLI, then restart the agent session when its runtime discovers
41088
41855
  skills only at startup.
41856
+
41857
+ ## Query and Render Scope
41858
+
41859
+ Use the model source as the normal diagram entry point:
41860
+
41861
+ \`\`\`shell
41862
+ archinsight query . -s <source.ai> -v c2 --format json
41863
+ archinsight render . -s <source.ai> -v c2 -f svg -o diagram.svg
41864
+ \`\`\`
41865
+
41866
+ The selected source supplies \`$tab\` and determines \`$context\`. C2, C3, C4,
41867
+ D1 (\`deployment-system\`), D2 (\`deployment-container\`), and the legacy
41868
+ Deployment view require a source when a project contains several model files.
41869
+ When the command input is one \`.ai\` file, the CLI selects it automatically.
41870
+
41871
+ C1 opens the complete context boundary declared by the selected source. For a
41872
+ context-wide C1 or \`no-filter\` query without a source, pass
41873
+ \`--context <context-id>\` instead. If both \`--source\` and \`--context\` are
41874
+ present, they must resolve to the same context; the context does not override
41875
+ the source.
41876
+
41877
+ A custom query file overrides \`--view\`. Pass \`--source\` when it uses
41878
+ \`$tab\`; the same source also supplies \`$context\`. A query that uses only
41879
+ \`$context\` may use an explicit \`--context\` without a source.
41880
+
41881
+ D1 spans every environment relevant to the selected source and rejects
41882
+ \`--environment\`. D2 selects the only relevant environment automatically. If
41883
+ several are relevant, pass one of the ids reported by the CLI with
41884
+ \`--environment <id>\`.
41885
+
41886
+ Discover environments through the linked model instead of parsing Insight
41887
+ sources:
41888
+
41889
+ \`\`\`shell
41890
+ archinsight environments . --format json
41891
+ archinsight environments . -s <source.ai> --format json
41892
+ \`\`\`
41893
+
41894
+ The first command returns every declared environment. The source-scoped command
41895
+ returns exactly the environments relevant to that source's D2 view. Its JSON
41896
+ uses schema \`deployment-environments.v1\` and contains the selected \`source\`
41897
+ plus \`environments\` entries with \`id\`, optional \`name\`, and the source file
41898
+ that declares the environment. Use this command before choosing D2
41899
+ \`--environment\`; do not scan source text to reconstruct the list.
41089
41900
  `;
41090
41901
  }
41091
41902
  function genericModelingReference() {
@@ -41240,12 +42051,11 @@ Deployment view. The wire must select an environment slot whose concrete value
41240
42051
  has a projection.
41241
42052
 
41242
42053
  A wire can use only a \`NetworkConnection\` descendant. The built-in \`Broker\`
41243
- is an \`InfrastructureComponent\`, so it remains useful as inventory but cannot
41244
- be selected directly by wire \`uses\`. When the broker belongs on the physical
41245
- path, define a project-specific \`NetworkConnection\` such as an event channel,
41246
- place the \`Broker\` behind one of its attributes, and project through it. Read
41247
- the complete [Broker](deployment-projections.md#broker) example before modeling
41248
- that path.
42054
+ is one, so an environment can expose a \`Broker\` slot directly and a wire can
42055
+ select it with \`uses\`. Derive product-specific types such as Kafka or RabbitMQ
42056
+ from \`Broker\`, then put the physical path projection on the concrete broker
42057
+ instance. Read the complete [Broker](deployment-projections.md#broker) example
42058
+ before modeling that path.
41249
42059
 
41250
42060
  If the chosen style is pragmatic mixed C2, a broker-like node can be acceptable,
41251
42061
  but document that the view mixes levels. If the producer or consumer is not
@@ -41613,7 +42423,7 @@ An explicit assignment on an instance replaces the constructor default.
41613
42423
  ### Object-valued attributes
41614
42424
 
41615
42425
  A named object attribute supports a full named declaration, a full anonymous
41616
- declaration, or a shortened declaration when exactly one constructor is valid:
42426
+ declaration, or a shortened declaration whose constructor can be inferred:
41617
42427
 
41618
42428
  \`\`\`insight
41619
42429
  config:
@@ -41628,8 +42438,10 @@ config:
41628
42438
 
41629
42439
  Use a named id when another declaration must reference the nested object. The
41630
42440
  \`_\` form creates an anonymous instance. The shortened form infers both its
41631
- constructor and anonymous identity and is rejected when construction is
41632
- ambiguous.
42441
+ constructor and anonymous identity. A constructor declared directly by the
42442
+ attribute type takes precedence over constructors inherited through descendant
42443
+ types. Without a direct constructor, more than one compatible descendant makes
42444
+ the shortened form ambiguous.
41633
42445
 
41634
42446
  ## Attributes
41635
42447
 
@@ -41694,7 +42506,7 @@ links:
41694
42506
  technology = HTTPS, JSON
41695
42507
  call = POST /checkout
41696
42508
  description = Places an order
41697
- ~> order_events
42509
+ ~> order_service
41698
42510
  technology = Kafka
41699
42511
  via = orders.created
41700
42512
  description = Consumes order events
@@ -42062,7 +42874,7 @@ is about boundaries and responsibilities, not implementation structure.
42062
42874
  4. Add \`external system\` declarations for dependencies outside the boundary.
42063
42875
  5. Add high-level links that explain business or capability flow.
42064
42876
  6. Validate with \`archinsight link . --format text\`.
42065
- 7. Render with \`archinsight render . -c <context-id> -v c1 -f svg -o c1.svg\`.
42877
+ 7. Render with \`archinsight render . -s <c1-source.ai> -v c1 -f svg -o c1.svg\`.
42066
42878
 
42067
42879
  ## Boundary Choices
42068
42880
 
@@ -42247,13 +43059,60 @@ external system analytics_platform
42247
43059
  \`\`\`shell
42248
43060
  archinsight structure . --format text
42249
43061
  archinsight link . --format text
42250
- archinsight render . -c commerce -v c1 -f svg -o commerce-c1.svg
43062
+ archinsight render . -s c1-context.ai -v c1 -f svg -o commerce-c1.svg
42251
43063
  \`\`\`
42252
43064
 
42253
43065
  Use \`examples/c1-context.ai\` as a compact valid C1 model when syntax is
42254
43066
  unclear.
42255
43067
  `;
42256
43068
  }
43069
+ function genericC2FileSplitExampleFiles() {
43070
+ return [
43071
+ {
43072
+ path: "examples/c2-file-split/context.ai",
43073
+ content: `context commerce
43074
+ name = Commerce Platform
43075
+
43076
+ external system payment_provider
43077
+ name = Payment Provider
43078
+ technology = HTTPS API
43079
+
43080
+ system storefront
43081
+ name = Storefront
43082
+ technology = Commerce system
43083
+ description = Lets shoppers browse products and place orders
43084
+ `
43085
+ },
43086
+ {
43087
+ path: "examples/c2-file-split/storefront.ai",
43088
+ content: `context commerce
43089
+
43090
+ import payment_provider from context commerce
43091
+
43092
+ extend system storefront
43093
+ container web_app
43094
+ name = Web app
43095
+ technology = SvelteKit, TypeScript
43096
+ description = Renders product pages and checkout screens
43097
+ links:
43098
+ -> checkout_api
43099
+ technology = HTTPS, JSON
43100
+ call = POST /checkout
43101
+ description = Starts checkout and shows order status
43102
+
43103
+ service checkout_api
43104
+ name = Checkout API
43105
+ technology = Kotlin, PostgreSQL
43106
+ description = Prices carts, creates orders, and coordinates payment
43107
+ links:
43108
+ -> payment_provider
43109
+ technology = HTTPS
43110
+ call = POST /payments/authorizations
43111
+ description = Requests payment authorization
43112
+ `
43113
+ }
43114
+ ];
43115
+ }
42257
43116
  function genericC2ContainersReference() {
42258
43117
  return `# C2 Containers and Services
42259
43118
 
@@ -42301,58 +43160,32 @@ because one system needs that style.
42301
43160
  1. Run \`archinsight structure . --format text\` to find the exact system id,
42302
43161
  existing containers/services, and external declarations.
42303
43162
  2. Create or edit a C2 file in the same \`context <id>\`.
42304
- 3. Import external systems from other contexts when needed.
43163
+ 3. Import every referenced declaration owned by another source, including a
43164
+ declaration in another file of the same context.
42305
43165
  4. Add \`container\` or \`service\` declarations for the system's logical runtime
42306
43166
  units, choosing the word that best communicates each unit's role.
42307
43167
  5. Add runtime links between containers/services and real external systems.
42308
43168
  6. Validate with \`archinsight link . --format text\`.
42309
- 7. Render with \`archinsight render . -c <context-id> -s <c2-file.ai> -v c2 -f svg -o c2.svg\`.
43169
+ 7. Render with \`archinsight render . -s <c2-file.ai> -v c2 -f svg -o c2.svg\`.
42310
43170
 
42311
43171
  ## File Split Pattern
42312
43172
 
42313
43173
  Keep C1 focused on the system boundary:
42314
43174
 
42315
43175
  \`\`\`insight
42316
- context commerce
42317
- name = Commerce Platform
42318
-
42319
- external system payment_provider
42320
- name = Payment Provider
42321
- technology = HTTPS API
42322
-
42323
- system storefront
42324
- name = Storefront
42325
- technology = Commerce system
42326
- description = Lets shoppers browse products and place orders
43176
+ ${genericC2FileSplitExampleFiles()[0].content.trimEnd()}
42327
43177
  \`\`\`
42328
43178
 
42329
43179
  Put C2 details in a system file:
42330
43180
 
42331
43181
  \`\`\`insight
42332
- context commerce
42333
-
42334
- extend system storefront
42335
- container web_app
42336
- name = Web app
42337
- technology = SvelteKit, TypeScript
42338
- description = Renders product pages and checkout screens
42339
- links:
42340
- -> checkout_api
42341
- technology = HTTPS, JSON
42342
- call = POST /checkout
42343
- description = Starts checkout and shows order status
42344
-
42345
- service checkout_api
42346
- name = Checkout API
42347
- technology = Kotlin, PostgreSQL
42348
- description = Prices carts, creates orders, and coordinates payment
42349
- links:
42350
- -> payment_provider
42351
- technology = HTTPS
42352
- call = POST /payments/authorizations
42353
- description = Requests payment authorization
43182
+ ${genericC2FileSplitExampleFiles()[1].content.trimEnd()}
42354
43183
  \`\`\`
42355
43184
 
43185
+ The extension target is resolved in the shared context and does not need an
43186
+ import. The ordinary reference to \`payment_provider\` crosses a source boundary,
43187
+ so the C2 file imports it even though both files use \`context commerce\`.
43188
+
42356
43189
  ## Frontend and Backend Pattern
42357
43190
 
42358
43191
  Use \`container\` when "application" or "executable unit" best communicates the
@@ -42499,55 +43332,18 @@ not compete with a broad duplicate.
42499
43332
  \`\`\`shell
42500
43333
  archinsight structure . --format text
42501
43334
  archinsight link . --format text
42502
- archinsight render . -c commerce -s storefront-containers.ai -v c2 -f svg -o storefront-c2.svg
43335
+ archinsight render . -s storefront-containers.ai -v c2 -f svg -o storefront-c2.svg
42503
43336
  \`\`\`
42504
43337
 
42505
43338
  Use \`examples/c2-containers.ai\` as a compact valid C2 model when syntax is
42506
43339
  unclear.
42507
43340
  `;
42508
43341
  }
42509
- function genericC3ComponentsReference() {
42510
- return `# C3 Components
42511
-
42512
- Use this reference only for C3 work: decomposing one selected container or
42513
- service into internal components and their collaborations.
42514
-
42515
- ## What C3 Answers
42516
-
42517
- A C3 view answers: "Inside this container/service, what named responsibilities
42518
- collaborate to deliver its behavior?"
42519
-
42520
- Prefer one focal container or service per C3 source file. The built-in C3 view
42521
- is scoped by the selected source file, so the C3 file should usually contain an
42522
- \`extend container <id>\` or \`extend service <id>\` block for the focal element.
42523
-
42524
- If the selected source opens several containers or services, components inside
42525
- all of them are internal to one diagram. A dependency leaving that set is
42526
- folded to the nearest closed container or service and shown as external to the
42527
- C3 view.
42528
-
42529
- Do not model every class, function, method, or package. A component should be a
42530
- stable architectural responsibility that is useful in a diagram and review.
42531
-
42532
- ## C3 Workflow
42533
-
42534
- 1. Run \`archinsight structure . --format text\` to find the exact container or
42535
- service id, available constructors, and existing imports.
42536
- 2. Create or edit a C3 file in the same \`context <id>\`.
42537
- 3. Import elements from other contexts only when the component links to them.
42538
- 4. Use \`extend container <id>\` or \`extend service <id>\`.
42539
- 5. Add \`component\` declarations with \`name\`, \`technology\`, and
42540
- \`responsibility\`.
42541
- 6. Add links between components and to real external endpoints.
42542
- 7. Validate with \`archinsight link . --format text\`.
42543
- 8. Render with \`archinsight render . -c <context-id> -s <c3-file.ai> -v c3 -f svg -o c3.svg\`.
42544
-
42545
- ## File Split Pattern
42546
-
42547
- Keep the C2 declaration small:
42548
-
42549
- \`\`\`insight
42550
- context commerce
43342
+ function genericC3FileSplitExampleFiles() {
43343
+ return [
43344
+ {
43345
+ path: "examples/c3-file-split/system.ai",
43346
+ content: `context commerce
42551
43347
  name = Commerce Platform
42552
43348
 
42553
43349
  external system payment_provider
@@ -42557,16 +43353,21 @@ external system payment_provider
42557
43353
  system storefront
42558
43354
  name = Storefront
42559
43355
 
43356
+ container web_app
43357
+ name = Web app
43358
+ technology = SvelteKit, TypeScript
43359
+
42560
43360
  service checkout_api
42561
43361
  name = Checkout API
42562
43362
  technology = Kotlin, PostgreSQL
42563
43363
  description = Handles cart pricing, order placement, and payment orchestration
42564
- \`\`\`
43364
+ `
43365
+ },
43366
+ {
43367
+ path: "examples/c3-file-split/checkout-components.ai",
43368
+ content: `context commerce
42565
43369
 
42566
- Put component details in a C3 file:
42567
-
42568
- \`\`\`insight
42569
- context commerce
43370
+ import payment_provider from context commerce
42570
43371
 
42571
43372
  extend service checkout_api
42572
43373
  component checkout_controller
@@ -42598,15 +43399,13 @@ extend service checkout_api
42598
43399
  name = Order repository
42599
43400
  technology = SQL
42600
43401
  responsibility = Persists order state and checkout audit records
42601
- \`\`\`
42602
-
42603
- ## Frontend Container Pattern
42604
-
42605
- Use C3 for UI responsibilities when the frontend container has distinct
42606
- architectural parts:
43402
+ `
43403
+ },
43404
+ {
43405
+ path: "examples/c3-file-split/web-components.ai",
43406
+ content: `context commerce
42607
43407
 
42608
- \`\`\`insight
42609
- context commerce
43408
+ import checkout_api from context commerce
42610
43409
 
42611
43410
  extend container web_app
42612
43411
  component route_shell
@@ -42637,8 +43436,77 @@ extend container web_app
42637
43436
  -> checkout_api
42638
43437
  technology = HTTPS, JSON
42639
43438
  call = POST /checkout
43439
+ `
43440
+ }
43441
+ ];
43442
+ }
43443
+ function genericC3ComponentsReference() {
43444
+ return `# C3 Components
43445
+
43446
+ Use this reference only for C3 work: decomposing one selected container or
43447
+ service into internal components and their collaborations.
43448
+
43449
+ ## What C3 Answers
43450
+
43451
+ A C3 view answers: "Inside this container/service, what named responsibilities
43452
+ collaborate to deliver its behavior?"
43453
+
43454
+ Prefer one focal container or service per C3 source file. The built-in C3 view
43455
+ is scoped by the selected source file, so the C3 file should usually contain an
43456
+ \`extend container <id>\` or \`extend service <id>\` block for the focal element.
43457
+
43458
+ If the selected source opens several containers or services, components inside
43459
+ all of them are internal to one diagram. A dependency leaving that set is
43460
+ folded to the nearest closed container or service and shown as external to the
43461
+ C3 view.
43462
+
43463
+ Do not model every class, function, method, or package. A component should be a
43464
+ stable architectural responsibility that is useful in a diagram and review.
43465
+
43466
+ ## C3 Workflow
43467
+
43468
+ 1. Run \`archinsight structure . --format text\` to find the exact container or
43469
+ service id, available constructors, and existing imports.
43470
+ 2. Create or edit a C3 file in the same \`context <id>\`.
43471
+ 3. Import every referenced declaration owned by another source, including a
43472
+ declaration in another file of the same context.
43473
+ 4. Use \`extend container <id>\` or \`extend service <id>\`.
43474
+ 5. Add \`component\` declarations with \`name\`, \`technology\`, and
43475
+ \`responsibility\`.
43476
+ 6. Add links between components and to real external endpoints.
43477
+ 7. Validate with \`archinsight link . --format text\`.
43478
+ 8. Render with \`archinsight render . -s <c3-file.ai> -v c3 -f svg -o c3.svg\`.
43479
+
43480
+ ## File Split Pattern
43481
+
43482
+ Keep the C2 declaration small:
43483
+
43484
+ \`\`\`insight
43485
+ ${genericC3FileSplitExampleFiles()[0].content.trimEnd()}
43486
+ \`\`\`
43487
+
43488
+ Put component details in a C3 file:
43489
+
43490
+ \`\`\`insight
43491
+ ${genericC3FileSplitExampleFiles()[1].content.trimEnd()}
42640
43492
  \`\`\`
42641
43493
 
43494
+ The extension target is resolved in the shared context without an import.
43495
+ \`payment_provider\` is an ordinary cross-source reference and therefore has an
43496
+ explicit same-context import.
43497
+
43498
+ ## Frontend Container Pattern
43499
+
43500
+ Use C3 for UI responsibilities when the frontend container has distinct
43501
+ architectural parts:
43502
+
43503
+ \`\`\`insight
43504
+ ${genericC3FileSplitExampleFiles()[2].content.trimEnd()}
43505
+ \`\`\`
43506
+
43507
+ Here \`checkout_api\` is declared in the system file, so the frontend component
43508
+ file imports it before creating the cross-container link.
43509
+
42642
43510
  This is useful when frontend structure affects architecture. If the frontend is
42643
43511
  only a thin page with no meaningful internal decisions, leave it at C2.
42644
43512
 
@@ -42804,7 +43672,7 @@ declaration. Keep a broader wire only when it describes a different interaction.
42804
43672
  \`\`\`shell
42805
43673
  archinsight structure . --format text
42806
43674
  archinsight link . --format text
42807
- archinsight render . -c commerce -s checkout_components.ai -v c3 -f svg -o checkout-c3.svg
43675
+ archinsight render . -s checkout_components.ai -v c3 -f svg -o checkout-c3.svg
42808
43676
  \`\`\`
42809
43677
 
42810
43678
  Use \`examples/c3-components.ai\` as a compact valid C3 model when syntax is
@@ -42906,13 +43774,13 @@ different type filter or grouping rule.
42906
43774
 
42907
43775
  \`\`\`shell
42908
43776
  archinsight link . --format text
42909
- archinsight query . -c <context-id> -s <code-source.ai> -v c4 --format json
42910
- archinsight render . -c <context-id> -s <code-source.ai> -v c4 -f svg -o code.svg
43777
+ archinsight query . -s <code-source.ai> -v c4 --format json
43778
+ archinsight render . -s <code-source.ai> -v c4 -f svg -o code.svg
42911
43779
  \`\`\`
42912
43780
 
42913
43781
  Use the self-contained \`examples/c4-code\` project when the syntax for a code
42914
- framework or model is unclear. Deployment is a separate view selected with
42915
- \`--view deployment\`; never use C4 as an alias for Deployment.
43782
+ framework or model is unclear. Deployment is a separate view family selected
43783
+ with \`--view deployment-system\` or \`--view deployment-container\`; never use C4 as an alias for Deployment.
42916
43784
  `;
42917
43785
  }
42918
43786
  function genericDeploymentReference() {
@@ -42923,10 +43791,34 @@ logical elements run, which infrastructure they use, and how their wires pass
42923
43791
  through the physical world. Keep C1-C4 logical; deployment inventory and
42924
43792
  projections supply the physical view.
42925
43793
 
42926
- The built-in Deployment view includes a logical element only when its deployment
42927
- resolves at least one \`runsOn\` or \`uses\` infrastructure object. A logical wire
42928
- appears only through physical edges created by its deployment projection. A
42929
- plain logical wire is intentionally omitted from Deployment.
43794
+ The built-in Deployment views include a placed logical element when its deployment
43795
+ resolves at least one \`runsOn\` or \`uses\` infrastructure object. An unplaced
43796
+ system or actor can remain visible as an endpoint of a projected wire; wire
43797
+ infrastructure supplies the relevant environments when the selected source has
43798
+ no placements of its own. A logical wire appears only through physical edges
43799
+ created by its deployment projection. A plain logical wire is intentionally
43800
+ omitted from Deployment.
43801
+
43802
+ Use \`deployment-system\` (D1) for a system-level overview across the environments
43803
+ relevant to the selected source. It folds deployed containers and services into
43804
+ their owning systems, contracts internal infrastructure paths, and retains the
43805
+ external integrations reached in each environment. Use \`deployment-container\`
43806
+ (D2) to inspect containers, services, and physical infrastructure in one
43807
+ environment. Pass \`--environment <id>\` when several environments are relevant;
43808
+ the CLI selects the environment automatically when there is only one. The older
43809
+ \`deployment\` view keeps the complete all-environment container graph for
43810
+ backward compatibility.
43811
+
43812
+ Obtain the available D2 choices from the linked model:
43813
+
43814
+ \`\`\`shell
43815
+ archinsight environments . -s <logical-source.ai> --format json
43816
+ \`\`\`
43817
+
43818
+ Read the ids from \`environments\` and pass the selected id to
43819
+ \`deployment-container --environment <id>\`. Run the command without
43820
+ \`--source\` only when the task needs the complete environment inventory rather
43821
+ than the choices relevant to one logical source.
42930
43822
 
42931
43823
  Model the infrastructure immediately relevant to those logical elements and
42932
43824
  connections. Do not expand a Deployment view into a complete provider, transit, replication,
@@ -42970,6 +43862,13 @@ The built-in Deployment view renders concrete \`InfrastructureComponent\`
42970
43862
  instances. A \`Deployment\` object organizes the inventory and is not itself a
42971
43863
  physical node, so projected relationships do not roll up to that owner.
42972
43864
 
43865
+ When one logical element resolves to several concrete compute placements,
43866
+ Deployment query output creates one visible occurrence per selected placement.
43867
+ Occurrence ids use \`<logical-id>@@<compute-id>\`, and their \`projectedFrom\`
43868
+ attribute retains the logical id. Projected edges connect the matching source
43869
+ and target occurrences. A graph whose logical elements all have one placement
43870
+ keeps their ordinary ids.
43871
+
42973
43872
  ## Framework and inventory
42974
43873
 
42975
43874
  Define the available slots on an environment type:
@@ -43121,10 +44020,11 @@ service frontend
43121
44020
  \`\`\`
43122
44021
 
43123
44022
  Do not attach \`DeploymentProfile\`, \`runsOn\`, \`Storage\`, \`Compute\`, or another
43124
- non-network infrastructure type to a wire. In particular, ingress, egress,
43125
- service-mesh, VPN, and broker paths used by wires must inherit
43126
- \`NetworkConnection\`. Read \`references/deployment-projections.md\` before
43127
- defining the concrete path or placing a built-in broker on that path.
44023
+ non-network infrastructure type to a wire. Ingress, egress, service-mesh, and
44024
+ VPN path types used by wires must inherit \`NetworkConnection\`. The built-in
44025
+ \`Broker\` already does; derive product-specific broker types from \`Broker\` and
44026
+ place their projection on the concrete instance. Read
44027
+ \`references/deployment-projections.md\` before defining the physical path.
43128
44028
 
43129
44029
  For each relevant concrete deployment, the linker checks the requested slot:
43130
44030
 
@@ -43179,8 +44079,9 @@ Run:
43179
44079
 
43180
44080
  \`\`\`shell
43181
44081
  archinsight link . --format text
43182
- archinsight query . -c <context> -s <logical-source.ai> -v deployment --format json
43183
- archinsight render . -c <context> -s <logical-source.ai> -v deployment -f svg -o deployment.svg
44082
+ archinsight query . -s <logical-source.ai> -v deployment-system --format json
44083
+ archinsight query . -s <logical-source.ai> -v deployment-container --environment <environment> --format json
44084
+ archinsight render . -s <logical-source.ai> -v deployment-container --environment <environment> -f svg -o deployment.svg
43184
44085
  \`\`\`
43185
44086
 
43186
44087
  A clean link proves that syntax, types, imports, and deployment references are
@@ -43242,14 +44143,13 @@ define type PublicGateway of NetworkConnection
43242
44143
  required InfrastructureComponent cdn
43243
44144
  required InfrastructureComponent loadBalancer
43244
44145
 
43245
- define type EventChannel of NetworkConnection
43246
- constructor eventChannel
43247
- required Broker transport
44146
+ define type KafkaBroker of Broker
44147
+ constructor kafka
43248
44148
 
43249
44149
  extend type Environment
43250
44150
  Compute compute
43251
44151
  Storage storage
43252
- EventChannel events
44152
+ Broker events
43253
44153
  PublicGateway publicGateway
43254
44154
  NetworkConnection network
43255
44155
  \`\`\`
@@ -43266,16 +44166,13 @@ deployment production
43266
44166
  name = ECS
43267
44167
  technology = AWS ECS
43268
44168
  events:
43269
- eventChannel event_bus
43270
- name = Event bus
43271
- transport:
43272
- broker kafka
43273
- name = Kafka
43274
- technology = MSK
43275
- address = kafka.prod.eu.internal
44169
+ kafka event_bus
44170
+ name = Kafka
44171
+ technology = MSK
44172
+ address = kafka.prod.eu.internal
43276
44173
  projection:
43277
- target $to connectTo target transport
43278
- target transport originalLink source $from
44174
+ target $to connectTo target $this
44175
+ target $this originalLink source $from
43279
44176
  \`\`\`
43280
44177
 
43281
44178
  \`\`\`insight
@@ -43292,7 +44189,8 @@ System files in the same logical context should import the context-owned profile
43292
44189
  when it is declared in a different source identity. Wires name compatible
43293
44190
  \`NetworkConnection\` slots directly. Read the
43294
44191
  [Broker](deployment-projections.md#broker) example before adding an event path;
43295
- a built-in \`Broker\` inventory value cannot itself fill a wire-facing slot.
44192
+ the built-in \`Broker\` can fill a wire-facing slot directly, and
44193
+ product-specific broker types should derive from it.
43296
44194
 
43297
44195
  ## System Files and External Contexts
43298
44196
 
@@ -43444,8 +44342,9 @@ Deployment output after changing which root a traffic relationship extends:
43444
44342
 
43445
44343
  \`\`\`shell
43446
44344
  archinsight link . --format text
43447
- archinsight query . -c deployment_shop -s deployment.ai -v deployment --format json
43448
- archinsight render . -c deployment_shop -s deployment.ai -v deployment -f svg -o deployment.svg
44345
+ archinsight query . -s deployment.ai -v deployment-system --format json
44346
+ archinsight query . -s deployment.ai -v deployment-container --environment eu --format json
44347
+ archinsight render . -s deployment.ai -v deployment-container --environment eu -f svg -o deployment.svg
43449
44348
  \`\`\`
43450
44349
 
43451
44350
  If projected infrastructure edges disappear after a split, first check whether
@@ -43624,9 +44523,12 @@ sed -n '1,160p' .core/core_system.ai
43624
44523
  \`\`\`insight
43625
44524
  define type System of SystemElement
43626
44525
  constructor system
44526
+ kind = internal
43627
44527
 
43628
44528
  required Text name
44529
+ required Text kind
43629
44530
  Text technology
44531
+ Text description
43630
44532
  List of Wire links
43631
44533
  List of Container _
43632
44534
  \`\`\`
@@ -43636,6 +44538,8 @@ Interpretation:
43636
44538
  - \`define type System of SystemElement\` means \`System\` inherits from
43637
44539
  \`SystemElement\`.
43638
44540
  - \`constructor system\` means \`system <id>\` is valid syntax for that type.
44541
+ - The constructor assigns \`kind = internal\`, so model authors do not repeat the
44542
+ built-in default on every system.
43639
44543
  - \`required Text name\` means \`name = ...\` is required.
43640
44544
  - \`Text technology\` means \`technology = ...\` is optional.
43641
44545
  - \`List of Wire links\` enables a \`links:\` block whose children are wires.
@@ -43661,8 +44565,9 @@ C4 query.
43661
44565
  \`description\`, plus deployment references.
43662
44566
  - \`Storage\` / constructor \`storage\`: for databases, buckets, volumes, and
43663
44567
  other stateful stores.
43664
- - \`Broker\` / constructor \`broker\`: for message brokers and event buses;
43665
- adds optional \`address\`.
44568
+ - \`Broker\` / constructor \`broker\`: a \`NetworkConnection\` specialization
44569
+ for message brokers and event buses; adds optional \`address\` and can carry
44570
+ a projected logical wire.
43666
44571
  - \`Compute\` / constructor \`compute\`: for runtimes, clusters, nodes, and
43667
44572
  platforms; adds optional \`address\` and can contain nested infrastructure
43668
44573
  components in a \`components:\` block.
@@ -43903,28 +44808,30 @@ Inspect the selected graph before rendering whenever a change affects C2, C3,
43903
44808
  C4, Deployment, projections, or query text:
43904
44809
 
43905
44810
  \`\`\`shell
43906
- archinsight query . -c <context-id> -s <source.ai> -v c2 --format json
43907
- archinsight query . -c <context-id> -s <source.ai> -v c3 --format json
43908
- archinsight query . -c <context-id> -s <source.ai> -v c4 --format json
43909
- archinsight query . -c <context-id> -s <source.ai> -v deployment --format json
44811
+ archinsight query . -s <source.ai> -v c2 --format json
44812
+ archinsight query . -s <source.ai> -v c3 --format json
44813
+ archinsight query . -s <source.ai> -v c4 --format json
44814
+ archinsight query . -s <source.ai> -v deployment-system --format json
44815
+ archinsight query . -s <source.ai> -v deployment-container --environment <environment> --format json
43910
44816
  \`\`\`
43911
44817
 
43912
- Then render the same context, source, and view:
44818
+ Then render the same source and view; the source carries the same context:
43913
44819
 
43914
44820
  \`\`\`shell
43915
- archinsight render . -c <context-id> -v c1 -f svg -o diagram.svg
43916
- archinsight render . -c <context-id> -s <source.ai> -v c2 -f svg -o diagram.svg
43917
- archinsight render . -c <context-id> -s <source.ai> -v c3 -f svg -o diagram.svg
43918
- archinsight render . -c <context-id> -s <source.ai> -v c4 -f svg -o diagram.svg
43919
- archinsight render . -c <context-id> -s <source.ai> -v deployment -f svg -o diagram.svg
44821
+ archinsight render . -s <source.ai> -v c1 -f svg -o diagram.svg
44822
+ archinsight render . -s <source.ai> -v c2 -f svg -o diagram.svg
44823
+ archinsight render . -s <source.ai> -v c3 -f svg -o diagram.svg
44824
+ archinsight render . -s <source.ai> -v c4 -f svg -o diagram.svg
44825
+ archinsight render . -s <source.ai> -v deployment-system -f svg -o deployment-system.svg
44826
+ archinsight render . -s <source.ai> -v deployment-container --environment <environment> -f svg -o deployment-container.svg
43920
44827
  \`\`\`
43921
44828
 
43922
44829
  Run a custom query from a file:
43923
44830
 
43924
44831
  \`\`\`shell
43925
- archinsight query . -c <context-id> -s <source.ai> -q query.aiq -f text
43926
- archinsight query . -c <context-id> -s <source.ai> -q query.aiq -f json
43927
- archinsight render . -c <context-id> -s <source.ai> -q query.aiq -f svg -o diagram.svg
44832
+ archinsight query . -s <source.ai> -q query.aiq -f text
44833
+ archinsight query . -s <source.ai> -q query.aiq -f json
44834
+ archinsight render . -s <source.ai> -q query.aiq -f svg -o diagram.svg
43928
44835
  \`\`\`
43929
44836
 
43930
44837
  Use three validation layers:
@@ -43945,12 +44852,17 @@ Useful built-in views:
43945
44852
  - \`c2\` for containers/services in the selected source.
43946
44853
  - \`c3\` for components in the selected source.
43947
44854
  - \`c4\` for project-defined code elements in the selected source.
43948
- - \`deployment\` for deployment-oriented views.
44855
+ - \`deployment-system\` for the D1 system deployment overview across relevant
44856
+ environments.
44857
+ - \`deployment-container --environment <id>\` for D2 physical detail in one
44858
+ environment.
44859
+ - \`deployment\` only when an all-environment container graph is intentionally
44860
+ required for backward compatibility or analysis.
43949
44861
  - \`no-filter\` for the full context.
43950
44862
 
43951
- C2, C3, C4, Deployment, and custom queries often depend on the active file. Pass
43952
- \`--source <file>\` / \`-s <file>\` whenever the query uses \`$tab\`; otherwise
43953
- the CLI may choose the first source and render a valid but wrong view.
44863
+ C2, C3, C4, Deployment, and custom queries that use \`$tab\` depend on the
44864
+ active file. Pass \`--source <file>\` / \`-s <file>\`; the CLI will not guess
44865
+ between several project files. That source also supplies \`$context\`.
43954
44866
 
43955
44867
  The bundled \`examples\` directory contains several independent model projects,
43956
44868
  not one project to link as a whole. Validate \`layered-architecture.ai\`, the C1,
@@ -44007,9 +44919,10 @@ the Insight query and which result was computed from its output.
44007
44919
  \`no-filter\` for a broad logical export.
44008
44920
  4. Inspect query JSON before rendering. Distinguish selected outer endpoints,
44009
44921
  underlying edge endpoints, derived relationships, and projection origins.
44010
- 5. If the question needs traversal, aggregation, comparison, or absence checks,
44011
- compute them over the exported JSON without changing the model or pretending
44012
- the computation was supported by the query DSL.
44922
+ 5. If the question needs traversal, aggregation, set intersection, or absence
44923
+ checks, compute them over the exported JSON without changing the model or
44924
+ pretending the computation was supported by the query DSL. Ordinary
44925
+ property equality and inequality can stay in the Insight query.
44013
44926
  6. Report the scope: context, selected source when \`$tab\` is used, query or
44014
44927
  built-in view, and whether derived or projected edges were included.
44015
44928
 
@@ -44043,21 +44956,25 @@ MATCH (service:Service {id: 'checkout_api', context: $context})-[dependency:REFE
44043
44956
  RETURN service, dependency, target
44044
44957
  \`\`\`
44045
44958
 
44046
- Incoming dependencies use the same left-to-right syntax with the target bound
44047
- on the right:
44959
+ Incoming dependencies can keep the inspected service at the start of the
44960
+ pattern by using the reverse arrow:
44048
44961
 
44049
44962
  \`\`\`cypher
44050
- MATCH (caller:Element)-[dependency:REFERENCES]->(service:Service {id: 'checkout_api', context: $context})
44051
- RETURN caller, dependency, service
44963
+ MATCH (service:Service {id: 'checkout_api', context: $context})<-[dependency:REFERENCES]-(caller:Element)
44964
+ RETURN service, dependency, caller
44052
44965
  \`\`\`
44053
44966
 
44967
+ This selects the same stored edge as placing \`caller\` on the left with \`->\`.
44968
+ Pattern orientation changes matching readability, not the relationship stored
44969
+ in the architecture model.
44970
+
44054
44971
  These answer one-hop questions. To find all transitively affected elements,
44055
44972
  export the relevant context graph and traverse its direct \`REFERENCES\` edges
44056
44973
  outside the query language. State whether derived and projected edges were
44057
44974
  excluded or analyzed separately so the same dependency is not counted at
44058
44975
  several architectural levels.
44059
44976
 
44060
- ## Build a Direct Service Dependency Map
44977
+ ## Choose the Dependency Scope
44061
44978
 
44062
44979
  The bundled \`examples/queries/direct-service-dependencies.aiq\` selects the
44063
44980
  one-hop dependency graph at container/service ownership level:
@@ -44070,9 +44987,9 @@ RETURN source, dependency, target
44070
44987
 
44071
44988
  \`withDerived\` includes relationships lifted from components to their owning
44072
44989
  containers or services. This is appropriate for a service dependency map, but
44073
- it is not an inventory of authored wires. Remove \`{withDerived}\` when the
44074
- question is specifically about relationships declared directly between those
44075
- elements.
44990
+ it is not an inventory of authored wires. Because both endpoints must be
44991
+ \`ContainerElement\`, it intentionally omits a direct service-to-system or
44992
+ system-to-system relationship.
44076
44993
 
44077
44994
  Run the query once for the context instead of querying every service
44078
44995
  individually:
@@ -44088,41 +45005,70 @@ preserve dependency ownership. The source depends on the target. Use qualified
44088
45005
  ids from those fields when building an adjacency map; do not infer direction
44089
45006
  from diagram placement.
44090
45007
 
44091
- ## Analyze Kafka Topics
45008
+ For every authored one-hop dependency regardless of architectural level, use
45009
+ \`examples/queries/direct-authored-dependencies.aiq\`:
45010
+
45011
+ \`\`\`cypher
45012
+ MATCH (source:Element)-[dependency:REFERENCES]->(target:Element)
45013
+ WHERE source.context = $context
45014
+ RETURN source, dependency, target
45015
+ \`\`\`
45016
+
45017
+ \`\`\`shell
45018
+ archinsight query . -c <context-id> \\
45019
+ -q <skill-path>/examples/queries/direct-authored-dependencies.aiq \\
45020
+ --format json
45021
+ \`\`\`
45022
+
45023
+ This query excludes derived and projected copies. It is the correct starting
45024
+ point for questions such as "what does this service or system directly depend
45025
+ on?" when the target may live at another architectural level.
45026
+
45027
+ ## Analyze Async Topics and Channels
44092
45028
 
44093
- The bundled \`examples/queries/kafka-service-dependencies.aiq\` selects authored
44094
- Kafka dependencies without derived or projected copies:
45029
+ The bundled \`examples/queries/async-topic-dependencies.aiq\` selects every
45030
+ authored async dependency without requiring a particular transport or endpoint
45031
+ level:
44095
45032
 
44096
45033
  \`\`\`cypher
44097
- MATCH (consumer:ContainerElement)-[event:REFERENCES]->(producer:ContainerElement)
45034
+ MATCH (consumer:Element)-[event:REFERENCES]->(producer:Element)
44098
45035
  WHERE consumer.context = $context
44099
45036
  AND event.type = 'AsyncWire'
44100
- AND event.technology CONTAINS 'Kafka'
44101
45037
  RETURN consumer, event, producer
44102
45038
  \`\`\`
44103
45039
 
44104
45040
  Insight eventing is consumer-owned: the consumer declares \`~> producer\`, and
44105
45041
  \`via\` names the topic or channel. Therefore an outgoing async edge lists what
44106
- a service consumes, while incoming async edges list the modeled consumers of a
44107
- producer's topic contract.
45042
+ an element consumes, while incoming async edges list the modeled consumers of a
45043
+ producer's topic contract. The endpoints may be systems, services, components,
45044
+ or project-defined element types.
45045
+
45046
+ Run the generic query for the whole context:
45047
+
45048
+ \`\`\`shell
45049
+ archinsight query . -c <context-id> \\
45050
+ -q <skill-path>/examples/queries/async-topic-dependencies.aiq \\
45051
+ --format json
45052
+ \`\`\`
45053
+
45054
+ To select a topic family, copy the query and add a case-sensitive membership or
45055
+ substring predicate such as \`AND event.via CONTAINS 'orders.'\`.
44108
45056
 
44109
45057
  For one consumer, constrain its local id in the first node pattern:
44110
45058
 
44111
45059
  \`\`\`cypher
44112
- MATCH (consumer:ContainerElement {id: 'order_processor', context: $context})
44113
- -[event:REFERENCES]->(producer:ContainerElement)
45060
+ MATCH (consumer:Element {id: 'order_processor', context: $context})
45061
+ -[event:REFERENCES]->(producer:Element)
44114
45062
  WHERE event.type = 'AsyncWire'
44115
- AND event.technology CONTAINS 'Kafka'
44116
45063
  RETURN consumer, event, producer
44117
45064
  \`\`\`
44118
45065
 
44119
45066
  For one producer, use the reverse pattern:
44120
45067
 
44121
45068
  \`\`\`cypher
44122
- MATCH (producer:ContainerElement {id: 'order_processor', context: $context})
44123
- <-[event:REFERENCES]-(consumer:ContainerElement)
45069
+ MATCH (producer:Element {id: 'order_processor', context: $context})
45070
+ <-[event:REFERENCES]-(consumer:Element)
44124
45071
  WHERE event.type = 'AsyncWire'
44125
- AND event.technology CONTAINS 'Kafka'
44126
45072
  RETURN producer, event, consumer
44127
45073
  \`\`\`
44128
45074
 
@@ -44132,7 +45078,7 @@ into the agent's context:
44132
45078
 
44133
45079
  \`\`\`shell
44134
45080
  archinsight query . -c <context-id> \\
44135
- -q <skill-path>/examples/queries/kafka-service-dependencies.aiq \\
45081
+ -q <skill-path>/examples/queries/async-topic-dependencies.aiq \\
44136
45082
  --format json |
44137
45083
  jq -r '.edges[] | [
44138
45084
  .edge.source,
@@ -44146,6 +45092,62 @@ This reports topic contracts used by at least one modeled consumer. Do not
44146
45092
  claim it is a complete producer catalog: a topic with no modeled wire is not
44147
45093
  discoverable unless the project represents that contract separately.
44148
45094
 
45095
+ The bundled \`examples/queries/kafka-service-dependencies.aiq\` is a narrower
45096
+ specialization for projects that consistently record \`technology = Kafka\`
45097
+ and want only authored container-to-container dependencies without derived or
45098
+ projected copies:
45099
+
45100
+ \`\`\`cypher
45101
+ MATCH (consumer:ContainerElement)-[event:REFERENCES]->(producer:ContainerElement)
45102
+ WHERE consumer.context = $context
45103
+ AND event.type = 'AsyncWire'
45104
+ AND event.technology CONTAINS 'Kafka'
45105
+ RETURN consumer, event, producer
45106
+ \`\`\`
45107
+
45108
+ Do not use this specialization when \`technology\` is absent or inconsistent;
45109
+ the generic async query still finds those modeled relationships.
45110
+
45111
+ ## Compare Endpoint Attributes
45112
+
45113
+ Equality and inequality can compare properties on two bound endpoints:
45114
+
45115
+ \`\`\`cypher
45116
+ MATCH (source:Element)-[dependency:REFERENCES]->(target:Element)
45117
+ WHERE source.runsOn <> target.runsOn
45118
+ RETURN source, dependency, target
45119
+ \`\`\`
45120
+
45121
+ Scalar references compare by qualified element id. List properties compare as
45122
+ complete ordered lists. If either property is absent, both \`=\` and \`<>\`
45123
+ evaluate to false for that row. The query language does not calculate list
45124
+ intersection or set difference; export JSON and post-process it when the
45125
+ question is whether two multi-valued placements overlap.
45126
+
45127
+ ## Report Annotations
45128
+
45129
+ Annotations are present in query JSON, but the current query language has no
45130
+ annotation predicate. Select a sufficiently broad graph and filter the JSON:
45131
+
45132
+ \`\`\`shell
45133
+ archinsight query . -c <context-id> -v no-filter --format json |
45134
+ jq '{
45135
+ elements: [
45136
+ .elements[] |
45137
+ select((.annotations // []) | length > 0) |
45138
+ {id, annotations}
45139
+ ],
45140
+ edges: [
45141
+ .edges[] |
45142
+ select((.edge.annotations // []) | length > 0) |
45143
+ {source, target, annotations: .edge.annotations}
45144
+ ]
45145
+ }'
45146
+ \`\`\`
45147
+
45148
+ Each annotation retains its name, optional value, and source position. This is
45149
+ a read-only reporting workaround, not an Insight query predicate.
45150
+
44149
45151
  ## Analyze a Source Fragment
44150
45152
 
44151
45153
  Use \`$tab\` when the question concerns the semantic fragment rooted in one
@@ -44164,13 +45166,19 @@ it is not a raw inventory of lines physically present in that file.
44164
45166
 
44165
45167
  ## Analyze Deployment Realization
44166
45168
 
44167
- Use the built-in Deployment query JSON when the question is how logical architecture is
44168
- realized physically:
45169
+ Use built-in Deployment query JSON when the question is how logical architecture
45170
+ is realized physically. D1 answers which systems and external integrations are
45171
+ present across relevant environments. D2 shows container and infrastructure
45172
+ detail inside one selected environment:
44169
45173
 
44170
45174
  \`\`\`shell
44171
- archinsight query . -c <context-id> -s <logical-source.ai> -v deployment --format json
45175
+ archinsight query . -s <logical-source.ai> -v deployment-system --format json
45176
+ archinsight query . -s <logical-source.ai> -v deployment-container --environment <environment> --format json
44172
45177
  \`\`\`
44173
45178
 
45179
+ Use the legacy \`deployment\` view only when the question intentionally requires
45180
+ one container-level graph across every relevant environment.
45181
+
44174
45182
  For each projected edge, compare outer \`source\` and \`target\` with nested
44175
45183
  \`edge.source\` and \`edge.target\`. Use \`edge.originSource\` and
44176
45184
  \`edge.originTarget\` for the logical origin selected by the query. When
@@ -44203,19 +45211,22 @@ diagram.
44203
45211
  ## CLI Shape
44204
45212
 
44205
45213
  \`\`\`shell
44206
- archinsight query . -c <context-id> -s <source.ai> -q query.aiq -f text
44207
- archinsight query . -c <context-id> -s <source.ai> -v deployment --format json
44208
- archinsight render . -c <context-id> -s <source.ai> -q query.aiq -f svg -o diagram.svg
45214
+ archinsight query . -s <source.ai> -q query.aiq -f text
45215
+ archinsight query . -s <source.ai> -v deployment-system --format json
45216
+ archinsight query . -s <source.ai> -v deployment-container --environment <environment> --format json
45217
+ archinsight render . -s <source.ai> -q query.aiq -f svg -o diagram.svg
44209
45218
  \`\`\`
44210
45219
 
44211
45220
  The scope variables are:
44212
45221
 
44213
- - \`$context\` - selected context id from \`--context\`.
45222
+ - \`$context\` - context declared by \`--source\`, or the explicit
45223
+ \`--context\` used for context-wide execution without a source.
44214
45224
  - \`$tab\` - the semantic model fragment rooted in the source selected by
44215
45225
  \`--source\` / \`--tab\`. It includes content contributed to those roots by
44216
45226
  \`extend\` in other files.
44217
45227
 
44218
- Pass \`--source\` when a query uses \`$tab\`.
45228
+ Pass \`--source\` when a query uses \`$tab\`. A selected source also supplies
45229
+ \`$context\`, so do not repeat its context on the command line.
44219
45230
 
44220
45231
  Path handling:
44221
45232
 
@@ -44227,7 +45238,7 @@ Path handling:
44227
45238
  Prefer paths relative to the project root:
44228
45239
 
44229
45240
  \`\`\`shell
44230
- archinsight query path/to/project -c shop -s models/storefront.ai -q queries/c2.aiq -f text
45241
+ archinsight query path/to/project -s models/storefront.ai -q queries/c2.aiq -f text
44231
45242
  \`\`\`
44232
45243
 
44233
45244
  ## Query Shape
@@ -44317,7 +45328,10 @@ OPTIONAL MATCH (container)-[link]->(target)
44317
45328
  RETURN container, link, target
44318
45329
  \`\`\`
44319
45330
 
44320
- Relationship aliases must be returned for edges to render.
45331
+ Return relationship aliases when the query must select or filter exact edges. A
45332
+ node-only query completes direct authored relationships between selected nodes.
45333
+ Once a returned relationship alias is present, its result is authoritative: if
45334
+ the relationship predicate matches nothing, no fallback edges are added.
44321
45335
 
44322
45336
  ## Filtering
44323
45337
 
@@ -44330,6 +45344,7 @@ WHERE node.deployed = true
44330
45344
  WHERE node IS External
44331
45345
  WHERE NOT node IS DeploymentElement
44332
45346
  WHERE edge.projected = 'true'
45347
+ WHERE edge.projectionRoot = 'eu/service_network'
44333
45348
  WHERE node.id IN ['api', 'web_app']
44334
45349
  WHERE node.technology CONTAINS 'PostgreSQL'
44335
45350
  WHERE node.type <> 'Context'
@@ -44341,10 +45356,46 @@ Use single quotes for string literals.
44341
45356
  \`CONTAINS\` is case-sensitive. For scalar text it performs substring matching;
44342
45357
  for a list property it tests membership. Match the stored spelling exactly.
44343
45358
 
45359
+ Attribute cardinality comes from the Insight type system, not from the JSON
45360
+ representation. Use \`CONTAINS\` for list-valued attributes such as \`uses\`.
45361
+ \`runsOn\` is declared as a scalar typed reference. When it has one resolved
45362
+ target, it resolves to one graph node: compare it with another bound node, or
45363
+ test its qualified id with \`node.runsOn IN ['eu/cluster']\`.
45364
+ \`node.runsOn CONTAINS 'eu/cluster'\` does not match a scalar reference because
45365
+ that value is neither scalar text nor a list.
45366
+
45367
+ A logical element materialized through several deployments can have several
45368
+ resolved \`runsOn\` targets. In that case bind a candidate infrastructure node
45369
+ and use \`candidate IN node.runsOn\`.
45370
+
45371
+ Query JSON serializes attribute values as arrays for a stable transport shape.
45372
+ The linked element or edge also exposes \`listAttributes\` and
45373
+ \`referenceAttributes\`; use that metadata when an automated analysis needs to
45374
+ recover the language-level cardinality and reference kind.
45375
+
45376
+ Properties of two bound aliases can be compared directly:
45377
+
45378
+ \`\`\`cypher
45379
+ WHERE source.runsOn = target.runsOn
45380
+ WHERE source.runsOn <> target.runsOn
45381
+ \`\`\`
45382
+
45383
+ Scalar references compare by qualified id and lists compare as complete ordered
45384
+ lists. If either property is absent, both comparisons evaluate to false. Set
45385
+ intersection and overlap tests are not supported; post-process query JSON for
45386
+ those operations.
45387
+
44344
45388
  \`node.deployed\` is true when an element's deployment resolves to at least one
44345
45389
  \`runsOn\` or \`uses\` infrastructure object. The built-in Deployment view uses it to keep
44346
45390
  undeployed logical elements out of the physical diagram.
44347
45391
 
45392
+ \`edge.projectionRoot\` is available on projected relationships and identifies
45393
+ the infrastructure element whose projection produced the selected segment.
45394
+ It can be used in relationship predicates and has the same value shown in
45395
+ \`query --format json\` output. Resolved \`node.runsOn\` can contain several
45396
+ concrete infrastructure objects, so use \`candidate IN node.runsOn\` when
45397
+ matching placement targets.
45398
+
44348
45399
  ## Relationship Selectors
44349
45400
 
44350
45401
  Relationship selectors are boolean flags inside relationship braces:
@@ -44407,15 +45458,21 @@ The top-level shape is:
44407
45458
  - \`groups\`: render groups created by \`GROUP BY\`;
44408
45459
  - \`externalElements\`: selected ids drawn outside the internal boundary.
44409
45460
 
45461
+ Elements expose their linked \`attributes\`, optional \`listAttributes\` and
45462
+ \`referenceAttributes\` metadata, and any \`annotations\`. Relationship data is
45463
+ nested under each result item's \`edge\` field and carries the same attribute
45464
+ metadata plus edge annotations. Annotations can be inspected in JSON but cannot
45465
+ currently be referenced by a \`WHERE\` predicate.
45466
+
44410
45467
  For built-in C1-C4 execution, this list combines explicit model externality
44411
45468
  with externality relative to the opened diagram boundaries. C2 folds a closed
44412
45469
  endpoint to its system, C3 to its container or service, and C4 to its component.
44413
45470
  \`IS External\` in a custom query continues to match only the explicit model
44414
45471
  marker.
44415
45472
 
44416
- To apply one of these boundary contracts to a custom CLI query, pass both
44417
- \`-q <query.aiq>\` and \`-v c1|c2|c3|c4\`. Without \`-v\`, custom query
44418
- execution does not apply a built-in view boundary.
45473
+ A custom query file supplies its own selection and grouping contract and
45474
+ overrides \`--view\`. To customize C1-C4 boundary behavior, copy the nearest
45475
+ bundled built-in \`.aiq\` file and modify its predicates or grouping explicitly.
44419
45476
 
44420
45477
  Each edge contains its selected category and two endpoint pairs:
44421
45478
 
@@ -44429,7 +45486,9 @@ Each edge contains its selected category and two endpoint pairs:
44429
45486
  - nested \`edge.projectionOrigins\`, when present, lists every logical source and
44430
45487
  target that shares the physical segment;
44431
45488
  - nested \`edge.projectionRoot\` identifies the infrastructure element whose
44432
- projection produced the segment.
45489
+ projection produced the segment;
45490
+ - nested \`edge.sourcePlacement\` and \`edge.targetPlacement\`, when present,
45491
+ identify the concrete placement of each logical endpoint independently.
44433
45492
 
44434
45493
  An abridged response remains ordinary JSON:
44435
45494
 
@@ -44503,6 +45562,8 @@ examples/builtin-views/c1.aiq
44503
45562
  examples/builtin-views/c2.aiq
44504
45563
  examples/builtin-views/c3.aiq
44505
45564
  examples/builtin-views/c4.aiq
45565
+ examples/builtin-views/deployment-system.aiq
45566
+ examples/builtin-views/deployment-container.aiq
44506
45567
  examples/builtin-views/deployment.aiq
44507
45568
  \`\`\`
44508
45569
 
@@ -44519,9 +45580,15 @@ C4 opens every component rooted in \`$tab\`, selects direct Code-element
44519
45580
  neighborhoods in either direction, and folds outside code to closed components.
44520
45581
  Concrete code types and containment remain project-defined.
44521
45582
 
44522
- Deployment selects deployment nodes and physically deployed logical nodes from \`$tab\`,
44523
- uses one undirected \`OPTIONAL MATCH ROLLUP\` for projected paths, and keeps
44524
- placement lookup separate from relationship matching.
45583
+ D1 and D2 select physically deployed logical nodes and source-owned system
45584
+ endpoints from \`$tab\`, use one undirected \`OPTIONAL MATCH ROLLUP\` for projected
45585
+ paths, and keep placement lookup separate from relationship matching. When the
45586
+ source owns no deployed elements, environment discovery falls back to
45587
+ infrastructure used by its authored wires. D1 folds logical nodes to systems.
45588
+ D1 then contracts internal infrastructure paths and groups the remaining systems
45589
+ and external integrations by environment. D2 applies the structured environment
45590
+ scope after discovery and retains its physical detail. The legacy Deployment
45591
+ query retains all relevant environments.
44525
45592
 
44526
45593
  When a built-in view is close but hides the wrong thing, read
44527
45594
  \`references/query-recipes.md\`, copy the nearest built-in \`.aiq\`, and change
@@ -44554,16 +45621,23 @@ examples/builtin-views/c1.aiq
44554
45621
  examples/builtin-views/c2.aiq
44555
45622
  examples/builtin-views/c3.aiq
44556
45623
  examples/builtin-views/c4.aiq
45624
+ examples/builtin-views/deployment-system.aiq
45625
+ examples/builtin-views/deployment-container.aiq
44557
45626
  examples/builtin-views/deployment.aiq
44558
45627
  \`\`\`
44559
45628
 
44560
45629
  Before inventing a query from scratch, open the nearest built-in query, copy it
44561
45630
  to the project, and make the smallest change.
44562
45631
 
45632
+ Choose \`deployment-system.aiq\` for a D1 overview and
45633
+ \`deployment-container.aiq\` for D2 detail in one environment. Start from the
45634
+ legacy \`deployment.aiq\` only when the intended result is one container-level
45635
+ graph across every relevant environment.
45636
+
44563
45637
  \`\`\`shell
44564
- archinsight query . -c <context-id> -s <source.ai> -q queries/custom.aiq -f text
44565
- archinsight query . -c <context-id> -s <source.ai> -q queries/custom.aiq -f json
44566
- archinsight render . -c <context-id> -s <source.ai> -q queries/custom.aiq -f svg -o custom.svg
45638
+ archinsight query . -s <source.ai> -q queries/custom.aiq -f text
45639
+ archinsight query . -s <source.ai> -q queries/custom.aiq -f json
45640
+ archinsight render . -s <source.ai> -q queries/custom.aiq -f svg -o custom.svg
44567
45641
  \`\`\`
44568
45642
 
44569
45643
  Use \`references/queries.md\` for syntax details. Use this file for common
@@ -44616,10 +45690,12 @@ archinsight link . --format text
44616
45690
  archinsight structure . --format text
44617
45691
  \`\`\`
44618
45692
 
44619
- 3. Run the built-in query explicitly and inspect its JSON:
45693
+ 3. Run the built-in query explicitly and inspect its JSON. Choose the command
45694
+ that matches the diagram being investigated:
44620
45695
 
44621
45696
  \`\`\`shell
44622
- archinsight query . -c <context-id> -s <source.ai> -q examples/builtin-views/deployment.aiq -f json
45697
+ archinsight query . -s <source.ai> -v deployment-system -f json
45698
+ archinsight query . -s <source.ai> -v deployment-container --environment <environment> -f json
44623
45699
  \`\`\`
44624
45700
 
44625
45701
  4. Check the query filters:
@@ -44627,17 +45703,17 @@ archinsight query . -c <context-id> -s <source.ai> -q examples/builtin-views/dep
44627
45703
  - \`node.sourceIdentity = $tab\` selects the semantic fragment rooted in the
44628
45704
  selected source, including contributions added to those roots through
44629
45705
  \`extend\` in other files.
44630
- - The built-in Deployment node filter includes deployment elements and logical
45706
+ - The built-in D1 and D2 node filters include deployment elements and logical
44631
45707
  container or external elements whose deployment resolves to physical
44632
45708
  infrastructure. External endpoints without their own placement still enter
44633
45709
  through projected paths attached to a deployed logical node.
44634
45710
  - \`{projected}\` means only deployment-projected edges are selected.
44635
45711
  - \`{derived}\` means only rolled-up relationships are selected.
44636
45712
  - A relationship property such as \`sourceIdentity: $tab\` is available for a
44637
- deliberately narrow custom view. The current built-in Deployment query does not add
44638
- that edge filter; the selected node scope and semantic tab closure provide
44639
- the boundary.
44640
- - The built-in Deployment view uses one undirected \`MATCH ROLLUP\` for the
45713
+ deliberately narrow custom view. The built-in D1 and D2 queries rely on the
45714
+ selected node scope and semantic tab closure instead of applying that filter
45715
+ to projected relationships.
45716
+ - The built-in D1 and D2 views use one undirected \`MATCH ROLLUP\` for the
44641
45717
  projected neighborhood. Projection origin metadata lets that clause find
44642
45718
  ingress and egress segments while every segment preserves its real physical
44643
45719
  source and target.
@@ -44672,19 +45748,20 @@ traceability, not for inventing a direct physical connection.
44672
45748
 
44673
45749
  ## Include Internal Actors In Deployment
44674
45750
 
44675
- The built-in Deployment query includes external actors reached by a projected physical
44676
- path. If a deployment diagram also needs internal actors, use the bundled,
44677
- tested customization:
45751
+ The built-in Deployment views include external actors reached by a projected
45752
+ physical path. If an all-environment deployment diagram also needs internal
45753
+ actors, use the bundled, tested legacy-view customization:
44678
45754
 
44679
45755
  \`\`\`shell
44680
- archinsight query . -c <context-id> -s <source.ai> -q examples/queries/deployment-internal-actors.aiq -f json
45756
+ archinsight query . -s <source.ai> -q examples/queries/deployment-internal-actors.aiq -f json
44681
45757
  \`\`\`
44682
45758
 
44683
- This query is generated from the exact built-in Deployment source and changes only the
44684
- node, projected-target, and incoming-source predicates to admit \`Actor\`.
44685
- Because the rest comes from the current built-in query, deployment targets,
44686
- incoming paths, and infrastructure-to-infrastructure projected path segments
44687
- stay synchronized with Deployment behavior.
45759
+ This query is generated from the exact legacy \`deployment.aiq\` source and
45760
+ changes only the node, projected-target, and incoming-source predicates to admit
45761
+ \`Actor\`. For a D1 or D2 customization, copy the corresponding
45762
+ \`deployment-system.aiq\` or \`deployment-container.aiq\` source and apply the
45763
+ same actor predicates there so its current scope and environment behavior remain
45764
+ intact.
44688
45765
 
44689
45766
  If the actor is declared in another source file, either render from that source
44690
45767
  or relax the \`node.sourceIdentity = $tab\` condition intentionally.
@@ -44722,8 +45799,8 @@ exist in the file but the diagram question is still logical.
44722
45799
 
44723
45800
  ## Projected Edges Across Split Files
44724
45801
 
44725
- The current built-in Deployment query matches \`{projected}\` edges without an explicit
44726
- \`sourceIdentity: $tab\` relationship filter. The query engine expands \`$tab\`
45802
+ The current built-in D1 and D2 queries match \`{projected}\` edges without an
45803
+ explicit \`sourceIdentity: $tab\` relationship filter. The query engine expands \`$tab\`
44727
45804
  to the roots declared by the selected source and contributions made to those
44728
45805
  roots through \`extend\`, so a normal multi-file split does not require a looser
44729
45806
  edge selector.
@@ -44733,7 +45810,8 @@ filter while keeping the node scope. Start again from the bundled current query
44733
45810
  instead of preserving the rest of the older copy:
44734
45811
 
44735
45812
  \`\`\`text
44736
- copy examples/builtin-views/deployment.aiq to the project's query directory
45813
+ copy examples/builtin-views/deployment-system.aiq for D1
45814
+ or copy examples/builtin-views/deployment-container.aiq for D2
44737
45815
  remove sourceIdentity: $tab only from the relationship selector if an old copy has it
44738
45816
  keep node.sourceIdentity = $tab
44739
45817
  \`\`\`
@@ -44744,17 +45822,16 @@ by the tab before changing the model or broadening the node scope.
44744
45822
 
44745
45823
  ## Change Grouping
44746
45824
 
44747
- Grouping controls visual clusters. If Deployment grouping by \`runsOn\` is not helpful,
44748
- copy the complete current \`examples/builtin-views/deployment.aiq\` and change only:
45825
+ Grouping controls visual clusters. If D1 or D2 grouping is not helpful, copy the
45826
+ complete corresponding built-in query and change only its \`GROUP BY\` expression:
44749
45827
 
44750
45828
  \`\`\`cypher
44751
45829
  GROUP BY node.parent
44752
45830
  \`\`\`
44753
45831
 
44754
45832
  Use \`GROUP BY node.runsOn\` for deployment placement; use \`GROUP BY node.parent\`
44755
- for logical ownership. If you still need target-owned ingress paths, start from
44756
- \`examples/builtin-views/deployment.aiq\` and change only the \`GROUP BY\` expression so
44757
- the incoming projection aliases stay in the \`RETURN\`.
45833
+ for logical ownership. Preserve the rest of the selected D1 or D2 query so its
45834
+ projected-neighborhood aliases and environment behavior remain unchanged.
44758
45835
 
44759
45836
  ## Working Rule
44760
45837
 
@@ -45225,11 +46302,10 @@ system commerce
45225
46302
  files: [
45226
46303
  projectionFile("definitions.ai", `define type BrokerEnvironment of Environment
45227
46304
  Compute compute
45228
- EventChannel events
46305
+ Broker events
45229
46306
 
45230
- define type EventChannel of NetworkConnection
45231
- constructor eventChannel
45232
- required Broker transport
46307
+ define type KafkaBroker of Broker
46308
+ constructor kafka
45233
46309
  `),
45234
46310
  projectionFile("infrastructure.ai", `environment eu
45235
46311
  name = Europe
@@ -45240,16 +46316,13 @@ deployment production
45240
46316
  name = Kubernetes
45241
46317
 
45242
46318
  events:
45243
- eventChannel order_events
45244
- name = Order events
45245
- transport:
45246
- broker kafka
45247
- name = Kafka
45248
- technology = Managed Kafka
46319
+ kafka kafka
46320
+ name = Kafka
46321
+ technology = Managed Kafka
45249
46322
  projection:
45250
- target $to connectTo target transport
46323
+ target $to connectTo target $this
45251
46324
  technology = Kafka
45252
- target transport originalLink source $from
46325
+ target $this originalLink source $from
45253
46326
  `),
45254
46327
  projectionFile("model.ai", `context broker_example
45255
46328
 
@@ -45436,7 +46509,7 @@ Run:
45436
46509
 
45437
46510
  \`\`\`shell
45438
46511
  archinsight link examples/deployment-projections/${example.slug} --format text
45439
- archinsight query examples/deployment-projections/${example.slug} -c ${example.context} -s model.ai -v deployment --format json
46512
+ archinsight query examples/deployment-projections/${example.slug} -s model.ai -v deployment-container --environment eu --format json
45440
46513
  \`\`\`
45441
46514
 
45442
46515
  Expected physical result:
@@ -45474,11 +46547,10 @@ receives only attributes written on that rule, while projection origin metadata
45474
46547
  still provides traceability. A path made entirely from \`connectTo\` therefore
45475
46548
  does not carry the logical operator or authored relationship attributes.
45476
46549
 
45477
- Wire \`uses\` accepts only a \`NetworkConnection\` descendant. A built-in
45478
- \`Broker\` is an \`InfrastructureComponent\`: it can exist in inventory, but a
45479
- wire cannot select it directly. Put the broker behind a project-defined
45480
- \`NetworkConnection\` vocabulary when it must participate in a physical wire
45481
- path. See [Broker](#broker), the consumer-owned relationship rules in
46550
+ Wire \`uses\` accepts only a \`NetworkConnection\` descendant. The built-in
46551
+ \`Broker\` is one and can fill a wire-facing environment slot directly. Derive
46552
+ product-specific broker types from \`Broker\`, and put the physical projection
46553
+ on the concrete broker instance. See [Broker](#broker), the consumer-owned relationship rules in
45482
46554
  [Eventing](modeling.md#eventing), and the shared-definition layout in
45483
46555
  [Scaling](scaling.md#framework-once-use-everywhere).
45484
46556
 
@@ -45753,6 +46825,19 @@ WHERE source.context = $context
45753
46825
  RETURN source, dependency, target
45754
46826
  `;
45755
46827
  }
46828
+ function genericDirectAuthoredDependenciesQuery() {
46829
+ return `MATCH (source:Element)-[dependency:REFERENCES]->(target:Element)
46830
+ WHERE source.context = $context
46831
+ RETURN source, dependency, target
46832
+ `;
46833
+ }
46834
+ function genericAsyncTopicDependenciesQuery() {
46835
+ return `MATCH (consumer:Element)-[event:REFERENCES]->(producer:Element)
46836
+ WHERE consumer.context = $context
46837
+ AND event.type = 'AsyncWire'
46838
+ RETURN consumer, event, producer
46839
+ `;
46840
+ }
45756
46841
  function genericKafkaServiceDependenciesQuery() {
45757
46842
  return `MATCH (consumer:ContainerElement)-[event:REFERENCES]->(producer:ContainerElement)
45758
46843
  WHERE consumer.context = $context