- 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.
182 lines
7.9 KiB
JavaScript
182 lines
7.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { spawnSync } from 'node:child_process';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const skillRoot = path.resolve(here, '..');
|
|
const cli = path.join(skillRoot, 'bin/archify.mjs');
|
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'archify-repair-receipt-'));
|
|
|
|
function run(args) {
|
|
return spawnSync(process.execPath, [cli, ...args], {
|
|
cwd: skillRoot,
|
|
encoding: 'utf8',
|
|
});
|
|
}
|
|
|
|
function writeFixture(name, source) {
|
|
const file = path.join(tmp, name);
|
|
fs.writeFileSync(file, JSON.stringify(source, null, 2));
|
|
return file;
|
|
}
|
|
|
|
function receipt(result) {
|
|
assert.doesNotThrow(() => JSON.parse(result.stdout), result.stdout || result.stderr);
|
|
return JSON.parse(result.stdout);
|
|
}
|
|
|
|
test('repair receipt: malformed JSON is one clean machine object without a Node stack', () => {
|
|
const input = path.join(tmp, 'malformed.workflow.json');
|
|
fs.writeFileSync(input, '{broken json');
|
|
|
|
const result = run(['validate', 'workflow', input, '--json']);
|
|
assert.equal(result.status, 1);
|
|
assert.equal(result.stderr, '');
|
|
const failure = receipt(result);
|
|
assert.equal(failure.schemaVersion, 1);
|
|
assert.equal(failure.ok, false);
|
|
assert.equal(failure.command, 'validate');
|
|
assert.equal(failure.stage, 'input');
|
|
assert.equal(failure.diagnostics.length, 1);
|
|
assert.deepEqual(failure.diagnostics[0].subject, { input });
|
|
assert.equal(failure.diagnostics[0].code, 'input/json-parse');
|
|
assert.equal(failure.diagnostics[0].severity, 'error');
|
|
assert.match(failure.diagnostics[0].evidence.reason, /JSON/);
|
|
assert.deepEqual(failure.diagnostics[0].supportedFixes, ['repair the JSON syntax and run validation again']);
|
|
assert.doesNotMatch(result.stdout, /\n\s+at\s|file:\/\//);
|
|
});
|
|
|
|
test('repair receipt: all five modes identify schema subjects and supported fixes', () => {
|
|
const cases = {
|
|
architecture: ['web-app.architecture.json', 'components'],
|
|
workflow: ['agent-tool-call.workflow.json', 'nodes'],
|
|
sequence: ['cache-miss-request.sequence.json', 'participants'],
|
|
dataflow: ['product-analytics.dataflow.json', 'nodes'],
|
|
lifecycle: ['agent-run.lifecycle.json', 'states'],
|
|
};
|
|
|
|
for (const [type, [example, collection]] of Object.entries(cases)) {
|
|
const source = JSON.parse(fs.readFileSync(path.join(skillRoot, 'examples', example), 'utf8'));
|
|
source[collection][0].unexpected = true;
|
|
const identity = source[collection][0].id;
|
|
const input = writeFixture(`schema-${type}.json`, source);
|
|
const result = run(['validate', type, input, '--json']);
|
|
|
|
assert.equal(result.status, 1, `${type}: ${result.stderr || result.stdout}`);
|
|
assert.equal(result.stderr, '', type);
|
|
const failure = receipt(result);
|
|
const repair = failure.diagnostics.find((entry) => entry.code === 'schema/additionalProperties');
|
|
assert.ok(repair, type);
|
|
assert.deepEqual(repair.subject, {
|
|
diagramType: type,
|
|
path: `/${collection}/0`,
|
|
identity,
|
|
});
|
|
assert.equal(repair.evidence.additionalProperty, 'unexpected');
|
|
assert.deepEqual(repair.supportedFixes, ['remove unsupported property "unexpected"']);
|
|
}
|
|
});
|
|
|
|
test('repair receipt: human validation formats the same rule without a stack', () => {
|
|
const source = JSON.parse(fs.readFileSync(path.join(skillRoot, 'examples/agent-tool-call.workflow.json'), 'utf8'));
|
|
source.nodes[0].unexpected = true;
|
|
const input = writeFixture('human-schema.workflow.json', source);
|
|
const result = run(['validate', 'workflow', input]);
|
|
|
|
assert.equal(result.status, 1);
|
|
assert.equal(result.stdout, '');
|
|
assert.match(result.stderr, /\[schema\/additionalProperties\]/);
|
|
assert.match(result.stderr, /Fix: remove unsupported property "unexpected"/);
|
|
assert.doesNotMatch(result.stderr, /\n\s+at\s|file:\/\//);
|
|
});
|
|
|
|
test('repair receipt: validate and deliver share exact Clean Flow evidence while delivery preserves the trusted artifact', () => {
|
|
const source = JSON.parse(fs.readFileSync(path.join(skillRoot, 'examples/web-app.architecture.json'), 'utf8'));
|
|
source.connections[0] = {
|
|
...source.connections[0],
|
|
fromSide: 'right',
|
|
toSide: 'left',
|
|
via: [[100, 140], [220, 140]],
|
|
};
|
|
const input = writeFixture('blocked-route.architecture.json', source);
|
|
const output = path.join(tmp, 'trusted.html');
|
|
const trusted = '<!doctype html><title>trusted prior artifact</title>\n';
|
|
fs.writeFileSync(output, trusted);
|
|
|
|
const validated = run(['validate', 'architecture', input, '--quality', 'showcase', '--json']);
|
|
const delivered = run(['deliver', 'architecture', input, output, '--quality', 'showcase', '--json']);
|
|
assert.equal(validated.status, 1, validated.stderr || validated.stdout);
|
|
assert.equal(delivered.status, 1, delivered.stderr || delivered.stdout);
|
|
assert.equal(validated.stderr, '');
|
|
assert.equal(delivered.stderr, '');
|
|
|
|
const validateRepair = receipt(validated).diagnostics.find((entry) => entry.code === 'clean-flow/edge-through-node');
|
|
const deliverRepair = receipt(delivered).diagnostics.find((entry) => entry.code === 'clean-flow/edge-through-node');
|
|
assert.ok(validateRepair);
|
|
assert.ok(deliverRepair);
|
|
assert.deepEqual(deliverRepair, validateRepair);
|
|
assert.equal(validateRepair.subject.id, 'users-to-cdn');
|
|
assert.equal(validateRepair.evidence.obstacleId, 'auth');
|
|
assert.equal(validateRepair.evidence.segmentIndex, 0);
|
|
assert.equal(validateRepair.evidence.clearancePx, 2);
|
|
assert.ok(validateRepair.supportedFixes.some((fix) => fix.includes('route/via')));
|
|
assert.equal(fs.readFileSync(output, 'utf8'), trusted);
|
|
assert.deepEqual(fs.readdirSync(tmp).filter((name) => name.startsWith('.archify-delivery-')), []);
|
|
});
|
|
|
|
test('repair receipt: repository evidence failures retain a stable rule and exact repair', () => {
|
|
const source = JSON.parse(fs.readFileSync(path.join(skillRoot, 'examples/web-app.architecture.json'), 'utf8'));
|
|
source.meta.repository = {
|
|
url: 'https://github.com/example/repository',
|
|
revision: '0123456789abcdef0123456789abcdef01234567',
|
|
};
|
|
source.components[0].sources = [{ path: 'src/index.js', line: 1 }];
|
|
const input = writeFixture('evidence-root-required.architecture.json', source);
|
|
const result = run(['validate', 'architecture', input, '--json']);
|
|
|
|
assert.equal(result.status, 1);
|
|
assert.equal(result.stderr, '');
|
|
const repair = receipt(result).diagnostics[0];
|
|
assert.equal(repair.code, 'repository-evidence/root-required');
|
|
assert.deepEqual(repair.subject, { surface: 'repository-evidence', path: '/meta/repository' });
|
|
assert.deepEqual(repair.supportedFixes, ['pass --repo-root with the matching local Git checkout']);
|
|
});
|
|
|
|
test('repair receipt: public validate reports borderline desktop readability with a supported fix', () => {
|
|
const input = writeFixture('borderline-readability.architecture.json', {
|
|
schema_version: 1,
|
|
diagram_type: 'architecture',
|
|
meta: {
|
|
title: 'Borderline desktop readability',
|
|
quality_profile: 'showcase',
|
|
viewBox: [1826, 804],
|
|
},
|
|
components: [{
|
|
id: 'tool-runtime',
|
|
type: 'security',
|
|
label: 'ToolRuntime',
|
|
sublabel: 'OpenAI · Anthropic · provider gateways',
|
|
pos: [100, 100],
|
|
size: [194, 70],
|
|
}],
|
|
connections: [],
|
|
});
|
|
const result = run(['validate', 'architecture', input, '--quality', 'showcase', '--json']);
|
|
|
|
assert.equal(result.status, 1, result.stderr || result.stdout);
|
|
assert.equal(result.stderr, '');
|
|
const repair = receipt(result).diagnostics.find(
|
|
(entry) => entry.code === 'composition/desktop-readability',
|
|
);
|
|
assert.ok(repair);
|
|
assert.deepEqual(repair.subject, { check: 'composition' });
|
|
assert.ok(repair.evidence.projectedFontPx < repair.evidence.minimumProjectedFontPx);
|
|
assert.ok(repair.supportedFixes.some((fix) => fix.includes('reduce the viewBox width')));
|
|
});
|
|
|
|
process.on('exit', () => fs.rmSync(tmp, { recursive: true, force: true }));
|