Files
semantica/docs/migration/kg-provenance-tracker.md
T
KaifAhmad1 db4361ad46 feat(provenance): close PROV-O compliance gaps and high-stakes trust blockers (closes #825)
Part A - high-stakes trust blockers:
- Invalidation tombstones via ProvenanceManager.invalidate() (archive-then-append,
  never mutates or deletes) instead of hard delete
- Hash-chained integrity: sequence_id/previous_checksum chain every entry to its
  predecessor; new verify_chain() detects wholesale row deletion that a lone
  per-row checksum cannot
- Typed Agent (AgentRecord: agent_type/is_automated) and Activity (ActivityRecord:
  start/end timing), wired through all 18 *_provenance.py wrappers
- Split parent_entity_id into previous_version_id (correction) vs derived_from_id
  (cross-source derivation), additive alongside the legacy combined field
- Downstream/descendant lineage traversal (get_descendants/trace_descendants,
  reverse BFS) closing the dead direction="downstream" code path in the
  Explorer's provenance route
- Qualified Association+hadRole and Invalidation in export_prov()
- New CLI: provenance invalidate|verify-chain|descendants

Part B - general PROV-O spec completeness:
- Qualified Generation/Usage/Derivation in export_prov()
- wasAssociatedWith, actedOnBehalfOf, wasInformedBy relations
- Bitemporal fields (valid_from/valid_until/revision_type/supersedes) plus
  revision_history()/query_recorded_between(), closing the deprecated
  kg.ProvenanceTracker's "no direct equivalent yet" migration gaps
- prov:Bundle/hadMember membership via bundle_id
- Configurable base_uri (--base-uri CLI flag), shared by RDFExporter's
  NamespaceManager and OWLExporter's default ontology_uri so KG/OWL/PROV
  exports co-resolve under one namespace instead of three hardcoded ones

Bugs fixed along the way:
- agent_id was a dead field: no track_* method read it from kwargs
- track_entities_batch silently absorbed typed kwargs into the metadata blob
- compute_checksum() had to exclude entity_id itself: hashing it made
  track_entity's versioning-archive relabel permanently orphan any entry
  already chained from the pre-relabel checksum, a false-positive "broken
  chain" for a legitimate rename
- InMemoryStorage.get_chain_head() ignored the committed head whenever the
  current transaction had staged entries, corrupting the next chain link
- several new ProvenanceEntry fields were wired into the dataclass and
  export_prov() but not into SQLiteStorage's DDL/INSERT/row-mapping;
  InMemoryStorage masked the gap. Added a permanent round-trip regression
  test to catch this class of bug for future field additions

Flagged, not fixed (separate pre-existing issues, out of scope for #825):
- pipeline/pipeline_provenance.py imports a nonexistent module and wraps a
  Pipeline dataclass with no run() method
- most *_provenance.py wrappers' backing classes are themselves missing or
  incomplete (context_manager, deduplicator, normalizer, etc.)
- kg_provenance.py passes entity_type inside metadata={} instead of as a
  top-level track_entity() kwarg across most of its call sites
2026-08-03 22:28:48 +05:30

3.4 KiB

title, description
title description
Migrating from kg.ProvenanceTracker How to move from the deprecated semantica.kg.ProvenanceTracker to the unified semantica.provenance.ProvenanceManager.

Why migrate

semantica.kg.ProvenanceTracker is deprecated and will be removed in a future major version. It was a standalone, in-memory implementation that never delegated to the unified provenance backend — semantica.provenance.ProvenanceManager is that backend, and is now the supported way to track entity and relationship provenance across every Semantica module (see the Provenance & Audit Trails guide).

Every method on kg.ProvenanceTracker now emits a DeprecationWarning on use, but existing code keeps working unchanged until the class is removed — there is no forced migration deadline yet.

Method mapping

kg.ProvenanceTracker ProvenanceManager equivalent Notes
ProvenanceTracker() ProvenanceManager() ProvenanceManager also accepts storage_path= for SQLite persistence instead of in-memory only.
track_entity(entity_id, source, metadata) track_entity(entity_id, source, metadata) Same call shape. ProvenanceManager additionally auto-links each update to its prior version via parent_entity_id.
get_all_sources(entity_id) get_all_sources(entity_id) Field name differs: the kg tracker returns each record's time under "recorded_at"; ProvenanceManager returns "timestamp".
clear(entity_id=None) clear() ProvenanceManager.clear() clears all provenance data; there is no per-entity clear yet.
query_recorded_between(start, end) query_recorded_between(start, end) Same call shape; filters by timestamp (ISO 8601 string comparison) across all tracked entries, not just one entity.
revision_history(fact_id) revision_history(fact_id) Same call shape and return shape (version, valid_from, valid_until, recorded_at, author, optional revision_type/supersedes) — walks the entity's previous_version_id chain rather than a flat per-entity dict.
export_audit_log(fact_ids, format) No direct equivalent yet Build the export from get_lineage() output, or serialize get_statistics() for a summary view.

Methods with no direct equivalent are not planned to be reimplemented on kg.ProvenanceTracker — they will need a small adapter in caller code, or a feature request against ProvenanceManager if you rely on them heavily.

Example

# Before
from semantica.kg import ProvenanceTracker

tracker = ProvenanceTracker()
tracker.track_entity("entity_1", source="doc_1", metadata={"confidence": 0.9})
sources = tracker.get_all_sources("entity_1")  # [{"source": ..., "recorded_at": ..., "confidence": 0.9}]

# After
from semantica.provenance import ProvenanceManager

prov = ProvenanceManager()
prov.track_entity("entity_1", source="doc_1", metadata={"confidence": 0.9})
sources = prov.get_all_sources("entity_1")  # [{"source": ..., "timestamp": ..., "metadata": {...}, ...}]

Suppressing the warning during migration

If you need to keep using kg.ProvenanceTracker temporarily and want to silence the warning while you plan the switch:

import warnings

with warnings.catch_warnings():
    warnings.simplefilter("ignore", DeprecationWarning)
    tracker = ProvenanceTracker()

This is a stopgap, not a fix — plan to move to ProvenanceManager before kg.ProvenanceTracker is removed.