# How to Perform Causal Chain Analysis in Semantica's ContextGraph

> Learn to perform causal chain analysis in Semantica's ContextGraph. Discover how to store decisions and traverse causal relationships using get_causal_chain() and CausalChainAnalyzer.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: how-to-guide
- Published: 2026-09-10

---

**Semantica's ContextGraph enables causal chain analysis by storing decisions as typed nodes and causal relationships as canonical edges—such as "CAUSED" or "INFLUENCED"—which you can traverse upstream or downstream using `get_causal_chain()` or the `CausalChainAnalyzer` class.**

Causal chain analysis traces how decisions influence one another through an in-memory graph structure. In the `semantica-agi/semantica` repository, the **ContextGraph** implementation provides native methods to record decision nodes, establish causal links between them, and retrieve complete ancestor or descendant chains for audit and explanation purposes.

## Understanding the ContextGraph Data Model

### Decision Nodes

The foundation of causal analysis rests on properly typed nodes. Decisions are stored with the node type `"decision"` and contain structured metadata including content, category, scenario, confidence, and timestamp.

In [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py), create these nodes using `graph.add_node()` or the higher-level `graph.record_decision()` helper method. Each decision receives a unique identifier that serves as the anchor for causal relationships.

### Causal Edge Types

Relationships between decisions are represented as edges drawn from a canonical set. The recognized causal edge types include `"CAUSED"`, `"INFLUENCED"`, and `"PRECEDENT_FOR"`, along with present-tense aliases such as `"causes"` and `"influences"`.

These edge types are normalized internally using the `_CAUSAL_EDGE_ALIASES` mapping (defined at lines 5330-5345 in [`context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/context_graph.py)). This guarantees that edges added with any alias remain visible during traversal operations.

## Building the Causal Graph

### Adding Decision Nodes

Before establishing causal links, populate the graph with decision nodes. Each node requires a unique identifier and metadata capturing the decision context.

```python
from semantica.context.context_graph import ContextGraph

g = ContextGraph()
g.add_node("dec_001", "decision", content="Approve loan",
           category="loan", scenario="first-time buyer",
           confidence=0.94, timestamp="2024-01-10T09:00:00")
g.add_node("dec_002", "decision", content="Increase rate",
           category="loan", scenario="risk review",
           confidence=0.88, timestamp="2024-01-12T14:30:00")

```

### Establishing Causal Relationships

Connect nodes using `graph.add_causal_relationship(source_id, target_id, relationship_type)`. This method accepts any canonical type or alias and automatically normalizes the spelling before storage.

```python

# dec_001 caused dec_002, dec_002 influenced dec_003

g.add_causal_relationship("dec_001", "dec_002", "CAUSED")
g.add_causal_relationship("dec_002", "dec_003", "INFLUENCED")

```

The edge becomes visible to traversal operations through the `_CAUSAL_TRAVERSAL_TYPES` set defined in the source code.

## Querying Causal Chains

### Direct Graph Method

For immediate traversal, call `graph.get_causal_chain(decision_id, direction="downstream", max_depth=10)` directly on your `ContextGraph` instance.

The implementation in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) (lines 3918-4000) uses a breadth-first search with a FIFO queue (`deque`) to explore neighbors. It respects the `max_depth` parameter, skips the starting decision itself, and filters edges against `_CAUSAL_TRAVERSAL_TYPES` to ensure only causal relationships are followed.

The method returns a list of `Decision` model objects (defined in [`semantica/context/decision_models.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_models.py)) ordered by causal distance. Each object contains a `causal_distance` field in its metadata indicating the hop count from the origin.

```python
chain = g.get_causal_chain("dec_001", direction="downstream", max_depth=5)
for d in chain:
    print(f"{d.decision_id} → {d.content} (distance={d.metadata['causal_distance']})")

```

### Using the CausalChainAnalyzer Class

For advanced analysis, instantiate `CausalChainAnalyzer` from [`semantica/context/causal_analyzer.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/causal_analyzer.py). This wrapper provides the same core traversal logic plus additional utilities such as time-bounded tracing via `trace_at_time()` and richer diagnostic output.

```python
from semantica.context.causal_analyzer import CausalChainAnalyzer

analyzer = CausalChainAnalyzer(graph_store=g)
upstream = analyzer.get_causal_chain("dec_003", direction="upstream", max_depth=5)

for d in upstream:
    print(d.decision_id, d.content)

```

## Implementation Details

### Edge Type Normalization

Whether you specify `"CAUSED"` or `"causes"`, the system stores and retrieves the relationship consistently. The `_CAUSAL_EDGE_ALIASES` dictionary ensures that no edge becomes invisible during causal chain analysis due to spelling variations.

### Traversal Algorithm

The `get_causal_chain` method implements breadth-first search using Python's `collections.deque`. It traverses only edges whose types belong to the internal `_CAUSAL_TRAVERSAL_TYPES` set, effectively ignoring non-causal edges that might coexist in the graph.

### Direction Handling

Directionality determines which edges the algorithm follows:

- **Upstream** (ancestors): Follows edges where the current node is the **target**
- **Downstream** (descendants): Follows edges where the current node is the **source**

This directional logic allows you to trace either the root causes or the cascading consequences of any decision.

## Complete Working Example

```python
from semantica.context.context_graph import ContextGraph
from semantica.context.causal_analyzer import CausalChainAnalyzer

# 1. Create graph and add decisions

g = ContextGraph()
g.add_node("dec_001", "decision", content="Approve loan",
           category="loan", confidence=0.94, timestamp="2024-01-10T09:00:00")
g.add_node("dec_002", "decision", content="Increase rate",
           category="loan", confidence=0.88, timestamp="2024-01-12T14:30:00")
g.add_node("dec_003", "decision", content="Reject loan",
           category="loan", confidence=0.97, timestamp="2024-01-15T11:15:00")

# 2. Declare causal relationships

g.add_causal_relationship("dec_001", "dec_002", "CAUSED")
g.add_causal_relationship("dec_002", "dec_003", "INFLUENCED")

# 3. Retrieve downstream chain via direct method

chain = g.get_causal_chain("dec_001", direction="downstream", max_depth=5)
for d in chain:
    print(f"{d.decision_id} → {d.content} (distance={d.metadata['causal_distance']})")

# Output:

# dec_002 → Increase rate (distance=1)

# dec_003 → Reject loan (distance=2)

# 4. Retrieve upstream chain via analyzer

analyzer = CausalChainAnalyzer(graph_store=g)
upstream = analyzer.get_causal_chain("dec_003", direction="upstream")
for d in upstream:
    print(d.decision_id, d.content)

# dec_002 Increase rate

# dec_001 Approve loan

```

## Summary

- **Decision nodes** use the type `"decision"` and store structured metadata in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)
- **Causal edges** belong to the canonical set `("CAUSED", "INFLUENCED", "PRECEDENT_FOR")` with automatic alias normalization via `_CAUSAL_EDGE_ALIASES`
- **Traversal methods** include the direct `graph.get_causal_chain()` and the wrapper `CausalChainAnalyzer` class from [`semantica/context/causal_analyzer.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/causal_analyzer.py)
- **Direction control** uses `"upstream"` for ancestors (target-facing edges) and `"downstream"` for descendants (source-facing edges)
- **Results** return as `Decision` model objects with `causal_distance` metadata indicating hop count from the origin

## Frequently Asked Questions

### What edge types does Semantica recognize for causal chain analysis?

Semantica recognizes `"CAUSED"`, `"INFLUENCED"`, and `"PRECEDENT_FOR"` as canonical types, plus aliases such as `"causes"` and `"influences"`. The system normalizes these automatically using the `_CAUSAL_EDGE_ALIASES` mapping in [`context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/context_graph.py), ensuring consistent traversal regardless of which alias you use when creating relationships.

### How do I trace decision consequences versus decision origins?

Use `direction="downstream"` to find consequences (descendants) where the starting decision is the source of influence, or `direction="upstream"` to find origins (ancestors) where the starting decision is the target of prior decisions. The algorithm follows edges directionally based on this parameter.

### Can I limit how far the causal chain traverses?

Yes, the `max_depth` parameter controls traversal depth in both the direct graph method and the `CausalChainAnalyzer` class. The breadth-first search implementation respects this limit strictly, preventing excessive computation on large graphs while still returning ordered results by causal distance.

### What data structure does `get_causal_chain` return?

The method returns a list of `Decision` objects defined in [`semantica/context/decision_models.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_models.py). Each object contains the original decision properties plus a `causal_distance` field in its metadata dictionary indicating the number of hops from the starting decision. The list is ordered by this distance, from nearest to farthest.