feat: introduce archify skill for generating architecture diagrams
- Added a new Archify skill, enabling users to create polished architecture, workflow, sequence, data-flow, and lifecycle diagrams. - Implemented comprehensive functionality including rendering, validation, and delivery of diagrams in various formats. - Integrated a user-friendly command-line interface for generating and previewing diagrams. - Developed supporting files including package.json, LICENSE, and SKILL.md for documentation and licensing. - Added unit tests to ensure reliability and functionality of the new skill. These changes enhance the application by providing a structured approach to visualizing system architecture and workflows, improving user experience and data representation.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
# Workflow Renderer
|
||||
|
||||
Render `diagram_type: "workflow"` JSON files into the standard Archify HTML
|
||||
template.
|
||||
|
||||
```bash
|
||||
node archify/renderers/workflow/render-workflow.mjs input.workflow.json output.html
|
||||
```
|
||||
|
||||
The renderer validates input against `archify/schemas/workflow.schema.json`
|
||||
with the bundled standalone validator. No dependency installation is required.
|
||||
|
||||
If `output.html` is omitted, the renderer uses `meta.output` from the JSON file
|
||||
or falls back to `workflow.html` in the current working directory.
|
||||
|
||||
After rendering, run the artifact checker:
|
||||
|
||||
```bash
|
||||
node archify/scripts/check-render-output.mjs output.html
|
||||
```
|
||||
|
||||
It catches final-SVG issues that are easiest to see in a browser: non-finite
|
||||
SVG values, accidental two-point diagonal arrows, and arrows crossing the
|
||||
legend.
|
||||
|
||||
## Input
|
||||
|
||||
Workflow JSON files must set:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 2,
|
||||
"diagram_type": "workflow",
|
||||
"meta": {
|
||||
"title": "Agent Tool Call Workflow"
|
||||
},
|
||||
"lanes": [],
|
||||
"phases": [],
|
||||
"groups": [],
|
||||
"mainPath": [],
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"cards": []
|
||||
}
|
||||
```
|
||||
|
||||
Use `schema_version: 2` for new workflows. Its readable layout compiler treats
|
||||
every `col` as a logical rank in `0..5` and derives geometry from the measured
|
||||
document. `schema_version: 1` remains the fixed legacy contract for existing
|
||||
sources; valid v1 output is preserved byte-for-byte and never silently
|
||||
reinterpreted as v2.
|
||||
|
||||
Omit `meta.viewBox` for the common v2 case so the compiler can use intrinsic
|
||||
measured bounds. In v1, the omitted width remains fixed at 720 and height is
|
||||
derived from lane count. A complete worked example lives at
|
||||
`archify/examples/agent-tool-call.workflow.json`; its `schema_version` selects
|
||||
the applicable contract.
|
||||
|
||||
The schema lives at:
|
||||
|
||||
```text
|
||||
archify/schemas/workflow.schema.json
|
||||
```
|
||||
|
||||
## Migration and layout receipt
|
||||
|
||||
Migrate an existing v1 source into a separate v2 file:
|
||||
|
||||
```bash
|
||||
node archify/bin/archify.mjs migrate workflow old.json new.json --to-schema 2 --json
|
||||
```
|
||||
|
||||
Running the command again with its schema-v2 output as the new source is an
|
||||
idempotent verification pass: the destination bytes and geometry stay unchanged.
|
||||
|
||||
The command never overwrites the source by default. It maps absolute
|
||||
`via[*][0]`, `labelAt[0]`, and `channelX` values from legacy to solved rank
|
||||
space, preserves y coordinates unless a reported vertical constraint needs
|
||||
author input, expands an explicit viewBox only for an unambiguous containment
|
||||
repair, and writes the destination only after v2 compilation and artifact
|
||||
checks pass. Ambiguous explicit pins fail without producing the destination.
|
||||
|
||||
Inspect the stable author-facing v2 plan with:
|
||||
|
||||
```bash
|
||||
node archify/bin/archify.mjs validate workflow input.workflow.json --layout-json
|
||||
```
|
||||
|
||||
The receipt reports the selected contract, measured `viewBox` and
|
||||
`requiredViewBox`, solved columns, nodes, edges, labels, and causal diagnostics.
|
||||
It deliberately omits solver iterations and candidate scores.
|
||||
|
||||
## Legend
|
||||
|
||||
The default legend derives component kinds from `nodes[].type`. Supported
|
||||
`meta.legend.entries` keys, in stable order, are `frontend`, `backend`,
|
||||
`security`, `messagebus`, `database`, `cloud`, and `external`. Labels and
|
||||
visibility may be overridden through the shared legend contract; only kinds
|
||||
backed by rendered nodes receive Semantic Legend controls.
|
||||
|
||||
## Layout contracts
|
||||
|
||||
### Fixed v1
|
||||
|
||||
| Constant | Value |
|
||||
|----------|-------|
|
||||
| viewBox | default `[720, auto]` — auto height = 52 + lanes×104 + (lanes−1)×20 + 124 |
|
||||
| Lane frame | x 40, width 640, height 104, gap 20; first lane top at y 52 |
|
||||
| Lane title strip | top 30px of each lane; node boxes must stay below it |
|
||||
| Column centers (`col` 0–5) | x = 88, 220, 300, 430, 500, 625 |
|
||||
| Phase headers | Optional `phases[]` render above the first lane, spanning `fromCol..toCol` |
|
||||
| Lane groups | Optional `groups[]` frame parallel work or branch work inside one lane |
|
||||
| Exception lanes | Set `lane.variant: "exception"` for retry, denial, fallback, or failure paths |
|
||||
| Main path lint | Optional `mainPath[]` checks that happy-path steps have matching edges and do not move backward |
|
||||
| Default node | 92×52 (height 68 when `tag` is set) |
|
||||
| Node spacing | ≥8px between nodes in the same lane |
|
||||
| Edge length | straight segments must span ≥28px |
|
||||
| Legend row | y = lane bottom + 44; viewBox height must be ≥ legend y + 18 |
|
||||
|
||||
Column-center gaps are 132 / 80 / 130 / 70 / 125 px: columns 1↔2 (80px) and
|
||||
3↔4 (70px) cannot both hold default-width 92px nodes in the same lane. Such an
|
||||
invalid v1 source receives one causal `workflow/column-capacity` diagnostic and
|
||||
a verified migration-to-v2 repair; v1 never falls through to adaptive layout.
|
||||
|
||||
### Readable v2
|
||||
|
||||
| Invariant | Contract |
|
||||
|----------|----------|
|
||||
| Logical columns | `col` is an integer in `0..5`; pixel centers are measured output |
|
||||
| Adjacent-rank baseline | 120px center distance before document-specific constraints |
|
||||
| Same-lane node clearance | ≥8px when vertical node intervals overlap |
|
||||
| Facing direct edge | clear gap ≥`max(28px, measured label mask width + 8px)` |
|
||||
| Automatic route rhythm | direct segment ≥28px; endpoint stub ≥8px; interior turn segment ≥16px |
|
||||
| Implicit viewBox | intrinsic content bounds plus contract padding |
|
||||
| Explicit viewBox | containment capacity; too-small input reports exact `requiredViewBox` and contributors |
|
||||
|
||||
The compiler applies constraints only to actual related or overlapping
|
||||
same-lane nodes, so a wide node in an unrelated lane does not expand every
|
||||
rank. Legacy centers are a soft preference after correctness constraints, not
|
||||
a geometry promise. Phase and group frames derive from the solved rank bands.
|
||||
Automatic routes are normalized once and the same final scene drives
|
||||
validation and SVG serialization. Long automatic labels compare direct-gutter
|
||||
growth with a legal channel instead of widening every downstream rank. Measured
|
||||
multi-row legends participate in intrinsic height and explicit viewBox
|
||||
capacity.
|
||||
|
||||
Authored `via`, `labelAt`, `channelX`, and `channelY` are absolute hard pins in
|
||||
v2; an infeasible pin returns `workflow/explicit-pin-conflict` rather than being
|
||||
silently moved. `fromSide` and `toSide` remain direction constraints. A route
|
||||
preset restricts the automatic candidate family but is not itself an absolute
|
||||
coordinate pin. When either endpoint side is omitted, the v2 compiler chooses
|
||||
a feasible side; an authored side restricts that endpoint to the named port.
|
||||
|
||||
## Design Rules
|
||||
|
||||
- Use lanes for ownership or runtime boundaries.
|
||||
- Use phase headers for high-level story beats such as Intake, Plan, Execute, and Report.
|
||||
- Use groups for parallel checks, branch handling, or bounded work within a lane; every group must contain at least one node.
|
||||
- Use `lane.variant: "exception"` for human wait, denial, retry, fallback, and failure lanes instead of mixing those paths into the happy path.
|
||||
- Set `mainPath` when the diagram has a clear happy path; the renderer validates that consecutive ids have matching edges and move left-to-right.
|
||||
- Place nodes with lane IDs and `col` indexes in `0..5`, not raw SVG coordinates.
|
||||
- Preserve semantic edge labels. Readable v2 allocates measured label clearance;
|
||||
when a label does not fit, repair the reported capacity or route constraint
|
||||
instead of deleting meaning.
|
||||
- Use labels for decisions, approvals, protocols, async traces, return paths,
|
||||
and any other relationship meaning not fully implied by its endpoints.
|
||||
- Prefer route presets — `drop` (bend between lanes; `bias` 0–1 picks where),
|
||||
`outside-right`, `return-left`, `bottom-channel`, and `up-channel` — before
|
||||
using raw `via` points. `straight` and the default `auto` cover the rest.
|
||||
- Keep workflow examples compact enough to render well in narrow chat/browser
|
||||
previews.
|
||||
|
||||
### Optional semantic checks
|
||||
|
||||
Layout validation cannot infer domain truth from labels or cards. When source
|
||||
evidence establishes roots, terminals, mandatory direct relationships, or
|
||||
mandatory directed reachability, encode those facts in `semanticChecks`:
|
||||
|
||||
```json
|
||||
"semanticChecks": {
|
||||
"allowedRoots": ["request", "resource_catalog"],
|
||||
"allowedTerminals": ["reply", "audit_log"],
|
||||
"requiredEdges": [
|
||||
{ "from": "dispatch", "to": "dispatch_ledger" }
|
||||
],
|
||||
"requiredPaths": [
|
||||
{ "from": "event_ledger", "to": "runtime_host" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When `allowedRoots` or `allowedTerminals` is present, it is the complete allow
|
||||
list for zero-incoming or zero-outgoing nodes respectively. `requiredEdges`
|
||||
requires one exact authored direction; `requiredPaths` permits intermediate
|
||||
nodes but follows authored edge direction. These checks run before layout, do
|
||||
not alter SVG or receipt bytes, and must not be weakened merely to resolve a
|
||||
route or composition diagnostic. Omit fields whose domain facts are unknown.
|
||||
|
||||
Schema violations exit non-zero with path-prefixed messages annotated with the
|
||||
element's id or label. The renderer additionally fails when it can detect
|
||||
layout problems, including node overlap, nodes outside their lanes, invalid
|
||||
phase/group column ranges, empty groups, broken `mainPath` steps, unknown edge
|
||||
targets, labels colliding with nodes or other labels, labels wider than their
|
||||
node, legends outside the viewBox, or straight arrows that are too short to
|
||||
read cleanly. The shared Clean Flow Gate also rejects edges crossing unrelated
|
||||
nodes with 2px clearance; lanes, phases, and groups remain intentional
|
||||
pass-through containers. Text width is estimated CJK-aware: fullwidth glyphs
|
||||
count as two units.
|
||||
|
||||
Diagnostics are causal: a rank-capacity failure suppresses derivative short
|
||||
edge, endpoint-direction, and label-overlap findings. Every
|
||||
`supportedFixes[]` entry is verified by replanning the proposed edit, and a
|
||||
diagnostic never proposes removing a semantic label when label presence does
|
||||
not cause the failed invariant.
|
||||
|
||||
Set `meta.quality_profile` to `showcase` for polished delivery. Unrelated proper
|
||||
X crossings then fail with `composition/proper-crossing`; default `standard`
|
||||
keeps them as artifact-receipt warnings. Collinear lane corridors are outside
|
||||
the proper-X rule, but a separate gate warns in `standard` and fails in
|
||||
`showcase` when unrelated edges overlap for at least 8px. Shared semantic
|
||||
endpoints, point touches, and shorter overlaps remain valid. Showcase also
|
||||
rejects any route segment below 8px and any interior turn segment below 16px;
|
||||
ordinary 8–15px endpoint stubs remain valid for fixed lane gaps.
|
||||
Reference in New Issue
Block a user