@clossys/architect 0.1.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +171 -0
  4. package/dist/assessment.d.ts +9 -0
  5. package/dist/assessment.d.ts.map +1 -0
  6. package/dist/assessment.js +98 -0
  7. package/dist/assessment.js.map +1 -0
  8. package/dist/cli.d.ts +6 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +96 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/index.d.ts +6 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +5 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/ontology/compatibility.d.ts +7 -0
  17. package/dist/ontology/compatibility.d.ts.map +1 -0
  18. package/dist/ontology/compatibility.js +135 -0
  19. package/dist/ontology/compatibility.js.map +1 -0
  20. package/dist/ontology/define.d.ts +8 -0
  21. package/dist/ontology/define.d.ts.map +1 -0
  22. package/dist/ontology/define.js +30 -0
  23. package/dist/ontology/define.js.map +1 -0
  24. package/dist/ontology/index.d.ts +9 -0
  25. package/dist/ontology/index.d.ts.map +1 -0
  26. package/dist/ontology/index.js +8 -0
  27. package/dist/ontology/index.js.map +1 -0
  28. package/dist/ontology/normalize.d.ts +6 -0
  29. package/dist/ontology/normalize.d.ts.map +1 -0
  30. package/dist/ontology/normalize.js +60 -0
  31. package/dist/ontology/normalize.js.map +1 -0
  32. package/dist/ontology/snapshot.d.ts +14 -0
  33. package/dist/ontology/snapshot.d.ts.map +1 -0
  34. package/dist/ontology/snapshot.js +299 -0
  35. package/dist/ontology/snapshot.js.map +1 -0
  36. package/dist/ontology/types.d.ts +123 -0
  37. package/dist/ontology/types.d.ts.map +1 -0
  38. package/dist/ontology/types.js +16 -0
  39. package/dist/ontology/types.js.map +1 -0
  40. package/dist/ontology/validate.d.ts +8 -0
  41. package/dist/ontology/validate.d.ts.map +1 -0
  42. package/dist/ontology/validate.js +190 -0
  43. package/dist/ontology/validate.js.map +1 -0
  44. package/dist/topology.d.ts +11 -0
  45. package/dist/topology.d.ts.map +1 -0
  46. package/dist/topology.js +237 -0
  47. package/dist/topology.js.map +1 -0
  48. package/dist/types.d.ts +110 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +11 -0
  51. package/dist/types.js.map +1 -0
  52. package/package.json +69 -0
  53. package/src/assessment.ts +98 -0
  54. package/src/cli.ts +74 -0
  55. package/src/index.ts +39 -0
  56. package/src/ontology/compatibility.ts +132 -0
  57. package/src/ontology/define.ts +35 -0
  58. package/src/ontology/index.ts +29 -0
  59. package/src/ontology/normalize.ts +63 -0
  60. package/src/ontology/snapshot.ts +324 -0
  61. package/src/ontology/types.ts +156 -0
  62. package/src/ontology/validate.ts +208 -0
  63. package/src/topology.ts +211 -0
  64. package/src/types.ts +142 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.2] - 2026-08-30
9
+
10
+ ### Changed
11
+
12
+ - Updated the package's public repository, issue-tracker, and homepage metadata to the canonical Foundry repository. This change is not a publication or qualification claim.
13
+
14
+ ## [0.1.1] - 2026-08-24
15
+
16
+ ### Fixed
17
+
18
+ - Corrected the install guidance now that Architect is a public package.
19
+
20
+ ## [0.1.0] - 2026-08-23
21
+
22
+ ### Added
23
+
24
+ - Initial architect role contract for provider-neutral scopes, systems,
25
+ responsibilities, authorities, systems of record, and interfaces.
26
+ - Deterministic topology validation, normalization, serialization, and
27
+ compatibility comparison.
28
+ - Evidence-based architecture exception assessment with explicit indeterminate
29
+ results when no material changes have been observed.
30
+ - `architect-check` topology and exception-assessment commands.
31
+ - Ontology model and snapshot API under `@vespeneventures/architect/ontology`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Calvin Hung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # @clossys/architect
2
+
3
+ The architect role for provider-neutral operating architecture. It answers:
4
+
5
+ > Do the business ontology, operating boundaries, authoritative systems, and
6
+ > interfaces match how material work actually crosses the business?
7
+
8
+ The package defines business and portfolio scopes, systems, responsibilities,
9
+ authoritative ownership, systems of record, and directional interfaces. It
10
+ then assesses independent change observations against that declared topology.
11
+ It performs no network I/O and has no runtime dependencies. Install it with:
12
+
13
+ ```bash
14
+ npm install @clossys/architect
15
+ ```
16
+
17
+ ## Job charter
18
+
19
+ Architect operates in **optimize** mode. Its durable objective is to reduce
20
+ architecture exception rate while respecting consumer-owned security,
21
+ privacy, authority, and lifecycle guardrails:
22
+
23
+ ```text
24
+ architecture exception rate
25
+ = observed material changes with at least one undeclared boundary crossing
26
+ / all observed material changes
27
+ ```
28
+
29
+ No observed material changes means the rate is **indeterminate**, never zero.
30
+ The role senses actual changes, judges them against declared boundaries,
31
+ proposes architecture changes, verifies subsequent evidence, and learns or
32
+ escalates. It does not transfer repositories, mutate GitHub, create systems,
33
+ or grant itself authority. A consumer approves changes; an authorized builder
34
+ may materialize them.
35
+
36
+ ## Usage
37
+
38
+ ```ts
39
+ import {
40
+ assessArchitectureExceptions,
41
+ defineOperatingTopology,
42
+ serializeOperatingTopology,
43
+ validateOperatingTopology,
44
+ } from "@clossys/architect";
45
+
46
+ const topology = defineOperatingTopology({
47
+ id: "example",
48
+ schemaVersion: "0.1.0",
49
+ scope: { id: "example-business", kind: "business" },
50
+ systems: [
51
+ { id: "workspace", kind: "workspace", responsibilities: ["control-plane"], visibility: "private" },
52
+ { id: "product", kind: "repository", responsibilities: ["product"], visibility: "private" },
53
+ ],
54
+ authorities: [
55
+ { responsibility: "control-plane", owner: "business-owner", systemOfRecord: "workspace" },
56
+ { responsibility: "product", owner: "product-owner", systemOfRecord: "product" },
57
+ ],
58
+ interfaces: [
59
+ { id: "workspace-to-product", from: "workspace", to: "product", responsibilities: ["product"] },
60
+ ],
61
+ });
62
+
63
+ if (validateOperatingTopology(topology).length > 0) throw new Error("Invalid topology");
64
+ const artifact = serializeOperatingTopology(topology);
65
+
66
+ const assessment = assessArchitectureExceptions(
67
+ topology,
68
+ [{
69
+ id: "change-1",
70
+ observedAt: "2026-08-23T12:00:00Z",
71
+ material: true,
72
+ crossings: [{ from: "workspace", to: "product", responsibility: "product", interface: "workspace-to-product" }],
73
+ }],
74
+ { maximumExceptionRate: 0.05 },
75
+ );
76
+ ```
77
+
78
+ Unknown systems, responsibility mismatches, and an interface name that does
79
+ not match the declared direction count as undeclared crossings. Observations are consumer-supplied
80
+ evidence; the assessment never treats a success flag as proof.
81
+
82
+ A valid topology has at least one system and authority, represents the
83
+ `control-plane` responsibility, binds each authority to a system of record
84
+ that implements its responsibility, and declares interfaces only for
85
+ responsibilities implemented somewhere in the topology.
86
+
87
+ ## CLI
88
+
89
+ ```bash
90
+ architect-check topology topology.json
91
+ architect-check exceptions topology.json observations.json --maximum-exception-rate 0.05
92
+ ```
93
+
94
+ Both commands emit JSON. Exit codes are `0` satisfied, `1` violated, and `2`
95
+ indeterminate or unable to run. Invalid topology is a violation for the
96
+ `topology` conformance command. Invalid inputs make the exception rate
97
+ indeterminate because no valid measurement can be computed.
98
+
99
+ ## API
100
+
101
+ | Export | Kind | Purpose |
102
+ | --- | --- | --- |
103
+ | `defineOperatingTopology(definition)` | function | Returns a detached, fully populated topology without claiming it is valid. |
104
+ | `validateOperatingTopology(value)` | function | Reports structural, identity, responsibility, authority, system-of-record, and interface findings. |
105
+ | `normalizeOperatingTopology(definition)` | function | Applies deterministic property and collection ordering. |
106
+ | `serializeOperatingTopology(definition)` | function | Produces canonical pretty JSON ending in one newline. |
107
+ | `compareOperatingTopologies(previous, next)` | function | Classifies additive and breaking topology contract changes. |
108
+ | `validateArchitectureChangeObservations(value)` | function | Validates independent material-change evidence without certifying it. |
109
+ | `assessArchitectureExceptions(topology, observations, options)` | function | Computes architecture exception rate and satisfied, violated, or indeterminate state. |
110
+ | `OPERATING_SCOPE_KINDS` | constant | Supported `portfolio` and `business` scope kinds. |
111
+ | `OPERATING_SYSTEM_KINDS` | constant | Provider-neutral system kinds. |
112
+ | `OPERATING_RESPONSIBILITIES` | constant | Stable operating-responsibility vocabulary. |
113
+ | `OperatingTopologyDefinition` / `OperatingTopology` | types | Authoring and detached topology shapes. |
114
+ | `OperatingScopeDefinition` / `OperatingScopeKind` | types | Consumer operating boundary and its kind. |
115
+ | `OperatingSystemDefinition` / `OperatingSystemKind` | types | One addressable system and its kind. |
116
+ | `OperatingResponsibility` | type | Stable responsibility assigned to systems and authorities. |
117
+ | `AuthorityDefinition` | type | Consumer-owned responsibility, owner, and system-of-record binding. |
118
+ | `OperatingInterfaceDefinition` | type | One allowed directional system crossing. |
119
+ | `ArchitectureFinding` / `ArchitectureFindingSeverity` | types | Validation issue and severity. |
120
+ | `OperatingTopologyChange` / `OperatingTopologyCompatibilityReport` | types | Classified topology change and compatibility report. |
121
+ | `BoundaryCrossingObservation` / `ArchitectureChangeObservation` | types | One observed crossing and material change. |
122
+ | `ArchitectureAssessmentOptions` / `ArchitectureAssessmentState` | types | Assessment setpoint and three-state result vocabulary. |
123
+ | `AssessedBoundaryCrossing` / `AssessedArchitectureChange` | types | Evidence annotated with declaration and exception status. |
124
+ | `ArchitectureExceptionAssessment` | type | Metric counts, rate, state, evidence details, and findings. |
125
+
126
+ ## Ontology
127
+
128
+ `@clossys/architect/ontology` contains the ontology mechanism used by
129
+ the role. Its public model and snapshot API is compatible with the former
130
+ standalone domain package.
131
+
132
+ | Export | Kind | Purpose |
133
+ | --- | --- | --- |
134
+ | `defineDomainModel(definition)` | function | Returns a detached domain model for authoring. |
135
+ | `validateDomainModel(value)` | function | Validates identifiers, fields, references, relations, and cardinality. |
136
+ | `normalizeDomainModel(model)` | function | Canonically orders a domain model. |
137
+ | `serializeDomainModel(model)` | function | Serializes a canonical domain model. |
138
+ | `compareDomainModels(previous, next)` | function | Classifies additive and breaking model changes. |
139
+ | `defineDomainSnapshot(definition)` | function | Returns a detached collection of records and relations. |
140
+ | `validateDomainSnapshot(model, value)` | function | Validates instance values, types, endpoints, and cardinality. |
141
+ | `normalizeDomainSnapshot(snapshot)` | function | Canonically orders a snapshot and nested values. |
142
+ | `serializeDomainSnapshot(snapshot)` | function | Serializes a canonical snapshot. |
143
+ | `PRIMITIVE_VALUE_TYPES` | constant | Supported primitive value types. |
144
+ | `RELATION_CARDINALITIES` | constant | Supported directed relation cardinalities. |
145
+ | `DomainModelDefinition` / `DomainModel` | types | Model authoring and detached shapes. |
146
+ | `ValueTypeDefinition` / `VocabularyDefinition` | types | Scalar and closed vocabulary declarations. |
147
+ | `DomainTypeDefinition` / `RelationDefinition` | types | Domain types and directed attributed relations. |
148
+ | `FieldDefinition` / `FieldValueType` | types | Stable field and value reference. |
149
+ | `PrimitiveValueType` / `RelationCardinality` | types | Primitive and relation-cardinality vocabularies. |
150
+ | `DomainModelFinding` | type | Domain model validation issue. |
151
+ | `DomainModelChange` / `DomainModelCompatibilityReport` | types | Classified model change and report. |
152
+ | `DomainRecordDefinition` / `DomainRecord` | types | Snapshot record authoring and detached shapes. |
153
+ | `DomainRelationDefinition` / `DomainRelation` | types | Snapshot relation authoring and detached shapes. |
154
+ | `DomainSnapshotDefinition` / `DomainSnapshot` | types | Snapshot authoring and detached shapes. |
155
+ | `DomainSnapshotFinding` | type | Snapshot validation issue. |
156
+
157
+ ## Topology compatibility
158
+
159
+ Removing a system, authority, or interface is breaking. Changing a system's
160
+ kind, responsibilities, provider binding, locator, visibility, an authority's
161
+ owner or system of record, or an interface's direction or responsibilities is
162
+ also breaking. Adding a system, authority, or interface is additive. Labels,
163
+ descriptions, and `schemaVersion` do not determine compatibility.
164
+
165
+ ## Requirements
166
+
167
+ Node 20+. ESM only. No runtime dependencies and no I/O in the library API.
168
+
169
+ ## Licence
170
+
171
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { ArchitectureAssessmentOptions, ArchitectureExceptionAssessment, ArchitectureFinding } from "./types.js";
2
+ /** Validates observations as evidence without using them to certify themselves. */
3
+ export declare function validateArchitectureChangeObservations(value: unknown): ArchitectureFinding[];
4
+ /**
5
+ * Measures the package's primary metric from independent observations.
6
+ * No material observations is explicitly indeterminate, never a zero rate.
7
+ */
8
+ export declare function assessArchitectureExceptions(topology: unknown, observations: unknown, options: ArchitectureAssessmentOptions): ArchitectureExceptionAssessment;
9
+ //# sourceMappingURL=assessment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assessment.d.ts","sourceRoot":"","sources":["../src/assessment.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,6BAA6B,EAE7B,+BAA+B,EAC/B,mBAAmB,EAKpB,MAAM,YAAY,CAAC;AAYpB,mFAAmF;AACnF,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,EAAE,CAuB5F;AAUD;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,OAAO,EACjB,YAAY,EAAE,OAAO,EACrB,OAAO,EAAE,6BAA6B,GACrC,+BAA+B,CAgCjC"}
@@ -0,0 +1,98 @@
1
+ import { validateOperatingTopology } from "./topology.js";
2
+ import { OPERATING_RESPONSIBILITIES } from "./types.js";
3
+ function isRecord(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ }
6
+ function finding(rule, message, path) {
7
+ return { rule, severity: "error", message, path };
8
+ }
9
+ /** Validates observations as evidence without using them to certify themselves. */
10
+ export function validateArchitectureChangeObservations(value) {
11
+ if (!Array.isArray(value))
12
+ return [finding("observations-shape", "Architecture observations must be an array.", "$")];
13
+ const findings = [];
14
+ const ids = new Set();
15
+ for (const [index, candidate] of value.entries()) {
16
+ const path = `[${index}]`;
17
+ if (!isRecord(candidate)) {
18
+ findings.push(finding("observation-shape", "An observation must be an object.", path));
19
+ continue;
20
+ }
21
+ if (typeof candidate.id !== "string" || candidate.id.length === 0)
22
+ findings.push(finding("observation-id", "id must be a non-empty string.", `${path}.id`));
23
+ else if (ids.has(candidate.id))
24
+ findings.push(finding("duplicate-observation-id", `Duplicate observation id "${candidate.id}".`, `${path}.id`));
25
+ else
26
+ ids.add(candidate.id);
27
+ if (typeof candidate.observedAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(candidate.observedAt))
28
+ findings.push(finding("observed-at", "observedAt must be an RFC 3339 timestamp with an offset.", `${path}.observedAt`));
29
+ if (typeof candidate.material !== "boolean")
30
+ findings.push(finding("material-shape", "material must be a boolean.", `${path}.material`));
31
+ if (!Array.isArray(candidate.crossings)) {
32
+ findings.push(finding("crossings-shape", "crossings must be an array.", `${path}.crossings`));
33
+ continue;
34
+ }
35
+ for (const [crossingIndex, crossing] of candidate.crossings.entries()) {
36
+ const crossingPath = `${path}.crossings[${crossingIndex}]`;
37
+ if (!isRecord(crossing)) {
38
+ findings.push(finding("crossing-shape", "A boundary crossing must be an object.", crossingPath));
39
+ continue;
40
+ }
41
+ for (const endpoint of ["from", "to"])
42
+ if (typeof crossing[endpoint] !== "string" || crossing[endpoint].length === 0)
43
+ findings.push(finding("crossing-endpoint", `${endpoint} must be a non-empty system id.`, `${crossingPath}.${endpoint}`));
44
+ if (typeof crossing.responsibility !== "string" || !OPERATING_RESPONSIBILITIES.includes(crossing.responsibility))
45
+ findings.push(finding("crossing-responsibility", "responsibility must be a supported value.", `${crossingPath}.responsibility`));
46
+ if (crossing.from !== undefined && crossing.from === crossing.to)
47
+ findings.push(finding("crossing-boundary", "A boundary crossing must name two different systems.", crossingPath));
48
+ if (crossing.interface !== undefined && (typeof crossing.interface !== "string" || crossing.interface.length === 0))
49
+ findings.push(finding("crossing-interface", "interface must be a non-empty string when provided.", `${crossingPath}.interface`));
50
+ }
51
+ }
52
+ return findings;
53
+ }
54
+ function isDeclared(crossing, interfaces) {
55
+ return interfaces.some((entry) => entry.from === crossing.from && entry.to === crossing.to && entry.responsibilities.includes(crossing.responsibility) && (crossing.interface === undefined || entry.id === crossing.interface));
56
+ }
57
+ function indeterminate(maximumExceptionRate, observedChanges, findings) {
58
+ return { state: "indeterminate", exceptionRate: null, maximumExceptionRate, observedChanges, observedMaterialChanges: 0, materialChangesWithExceptions: 0, changes: [], findings };
59
+ }
60
+ /**
61
+ * Measures the package's primary metric from independent observations.
62
+ * No material observations is explicitly indeterminate, never a zero rate.
63
+ */
64
+ export function assessArchitectureExceptions(topology, observations, options) {
65
+ const topologyFindings = validateOperatingTopology(topology);
66
+ const observationFindings = validateArchitectureChangeObservations(observations);
67
+ const optionFindings = [];
68
+ if (!Number.isFinite(options.maximumExceptionRate) || options.maximumExceptionRate < 0 || options.maximumExceptionRate > 1)
69
+ optionFindings.push(finding("maximum-exception-rate", "maximumExceptionRate must be a finite number in [0, 1].", "options.maximumExceptionRate"));
70
+ const observedChanges = Array.isArray(observations) ? observations.length : 0;
71
+ const findings = [...topologyFindings, ...observationFindings, ...optionFindings];
72
+ if (findings.some((entry) => entry.severity === "error"))
73
+ return indeterminate(options.maximumExceptionRate, observedChanges, findings);
74
+ const definition = topology;
75
+ const evidence = observations;
76
+ const interfaces = definition.interfaces ?? [];
77
+ const changes = evidence.map((observation) => {
78
+ const crossings = observation.crossings.map((crossing) => ({ ...crossing, declared: isDeclared(crossing, interfaces) }));
79
+ return { id: observation.id, observedAt: observation.observedAt, material: observation.material, crossings, hasException: observation.material && crossings.some((crossing) => !crossing.declared) };
80
+ });
81
+ const material = changes.filter((change) => change.material);
82
+ if (material.length === 0) {
83
+ return { state: "indeterminate", exceptionRate: null, maximumExceptionRate: options.maximumExceptionRate, observedChanges: changes.length, observedMaterialChanges: 0, materialChangesWithExceptions: 0, changes, findings: [{ rule: "material-evidence-required", severity: "warning", message: "No observed material changes are available; architecture exception rate is unobserved." }] };
84
+ }
85
+ const exceptions = material.filter((change) => change.hasException).length;
86
+ const exceptionRate = exceptions / material.length;
87
+ return {
88
+ state: exceptionRate <= options.maximumExceptionRate ? "satisfied" : "violated",
89
+ exceptionRate,
90
+ maximumExceptionRate: options.maximumExceptionRate,
91
+ observedChanges: changes.length,
92
+ observedMaterialChanges: material.length,
93
+ materialChangesWithExceptions: exceptions,
94
+ changes,
95
+ findings: [],
96
+ };
97
+ }
98
+ //# sourceMappingURL=assessment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assessment.js","sourceRoot":"","sources":["../src/assessment.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ export declare class ArchitectCliInputError extends Error {
3
+ }
4
+ /** Testable command dispatcher. Invalid arguments throw and the executable maps them to 2. */
5
+ export declare function main(argv: readonly string[]): number;
6
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAgBA,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AAwCpD,8FAA8F;AAC9F,wBAAgB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAMpD"}
package/dist/cli.js ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { assessArchitectureExceptions } from "./assessment.js";
6
+ import { validateOperatingTopology } from "./topology.js";
7
+ const USAGE = `Usage: architect-check topology <topology-file>
8
+ architect-check exceptions <topology-file> <observations-file> --maximum-exception-rate <rate>
9
+
10
+ Commands:
11
+ topology Validate a provider-neutral operating topology.
12
+ exceptions Assess architecture exception rate from observed changes.
13
+
14
+ Exit codes: 0 = satisfied, 1 = violated, 2 = indeterminate or could not run.`;
15
+ export class ArchitectCliInputError extends Error {
16
+ }
17
+ function readJson(path, label) {
18
+ const resolved = resolve(path);
19
+ if (!existsSync(resolved))
20
+ throw new ArchitectCliInputError(`${label} "${path}" does not exist`);
21
+ try {
22
+ if (!statSync(resolved).isFile())
23
+ throw new ArchitectCliInputError(`${label} "${path}" is not a file`);
24
+ }
25
+ catch (error) {
26
+ if (error instanceof ArchitectCliInputError)
27
+ throw error;
28
+ throw new ArchitectCliInputError(`cannot inspect ${label} "${path}": ${error instanceof Error ? error.message : String(error)}`);
29
+ }
30
+ try {
31
+ return JSON.parse(readFileSync(resolved, "utf8"));
32
+ }
33
+ catch (error) {
34
+ throw new ArchitectCliInputError(`${label} "${path}" is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
35
+ }
36
+ }
37
+ function topologyCommand(argv) {
38
+ if (argv.length !== 1)
39
+ throw new ArchitectCliInputError("topology requires exactly one topology-file");
40
+ const findings = validateOperatingTopology(readJson(argv[0], "topology-file"));
41
+ const report = { state: findings.some((entry) => entry.severity === "error") ? "violated" : "satisfied", findings };
42
+ console.log(JSON.stringify(report, null, 2));
43
+ return report.state === "satisfied" ? 0 : 1;
44
+ }
45
+ function exceptionsCommand(argv) {
46
+ let topologyFile;
47
+ let observationsFile;
48
+ let rate;
49
+ for (let index = 0; index < argv.length; index++) {
50
+ const argument = argv[index];
51
+ if (argument === "--maximum-exception-rate") {
52
+ const value = argv[++index];
53
+ if (value === undefined)
54
+ throw new ArchitectCliInputError("--maximum-exception-rate requires a value");
55
+ rate = Number(value);
56
+ }
57
+ else if (argument.startsWith("-"))
58
+ throw new ArchitectCliInputError(`unknown option "${argument}"`);
59
+ else if (topologyFile === undefined)
60
+ topologyFile = argument;
61
+ else if (observationsFile === undefined)
62
+ observationsFile = argument;
63
+ else
64
+ throw new ArchitectCliInputError(`unexpected extra argument "${argument}"`);
65
+ }
66
+ if (topologyFile === undefined || observationsFile === undefined || rate === undefined)
67
+ throw new ArchitectCliInputError("exceptions requires topology-file, observations-file, and --maximum-exception-rate");
68
+ const report = assessArchitectureExceptions(readJson(topologyFile, "topology-file"), readJson(observationsFile, "observations-file"), { maximumExceptionRate: rate });
69
+ console.log(JSON.stringify(report, null, 2));
70
+ return report.state === "satisfied" ? 0 : report.state === "violated" ? 1 : 2;
71
+ }
72
+ /** Testable command dispatcher. Invalid arguments throw and the executable maps them to 2. */
73
+ export function main(argv) {
74
+ if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) {
75
+ console.log(USAGE);
76
+ return 0;
77
+ }
78
+ const [command, ...rest] = argv;
79
+ if (command === "topology")
80
+ return topologyCommand(rest);
81
+ if (command === "exceptions")
82
+ return exceptionsCommand(rest);
83
+ throw new ArchitectCliInputError(command === undefined ? "a command is required" : `unknown command "${command}"`);
84
+ }
85
+ function run() {
86
+ try {
87
+ process.exitCode = main(process.argv.slice(2));
88
+ }
89
+ catch (error) {
90
+ console.error(`architect-check: ${error instanceof Error ? error.message : String(error)}`);
91
+ process.exitCode = 2;
92
+ }
93
+ }
94
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === resolve(process.argv[1]))
95
+ run();
96
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Provider-neutral operating-architecture contracts and evidence-based assessment. */
2
+ export { OPERATING_RESPONSIBILITIES, OPERATING_SCOPE_KINDS, OPERATING_SYSTEM_KINDS, } from "./types.js";
3
+ export { compareOperatingTopologies, defineOperatingTopology, normalizeOperatingTopology, serializeOperatingTopology, validateOperatingTopology, } from "./topology.js";
4
+ export { assessArchitectureExceptions, validateArchitectureChangeObservations, } from "./assessment.js";
5
+ export type { ArchitectureAssessmentOptions, ArchitectureAssessmentState, ArchitectureChangeObservation, ArchitectureExceptionAssessment, ArchitectureFinding, ArchitectureFindingSeverity, AssessedArchitectureChange, AssessedBoundaryCrossing, AuthorityDefinition, BoundaryCrossingObservation, OperatingInterfaceDefinition, OperatingResponsibility, OperatingScopeDefinition, OperatingScopeKind, OperatingSystemDefinition, OperatingSystemKind, OperatingTopology, OperatingTopologyChange, OperatingTopologyCompatibilityReport, OperatingTopologyDefinition, } from "./types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,4BAA4B,EAC5B,sCAAsC,GACvC,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,6BAA6B,EAC7B,2BAA2B,EAC3B,6BAA6B,EAC7B,+BAA+B,EAC/B,mBAAmB,EACnB,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,2BAA2B,EAC3B,4BAA4B,EAC5B,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,oCAAoC,EACpC,2BAA2B,GAC5B,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /** Provider-neutral operating-architecture contracts and evidence-based assessment. */
2
+ export { OPERATING_RESPONSIBILITIES, OPERATING_SCOPE_KINDS, OPERATING_SYSTEM_KINDS, } from "./types.js";
3
+ export { compareOperatingTopologies, defineOperatingTopology, normalizeOperatingTopology, serializeOperatingTopology, validateOperatingTopology, } from "./topology.js";
4
+ export { assessArchitectureExceptions, validateArchitectureChangeObservations, } from "./assessment.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,4BAA4B,EAC5B,sCAAsC,GACvC,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { DomainModelCompatibilityReport, DomainModelDefinition } from "./types.js";
2
+ /**
3
+ * Compares actual model content. Labels and `schemaVersion` are intentionally
4
+ * not compatibility surface; stable identifiers and structural contracts are.
5
+ */
6
+ export declare function compareDomainModels(previous: DomainModelDefinition, next: DomainModelDefinition): DomainModelCompatibilityReport;
7
+ //# sourceMappingURL=compatibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/ontology/compatibility.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkC,8BAA8B,EAAE,qBAAqB,EAAwG,MAAM,YAAY,CAAC;AAsG9N;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,IAAI,EAAE,qBAAqB,GAAG,8BAA8B,CAuBhI"}
@@ -0,0 +1,135 @@
1
+ import { normalizeDomainModel } from "./normalize.js";
2
+ import { validateDomainModel } from "./validate.js";
3
+ function mapById(items) {
4
+ return new Map(items.map((item) => [item.id, item]));
5
+ }
6
+ function stable(value) {
7
+ return JSON.stringify(value);
8
+ }
9
+ function compareFields(previous, next, subject, ownerId, changes) {
10
+ const before = mapById(previous);
11
+ const after = mapById(next);
12
+ for (const [id, field] of before) {
13
+ const replacement = after.get(id);
14
+ if (replacement === undefined) {
15
+ changes.push({ kind: "breaking", subject, id, message: `${subject} "${id}" was removed from "${ownerId}".` });
16
+ continue;
17
+ }
18
+ if (field.valueType !== replacement.valueType) {
19
+ changes.push({ kind: "breaking", subject, id, message: `${subject} "${id}" changed valueType.` });
20
+ }
21
+ if (field.required !== true && replacement.required === true) {
22
+ changes.push({ kind: "breaking", subject, id, message: `${subject} "${id}" became required.` });
23
+ }
24
+ if (field.required === true && replacement.required !== true) {
25
+ changes.push({ kind: "additive", subject, id, message: `${subject} "${id}" became optional.` });
26
+ }
27
+ }
28
+ for (const [id, field] of after) {
29
+ if (!before.has(id)) {
30
+ changes.push({
31
+ kind: field.required === true ? "breaking" : "additive",
32
+ subject,
33
+ id,
34
+ message: `${subject} "${id}" was added to "${ownerId}"${field.required === true ? " as required" : ""}.`,
35
+ });
36
+ }
37
+ }
38
+ }
39
+ function compareValueTypes(previous, next, changes) {
40
+ const before = mapById(previous);
41
+ const after = mapById(next);
42
+ for (const [id, valueType] of before) {
43
+ const replacement = after.get(id);
44
+ if (replacement === undefined)
45
+ changes.push({ kind: "breaking", subject: "value-type", id, message: `Value type "${id}" was removed.` });
46
+ else if (valueType.primitive !== replacement.primitive)
47
+ changes.push({ kind: "breaking", subject: "value-type", id, message: `Value type "${id}" changed primitive.` });
48
+ }
49
+ for (const [id] of after)
50
+ if (!before.has(id))
51
+ changes.push({ kind: "additive", subject: "value-type", id, message: `Value type "${id}" was added.` });
52
+ }
53
+ function compareVocabularies(previous, next, changes) {
54
+ const before = mapById(previous);
55
+ const after = mapById(next);
56
+ for (const [id, vocabulary] of before) {
57
+ const replacement = after.get(id);
58
+ if (replacement === undefined) {
59
+ changes.push({ kind: "breaking", subject: "vocabulary", id, message: `Vocabulary "${id}" was removed.` });
60
+ continue;
61
+ }
62
+ const previousValues = new Set(vocabulary.values);
63
+ const nextValues = new Set(replacement.values);
64
+ for (const value of previousValues)
65
+ if (!nextValues.has(value))
66
+ changes.push({ kind: "breaking", subject: "vocabulary-value", id: `${id}.${value}`, message: `Vocabulary value "${value}" was removed from "${id}".` });
67
+ for (const value of nextValues)
68
+ if (!previousValues.has(value))
69
+ changes.push({ kind: "additive", subject: "vocabulary-value", id: `${id}.${value}`, message: `Vocabulary value "${value}" was added to "${id}".` });
70
+ }
71
+ for (const [id] of after)
72
+ if (!before.has(id))
73
+ changes.push({ kind: "additive", subject: "vocabulary", id, message: `Vocabulary "${id}" was added.` });
74
+ }
75
+ function compareTypes(previous, next, changes) {
76
+ const before = mapById(previous);
77
+ const after = mapById(next);
78
+ for (const [id, type] of before) {
79
+ const replacement = after.get(id);
80
+ if (replacement === undefined)
81
+ changes.push({ kind: "breaking", subject: "type", id, message: `Type "${id}" was removed.` });
82
+ else
83
+ compareFields(type.fields ?? [], replacement.fields ?? [], "field", id, changes);
84
+ }
85
+ for (const [id] of after)
86
+ if (!before.has(id))
87
+ changes.push({ kind: "additive", subject: "type", id, message: `Type "${id}" was added.` });
88
+ }
89
+ function compareRelations(previous, next, changes) {
90
+ const before = mapById(previous);
91
+ const after = mapById(next);
92
+ for (const [id, relation] of before) {
93
+ const replacement = after.get(id);
94
+ if (replacement === undefined) {
95
+ changes.push({ kind: "breaking", subject: "relation", id, message: `Relation "${id}" was removed.` });
96
+ continue;
97
+ }
98
+ if (relation.from !== replacement.from || relation.to !== replacement.to)
99
+ changes.push({ kind: "breaking", subject: "relation", id, message: `Relation "${id}" changed an endpoint.` });
100
+ if (relation.cardinality !== replacement.cardinality)
101
+ changes.push({ kind: "breaking", subject: "relation", id, message: `Relation "${id}" changed cardinality.` });
102
+ compareFields(relation.properties ?? [], replacement.properties ?? [], "relation-property", id, changes);
103
+ }
104
+ for (const [id] of after)
105
+ if (!before.has(id))
106
+ changes.push({ kind: "additive", subject: "relation", id, message: `Relation "${id}" was added.` });
107
+ }
108
+ /**
109
+ * Compares actual model content. Labels and `schemaVersion` are intentionally
110
+ * not compatibility surface; stable identifiers and structural contracts are.
111
+ */
112
+ export function compareDomainModels(previous, next) {
113
+ const previousFindings = validateDomainModel(previous);
114
+ const nextFindings = validateDomainModel(next);
115
+ if (previousFindings.some((entry) => entry.severity === "error") || nextFindings.some((entry) => entry.severity === "error")) {
116
+ return { compatible: false, changes: [], previousFindings, nextFindings };
117
+ }
118
+ const before = normalizeDomainModel(previous);
119
+ const after = normalizeDomainModel(next);
120
+ const changes = [];
121
+ if (before.id !== after.id)
122
+ changes.push({ kind: "breaking", subject: "model", id: after.id, message: `Model id changed from "${before.id}" to "${after.id}".` });
123
+ compareValueTypes(before.valueTypes, after.valueTypes, changes);
124
+ compareVocabularies(before.vocabularies, after.vocabularies, changes);
125
+ compareTypes(before.types, after.types, changes);
126
+ compareRelations(before.relations, after.relations, changes);
127
+ changes.sort((left, right) => stable(left).localeCompare(stable(right)));
128
+ return {
129
+ compatible: changes.every((entry) => entry.kind !== "breaking"),
130
+ changes,
131
+ previousFindings,
132
+ nextFindings,
133
+ };
134
+ }
135
+ //# sourceMappingURL=compatibility.js.map