# How to Build a Causal Decision Chain with Provenance in Semantica

> Learn how to build a causal decision chain with provenance in Semantica. Model decisions, link them with causal edges, and use CausalChainAnalyzer for secure audit trails.

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

---

**To build a causal decision chain with provenance in Semantica, model each decision as a `Decision` node, link them with causal edge types (`CAUSED`, `INFLUENCED`, `PRECEDENT_FOR`), and use `CausalChainAnalyzer` to traverse the graph while `capture_decision_trace` generates immutable, hash-chained audit events that cryptographically secure the provenance trail.**

Semantica treats decisions as first-class graph entities, enabling you to reconstruct the exact lineage of any outcome through cryptographic audit trails. By combining the traversal capabilities in [`semantica/context/causal_analyzer.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/causal_analyzer.py) with the provenance logging in [`semantica/context/decision_methods.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_methods.py), you create reproducible, tamper-evident records of how decisions influence one another across complex workflows.

## Core Components for Causal Chains

Semantica implements causal decision tracking through three interconnected components that work directly with your graph store.

### Decision Nodes and Causal Edges

Every decision is stored as a **`Decision`** node with a unique `decision_id`. The framework recognizes three semantic edge types that express causality:

- **`CAUSED`** – Direct causal relationship where one decision necessitates another
- **`INFLUENCED`** – Partial or supporting influence on a downstream decision  
- **`PRECEDENT_FOR`** – Historical precedent that justifies but does not mandate a subsequent decision

These edge types are defined in [`semantica/context/graph_schema.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/graph_schema.py) and are the only relationships considered by the causal traversal engine.

### CausalChainAnalyzer

The **`CausalChainAnalyzer`** class (located in [`semantica/context/causal_analyzer.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/causal_analyzer.py)) performs graph traversals to discover decision lineages. It accepts parameters for `direction` (`"upstream"` to find causes or `"downstream"` to find effects) and `max_depth` to limit traversal scope. The analyzer decorates each returned `Decision` object with a `metadata` dictionary containing `causal_distance`, `recorded_at`, and traversal context.

### Immutable Trace Events

Provenance is secured through **`DecisionTraceEvent`** nodes created by `capture_decision_trace`. Each event stores a SHA-256 hash of its payload and references the `previous_hash`, forming an immutable chain within the graph store. This mechanism, implemented in the `_append_immutable_trace_events` helper inside [`semantica/context/decision_methods.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_methods.py), guarantees that the audit trail cannot be altered without detection.

## Step-by-Step Implementation Workflow

Follow this sequence to construct auditable causal chains in your application.

### 1. Record Base Decisions

Use `record_decision` to persist decision nodes to the graph store. This function, defined in [`semantica/context/decision_methods.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_methods.py), accepts parameters for `category`, `scenario`, `reasoning`, `outcome`, and `confidence`, returning a unique `decision_id` that serves as the anchor for subsequent causal links.

### 2. Establish Causal Relationships

Connect decisions using Cypher queries that create the appropriate causal edge types. The edge should include a `recorded_at` timestamp to support temporal queries.

### 3. Capture Provenance Traces

Call `capture_decision_trace` immediately after recording or executing a decision. This function serializes the decision context, generates cryptographic hashes, and links `DecisionTraceEvent` nodes to the parent `Decision` via `HAS_TRACE_EVENT` relationships.

### 4. Traverse the Causal Chain

Instantiate `CausalChainAnalyzer` with your graph store and invoke `get_causal_chain`. The analyzer first checks if your graph store implements a native `get_causal_chain` method; otherwise, it constructs an optimized Cypher query that walks the three causal edge types, extracts decision records, and computes `causal_distance` from the origin.

### 5. Verify Provenance

Query the trace events attached to any decision in the chain to verify the cryptographic integrity. Each event’s `event_hash` must correctly reference the `previous_hash` of the preceding event in the sequence.

## Complete Working Example

This example demonstrates recording a loan approval decision, linking it to a downstream fund disbursement, capturing audit traces, and querying the causal chain with full provenance.

```python
from datetime import datetime
from semantica.context.decision_methods import record_decision, get_causal_chain, capture_decision_trace
from semantica.context.decision_models import Decision

# 1. Record the root decision (loan approval)

root_id = record_decision(
    graph_store=my_graph,
    category="loan_approval",
    scenario="apply_small_business_loan",
    reasoning="credit_score >= 700",
    outcome="approved",
    confidence=0.93,
)

# 2. Record a downstream decision caused by the approval

downstream_id = record_decision(
    graph_store=my_graph,
    category="loan_disbursement",
    scenario="release_funds",
    reasoning="approved loan from previous step",
    outcome="funds_transferred",
    confidence=0.98,
)

# 3. Create the causal edge (CAUSED) between decisions

my_graph.execute_query(
    """
    MATCH (a:Decision {decision_id: $a}), (b:Decision {decision_id: $b})
    MERGE (a)-[:CAUSED {recorded_at: timestamp()}]->(b)
    """,
    {"a": root_id, "b": downstream_id},
)

# 4. Capture immutable provenance trace for the root decision

capture_decision_trace(
    decision=Decision(
        decision_id=root_id,
        category="loan_approval",
        scenario="apply_small_business_loan",
        reasoning="credit_score >= 700",
        outcome="approved",
        confidence=0.93,
        timestamp=datetime.utcnow(),
        decision_maker="ai_agent",
    ),
    cross_system_context={"origin": "loan_service"},
    graph_store=my_graph,
)

# 5. Query the downstream causal chain

chain = get_causal_chain(
    graph_store=my_graph,
    decision_id=root_id,
    direction="downstream",
    max_depth=5,
)

for step in chain:
    print(f"→ {step.decision_id}: {step.scenario} (distance={step.metadata['causal_distance']})")

# 6. Verify the cryptographic provenance trail

trace = my_graph.execute_query(
    """
    MATCH (d:Decision {decision_id: $did})-[:HAS_TRACE_EVENT]->(t:DecisionTraceEvent)
    RETURN t.event_type, t.event_timestamp, t.event_hash, t.previous_hash
    ORDER BY t.event_index
    """,
    {"did": root_id},
)

```

## Why Provenance is Reliable

Semantica ensures causal chain integrity through three architectural safeguards:

- **Hash-chained immutability** – Every `DecisionTraceEvent` stores a SHA-256 hash of its content plus the `previous_hash`, creating a cryptographic ledger that detects any tampering with historical records.

- **Temporal consistency** – The `trace_at_time` parameter allows analysts to limit graph traversals to the state of knowledge at a specific moment, ensuring that the causal chain reflects only decisions and edges that existed at that timestamp.

- **Explicit causal semantics** – The analyzer strictly filters for `CAUSED`, `INFLUENCED`, and `PRECEDENT_FOR` edges (as implemented in `interpret_causal_distance`), preventing accidental inclusion of correlational or associative relationships that do not represent true causation.

## Summary

- **Model decisions as nodes** using `record_decision` from [`semantica/context/decision_methods.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/decision_methods.py) to create `Decision` entities with unique identifiers.
- **Link causality explicitly** via `CAUSED`, `INFLUENCED`, or `PRECEDENT_FOR` edges to establish directional relationships in the graph.
- **Secure provenance cryptographically** by invoking `capture_decision_trace`, which generates immutable `DecisionTraceEvent` nodes with hash-linking.
- **Traverse chains programmatically** using `CausalChainAnalyzer.get_causal_chain`, specifying direction and depth to retrieve enriched `Decision` objects with `causal_distance` metadata.
- **Audit immutably** by querying trace events to verify hash chains and temporal consistency of the decision lineage.

## Frequently Asked Questions

### What is the difference between the `CAUSED`, `INFLUENCED`, and `PRECEDENT_FOR` edge types?

**`CAUSED`** indicates a direct deterministic relationship where the source decision necessitates the target decision. **`INFLUENCED`** represents a partial or probabilistic impact where the source decision affects but does not mandate the outcome. **`PRECEDENT_FOR`** denotes a historical justification or legal/ethical precedent that supports the target decision without forcing it. The `CausalChainAnalyzer` treats all three as valid causal links during traversal but preserves the distinction in metadata for interpretability.

### How does the hash chain in `DecisionTraceEvent` ensure provenance integrity?

Each trace event computes a SHA-256 hash of its payload concatenated with the `previous_hash` from the preceding event in the sequence, as implemented in `_append_immutable_trace_events`. This creates a Merkel-like chain where altering any historical event would invalidate all subsequent hashes. When auditing, you can verify that each event’s `event_hash` correctly resolves given the stored `previous_hash`, proving the chain has not been tampered with since recording.

### Can I query causal chains in both temporal directions?

Yes. The `get_causal_chain` method accepts a `direction` parameter that accepts `"upstream"` (to identify root causes and antecedent decisions) or `"downstream"` (to identify effects and consequent decisions). You can also combine directional queries with `trace_at_time` to view the chain as it existed at a specific historical moment, enabling you to reconstruct decision states for retrospective analysis or regulatory audit.

### What is the performance impact of deep causal chain queries?

The `CausalChainAnalyzer` mitigates performance costs by first checking for native graph store implementations of `get_causal_chain` before falling back to generated Cypher. For deep traversals (depth > 10), the analyzer supports `max_depth` limits to prevent unbounded graph walks. Since causal edges are explicitly typed, the underlying database can leverage indexes on `CAUSED`, `INFLUENCED`, and `PRECEDENT_FOR` relationships, ensuring that provenance queries remain performant even with millions of decision nodes.