Files
archify-vscode-ext/references/authoring.md
T
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

170 lines
11 KiB
Markdown

# 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
```yaml
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 `id`s 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)
```yaml
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
```yaml
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
```yaml
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)
```yaml
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:
```bash
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.