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 anotherINFLUENCED– Partial or supporting influence on a downstream decisionPRECEDENT_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
DecisionTraceEventstores a SHA-256 hash of its content plus theprevious_hash, creating a cryptographic ledger that detects any tampering with historical records. -
Temporal consistency – The
trace_at_timeparameter 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, andPRECEDENT_FORedges (as implemented ininterpret_causal_distance), preventing accidental inclusion of correlational or associative relationships that do not represent true causation.
Summary
- Model decisions as nodes using
record_decisionfromsemantica/context/decision_methods.pyto createDecisionentities with unique identifiers. - Link causality explicitly via
CAUSED,INFLUENCED, orPRECEDENT_FORedges to establish directional relationships in the graph. - Secure provenance cryptographically by invoking
capture_decision_trace, which generates immutableDecisionTraceEventnodes with hash-linking. - Traverse chains programmatically using
CausalChainAnalyzer.get_causal_chain, specifying direction and depth to retrieve enrichedDecisionobjects withcausal_distancemetadata. - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →