forked from HAL9000/cleveragents-core
d5f7f15215
Temporal metadata fields (validFrom, validUntil, isCurrent, isRevisionOf) on UKO InformationUnit nodes enable revision chain tracking: when code changes, old nodes are marked historical and new revision nodes are created with back-links. Three storage tiers (hot/warm/cold) filter nodes by temporal scope (current/recent/all) with configurable retention (warm_retention_hours default 24h, cold_retention_days default 90d). Includes TemporalMetadata, TemporalNode, RevisionChain, TierQueryResult, TierRetentionConfig frozen domain models, TemporalBackend protocol, InMemoryTemporalBackend stub, TemporalService with structlog and DI, BackendSet.temporal typing upgrade from object|None to TemporalBackend|None. 67 Behave scenarios, 8 Robot Framework tests, ASV benchmarks, and reference documentation. ISSUES CLOSED: #577
122 lines
5.1 KiB
Markdown
122 lines
5.1 KiB
Markdown
# 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).
|