- Added a new Archify skill, enabling users to create polished architecture, workflow, sequence, data-flow, and lifecycle diagrams. - Implemented comprehensive functionality including rendering, validation, and delivery of diagrams in various formats. - Integrated a user-friendly command-line interface for generating and previewing diagrams. - Developed supporting files including package.json, LICENSE, and SKILL.md for documentation and licensing. - Added unit tests to ensure reliability and functionality of the new skill. These changes enhance the application by providing a structured approach to visualizing system architecture and workflows, improving user experience and data representation.
158 lines
6.8 KiB
JavaScript
158 lines
6.8 KiB
JavaScript
import { throwDiagnosticError } from './diagnostics.mjs';
|
|
|
|
const DEPLOYMENT_PROFILE = 'deployment-ownership';
|
|
const DEPLOYMENT_BOUNDARY_KINDS = new Set(['region', 'security-group']);
|
|
const PRIVATE_STATE_TYPES = new Set(['database']);
|
|
|
|
function subject(collection, index, item = {}) {
|
|
return {
|
|
diagramType: 'architecture',
|
|
profile: DEPLOYMENT_PROFILE,
|
|
collection,
|
|
index,
|
|
...(item.id ? { id: item.id } : {}),
|
|
};
|
|
}
|
|
|
|
function membership(boundaries, componentId, kind) {
|
|
return boundaries
|
|
.map((boundary, index) => ({ boundary, index }))
|
|
.filter(({ boundary }) => boundary.kind === kind && boundary.wraps.includes(componentId));
|
|
}
|
|
|
|
export function deploymentOwnershipDiagnostics(diagram) {
|
|
const components = Array.isArray(diagram.components) ? diagram.components : [];
|
|
const boundaries = (Array.isArray(diagram.boundaries) ? diagram.boundaries : [])
|
|
.map((boundary) => ({ ...boundary, wraps: Array.isArray(boundary.wraps) ? boundary.wraps : [] }));
|
|
const connections = Array.isArray(diagram.connections) ? diagram.connections : [];
|
|
const diagnostics = [];
|
|
|
|
for (const kind of DEPLOYMENT_BOUNDARY_KINDS) {
|
|
const count = boundaries.filter((boundary) => boundary.kind === kind).length;
|
|
if (count > 0) continue;
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-boundary-kind',
|
|
severity: 'error',
|
|
message: `Deployment ownership requires at least one ${kind} boundary.`,
|
|
subject: subject('boundaries', -1),
|
|
evidence: { requiredKind: kind, found: count },
|
|
supportedFixes: [`add one ${kind} boundary with an explicit wraps list`],
|
|
});
|
|
}
|
|
|
|
components.forEach((component, index) => {
|
|
if (component.type === 'external') return;
|
|
if (typeof component.tag !== 'string' || component.tag.trim() === '') {
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-owner-missing',
|
|
severity: 'error',
|
|
message: `Deployment component ${JSON.stringify(component.id)} does not name its owner in tag.`,
|
|
subject: subject('components', index, component),
|
|
evidence: { componentType: component.type, ownerField: 'tag' },
|
|
supportedFixes: [`set /components/${index}/tag to the responsible team or owner`],
|
|
});
|
|
}
|
|
|
|
const regions = membership(boundaries, component.id, 'region');
|
|
if (regions.length === 0) {
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-region-scope',
|
|
severity: 'error',
|
|
message: `Deployment component ${JSON.stringify(component.id)} is not assigned to a region boundary.`,
|
|
subject: subject('components', index, component),
|
|
evidence: { componentType: component.type, regionMemberships: 0 },
|
|
supportedFixes: ['add the component id to the real region boundary wraps list'],
|
|
});
|
|
} else if (regions.length > 1) {
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-region-ambiguous',
|
|
severity: 'error',
|
|
message: `Deployment component ${JSON.stringify(component.id)} belongs to more than one region boundary.`,
|
|
subject: subject('components', index, component),
|
|
evidence: {
|
|
componentType: component.type,
|
|
regions: regions.map(({ boundary, index: boundaryIndex }) => ({ boundaryIndex, label: boundary.label })),
|
|
},
|
|
supportedFixes: ['keep the component id in exactly one real region boundary wraps list'],
|
|
});
|
|
}
|
|
|
|
if (PRIVATE_STATE_TYPES.has(component.type)) {
|
|
const privateScopes = membership(boundaries, component.id, 'security-group');
|
|
if (privateScopes.length === 0) {
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-private-state',
|
|
severity: 'error',
|
|
message: `Stateful component ${JSON.stringify(component.id)} is not assigned to a private security-group boundary.`,
|
|
subject: subject('components', index, component),
|
|
evidence: { componentType: component.type, privateMemberships: 0 },
|
|
supportedFixes: ['add the component id to the real private security-group boundary wraps list'],
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
boundaries.forEach((boundary, index) => {
|
|
if (boundary.kind !== 'security-group') return;
|
|
const members = boundary.wraps.map((id) => ({
|
|
id,
|
|
regions: membership(boundaries, id, 'region').map(({ boundary: region, index: boundaryIndex }) => ({
|
|
boundaryIndex,
|
|
label: region.label,
|
|
})),
|
|
}));
|
|
const regionIndexes = new Set(members.flatMap((member) => member.regions.map((region) => region.boundaryIndex)));
|
|
const consistent = members.length > 0
|
|
&& members.every((member) => member.regions.length === 1)
|
|
&& regionIndexes.size === 1;
|
|
if (consistent) return;
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-private-region-consistency',
|
|
severity: 'error',
|
|
message: `Private boundary ${JSON.stringify(boundary.label)} must contain components from exactly one shared region.`,
|
|
subject: subject('boundaries', index, boundary),
|
|
evidence: { boundaryKind: boundary.kind, members },
|
|
supportedFixes: ['assign every private-boundary component to exactly one shared region boundary'],
|
|
});
|
|
});
|
|
|
|
connections.forEach((connection, index) => {
|
|
const crossedBoundaries = boundaries
|
|
.map((boundary, boundaryIndex) => ({
|
|
boundaryIndex,
|
|
kind: boundary.kind,
|
|
label: boundary.label,
|
|
fromInside: boundary.wraps.includes(connection.from),
|
|
toInside: boundary.wraps.includes(connection.to),
|
|
}))
|
|
.filter((boundary) => DEPLOYMENT_BOUNDARY_KINDS.has(boundary.kind) && boundary.fromInside !== boundary.toInside);
|
|
if (crossedBoundaries.length === 0 || (typeof connection.label === 'string' && connection.label.trim() !== '')) return;
|
|
diagnostics.push({
|
|
code: 'engineering/deployment-crossing-mechanism',
|
|
severity: 'error',
|
|
message: `Cross-boundary connection ${JSON.stringify(connection.id || `${connection.from}->${connection.to}`)} does not name its mechanism.`,
|
|
subject: subject('connections', index, connection),
|
|
evidence: {
|
|
from: connection.from,
|
|
to: connection.to,
|
|
crossedBoundaries: crossedBoundaries.map(({ boundaryIndex, kind, label }) => ({ boundaryIndex, kind, label })),
|
|
},
|
|
supportedFixes: [`set /connections/${index}/label to the real cross-boundary mechanism`],
|
|
});
|
|
});
|
|
|
|
return diagnostics;
|
|
}
|
|
|
|
export function validateEngineeringProfile(diagramType, diagram) {
|
|
const profile = diagram.meta?.engineering_profile;
|
|
if (!profile) return;
|
|
if (diagramType !== 'architecture' || profile !== DEPLOYMENT_PROFILE) return;
|
|
const diagnostics = deploymentOwnershipDiagnostics(diagram);
|
|
if (!diagnostics.length) return;
|
|
throwDiagnosticError(
|
|
`Engineering profile ${JSON.stringify(profile)} failed:\n${diagnostics.map((entry) => `- ${entry.message}`).join('\n')}`,
|
|
diagnostics,
|
|
);
|
|
}
|