Timeline events
A timeline event is an immutable, source-anchored record that fixes one entity in time at a specific date drawn verbatim from a source field. It is a primitive record type, distinct from the application-level event entity (calendar/meeting), and distinct from system observability events.
Derived as a side-effect of writing an entity snapshot. Sources produce observations; observations produce snapshots; snapshots produce timeline events for any field that parses as a date.
Schema#
timeline_events table (Postgres / hosted)
| Field | Type | Purpose |
|---|---|---|
id | UUID | Deterministic, SHA-256 of (source_id, entity_id, source_field, event_timestamp) projected into UUID shape |
event_type | TEXT | Stable label like InvoiceIssued, FlightDeparture, TaskCompleted |
event_timestamp | TIMESTAMPTZ | ISO 8601 in UTC, drawn verbatim from a source field |
source_id | UUID | Owning source, used for RLS filtering |
source_field | TEXT | The exact entity/field name that produced the date (invoice_date, due_date, …) |
entity_id | UUID | Subject of the event (the entity whose snapshot field carried the date) |
metadata | JSONB | Optional event-level provenance (agent attribution, tier, schema version) |
Four invariants#
Source-linked: every event has source_id and source_field. Deterministic: id is SHA-256(source_id, entity_id, source_field, event_timestamp) shaped as a UUIDv4, re-derivation upserts the same row. Timestamp-normalized: only strings matching strict date shapes (or numeric epoch values in a sane range) are accepted. Immutable: events are never mutated; reinterpretation produces new rows alongside old ones.
How events are derived#
Three writers invoke timeline event derivation: structured store_structured ingestion, AI interpretation completion, and reducer snapshot recomputation. The writer selects fields via the schema's temporal_fields declaration (preferred) or a curated allow-set + strict date-shape regex (legacy fallback). System fields like created_at, updated_at, and computed_at are denylisted.
Deterministic ID, idempotent upsert#
generateTimelineEventId hashes the four-tuple and shapes the first 32 hex chars as a UUIDv4. Upserts use onConflict: 'id', re-deriving an event with identical inputs converges on the same row. A reinterpretation that emits a different timestamp for the same (source, entity, field) writes a new event row alongside the old one; nothing is retroactively rewritten.
Not the same as system observability events#
Timeline events are user-facing temporal records anchored to source data. System observability events (source.created, ingestion.failed, …) are emitted by the platform and live in a separate subsystem. The application-level event entity type (calendar/meeting) is also distinct, that is an entity, not a timeline_events row.
Read path#
GET /api/timeline returns paginated events for the authenticated user, filtered by entity_id, event_type, start_date, or end_date. The MCP layer exposes list_timeline_events with the same filters. Both paths defensively load the user's source set first and only return events whose source_id is in that set.
Invariants#
Every timeline event satisfies the following constraints:
MUST
- Derive only from extracted source date fields, never inferred, predicted, or computed
- Carry a non-null source_id, source_field, and event_timestamp
- Use the deterministic generateTimelineEventId hash so re-derivation is idempotent
- Pass attribution policy enforcement before write
- Be filtered by source ownership on every read path
MUST NOT
- Be created by agents through a direct write surface
- Mutate after creation
- Inherit dates from created_at, updated_at, or other system fields
- Be derived from string values that fail strict date-shape validation
- Be returned across user boundaries, source-scoped filtering is required even where user_id matches
Related#
- Timeline events subsystem doc , Derivation rules, deterministic ID, event type mapping, read paths
- Timeline events doctrine , Foundational invariants and generation rules
- Sources , Every event traces back to a source
- Observations , Date fields on observations are what feed timeline derivation
- Replayable timeline , How deterministic timeline reconstruction works end-to-end
- Implementation , src/services/timeline_events.ts, derivation and upsert
Where to go next#
- All primitive record types , index of sources, interpretations, observations, relationships, and timeline events
- Architecture , how the primitives compose into Neotoma's deterministic state
- Terminology , canonical glossary of terms used across Neotoma docs