Append-only factual memory with dual time axes for auditable decision-making.
BiTemporalMemory tracks facts along two independent time axes:
Unlike HistoneStore (epigenetic marks) or EpisodicMemory (tiered decay), bi-temporal memory is append-only — corrections close old records and create new ones, so the full history is always reconstructible.
A single timestamp conflates two different questions:
These diverge whenever facts are ingested with delay or corrected retroactively — the norm in compliance, healthcare, and finance workflows.
from operon_ai import BiTemporalMemory
mem = BiTemporalMemory()
# Record: risk tier is "medium" (valid day 1, ingested day 3)
fact = mem.record_fact("client:42", "risk_tier", "medium",
valid_from=day1, recorded_from=day3, source="crm")
# Correct: actually "high", retroactive to day 1 (recorded day 5)
mem.correct_fact(fact.fact_id, "high",
valid_from=day1, recorded_from=day5, source="manual_review")
Three retrieval methods correspond to three questions:
# What is true at day 2? (active records only)
mem.retrieve_valid_at(at=day2, subject="client:42") # → "high"
# What had the system recorded by day 2?
mem.retrieve_known_at(at=day2, subject="client:42") # → [] (not yet ingested)
# What did the system believe on day 2, given what it knew by day 4?
mem.retrieve_belief_state(at_valid=day2, at_record=day4) # → "medium"
# Same question, given what it knows by day 6 (after correction)?
mem.retrieve_belief_state(at_valid=day2, at_record=day6) # → "high"
# Full correction history, sorted by record time
mem.history("client:42")
# What changed between two points on either axis?
mem.diff_between(day4, day6, axis="record")
# World-time timeline, sorted by valid_from
mem.timeline_for("client:42")
Corrections never mutate existing records. Instead:
recorded_to is set (closing it on the record-time axis).supersedes pointing to the old fact_id.This means any past belief state is always reconstructible — the foundation for compliance auditing.
| Type | Purpose |
|---|---|
BiTemporalFact | Immutable fact with dual time intervals (frozen dataclass) |
BiTemporalQuery | Filter spec for point-in-time queries |
BiTemporalMemory | Mutable store with append-only write semantics |
FactSnapshot | Query result container |
CorrectionResult | Result of correct_fact() |
SubstrateView | Frozen read-only envelope of facts for stage handlers (v0.20.0) |
All types are exported from the top level: from operon_ai import BiTemporalMemory, BiTemporalFact, SubstrateView, ...
| System | Time Model | Mutation | Use Case |
|---|---|---|---|
HistoneStore | Single timestamp | Mutable marks | Epigenetic metadata on retrieval |
EpisodicMemory | Single timestamp | Mutable (decay, promote) | Tiered memory with relevance scoring |
BiTemporalMemory | Dual timestamps | Append-only | Auditable fact tracking with corrections |
Since v0.20.0, BiTemporalMemory can serve as a shared substrate for SkillOrganism workflows. Pass substrate=BiTemporalMemory() to skill_organism(...), then use per-stage hooks to read and write facts:
SkillStage field | Purpose |
|---|---|
read_query | Subject string or callable → SubstrateView injected before stage runs |
fact_extractor | Callable → emits assert/correct/invalidate events after stage runs |
emit_output_fact | Convenience flag: auto-records (task, stage.name, output) |
fact_tags | Default tags applied to all facts emitted by this stage |
Handlers receive the SubstrateView as an additional argument (via arity-aware dispatch — existing handlers are unaffected). See Skill Organisms for the full three-layer context model.
69_bitemporal_memory.py — core valid-time vs record-time divergence70_bitemporal_compliance_audit.py — multi-fact compliance audit71_bitemporal_skill_organism.py — multi-stage organism with substrate