# How to Perform Semantic Checks on Archify Workflow Diagrams: A Complete Guide

> Learn how to perform semantic checks on Archify workflow diagrams using the semanticChecks property for allowedRoots, allowedTerminals, requiredEdges, and requiredPaths rules.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-09-06

---

**Archify validates workflow diagrams against a semantic contract declared in the diagram's JSON source under the `semanticChecks` property, which supports `allowedRoots`, `allowedTerminals`, `requiredEdges`, and `requiredPaths` rules.**

Archify's workflow renderer lets you enforce architectural correctness directly in your diagram definitions. By adding a `semanticChecks` block to your workflow JSON, you can prevent accidental orphan nodes, mandate critical dependencies, and guarantee reachability conditions—before the compiler ever produces an SVG.

## What Are Semantic Checks in Archify?

Semantic checks in Archify are **declarative constraints** that validate the structure of your workflow graph. Unlike syntactic validation (which ensures your JSON is well-formed), semantic checks verify that the topology matches your intended design.

The contract is evaluated by `semanticContractDiagnostics` in [`archify/renderers/workflow/workflow‑compiler.mjs`](https://github.com/tt-a1i/archify/blob/main/archify/renderers/workflow/workflow-compiler.mjs#L638-L652). This function runs automatically when you invoke `archify compile workflow …` and will abort rendering if any constraint is violated.

## The Four Semantic Contract Fields

Your `semanticChecks` object can contain any combination of these optional fields:

- **`allowedRoots`** — Node IDs permitted to have **no incoming edge**. Use this to declare intentional start nodes like "CI-trigger" or "Git Push".

- **`allowedTerminals`** — Node IDs permitted to have **no outgoing edge**. Use this to declare intentional end nodes like "Deploy" or "Archive".

- **`requiredEdges`** — Array of `{ from, to }` objects that must exist **exactly** as authored edges. Use this to force direct dependencies.

- **`requiredPaths`** — Array of `{ from, to }` objects requiring a **directed path** (with any number of intermediate hops). Use this to enforce reachability without dictating the route.

## How Semantic Validation Works

According to the Archify source code, `semanticContractDiagnostics` executes five validation passes:

1. **Collect node IDs** and build adjacency maps from the `nodes` and `edges` arrays.

2. **Validate references** — Any ID in the contract missing from the diagram triggers `workflow/semantic-node-reference`.

3. **Check unexpected roots/terminals** — Unlisted nodes without incoming/outgoing edges raise `workflow/unexpected-root` or `workflow/unexpected-terminal`.

4. **Ensure required edges** exist exactly as specified; missing edges produce `workflow/required-edge`.

5. **Verify required paths** using breadth-first search; unreachable pairs generate `workflow/required-path`.

All failures are emitted as machine-readable diagnostics containing: `code`, `message`, `subject` (with path), `evidence`, and `supportedFixes`.

## Writing a Workflow with Semantic Checks

Here is a complete, valid workflow JSON that uses all four contract fields:

```json
{
  "schema_version": 2,
  "diagram_type": "workflow",
  "lanes": [{ "id": "dev", "label": "Dev" }],
  "nodes": [
    { "id": "trigger", "label": "Git Push", "lane": "dev", "col": 1 },
    { "id": "build",   "label": "Build",    "lane": "dev", "col": 2 },
    { "id": "test",    "label": "Test",     "lane": "dev", "col": 3 },
    { "id": "deploy",  "label": "Deploy",   "lane": "dev", "col": 4 }
  ],
  "edges": [
    { "id": "e1", "from": "trigger", "to": "build", "label": "" },
    { "id": "e2", "from": "build",   "to": "test",   "label": "" },
    { "id": "e3", "from": "test",    "to": "deploy", "label": "" }
  ],
  "semanticChecks": {
    "allowedRoots": ["trigger"],
    "allowedTerminals": ["deploy"],
    "requiredEdges": [{ "from": "build", "to": "test" }],
    "requiredPaths": [{ "from": "trigger", "to": "deploy" }]
  }
}

```

Compile with:

```bash
node archify/bin/archify.mjs compile workflow my.json out.html

```

This succeeds because:
- Only `trigger` lacks an incoming edge (per `allowedRoots`)
- Only `deploy` lacks an outgoing edge (per `allowedTerminals`)
- The edge `build → test` exists (per `requiredEdges`)
- A path exists from `trigger` to `deploy` (per `requiredPaths`)

## Interpreting Semantic Check Failures

When a contract is violated, Archify produces detailed diagnostics. For example, omitting the `build → test` edge while keeping its requirement yields:

```json
{
  "code": "workflow/required-edge",
  "message": "Workflow semantic contract requires edge \"build\" -> \"test\", but no authored edge matches it.",
  "subject": { "diagramType":"workflow", "from":"build", "to":"test", "path":"/semanticChecks/requiredEdges/0" },
  "evidence": { "authoredEdgeCount":3 },
  "supportedFixes": ["add an edge from \"build\" to \"test\" without deleting the semantic requirement"]
}

```

Fix options are explicit: add the missing edge, or remove the contract entry if the requirement is no longer valid.

## Testing Semantic Contracts Programmatically

Archify's test suite in [`archify/test/workflow-semantic-contract.test.mjs`](https://github.com/tt-a1i/archify/blob/main/archify/test/workflow-semantic-contract.test.mjs) demonstrates how to validate contracts in your own tests:

```js
test('semanticChecks rejects an undeclared root', () => {
  const workflow = { /* … nodes/edges … */ };
  const result = compileWorkflow({ workflow, discoverFixes: false });
  assert.equal(result.diagnostics[0].code, 'workflow/unexpected-root');
});

```

Use this pattern to gate CI pipelines: compile workflows and assert `diagnostics.length === 0` before allowing deployment.

## Key Source Files for Semantic Checks

| File | Purpose |
|------|---------|
| [`workflow‑compiler.mjs`](https://github.com/tt-a1i/archify/blob/main/archify/renderers/workflow/workflow-compiler.mjs) | Contains `semanticContractDiagnostics` (lines 638–652), the core validation engine |
| [`workflow-semantic-contract.test.mjs`](https://github.com/tt-a1i/archify/blob/main/archify/test/workflow-semantic-contract.test.mjs) | Comprehensive test suite showing valid and invalid contract configurations |
| [[`workflow/README.md`](https://github.com/tt-a1i/archify/blob/main/workflow/README.md)](https://github.com/tt-a1i/archify/blob/main/archify/renderers/workflow/README.md) | Renderer documentation with field descriptions and examples |
| [[`examples/workflow-agent-tool-call.html`](https://github.com/tt-a1i/archify/blob/main/examples/workflow-agent-tool-call.html)](https://github.com/tt-a1i/archify/blob/main/examples/workflow-agent-tool-call.html) | Live example demonstrating semantic contracts in production diagrams |

## Summary

- **Semantic checks** in Archify are declared via the `semanticChecks` property in workflow JSON.

- **Four constraint types** cover start nodes (`allowedRoots`), end nodes (`allowedTerminals`), direct dependencies (`requiredEdges`), and reachability (`requiredPaths`).

- **Validation runs at compile time** via `semanticContractDiagnostics` in `workflow‑compiler.mjs`, blocking rendering on any violation.

- **Diagnostics are machine-readable** with structured evidence and suggested fixes, enabling automated CI/CD integration.

- **Testability is built-in** through the `compileWorkflow` API, letting you assert contract compliance programmatically.

## Frequently Asked Questions

### What happens if a node ID in my semantic contract doesn't exist?

Archify emits a `workflow/semantic-node-reference` diagnostic with the invalid ID and its location in the contract. The compiler treats this as a hard error and aborts rendering until the reference is corrected or removed.

### Can I use semantic checks without lanes?

Yes. The `semanticChecks` contract operates on the graph structure formed by `nodes` and `edges`. Lanes are a visual organization feature and do not affect semantic validation logic.

### How do requiredPaths differ from requiredEdges?

**`requiredEdges`** demands a direct, single-hop connection between two nodes, while **`requiredPaths`** only requires that a directed path exists—any number of intermediate nodes and edges may appear between the start and end. Use `requiredEdges` to enforce immediate dependencies; use `requiredPaths` to guarantee overall reachability without prescribing the route.

### Will semantic checks slow down compilation?

No. According to the implementation in `workflow‑compiler.mjs`, validation uses efficient adjacency maps and BFS for path queries. The overhead is negligible for typical workflow sizes, and checks can be disabled for development if needed by omitting the `semanticChecks` property entirely.