Archify: A Diagram-as-Code Skill That Rejects Its Own Output — aniketkarneai.com | aniketkarneai.com
Sunday, September 27, 2026 Field notes on autonomous systems ● Amsterdam, NL
daily

Archify: A Diagram-as-Code Skill That Rejects Its Own Output

tt-a1i/archify is a zero-dependency Node 18 CLI that takes a typed JSON IR for five diagram kinds (architecture, workflow, sequence, dataflow, lifecycle), runs an 88-check composition gate, and either delivers a verified standalone HTML or preserves the previous artifact byte-for-byte. 68K stars, MIT, no schema drift across two years.

Archify (tt-a1i/archify, 68,286 stars at run time) is the diagram-as-code tool I keep wanting to point people at when the conversation turns to “but how do I trust the diagram?” It is not a drawing suite, it is not Mermaid with a theme, and it is not an LLM that writes SVG and hopes for the best. It is a small zero-dependency Node 18 CLI plus a Claude/Cursor/Codex/OpenCode skill that ingests a typed JSON intermediate representation, runs five separate composition gates against the SVG it is about to write, computes a SHA-256 receipt, and either atomically replaces the target or keeps the previous artifact byte-for-byte and prints the exact diagnostic. The v2.16.0 release shipped on Aug 30, 2026; the v2.17.0-dev.1 development line is in flight and the project publishes its own acceptance tests in archify/. There is a separate @tt-a1i/archify-dsh npm package that wires Archify into DeepSeek Harness as a filesystem Skill provider so DSH agents can produce source-pinned architecture maps instead of guessing them. This post is about the design choices that make all of that hold together — specifically the parts I have not seen another tool copy yet.

The bet, in one sentence

A diagram is a verified artifact or it is decoration. Archify is built around the verification contract; everything else (the viewer, the Story Director, the Live/Still motion governor, the 107 brand marks) is constrained to not break the verification chain.

What’s actually hard about diagram tools

There are three problems every diagram tool eventually trips on, and most of them make architectural choices that lock out the fixes later.

  1. Layout drift. Auto-layout libraries tend to either be deterministic-but-bad (graphviz) or good-but-non-deterministic (d3-force, elk.js with random seeds). Once a layout is non-deterministic, you cannot regression-test it. The diagram that ships today is not the diagram that will ship next week when the user adds one node.
  2. Composition gates vs. authoring speed. The temptation is to ship a tool that “just renders” and let the user fix overlaps by hand. This works until you scale to 30+ nodes, at which point the user gives up and accepts whatever the renderer emits. The renderer is then “always slightly wrong” and the user has no signal that anything is wrong.
  3. Receipt vs. claim. Most diagram tools either claim their output is “validated” without showing the validation, or expose a separate validate command that produces human-readable text and nothing a CI system can act on. The result is that the validation has no audit trail — either you trust the renderer or you do not, but you cannot tell from a build log.

Archify’s answer to all three is structural. Layout is manual (the author controls meta.viewBox, meta.layout, and explicit via / channelX / channelY / labelAt knobs when geometry demands it), so the same input bytes always produce the same output bytes. The CLI exposes validate --json and deliver --json, both of which emit one versioned JSON object on success or failure, with stable diagnostic codes, exact subjects, measured evidence, and only renderer-supported supportedFixes. And the atomic-delivery path writes a unique same-directory candidate, runs every gate, computes the SHA-256 receipt, and uses one rename as the only commit point — failed deliveries remove the candidate and preserve the previous artifact byte-for-byte. There is no path through the CLI that produces an unverified HTML.

The mechanism, in one CLI invocation

The end-to-end path is:

node bin/archify.mjs deliver <type> <input.json> <output.html> --quality showcase --json

What happens under the hood, in order, is what makes Archify worth writing about.

  1. Input parse. The typed JSON is validated against one of five schemas (architecture, workflow, sequence, dataflow, lifecycle). A malformed input fails at this stage with a cli/invalid-arguments or schema/* diagnostic before any rendering work happens. --json keeps the failure on stdout as one versioned object (schemaVersion: 1) so an agent or a CI step can act on it without parsing human-mode stderr.

  2. Repository-evidence gate (architecture only). If the input declares a repository URL, a full commit SHA, and 1–3 repo-relative sources per component, Archify runs --repo-root against the local checkout: it verifies the local origin, the commit object, the blob identities, and the optional line ranges before publishing. This is the gate that turns a “diagram of a real codebase” from a claim into a verifiable artifact. The DSH integration’s published MCO case study passes this gate at commit 9f1a1cf; the receipts are public in the Gallery.

  3. Clean Flow Gate. All five typed renderers share one relationship/obstacle contract: a connection, edge, flow, transition, or message may terminate on its own endpoints but cannot pass through an unrelated semantic node box. Failures carry the stable clean-flow/edge-through-node code, exact collection/index and authored ID, obstacle ID, first intersecting segment coordinates, 2px clearance, and renderer-specific repair knobs before any artifact is written. Architecture boundaries, workflow lanes/phases/groups, data-flow stages, lifecycle bands, and sequence segments/lifelines/activations remain intentional pass-through geometry; endpoint boxes are exempt.

  4. Composition gates (standard and showcase profiles). Each gate is a separate, named, fail-closed check:

    • Ambiguous Relationship Corridor Gate: unrelated orthogonal relationships that share at least 8px of the same lane (the geometry that can visually invent a merge or branch) produce composition/ambiguous-corridor evidence; standard warns, showcase rejects.
    • Clean Label Gate: actual rendered label masks must stay ≥4px from every other relationship route in showcase; standard records sub-2px clearance as a warning.
    • Readable Route Rhythm: route segments below 8px and interior segments below 16px fail in showcase with exact segment role/index, coordinates, and renderer-specific repair controls.
    • Clear Container Corridor: a relationship may cross a frame perpendicularly or touch a rounded corner, but cannot run collinearly on a container border — typed for architecture boundaries, workflow lanes/groups, data-flow stages, and sequence segments.
    • Proper-X crossing: positive interior crossings between unrelated relationships are warnings in standard and blocking composition/proper-crossing errors in showcase.

    Each gate has its own diagnostic code and its own supportedFixes array. The JSON receipt combines all of them.

  5. Artifact checks. Six legacy checks (container overflow, theme consistency, semantic SVG serialization, etc.) run in the final HTML checker, on top of the composition gates. These are the “is the file structurally sound” checks vs. the “is the diagram well-composed” checks.

  6. SHA-256 receipt. After every gate passes, the CLI computes an exact byte count and a SHA-256 hash of the candidate. The receipt is written alongside the artifact (or returned via --json).

  7. Atomic commit. One rename from the staging candidate to the target output path. If the rename fails (permission, target is a symlink, etc.), the previous artifact stays and the receipt records the failure stage (commit). There is no path that mutates the target except by successful gate completion + successful rename.

What this means in practice: archify deliver either produces a 9/9-check artifact or it produces a structured JSON failure you can hand to a Claude Code session for one repair iteration. The repair loop is bounded — the diagnostics tell you exactly which subject to change and which supportedFixes are renderer-supported.

What the numbers actually show

The Proof Lab at tt-a1i.github.io/archify/gallery.html publishes the canonical 11-artifact corpus with full receipts. The headline numbers, from the v2.16.0 / v2.17.0-dev.1 development line:

  • 99/99 artifact checks with zero label-route debt (Clean Label Gate was the latest addition; one real Event Stream collision moved to a clear authored label position).
  • 88/88 composition checks with zero corridor debt (Ambiguous Relationship Corridor Gate repaired two canonical outer-rail routes).
  • 77/77 readable-route checks with zero micro or short-interior segments (Readable Route Rhythm repaired three real routes; the event-stream dead-letter path fell from five bends to three, a deployment route lost its 5px hook and third bend, and lifecycle failure routing now enters through a clean side corridor).
  • 66/66 artifact checks + separate SHOWCASE · PASS receipt (Composition Receipt + showcase quality profile).

The numbers are not a marketing claim; each is the receipt of a specific gate against the committed test corpus. The same corpus runs in CI on every release (node test/golden.mjs and node scripts/run-tests.mjs), and the smoke test runs the committed ZIP across Ubuntu, macOS, and Windows without installing dependencies.

The --quality showcase profile is the right default for anything that ships externally. The --quality standard profile is the right default for iteration and dense engineering diagrams where the gates would over-constrain the authoring loop. This is a deliberate split: standard warns instead of failing, so dense diagrams remain renderable and the author sees the debt without being blocked. showcase fails closed — if a single gate rejects, the artifact is not written.

A concrete example of how the profiles change authoring: in standard, a 3px label-route clearance emits a warning with both relationship identities, the hit segment, label rectangle, measured distance, threshold, and supported repairs. In showcase, the same configuration fails before write and again in the final artifact check; the author must either move the label, remove the relationship, or restructure the geometry. The CLI never tells you “this looks wrong” without giving you the data to know what to do about it.

Trade-offs and what it doesn’t fix

Three honest limits I noticed while reading the source and running the install commands.

The author does the layout. This is the biggest design choice and the one that will feel wrong to anyone coming from Mermaid or d3. Archify is explicitly not an auto-layout tool — the meta.viewBox, meta.layout, and explicit geometry controls are author responsibilities. The README is candid: “Do not plan exact coordinates in prose. Start with one clear main path, short side branches, sparse labels, and at most 12 primary nodes.” For the 12-primary-node target the project is well-tuned; for a 40-node architecture diagram you will spend real time on geometry. The benefit is byte-stable canonical SVG and a layout you can regression-test; the cost is authoring time. Mermaid wins on quick-and-dirty; Archify wins on “this is the same diagram in 18 months.”

The Skill path is a real boundary, not a marketing claim. The --repo-root gate is the most interesting thing in the project and also the most constrained. It only works for Architecture diagrams (not workflow, sequence, dataflow, lifecycle), it requires a local repository clone with the target commit checked out, and it deliberately does not support private repos or unpinned evidence. The accepted path is: clone public repo, git checkout <sha>, declare repo + commit + 1-3 repo-relative sources per component in the typed JSON, run archify render ... --repo-root <path>. Verified links appear in the Semantic Passport and Node Finder but stay outside the canonical SVG and every visual export — this is intentional, so a verified-link drift does not poison the published artifact. If your evidence is private or you cannot pin to a commit, you cannot use the gate; the README is explicit about this being out of scope rather than a TODO.

The DSH integration is opt-in, not bundled. The @tt-a1i/archify-dsh npm package is a separate install (dsh plugin --profile web add @tt-a1i/archify-dsh@0.1.0), pinned to a specific DSH developer-preview version (@deepseek-ai/dsh@0.1.0-rc.6), and the pending v0.2.0 is prepared for 0.1.2-rc.1 but not yet published. The adapter registers one filesystem Skill provider named archify-plugin and exposes an Archify 2.17.0-dev.1 development snapshot. The adapter itself makes no network requests, has no telemetry, and ships no prepare/install/postinstall scripts. The cordis.patch.yml resolves the packaged Skill root from the installed npm identity anchored at the DSH profile Loader baseUrl, never as a path concatenated onto baseUrl — which is the correct way to do plugin Skill paths but worth noting if you are writing your own DSH plugin and wondering how the example avoids the baseUrl-join footgun. The catch is that “Works with DeepSeek Harness” is conditional on a specific DSH developer-preview version, and that version is moving.

Why the receipt matters

The thing I keep coming back to is what the receipt buys you in practice. A Claude Code or DSH session that produces an Archify diagram can:

  1. Run archify deliver ... --json and either get a receipt object with format: success and a sha256 field, or get a failure object with stage, diagnostics[], and supportedFixes[].
  2. On failure, read the subject field to identify the exact authored ID to change, read the evidence field to see the measured problem (e.g. "labelClearance": 1.8, "threshold": 4), and pick one of the renderer-supported supportedFixes.
  3. Edit the JSON, re-run, get a new receipt. Two consecutive rounds without improvement → the CLI’s authoring guidance is “stop and report the unresolved diagnostics truthfully.”

This is the right shape for an agent loop. The structured repair receipt is a feedback protocol, not auto-layout or auto-fix — there is no LLM call inside the CLI, no retry expansion, no schema or IR change driven by the failure. The agent stays the repair authority; the CLI gives it the data to make a real repair decision. That separation is why the receipts survive a long-lived agent loop without the renderer drifting into “always produces something plausible, never verifies anything” territory.

How I tried it

The surface I actually exercised:

npx skills add tt-a1i/archify -g
# Authored a six-component typed Architecture JSON (a CDN + cache + DB + auth + analytics + external webhook map)
# Ran: node bin/archify.mjs validate architecture ./input.json --quality showcase --json
#   → 9/9 checks, 0 warnings, format=success, sha256=<...>
# Ran: node bin/archify.mjs deliver architecture ./input.json ./out.html --quality showcase --json
#   → 9/9 artifact checks, format=success, byteCount=<...>, sha256=<...>
# Opened out.html in a browser; the canonical SVG matched the JSON, the Semantic Passport on each
#   component named the exact authored label and role, and the 107 brand marks catalog had the
#   CDN provider's mark without me configuring it.

The thing that surprised me: the Live/Still motion governor in the viewer is real. Setting Live runs one finite ambient edge/node pass and then settles — the trace is not decorative loop, it is a one-shot story. Switching to Still returns to the authored state immediately, and switching back to Live does not replay. The Story Beat Navigator in the same viewer is also real — every resolved stop across the 33 Proof Lab chapters (150 stops total at run time) is a native, directly activatable control with exact position, node identity, relationship truth, focus treatment, and aria-current="step". These are viewer-side concerns, not authoring concerns; they do not enter the canonical SVG or any export. The composition gates and the receipts are the part I trust; the Story Director is the part I enjoy.

What it doesn’t fix (the deeper cut)

A diagram tool cannot make a wrong architecture right. The Clean Flow Gate rejects a relationship that passes through a node box, but if the author drew the relationship in the first place they meant something; the gate’s job is to make the meaning legible, not to invent a better meaning. The Repository Evidence Gate verifies that the link you authored points at the lines you claimed it points at; it does not tell you whether those lines are correct. The Composition Receipt gives you the same fact for layout: it tells you the diagram is compositionally honest, not that the system is honestly designed.

What Archify does is refuse to launder the author’s intent. If a relationship looks like it goes through a node, the CLI tells you the exact subject and the exact 2px clearance. If a label is too close to a route, the CLI tells you the label rectangle and the hit segment. If a component claim points at lines that don’t exist, the CLI fails closed before the link ever reaches the Semantic Passport. The opposite — a tool that produces plausible-looking output without verification — is the default in this category, and the cost is that diagrams become the most confidently-wrong artifacts in any architecture review.

A diagonal I did not expect: the DSH integration’s cordis.patch.yml is a small model of how to write a filesystem Skill plugin for a plugin-runtime that already has a baseUrl. The accepted practice in the broader plugin ecosystem is to do path.join(baseUrl, ...) and call it done; the DSH integration deliberately uses process.getBuiltinModule('node:path').dirname(process.getBuiltinModule('node:module').createRequire(baseUrl).resolve('<package>/package.json')) to anchor the Skill root to the installed npm package’s resolved package.json directory, not to baseUrl. The pattern is not obvious until you see it written down, and it is the right pattern for any plugin runtime that resolves npm packages and exposes them via baseUrl.

The repo is at tt-a1i/archify. The CLI is node bin/archify.mjs. The DSH integration is @tt-a1i/archify-dsh. The proof corpus is at tt-a1i.github.io/archify/gallery.html if you want to see what 99/99 looks like before you install anything.

References and where to dig further

  • tt-a1i/archify — repo, with archify/SKILL.md as the canonical Skill body and archify/bin/archify.mjs as the CLI entrypoint
  • archify/CHANGELOG.md — every accepted slice with the failure mode it closed; the v2.16.0 entry for “Constraint-driven Workflow Compiler” is the clearest single-piece read on what the readable-v2 layout contract buys you
  • archify/DESIGN.md — the design tokens, semantic color vocabulary, and motion-budget contract (140–200ms transitions; finite Story motion with explicit ownership)
  • archify/ROADMAP.md — explicitly declined ideas (no auto-layout, no browser dependency in the installed package, no hosted service, no second mobile surface); archived delivered-slice context that is frozen, no longer maintained as a second changelog
  • archify/references/authoring-contract.md — the failure codes, the gate names, and the supportedFixes taxonomy
  • archify/renderers/shared/diagnostics.mjs and archify/renderers/shared/geometry.mjs — the gate implementations; reading these is the fastest way to understand what “Clean Flow” actually checks
  • integrations/deepseek-harness/cordis.patch.yml — the filesystem Skill provider pattern that anchors Skill roots to installed npm package paths, not to baseUrl
  • @tt-a1i/archify-dsh on npm — the DSH 0.1.0 published package; v0.2.0 is prepared for @deepseek-ai/dsh@0.1.2-rc.1 but not yet published
  • tt-a1i.github.io/archify/gallery.html — the Proof Lab with 99/99 artifact checks and the full receipt set on each canonical artifact
  • mco-org/mco case at commit 9f1a1cf — the real-repository Architecture proof; the typed source is in archify/cases/mco-runtime.architecture.json and the live artifact is at tt-a1i.github.io/archify/cases/mco-runtime.architecture.html
Aniket Karne
DevOps & AI Engineer · Amsterdam
Back to all posts
Reader correspondence

Comments

Powered by GitHub Discussions via Giscus. Sign in with GitHub to leave a comment.