Neotoma

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)

SQL / TS
Schema or pattern reference for this primitive.
CREATE TABLE timeline_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_type TEXT NOT NULL, event_timestamp TIMESTAMP WITH TIME ZONE NOT NULL, source_id UUID REFERENCES sources(id), source_field TEXT, entity_id UUID, metadata JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), user_id UUID NOT NULL );
FieldTypePurpose
idUUIDDeterministic, SHA-256 of (source_id, entity_id, source_field, event_timestamp) projected into UUID shape
event_typeTEXTStable label like InvoiceIssued, FlightDeparture, TaskCompleted
event_timestampTIMESTAMPTZISO 8601 in UTC, drawn verbatim from a source field
source_idUUIDOwning source, used for RLS filtering
source_fieldTEXTThe exact entity/field name that produced the date (invoice_date, due_date, …)
entity_idUUIDSubject of the event (the entity whose snapshot field carried the date)
metadataJSONBOptional 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

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