archctx 0.11.3 → 0.11.4

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.11.3",
4
+ "productVersion": "0.11.4",
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:642dc69ef47da32c8af19354d4530583fcb970e54b5539cbfbfbae0ee98786f9"
392
+ "catalogDigest": "sha256:eccb96c1058b021be25a7efc44a0298077852d44bd6cb04f94e76d20ace812ce"
393
393
  }
package/bin/archctx.mjs CHANGED
@@ -4125,7 +4125,7 @@ function compareVersionTriples(left, right) {
4125
4125
  }
4126
4126
  return 0;
4127
4127
  }
4128
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.11.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";
4128
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.11.4", 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";
4129
4129
 
4130
4130
  // packages/contracts/src/refactor.ts
4131
4131
  function refactorNamespaceIssues(value, label = "product.id") {
@@ -22360,6 +22360,7 @@ function evaluateDependencyConstraints(input) {
22360
22360
  const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
22361
22361
  const reasonCodes = new Set(certification.reasonCodes);
22362
22362
  const violations = new Map;
22363
+ const layerEdgeCounts = new Map;
22363
22364
  for (const edge of edges) {
22364
22365
  if (edge.to === null)
22365
22366
  continue;
@@ -22367,12 +22368,25 @@ function evaluateDependencyConstraints(input) {
22367
22368
  const toOwners = ownerChains(edge.to);
22368
22369
  for (const constraint of constraints) {
22369
22370
  const key = JSON.stringify([constraint.id, edge.from, edge.to]);
22370
- if (violations.has(key))
22371
- continue;
22372
22371
  const rule = constraint.rule;
22373
22372
  if (rule.type === "layer-direction") {
22374
22373
  const layer = (owner) => nodeById.get(owner.owner)?.layer;
22375
22374
  const classified = (owner) => typeof layer(owner) === "string" && rule.layers.includes(layer(owner));
22375
+ const soleLayer = (owners) => {
22376
+ const layers = new Set(owners.map(layer));
22377
+ const [only] = layers;
22378
+ return layers.size === 1 && only !== undefined && rule.layers.includes(only) ? only : undefined;
22379
+ };
22380
+ const fromSole = soleLayer(fromOwners);
22381
+ const toSole = soleLayer(toOwners);
22382
+ if (fromSole !== undefined && toSole !== undefined) {
22383
+ const rowKey = JSON.stringify([constraint.id, fromSole, toSole]);
22384
+ const row = layerEdgeCounts.get(rowKey);
22385
+ if (row)
22386
+ row.imports += 1;
22387
+ else
22388
+ layerEdgeCounts.set(rowKey, { constraintId: constraint.id, fromLayer: fromSole, toLayer: toSole, imports: 1 });
22389
+ }
22376
22390
  const hasLayer = [...fromOwners, ...toOwners].some((owner) => layer(owner) !== undefined);
22377
22391
  if (hasLayer && (fromOwners.length === 0 || toOwners.length === 0 || [...fromOwners, ...toOwners].some((owner) => !classified(owner))))
22378
22392
  reasonCodes.add("layer-endpoint-unclassified");
@@ -22389,6 +22403,8 @@ function evaluateDependencyConstraints(input) {
22389
22403
  }
22390
22404
  continue;
22391
22405
  }
22406
+ if (violations.has(key))
22407
+ continue;
22392
22408
  const from = fromOwners.find((owner) => meets(owner.chain, constraint.scope.nodes) && !meets(owner.chain, rule.targets));
22393
22409
  const to = toOwners.find((owner) => meets(owner.chain, rule.targets));
22394
22410
  if (!from || !to)
@@ -22410,15 +22426,19 @@ function evaluateDependencyConstraints(input) {
22410
22426
  if (input.workspacePackagesResolved === false)
22411
22427
  reasonCodes.add("workspace-resolution-failed");
22412
22428
  const sortedViolations = [...violations.values()].sort((left, right) => compare3(left.constraintId, right.constraintId) || compare3(left.fromPath, right.fromPath) || compare3(left.toPath, right.toPath));
22429
+ const status = sortedViolations.length > 0 ? "violated" : reasonCodes.size > 0 ? "undetermined" : "pass";
22430
+ const layerEdgesObserved = constraints.some((constraint) => constraint.rule.type === "layer-direction") && (status === "pass" || status === "violated") && certification.coverage === "complete";
22431
+ const layerEdges = [...layerEdgeCounts.values()].sort((left, right) => compare3(left.constraintId, right.constraintId) || compare3(left.fromLayer, right.fromLayer) || compare3(left.toLayer, right.toLayer));
22413
22432
  return {
22414
22433
  schemaVersion: "archcontext.dependency-constraint-evaluation/v1",
22415
- status: sortedViolations.length > 0 ? "violated" : reasonCodes.size > 0 ? "undetermined" : "pass",
22434
+ status,
22416
22435
  coverage: certification.coverage,
22417
22436
  reasonCodes: [...reasonCodes].sort(),
22418
22437
  constraintIds: constraints.map((constraint) => constraint.id),
22419
22438
  importEdgeCount: edges.length,
22420
22439
  unresolvedImports,
22421
- violations: sortedViolations
22440
+ violations: sortedViolations,
22441
+ ...layerEdgesObserved ? { layerEdges } : {}
22422
22442
  };
22423
22443
  }
22424
22444
  function measureCapabilityDependencies(input) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archctx",
3
- "version": "0.11.3",
3
+ "version": "0.11.4",
4
4
  "description": "Local architecture context CLI for agentic coding workflows.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,207 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://archcontext.dev/schemas/runtime/dependency-constraint-evaluation.schema.json",
4
+ "title": "DependencyConstraintEvaluationV1",
5
+ "description": "The data of `archctx constraints check`: a read-only evaluation of the model's dependency constraints against observed import edges. `warnings` is added by the constraints-check command from the declaration reader, so it is not part of the evaluator's own type. Without code facts that attest to the measured tree, the evaluation still answers, but `coverage` is not `complete` and the import observations are dropped rather than reported as facts about some other tree.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "schemaVersion",
10
+ "status",
11
+ "coverage",
12
+ "reasonCodes",
13
+ "constraintIds",
14
+ "importEdgeCount",
15
+ "unresolvedImports",
16
+ "violations",
17
+ "warnings"
18
+ ],
19
+ "properties": {
20
+ "schemaVersion": {
21
+ "const": "archcontext.dependency-constraint-evaluation/v1"
22
+ },
23
+ "status": {
24
+ "enum": [
25
+ "not-applicable",
26
+ "pass",
27
+ "undetermined",
28
+ "violated"
29
+ ]
30
+ },
31
+ "coverage": {
32
+ "enum": [
33
+ "complete",
34
+ "partial",
35
+ "unknown"
36
+ ],
37
+ "description": "`complete` only when an index attested to the evaluated worktree and was not truncated."
38
+ },
39
+ "reasonCodes": {
40
+ "type": "array",
41
+ "uniqueItems": true,
42
+ "description": "Why a result is not a complete observation, sorted.",
43
+ "items": {
44
+ "enum": [
45
+ "code-facts-import-limit",
46
+ "code-facts-stale",
47
+ "code-facts-truncated",
48
+ "code-facts-unavailable",
49
+ "unresolved-import",
50
+ "layer-endpoint-unclassified",
51
+ "workspace-resolution-failed"
52
+ ]
53
+ }
54
+ },
55
+ "constraintIds": {
56
+ "type": "array",
57
+ "description": "The constraints this evaluation covered, in declaration order.",
58
+ "items": {
59
+ "type": "string",
60
+ "minLength": 1
61
+ }
62
+ },
63
+ "importEdgeCount": {
64
+ "type": "integer",
65
+ "minimum": 0,
66
+ "description": "Import observations the evaluation read; zero when the index did not attest to this tree."
67
+ },
68
+ "unresolvedImports": {
69
+ "type": "array",
70
+ "description": "Repository-local specifiers from constrained files that resolved to no file.",
71
+ "items": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "required": [
75
+ "from",
76
+ "specifier"
77
+ ],
78
+ "properties": {
79
+ "from": {
80
+ "type": "string",
81
+ "minLength": 1
82
+ },
83
+ "specifier": {
84
+ "type": "string",
85
+ "minLength": 1
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "violations": {
91
+ "type": "array",
92
+ "description": "One record per (constraint, from path, to path), sorted by those keys.",
93
+ "items": {
94
+ "type": "object",
95
+ "additionalProperties": false,
96
+ "required": [
97
+ "constraintId",
98
+ "fromPath",
99
+ "toPath",
100
+ "fromNode",
101
+ "toNode",
102
+ "severity"
103
+ ],
104
+ "properties": {
105
+ "constraintId": {
106
+ "type": "string",
107
+ "minLength": 1
108
+ },
109
+ "fromPath": {
110
+ "type": "string",
111
+ "minLength": 1
112
+ },
113
+ "toPath": {
114
+ "type": "string",
115
+ "minLength": 1
116
+ },
117
+ "fromNode": {
118
+ "type": "string",
119
+ "minLength": 1
120
+ },
121
+ "toNode": {
122
+ "type": "string",
123
+ "minLength": 1
124
+ },
125
+ "fromLayer": {
126
+ "type": "string",
127
+ "minLength": 1
128
+ },
129
+ "toLayer": {
130
+ "type": "string",
131
+ "minLength": 1
132
+ },
133
+ "severity": {
134
+ "enum": [
135
+ "error",
136
+ "warning"
137
+ ]
138
+ }
139
+ }
140
+ }
141
+ },
142
+ "layerEdges": {
143
+ "type": "array",
144
+ "description": "Resolved imports between two layers of one `layer-direction` constraint. An import counts only when every owner of each endpoint carries one shared layer listed in the rule. Present only with a `layer-direction` constraint, `status` `pass` or `violated`, and `coverage` `complete`; otherwise absent. When present it may be an empty list, and it is sorted by `(constraintId, fromLayer, toLayer)`.",
145
+ "items": {
146
+ "type": "object",
147
+ "additionalProperties": false,
148
+ "required": [
149
+ "constraintId",
150
+ "fromLayer",
151
+ "toLayer",
152
+ "imports"
153
+ ],
154
+ "properties": {
155
+ "constraintId": {
156
+ "type": "string",
157
+ "minLength": 1
158
+ },
159
+ "fromLayer": {
160
+ "type": "string",
161
+ "minLength": 1
162
+ },
163
+ "toLayer": {
164
+ "type": "string",
165
+ "minLength": 1
166
+ },
167
+ "imports": {
168
+ "type": "integer",
169
+ "minimum": 1,
170
+ "description": "Import edges, so two specifiers from one file to the same target count twice."
171
+ }
172
+ }
173
+ }
174
+ },
175
+ "warnings": {
176
+ "type": "array",
177
+ "description": "Declaration-reader warnings, such as an unsupported rule. Every unevaluated constraint type produces one, because a validated constraint is not proof that its rule was evaluated.",
178
+ "items": {
179
+ "type": "string",
180
+ "minLength": 1
181
+ }
182
+ }
183
+ },
184
+ "allOf": [
185
+ {
186
+ "description": "The layer edges are an observation, so they exist only for a settled verdict over complete coverage.",
187
+ "if": {
188
+ "required": [
189
+ "layerEdges"
190
+ ]
191
+ },
192
+ "then": {
193
+ "properties": {
194
+ "status": {
195
+ "enum": [
196
+ "pass",
197
+ "violated"
198
+ ]
199
+ },
200
+ "coverage": {
201
+ "const": "complete"
202
+ }
203
+ }
204
+ }
205
+ }
206
+ ]
207
+ }