How Archify Generates Stable Link Anchors for Navigation
Archify creates deterministic, human-readable HTML anchors by combining slugified node names with UUID-derived hashes, ensuring deep links survive code changes.
Stable link anchors are essential for navigating large architecture diagrams. In the tt-a1i/archify codebase, every rendered node receives a unique anchor that remains constant across builds—enabling reliable deep-linking from documentation, tickets, and external tools.
Core Mechanics of Archify's Anchor Generation
Archify generates stable anchors through a five-step pipeline implemented across its renderer modules.
Step 1: Normalize the Target Name
The first step converts human-readable node names into URL-friendly slugs. The slugify(name) function in archify/renderers/markdown-utils.js handles this transformation:
- Converts to lowercase
- Replaces spaces and punctuation with dashes
- Removes special characters
A node named "User Profile Component" becomes user-profile-component.
Step 2: Create a Deterministic Hash
To prevent collisions when multiple nodes share the same display name, Archify appends a short hash derived from the node's stable UUID. The stableHash(uuid) function in archify/renderers/anchor-utils.js computes this:
- Takes an SHA-1 digest of the UUID
- Truncates to six hexadecimal characters
This guarantees uniqueness without sacrificing determinism.
Step 3: Assemble the Final Anchor
The makeAnchor(node) function in archify/renderers/anchor-utils.js combines both parts:
<slug>-<hash>
For example: user-profile-component-a1b2c3
This string becomes the HTML id attribute.
Step 4: Inject Into Rendered Output
Each renderer calls makeAnchor(node) before emitting markup. In archify/renderers/workflow/index.js, the anchor is inserted directly into the rendered element:
import { renderNode } from 'archify/renderers/workflow';
import { makeAnchor } from 'archify/renderers/anchor-utils';
const node = {
id: 'c9f8a7e4-1234-5678-90ab-cdef12345678',
name: 'User Profile Component',
};
const anchor = makeAnchor(node); // "user-profile-component-a1b2c3"
const html = renderNode(node, { id: anchor });
console.log(html);
// <div id="user-profile-component-a1b2c3" class="node">…</div>
Step 5: Resolve Links at Runtime
Browser-native anchor handling takes over. Clicking #user-profile-component-a1b2c3 scrolls directly to the element. No additional JavaScript is required.
Why This Design Works
Determinism — Both the slug and hash are pure functions. The same inputs always produce the same output, eliminating randomness that could break links.
Collision-resistance — The six-character hash provides 16.7 million possible suffixes, ensuring unique anchors even for identically named nodes.
Human-readability — The slug component preserves context. Links like #user-profile-component-a1b2c3 are self-documenting unlike opaque hashes alone.
Stability across builds — Since anchors derive from the node's UUID (stored in archify/skill-release.json and workflow definitions), they persist through code refactors, renames, and render pipeline updates.
Using Stable Anchors in Documentation
Once rendered, these anchors enable reliable deep-linking:
# Architecture Overview
The **User Profile Component** is rendered below.
You can link directly to it:
[Jump to component](#user-profile-component-a1b2c3)
Teams embed these anchors in README files, Jira tickets, Confluence pages, or Slack messages—confident the links will resolve correctly after future Archify runs.
Key Implementation Files
| File | Purpose |
|---|---|
archify/renderers/markdown-utils.js |
slugify() — normalizes display names |
archify/renderers/anchor-utils.js |
stableHash() and makeAnchor() — generates deterministic anchors |
archify/renderers/workflow/index.js |
Integrates anchors into workflow diagram markup |
archify/bin/archify.mjs |
CLI entry point orchestrating the render pipeline |
Summary
- Archify stable link anchors combine slugified names with UUID-derived hashes for uniqueness
- The implementation spans
anchor-utils.jsandmarkdown-utils.jswith renderer-specific integration - Anchors survive code changes because they derive from stable node UUIDs, not volatile properties
- Browser-native handling requires no runtime JavaScript
- The CLI in
archify.mjsdrives the entire pipeline end-to-end
Frequently Asked Questions
How does Archify prevent anchor collisions between nodes with identical names?
Archify appends a six-character hash from stableHash(uuid) in archify/renderers/anchor-utils.js. Even if two nodes share the display name "Service", their different UUIDs guarantee unique suffixes like service-a1b2c3 versus service-d4e5f6.
Where are stable anchors stored in the output HTML?
The makeAnchor(node) function returns the final string, which renderers pass as the id attribute. In archify/renderers/workflow/index.js, this becomes <div id="user-profile-component-a1b2c3"> or equivalent markup depending on the output format.
Do anchors change when I rename a node?
Yes—the slug portion derives from the display name, so renaming alters the anchor. However, the UUID remains constant, so downstream tools tracking by UUID can detect and update references. For fully immutable anchors, Archify would need to store historical slug mappings.
Can I customize the hash length or format?
The current implementation in archify/renderers/anchor-utils.js hardcodes six-character SHA-1 truncation. To modify this, override stableHash() or inject a custom anchor generator before the rendering phase in your workflow renderer.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →