0.3.0: .archify files are YAML and open straight into the diagram

- .archify content is YAML (JSON still accepted); converted to JSON for the renderer
- Archify Diagram custom editor is the default for *.archify; Show Source opens the YAML beside it
- YAML syntax errors and validation problems mapped to YAML lines
- yamlValidation for schema completion with redhat.vscode-yaml
- scripts/to-yaml.mjs converts existing .archify files
This commit is contained in:
2026-09-30 15:21:55 +03:00
parent 7c2624c465
commit d2f52d43ac
18 changed files with 744 additions and 250 deletions
+27 -1
View File
@@ -1,7 +1,7 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { hasSourceEvidence, isArchifyHtml, readDiagramSource, stripSourceEvidence, typeFromFilename } from '../src/detect';
import { pointerSegments, rangeForPointer } from '../src/jsonPointer';
import { pointerSegments, rangeForPointer, rangeForPointerYaml, yamlSyntaxProblems } from '../src/jsonPointer';
test('reads diagram_type from content', () => {
const source = readDiagramSource('{"schema_version":1,"diagram_type":"sequence","meta":{}}', 'plain.json');
@@ -58,4 +58,30 @@ test('.archify files are diagram sources typed by their content', async () => {
assert.equal(diagramBaseName('/w/web.architecture.json'), 'web.architecture');
assert.equal(readDiagramSource('{"diagram_type":"lifecycle"}', 'run.archify')?.type, 'lifecycle');
assert.equal(readDiagramSource('{"title":"no type yet"}', 'run.archify'), undefined);
const yaml = readDiagramSource('schema_version: 1\ndiagram_type: sequence\nmeta:\n title: T\n', 'flow.archify');
assert.equal(yaml?.type, 'sequence');
assert.equal(yaml?.format, 'yaml');
assert.deepEqual(JSON.parse(yaml!.json), { schema_version: 1, diagram_type: 'sequence', meta: { title: 'T' } });
assert.equal(readDiagramSource('diagram_type: [unclosed', 'bad.archify'), undefined);
});
test('yaml pointer ranges match the json behaviour', () => {
const text = 'meta:\n title: x\ncomponents:\n - id: a\n label: A\n';
const meta = rangeForPointerYaml(text, '/meta');
assert.equal(text.slice(meta.start, meta.end), 'meta');
const title = rangeForPointerYaml(text, '/meta/title');
assert.equal(text.slice(title.start, title.end), 'x');
const item = rangeForPointerYaml(text, '/components/0');
assert.equal(text.slice(item.start, item.end), 'i', 'first character of the item');
const label = rangeForPointerYaml(text, '/components/0/label');
assert.equal(text.slice(label.start, label.end), 'A');
const missing = rangeForPointerYaml(text, '/components/0/type');
assert.equal(text.slice(missing.start, missing.end), 'i', 'falls back to the nearest existing ancestor');
});
test('yaml syntax problems carry offsets', () => {
assert.deepEqual(yamlSyntaxProblems('a: 1\nb: 2\n'), []);
const problems = yamlSyntaxProblems('a: [1, 2\nb: 3\n');
assert.ok(problems.length > 0);
assert.ok(problems[0].start >= 0 && problems[0].end > problems[0].start);
});
+165 -77
View File
@@ -1,77 +1,165 @@
{
"schema_version": 1,
"diagram_type": "sequence",
"meta": {
"title": "Cache Miss Request Sequence",
"output": "examples/sequence-cache-miss-request.html",
"viewBox": [1080, 580],
"column_fit": "spread",
"animation": "trace",
"quality_profile": "showcase"
},
"participants": [
{ "id": "user", "type": "external", "label": "User", "sublabel": "browser session" },
{ "id": "web", "type": "frontend", "label": "Web App", "sublabel": "React UI" },
{ "id": "api", "type": "backend", "label": "API", "sublabel": "request handler" },
{ "id": "auth", "type": "security", "label": "Auth", "sublabel": "JWT verify" },
{ "id": "redis", "type": "database", "label": "Redis", "sublabel": "cache" },
{ "id": "db", "type": "database", "label": "Postgres", "sublabel": "source of truth" },
{ "id": "trace", "type": "messagebus", "label": "Trace", "sublabel": "async event" }
],
"segments": [
{ "from": 150, "to": 250, "label": "Request" },
{ "from": 260, "to": 370, "label": "Fallback" },
{ "from": 380, "to": 480, "label": "Response + trace" }
],
"messages": [
{ "id": "open-page", "from": "user", "to": "web", "y": 160, "label": "open page", "variant": "default" },
{ "id": "dashboard-request", "from": "web", "to": "api", "y": 185, "label": "GET /dashboard", "variant": "emphasis" },
{ "id": "verify-jwt", "from": "api", "to": "auth", "y": 210, "label": "verify JWT", "variant": "security" },
{ "id": "auth-claims", "from": "auth", "to": "api", "y": 238, "label": "claims ok", "variant": "return" },
{ "id": "cache-read", "from": "api", "to": "redis", "y": 270, "label": "read cache", "variant": "default" },
{ "id": "cache-miss", "from": "redis", "to": "api", "y": 298, "label": "miss", "variant": "return" },
{ "id": "profile-query", "from": "api", "to": "db", "y": 330, "label": "query profile + metrics", "variant": "emphasis" },
{ "id": "profile-rows", "from": "db", "to": "api", "y": 358, "label": "rows", "variant": "return" },
{ "id": "cache-write", "from": "api", "to": "redis", "y": 390, "label": "set cache", "variant": "dashed" },
{ "id": "trace-emit", "from": "api", "to": "trace", "y": 418, "label": "emit trace", "variant": "dashed" },
{ "id": "dashboard-response", "from": "api", "to": "web", "y": 443, "label": "200 JSON", "variant": "return" },
{ "id": "page-render", "from": "web", "to": "user", "y": 468, "label": "render", "variant": "return" }
],
"activations": [
{ "participant": "web", "from": 180, "to": 474, "type": "frontend" },
{ "participant": "api", "from": 185, "to": 450, "type": "backend" },
{ "participant": "auth", "from": 205, "to": 244, "type": "security" },
{ "participant": "redis", "from": 265, "to": 304, "type": "database" },
{ "participant": "db", "from": 325, "to": 364, "type": "database" },
{ "participant": "trace", "from": 413, "to": 449, "type": "messagebus" }
],
"cards": [
{
"dot": "emerald",
"title": "Happy Path",
"items": [
"The main request is Web App -> API -> data source -> response",
"Return messages are quieter than forward calls",
"Activation bars make ownership duration visible"
]
},
{
"dot": "rose",
"title": "Policy + Fallback",
"items": [
"JWT verification is colored as a security interaction",
"Cache miss is visible without overpowering the main path",
"Database access only appears after cache fallback"
]
},
{
"dot": "orange",
"title": "Async Trace",
"items": [
"Trace emission is dashed and secondary",
"It does not block the response path",
"The diagram separates user-facing latency from observability"
]
}
]
}
schema_version: 1
diagram_type: sequence
meta:
title: Cache Miss Request Sequence
output: examples/sequence-cache-miss-request.html
viewBox: [1080, 580]
column_fit: spread
animation: trace
quality_profile: showcase
participants:
- id: user
type: external
label: User
sublabel: browser session
- id: web
type: frontend
label: Web App
sublabel: React UI
- id: api
type: backend
label: API
sublabel: request handler
- id: auth
type: security
label: Auth
sublabel: JWT verify
- id: redis
type: database
label: Redis
sublabel: cache
- id: db
type: database
label: Postgres
sublabel: source of truth
- id: trace
type: messagebus
label: Trace
sublabel: async event
segments:
- from: 150
to: 250
label: Request
- from: 260
to: 370
label: Fallback
- from: 380
to: 480
label: Response + trace
messages:
- id: open-page
from: user
to: web
y: 160
label: open page
variant: default
- id: dashboard-request
from: web
to: api
y: 185
label: GET /dashboard
variant: emphasis
- id: verify-jwt
from: api
to: auth
y: 210
label: verify JWT
variant: security
- id: auth-claims
from: auth
to: api
y: 238
label: claims ok
variant: return
- id: cache-read
from: api
to: redis
y: 270
label: read cache
variant: default
- id: cache-miss
from: redis
to: api
y: 298
label: miss
variant: return
- id: profile-query
from: api
to: db
y: 330
label: query profile + metrics
variant: emphasis
- id: profile-rows
from: db
to: api
y: 358
label: rows
variant: return
- id: cache-write
from: api
to: redis
y: 390
label: set cache
variant: dashed
- id: trace-emit
from: api
to: trace
y: 418
label: emit trace
variant: dashed
- id: dashboard-response
from: api
to: web
y: 443
label: 200 JSON
variant: return
- id: page-render
from: web
to: user
y: 468
label: render
variant: return
activations:
- participant: web
from: 180
to: 474
type: frontend
- participant: api
from: 185
to: 450
type: backend
- participant: auth
from: 205
to: 244
type: security
- participant: redis
from: 265
to: 304
type: database
- participant: db
from: 325
to: 364
type: database
- participant: trace
from: 413
to: 449
type: messagebus
cards:
- dot: emerald
title: Happy Path
items:
- The main request is Web App -> API -> data source -> response
- Return messages are quieter than forward calls
- Activation bars make ownership duration visible
- dot: rose
title: Policy + Fallback
items:
- JWT verification is colored as a security interaction
- Cache miss is visible without overpowering the main path
- Database access only appears after cache fallback
- dot: orange
title: Async Trace
items:
- Trace emission is dashed and secondary
- It does not block the response path
- The diagram separates user-facing latency from observability
+212 -51
View File
@@ -1,51 +1,212 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Production Deployment Ownership",
"output": "examples/production-deployment.html",
"visual_preset": "blueprint",
"animation": "trace",
"quality_profile": "showcase",
"engineering_profile": "deployment-ownership"
},
"components": [
{ "id": "clients", "type": "external", "label": "Customers", "sublabel": "web + mobile", "pos": [38, 300], "size": [122, 60] },
{ "id": "edge", "type": "cloud", "label": "Global Edge", "sublabel": "CDN + WAF", "pos": [230, 300], "size": [126, 60], "tag": "edge team" },
{ "id": "gateway", "type": "security", "label": "API Gateway", "sublabel": "public :443", "pos": [430, 300], "size": [128, 60], "tag": "platform" },
{ "id": "api_a", "type": "backend", "label": "API Pods / AZ-a", "sublabel": "private subnet", "pos": [630, 195], "size": [136, 62], "tag": "app team" },
{ "id": "api_b", "type": "backend", "label": "API Pods / AZ-b", "sublabel": "private subnet", "pos": [630, 405], "size": [136, 62], "tag": "app team" },
{ "id": "redis", "type": "database", "label": "Redis", "sublabel": "multi-AZ cache", "pos": [840, 195], "size": [126, 62], "tag": "platform" },
{ "id": "postgres", "type": "database", "label": "PostgreSQL", "sublabel": "primary / encrypted", "pos": [840, 405], "size": [126, 62], "tag": "data team" },
{ "id": "events", "type": "messagebus", "label": "Event Bus", "sublabel": "orders.v1", "pos": [1040, 300], "size": [126, 60], "tag": "platform" },
{ "id": "worker", "type": "backend", "label": "Workers", "sublabel": "private workload", "pos": [1190, 300], "size": [126, 60], "tag": "app team" },
{ "id": "replica", "type": "database", "label": "DR Replica", "sublabel": "eu-west-1", "pos": [1040, 578], "size": [126, 62], "tag": "data team" },
{ "id": "audit", "type": "cloud", "label": "Audit Archive", "sublabel": "immutable objects", "pos": [1190, 450], "size": [126, 62], "tag": "security" },
{ "id": "observability", "type": "external", "label": "Observability", "sublabel": "metrics + traces", "pos": [1190, 85], "size": [126, 62], "tag": "SRE" }
],
"boundaries": [
{ "kind": "region", "label": "AWS us-east-1 / production", "wraps": ["edge", "gateway", "api_a", "api_b", "redis", "postgres", "events", "worker", "audit"], "pad": 20 },
{ "kind": "security-group", "label": "private application network", "wraps": ["api_a", "api_b", "redis", "postgres", "events", "worker"], "pad": 14 },
{ "kind": "region", "label": "AWS eu-west-1 / disaster recovery", "wraps": ["replica"] },
{ "kind": "security-group", "label": "DR private subnet", "wraps": ["replica"], "pad": 14 }
],
"connections": [
{ "from": "clients", "to": "edge", "label": "HTTPS", "variant": "emphasis" },
{ "from": "edge", "to": "gateway", "label": "mTLS", "variant": "security" },
{ "from": "gateway", "to": "api_a", "label": "VPC route", "variant": "emphasis", "route": "orthogonal-h", "labelAt": [594, 275] },
{ "from": "gateway", "to": "api_b", "label": "VPC route", "variant": "emphasis", "route": "orthogonal-h", "labelAt": [594, 385] },
{ "from": "api_a", "to": "redis", "label": "cache", "route": "straight" },
{ "from": "api_b", "to": "postgres", "label": "SQL", "route": "straight" },
{ "from": "api_a", "to": "events", "label": "publish", "variant": "dashed", "fromSide": "top", "toSide": "top", "via": [[698, 170], [1103, 170]] },
{ "from": "api_b", "to": "events", "variant": "dashed", "fromSide": "top", "toSide": "bottom", "via": [[698, 380], [1103, 380]] },
{ "from": "events", "to": "worker", "variant": "emphasis" },
{ "from": "postgres", "to": "replica", "label": "cross-region WAL", "variant": "security", "route": "orthogonal-v", "labelAt": [1003, 529] },
{ "from": "worker", "to": "audit", "label": "evidence", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "labelDy": 58 },
{ "from": "worker", "to": "observability", "label": "OTLP", "variant": "dashed", "route": "orthogonal-v" }
],
"cards": [
{ "dot": "cyan", "title": "Runtime Ownership", "items": ["Platform owns the edge, gateway, cache, and event bus", "Application teams own API pods and workers", "Data owns primary and disaster-recovery state"] },
{ "dot": "rose", "title": "Named Crossings", "items": ["Public HTTPS terminates at the managed edge", "mTLS crosses into the application network", "Cross-region WAL is explicit and encrypted"] },
{ "dot": "emerald", "title": "Operational Evidence", "items": ["Workers emit traces to SRE-owned observability", "Audit evidence lands in immutable storage", "Unknown placement should remain marked, never invented"] }
]
}
schema_version: 1
diagram_type: architecture
meta:
title: Production Deployment Ownership
output: examples/production-deployment.html
visual_preset: blueprint
animation: trace
quality_profile: showcase
engineering_profile: deployment-ownership
components:
- id: clients
type: external
label: Customers
sublabel: web + mobile
pos: [38, 300]
size: [122, 60]
- id: edge
type: cloud
label: Global Edge
sublabel: CDN + WAF
pos: [230, 300]
size: [126, 60]
tag: edge team
- id: gateway
type: security
label: API Gateway
sublabel: public :443
pos: [430, 300]
size: [128, 60]
tag: platform
- id: api_a
type: backend
label: API Pods / AZ-a
sublabel: private subnet
pos: [630, 195]
size: [136, 62]
tag: app team
- id: api_b
type: backend
label: API Pods / AZ-b
sublabel: private subnet
pos: [630, 405]
size: [136, 62]
tag: app team
- id: redis
type: database
label: Redis
sublabel: multi-AZ cache
pos: [840, 195]
size: [126, 62]
tag: platform
- id: postgres
type: database
label: PostgreSQL
sublabel: primary / encrypted
pos: [840, 405]
size: [126, 62]
tag: data team
- id: events
type: messagebus
label: Event Bus
sublabel: orders.v1
pos: [1040, 300]
size: [126, 60]
tag: platform
- id: worker
type: backend
label: Workers
sublabel: private workload
pos: [1190, 300]
size: [126, 60]
tag: app team
- id: replica
type: database
label: DR Replica
sublabel: eu-west-1
pos: [1040, 578]
size: [126, 62]
tag: data team
- id: audit
type: cloud
label: Audit Archive
sublabel: immutable objects
pos: [1190, 450]
size: [126, 62]
tag: security
- id: observability
type: external
label: Observability
sublabel: metrics + traces
pos: [1190, 85]
size: [126, 62]
tag: SRE
boundaries:
- kind: region
label: AWS us-east-1 / production
wraps:
- edge
- gateway
- api_a
- api_b
- redis
- postgres
- events
- worker
- audit
pad: 20
- kind: security-group
label: private application network
wraps:
- api_a
- api_b
- redis
- postgres
- events
- worker
pad: 14
- kind: region
label: AWS eu-west-1 / disaster recovery
wraps:
- replica
- kind: security-group
label: DR private subnet
wraps:
- replica
pad: 14
connections:
- from: clients
to: edge
label: HTTPS
variant: emphasis
- from: edge
to: gateway
label: mTLS
variant: security
- from: gateway
to: api_a
label: VPC route
variant: emphasis
route: orthogonal-h
labelAt: [594, 275]
- from: gateway
to: api_b
label: VPC route
variant: emphasis
route: orthogonal-h
labelAt: [594, 385]
- from: api_a
to: redis
label: cache
route: straight
- from: api_b
to: postgres
label: SQL
route: straight
- from: api_a
to: events
label: publish
variant: dashed
fromSide: top
toSide: top
via:
- [698, 170]
- [1103, 170]
- from: api_b
to: events
variant: dashed
fromSide: top
toSide: bottom
via:
- [698, 380]
- [1103, 380]
- from: events
to: worker
variant: emphasis
- from: postgres
to: replica
label: cross-region WAL
variant: security
route: orthogonal-v
labelAt: [1003, 529]
- from: worker
to: audit
label: evidence
variant: dashed
fromSide: bottom
toSide: top
labelDy: 58
- from: worker
to: observability
label: OTLP
variant: dashed
route: orthogonal-v
cards:
- dot: cyan
title: Runtime Ownership
items:
- Platform owns the edge, gateway, cache, and event bus
- Application teams own API pods and workers
- Data owns primary and disaster-recovery state
- dot: rose
title: Named Crossings
items:
- Public HTTPS terminates at the managed edge
- mTLS crosses into the application network
- Cross-region WAL is explicit and encrypted
- dot: emerald
title: Operational Evidence
items:
- Workers emit traces to SRE-owned observability
- Audit evidence lands in immutable storage
- Unknown placement should remain marked, never invented
+32 -25
View File
@@ -49,36 +49,31 @@ export async function run(): Promise<void> {
await waitFor('activation', () => extension.isActive);
});
await check('.archify files open as JSON with the preview available', async () => {
const doc = await vscode.workspace.openTextDocument(workspaceFile('web-platform.archify'));
assert.equal(doc.languageId, 'json');
await vscode.window.showTextDocument(doc);
await vscode.commands.executeCommand('archify.showPreviewToSide');
const tabs = await waitFor('.archify preview tab', () => {
const found = webviewTabs('archify.preview').filter((tab) => /web-platform\.archify/.test(tab.label));
return found.length > 0 && found;
});
assert.equal(tabs.length, 1);
await check('clicking a .archify file opens the diagram, not the text', async () => {
const uri = workspaceFile('web-platform.archify');
await vscode.commands.executeCommand('vscode.open', uri);
const tab = await waitFor('diagram editor tab', () =>
vscode.window.tabGroups.all
.flatMap((group) => group.tabs)
.find((t) => t.input instanceof vscode.TabInputCustom && t.input.viewType === 'archify.diagram' && t.input.uri.toString() === uri.toString()),
);
assert.ok(tab.isActive, 'the diagram tab is active');
assert.equal(vscode.window.activeTextEditor?.document.uri.toString() === uri.toString(), false, 'no text editor was opened for it');
});
await check('.archify completion comes from the schema of its diagram_type', async () => {
const doc = await vscode.workspace.openTextDocument(workspaceFile('web-platform.archify'));
await vscode.window.showTextDocument(doc);
// Complete the value of the first component's "type".
const offset = doc.getText().indexOf('"type": "') + '"type": "'.length;
const position = doc.positionAt(offset);
let labels: string[] = [];
for (let attempt = 0; attempt < 50 && !labels.includes('backend'); attempt += 1) {
const list = await vscode.commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', doc.uri, position);
labels = list.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label).replace(/"/g, ''));
if (!labels.includes('backend')) await new Promise((resolve) => setTimeout(resolve, 300));
}
assert.ok(labels.includes('backend') && labels.includes('database'), `offered: ${labels.join(', ')}`);
await check('.archify files are YAML and Show Source opens the text beside the diagram', async () => {
const uri = workspaceFile('web-platform.archify');
await vscode.commands.executeCommand('archify.showSource');
const editor = await waitFor('source text editor', () =>
vscode.window.visibleTextEditors.find((e) => e.document.uri.toString() === uri.toString()),
);
assert.equal(editor.document.languageId, 'yaml');
assert.match(editor.document.getText(), /^schema_version: 1/m);
});
await check('.archify problems are reported against the file', async () => {
await check('.archify validation problems are reported against the YAML', async () => {
const good = await vscode.workspace.openTextDocument(workspaceFile('cache-miss.archify'));
const broken = good.getText().replace('"participants"', '"actors"');
const broken = good.getText().replace(/^participants:/m, 'actors:');
const target = workspaceFile('broken.archify');
await vscode.workspace.fs.writeFile(target, new TextEncoder().encode(broken));
const doc = await vscode.workspace.openTextDocument(target);
@@ -90,6 +85,18 @@ export async function run(): Promise<void> {
assert.ok(diagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error));
});
await check('.archify YAML syntax errors are reported at their line', async () => {
const target = workspaceFile('syntax.archify');
await vscode.workspace.fs.writeFile(target, new TextEncoder().encode('schema_version: 1\ndiagram_type: architecture\nmeta: [unclosed\n'));
const doc = await vscode.workspace.openTextDocument(target);
await vscode.window.showTextDocument(doc);
const diagnostics = await waitFor('yaml syntax diagnostics', () => {
const found = vscode.languages.getDiagnostics(doc.uri).filter((d) => d.source === 'archify' && d.message.startsWith('YAML:'));
return found.length > 0 && found;
});
assert.ok(diagnostics[0].range.start.line >= 2, `reported on line ${diagnostics[0].range.start.line + 1}`);
});
await check('opens a live preview beside the source', async () => {
await vscode.commands.executeCommand('archify.showPreviewToSide', workspaceFile('production-deployment.architecture.json'));
const tabs = await waitFor('preview tab', () => {
+2 -2
View File
@@ -24,7 +24,7 @@ function load(name: string) {
const text = fs.readFileSync(path.join(fixtures, name), 'utf8');
const source = readDiagramSource(text, name);
assert.ok(source, `${name} is a diagram source`);
return { ...source, text };
return source;
}
for (const name of fs.readdirSync(fixtures).filter((file) => !file.startsWith('mco-'))) {
@@ -46,7 +46,7 @@ test('reports schema problems with JSON pointer paths', async () => {
test('render failure returns the CLI error', async () => {
const text = JSON.stringify({ schema_version: 1, diagram_type: 'architecture', meta: {}, components: [] });
const result = await renderDiagram(runtime(), { type: 'architecture', diagram: JSON.parse(text), text }, 'fallback');
const result = await renderDiagram(runtime(), { type: 'architecture', diagram: JSON.parse(text), format: 'json', json: text }, 'fallback');
assert.equal(result.ok, false);
assert.match(result.error ?? '', /schema validation failed/);
});
+3 -2
View File
@@ -3,6 +3,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { test } from 'node:test';
import Ajv2020 from 'ajv/dist/2020';
import { parse as parseYaml } from 'yaml';
const root = path.resolve(__dirname, '..', '..');
const schemas = path.join(root, 'schemas');
@@ -27,14 +28,14 @@ test('bundled schemas carry no absolute $id', () => {
test('the .archify schema accepts every fixture', () => {
const validate = validator();
for (const file of fs.readdirSync(fixtures)) {
const ok = validate(JSON.parse(fs.readFileSync(path.join(fixtures, file), 'utf8')));
const ok = validate(parseYaml(fs.readFileSync(path.join(fixtures, file), 'utf8')));
assert.ok(ok, `${file}: ${JSON.stringify(validate.errors?.slice(0, 3))}`);
}
});
test('the .archify schema applies the schema of the declared type', () => {
const validate = validator();
const sequence = JSON.parse(fs.readFileSync(path.join(fixtures, 'cache-miss.archify'), 'utf8'));
const sequence = parseYaml(fs.readFileSync(path.join(fixtures, 'cache-miss.archify'), 'utf8'));
// A sequence diagram is not a valid architecture diagram.
assert.equal(validate({ ...sequence, diagram_type: 'architecture' }), false);
assert.equal(validate({ ...sequence, diagram_type: 'poster' }), false);