archctx 0.9.2 → 0.9.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "archcontext.practice-catalog-manifest/v1",
3
3
  "catalogVersion": "2026.06.0",
4
- "productVersion": "0.9.2",
4
+ "productVersion": "0.9.3",
5
5
  "generatedAt": "1970-01-01T00:00:00.000Z",
6
6
  "entries": [
7
7
  {
@@ -389,5 +389,5 @@
389
389
  "structurizr.dsl",
390
390
  "twelve-factor"
391
391
  ],
392
- "catalogDigest": "sha256:2902680bd3732b42edd01c1dc899acff71768f03782c1747a4a53017dbb3730d"
392
+ "catalogDigest": "sha256:4fdfbd8668e8d00e0d6f46f094a86e8e0a43075927b20b7dd5e554eab899fdcf"
393
393
  }
package/bin/archctx.mjs CHANGED
@@ -3462,6 +3462,9 @@ var PROJECTION_PUBLISH_RUN_SCHEMA_VERSION = "archcontext.projection-publish-run/
3462
3462
  var init_projection_writer = () => {};
3463
3463
 
3464
3464
  // packages/contracts/src/projection-manifest.ts
3465
+ function isRuntimeTopologyTargetType(type) {
3466
+ return typeof type === "string" && RUNTIME_TOPOLOGY_TARGET_TYPES.includes(type);
3467
+ }
3465
3468
  function architectureDocsProjectionManifestIssues(value) {
3466
3469
  const issues = [];
3467
3470
  const manifest = record3(value);
@@ -3497,6 +3500,7 @@ function architectureDocsProjectionManifestIssues(value) {
3497
3500
  }
3498
3501
  const paths = new Set;
3499
3502
  const stampOwners = new Map;
3503
+ let topologyStamps;
3500
3504
  for (const [targetId, row] of Object.entries(targets)) {
3501
3505
  const prefix = `manifest.targets[${JSON.stringify(targetId)}]`;
3502
3506
  if (!isNonEmptyString(targetId))
@@ -3508,7 +3512,23 @@ function architectureDocsProjectionManifestIssues(value) {
3508
3512
  issues.push(`${prefix}.path is recorded by another target`);
3509
3513
  paths.add(path);
3510
3514
  }
3511
- for (const nodeId of Object.keys(record3(record3(row)?.sourceStamps) ?? {})) {
3515
+ const stamps = record3(record3(row)?.sourceStamps) ?? {};
3516
+ if (isRuntimeTopologyTargetType(record3(row)?.type)) {
3517
+ const serialized = JSON.stringify(Object.keys(stamps).sort(compareStrings).map((nodeId) => [nodeId, stamps[nodeId]]));
3518
+ if (topologyStamps === undefined)
3519
+ topologyStamps = { targetId, stamps: serialized };
3520
+ else if (topologyStamps.stamps !== serialized)
3521
+ issues.push(`${prefix}.sourceStamps must equal the sourceStamps of ${topologyStamps.targetId}`);
3522
+ for (const nodeId of Object.keys(stamps)) {
3523
+ const owner = stampOwners.get(nodeId);
3524
+ if (owner !== undefined && !isRuntimeTopologyTargetType(record3(targets[owner])?.type))
3525
+ issues.push(`${prefix}.sourceStamps.${nodeId} is already stamped on ${owner}`);
3526
+ else if (owner === undefined)
3527
+ stampOwners.set(nodeId, targetId);
3528
+ }
3529
+ continue;
3530
+ }
3531
+ for (const nodeId of Object.keys(stamps)) {
3512
3532
  const owner = stampOwners.get(nodeId);
3513
3533
  if (owner !== undefined)
3514
3534
  issues.push(`${prefix}.sourceStamps.${nodeId} is already stamped on ${owner}`);
@@ -3763,9 +3783,11 @@ function manifestTargetIssues(value, prefix) {
3763
3783
  issues.push(`${prefix}.format is unsupported`);
3764
3784
  if (!isDigest(target.outputDigest))
3765
3785
  issues.push(`${prefix}.outputDigest must be a SHA-256 digest`);
3766
- if ((target.sourceStamps !== undefined || target.scale !== undefined) && target.type !== "entity-summary") {
3767
- issues.push(`${prefix}.sourceStamps and scale are only allowed on entity-summary targets`);
3786
+ if (target.sourceStamps !== undefined && target.type !== "entity-summary" && !isRuntimeTopologyTargetType(target.type)) {
3787
+ issues.push(`${prefix}.sourceStamps are only allowed on entity-summary and runtime topology targets`);
3768
3788
  }
3789
+ if (target.scale !== undefined && target.type !== "entity-summary")
3790
+ issues.push(`${prefix}.scale is only allowed on entity-summary targets`);
3769
3791
  if (target.sourceStamps !== undefined) {
3770
3792
  const stamps = record3(target.sourceStamps);
3771
3793
  if (!stamps || Object.keys(stamps).length === 0)
@@ -3854,7 +3876,7 @@ function architectureDocsProjectionCompatibilityIssues(value, engine = {
3854
3876
  }
3855
3877
  return issues;
3856
3878
  }
3857
- var ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION = "archcontext.architecture-docs-projection-manifest/v3", SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS, PROJECTION_MANIFEST_UPGRADE_REQUIRED = "projection-manifest-upgrade-required", PROJECTION_MANIFEST_INVALID = "projection-manifest-invalid", PROJECTION_FOOTPRINT_UNCOVERED = "projection-footprint-uncovered", ARCHITECTURE_DOCS_PROJECTION_PROVENANCE_SCHEMA_VERSION = "archcontext.architecture-docs-projection-provenance/v3", ARCHITECTURE_DOCS_LAYOUT_VERSION_V1 = "archcontext.docs-layout/v1", ARCHITECTURE_SEMANTIC_STATE_SCHEMA_VERSION = "archcontext.architecture-semantic-state/v1", ARCHITECTURE_DOCS_PROJECTION_PROFILES, SHA256, TARGET_TYPES, SCOPE_KINDS, OWNERSHIPS, FORMATS;
3879
+ var ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSION = "archcontext.architecture-docs-projection-manifest/v3", SUPERSEDED_ARCHITECTURE_DOCS_PROJECTION_MANIFEST_SCHEMA_VERSIONS, PROJECTION_MANIFEST_UPGRADE_REQUIRED = "projection-manifest-upgrade-required", PROJECTION_MANIFEST_INVALID = "projection-manifest-invalid", PROJECTION_FOOTPRINT_UNCOVERED = "projection-footprint-uncovered", ARCHITECTURE_DOCS_PROJECTION_PROVENANCE_SCHEMA_VERSION = "archcontext.architecture-docs-projection-provenance/v3", ARCHITECTURE_DOCS_LAYOUT_VERSION_V1 = "archcontext.docs-layout/v1", ARCHITECTURE_SEMANTIC_STATE_SCHEMA_VERSION = "archcontext.architecture-semantic-state/v1", ARCHITECTURE_DOCS_PROJECTION_PROFILES, SHA256, TARGET_TYPES, RUNTIME_TOPOLOGY_TARGET_TYPES, SCOPE_KINDS, OWNERSHIPS, FORMATS;
3858
3880
  var init_projection_manifest = __esm(() => {
3859
3881
  init_projection();
3860
3882
  init_schema();
@@ -3877,6 +3899,7 @@ var init_projection_manifest = __esm(() => {
3877
3899
  "runtime-topology-summary": true,
3878
3900
  "agent-context": true
3879
3901
  };
3902
+ RUNTIME_TOPOLOGY_TARGET_TYPES = ["diagram-runtime-topology", "runtime-topology-summary"];
3880
3903
  SCOPE_KINDS = { repository: true, entity: true, relation: true, decision: true, diagram: true, changelog: true };
3881
3904
  OWNERSHIPS = { generated: true, mixed: true };
3882
3905
  FORMATS = { markdown: true, mermaid: true, "structurizr-json": true, likec4: true };
@@ -3967,7 +3990,7 @@ function compareVersionTriples(left, right) {
3967
3990
  }
3968
3991
  return 0;
3969
3992
  }
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";
3993
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.3", 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";
3971
3994
 
3972
3995
  // packages/contracts/src/refactor.ts
3973
3996
  function refactorNamespaceIssues(value, label = "product.id") {
@@ -17788,9 +17811,10 @@ var FIXED_TARGETS = [
17788
17811
  ];
17789
17812
  var RUNTIME_TOPOLOGY_DIAGRAM_PATH = "docs/architecture/diagrams/runtime-topology.mmd";
17790
17813
  var RUNTIME_TOPOLOGY_SUMMARY_PATH = "docs/architecture/runtime-topology.md";
17814
+ var RUNTIME_TOPOLOGY_SCOPE_ID = "runtime-topology";
17791
17815
  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")
17816
+ layoutTarget("projection_target.diagram.runtime-topology", "diagram-runtime-topology", { kind: "diagram", id: RUNTIME_TOPOLOGY_SCOPE_ID }, RUNTIME_TOPOLOGY_DIAGRAM_PATH, "generated", "mermaid"),
17817
+ layoutTarget("projection_target.runtime-topology.summary", "runtime-topology-summary", { kind: "diagram", id: RUNTIME_TOPOLOGY_SCOPE_ID }, RUNTIME_TOPOLOGY_SUMMARY_PATH, "generated", "markdown")
17794
17818
  ];
17795
17819
  function hasRuntimeTopologyNodes(nodes) {
17796
17820
  return nodes.some((node) => isRuntimeTopologyNodeKind(node.kind));
@@ -19471,6 +19495,7 @@ function renderArchitectureDocumentationProjection(input) {
19471
19495
  const rendered = targetDrafts.map((draft) => {
19472
19496
  const existing = existingByPath.get(draft.path);
19473
19497
  const nodeId = draft.type === "entity-summary" ? draft.scope.id : undefined;
19498
+ const topology = isRuntimeTopologyTargetType(draft.type);
19474
19499
  const generatedBody = renderTargetGeneratedBody(draft, model, {
19475
19500
  scaleSignalsByNodeId,
19476
19501
  scaleByNodeId,
@@ -19494,6 +19519,10 @@ function renderArchitectureDocumentationProjection(input) {
19494
19519
  if (scale === undefined)
19495
19520
  throw new Error(`architecture-docs-projection-scale-signal-missing: ${nodeId}`);
19496
19521
  }
19522
+ } else if (topology) {
19523
+ const owned = Object.keys(sourceStamps).filter((stamped) => footprintOwners.get(stamped) === RUNTIME_TOPOLOGY_SCOPE_ID);
19524
+ if (owned.length > 0)
19525
+ targetStamps = Object.fromEntries(owned.map((stamped) => [stamped, sourceStamps[stamped]]));
19497
19526
  }
19498
19527
  const generatedBodyDigest = digestJson({ targetId: draft.targetId, body: generatedBody });
19499
19528
  const target = projectionTarget({
@@ -19859,6 +19888,10 @@ function architectureProjectionFootprintOwners(model, profile = "default") {
19859
19888
  for (const node of [...model.nodes].sort((left, right) => compareStrings3(left.id, right.id))) {
19860
19889
  if ((nativeNodeSource(node)?.include ?? []).length === 0)
19861
19890
  continue;
19891
+ if (isRuntimeTopologyNodeKind(node.kind)) {
19892
+ owners.set(node.id, RUNTIME_TOPOLOGY_SCOPE_ID);
19893
+ continue;
19894
+ }
19862
19895
  const seen = new Set;
19863
19896
  let current = node;
19864
19897
  while (current && !targetNodeIds.has(current.id) && !seen.has(current.id)) {
@@ -20522,7 +20555,7 @@ function renderRuntimeTopologySummary(model, summaryPath) {
20522
20555
  const lines = [
20523
20556
  "# Runtime Topology",
20524
20557
  "",
20525
- "The architecture model declares this topology. No code facts verify it.",
20558
+ "The architecture model declares this topology. No code facts verify it: archctx never reads the configuration it describes. A change to a file a node lists in `source.include` marks this page and its diagram stale.",
20526
20559
  "",
20527
20560
  `- Diagram: [Mermaid](${posix.relative(posix.dirname(summaryPath), RUNTIME_TOPOLOGY_DIAGRAM_PATH)})`,
20528
20561
  "",
@@ -20573,6 +20606,11 @@ function renderRuntimeTopologySummary(model, summaryPath) {
20573
20606
  for (const relation of otherDeployments)
20574
20607
  lines.push(`- \`${relation.source}\` -> \`${relation.target}\` — ${relation.intent}`);
20575
20608
  }
20609
+ lines.push("", "## Source Anchors", "", "| Node | Source |", "| --- | --- |");
20610
+ for (const node of [...view.nodesById.values()].filter((entry) => isRuntimeTopologyNodeKind(entry.kind)).sort((left, right) => compareStrings3(left.id, right.id))) {
20611
+ const include = nativeNodeSource(node)?.include ?? [];
20612
+ lines.push(`| ${named(node.id)} | ${include.length === 0 ? "model-authored, no source anchor" : include.map((pattern) => `\`${cell(pattern)}\``).join(", ")} |`);
20613
+ }
20576
20614
  return `${lines.join(`
20577
20615
  `)}
20578
20616
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archctx",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Local architecture context CLI for agentic coding workflows.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -332,7 +332,7 @@
332
332
  },
333
333
  "sourceStamps": {
334
334
  "type": "object",
335
- "description": "entity-summary targets only: the content digest of every declared source footprint this target documents, by node id. Each footprint belongs to its node's nearest ancestor-or-self with an entity-summary target, so each node is stamped on exactly one row.",
335
+ "description": "entity-summary and runtime topology targets only: the content digest of every declared source footprint this target documents, by node id. A footprint belongs to its node's nearest ancestor-or-self with an entity-summary target, so it is stamped on exactly one entity-summary row; a runtime topology node's footprint is stamped on every runtime topology row instead, and those rows carry identical stamps (ADR-0049).",
336
336
  "additionalProperties": {
337
337
  "$ref": "#/$defs/digest"
338
338
  }
@@ -360,24 +360,37 @@
360
360
  "properties": {
361
361
  "type": {
362
362
  "not": {
363
- "const": "entity-summary"
363
+ "enum": [
364
+ "entity-summary",
365
+ "diagram-runtime-topology",
366
+ "runtime-topology-summary"
367
+ ]
364
368
  }
365
369
  }
366
370
  }
367
371
  },
368
372
  "then": {
369
373
  "not": {
370
- "anyOf": [
371
- {
372
- "required": [
373
- "sourceStamps"
374
- ]
375
- },
376
- {
377
- "required": [
378
- "scale"
379
- ]
374
+ "required": [
375
+ "sourceStamps"
376
+ ]
377
+ }
378
+ }
379
+ },
380
+ {
381
+ "if": {
382
+ "properties": {
383
+ "type": {
384
+ "not": {
385
+ "const": "entity-summary"
380
386
  }
387
+ }
388
+ }
389
+ },
390
+ "then": {
391
+ "not": {
392
+ "required": [
393
+ "scale"
381
394
  ]
382
395
  }
383
396
  }