archctx 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/archctx.mjs CHANGED
@@ -2377,8 +2377,9 @@ var init_failure = __esm(() => {
2377
2377
  "model-missing": { code: "AC_MODEL_ADOPTION_REQUIRED", message: "Architecture debt requires an adopted model.", fields: ["checks"] },
2378
2378
  "changed-path-invalid": { code: "AC_SCHEMA_INVALID", message: "Changed paths must be an array of repository-relative POSIX paths.", fields: ["checks"] },
2379
2379
  "debt-report-invalid": { code: "AC_SCHEMA_INVALID", message: "Architecture debt evidence violates its contract.", fields: ["checks"] },
2380
- "projection-manifest-upgrade-required": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest requires supported re-baselining." },
2380
+ "projection-manifest-upgrade-required": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest requires supported re-baselining. First restore the original model if missing; the manifest digest cannot recover it. With the reviewed model available, follow docs/runbooks/architecture-documentation-projections.md before running archctx docs apply --approved --profile <default|repo-harness/v1>." },
2381
2381
  "projection-manifest-invalid": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest violates its contract." },
2382
+ "architecture-model-ignored": { code: "AC_PRECONDITION_FAILED", action: "commit-architecture-model", message: "Git ignore rules exclude architecture model paths, so the model cannot be committed and a manifest digest cannot rebuild it. Remove or narrow the rule, commit the model, then retry adoption. The diagnostic command prints each deciding rule.", facts: { pathCount: "uint", paths: "optional:diagnosticPaths", commands: "optional:diagnosticCommands", omittedPathCount: "uint" } },
2382
2383
  "projection-footprint-uncovered": { code: "AC_PRECONDITION_FAILED", message: "Declared source footprints lack a projection target." },
2383
2384
  "index-missing": { code: "AC_CODE_FACTS_UNAVAILABLE", message: "Required code facts have no available index. Run codegraph init." },
2384
2385
  "code-facts-not-ready": { code: "AC_CODE_FACTS_NOT_READY", message: "Repository code facts are not ready. Run archctx doctor --json and follow the reported action.", facts: { issues: "codeFactsIssues" } },
@@ -2816,6 +2817,89 @@ var init_failure = __esm(() => {
2816
2817
  };
2817
2818
  });
2818
2819
 
2820
+ // packages/contracts/src/runtime-topology.ts
2821
+ function isRuntimeTopologyNodeKind(kind) {
2822
+ return typeof kind === "string" && RUNTIME_TOPOLOGY_NODE_KINDS.includes(kind);
2823
+ }
2824
+ function runtimeTopologyModelIssues(model) {
2825
+ const issues = [];
2826
+ const nodesById = new Map;
2827
+ for (const node of model.nodes)
2828
+ if (typeof node.id === "string")
2829
+ nodesById.set(node.id, node);
2830
+ const namespacesByProcess = new Map;
2831
+ const mountersByMount = new Map;
2832
+ for (const relation of model.relations) {
2833
+ if (typeof relation.id !== "string" || typeof relation.kind !== "string" || !Object.hasOwn(RUNTIME_TOPOLOGY_RELATION_ENDPOINT_KINDS, relation.kind))
2834
+ continue;
2835
+ const rule = RUNTIME_TOPOLOGY_RELATION_ENDPOINT_KINDS[relation.kind];
2836
+ let endpointsValid = true;
2837
+ for (const field of ["source", "target"]) {
2838
+ const endpoint = relation[field];
2839
+ const node = typeof endpoint === "string" ? nodesById.get(endpoint) : undefined;
2840
+ if (!node) {
2841
+ issues.push({ entityId: relation.id, field, invariant: "topology-relation-endpoint-missing" });
2842
+ endpointsValid = false;
2843
+ } else if (!rule[field].includes(node.kind)) {
2844
+ issues.push({ entityId: relation.id, field, invariant: field === "source" ? "topology-relation-source-kind-invalid" : "topology-relation-target-kind-invalid" });
2845
+ endpointsValid = false;
2846
+ }
2847
+ }
2848
+ if (!endpointsValid)
2849
+ continue;
2850
+ const source = relation.source;
2851
+ const target = relation.target;
2852
+ if (relation.kind === "runs-in")
2853
+ namespacesByProcess.set(source, [...namespacesByProcess.get(source) ?? [], target]);
2854
+ if (relation.kind === "mounts")
2855
+ mountersByMount.set(target, [...mountersByMount.get(target) ?? [], source]);
2856
+ }
2857
+ for (const [processId, namespaces] of namespacesByProcess) {
2858
+ if (new Set(namespaces).size > 1)
2859
+ issues.push({ entityId: processId, field: "runs-in", invariant: "topology-process-namespace-ambiguous" });
2860
+ }
2861
+ for (const [mountId, mounters] of mountersByMount) {
2862
+ if (new Set(mounters).size > 1)
2863
+ issues.push({ entityId: mountId, field: "mounts", invariant: "topology-mount-mounter-ambiguous" });
2864
+ }
2865
+ for (const [id, node] of nodesById) {
2866
+ if (node.kind !== "namespace" || node.parent === undefined)
2867
+ continue;
2868
+ const parent = typeof node.parent === "string" ? nodesById.get(node.parent) : undefined;
2869
+ if (!parent) {
2870
+ issues.push({ entityId: id, field: "parent", invariant: "topology-namespace-parent-missing" });
2871
+ continue;
2872
+ }
2873
+ if (parent.kind !== "namespace") {
2874
+ issues.push({ entityId: id, field: "parent", invariant: "topology-namespace-parent-kind-invalid" });
2875
+ continue;
2876
+ }
2877
+ const seen = new Set([id]);
2878
+ let cursor = parent;
2879
+ while (cursor && cursor.kind === "namespace" && typeof cursor.id === "string") {
2880
+ if (seen.has(cursor.id)) {
2881
+ issues.push({ entityId: id, field: "parent", invariant: "topology-namespace-parent-cycle" });
2882
+ break;
2883
+ }
2884
+ seen.add(cursor.id);
2885
+ cursor = typeof cursor.parent === "string" ? nodesById.get(cursor.parent) : undefined;
2886
+ }
2887
+ }
2888
+ return issues.sort((left, right) => compare(left.entityId, right.entityId) || compare(left.field, right.field) || compare(left.invariant, right.invariant));
2889
+ }
2890
+ function compare(left, right) {
2891
+ return left < right ? -1 : left > right ? 1 : 0;
2892
+ }
2893
+ var RUNTIME_TOPOLOGY_NODE_KINDS, RUNTIME_TOPOLOGY_RELATION_ENDPOINT_KINDS;
2894
+ var init_runtime_topology = __esm(() => {
2895
+ RUNTIME_TOPOLOGY_NODE_KINDS = ["process", "namespace", "mount"];
2896
+ RUNTIME_TOPOLOGY_RELATION_ENDPOINT_KINDS = {
2897
+ spawns: { source: ["process"], target: ["process"] },
2898
+ "runs-in": { source: ["process"], target: ["namespace"] },
2899
+ mounts: { source: ["process", "namespace"], target: ["mount"] }
2900
+ };
2901
+ });
2902
+
2819
2903
  // packages/contracts/src/validator.ts
2820
2904
  function jsonSchemaResolver(documents) {
2821
2905
  const byId = new Map;
@@ -3159,6 +3243,10 @@ function nativeContextModelIssues(model, schemas, recordPaths) {
3159
3243
  if (relation[field] !== undefined && !nodes.has(relation[field]))
3160
3244
  add(relation, field, "relation-node-missing");
3161
3245
  }
3246
+ const relations = new Map(model.relations.map((relation) => [relation.id, relation]));
3247
+ for (const issue of runtimeTopologyModelIssues(model)) {
3248
+ add(relations.get(issue.entityId) ?? nodes.get(issue.entityId) ?? issue.entityId, issue.field, issue.invariant);
3249
+ }
3162
3250
  for (const flow of model.flows) {
3163
3251
  if (nodes.get(flow.capabilityId)?.kind !== "capability")
3164
3252
  add(flow, "capabilityId", "flow-capability-missing");
@@ -3201,6 +3289,7 @@ var NATIVE_CONTEXT_SCHEMA_VERSION = "archcontext.native-context-export/v1", NATI
3201
3289
  var init_native_context = __esm(() => {
3202
3290
  init_failure();
3203
3291
  init_schema();
3292
+ init_runtime_topology();
3204
3293
  init_validator();
3205
3294
  NATIVE_CONTEXT_LIMITS = Object.freeze({
3206
3295
  inputFiles: 1e4,
@@ -3784,6 +3873,8 @@ var init_projection_manifest = __esm(() => {
3784
3873
  "diagram-mermaid": true,
3785
3874
  "diagram-structurizr": true,
3786
3875
  "diagram-likec4": true,
3876
+ "diagram-runtime-topology": true,
3877
+ "runtime-topology-summary": true,
3787
3878
  "agent-context": true
3788
3879
  };
3789
3880
  SCOPE_KINDS = { repository: true, entity: true, relation: true, decision: true, diagram: true, changelog: true };
@@ -3876,7 +3967,7 @@ function compareVersionTriples(left, right) {
3876
3967
  }
3877
3968
  return 0;
3878
3969
  }
3879
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.1", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
3970
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.2", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
3880
3971
 
3881
3972
  // packages/contracts/src/refactor.ts
3882
3973
  function refactorNamespaceIssues(value, label = "product.id") {
@@ -4981,6 +5072,7 @@ var init_src = __esm(() => {
4981
5072
  init_projection_writer();
4982
5073
  init_projection_manifest();
4983
5074
  init_refactor();
5075
+ init_runtime_topology();
4984
5076
  init_schema();
4985
5077
  init_failure();
4986
5078
  init_validator();
@@ -17694,6 +17786,15 @@ var FIXED_TARGETS = [
17694
17786
  layoutTarget("projection_target.diagram.structurizr", "diagram-structurizr", { kind: "diagram", id: "architecture" }, "docs/architecture/diagrams/architecture.structurizr.json", "generated", "structurizr-json"),
17695
17787
  layoutTarget("projection_target.diagram.likec4", "diagram-likec4", { kind: "diagram", id: "architecture" }, "docs/architecture/diagrams/architecture.likec4", "generated", "likec4")
17696
17788
  ];
17789
+ var RUNTIME_TOPOLOGY_DIAGRAM_PATH = "docs/architecture/diagrams/runtime-topology.mmd";
17790
+ var RUNTIME_TOPOLOGY_SUMMARY_PATH = "docs/architecture/runtime-topology.md";
17791
+ var RUNTIME_TOPOLOGY_TARGETS = [
17792
+ layoutTarget("projection_target.diagram.runtime-topology", "diagram-runtime-topology", { kind: "diagram", id: "runtime-topology" }, RUNTIME_TOPOLOGY_DIAGRAM_PATH, "generated", "mermaid"),
17793
+ layoutTarget("projection_target.runtime-topology.summary", "runtime-topology-summary", { kind: "diagram", id: "runtime-topology" }, RUNTIME_TOPOLOGY_SUMMARY_PATH, "generated", "markdown")
17794
+ ];
17795
+ function hasRuntimeTopologyNodes(nodes) {
17796
+ return nodes.some((node) => isRuntimeTopologyNodeKind(node.kind));
17797
+ }
17697
17798
  function parseRepoHarnessNodeProfile(node) {
17698
17799
  const match = /^capability\.([a-z0-9]+(?:-[a-z0-9]+)*)\.([a-z0-9]+(?:-[a-z0-9]+)*)$/.exec(node.id);
17699
17800
  if (!match || node.kind !== "capability") {
@@ -17721,7 +17822,7 @@ function parseRepoHarnessNodeProfile(node) {
17721
17822
  };
17722
17823
  }
17723
17824
  function entitySummaryTargetNodes(nodes, profile) {
17724
- return profile === REPO_HARNESS_PROJECTION_PROFILE ? nodes.filter((node) => node.kind === "capability") : [...nodes];
17825
+ return profile === REPO_HARNESS_PROJECTION_PROFILE ? nodes.filter((node) => node.kind === "capability") : nodes.filter((node) => !isRuntimeTopologyNodeKind(node.kind));
17725
17826
  }
17726
17827
  function resolveArchitectureDocumentationLayout(input) {
17727
17828
  const profile = input.profile ?? "default";
@@ -17731,7 +17832,8 @@ function resolveArchitectureDocumentationLayout(input) {
17731
17832
  return layoutTarget(`projection_target.entity.${stableId(node.id)}`, "entity-summary", { kind: "entity", id: node.id, entityKind: node.kind }, path, "mixed", "markdown");
17732
17833
  });
17733
17834
  const relationTargets = (profile === REPO_HARNESS_PROJECTION_PROFILE ? [] : input.relations).map((relation) => layoutTarget(`projection_target.relation.${stableId(relation.id)}`, "relation-summary", { kind: "relation", id: relation.id }, `docs/architecture/relations/${pathSegment(relation.id)}.md`, "mixed", "markdown"));
17734
- const targets = [FIXED_TARGETS[0], ...entityTargets, ...relationTargets, ...FIXED_TARGETS.slice(1)].map((target) => ({ ...target, path: requiredPath(target.path, target.targetId) }));
17835
+ const topologyTargets = hasRuntimeTopologyNodes(input.nodes) ? RUNTIME_TOPOLOGY_TARGETS : [];
17836
+ const targets = [FIXED_TARGETS[0], ...entityTargets, ...relationTargets, ...FIXED_TARGETS.slice(1), ...topologyTargets].map((target) => ({ ...target, path: requiredPath(target.path, target.targetId) }));
17735
17837
  assertUniqueTargets(targets);
17736
17838
  return {
17737
17839
  profile,
@@ -20103,12 +20205,52 @@ function normalizeNativeModel(model) {
20103
20205
  flows: [...model.flows ?? []].sort((a, b) => a.id.localeCompare(b.id))
20104
20206
  };
20105
20207
  }
20208
+ var ARCHITECTURE_NODE_LAYER_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
20209
+ function nativeNodeLayer(node) {
20210
+ const layer = node.layer;
20211
+ if (layer === undefined)
20212
+ return;
20213
+ if (typeof layer !== "string" || !ARCHITECTURE_NODE_LAYER_PATTERN.test(layer)) {
20214
+ throw new Error(`architecture-node-layer-invalid: ${node.id} (${JSON.stringify(layer)})`);
20215
+ }
20216
+ return layer;
20217
+ }
20218
+ function architectureLayerGroups(model) {
20219
+ const nodesByLayer = new Map;
20220
+ const ungroupedNodes = [];
20221
+ const layerByNodeId = new Map;
20222
+ for (const node of [...model.nodes].sort((a, b) => a.id.localeCompare(b.id))) {
20223
+ const layer = nativeNodeLayer(node);
20224
+ if (layer === undefined) {
20225
+ ungroupedNodes.push(node);
20226
+ continue;
20227
+ }
20228
+ const members = nodesByLayer.get(layer);
20229
+ if (members)
20230
+ members.push(node);
20231
+ else
20232
+ nodesByLayer.set(layer, [node]);
20233
+ layerByNodeId.set(node.id, layer);
20234
+ }
20235
+ const layers = [...nodesByLayer.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([layer, nodes]) => ({ layer, nodes }));
20236
+ return { layers, ungroupedNodes, layerByNodeId };
20237
+ }
20238
+ function mermaidLayerId(layer) {
20239
+ return `layer__${layer.replace(/-/g, "_")}`;
20240
+ }
20106
20241
  function exportMermaidModel(model) {
20107
20242
  const normalized = normalizeNativeModel(model);
20243
+ const grouping = architectureLayerGroups(normalized);
20244
+ const nodeLine = (indent, node) => `${indent}${mermaidId(node.id)}["${escapeMermaid(node.name)}"]`;
20108
20245
  const lines = ["%% Generated by ArchContext. Native model remains source of truth.", "flowchart LR"];
20109
- for (const node of normalized.nodes) {
20110
- lines.push(` ${mermaidId(node.id)}["${escapeMermaid(node.name)}"]`);
20246
+ for (const group of grouping.layers) {
20247
+ lines.push(` subgraph ${mermaidLayerId(group.layer)}["${escapeMermaid(group.layer)}"]`);
20248
+ for (const node of group.nodes)
20249
+ lines.push(nodeLine(" ", node));
20250
+ lines.push(" end");
20111
20251
  }
20252
+ for (const node of grouping.ungroupedNodes)
20253
+ lines.push(nodeLine(" ", node));
20112
20254
  for (const relation of normalized.relations) {
20113
20255
  lines.push(` ${mermaidId(relation.source)} -->|"${escapeMermaid(relation.kind)}"| ${mermaidId(relation.target)}`);
20114
20256
  }
@@ -20125,22 +20267,15 @@ function exportDocumentationLikeC4Model(model) {
20125
20267
  const normalized = normalizeNativeModel(model);
20126
20268
  const lines = [
20127
20269
  "// Generated by ArchContext. Native model remains source of truth.",
20128
- "specification {",
20129
- " element capability",
20130
- " element module",
20131
- " element component",
20132
- " element interface",
20133
- " element datastore",
20134
- " element external_system",
20135
- "}",
20270
+ ...likeC4SpecificationLines(normalized.nodes),
20136
20271
  "",
20137
20272
  "model {"
20138
20273
  ];
20139
20274
  for (const node of normalized.nodes)
20140
- lines.push(` ${mermaidId(node.id)} = ${documentationLikeC4Kind(node.kind)} "${escapeDsl(node.name)}"`);
20275
+ lines.push(` ${mermaidId(node.id)} = ${likeC4ElementKind(node.kind)} "${escapeDsl(node.name)}"`);
20141
20276
  for (const relation of normalized.relations)
20142
20277
  lines.push(` ${mermaidId(relation.source)} -> ${mermaidId(relation.target)} "${escapeDsl(relation.kind)}"`);
20143
- lines.push("}", "", "views {", " view index {", " include *", " autoLayout TopBottom", " }", "}");
20278
+ lines.push("}", "", ...likeC4IndexViewLines(architectureLayerGroups(normalized), mermaidId));
20144
20279
  const content = `${lines.join(`
20145
20280
  `)}
20146
20281
  `;
@@ -20150,18 +20285,73 @@ function exportDocumentationLikeC4Model(model) {
20150
20285
  files: [{ path: "docs/architecture/diagrams/architecture.likec4", content }]
20151
20286
  };
20152
20287
  }
20288
+ function likeC4SpecificationLines(nodes) {
20289
+ const topologyKinds = RUNTIME_TOPOLOGY_NODE_KINDS.filter((kind) => nodes.some((node) => node.kind === kind));
20290
+ return [
20291
+ "specification {",
20292
+ " element capability",
20293
+ " element module",
20294
+ " element component",
20295
+ " element interface",
20296
+ " element datastore",
20297
+ " element external_system",
20298
+ ...topologyKinds.map((kind) => ` element ${kind}`),
20299
+ "}"
20300
+ ];
20301
+ }
20302
+ function likeC4ElementKind(kind) {
20303
+ if (kind === "external-system")
20304
+ return "external_system";
20305
+ if (kind === "datastore")
20306
+ return "datastore";
20307
+ if (["capability", "module", "component", "interface"].includes(kind))
20308
+ return kind;
20309
+ if (isRuntimeTopologyNodeKind(kind))
20310
+ return kind;
20311
+ return "component";
20312
+ }
20313
+ function architectureStructurizrElementType(kind) {
20314
+ if (kind === "capability")
20315
+ return "Software System";
20316
+ if (kind === "module")
20317
+ return "Container";
20318
+ if (kind === "datastore")
20319
+ return "Database";
20320
+ if (kind === "external-system")
20321
+ return "External System";
20322
+ if (kind === "namespace")
20323
+ return "Deployment Node";
20324
+ if (kind === "process")
20325
+ return "Container Instance";
20326
+ if (kind === "mount")
20327
+ return "Infrastructure Node";
20328
+ return "Component";
20329
+ }
20330
+ function likeC4IndexViewLines(grouping, elementId) {
20331
+ const groupLines = grouping.layers.flatMap((group) => [
20332
+ ` group "${escapeDsl(group.layer)}" {`,
20333
+ ...group.nodes.map((node) => ` include ${elementId(node.id)}`),
20334
+ " }"
20335
+ ]);
20336
+ return ["views {", " view index {", ...groupLines, " include *", " autoLayout TopBottom", " }", "}"];
20337
+ }
20153
20338
  function exportDocumentationStructurizrWorkspace(model) {
20154
20339
  const normalized = normalizeNativeModel(model);
20340
+ const { layerByNodeId } = architectureLayerGroups(normalized);
20155
20341
  const workspace = {
20156
20342
  schemaVersion: "archcontext.structurizr-export/v1",
20157
20343
  name: "ArchContext Architecture",
20158
20344
  model: {
20159
- elements: normalized.nodes.map((node) => ({
20160
- id: node.id,
20161
- name: node.name,
20162
- type: documentationStructurizrElementType(node.kind),
20163
- tags: [node.kind]
20164
- })),
20345
+ elements: normalized.nodes.map((node) => {
20346
+ const layer = layerByNodeId.get(node.id);
20347
+ return {
20348
+ id: node.id,
20349
+ name: node.name,
20350
+ type: architectureStructurizrElementType(node.kind),
20351
+ tags: [node.kind],
20352
+ ...layer === undefined ? {} : { group: layer }
20353
+ };
20354
+ }),
20165
20355
  relationships: normalized.relations.map((relation) => ({
20166
20356
  id: relation.id,
20167
20357
  sourceId: relation.source,
@@ -20184,6 +20374,209 @@ function exportDocumentationStructurizrWorkspace(model) {
20184
20374
  files: [{ path: "docs/architecture/diagrams/architecture.structurizr.json", content }]
20185
20375
  };
20186
20376
  }
20377
+ var RUNTIME_TOPOLOGY_NAMESPACE_TYPES = ["host", "container", "pod", "kubernetes", "pid", "mount", "net", "user"];
20378
+ var RUNTIME_TOPOLOGY_MOUNT_TARGET_PATTERN = /^\/([^/\u0000]+(\/[^/\u0000]+)*)?$/;
20379
+ function runtimeTopologyView(model) {
20380
+ const issues = runtimeTopologyModelIssues(model);
20381
+ if (issues.length > 0) {
20382
+ throw new Error(`runtime-topology-model-invalid: ${issues.map((issue) => `${issue.entityId} ${issue.field} ${issue.invariant}`).join("; ")}`);
20383
+ }
20384
+ const byId = (left, right) => compareStrings3(left.id, right.id);
20385
+ const nodes = [...model.nodes].sort(byId);
20386
+ const relations = [...model.relations].sort(byId);
20387
+ const nodesById = new Map(nodes.map((node) => [node.id, node]));
20388
+ const namespaces = nodes.filter((node) => node.kind === "namespace").map((node) => ({
20389
+ node,
20390
+ type: runtimeTopologyNamespaceType(node),
20391
+ ...typeof node.parent === "string" ? { parentId: node.parent } : {},
20392
+ namespaceIds: [],
20393
+ processIds: []
20394
+ }));
20395
+ const namespaceById = new Map(namespaces.map((namespace) => [namespace.node.id, namespace]));
20396
+ for (const namespace of namespaces) {
20397
+ if (namespace.parentId !== undefined)
20398
+ namespaceById.get(namespace.parentId).namespaceIds.push(namespace.node.id);
20399
+ }
20400
+ const namespaceByProcessId = new Map(relations.filter((relation) => relation.kind === "runs-in").map((relation) => [relation.source, relation.target]));
20401
+ const processes = nodes.filter((node) => node.kind === "process").map((node) => {
20402
+ const namespaceId = namespaceByProcessId.get(node.id);
20403
+ return namespaceId === undefined ? { node } : { node, namespaceId };
20404
+ });
20405
+ for (const topologyProcess of processes) {
20406
+ if (topologyProcess.namespaceId !== undefined)
20407
+ namespaceById.get(topologyProcess.namespaceId).processIds.push(topologyProcess.node.id);
20408
+ }
20409
+ const mounts = nodes.filter((node) => node.kind === "mount").map(runtimeTopologyMount);
20410
+ const mounterByMountId = new Map(relations.filter((relation) => relation.kind === "mounts").map((relation) => [relation.target, relation.source]));
20411
+ const stacksByMounterId = new Map;
20412
+ for (const mount of mounts) {
20413
+ const mounterId = mounterByMountId.get(mount.node.id);
20414
+ if (mounterId === undefined)
20415
+ continue;
20416
+ stacksByMounterId.set(mounterId, [...stacksByMounterId.get(mounterId) ?? [], mount]);
20417
+ }
20418
+ const mountStacks = [...stacksByMounterId.entries()].sort(([left], [right]) => compareStrings3(left, right)).map(([mounterId, stack]) => ({
20419
+ mounterId,
20420
+ mounts: stack.sort((left, right) => left.order - right.order || compareStrings3(left.node.id, right.node.id))
20421
+ }));
20422
+ const deployments = relations.filter((relation) => relation.kind === "deployed-on" && isRuntimeTopologyNodeKind(nodesById.get(relation.target)?.kind));
20423
+ const linkedNodeIds = new Set;
20424
+ for (const relation of deployments) {
20425
+ const source = nodesById.get(relation.source);
20426
+ if (!source)
20427
+ throw new Error(`runtime-topology-relation-endpoint-missing: ${relation.id} (${relation.source})`);
20428
+ if (!isRuntimeTopologyNodeKind(source.kind))
20429
+ linkedNodeIds.add(source.id);
20430
+ }
20431
+ return {
20432
+ nodesById,
20433
+ namespaces,
20434
+ rootNamespaceIds: namespaces.filter((namespace) => namespace.parentId === undefined).map((namespace) => namespace.node.id),
20435
+ processes,
20436
+ mountStacks,
20437
+ unmountedMounts: mounts.filter((mount) => !mounterByMountId.has(mount.node.id)),
20438
+ spawns: relations.filter((relation) => relation.kind === "spawns"),
20439
+ mounts: relations.filter((relation) => relation.kind === "mounts"),
20440
+ deployments,
20441
+ linkedNodes: nodes.filter((node) => linkedNodeIds.has(node.id))
20442
+ };
20443
+ }
20444
+ function runtimeTopologyNamespaceType(node) {
20445
+ const value = node.namespace;
20446
+ const type = value !== null && typeof value === "object" && !Array.isArray(value) ? value.type : undefined;
20447
+ if (typeof type !== "string" || !RUNTIME_TOPOLOGY_NAMESPACE_TYPES.includes(type)) {
20448
+ throw new Error(`runtime-topology-namespace-invalid: ${node.id}`);
20449
+ }
20450
+ return type;
20451
+ }
20452
+ function runtimeTopologyMount(node) {
20453
+ const value = node.mount;
20454
+ if (value === null || typeof value !== "object" || Array.isArray(value))
20455
+ throw new Error(`runtime-topology-mount-invalid: ${node.id}`);
20456
+ const { source, target, mode, order } = value;
20457
+ if (source !== undefined && (typeof source !== "string" || source === "") || typeof target !== "string" || !RUNTIME_TOPOLOGY_MOUNT_TARGET_PATTERN.test(target) || mode !== "ro" && mode !== "rw" || typeof order !== "number" || !Number.isInteger(order) || order < 0) {
20458
+ throw new Error(`runtime-topology-mount-invalid: ${node.id}`);
20459
+ }
20460
+ return { node, ...source === undefined ? {} : { source }, target, mode, order };
20461
+ }
20462
+ function runtimeTopologyMountStackId(mounterId) {
20463
+ return `mounts__${mermaidId(mounterId)}`;
20464
+ }
20465
+ function runtimeTopologyMountLabel(mount) {
20466
+ return `${mount.node.name} (${mount.target}, ${mount.mode}, order ${mount.order})`;
20467
+ }
20468
+ function exportRuntimeTopologyMermaid(model) {
20469
+ const view = runtimeTopologyView(model);
20470
+ const namespaceById = new Map(view.namespaces.map((namespace) => [namespace.node.id, namespace]));
20471
+ const nodeLine = (indent, node, label = node.name) => `${indent}${mermaidId(node.id)}["${escapeMermaid(label)}"]`;
20472
+ const lines = ["%% Generated by ArchContext. Native model remains source of truth.", "flowchart TB"];
20473
+ const pushNamespace = (namespaceId, depth) => {
20474
+ const namespace = namespaceById.get(namespaceId);
20475
+ const indent = " ".repeat(depth);
20476
+ lines.push(`${indent}subgraph ${mermaidId(namespaceId)}["${escapeMermaid(`${namespace.node.name} (${namespace.type})`)}"]`);
20477
+ for (const processId of namespace.processIds)
20478
+ lines.push(nodeLine(`${indent} `, view.nodesById.get(processId)));
20479
+ for (const childId of namespace.namespaceIds)
20480
+ pushNamespace(childId, depth + 1);
20481
+ lines.push(`${indent}end`);
20482
+ };
20483
+ for (const namespaceId of view.rootNamespaceIds)
20484
+ pushNamespace(namespaceId, 1);
20485
+ for (const topologyProcess of view.processes)
20486
+ if (topologyProcess.namespaceId === undefined)
20487
+ lines.push(nodeLine(" ", topologyProcess.node));
20488
+ for (const stack of view.mountStacks) {
20489
+ const mounter = view.nodesById.get(stack.mounterId);
20490
+ lines.push(` subgraph ${runtimeTopologyMountStackId(stack.mounterId)}["${escapeMermaid(`Mounts: ${mounter.name}`)}"]`, " direction TB");
20491
+ for (const mount of stack.mounts)
20492
+ lines.push(nodeLine(" ", mount.node, runtimeTopologyMountLabel(mount)));
20493
+ for (let index = 1;index < stack.mounts.length; index += 1) {
20494
+ lines.push(` ${mermaidId(stack.mounts[index - 1].node.id)} ~~~ ${mermaidId(stack.mounts[index].node.id)}`);
20495
+ }
20496
+ lines.push(" end");
20497
+ }
20498
+ for (const mount of view.unmountedMounts)
20499
+ lines.push(nodeLine(" ", mount.node, runtimeTopologyMountLabel(mount)));
20500
+ for (const node of view.linkedNodes)
20501
+ lines.push(nodeLine(" ", node));
20502
+ for (const relation of [...view.spawns, ...view.mounts, ...view.deployments].sort((left, right) => compareStrings3(left.id, right.id))) {
20503
+ lines.push(` ${mermaidId(relation.source)} -->|"${escapeMermaid(relation.kind)}"| ${mermaidId(relation.target)}`);
20504
+ }
20505
+ const content = `${lines.join(`
20506
+ `)}
20507
+ `;
20508
+ return {
20509
+ format: "mermaid",
20510
+ digest: digestJson({ format: "mermaid", content }),
20511
+ files: [{ path: RUNTIME_TOPOLOGY_DIAGRAM_PATH, content }]
20512
+ };
20513
+ }
20514
+ function renderRuntimeTopologySummary(model, summaryPath) {
20515
+ const view = runtimeTopologyView(model);
20516
+ const cell = (value) => value.replaceAll("|", "\\|");
20517
+ const named = (id) => {
20518
+ const node = view.nodesById.get(id);
20519
+ return `\`${id}\`${node ? ` ${cell(node.name)}` : ""}`;
20520
+ };
20521
+ const list = (ids) => ids.length === 0 ? "none" : ids.map((id) => `\`${id}\``).join(", ");
20522
+ const lines = [
20523
+ "# Runtime Topology",
20524
+ "",
20525
+ "The architecture model declares this topology. No code facts verify it.",
20526
+ "",
20527
+ `- Diagram: [Mermaid](${posix.relative(posix.dirname(summaryPath), RUNTIME_TOPOLOGY_DIAGRAM_PATH)})`,
20528
+ "",
20529
+ "## Namespaces",
20530
+ ""
20531
+ ];
20532
+ if (view.namespaces.length === 0)
20533
+ lines.push("- No namespaces recorded.");
20534
+ else {
20535
+ lines.push("| Namespace | Type | Parent | Namespaces | Processes |", "| --- | --- | --- | --- | --- |");
20536
+ for (const namespace of view.namespaces) {
20537
+ lines.push(`| ${named(namespace.node.id)} | ${namespace.type} | ${namespace.parentId === undefined ? "none" : `\`${namespace.parentId}\``} | ${list(namespace.namespaceIds)} | ${list(namespace.processIds)} |`);
20538
+ }
20539
+ }
20540
+ lines.push("", "## Processes", "");
20541
+ if (view.processes.length === 0)
20542
+ lines.push("- No processes recorded.");
20543
+ else {
20544
+ lines.push("| Process | Namespace | Spawns | Deployed here |", "| --- | --- | --- | --- |");
20545
+ for (const topologyProcess of view.processes) {
20546
+ const id = topologyProcess.node.id;
20547
+ const spawned = view.spawns.filter((relation) => relation.source === id).map((relation) => relation.target);
20548
+ const deployed = view.deployments.filter((relation) => relation.target === id).map((relation) => relation.source);
20549
+ lines.push(`| ${named(id)} | ${topologyProcess.namespaceId === undefined ? "none" : `\`${topologyProcess.namespaceId}\``} | ${list([...new Set(spawned)].sort(compareStrings3))} | ${list([...new Set(deployed)].sort(compareStrings3))} |`);
20550
+ }
20551
+ }
20552
+ lines.push("", "## Mount Stacks", "");
20553
+ const mountRows = (stack) => [
20554
+ "| Order | Mount | Source | Target | Mode |",
20555
+ "| --- | --- | --- | --- | --- |",
20556
+ ...stack.map((mount) => `| ${mount.order} | ${named(mount.node.id)} | ${mount.source === undefined ? "none" : `\`${cell(mount.source)}\``} | \`${cell(mount.target)}\` | ${mount.mode} |`)
20557
+ ];
20558
+ if (view.mountStacks.length === 0 && view.unmountedMounts.length === 0)
20559
+ lines.push("- No mounts recorded.");
20560
+ for (const [index, stack] of view.mountStacks.entries()) {
20561
+ if (index > 0)
20562
+ lines.push("");
20563
+ lines.push(`### ${named(stack.mounterId)}`, "", ...mountRows(stack.mounts));
20564
+ }
20565
+ if (view.unmountedMounts.length > 0) {
20566
+ if (view.mountStacks.length > 0)
20567
+ lines.push("");
20568
+ lines.push("### Not mounted", "", ...mountRows(view.unmountedMounts));
20569
+ }
20570
+ const otherDeployments = view.deployments.filter((relation) => view.nodesById.get(relation.target)?.kind !== "process");
20571
+ if (otherDeployments.length > 0) {
20572
+ lines.push("", "## Other Deployments", "");
20573
+ for (const relation of otherDeployments)
20574
+ lines.push(`- \`${relation.source}\` -> \`${relation.target}\` — ${relation.intent}`);
20575
+ }
20576
+ return `${lines.join(`
20577
+ `)}
20578
+ `;
20579
+ }
20187
20580
  function loadNativeModelFromArchContext(root) {
20188
20581
  return nativeModelFromYamlObjects({
20189
20582
  nodes: readYamlObjects(resolve6(root, ".archcontext/model/nodes")),
@@ -20202,6 +20595,7 @@ function nativeModelFromYamlObjects(input) {
20202
20595
  throw new Error(`architecture-node-schema-version-unsupported: ${node.id || "unknown"} (${String(node.schemaVersion)})`);
20203
20596
  }
20204
20597
  assertArchitectureNodeSourceV2(node);
20598
+ nativeNodeLayer(node);
20205
20599
  }
20206
20600
  const flows = input.flows;
20207
20601
  for (const flow of flows)
@@ -20386,8 +20780,15 @@ function renderTargetGeneratedBody(target, model, input) {
20386
20780
  return exportDocumentationStructurizrWorkspace(model).files[0].content;
20387
20781
  if (target.type === "diagram-likec4")
20388
20782
  return exportDocumentationLikeC4Model(model).files[0].content;
20783
+ if (target.type === "diagram-runtime-topology")
20784
+ return exportRuntimeTopologyMermaid(model).files[0].content;
20785
+ if (target.type === "runtime-topology-summary")
20786
+ return renderRuntimeTopologySummary(model, target.path);
20389
20787
  return "";
20390
20788
  }
20789
+ function architectureIndexLink(path) {
20790
+ return path.replace(/^docs\/architecture\//, "");
20791
+ }
20391
20792
  function renderArchitectureIndex(model, layout2) {
20392
20793
  const lines = [
20393
20794
  "# Architecture Index",
@@ -20412,6 +20813,7 @@ function renderArchitectureIndex(model, layout2) {
20412
20813
  "- [Mermaid](diagrams/architecture.mmd)",
20413
20814
  "- [Structurizr JSON](diagrams/architecture.structurizr.json)",
20414
20815
  "- [LikeC4](diagrams/architecture.likec4)",
20816
+ ...layout2.targets.flatMap((target) => target.type === "diagram-runtime-topology" ? [`- [Runtime topology Mermaid](${architectureIndexLink(target.path)})`] : target.type === "runtime-topology-summary" ? [`- [Runtime topology](${architectureIndexLink(target.path)})`] : []),
20415
20817
  "- [Decision index](decisions/index.md)",
20416
20818
  "- [Architecture changelog](changelog.md)"
20417
20819
  ];
@@ -20865,26 +21267,6 @@ function isManagedArchitectureDocumentationMarkdownPath(path) {
20865
21267
  function pathSegment2(id) {
20866
21268
  return stableId(id).replace(/\./g, "-");
20867
21269
  }
20868
- function documentationLikeC4Kind(kind) {
20869
- if (kind === "external-system")
20870
- return "external_system";
20871
- if (kind === "datastore")
20872
- return "datastore";
20873
- if (["capability", "module", "component", "interface"].includes(kind))
20874
- return kind;
20875
- return "component";
20876
- }
20877
- function documentationStructurizrElementType(kind) {
20878
- if (kind === "capability")
20879
- return "Software System";
20880
- if (kind === "module")
20881
- return "Container";
20882
- if (kind === "datastore")
20883
- return "Database";
20884
- if (kind === "external-system")
20885
- return "External System";
20886
- return "Component";
20887
- }
20888
21270
  function escapeDsl(value) {
20889
21271
  return value.replace(/"/g, "'");
20890
21272
  }
@@ -21548,7 +21930,7 @@ function sameOwners(left, right) {
21548
21930
 
21549
21931
  // packages/core/module-statistics/src/index.ts
21550
21932
  function evaluateDependencyConstraints(input) {
21551
- const constraints = [...input.constraints].sort((left, right) => compare(left.id, right.id));
21933
+ const constraints = [...input.constraints].sort((left, right) => compare2(left.id, right.id));
21552
21934
  const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated, input.importNodeLimitExceeded ?? false);
21553
21935
  if (constraints.length === 0) {
21554
21936
  return {
@@ -21605,7 +21987,7 @@ function evaluateDependencyConstraints(input) {
21605
21987
  reasonCodes.add("unresolved-import");
21606
21988
  if (input.workspacePackagesResolved === false)
21607
21989
  reasonCodes.add("workspace-resolution-failed");
21608
- const sortedViolations = [...violations.values()].sort((left, right) => compare(left.constraintId, right.constraintId) || compare(left.fromPath, right.fromPath) || compare(left.toPath, right.toPath));
21990
+ const sortedViolations = [...violations.values()].sort((left, right) => compare2(left.constraintId, right.constraintId) || compare2(left.fromPath, right.fromPath) || compare2(left.toPath, right.toPath));
21609
21991
  return {
21610
21992
  schemaVersion: "archcontext.dependency-constraint-evaluation/v1",
21611
21993
  status: sortedViolations.length > 0 ? "violated" : reasonCodes.size > 0 ? "undetermined" : "pass",
@@ -21682,7 +22064,7 @@ function measureCapabilityDependencies(input) {
21682
22064
  const cellKey = JSON.stringify([from.capability, to.capability]);
21683
22065
  let cell = cells.get(cellKey);
21684
22066
  if (cell === undefined) {
21685
- const relationIds = [...new Set(declaringRelations.get(cellKey) ?? [])].sort(compare);
22067
+ const relationIds = [...new Set(declaringRelations.get(cellKey) ?? [])].sort(compare2);
21686
22068
  cell = { from: from.capability, to: to.capability, imports: 0, calls: null, declared: from.capability === to.capability || relationIds.length > 0, relationIds };
21687
22069
  cells.set(cellKey, cell);
21688
22070
  }
@@ -21690,14 +22072,14 @@ function measureCapabilityDependencies(input) {
21690
22072
  attributedEdges.push({ fromPath: edge.from, toPath: edge.to, fromCapability: from.capability, toCapability: to.capability });
21691
22073
  }
21692
22074
  const edgeLimit = input.edgeLimit ?? CAPABILITY_DEPENDENCIES_EDGE_LIMIT;
21693
- attributedEdges.sort((left, right) => compare(left.fromPath, right.fromPath) || compare(left.toPath, right.toPath));
22075
+ attributedEdges.sort((left, right) => compare2(left.fromPath, right.fromPath) || compare2(left.toPath, right.toPath));
21694
22076
  const listed = attributedEdges.slice(0, edgeLimit);
21695
22077
  return {
21696
22078
  schemaVersion: CAPABILITY_DEPENDENCIES_SCHEMA_VERSION,
21697
22079
  ownershipRule: "parent-chain",
21698
22080
  filter: { from: input.from ?? null, into: input.into ?? null },
21699
- capabilities: input.nodes.filter((node) => node.kind === "capability").map((node) => node.id).sort(compare),
21700
- matrix: [...cells.values()].sort((left, right) => compare(left.from, right.from) || compare(left.to, right.to)),
22081
+ capabilities: input.nodes.filter((node) => node.kind === "capability").map((node) => node.id).sort(compare2),
22082
+ matrix: [...cells.values()].sort((left, right) => compare2(left.from, right.from) || compare2(left.to, right.to)),
21701
22083
  imports: {
21702
22084
  coverage: certification.coverage,
21703
22085
  reasonCodes: [...certification.reasonCodes].sort(),
@@ -21733,7 +22115,7 @@ function isRepositoryLocalSpecifier(specifier, workspacePackages) {
21733
22115
  return true;
21734
22116
  return workspacePackages.some((workspacePackage) => specifier === workspacePackage.name || specifier.startsWith(`${workspacePackage.name}/`));
21735
22117
  }
21736
- function compare(left, right) {
22118
+ function compare2(left, right) {
21737
22119
  return left < right ? -1 : left > right ? 1 : 0;
21738
22120
  }
21739
22121
  function measureModuleStatistics(input) {
@@ -21818,7 +22200,7 @@ function modelDigest(model, constraints) {
21818
22200
  nodes: [...model.nodes].sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0),
21819
22201
  relations: [...model.relations].sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0),
21820
22202
  flows: [...model.flows ?? []].sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0),
21821
- ...constraints.length === 0 ? {} : { constraints: [...constraints].sort((left, right) => compare(left.id, right.id)) }
22203
+ ...constraints.length === 0 ? {} : { constraints: [...constraints].sort((left, right) => compare2(left.id, right.id)) }
21822
22204
  });
21823
22205
  }
21824
22206
  function countViolationsByNode(evaluation) {
@@ -22715,6 +23097,29 @@ function listGitVisibleFiles(root, pathspec) {
22715
23097
  }
22716
23098
  }).sort((left, right) => left.localeCompare(right));
22717
23099
  }
23100
+ function ignoredRepositoryPaths(root, paths) {
23101
+ if (paths.length === 0)
23102
+ return [];
23103
+ const result = spawnSync2("git", ["check-ignore", "--verbose", "--no-index", "-z", "--stdin"], {
23104
+ cwd: root,
23105
+ encoding: "utf8",
23106
+ input: paths.map((path) => `${path}\x00`).join(""),
23107
+ stdio: ["pipe", "pipe", "pipe"]
23108
+ });
23109
+ if (result.status !== 0 && result.status !== 1) {
23110
+ const detail = result.error?.message ?? (result.stderr.trim() || `git check-ignore exited ${result.status}`);
23111
+ throw new GitVisibleFilesUnavailableError(root, detail);
23112
+ }
23113
+ const fields = result.stdout.split("\x00");
23114
+ const ignored = [];
23115
+ for (let index = 0;index + 3 < fields.length; index += 4) {
23116
+ const [source, line, pattern, path] = fields.slice(index, index + 4);
23117
+ if (pattern.startsWith("!"))
23118
+ continue;
23119
+ ignored.push({ path, source, line: Number(line), pattern });
23120
+ }
23121
+ return ignored;
23122
+ }
22718
23123
  function readHeadSha(root) {
22719
23124
  try {
22720
23125
  return execFileSync("git", ["rev-parse", "HEAD"], {
@@ -26083,7 +26488,7 @@ function buildObservations(snapshot, structure, model) {
26083
26488
  evidence: evidence.evidenceGap()
26084
26489
  });
26085
26490
  }
26086
- return drafts.map((draft, index) => ({ draft, order: draft.kind === "cycle" ? index : 0 })).sort((left, right) => compare2(left.draft.kind, right.draft.kind) || left.order - right.order || compare2(left.draft.subjectSelectorId, right.draft.subjectSelectorId)).map(({ draft }) => ({
26491
+ return drafts.map((draft, index) => ({ draft, order: draft.kind === "cycle" ? index : 0 })).sort((left, right) => compare3(left.draft.kind, right.draft.kind) || left.order - right.order || compare3(left.draft.subjectSelectorId, right.draft.subjectSelectorId)).map(({ draft }) => ({
26087
26492
  kind: draft.kind,
26088
26493
  subjectSelectorId: draft.subjectSelectorId,
26089
26494
  affectedNodeIds: draft.affectedNodeIds,
@@ -26273,13 +26678,13 @@ function signalIdFor(draft) {
26273
26678
  }
26274
26679
  function modelDigest2(model, constraints) {
26275
26680
  return digestJson({
26276
- nodes: [...model.nodes].sort((left, right) => compare2(left.id, right.id)),
26277
- relations: [...model.relations].sort((left, right) => compare2(left.id, right.id)),
26278
- flows: [...model.flows ?? []].sort((left, right) => compare2(left.id, right.id)),
26279
- ...constraints.length === 0 ? {} : { constraints: [...constraints].sort((left, right) => compare2(left.id, right.id)) }
26681
+ nodes: [...model.nodes].sort((left, right) => compare3(left.id, right.id)),
26682
+ relations: [...model.relations].sort((left, right) => compare3(left.id, right.id)),
26683
+ flows: [...model.flows ?? []].sort((left, right) => compare3(left.id, right.id)),
26684
+ ...constraints.length === 0 ? {} : { constraints: [...constraints].sort((left, right) => compare3(left.id, right.id)) }
26280
26685
  });
26281
26686
  }
26282
- function compare2(left, right) {
26687
+ function compare3(left, right) {
26283
26688
  return left < right ? -1 : left > right ? 1 : 0;
26284
26689
  }
26285
26690
  function isNumber(value) {
@@ -26301,17 +26706,49 @@ var architecture_node_schema_default = {
26301
26706
  type: "object",
26302
26707
  additionalProperties: false,
26303
26708
  required: ["schemaVersion", "id", "kind", "name", "status", "summary"],
26709
+ allOf: [
26710
+ {
26711
+ if: { properties: { kind: { const: "namespace" } }, required: ["kind"] },
26712
+ then: { required: ["namespace"] },
26713
+ else: { not: { required: ["namespace"] } }
26714
+ },
26715
+ {
26716
+ if: { properties: { kind: { const: "mount" } }, required: ["kind"] },
26717
+ then: { required: ["mount"] },
26718
+ else: { not: { required: ["mount"] } }
26719
+ }
26720
+ ],
26304
26721
  properties: {
26305
26722
  schemaVersion: { const: "archcontext.node/v2" },
26306
26723
  id: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
26307
26724
  kind: {
26308
26725
  type: "string",
26309
- enum: ["capability", "module", "component", "interface", "event", "datastore", "external-system", "deployment"]
26726
+ enum: ["capability", "module", "component", "interface", "event", "datastore", "external-system", "deployment", "process", "namespace", "mount"]
26310
26727
  },
26311
26728
  name: { type: "string" },
26312
26729
  status: { type: "string", enum: ["active", "planned", "deprecated", "removed"] },
26313
26730
  parent: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
26731
+ layer: { type: "string", pattern: "^[a-z][a-z0-9]*(-[a-z0-9]+)*$" },
26314
26732
  summary: { type: "string" },
26733
+ namespace: {
26734
+ type: "object",
26735
+ additionalProperties: false,
26736
+ required: ["type"],
26737
+ properties: {
26738
+ type: { type: "string", enum: ["host", "container", "pod", "kubernetes", "pid", "mount", "net", "user"] }
26739
+ }
26740
+ },
26741
+ mount: {
26742
+ type: "object",
26743
+ additionalProperties: false,
26744
+ required: ["target", "mode", "order"],
26745
+ properties: {
26746
+ source: { type: "string", minLength: 1 },
26747
+ target: { type: "string", pattern: "^/([^/\\u0000]+(/[^/\\u0000]+)*)?$" },
26748
+ mode: { type: "string", enum: ["ro", "rw"] },
26749
+ order: { type: "integer", minimum: 0 }
26750
+ }
26751
+ },
26315
26752
  responsibilities: { type: "array", items: { type: "string" }, minItems: 1 },
26316
26753
  source: {
26317
26754
  type: "object",
@@ -26400,7 +26837,7 @@ var architecture_relation_schema_default = {
26400
26837
  id: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
26401
26838
  kind: {
26402
26839
  type: "string",
26403
- enum: ["depends-on", "calls", "publishes", "subscribes", "reads", "writes", "implements", "deployed-on"]
26840
+ enum: ["depends-on", "calls", "publishes", "subscribes", "reads", "writes", "implements", "deployed-on", "spawns", "runs-in", "mounts"]
26404
26841
  },
26405
26842
  source: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
26406
26843
  target: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
@@ -27108,6 +27545,22 @@ function createDefaultProjectionTargetManifest() {
27108
27545
  ownership: "generated",
27109
27546
  format: "likec4"
27110
27547
  },
27548
+ {
27549
+ id: "projection_rule.diagram.runtime-topology",
27550
+ targetType: "diagram-runtime-topology",
27551
+ scope: { kind: "diagram", id: "runtime-topology" },
27552
+ pathTemplate: "docs/architecture/diagrams/runtime-topology.mmd",
27553
+ ownership: "generated",
27554
+ format: "mermaid"
27555
+ },
27556
+ {
27557
+ id: "projection_rule.runtime-topology.summary",
27558
+ targetType: "runtime-topology-summary",
27559
+ scope: { kind: "diagram", id: "runtime-topology" },
27560
+ pathTemplate: "docs/architecture/runtime-topology.md",
27561
+ ownership: "generated",
27562
+ format: "markdown"
27563
+ },
27111
27564
  {
27112
27565
  id: "projection_rule.agent-context.claude",
27113
27566
  targetType: "agent-context",
@@ -27253,10 +27706,19 @@ class YamlModelStore {
27253
27706
  }
27254
27707
  }
27255
27708
  const files = listModelFiles(workspace.root);
27709
+ const topologyRecords = { nodes: [], relations: [] };
27710
+ const topologyRecordPaths = new Map;
27256
27711
  for (const file of files) {
27257
27712
  if (file.path.startsWith(".archcontext/model/") && /\.ya?ml$/.test(file.path)) {
27258
27713
  try {
27259
27714
  const value = parseJsonOrStableYaml(file.body, file.path);
27715
+ for (const [directory, records] of [["nodes", topologyRecords.nodes], ["relations", topologyRecords.relations]]) {
27716
+ if (!file.path.startsWith(`.archcontext/model/${directory}/`) || !isJsonRecord2(value))
27717
+ continue;
27718
+ records.push(value);
27719
+ if (typeof value.id === "string")
27720
+ topologyRecordPaths.set(value.id, file.path);
27721
+ }
27260
27722
  if (file.path.startsWith(".archcontext/model/nodes/")) {
27261
27723
  const { issues } = validateJsonSchema(architecture_node_schema_default, value);
27262
27724
  for (const issue of issues) {
@@ -27277,6 +27739,9 @@ class YamlModelStore {
27277
27739
  errors.push(`${file.path}: expected archcontext.flow/v1, got ${file.schemaVersion || "missing"}`);
27278
27740
  }
27279
27741
  }
27742
+ for (const issue of runtimeTopologyModelIssues(topologyRecords)) {
27743
+ errors.push(`${topologyRecordPaths.get(issue.entityId) ?? issue.entityId}: ${issue.field}: ${issue.invariant}`);
27744
+ }
27280
27745
  const adr = validateAdrAppliesTo(files);
27281
27746
  const constraints = readDependencyConstraints(files);
27282
27747
  const reviewPolicy = readReviewPolicy(files);
@@ -27297,6 +27762,21 @@ class YamlModelStore {
27297
27762
  return { digest: digestJson(changeSet), summary: "ChangeSet preview generated" };
27298
27763
  }
27299
27764
  }
27765
+ var ARCHITECTURE_MODEL_COMMITTED_PATHS = Object.freeze([
27766
+ ".archcontext/manifest.yaml",
27767
+ ".archcontext/product.yaml",
27768
+ ".archcontext/model/nodes/",
27769
+ ".archcontext/model/relations/",
27770
+ ".archcontext/model/flows/",
27771
+ ".archcontext/decisions/",
27772
+ ".archcontext/policies/",
27773
+ ".archcontext/practices/",
27774
+ ".archcontext/projections/"
27775
+ ]);
27776
+ function architectureModelIgnoreProbePaths(root) {
27777
+ const directories = ARCHITECTURE_MODEL_COMMITTED_PATHS.filter((path) => path.endsWith("/")).map((path) => path.slice(0, -1));
27778
+ return [...new Set([...ARCHITECTURE_MODEL_COMMITTED_PATHS, ...collectArchContextFiles(root, directories)])];
27779
+ }
27300
27780
  function listModelFiles(root) {
27301
27781
  const paths = collectArchContextFiles(root, [
27302
27782
  ".archcontext/manifest.yaml",
@@ -27381,6 +27861,9 @@ function extractSchemaVersion(body, path) {
27381
27861
  return "archcontext.generated/v1";
27382
27862
  return "";
27383
27863
  }
27864
+ function isJsonRecord2(value) {
27865
+ return value !== null && typeof value === "object" && !Array.isArray(value);
27866
+ }
27384
27867
  function withTrailingNewline(body) {
27385
27868
  return body.endsWith(`
27386
27869
  `) ? body : `${body}
@@ -31318,13 +31801,10 @@ class HttpContext7Transport {
31318
31801
  url.searchParams.set("libraryName", input.libraryName);
31319
31802
  url.searchParams.set("query", input.query);
31320
31803
  url.searchParams.set("fast", input.fast ? "true" : "false");
31321
- const response = await fetchWithTimeout(url, {
31804
+ const { payload } = await fetchJsonWithTimeout(url, {
31322
31805
  method: "GET",
31323
31806
  headers: authHeaders(input.apiKey)
31324
- }, input.timeoutMs);
31325
- if (!response.ok)
31326
- throw httpStatusError("Context7 search failed", response);
31327
- const { payload } = await parseJsonResponse(response, "Context7 search");
31807
+ }, input.timeoutMs, "Context7 search");
31328
31808
  return parseSearchResponse(payload);
31329
31809
  }
31330
31810
  async getContext(input) {
@@ -31333,13 +31813,10 @@ class HttpContext7Transport {
31333
31813
  url.searchParams.set("query", input.query);
31334
31814
  url.searchParams.set("type", "json");
31335
31815
  url.searchParams.set("fast", "true");
31336
- const response = await fetchWithTimeout(url, {
31816
+ const { payload } = await fetchJsonWithTimeout(url, {
31337
31817
  method: "GET",
31338
31818
  headers: authHeaders(input.apiKey)
31339
- }, input.timeoutMs);
31340
- if (!response.ok)
31341
- throw httpStatusError("Context7 context fetch failed", response);
31342
- const { payload } = await parseJsonResponse(response, "Context7 context fetch");
31819
+ }, input.timeoutMs, "Context7 context fetch");
31343
31820
  return parseDocumentationResponse(payload).slice(0, input.maxResults);
31344
31821
  }
31345
31822
  }
@@ -31437,11 +31914,14 @@ function normalizeSourceUri(value) {
31437
31914
  return;
31438
31915
  }
31439
31916
  }
31440
- async function fetchWithTimeout(input, init, timeoutMs) {
31917
+ async function fetchJsonWithTimeout(input, init, timeoutMs, label) {
31441
31918
  const controller = new AbortController;
31442
31919
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
31443
31920
  try {
31444
- return await withLocalEgress2("context7", () => fetch(input, { ...init, redirect: "error", signal: controller.signal }));
31921
+ const response = await withLocalEgress2("context7", () => fetch(input, { ...init, redirect: "error", signal: controller.signal }));
31922
+ if (!response.ok)
31923
+ throw httpStatusError(`${label} failed`, response);
31924
+ return await parseJsonResponse(response, label);
31445
31925
  } catch (error) {
31446
31926
  if (isAbortError(error)) {
31447
31927
  throw new Context7ProviderError("timeout", "Context7 provider request timed out", { retryable: true, cause: error });
@@ -32674,6 +33154,11 @@ async function runArchitectureDocsProjectionCommand(input, cwd, daemon) {
32674
33154
  try {
32675
33155
  const subcommand = input.action ?? "plan";
32676
33156
  const root = findRepositoryRoot(cwd);
33157
+ if (subcommand === "adopt") {
33158
+ const ignored = architectureModelIgnoredEnvelope("docs.adopt", root);
33159
+ if (ignored)
33160
+ return ignored;
33161
+ }
32677
33162
  let profile;
32678
33163
  let projection;
32679
33164
  let acceptedChange;
@@ -32789,6 +33274,23 @@ async function runArchitectureDocsProjectionCommand(input, cwd, daemon) {
32789
33274
  throw publicOperationFailure(error);
32790
33275
  }
32791
33276
  }
33277
+ function ignoredArchitectureModelPaths(root) {
33278
+ const ignored = ignoredRepositoryPaths(root, architectureModelIgnoreProbePaths(root));
33279
+ const directories = ignored.filter((entry) => entry.path.endsWith("/")).map((entry) => entry.path);
33280
+ return ignored.filter((entry) => entry.path.endsWith("/") || !directories.some((directory) => entry.path.startsWith(directory)));
33281
+ }
33282
+ function architectureModelIgnoredEnvelope(requestId, root) {
33283
+ const ignored = ignoredArchitectureModelPaths(root);
33284
+ if (ignored.length === 0)
33285
+ return;
33286
+ const paths = ignored.map((entry) => entry.path).slice(0, 8);
33287
+ return publicFailureEnvelope(requestId, "AC_PRECONDITION_FAILED", "architecture-model-ignored", {
33288
+ pathCount: ignored.length,
33289
+ paths,
33290
+ commands: [`git check-ignore --verbose --no-index -- ${paths.join(" ")}`],
33291
+ omittedPathCount: ignored.length - paths.length
33292
+ });
33293
+ }
32792
33294
  function committedArchitectureDocsProfile(root) {
32793
33295
  return existsSync9(resolve13(root, ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH)) ? loadArchitectureDocumentationProfile(root) : "default";
32794
33296
  }
@@ -33099,6 +33601,11 @@ async function runProjectionProtocolCommand(invocation, cwd, daemon) {
33099
33601
  if (replay)
33100
33602
  return replay;
33101
33603
  }
33604
+ if (request.mode === "adopt") {
33605
+ const ignored = architectureModelIgnoredEnvelope("projection.run", root);
33606
+ if (ignored)
33607
+ return ignored;
33608
+ }
33102
33609
  let observed;
33103
33610
  let acceptedChange = request.acceptedChange;
33104
33611
  let declinedUnprovableNodeIds;
@@ -34663,6 +35170,7 @@ init_projection_recovery();
34663
35170
  init_projection_writer();
34664
35171
  init_projection_manifest();
34665
35172
  init_refactor();
35173
+ init_runtime_topology();
34666
35174
  init_schema();
34667
35175
  init_failure();
34668
35176
  init_validator();
@@ -47072,7 +47580,7 @@ function upgradeExistingLocalStoreTarget2(paths, integrityCheck) {
47072
47580
  var RUNTIME_AGENT_JOB_STATUSES = ["queued", "running", "succeeded", "failed", "cancelled", "superseded", "expired"];
47073
47581
  var CHANGESET_MODEL_TRANSITION_SCHEMA_VERSION = "archcontext.changeset-model-transition/v1";
47074
47582
  function assertChangeSetModelTransition(value, label = "changeset-model-transition-invalid") {
47075
- if (!isJsonRecord2(value) || Object.keys(value).sort().join(",") !== "after,before,schemaVersion" || value.schemaVersion !== CHANGESET_MODEL_TRANSITION_SCHEMA_VERSION || typeof value.before !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.before) || typeof value.after !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.after)) {
47583
+ if (!isJsonRecord3(value) || Object.keys(value).sort().join(",") !== "after,before,schemaVersion" || value.schemaVersion !== CHANGESET_MODEL_TRANSITION_SCHEMA_VERSION || typeof value.before !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.before) || typeof value.after !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.after)) {
47076
47584
  throw new Error(label);
47077
47585
  }
47078
47586
  return { schemaVersion: CHANGESET_MODEL_TRANSITION_SCHEMA_VERSION, before: value.before, after: value.after };
@@ -51360,10 +51868,10 @@ function readChangeSetJournalTaskSessionId(metadataJson, journalId) {
51360
51868
  } catch {
51361
51869
  throw new Error(`changeset-journal-metadata-malformed: ${journalId}`);
51362
51870
  }
51363
- if (!isJsonRecord2(metadata))
51871
+ if (!isJsonRecord3(metadata))
51364
51872
  throw new Error(`changeset-journal-metadata-malformed: ${journalId}`);
51365
51873
  const reason = metadata.reason;
51366
- if (!isJsonRecord2(reason) || typeof reason.taskSessionId !== "string" || reason.taskSessionId === "") {
51874
+ if (!isJsonRecord3(reason) || typeof reason.taskSessionId !== "string" || reason.taskSessionId === "") {
51367
51875
  throw new Error(`changeset-journal-reason-malformed: ${journalId}`);
51368
51876
  }
51369
51877
  return reason.taskSessionId;
@@ -51396,7 +51904,7 @@ function committedChangeSetJournalFiles(filesJson, journalId) {
51396
51904
  if (!Array.isArray(parsed))
51397
51905
  throw new Error(`changeset-journal-files-malformed: ${journalId}`);
51398
51906
  return parsed.map((entry) => {
51399
- if (!isJsonRecord2(entry) || typeof entry.path !== "string" || typeof entry.operation !== "string") {
51907
+ if (!isJsonRecord3(entry) || typeof entry.path !== "string" || typeof entry.operation !== "string") {
51400
51908
  throw new Error(`changeset-journal-file-malformed: ${journalId}`);
51401
51909
  }
51402
51910
  if (typeof entry.bodyHash !== "string" || entry.bodyHash === "") {
@@ -51416,7 +51924,7 @@ function committedChangeSetProjectionOwned(metadataJson, journalId) {
51416
51924
  } catch {
51417
51925
  throw new Error(`changeset-journal-metadata-malformed: ${journalId}`);
51418
51926
  }
51419
- return isJsonRecord2(metadata) && metadata.projectionOwned === true;
51927
+ return isJsonRecord3(metadata) && metadata.projectionOwned === true;
51420
51928
  }
51421
51929
  function committedChangeSetModelTransition(metadataJson, journalId) {
51422
51930
  let metadata;
@@ -51425,7 +51933,7 @@ function committedChangeSetModelTransition(metadataJson, journalId) {
51425
51933
  } catch {
51426
51934
  throw new Error(`changeset-journal-metadata-malformed: ${journalId}`);
51427
51935
  }
51428
- if (!isJsonRecord2(metadata))
51936
+ if (!isJsonRecord3(metadata))
51429
51937
  throw new Error(`changeset-journal-metadata-malformed: ${journalId}`);
51430
51938
  if (metadata.modelTransition === undefined)
51431
51939
  return;
@@ -51439,7 +51947,7 @@ function readChangeSetJournalMetadata(db, journalId) {
51439
51947
  }
51440
51948
  function withChangeSetJournalArchitectureLedger(metadata, patch) {
51441
51949
  const existing = metadata.architectureLedger;
51442
- const architectureLedger = isJsonRecord2(existing) ? { ...existing, ...patch } : {
51950
+ const architectureLedger = isJsonRecord3(existing) ? { ...existing, ...patch } : {
51443
51951
  schemaVersion: "archcontext.changeset-ledger-recovery/v1",
51444
51952
  ...patch
51445
51953
  };
@@ -51461,12 +51969,12 @@ function changeSetJournalPlannedLedgerEvent(metadata) {
51461
51969
  const architectureLedger = metadata.architectureLedger;
51462
51970
  if (architectureLedger === undefined)
51463
51971
  return;
51464
- if (!isJsonRecord2(architectureLedger))
51972
+ if (!isJsonRecord3(architectureLedger))
51465
51973
  throw new Error("changeset-ledger-recovery-metadata-malformed");
51466
51974
  const plannedEvent = architectureLedger.plannedEvent;
51467
51975
  if (plannedEvent === undefined)
51468
51976
  return;
51469
- if (!isJsonRecord2(plannedEvent))
51977
+ if (!isJsonRecord3(plannedEvent))
51470
51978
  throw new Error("changeset-ledger-recovery-planned-event-malformed");
51471
51979
  const event = plannedEvent;
51472
51980
  try {
@@ -51476,7 +51984,7 @@ function changeSetJournalPlannedLedgerEvent(metadata) {
51476
51984
  }
51477
51985
  return event;
51478
51986
  }
51479
- function isJsonRecord2(value) {
51987
+ function isJsonRecord3(value) {
51480
51988
  return typeof value === "object" && value !== null && !Array.isArray(value);
51481
51989
  }
51482
51990
  function recoverJournalFiles(root, files) {
@@ -59359,11 +59867,13 @@ class ArchctxDaemon {
59359
59867
  return okEnvelope("status", status);
59360
59868
  const repositoryId = repositoryFingerprint(root);
59361
59869
  const session = this.sessions.get(repositoryId);
59870
+ const ignoredModelPaths = ignoredArchitectureModelPaths(root);
59362
59871
  return okEnvelope("status", {
59363
59872
  ...status,
59364
59873
  repositoryId,
59365
59874
  headSha: session?.workspace.headSha ?? readHeadSha(root),
59366
- worktreeDigest: computeWorktreeDigest(root, listGitVisibleFiles(root))
59875
+ worktreeDigest: computeWorktreeDigest(root, listGitVisibleFiles(root)),
59876
+ ...ignoredModelPaths.length === 0 ? {} : { architectureModelIgnoredPaths: ignoredModelPaths }
59367
59877
  });
59368
59878
  } catch (error) {
59369
59879
  throw publicOperationFailure(error);
@@ -60078,26 +60588,19 @@ function exportLikeC4Model(model) {
60078
60588
  const normalized = normalizeNativeModel(model);
60079
60589
  const lines = [
60080
60590
  "// Generated by ArchContext. Native model remains source of truth.",
60081
- "specification {",
60082
- " element capability",
60083
- " element module",
60084
- " element component",
60085
- " element interface",
60086
- " element datastore",
60087
- " element external_system",
60088
- "}",
60591
+ ...likeC4SpecificationLines(normalized.nodes),
60089
60592
  "",
60090
60593
  "model {"
60091
60594
  ];
60092
60595
  for (const node of normalized.nodes) {
60093
60596
  lines.push(` // archctx-node ${JSON.stringify(node)}`);
60094
- lines.push(` ${likec4Id(node.id)} = ${likec4Kind(node.kind)} "${escapeDsl2(node.name)}"`);
60597
+ lines.push(` ${likec4Id(node.id)} = ${likeC4ElementKind(node.kind)} "${escapeDsl2(node.name)}"`);
60095
60598
  }
60096
60599
  for (const relation of normalized.relations) {
60097
60600
  lines.push(` // archctx-relation ${JSON.stringify(relation)}`);
60098
60601
  lines.push(` ${likec4Id(relation.source)} -> ${likec4Id(relation.target)} "${escapeDsl2(relation.kind)}"`);
60099
60602
  }
60100
- lines.push("}", "", "views {", " view index {", " include *", " autoLayout TopBottom", " }", "}");
60603
+ lines.push("}", "", ...likeC4IndexViewLines(architectureLayerGroups(normalized), likec4Id));
60101
60604
  const content = `${lines.join(`
60102
60605
  `)}
60103
60606
  `;
@@ -60127,15 +60630,6 @@ function importLikeC4InitialModel(content) {
60127
60630
  function likec4Id(id) {
60128
60631
  return stableId(id).replace(/-/g, "_").replace(/\./g, "_").replace(/::/g, "__");
60129
60632
  }
60130
- function likec4Kind(kind) {
60131
- if (kind === "external-system")
60132
- return "external_system";
60133
- if (kind === "datastore")
60134
- return "datastore";
60135
- if (["capability", "module", "component", "interface"].includes(kind))
60136
- return kind;
60137
- return "component";
60138
- }
60139
60633
  function escapeDsl2(value) {
60140
60634
  return value.replace(/"/g, "'");
60141
60635
  }
@@ -60144,17 +60638,22 @@ function escapeDsl2(value) {
60144
60638
  init_src();
60145
60639
  function exportStructurizrWorkspace(model, name = "ArchContext Export") {
60146
60640
  const normalized = normalizeNativeModel(model);
60641
+ const { layerByNodeId } = architectureLayerGroups(normalized);
60147
60642
  const workspace = {
60148
60643
  schemaVersion: "archcontext.structurizr-export/v1",
60149
60644
  name,
60150
60645
  model: {
60151
- elements: normalized.nodes.map((node) => ({
60152
- id: node.id,
60153
- name: node.name,
60154
- type: structurizrElementType(node.kind),
60155
- tags: [node.kind],
60156
- archcontextNative: node
60157
- })),
60646
+ elements: normalized.nodes.map((node) => {
60647
+ const layer = layerByNodeId.get(node.id);
60648
+ return {
60649
+ id: node.id,
60650
+ name: node.name,
60651
+ type: architectureStructurizrElementType(node.kind),
60652
+ tags: [node.kind],
60653
+ ...layer === undefined ? {} : { group: layer },
60654
+ archcontextNative: node
60655
+ };
60656
+ }),
60158
60657
  relationships: normalized.relations.map((relation) => ({
60159
60658
  id: relation.id,
60160
60659
  sourceId: relation.source,
@@ -60198,17 +60697,6 @@ function importStructurizrInitialModel(content) {
60198
60697
  warnings: ["Structurizr import is initialization-only; Native ArchContext remains source of truth."]
60199
60698
  };
60200
60699
  }
60201
- function structurizrElementType(kind) {
60202
- if (kind === "capability")
60203
- return "Software System";
60204
- if (kind === "module")
60205
- return "Container";
60206
- if (kind === "datastore")
60207
- return "Database";
60208
- if (kind === "external-system")
60209
- return "External System";
60210
- return "Component";
60211
- }
60212
60700
 
60213
60701
  // packages/surfaces/mcp-local/src/tool-schemas.ts
60214
60702
  var text2 = { type: "string", minLength: 1 };
@@ -60782,17 +61270,49 @@ var architecture_node_schema_default2 = {
60782
61270
  type: "object",
60783
61271
  additionalProperties: false,
60784
61272
  required: ["schemaVersion", "id", "kind", "name", "status", "summary"],
61273
+ allOf: [
61274
+ {
61275
+ if: { properties: { kind: { const: "namespace" } }, required: ["kind"] },
61276
+ then: { required: ["namespace"] },
61277
+ else: { not: { required: ["namespace"] } }
61278
+ },
61279
+ {
61280
+ if: { properties: { kind: { const: "mount" } }, required: ["kind"] },
61281
+ then: { required: ["mount"] },
61282
+ else: { not: { required: ["mount"] } }
61283
+ }
61284
+ ],
60785
61285
  properties: {
60786
61286
  schemaVersion: { const: "archcontext.node/v2" },
60787
61287
  id: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
60788
61288
  kind: {
60789
61289
  type: "string",
60790
- enum: ["capability", "module", "component", "interface", "event", "datastore", "external-system", "deployment"]
61290
+ enum: ["capability", "module", "component", "interface", "event", "datastore", "external-system", "deployment", "process", "namespace", "mount"]
60791
61291
  },
60792
61292
  name: { type: "string" },
60793
61293
  status: { type: "string", enum: ["active", "planned", "deprecated", "removed"] },
60794
61294
  parent: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
61295
+ layer: { type: "string", pattern: "^[a-z][a-z0-9]*(-[a-z0-9]+)*$" },
60795
61296
  summary: { type: "string" },
61297
+ namespace: {
61298
+ type: "object",
61299
+ additionalProperties: false,
61300
+ required: ["type"],
61301
+ properties: {
61302
+ type: { type: "string", enum: ["host", "container", "pod", "kubernetes", "pid", "mount", "net", "user"] }
61303
+ }
61304
+ },
61305
+ mount: {
61306
+ type: "object",
61307
+ additionalProperties: false,
61308
+ required: ["target", "mode", "order"],
61309
+ properties: {
61310
+ source: { type: "string", minLength: 1 },
61311
+ target: { type: "string", pattern: "^/([^/\\u0000]+(/[^/\\u0000]+)*)?$" },
61312
+ mode: { type: "string", enum: ["ro", "rw"] },
61313
+ order: { type: "integer", minimum: 0 }
61314
+ }
61315
+ },
60796
61316
  responsibilities: { type: "array", items: { type: "string" }, minItems: 1 },
60797
61317
  source: {
60798
61318
  type: "object",