# How to Trace Upstream and Downstream Reachability from a Focused Node in Archify

> Master Archify's focus.reachability API to trace upstream and downstream node connections. Understand dependencies and impacts across your architecture diagrams efficiently.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-08-04

---

**Archify exposes a `focus.reachability()` API that returns all ancestor (upstream) or descendant (downstream) nodes reachable from any focused node through the diagram's directed edges.**

Archify treats every diagram as a **directed graph** where nodes represent visual elements and edges represent relationships. When you focus a node, you can programmatically query which other nodes are reachable in either direction—essential for understanding dependencies, call chains, or data flow. This guide covers the core API, the underlying graph traversal algorithm, and practical code patterns based on the official source code in `tt-a1i/archify`.

## Core Reachability API

Archify's reachability system centers on three interconnected components: the **focus object**, the **relationship map**, and the **computation engine**.

### The Focus Object

`Archify.focus` holds state for the currently selected node and exposes helper methods. In [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html) at line 5967, the focus object provides:

- `reachabilitySnapshot()` – Returns a serializable state object for deep-linking
- `reachability(direction?)` – Initiates reachability computation and UI highlighting

The focus object is automatically updated when a user clicks any node, or you can set it programmatically.

### The Relationship Map

Before computing reachability, Archify builds a directed adjacency map. The function `reachabilityRelationships()` (line 7003 in [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html)) constructs:

```javascript
// Returns: { nodeId: [adjacentNodeId, ...], ... }

```

This map is cached per render and represents the graph structure derived from the diagram's connections.

### The Computation Engine

The core algorithm lives in `computeReachability(originId, direction, relationships)` at line 7016. It performs **depth-first search** with directional edge following:

- **Upstream**: Follows **incoming** edges using a reverse-lookup map
- **Downstream**: Follows **outgoing** edges from the standard adjacency list

The implementation is **pure**—no side effects—making it safe to run repeatedly without mutating diagram state.

## How to Compute Reachability Programmatically

### Triggering Upstream Reachability

To find all ancestor nodes from a focused node:

```javascript
// Set focus on a specific node
Archify.focus = { id: 'order-123' };

// Compute upstream reachability
const upstream = Archify.focus.reachability('upstream');
// Returns: { direction: 'upstream', nodeIds: ['customer-789', 'account-456', ...] }

// Highlight reachable nodes in the UI
Archify.view.reveal(upstream.nodeIds, { 
  includeNeighbors: false, 
  reason: 'reachability' 
});

```

The `reachability()` method internally calls `reachabilityFor(id, 'upstream')` as implemented at line 7113-7114 of [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html).

### Triggering Downstream Reachability

Swap the direction parameter:

```javascript
const downstream = Archify.focus.reachability('downstream');
// Returns: { direction: 'downstream', nodeIds: ['shipment-456', 'invoice-789', ...] }

Archify.view.reveal(downstream.nodeIds, { 
  includeNeighbors: false, 
  reason: 'reachability' 
});

```

### Toggle Behavior

The UI implements **toggle semantics**: calling `reachability('upstream')` when upstream mode is already active collapses the view. This matches the button behavior at line 6988 where `reachabilityMode` is checked and cleared if unchanged.

## Creating Deep Links with Snapshots

The snapshot API enables bookmarkable views of reachability explorations:

```javascript
// After computing reachability
const snap = Archify.focus.reachabilitySnapshot();
// Returns: { direction: 'downstream', nodeIds: ['order-123', 'shipment-456', 'invoice-789'] }

// Build a shareable URL
const url = `${location.origin}${location.pathname}#focus=${encodeURIComponent(Archify.focus.id)}&reach=${snap.direction}`;
// Result: https://example.com/diagram#focus=order-123&reach=downstream

```

The snapshot implementation at line 7228-7233 in [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html) serializes only the essential state—direction and node IDs—keeping URLs compact while preserving full context.

## Wiring UI Controls

For interactive applications, bind reachability to button controls:

```javascript
// Upstream button handler
document.getElementById('btn-reach-upstream').addEventListener('click', () => {
  Archify.focus.reachability('upstream');
});

// Downstream button handler  
document.getElementById('btn-reach-downstream').addEventListener('click', () => {
  Archify.focus.reachability('downstream');
});

```

The button markup at line 4863-4867 in [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html) includes ARIA labels for accessibility:

```html
<button id="btn-reach-upstream" aria-label="Show upstream dependencies">← Upstream</button>
<button id="btn-reach-downstream" aria-label="Show downstream dependencies">Downstream →</button>

```

## Under the Hood: [`archify/assets/template.html`](https://github.com/tt-a1i/archify/blob/main/archify/assets/template.html)

The base template at [`archify/assets/template.html`](https://github.com/tt-a1i/archify/blob/main/archify/assets/template.html) contains a shared `computeReachability` implementation (line 6949) used by all rendered diagrams. This ensures consistent behavior across:

- Static exports
- Interactive web views
- Embedded diagrams

The template defines the core traversal without UI dependencies, allowing custom interfaces to leverage the same algorithm.

## Testing Reachability Logic

The test suite at `archify/test/authored-reachability.test.mjs` validates the algorithm against known graph fixtures. Key test patterns include:

- **Diamond graphs**: Confirming upstream from a bottom node returns both top ancestors
- **Cycles**: Verifying termination despite circular references
- **Isolated nodes**: Ensuring empty results for disconnected elements

Running these tests guarantees that reachability behaves predictably across diagram types.

## Summary

- **`Archify.focus.reachability(direction)`** – Primary API for upstream/downstream queries; returns node IDs reachable from the focused node
- **`computeReachability(originId, direction, relationships)`** – Pure DFS implementation in [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html) line 7016
- **`reachabilitySnapshot()`** – Serializes state for deep-linking at line 7228
- **`reachabilityRelationships()`** – Builds the directed adjacency map at line 7003
- **Toggle behavior** – Calling the same direction twice collapses the highlight
- **Performance** – Pure algorithm on cached relationship map enables instant browser-side computation

## Frequently Asked Questions

### How does Archify handle cycles in the graph when computing reachability?

Archify's `computeReachability` prevents infinite loops by **tracking visited nodes**. During DFS traversal, each node ID is added to an internal `visited` Set before recursion. Duplicate encounters skip processing. This is validated in `archify/test/authored-reachability.test.mjs` against cyclic graph fixtures.

### Can I compute reachability without triggering the UI highlight?

Yes—call `reachabilityFor(id, direction)` directly instead of `Archify.focus.reachability()`. The wrapper at line 7113 returns raw results without invoking `Archify.view.reveal()`. Use this for background analysis or custom rendering pipelines.

### What's the difference between `reachability()` and `reachabilitySnapshot()`?

**`reachability(direction)`** actively computes and visualizes reachability, returning the result and updating the diagram highlight. **`reachabilitySnapshot()`** returns the **last computed state** without recalculation—useful for URL generation or state persistence. The snapshot contains the same structure (`{ direction, nodeIds }`) but reads from cached focus state.

### Where is the reachability algorithm defined if I need to customize it?

The canonical implementation lives in two places: [`archify/assets/template.html`](https://github.com/tt-a1i/archify/blob/main/archify/assets/template.html) at line 6949 (used by all rendered diagrams) and [`examples/web-app.html`](https://github.com/tt-a1i/archify/blob/main/examples/web-app.html) at line 7016 (full UI example). Both use the same `computeReachability` signature. Override in [`template.html`](https://github.com/tt-a1i/archify/blob/main/template.html) for global changes, or shadow in specific examples for localized behavior.