How Archify Traces Upstream and Downstream Relationships in the Viewer: A Complete Technical Guide

Archify's interactive viewer performs upstream/downstream reachability analysis by computing the transitive closure of directed edges in the authored graph, using depth-first search over the JSON IR to highlight all reachable nodes from any selected focus point.

The tt-a1i/archify repository provides a specialized tool for visualizing software architecture through authored facts rather than runtime telemetry. Its viewer component includes a sophisticated reachability engine that lets developers trace dependency chains in both directions through their system's topology. This article explains exactly how that analysis works, where the implementation lives, and how to invoke it programmatically.

What Upstream/Downstream Reachability Analysis Actually Does

Archify's viewer operates on authored facts — deterministic, source-verified relationships stored in a typed JSON intermediate representation. When you select a node and trigger reachability analysis, the viewer does not query live systems or parse code on demand. Instead, it walks the pre-computed edge list that was generated when the architecture was authored.

The analysis produces the transitive closure of all relationships connected to your chosen node:

  • Upstream — finds every node that can reach the focus node (dependencies flowing in)
  • Downstream — finds every node reachable from the focus node (dependencies flowing out)

Because the graph is fully typed and the edge directions are explicit in the JSON IR, this computation is deterministic and fast regardless of graph size.

The Algorithm: Depth-First Search Over Directed Edges

In archify/viewer.js (bundled within examples/web-app.html), the reach() method implements the core traversal logic. The algorithm maintains two data structures: a visited Set to track processed nodes and a stack for iterative depth-first search.

// Simplified implementation from archify/viewer.js
function reach(direction) {
  const startId = this.currentFocus;          // id of the focused node
  const graph = this.graph;                   // authored nodes + edges
  const visited = new Set();
  const stack = [startId];

  while (stack.length) {
    const id = stack.pop();
    if (visited.has(id)) continue;
    visited.add(id);

    const edges = direction === 'upstream' ? graph.inEdges[id] : graph.outEdges[id];
    for (const e of edges) stack.push(e.source === id ? e.target : e.source);
  }

  this.highlightNodes([...visited]);          // visual cue for the reachable set
}

The method selects edge direction based on the direction parameter:

  • graph.inEdges[id] — all edges where id is the target (for upstream)
  • graph.outEdges[id] — all edges where id is the source (for downstream)

The edge traversal logic handles both cases uniformly: for each edge, it pushes the other endpoint onto the stack to continue the walk.

Data Source: The JSON IR Schema

The reachability algorithm consumes data structured according to the schema defined in archify/schemas/README.md. This JSON document contains:

  • nodes — typed entities (services, databases, queues, etc.) with stable identifiers
  • edges — directed relationships with explicit source and target fields

The deterministic nature of this authored data guarantees that reachability results match the source-verified architecture exactly. There is no drift between the visual trace and the actual system design because both derive from the same immutable facts.

User Interface Controls and Interactions

The README documents three ways to initiate reachability analysis in the viewer:

  1. Toolbar buttons — click Upstream or Downstream after focusing a node
  2. Keyboard shortcuts — press / to focus, then U (upstream) or D (downstream)
  3. Programmatic API — call viewer.reach('upstream') or viewer.reach('downstream')

The focused node receives visual emphasis, and all reachable nodes are highlighted with a distinct styling. Nodes outside the reachable set remain visible but de-emphasized, providing immediate visual context for the dependency footprint.

Deep-Linking and State Persistence

Archify's viewer encodes reachability state directly in the URL hash, enabling shareable, bookmarkable views. The format uses two fragments:


#focus=router&reach=downstream

  • #focus=<node-id> — identifies the centered/highlighted node
  • #reach=upstream|downstream — activates the corresponding reachability mode

When a URL with these fragments loads, the viewer:

  1. Parses the hash parameters
  2. Centers the viewport on the specified node
  3. Executes the reachability analysis automatically
  4. Highlights the computed reachable set

This deep-linking capability is implemented in the viewer's initialization sequence within examples/web-app.html, making architecture reviews and incident response workflows reproducible across teams.

Programmatic Usage Example

For integrating reachability into custom workflows, the viewer exposes a clean JavaScript API:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Archify Viewer</title>
  <script src="archify/viewer.js"></script>
</head>
<body>
  <archify-viewer src="examples/web-app.architecture.json"></archify-viewer>

  <script>
    const viewer = document.querySelector('archify-viewer');

    viewer.addEventListener('load', () => {
      // Focus the "router" node and trace downstream dependencies
      viewer.focusNode('router');
      viewer.reach('downstream');
    });
  </script>
</body>
</html>

The focusNode() method establishes the traversal origin. The reach() method accepts either 'upstream' or 'downstream' as string parameters, matching the UI controls.

Where the Implementation Lives

File Purpose
examples/web-app.html Full-screen demo with embedded viewer source, includes reach() implementation and UI controls
archify/schemas/README.md JSON IR schema specification defining the node/edge structure consumed by reachability analysis
README.md (section "Trace upstream/downstream authored reach") User-facing documentation of controls and deep-link format

The viewer implementation in examples/web-app.html contains the bundled archify/viewer.js source, which is not separately distributed as a standalone file in the repository. For modification or debugging, examine the source embedded in the HTML file between the <script> tags.

Performance Characteristics

The depth-first search implementation provides O(V + E) time complexity where V is reachable nodes and E is traversed edges. The iterative stack approach avoids recursion limits on large graphs. Memory overhead is O(V) for the visited set and active stack.

Because the authored graph is static, reachability results can technically be pre-computed and cached, though the current implementation computes on-demand to support dynamic focus changes. The deterministic IR ensures identical results across repeated executions.

Summary

  • Upstream/downstream reachability analysis in Archify computes transitive closures over authored dependency graphs, not live runtime data
  • The algorithm uses depth-first search with iterative stack traversal in archify/viewer.js
  • Data source is the typed JSON IR defined in archify/schemas/README.md, ensuring deterministic, source-verified results
  • Three interaction modes: toolbar buttons, keyboard shortcuts (/U/D), and programmatic viewer.reach() API
  • Deep-linking via #focus=<id>&reach=<direction> URL fragments enables shareable analysis states
  • Time complexity is O(V + E), suitable for interactive use on large architecture graphs

Frequently Asked Questions

How does Archify's reachability analysis handle cycles in the dependency graph?

The visited Set in the reach() implementation prevents infinite loops. Before processing any node, the algorithm checks visited.has(id) and skips if already processed. This standard cycle-handling approach ensures termination regardless of graph topology, and the reachable set correctly includes all nodes in cyclic dependency chains without duplication.

Can I trigger upstream/downstream analysis without using the keyboard shortcuts?

Yes. The viewer exposes the reach(direction) method on the DOM element. After selecting your target node with viewer.focusNode(id), call viewer.reach('upstream') or viewer.reach('downstream') from JavaScript. This is the same API the toolbar buttons and keyboard shortcuts invoke internally.

What's the difference between Archify's reachability and static analysis tools?

Archify operates on authored facts — explicit architectural decisions captured in the JSON IR. Static analysis tools infer relationships by parsing source code. Archify's approach guarantees the visualized dependencies match the intended design, making it valuable for design reviews, compliance checks, and communication with stakeholders who need trustworthy topology views.

Where is the standalone archify/viewer.js file located?

The viewer is currently distributed as bundled source embedded in examples/web-app.html. There is no separate viewer.js file in the repository structure. For production deployments, extract the JavaScript from between the <script> tags in that example file, or reference the complete HTML demo directly.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →