# How to Add Causal Relationships Between Decisions in Semantica

> Learn how to add causal relationships between decisions in Semantica using ContextGraph.add_causal_relationship(). Understand source, target, and relationship types like CAUSED or INFLUENCED.

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

---

**To link decisions in Semantica, use `ContextGraph.add_causal_relationship()` from [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py), specifying the source decision ID, target decision ID, and a relationship type such as `CAUSED` or `INFLUENCED`.**

Semantica is an open-source AGI framework that stores decisions as nodes in a **ContextGraph**, enabling systematic tracking of how choices influence outcomes. Adding causal relationships between decisions allows the system to construct traceable chains of reasoning for explainable AI workflows. According to the `semantica-agi/semantica` source code, this is implemented through a dedicated graph edge method that enforces strict validation and type safety.

## Implementation of `add_causal_relationship`

In [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) (lines 722–796), the `add_causal_relationship()` method creates directed edges between decision nodes. The method accepts three parameters: `source_decision_id`, `target_decision_id`, and `relationship_type`.

### Relationship Type Normalization

The implementation accepts both canonical uppercase constants and lowercase aliases. Valid canonical forms include:

- `CAUSED`
- `INFLUENCED`
- `PRECEDENT_FOR`

Aliases such as `"causes"` are automatically normalized to the canonical constant before storage, ensuring consistency with the `CausalChainAnalyzer` while maintaining a uniform graph schema.

### Validation and Safety Checks

The method performs four sequential validation steps before edge creation:

1. **Type verification** – Raises `ValueError` if the relationship type is not a string or not in the supported set
2. **Existence checks** – Silently ignores requests where either decision ID is missing from the graph to prevent corruption from stale references
3. **Node type enforcement** – Confirms both nodes are of type `"Decision"`; non-decision nodes are rejected
4. **Edge instantiation** – Creates a `ContextEdge` with default weight `1.0` and timestamped metadata, then persists via `_add_internal_edge`

## Traversing Causal Chains

Retrieve ordered decision sequences using `get_causal_chain()` defined at lines 819–999 in the same file. This method executes a breadth-first search (BFS) following canonical causal edge types.

**Parameters:**

- `decision_id` – The UUID of the starting decision node
- `direction` – String value `"upstream"` (for causes) or `"downstream"` (for effects)
- `max_depth` – Optional integer to limit traversal depth

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)) representing the causal path.

## Practical Code Example

The following example demonstrates recording decisions, establishing a causal link, and querying the resulting chain:

```python
from semantica.context import ContextGraph

# 1️⃣ Create a graph (or use the existing Context instance)

g = ContextGraph()

# 2️⃣ Record two decisions (the Context API is a thin wrapper around add_node)

decision_a_id = g.record_decision(
    category="Finance",
    scenario="Approve loan for client A",
    reasoning="Credit score > 700",
    outcome="Approved",
    confidence=0.95,
)

decision_b_id = g.record_decision(
    category="Finance",
    scenario="Allocate budget for project X",
    reasoning="Strategic priority",
    outcome="Allocated",
    confidence=0.88,
)

# 3️⃣ Add a causal link – decision A **caused** decision B

g.add_causal_relationship(
    source_decision_id=decision_a_id,
    target_decision_id=decision_b_id,
    relationship_type="CAUSED",   # alias "causes" would also work

)

# 4️⃣ Retrieve downstream causal chain from decision A

downstream = g.get_causal_chain(decision_a_id, direction="downstream")
print("Downstream chain:", [d.scenario for d in downstream])

# 5️⃣ Retrieve upstream causal chain for decision B

upstream = g.get_causal_chain(decision_b_id, direction="upstream")
print("Upstream chain:", [d.scenario for d in upstream])

```

**Output:**

```

Downstream chain: ['Allocate budget for project X']
Upstream chain: ['Approve loan for client A']

```

## Summary

- **Primary method**: `add_causal_relationship()` in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) (lines 722–796) creates causal links between decision nodes
- **Supported relationships**: Canonical constants `CAUSED`, `INFLUENCED`, `PRECEDENT_FOR` with automatic normalization of lowercase aliases like `"causes"`
- **Safety mechanisms**: Silent failure on missing IDs, strict `"Decision"` node type requirements, and `ValueError` on invalid relationship types prevent graph corruption
- **Edge properties**: All causal edges carry weight `1.0` and timestamped metadata via `ContextEdge` instances stored through `_add_internal_edge`
- **Traversal method**: `get_causal_chain()` (lines 819–999) performs BFS to retrieve upstream or downstream decision sequences as `Decision` objects from [`semantica/context/decision_models.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_models.py)

## Frequently Asked Questions

### What happens if I reference a decision ID that does not exist in the graph?

The method silently ignores the operation. As implemented in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py), if either the `source_decision_id` or `target_decision_id` is not present in the graph, the function returns early without raising an exception or modifying the graph. This design prevents stray or obsolete IDs from corrupting the causal structure.

### Can I use lowercase strings for relationship types?

Yes. The implementation normalizes relationship types automatically. While the canonical stored form uses uppercase constants (`CAUSED`, `INFLUENCED`, `PRECEDENT_FOR`), you can pass lowercase aliases such as `"causes"` and the method will convert them to the proper canonical form before creating the edge.

### How does the system traverse causal relationships to find related decisions?

The `get_causal_chain()` method uses breadth-first search (BFS) starting from the specified decision ID. When `direction="downstream"`, it follows outgoing edges to identify consequences; when `direction="upstream"`, it follows incoming edges to identify root causes. The search respects the canonical edge types defined in the graph and returns ordered `Decision` objects.

### Where is the Decision dataclass defined for causal chain results?

The `Decision` dataclass resides in [`semantica/context/decision_models.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_models.py). When you call `get_causal_chain()`, the method returns instances of this class, providing typed access to attributes including `scenario`, `reasoning`, `outcome`, and `confidence` for each decision in the causal path.