# Temporal Data Model (Revision-Aware RDF) The Temporal Data Model enables revision tracking of UKO knowledge graph nodes across three storage tiers (hot/warm/cold). When code changes, existing nodes are **not deleted** -- they are marked historical and a new revision node is created with a back-link to the predecessor. Based on `docs/specification.md` lines 41940--42499. ## Architecture ``` Code Change ──► Analyzer ──► TemporalService.create_revision() │ ┌──────────┴──────────┐ │ TemporalBackend │ │ ┌─────────────────┐ │ │ │ Old node: │ │ │ │ isCurrent=false│ │ │ │ validUntil=now │ │ │ ├─────────────────┤ │ │ │ New node: │ │ │ │ isCurrent=true │ │ │ │ isRevisionOf= │ │ │ │ old_uri │ │ │ └─────────────────┘ │ └──────────────────────┘ ``` ## Domain Models All models are frozen Pydantic `BaseModel` instances (immutable value objects). ### TemporalMetadata Temporal fields carried by every UKO InformationUnit. Maps to the OWL temporal properties defined in UKO Layer 0. | Field | Type | Default | Spec Property | |-------|------|---------|---------------| | `valid_from` | `datetime` (UTC) | **required** | `uko:validFrom` | | `valid_until` | `datetime \| None` | `None` | `uko:validUntil` | | `is_current` | `bool` | `True` | `uko:isCurrent` | | `is_revision_of` | `str \| None` | `None` | `uko:isRevisionOf` | ### TemporalNode A UKO InformationUnit with full temporal metadata and provenance. | Field | Type | Default | Description | |-------|------|---------|-------------| | `node_uri` | `str` | **required** | Unique UKO URI (e.g., `uko-py:class/Auth_v2`) | | `source_resource` | `str` | **required** | ULID of originating resource | | `source_path` | `str` | **required** | File path within the resource | | `source_range` | `str \| None` | `None` | Source range (e.g., `"15:1-87:0"`) | | `temporal` | `TemporalMetadata` | **required** | Embedded temporal metadata | ### RevisionChain Ordered chain of node versions for a single UKO concept. | Field | Type | Default | Description | |-------|------|---------|-------------| | `current_uri` | `str` | **required** | URI of the current head node | | `predecessors` | `tuple[str, ...]` | `()` | Predecessor URIs (oldest-first) | Properties: `depth` (total versions), `all_uris` (predecessors + current). ### TierRetentionConfig Retention policy for warm and cold tiers. | Field | Type | Default | Config Key | |-------|------|---------|------------| | `warm_retention_hours` | `int` (≥1) | `24` | `context.tiers.warm.retention-hours` | | `cold_retention_days` | `int` (≥1) | `90` | `context.tiers.cold.retention-days` | ### TierQueryResult Result from a tier-aware temporal query. | Field | Type | Default | Description | |-------|------|---------|-------------| | `nodes` | `tuple[TemporalNode, ...]` | `()` | Matching nodes | | `tier` | `ContextTier` | **required** | HOT / WARM / COLD | | `temporal_scope` | `TemporalScope` | **required** | CURRENT / RECENT / ALL | ## TemporalBackend Protocol ```python class TemporalBackend(Protocol): def store_node(self, node) -> None: ... def create_revision(self, current_uri, new_node, timestamp) -> TemporalNode: ... def get_current(self, node_uri_base) -> TemporalNode | None: ... def get_history(self, node_uri_base, temporal_scope) -> tuple[TemporalNode, ...]: ... def get_revision_chain(self, node_uri) -> RevisionChain: ... def query_by_tier(self, tier, temporal_scope, retention) -> TierQueryResult: ... def mark_historical(self, node_uri, timestamp) -> TemporalNode: ... ``` ## Three Storage Tiers | Tier | Content | Temporal Filter | Retention | |------|---------|-----------------|-----------| | **Hot** | Current UKO graph | `isCurrent = true` only | Until removed | | **Warm** | Recent context | Current + recently-expired | `warm_retention_hours` (24h) | | **Cold** | All historical | All temporal versions | `cold_retention_days` (90d) | ## Revision Chain Mechanics 1. Old node: `is_current` → `False`, `valid_until` → timestamp 2. New node: `is_current` → `True`, `valid_from` → timestamp 3. New node: `is_revision_of` → old node URI 4. Old node's `valid_from` is preserved unchanged ## v1 Limitations - `InMemoryTemporalBackend` stores all nodes in a `dict`; no persistence. - No SPARQL query helpers yet (deferred to graph backend integration). - `temporal-archaeology` strategy stub exists but does not yet use the temporal backend (wiring deferred to strategy integration milestone).