# How Semantica's ContextGraph Tracks Decision Provenance Using W3C PROV-O

> Semantica's ContextGraph tracks decision provenance with W3C PROV-O. Discover how it records decisions, maps to PROV-O, and persists lineage with cryptographic integrity for auditable AI.

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

---

**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`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) | Creates decision records with normalized temporal bounds |
| `ProvenanceEntry` dataclass | [`semantica/provenance/schemas.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/schemas.py) | Defines PROV-O field mappings |
| `ProvenanceManager.track_entity` | [`semantica/provenance/manager.py`](https://github.com/semantica-agi/semantica/blob/main/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`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py) ([lines 3174–3222](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py#L3174)) validates inputs, normalizes temporal data, and stores the decision.

```python

# 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 per [`semantica/utils/helpers.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/utils/helpers.py) [lines 73–89](https://github.com/semantica-agi/semantica/blob/main/semantica/utils/helpers.py#L73))
- **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`](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/schemas.py) ([lines 36–66](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/schemas.py#L36)) 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`](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/manager.py) ([lines 66–84](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/manager.py#L66)) bridges decision creation and PROV-O storage:

```python

# 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:

1. **Checksum computation** – SHA-256 hashes create an immutable chain (`checksum`, `previous_checksum`)
2. **Lineage linking** – Connects to parent entities for corrections or versioning
3. **Backend abstraction** – Supports in-memory or SQLite storage

Lineage retrieval via `get_lineage` ([lines 752–782](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/manager.py#L752)) traverses these links to reconstruct full provenance chains.

## Complete Integration Example

```python
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](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/manager.py#L378)) 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 resulting `entity`
- **Derivation tracking** – `prov:wasDerivedFrom` links corrected decisions to their predecessors
- **Specialization** – `prov:specializationOf` handles 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_decision` creates decision entities with UUIDs and normalized temporal bounds in [`semantica/context/context_graph.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/context_graph.py)
- `ProvenanceEntry` maps 15+ fields to W3C PROV-O concepts (`prov:Entity`, `prov:Activity`, `prov:Agent`, `prov:wasDerivedFrom`) in [`semantica/provenance/schemas.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/schemas.py)
- `ProvenanceManager.track_entity` persists provenance with SHA-256 checksum chains and lineage linking in [`semantica/provenance/manager.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/provenance/manager.py)
- The `export_prov` method produces interoperable RDF/Turtle for external provenance analysis
- Temporal normalization utilities in [`semantica/utils/helpers.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/utils/helpers.py) ensure 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.