How to Build a Causal Decision Chain with Provenance in Semantica

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 with the provenance logging in 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 and are the only relationships considered by the causal traversal engine.

CausalChainAnalyzer

The CausalChainAnalyzer class (located in 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, 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, 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.

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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →