How Semantica's ContextGraph Tracks Decision Provenance Using W3C PROV-O
Semantica tracks decision provenance using W3C PROV-O through three integrated components: ContextGraph.record_decision creates decision entities, ProvenanceEntry schema maps to PROV-O concepts, and ProvenanceManager.track_entity persists lineage with cryptographic integrity.
Semantica is an open-source framework for semantic AI systems that records every decision in a ContextGraph with full W3C PROV-O compliance. Decision provenance tracking ensures auditability, versioning, and lineage reconstruction for AI-driven outcomes. This article examines the implementation details, source file locations, and code patterns that make Semantica's provenance system production-ready.
The Three-Component Architecture
Semantica's provenance pipeline splits responsibility across distinct layers:
| Component | Source File | Core Responsibility |
|---|---|---|
ContextGraph.record_decision |
semantica/context/context_graph.py |
Creates decision records with normalized temporal bounds |
ProvenanceEntry dataclass |
semantica/provenance/schemas.py |
Defines PROV-O field mappings |
ProvenanceManager.track_entity |
semantica/provenance/manager.py |
Persists provenance with hash-chain integrity |
Each component transforms raw decision data into standards-compliant provenance graphs that can be exported as RDF/Turtle for interoperability.
Recording Decisions in ContextGraph
The record_decision method in semantica/context/context_graph.py (lines 3174–3222) validates inputs, normalizes temporal data, and stores the decision.
# semantica/context/context_graph.py
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, int, float, datetime]] = None,
valid_until: Optional[Union[str, int, float, datetime]] = None,
**kwargs
) -> str:
decision_id = str(uuid.uuid4())
normalized_valid_from = _normalize_temporal_input(valid_from)
normalized_valid_until = _normalize_temporal_input(valid_until)
decision = {
"id": decision_id,
"category": category,
"scenario": scenario,
"reasoning": reasoning,
"outcome": outcome,
"confidence": confidence,
"entities": entities or [],
"decision_maker": decision_maker,
"timestamp": datetime.utcnow().timestamp(),
"valid_from": normalized_valid_from,
"valid_until": normalized_valid_until,
"metadata": metadata or {},
**kwargs,
}
self._add_decision_to_graph(decision)
return decision_id
Key operations performed:
- UUID generation for immutable decision identification
- Temporal normalization via
_normalize_temporal_input(ISO-8601 conversion persemantica/utils/helpers.pylines 73–89) - Domain-time capture (
valid_from,valid_until) distinct from system-time - Graph persistence through
_add_decision_to_graph
The returned decision_id becomes the entity_id in the provenance layer.
ProvenanceEntry: The PROV-O Schema Mapping
The ProvenanceEntry dataclass in semantica/provenance/schemas.py (lines 36–66) implements the W3C PROV-O ontology as native Python types:
| Field | PROV-O Concept | Semantic Purpose |
|---|---|---|
entity_id |
prov:Entity |
The decision itself |
entity_type |
rdf:type |
Classification (e.g., "decision") |
activity_id |
prov:Activity |
The recording action |
agent_id, agent_type |
prov:Agent |
Decision maker or system |
role |
prov:hadRole |
Function in the activity |
timestamp |
prov:generatedAtTime |
System-time of creation |
valid_from / valid_until |
Domain extension | Business-time validity |
parent_entity_id |
prov:wasDerivedFrom |
Correction lineage |
previous_version_id |
prov:specializationOf |
Version succession |
checksum / previous_checksum |
Integrity extension | Tamper detection |
This schema design separates system-time (timestamp, prov:generatedAtTime) from domain-time (valid_from, valid_until), enabling retroactive corrections without destroying audit history.
Persisting Provenance with ProvenanceManager
The ProvenanceManager.track_entity method in semantica/provenance/manager.py (lines 66–84) bridges decision creation and PROV-O storage:
# semantica/provenance/manager.py – excerpt
def track_entity(self, entity_id: str, source: str,
metadata: Optional[Dict[str, Any]] = None, **kwargs):
agent_info = self._resolve_agent_kwargs(kwargs)
activity_info = self._resolve_activity_kwargs(kwargs, "entity_tracking")
entry = ProvenanceEntry(
entity_id=entity_id,
entity_type=kwargs.get("entity_type", "entity"),
activity_id=activity_info["activity_id"],
agent_id=agent_info["agent_id"],
agent_type=agent_info["agent_type"],
is_automated=agent_info["is_automated"],
role=agent_info["role"],
source_document=source,
source_location=kwargs.get("source_location"),
confidence=kwargs.get("confidence", 1.0),
metadata=metadata or {},
first_seen=utc_now_iso(),
last_updated=utc_now_iso(),
valid_from=kwargs.get("valid_from"),
valid_until=kwargs.get("valid_until"),
)
return self._save_entry(entry)
The manager performs three critical functions:
- Checksum computation – SHA-256 hashes create an immutable chain (
checksum,previous_checksum) - Lineage linking – Connects to parent entities for corrections or versioning
- Backend abstraction – Supports in-memory or SQLite storage
Lineage retrieval via get_lineage (lines 752–782) traverses these links to reconstruct full provenance chains.
Complete Integration Example
from semantica.context import ContextGraph
from semantica.provenance import ProvenanceManager
# Initialize components
graph = ContextGraph()
prov_mgr = ProvenanceManager()
# Create decision in ContextGraph
decision_id = graph.record_decision(
category="loan_approval",
scenario="First-time home-buyer",
reasoning="Excellent credit score",
outcome="approved",
confidence=0.95,
entities=["customer_123", "property_456"],
valid_from="2023-01-01",
valid_until="2024-01-01",
metadata={"risk_score": 0.2},
)
# Mirror to provenance layer
prov_mgr.track_entity(
entity_id=decision_id,
source="decision_log",
metadata={
"category": "loan_approval",
"scenario": "First-time home-buyer",
"outcome": "approved",
"confidence": 0.95,
},
entity_type="decision",
agent="automated_engine",
activity="decision_recording",
)
# Export as W3C PROV-O RDF
turtle_output = prov_mgr.export_prov(decision_id)
The export_prov method (line 378) generates standards-compliant RDF for external provenance tools.
PROV-O Compliance Features
Semantica's implementation satisfies W3C PROV-O requirements while adding operational safeguards:
- Entity-Activity-Agent pattern – Every decision traces to who (
agent), what (activity), and the resultingentity - Derivation tracking –
prov:wasDerivedFromlinks corrected decisions to their predecessors - Specialization –
prov:specializationOfhandles versioning of the same logical decision - Temporal precision – Dual timestamping separates observation time from validity period
- Cryptographic integrity – Hash chains detect tampering beyond standard PROV-O
Summary
ContextGraph.record_decisioncreates decision entities with UUIDs and normalized temporal bounds insemantica/context/context_graph.pyProvenanceEntrymaps 15+ fields to W3C PROV-O concepts (prov:Entity,prov:Activity,prov:Agent,prov:wasDerivedFrom) insemantica/provenance/schemas.pyProvenanceManager.track_entitypersists provenance with SHA-256 checksum chains and lineage linking insemantica/provenance/manager.py- The
export_provmethod produces interoperable RDF/Turtle for external provenance analysis - Temporal normalization utilities in
semantica/utils/helpers.pyensure ISO-8601 compliance across all timestamps
Frequently Asked Questions
What is W3C PROV-O and why does Semantica use it?
W3C PROV-O is the ontology-based standard for provenance interchange on the web. Semantica adopts it to ensure decision audit records are interoperable with corporate governance tools, regulatory systems, and academic provenance research. The standard defines Entity, Activity, and Agent classes with relationships like wasDerivedFrom and generatedAtTime—all directly implemented in Semantica's ProvenanceEntry schema.
How does Semantica handle decision corrections without breaking audit trails?
Corrections create new entities linked via prov:wasDerivedFrom to the original. The parent_entity_id field in ProvenanceEntry establishes this derivation chain, while previous_version_id tracks versioning through prov:specializationOf. The original decision remains immutable; the new decision explicitly references its predecessor, enabling full lineage reconstruction without data loss.
Can Semantica export provenance for external compliance audits?
Yes, via ProvenanceManager.export_prov which generates W3C-compliant RDF/Turtle. The export includes all PROV-O entities, activities, agents, and relationships from a decision's lineage. This output validates against standard PROV-O validators and loads into tools like Provenance Explorer or corporate SIEM systems for regulatory reporting.
What storage backends does ProvenanceManager support?
In-memory and SQLite backends are built in, with extensible interfaces. The manager abstracts storage through a backend protocol, defaulting to SQLite for persistence across process restarts. The checksum and previous_checksum fields maintain integrity regardless of backend, enabling verification via verify_checksum even when data moves between systems.
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 →