Files
freemo a0ac30d5de
CI / lint (pull_request) Successful in 21s
CI / typecheck (pull_request) Successful in 52s
CI / security (pull_request) Successful in 55s
CI / quality (pull_request) Successful in 39s
CI / build (pull_request) Successful in 29s
CI / helm (pull_request) Successful in 23s
CI / unit_tests (pull_request) Failing after 6m49s
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 17m22s
CI / integration_tests (pull_request) Failing after 23m1s
CI / coverage (pull_request) Successful in 11m3s
CI / status-check (pull_request) Failing after 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 56m57s
docs: update and extend documentation for v3.8.0 changes
- docs/reference/plan_cli.md: add note about legacy/v3 plan workflow
  mixing being disallowed (introduced in v3.8.0, issue #1577)
- docs/reference/uko_runtime.md: document v3.8.0 provenance tracking
  (sourceResource, validFrom, isCurrent on typed triples) and revision
  chain for temporal queries across indexing runs (issue #891)
- docs/reference/decision_correction.md: add CorrectionAttemptRecord
  section documenting the correction_attempts table, state lifecycle,
  repository API, and usage examples (issue #920)
- docs/modules/uko-provenance.md: new module guide for UKO provenance
  tracking covering purpose, how provenance is attached, revision chain
  mechanics, query patterns, persistence format, BDD coverage, and gotchas
2026-04-05 20:05:48 +00:00

6.0 KiB

UKO Provenance Tracking

Package: cleveragents.application.services (UKOIndexer, UKOGraphPersistence) Introduced: v3.8.0 (issue #891)

This module guide covers the provenance tracking and temporal versioning capabilities added to the UKO runtime in v3.8.0. Provenance metadata enables audit trails, point-in-time queries, and historical analysis of the knowledge graph.

For the full UKO runtime API reference, see docs/reference/uko_runtime.md. For the temporal data model, see docs/reference/temporal_data_model.md.


Purpose

Every typed triple produced by UKOIndexer.index_graph() now carries three provenance fields:

Field OWL Property Type Description
sourceResource uko:sourceResource str (ULID) The CleverAgents resource that produced this triple
validFrom uko:validFrom datetime (UTC) When this triple version became valid
isCurrent uko:isCurrent bool Whether this is the current (non-superseded) version

These fields are defined in UKO Layer 0 (see docs/reference/uko.md).


How Provenance Is Attached

When UKOIndexer.index_graph() runs, it calls attach_provenance() on every triple before storing it in the graph backend:

# Conceptual flow (simplified)
triples = analyzer.analyze(content, resource_uri)
implicit = inference_engine.infer(triples)
all_triples = triples + implicit

for triple in all_triples:
    provenanced = ProvenancedTriple(
        triple=triple,
        provenance=ProvenanceMetadata(
            source_resource=resource.id,
            source_path=resource.location,
            valid_from=datetime.now(UTC),
            is_current=True,
        ),
    )
    graph_backend.add_triple(project, provenanced)

Revision Chain

A revision chain tracks the full history of ontology state across indexing runs. When a resource is re-indexed:

  1. All existing triples for that resource are marked isCurrent=False and validUntil is set to the re-indexing timestamp.
  2. New triples are created with isCurrent=True and a fresh validFrom.
  3. A new revision entry is appended to the chain, linked to the previous revision.

This enables point-in-time queries: "what did the knowledge graph look like at timestamp T?"

Revision 1 (2026-03-01)  →  Revision 2 (2026-03-15)  →  Revision 3 (2026-04-01)
   isCurrent=False            isCurrent=False              isCurrent=True
   validUntil=2026-03-15      validUntil=2026-04-01        validUntil=None

Querying Provenance

Current Triples Only

from cleveragents.application.services.uko_query_interface import UKOQueryInterface

query = UKOQueryInterface(graph_backend=backend, project="local/my-app")

# Classify a resource using only current triples
result = query.classify_resource("01HXYZ...")
# result.layer, result.primary_type, result.all_types, result.implicit_relations

Historical Triples (Point-in-Time)

from datetime import datetime, timezone
from cleveragents.application.services.uko_persistence import UKOGraphPersistence

persistence = UKOGraphPersistence(
    graph_backend=backend,
    project="local/my-app",
    persist_path=Path("~/.config/cleveragents/uko-graph.json").expanduser(),
)

# Query triples valid at a specific point in time
historical = persistence.query_at_time(
    timestamp=datetime(2026, 3, 15, tzinfo=timezone.utc)
)

Triples by Source Resource

# Get all triples (current and historical) from a specific resource
triples = graph_backend.query(
    project="local/my-app",
    sparql="""
    SELECT ?s ?p ?o WHERE {
        ?s ?p ?o .
        ?s uko:sourceResource "01HXYZ..." .
    }
    """
)

Persistence Format

UKOGraphPersistence serialises the full graph (including provenance) to JSON. The format includes all triples with their provenance metadata:

{
    "version": "1",
    "project": "local/my-app",
    "revision": 3,
    "triples": [
        {
            "subject": "uko-py:class/AuthService",
            "predicate": "rdf:type",
            "object": "uko-oo:Class",
            "provenance": {
                "source_resource": "01HXYZ...",
                "source_path": "src/auth/service.py",
                "source_range": "15:1-87:0",
                "valid_from": "2026-04-01T10:00:00Z",
                "valid_until": null,
                "is_current": true
            }
        }
    ]
}

BDD Coverage

The provenance tracking feature is covered by 47 BDD scenarios in features/uko_provenance.feature:

  • Typed triple provenance fields (sourceResource, validFrom, isCurrent)
  • Temporal queries (point-in-time, current-only, historical)
  • Revision chain creation and traversal
  • Graph persistence with provenance (save/restore round-trip)
  • Re-indexing marks previous triples as historical

Gotchas

  • Naive datetimes are rejected. All validFrom and validUntil values must be timezone-aware UTC datetimes. Passing a naive datetime raises ValueError.
  • Re-indexing is not atomic. If index_graph() fails partway through, some triples may be marked historical while their replacements were not yet written. The indexer logs a warning in this case; re-running reindex_resource() will repair the state.
  • In-memory backend has no persistence. The InMemoryGraphIndexBackend used in tests does not persist across process restarts. Use UKOGraphPersistence with a file backend in production.