Files
archify-vscode-ext/references/authoring.md
root-at-skic 55894fbade Ship the three C4 examples and model:scaffold --example
- examples/: c4-standard-web-app, c4-e-shop (was online-shop),
  c4-event-based-integration; the same models the extension offers
  under Archify | New Model…
- model-scaffold --example <name> copies one and renames id, name,
  description and the overview title; never overwrites
- tests cover every example and the copy; docs updated
2026-10-19 12:30:07 +03:00

11 KiB

Authoring Archify diagrams

What an agent needs to write a diagram that validates and reads well. The schemas under scripts/vendor/archify/schemas/ are the authority; read the kind's schema and common.schema.json before using a field not listed here. Archify rejects unknown keys everywhere (additionalProperties: false).

Every diagram

schema_version: 1            # 1 for architecture, sequence, dataflow; 2 for new workflow and lifecycle
diagram_type: architecture   # architecture | workflow | sequence | dataflow | lifecycle
meta:
  title: Containers          # required
  output: containers.html    # required: relative POSIX path ending in .html, no "..", no drive letters

Optional meta keys: subtitle (omit unless asked), viewBox: [w, h] (omit for architecture; Archify measures), legend (mode: auto|all|hidden, entries: { backend: { label: Service } } to rename), locale (en, zh-CN; others need translations), visual_preset (classic default, signal-flow, blueprint, editorial; only on request), quality_profile (standard, showcase), animation: trace, repository plus node sources for source-backed diagrams, cards (summary blocks under the diagram: dot, title, items).

Ids match ^[a-zA-Z][a-zA-Z0-9_-]*$ and are unique in their collection. Relationship ids are optional (they give stable #relation= links).

Node fields (all kinds)

Field Values
type frontend, backend, database, cloud, security, messagebus, external (lifecycle states: start, active, waiting, decision, success, failure, neutral, external)
label required; keep it short, the box is small
sublabel one fact: technology, protocol, role
tag small badge text (not in sequence participants)
icon corner symbol: calendar, clock, person, briefcase, flag, moon, a type name, or none
brand an Archify brand id (see Brands)
sources 1 to 3 { path, line, end_line, label } repository references, with meta.repository

type sets colour and legend entry. Pick by role: people and outside systems external; the own system as one box cloud; UIs frontend; services and jobs backend; stores and caches database; queues and topics messagebus; auth, gateways, secrets security.

Architecture

Collections: components (required), connections, boundaries, cards, layout.

Placement is yours; Archify routes the lines but never moves boxes. Use free positions on this grid, which is what the templates and examples/c4-e-shop use:

  • box size: [130, 60] ([150, 60] for a system-level box with a longer label)
  • columns at x = 40, 250, 460, 670, 880 (step 210: an 80px clear gap between boxes)
  • rows at y = 60, 160, 260, 360, 460 (step 100: a 40px gap)

Rules of thumb that keep the validator quiet:

  • Place by connections first: main path left to right, neighbours adjacent; a store or branch directly above or below its owner, centred, so the edge is one straight segment; a return edge on the side without branches.
  • A horizontal label on the 80px gap fits about 9 ASCII characters (6.5px per character + 21px). Longer labels: widen the step (250), or set labelAt when the validator asks.
  • Label width inside a box: 6.5px per character + 13px must be under the box width; a 130px box takes about 18 characters. Sublabel: 5.4px per character + 8px.
  • A side with k connections needs 32 + 14 * (k - 1) px; spread a hub's partners over two or three sides.
  • External actors stay outside the boundary, including its padding.
  • Omit meta.viewBox; Archify sizes the canvas.

Connections: from, to, label, variant (default, emphasis for the main path, security, dashed), fromSide/toSide (left|right|top|bottom, pin only when needed), route (auto, straight, orthogonal-h, orthogonal-v), via: [[x, y], ...] (explicit waypoints, must leave the source outward on the pinned side), labelAt: [x, y], labelDx, labelDy, labelSegment, width.

Boundaries: kind: region | security-group, label, wraps: [ids], pad. A boundary states a real ownership, trust or deployment fact; it does not replace connections.

Alternative to pos: row/col per component with layout: { mode: grid, origin, cols, cellW, cellH, gapX, gapY }. Do not mix pos and row/col in one diagram: Archify stops with internal/unclassified instead of a readable error.

Workflow (schema_version 2)

schema_version: 2
diagram_type: workflow
meta: { title: Change approval, output: workflow.html }
lanes:
  - { id: author, label: Author }
  - { id: review, label: Reviewers }
mainPath: [draft, review, approved, released]
nodes:
  - { id: draft, lane: author, col: 0, type: frontend, label: Draft change }
  - { id: review, lane: review, col: 1, type: security, label: Review, sublabel: two approvals }
edges:
  - { id: submit, from: draft, to: review, label: submit }
  - { from: review, to: draft, label: changes requested, role: branch, variant: dashed }

col is 0..5, the logical step. mainPath lists the happy path in order; keep it monotonic. edges.role: main, branch, async, return, error. Optional phases and groups (fromCol, toCol) and yOffset per node (symmetric values like -90/0/90 inside one lane). meta.legend.entries renames types for non-software plans (see everyday-plan in the extension's templates: backend as "Time off", icons calendar, briefcase).

Sequence

schema_version: 1
diagram_type: sequence
meta: { title: Login, output: sequence.html, viewBox: [960, 520] }
participants:
  - { id: client, type: external, label: Client }
  - { id: api, type: backend, label: API }
messages:
  - { from: client, to: api, y: 160, label: POST /login, variant: emphasis }
  - { from: api, to: client, y: 210, label: 200 OK, variant: return }

Participants are columns in order; messages run top to bottom by y (50px apart is comfortable). variant adds return. Optional note per message, segments (from, to, label) and activations (participant, from, to). meta.column_fit: spread uses a wide viewBox for column distance.

Dataflow

schema_version: 1
diagram_type: dataflow
meta: { title: Sales reporting, output: dataflow.html, viewBox: [960, 420] }
stages: [{ label: Sources }, { label: Processing }, { label: Storage }]
nodes:
  - { id: shop, type: frontend, label: Web shop, stage: 0, row: 0 }
  - { id: etl, type: backend, label: Nightly ETL, stage: 1, row: 0 }
flows:
  - { from: shop, to: etl, label: orders }
  - { from: crm, to: etl, label: customers, classification: PII, variant: security }

Stages are columns (index into stages), rows separate parallel streams. Label only data contracts, classifications and cross-boundary movement.

Lifecycle (schema_version 2)

schema_version: 2
diagram_type: lifecycle
meta: { title: Order status, output: lifecycle.html }
lanes:
  - { id: main, label: Normal path }
  - { id: exits, label: Exits }
states:
  - { id: placed, type: start, label: Placed, lane: main, col: 0 }
  - { id: paid, type: active, label: Paid, lane: main, col: 1 }
  - { id: cancelled, type: failure, label: Cancelled, lane: exits, col: 0 }
transitions:
  - { id: pay, from: placed, to: paid }
  - { id: cancel, from: placed, to: cancelled, variant: security }

Each lane is a row (main first, terminal last); col 0..4 is one shared x grid, so put an exit in the column of the state it leaves. Every transition is authored, including the main path. A recoverable failure needs a real transition back. Omit viewBox in v2.

Brands

brand puts a product logo on a node. Use the canonical id from the bundled catalogue:

node scripts/vendor/archify/bin/archify.mjs brands "kafka" --json      # id apache-kafka
node scripts/vendor/archify/bin/archify.mjs brands --json              # all 107

Ids include postgresql, redis, mysql, mongodb, apache-kafka, rabbitmq, docker, kubernetes, terraform, node-js, react, vue, python, go, rust, typescript, spring, django, fastapi, github, gitlab, jenkins, grafana, prometheus, stripe, paypal, openai, anthropic, claude, google-cloud, cloudflare, vercel, supabase, snowflake, databricks. An unknown brand is a validation error; omit brand rather than guess. A URL brand needs archify brands capture <url> first (network) and the returned digest.

Validator output and fixes

task model:validate and task diagram:validate map Archify's diagnostics to the file, line and JSON pointer. Fix in this order, one coherent change at a time, validating after each:

Code or message Cause Fix
YAML: ... syntax error, usually : inside a plain scalar > block scalar or quotes
schema/additionalProperties an unknown key remove or rename it; model keys only in registries and ref nodes
schema/... other wrong enum, missing meta.output, bad id, schema_version not allowed for the kind read the message; the pointer names the node
Label "..." is wider than component label too long for size widen size[0] or shorten
Components "a" and "b" are less than 8px apart overlap use the suggested pos
clean-flow/edge-through-node an edge crosses an unrelated box move the box off the corridor, or fromSide/toSide, or via
clean-flow/endpoint-side-direction via leaves the box inward first waypoint must be outward on the pinned side
composition/label-gap, label_route_clearance labels collide with routes, boxes or each other enlarge the gap or set the suggested labelAt; never delete the label
layout/boundary-out-of-bounds, layout/route-out-of-bounds negative coordinates or an authored viewBox too small move inward, or remove/enlarge viewBox
composition/excessive-route-detour an explicit via is far longer than needed remove the via
Component ids must be unique duplicate id rename, or id: next to ref:
brand/unknown brand id not in the catalogue; the message lists the closest ids use one of them, or omit brand

--quality=showcase turns composition warnings (crossings, corridors, border runs, route rhythm) into failures; use it for diagrams that will be presented. --layout-json on the plain Archify CLI (archify validate architecture x.json --layout-json) prints the resolved boxes, routes and label positions when a repair needs measurements; get x.json from task model:resolve.

Composition

  • One obvious main path, variant: emphasis on it. Branches beside their owner. Stores below their writer.
  • Labels carry meaning (protocol, action, direction, async). Omit one only when both endpoints imply it.
  • Group only real boundaries. A boundary around one box needs a real isolation fact.
  • A C4 level answers one question; put details in the next level and link it with diagram:.
  • Title only; no subtitle, no preset, no legend overrides unless asked.