Skill Tracer
Skills › Archify › archify › engineering-profiles.mjs

engineering-profiles.mjs

157 lines · archify/renderers/shared/engineering-profiles.mjs at a406594

1import { throwDiagnosticError } from './diagnostics.mjs';
2
3const DEPLOYMENT_PROFILE = 'deployment-ownership';
4const DEPLOYMENT_BOUNDARY_KINDS = new Set(['region', 'security-group']);
5const PRIVATE_STATE_TYPES = new Set(['database']);
6
7function subject(collection, index, item = {}) {
8 return {
9 diagramType: 'architecture',
10 profile: DEPLOYMENT_PROFILE,
11 collection,
12 index,
13 ...(item.id ? { id: item.id } : {}),
14 };
17function membership(boundaries, componentId, kind) {
18 return boundaries
19 .map((boundary, index) => ({ boundary, index }))
20 .filter(({ boundary }) => boundary.kind === kind && boundary.wraps.includes(componentId));
23export function deploymentOwnershipDiagnostics(diagram) {
24 const components = Array.isArray(diagram.components) ? diagram.components : [];
25 const boundaries = (Array.isArray(diagram.boundaries) ? diagram.boundaries : [])
26 .map((boundary) => ({ ...boundary, wraps: Array.isArray(boundary.wraps) ? boundary.wraps : [] }));
27 const connections = Array.isArray(diagram.connections) ? diagram.connections : [];
28 const diagnostics = [];
30 for (const kind of DEPLOYMENT_BOUNDARY_KINDS) {
31 const count = boundaries.filter((boundary) => boundary.kind === kind).length;
32 if (count > 0) continue;
33 diagnostics.push({
34 code: 'engineering/deployment-boundary-kind',
35 severity: 'error',
36 message: `Deployment ownership requires at least one ${kind} boundary.`,
37 subject: subject('boundaries', -1),
38 evidence: { requiredKind: kind, found: count },
39 supportedFixes: [`add one ${kind} boundary with an explicit wraps list`],
40 });
41 }
43 components.forEach((component, index) => {
44 if (component.type === 'external') return;
45 if (typeof component.tag !== 'string' || component.tag.trim() === '') {
46 diagnostics.push({
47 code: 'engineering/deployment-owner-missing',
48 severity: 'error',
49 message: `Deployment component ${JSON.stringify(component.id)} does not name its owner in tag.`,
50 subject: subject('components', index, component),
51 evidence: { componentType: component.type, ownerField: 'tag' },
52 supportedFixes: [`set /components/${index}/tag to the responsible team or owner`],
53 });
54 }
56 const regions = membership(boundaries, component.id, 'region');
57 if (regions.length === 0) {
58 diagnostics.push({
59 code: 'engineering/deployment-region-scope',
60 severity: 'error',
61 message: `Deployment component ${JSON.stringify(component.id)} is not assigned to a region boundary.`,
62 subject: subject('components', index, component),
63 evidence: { componentType: component.type, regionMemberships: 0 },
64 supportedFixes: ['add the component id to the real region boundary wraps list'],
65 });
66 } else if (regions.length > 1) {
67 diagnostics.push({
68 code: 'engineering/deployment-region-ambiguous',
69 severity: 'error',
70 message: `Deployment component ${JSON.stringify(component.id)} belongs to more than one region boundary.`,
71 subject: subject('components', index, component),
72 evidence: {
73 componentType: component.type,
74 regions: regions.map(({ boundary, index: boundaryIndex }) => ({ boundaryIndex, label: boundary.label })),
75 },
76 supportedFixes: ['keep the component id in exactly one real region boundary wraps list'],
77 });
78 }
80 if (PRIVATE_STATE_TYPES.has(component.type)) {
81 const privateScopes = membership(boundaries, component.id, 'security-group');
82 if (privateScopes.length === 0) {
83 diagnostics.push({
84 code: 'engineering/deployment-private-state',
85 severity: 'error',
86 message: `Stateful component ${JSON.stringify(component.id)} is not assigned to a private security-group boundary.`,
87 subject: subject('components', index, component),
88 evidence: { componentType: component.type, privateMemberships: 0 },
89 supportedFixes: ['add the component id to the real private security-group boundary wraps list'],
90 });
91 }
92 }
93 });
95 boundaries.forEach((boundary, index) => {
96 if (boundary.kind !== 'security-group') return;
97 const members = boundary.wraps.map((id) => ({
98 id,
99 regions: membership(boundaries, id, 'region').map(({ boundary: region, index: boundaryIndex }) => ({
100 boundaryIndex,
101 label: region.label,
102 })),
103 }));
104 const regionIndexes = new Set(members.flatMap((member) => member.regions.map((region) => region.boundaryIndex)));
105 const consistent = members.length > 0
106 && members.every((member) => member.regions.length === 1)
107 && regionIndexes.size === 1;
108 if (consistent) return;
109 diagnostics.push({
110 code: 'engineering/deployment-private-region-consistency',
111 severity: 'error',
112 message: `Private boundary ${JSON.stringify(boundary.label)} must contain components from exactly one shared region.`,
113 subject: subject('boundaries', index, boundary),
114 evidence: { boundaryKind: boundary.kind, members },
115 supportedFixes: ['assign every private-boundary component to exactly one shared region boundary'],
116 });
117 });
119 connections.forEach((connection, index) => {
120 const crossedBoundaries = boundaries
121 .map((boundary, boundaryIndex) => ({
122 boundaryIndex,
123 kind: boundary.kind,
124 label: boundary.label,
125 fromInside: boundary.wraps.includes(connection.from),
126 toInside: boundary.wraps.includes(connection.to),
127 }))
128 .filter((boundary) => DEPLOYMENT_BOUNDARY_KINDS.has(boundary.kind) && boundary.fromInside !== boundary.toInside);
129 if (crossedBoundaries.length === 0 || (typeof connection.label === 'string' && connection.label.trim() !== '')) return;
130 diagnostics.push({
131 code: 'engineering/deployment-crossing-mechanism',
132 severity: 'error',
133 message: `Cross-boundary connection ${JSON.stringify(connection.id || `${connection.from}->${connection.to}`)} does not name its mechanism.`,
134 subject: subject('connections', index, connection),
135 evidence: {
136 from: connection.from,
137 to: connection.to,
138 crossedBoundaries: crossedBoundaries.map(({ boundaryIndex, kind, label }) => ({ boundaryIndex, kind, label })),
139 },
140 supportedFixes: [`set /connections/${index}/label to the real cross-boundary mechanism`],
141 });
142 });
144 return diagnostics;
147export function validateEngineeringProfile(diagramType, diagram) {
148 const profile = diagram.meta?.engineering_profile;
149 if (!profile) return;
150 if (diagramType !== 'architecture' || profile !== DEPLOYMENT_PROFILE) return;
151 const diagnostics = deploymentOwnershipDiagnostics(diagram);
152 if (!diagnostics.length) return;
153 throwDiagnosticError(
154 `Engineering profile ${JSON.stringify(profile)} failed:\n${diagnostics.map((entry) => `- ${entry.message}`).join('\n')}`,
155 diagnostics,
156 );