# How to Use ContextGraph for Decision Recording and Tracing in Semantica

> Learn to use ContextGraph for decision recording and tracing in Semantica. Utilize record_decision() and trace_decision_causality() for structured metadata and BFS causal traversal.

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

---

**The `ContextGraph` class provides built-in `record_decision()` and `trace_decision_causality()` methods to capture structured decision metadata and traverse causal relationships through breadth-first search.**

The **ContextGraph** is the core knowledge structure in [semantica-agi/semantica](https://github.com/semantica-agi/semantica) that enables agentic systems to maintain an auditable history of choices. This guide demonstrates how to use ContextGraph for decision recording and tracing, with direct references to the implementation in [[`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py).

## Creating a ContextGraph with Decision Support

Before recording decisions, initialize a `ContextGraph` with optional analytics enabled. The constructor accepts flags for centrality analysis, community detection, and node embeddings—all useful for later exploration of decision clusters.

```python
from semantica.context import ContextGraph

graph = ContextGraph(
    advanced_analytics=True,
    centrality_analysis=True,
    community_detection=True,
    node_embeddings=True,
)

```

These settings activate knowledge graph algorithms that power similarity searches and precedent discovery discussed later.

## Recording Decisions with `record_decision()`

The `record_decision()` method (lines 4334–4369 in [[`context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/context_graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)) creates a persistent decision node with comprehensive metadata.

### Method Signature and Validation

```python
def record_decision(
    self,
    category: str,
    scenario: str,
    reasoning: str,
    outcome: str,
    confidence: float,
    entities: Optional[List[str]] = None,
    decision_maker: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None,
    valid_from: Optional[Union[str, datetime]] = None,
    valid_until: Optional[Union[str, datetime]] = None,
    **kwargs
) -> str:

```

The implementation performs strict **input validation** (lines 4334–4348):

- **Required fields**: `category`, `scenario`, `reasoning`, `outcome`, and `confidence` are checked for type, length constraints, and valid value ranges
- **Temporal normalization**: `valid_from` and `valid_until` pass through `_normalize_temporal_input()` (lines 4327–4332), converting to tz-naive UTC ISO strings
- **UUID generation**: Each decision receives a unique identifier with attached timestamp (lines 4349–4350)

### Internal Graph Insertion

The private helper `_add_decision_to_graph()` (lines 4448–4664) handles three critical indexing operations:

1. **Node creation**: Stores the decision with type `"decision"` containing all validated fields
2. **Index updates**: Maintains `_decision_index`, `_entity_index`, and `_temporal_index` for fast lookups
3. **Entity linking**: Creates edges between the decision node and any referenced entities

### Complete Recording Example

```python
decision_id = graph.record_decision(
    category="fraud_detection",
    scenario="Multiple failed login attempts from new device",
    reasoning="IP address flagged in threat intelligence blacklist",
    outcome="account_blocked",
    confidence=0.98,
    entities=["user_42", "ip_203.0.113.5", "device_mobile_android"],
    decision_maker="security_automation_engine",
    metadata={"alert_severity": "critical", " threat_intel_source": "MISP"},
    valid_from="2024-09-01T00:00:00",
    valid_until="2025-09-01T00:00:00",
)

print(f"Decision recorded with ID: {decision_id}")

# Output: 550e8400-e29b-41d4-a716-446655440000

```

The returned UUID serves as the stable anchor for all subsequent tracing and linking operations.

## Building Causal Chains Between Decisions

Decisions gain explanatory power through relationships. The ContextGraph supports canonical causal edge types defined in `_CAUSAL_EDGE_TYPES` and `_CAUSAL_EDGE_ALIASES`.

### Adding Causal Relationships

Use `add_edge()` with causal edge types to express influence:

```python

# Link a precedent decision as direct cause

graph.add_edge(
    source_id=previous_decision_id,
    target_id=decision_id,
    edge_type="CAUSED",
    weight=1.0,
    metadata={"causal_strength": "definitive"}
)

# Or express partial influence

graph.add_edge(
    source_id="risk_model_v3_assessment_001",
    target_id=decision_id,
    edge_type="INFLUENCED",
    weight=0.75,
)

```

Supported causal edge types include `CAUSED`, `INFLUENCED`, `ENABLED`, `PREVENTED`, and their aliases—enabling nuanced provenance modeling.

## Tracing Decision Causality with `trace_decision_causality()`

The `trace_decision_causality()` method (lines 4696–4738) reconstructs the full causal history of any decision through **breadth-first search**.

### Method Signature

```python
def trace_decision_causality(
    self,
    decision_id: str,
    limit: Optional[int] = None,
    depth: int = 5,
    include_entities: bool = True
) -> List[Dict[str, Any]]:

```

### Tracing Behavior

- **Search scope**: Follows only edges whose type appears in `_CAUSAL_EDGE_TYPES` or `_CAUSAL_EDGE_ALIASES`
- **Traversal direction**: Backward from target to sources (provenance), with configurable `depth` (default 5 hops)
- **Result enrichment**: Each returned dictionary contains `source`, `target`, `type`, `weight`, `hop`, and timestamp fields
- **Entity integration**: When `include_entities=True`, adjacent entity nodes enrich the trace with contextual information

### Practical Tracing Example

```python

# Retrieve full causality chain

trace = graph.trace_decision_causality(
    decision_id=decision_id,
    depth=4,
    limit=50,
    include_entities=True
)

print("Causality trace:")
for hop in trace:
    print(f"  [{hop['hop']}] {hop['source']} → {hop['target']}")
    print(f"       Edge: {hop['type']} (weight: {hop.get('weight', 1.0)})")
    if 'timestamp' in hop:
        print(f"       Time: {hop['timestamp']}")

```

This produces a traversable audit trail showing every decision and entity that contributed to the final outcome.

## Finding Decision Precedents

Beyond explicit causal tracing, the ContextGraph supports **semantic precedent discovery** through `find_precedents()` and `find_precedents_by_scenario()`. These methods leverage:

- **Vector similarity**: Embedding-based matching of scenario descriptions
- **Entity overlap**: Shared entities between past and current decisions
- **Category clustering**: Decisions from the same functional domain

Locate these utilities in the same [[`context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/context_graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) file, typically after the core traversal methods.

## End-to-End Decision Workflow

```python
from semantica.context import ContextGraph

# Initialize

graph = ContextGraph(advanced_analytics=True, node_embeddings=True)

# Stage 1: Record initial risk assessment

risk_decision = graph.record_decision(
    category="risk_assessment",
    scenario="Failed security challenge",
    reasoning="Behavioral biometrics anomaly detected",
    outcome="elevated_risk_score",
    confidence=0.87,
    entities=["session_abc123", "user_42"],
    decision_maker="risk_engine_v2",
)

# Stage 2: Record follow-up action, causally linked

block_decision = graph.record_decision(
    category="access_control",
    scenario="Elevated risk score triggered step-up auth",
    reasoning="Risk score exceeded dynamic threshold",
    outcome="session_terminated",
    confidence=0.95,
    entities=["session_abc123", "user_42"],
    decision_maker="access_policy_engine",
)

# Establish causality

graph.add_edge(
    source_id=risk_decision,
    target_id=block_decision,
    edge_type="CAUSED",
    weight=1.0
)

# Stage 3: Trace complete provenance

audit_trail = graph.trace_decision_causality(block_decision, depth=3)

print(f"\nFull audit for {block_decision[:8]}...")
for entry in audit_trail:
    print(f"  Hop {entry['hop']}: {entry['type']} from {entry['source'][:8]}")

```

## MCP Tool Integration

The repository provides higher-level wrappers in [[`semantica/mcp/tools/decisions.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/mcp/tools/decisions.py)](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/decisions.py) that expose `record_decision` as an MCP tool for multi-context processor environments. Similarly, [[`semantica/mcp/tools/graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/mcp/tools/graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/graph.py) wraps causal edge operations for remote graph manipulation.

## Source Reference Summary

| Component | Location | Purpose |
|-----------|----------|---------|
| Core decision recording | [[`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) lines 4334–4664 | `record_decision()` and `_add_decision_to_graph()` |
| Causality tracing | [[`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) lines 4696–4738 | `trace_decision_causality()` BFS implementation |
| MCP decision tools | [[`semantica/mcp/tools/decisions.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/mcp/tools/decisions.py)](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/decisions.py) | Exposed tool interface |
| MCP graph tools | [[`semantica/mcp/tools/graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/mcp/tools/graph.py)](https://github.com/semantica-agi/semantica/blob/main/semantica_mcp/mcp/tools/graph.py) | Edge manipulation helpers |
| Integration tests | [[`tests/vector_store/test_end_to_end_decision_tracking.py`](https://github.com/semantica-agi/semantica/blob/main/tests/vector_store/test_end_to_end_decision_tracking.py)](https://github.com/semantica-agi/semantica/blob/main/tests/vector_store/test_end_to_end_decision_tracking.py) | Workflow validation |

## Summary

- **`record_decision()`** creates validated, indexed decision nodes with UUIDs, temporal bounds, and entity references
- **`trace_decision_causality()`** performs controlled BFS across causal edges to reconstruct decision provenance
- **Causal edge types** (`CAUSED`, `INFLUENCED`, etc.) enable fine-grained modeling of decision relationships
- **Dual indexing** (`_decision_index`, `_temporal_index`) ensures efficient retrieval at scale
- **MCP tooling** exposes these capabilities to external agent orchestration systems

## Frequently Asked Questions

### What makes a recorded decision retrievable for tracing?

The `record_decision()` method automatically indexes decisions in `_decision_index` by UUID, `_entity_index` by referenced entities, and `_temporal_index` by validity period. Without these indexes, `trace_decision_causality()` could not efficiently locate starting nodes or traverse relationships. The implementation guarantees O(1) UUID lookups and O(n) traversal bounded by `depth` and `limit` parameters.

### How does temporal validation work for decisions?

The `_normalize_temporal_input()` helper (lines 4327–4332) converts `valid_from` and `valid_until` to standardized tz-naive UTC ISO strings. This normalization ensures consistent comparisons during temporal range queries and prevents timezone-related provenance errors. Invalid or malformed temporal inputs raise `ValueError` before any graph mutation occurs.

### Can I trace forward from a decision to its effects?

The current `trace_decision_causality()` implementation traverses backward (provenance direction) following incoming causal edges. To trace forward effects, you would either reverse the edge direction in the search or construct an inverse index of outgoing edges. The underlying graph structure in [`NetworkX`](https://networkx.org/) supports both directions—adapt the BFS logic in a subclass or wrapper for downstream impact analysis.

### What confidence threshold should I set for decision recording?

The `confidence` parameter accepts any float in `[0.0, 1.0]` with no enforced minimum threshold in the core implementation. However, downstream analytics in `find_precedents()` and similarity scoring may weight decisions by confidence. The integration tests in [[`test_end_to_end_decision_tracking.py`](https://github.com/semantica-agi/semantica/blob/main/test_end_to_end_decision_tracking.py)](https://github.com/semantica-agi/semantica/blob/main/tests/vector_store/test_end_to_end_decision_tracking.py) demonstrate filtering precedent searches by confidence ≥ 0.7 for high-reliability historical matching.