# How Archify's Validation System Works: Schema Checks and Composition Checks Explained

> Discover how Archify's validation system ensures architecture diagram accuracy through schema and composition checks. Learn about its two-stage process and CI integration.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: deep-dive
- Published: 2026-08-06

---

**Archify validates every architecture diagram in two independent stages—schema validation against a JSON-IR type system, followed by geometric composition checks on the rendered SVG—producing a validation receipt that powers CI gates and proof galleries.**

Archify's validation system ensures that authored diagrams meet both structural and visual quality standards before they reach production. The system is implemented across `archify/renderers/shared/validator.mjs` and `archify/renderers/shared/geometry.mjs`, with the CLI at `archify/bin/archify.mjs` orchestrating the full pipeline. This article breaks down how both validation stages work and what each composition check evaluates.

---

## Schema Validation: Type-Safe JSON-IR Verification

The first stage ensures your diagram data conforms to the expected structure for its type. This check is **fail-closed**: any schema error aborts the pipeline immediately.

### How Schema Validation Runs

1. **Load the validator** – `validateSchema(diagramType, data)` imports the pre-compiled AJV function from `archify/renderers/shared/generated-validators.mjs`.
2. **Execute AJV** – The validator runs against your diagram's JSON-IR. If `validate(data)` returns false, AJV populates `validate.errors`.
3. **Format diagnostics** – `formatErrors` builds human-readable messages, while `annotatedPath` enriches JSON pointers with node identifiers (e.g., `/nodes/3 (id: "router")/label`).
4. **Abort on failure** – `throwDiagnosticError` creates a structured diagnostic object with `code`, `severity`, `subject`, `evidence`, and `supportedFixes`, then halts execution.

The schema validator is **deterministic** and environment-independent—it is compiled ahead of time and bundled with the package.

### Running Schema Validation Programmatically

```javascript
import { validateSchema } from './archify/renderers/shared/validator.mjs';
import fs from 'fs';

const diagram = JSON.parse(fs.readFileSync('my-diagram.json'));
try {
  validateSchema('workflow', diagram);   // throws on any schema error
  console.log('✅ Schema OK');
} catch (err) {
  console.error('❌ Schema validation failed:', err.diagnostics);
}

```

---

## Composition Checks: Geometric Quality Validation

After the deterministic renderer produces SVG geometry, Archify extracts attributes like `data-composition-points` and `data-composition-frame-*` to run geometric **composition checks**. These evaluate visual quality and structural soundness. All checks are implemented in `archify/renderers/shared/geometry.mjs`.

### The Eight Composition Checks

| Check | Measurement | Severity Behavior |
|-------|-------------|-----------------|
| **Proper crossing** | X-shaped edge intersections | Warning in `showcase` profile, error in `standard` |
| **Ambiguous corridor** | Overlapping parallel edge segments that create indistinguishable paths | Warning or error depending on profile |
| **Label route clearance** | Minimum distance between relationship label routes and intersecting components | Warning or error |
| **Container-border runs** | Edge crossings of structural frame borders (regions, groups, lanes) | Warning or error when excessive |
| **Short interior segment** | Edge segments shorter than `segmentPx` threshold | Warning or error |
| **Micro segment** | Sub-pixel segments below `microSegmentPx` that cause rendering artifacts | Warning only |
| **Bend count & stretch** | Route complexity metrics—maximum bends, routes over suggested bend count, overall stretch | Used for `rhythm` profile |
| **Metrics aggregation** | Normalized summary via `routeBudgetMetrics` including all counts and `suggestedLimits` per profile | Summary output |

### Key Functions in `geometry.mjs`

- `collectAmbiguousCorridors()` → `cleanAmbiguousCorridorProblems()`
- `collectBorderRuns()` → `cleanBorderRunProblems()`
- `collectLabelRouteClearance()` → `cleanLabelRouteClearanceProblems()`
- `routeBudgetMetrics()` – returns aggregate statistics and threshold guidance

Each `collect*` function gathers raw geometry; each `clean*Problems` function transforms that data into structured **issue objects** with `code`, `severity`, `message`, and optional `threshold`.

### Running Composition Checks Programmatically

```javascript
import {
  collectAmbiguousCorridors,
  cleanAmbiguousCorridorProblems,
  collectBorderRuns,
  cleanBorderRunProblems,
  collectLabelRouteClearance,
  cleanLabelRouteClearanceProblems,
  routeBudgetMetrics
} from './archify/renderers/shared/geometry.mjs';

const svg = parseSVG(fs.readFileSync('my-diagram.svg'));
const composition = {};

composition.ambiguousCorridors = cleanAmbiguousCorridorProblems(
  collectAmbiguousCorridors(svg)
);
composition.containerBorderRuns = cleanBorderRunProblems(
  collectBorderRuns(svg)
);
composition.labelRouteClearance = cleanLabelRouteClearanceProblems(
  collectLabelRouteClearance(svg)
);
composition.metrics = routeBudgetMetrics(svg);

console.log('Composition summary:', composition);

```

---

## The Validation Receipt: Merging Results

The CLI merges schema and composition results into a single **validation receipt** (`receipt.validation`) written alongside each rendered artifact.

### Receipt Structure

```javascript
{
  ok: boolean,                          // true if both stages pass
  checksPassed: number,
  checkCount: number,
  compositionProfile: 'showcase' | 'standard' | 'rhythm',
  compositionStatus: 'pass' | 'fail',   // pass = warnings only, no errors
  composition: {
    issues: [/* issue objects */],
    metrics: { /* routeBudgetMetrics output */ }
  }
}

```

**Status determination**: `compositionStatus` is **pass** only when every issue has `"warning"` severity. Any `"error"` flips it to **fail**. The `compositionProfile` is derived from the rendering profile.

### CLI Output Example

```text
9/9 artifact checks; composition showcase: PASS·SHA256 a1b2c3…

```

### Gallery Integration

`scripts/build-gallery.mjs` injects receipt data into HTML proof galleries:

```html
<div class="receipt-cell">
  <span class="receipt-label">Composition</span>
  <span class="receipt-value ok"
        title="1 crossings · 0 border runs · 0 micro segments · 0 cramped turns">
    SHOWCASE · PASS
  </span>
</div>

```

---

## Full Validation Pipeline in Code

This illustrates what `archify/bin/archify.mjs` executes internally:

```javascript
import { validateSchema } from './archify/renderers/shared/validator.mjs';
import { runCompositionChecks } from './archify/renderers/shared/geometry.mjs';

function createReceipt(diagramType, diagram, renderedSvg) {
  // 1️⃣ Schema validation
  try {
    validateSchema(diagramType, diagram);
  } catch (e) {
    return { ok: false, schema: e.diagnostics };
  }

  // 2️⃣ Composition validation
  const composition = runCompositionChecks(renderedSvg);

  // 3️⃣ Aggregate receipt
  const checksPassed = composition.issues.filter(i => i.severity === 'warning').length;
  const checkCount = composition.issues.length;

  return {
    ok: composition.status === 'pass',
    checksPassed,
    checkCount,
    compositionProfile: composition.profile,
    compositionStatus: composition.status,
    composition,
  };
}

```

---

## Profile-Based Severity Mapping

| Profile | Purpose | Strictness |
|---------|---------|------------|
| **showcase** | Highest visual quality, publication-ready | Warnings for most issues |
| **standard** | Production diagrams | Errors for crossings, corridors, clearance |
| **rhythm** | Emphasizes clean routing cadence | Errors for excessive bends or stretch |

The `routeBudgetMetrics` function provides `suggestedLimits` objects describing thresholds for each profile, enabling profile-aware CI gates.

---

## Summary

- **Schema validation** in `archify/renderers/shared/validator.mjs` provides fail-closed type checking using pre-compiled AJV validators from `generated-validators.mjs`.
- **Composition checks** in `archify/renderers/shared/geometry.mjs` evaluate eight geometric quality criteria including crossings, corridors, label clearance, and border runs.
- **Validation receipts** merge both stages into `receipt.validation`, consumed by the CLI, gallery scripts (`scripts/build-gallery.mjs`), and CI gates (`scripts/package-smoke.mjs`).
- **Profile-driven severity** allows `showcase`, `standard`, and `rhythm` modes to tune strictness for different output contexts.

---

## Frequently Asked Questions

### What happens if schema validation fails?

The pipeline aborts immediately. `throwDiagnosticError` creates structured diagnostics with annotated JSON pointers, and no SVG is rendered. This fail-closed design prevents invalid data from reaching composition checks.

### How does Archify detect ambiguous corridors?

The `collectAmbiguousCorridors` function in `geometry.mjs` identifies overlapping parallel edge segments that create visually indistinguishable paths. `cleanAmbiguousCorridorProblems` converts these into issue objects with severity determined by the active composition profile.

### Can I run validation without using the CLI?

Yes. Import `validateSchema` and the composition helpers directly from `archify/renderers/shared/validator.mjs` and `geometry.mjs` to build custom pipelines. The receipt format documented above is stable for下游 tooling.

### What's the difference between warnings and errors in composition checks?

Errors fail the composition stage (`compositionStatus: 'fail'`), while warnings allow it to pass. The threshold for each check varies by profile—`standard` and `rhythm` promote more warnings to errors than `showcase`.