# How Semantica's ContextGraph trace_decision_causality Method Works for Audit Trails

> Explore Semantica's ContextGraph trace_decision_causality method to reconstruct agent decision chains and calculate confidence scores for transparent audit trails.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: deep-dive
- Published: 2026-08-09

---

**The `trace_decision_causality` method performs a thread-safe breadth-first search across `PRECEDENT_FOR` edges to reconstruct a complete causal chain of agent decisions, calculating cumulative confidence scores for full audit trail transparency.**

Semantica, an open-source agentic framework maintained at `semantica-agi/semantica`, stores every agent decision as a node in a **Context Graph** linked by causal relationships. The `trace_decision_causality` method transforms this raw graph data into a structured audit trail that compliance systems and debuggers can consume directly.

## Overview of Decision Causality Tracing

In [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py), the `ContextGraph` class implements a provenance system where decisions are immutable nodes connected by typed edges. When an agent makes a decision that references previous reasoning, the `record_decision` helper (implemented in [`semantica/context/decision_recorder.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_recorder.py)) creates a `PRECEDENT_FOR` edge (defined in [`semantica/context/decision_models.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_models.py)) linking the new decision to its antecedents.

The `trace_decision_causality(decision_id)` method reverses this process. Starting from any decision node, it walks backward through causal edges to answer three critical audit questions: *what* happened, *when* it happened, and *why* it happened.

## How trace_decision_causality Builds Audit Trails

The method executes a four-phase traversal algorithm under a re-entrant lock to ensure consistency during concurrent graph mutations.

### Locating the Decision Node

The method first validates the input by checking `self.nodes` for the supplied `decision_id`. If the identifier does not exist, it raises a `KeyError` immediately. This validation ensures that audit queries fail fast rather than returning empty or misleading chains.

### Traversing Causal Edges

Using a breadth-first search (BFS), the walker follows outgoing edges filtered by the causal relationship label (defaulting to `PRECEDENT_FOR`). The search continues until it encounters nodes with no further causal edges or reaches a configurable `max_hops` limit. This prevents runaway traversals on densely connected graphs while preserving the ability to retrieve deep historical context when needed.

### Calculating Confidence Decay

For each hop in the chain, the method extracts:
- **Source and target** node IDs
- **Edge type** and **weight** (typically representing LLM confidence scores)
- **Edge metadata** containing reasoning context
- **Cumulative confidence decay** calculated as the running product of edge weights

The final `overall_confidence` value represents the multiplicative probability that the entire decision chain is valid, enabling quantitative risk assessment in compliance workflows.

### Thread-Safe Execution

The entire traversal executes under `self._lock`, a re-entrant lock held by the `ContextGraph` instance. This guarantees that the returned audit trail represents a consistent snapshot even when other threads are actively recording new decisions or modifying graph topology.

## Anatomy of the Audit Trail Output

The method returns a `_CausalChain` dictionary subclass (for backward compatibility) containing a structured hop list:

```python
{
    "decision_id": "2c3f7b1e-...",
    "hops": [
        {
            "source": "2c3f7b1e-...",
            "target": "a7e4d2f9-...",
            "edge_type": "PRECEDENT_FOR",
            "weight": 0.88,
            "metadata": {"reasoning": "customer requested lower rate"},
            "confidence_decay": 0.88,
            "path": ["2c3f7b1e-...", "a7e4d2f9-..."],
        },
        {
            "source": "a7e4d2f9-...",
            "target": "5f1e2c3d-...",
            "edge_type": "PRECEDENT_FOR",
            "weight": 0.96,
            "metadata": {"reasoning": "good credit score"},
            "confidence_decay": 0.8448,  # 0.88 * 0.96

            "path": ["2c3f7b1e-...", "a7e4d2f9-...", "5f1e2c3d-..."],
        },
    ],
    "total_hops": 2,
    "overall_confidence": 0.8448,
}

```

Because the return type subclasses `dict`, existing code that iterates over `chain["hops"]` continues to function without modification.

## Practical Implementation Example

The following example demonstrates recording interdependent loan approval decisions and retrieving their audit trail:

```python
from semantica.context import ContextGraph

# Initialize graph with thread-safe locking

g = ContextGraph(advanced_analytics=False)

# Record initial decision (root of the chain)

decision_a = g.record_decision(
    category="loan_approval",
    scenario="first_homebuyer",
    reasoning="good credit score",
    outcome="approved",
    confidence=0.96,
    entities=["customer_123", "property_456"],
)

# Record subsequent decision citing precedent

decision_b = g.record_decision(
    category="loan_approval",
    scenario="refinance",
    reasoning="customer requested lower rate",
    outcome="approved",
    confidence=0.88,
    entities=["customer_123"],
    precedents=[decision_a],  # Creates PRECEDENT_FOR edge

)

# Generate audit trail for compliance review

causal_chain = g.trace_decision_causality(decision_b)

print(f"Audit trail for decision: {decision_b}")
for hop in causal_chain["hops"]:
    print(
        f"← {hop['source']} --{hop['edge_type']} ({hop['weight']})→ {hop['target']}"
    )
print(f"Overall confidence: {causal_chain['overall_confidence']:.4f}")

```

**Output:**

```

Audit trail for decision: 2c3f7b1e-...
← 2c3f7b1e-... --PRECEDENT_FOR (0.88)→ a7e4d2f9-...
← a7e4d2f9-... --PRECEDENT_FOR (0.96)→ 5f1e2c3d-...
Overall confidence: 0.8448

```

## Why This Matters for Compliance and Debugging

**Complete Provenance**: Every node stores full reasoning, outcome, associated entities, and timestamps. Auditors can traverse from any decision back to its ultimate origin.

**Temporal Validity**: Nodes and edges support `valid_from` and `valid_until` attributes (parsed in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) lines 42-64), enabling time-windowed queries that show only decisions active at a specific moment.

**Quantifiable Trust**: The multiplicative confidence decay provides a numerical metric for automated risk assessment pipelines.

**Scalable Investigation**: The `max_hops` parameter allows investigators to balance between deep historical analysis and rapid high-level overviews.

## Summary

- **`trace_decision_causality`** in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) implements BFS traversal over `PRECEDENT_FOR` edges to reconstruct decision history.
- The method returns a structured dictionary containing ordered hops with **confidence decay calculations** and **edge metadata**.
- **Thread-safety** is guaranteed via a re-entrant lock held during the entire traversal.
- The system supports **temporal filtering** through `valid_from`/`valid_until` attributes on nodes and edges.
- Audit trails include **quantitative confidence metrics** calculated as the product of edge weights along the causal chain.

## Frequently Asked Questions

### What is the difference between trace_decision_causality and a simple graph query?

The `trace_decision_causality` method is a specialized walker that understands decision-specific semantics. Unlike a generic graph query, it automatically filters for causal edge types (such as `PRECEDENT_FOR`), calculates cumulative confidence decay across the path, and respects temporal validity windows. It also handles thread-safety internally via the graph's re-entrant lock, whereas raw graph queries would require manual synchronization.

### How does Semantica handle concurrent modifications during audit trail generation?

The method acquires `self._lock` before accessing the internal `self.nodes` dictionary and maintains the lock throughout the BFS traversal. This ensures that even if other threads are recording new decisions or adding edges concurrently, the returned audit trail represents a consistent point-in-time snapshot of the graph state.

### Can I limit the depth of the audit trail search?

Yes. The `trace_decision_causality` method accepts a `max_hops` parameter that caps the BFS traversal depth. This prevents performance degradation on large graphs with deeply nested decision chains while still exposing the most recent and relevant causal history for debugging or compliance review.

### What happens if a decision references multiple precedents?

The BFS walker follows all outgoing `PRECEDENT_FOR` edges from each node, creating branching paths in the audit trail. Each branch is recorded as a separate hop in the `hops` list with its own confidence decay calculation. The `overall_confidence` field represents the product of weights along the primary path, but investigators can examine individual hop metadata to understand divergent reasoning paths.