Skip to content

Point-in-Time Reasoning and change over time

This page explains how Alvia handles information that changes: how it relates a new fact to what it already holds, how it records when each fact was stated, how age affects ranking and how date bounds filter reads. Together, these are what Alvia means by Point-in-Time Reasoning: at write time it resolves how each new fact relates to the others, it keeps the lineage and it answers from what applies now, using when each fact was stated.

Facts change. A team moves to a new database, a customer changes plan, a user stops wanting short hints. A memory that stores every message and returns the most similar ones returns the old fact next to the new one, and leaves your application to work out which is true. See Why memory for AI is hard.

When a write forms a fact, Alvia compares it with the memories in the same project and scope and classifies how it relates to them:

  • It replaces a fact (supersession). The new fact becomes the memory’s next version.
  • It adds detail (refinement). Alvia merges it into the memory’s next version.
  • It repeats a fact (duplicate). Alvia stores nothing new.
  • It contradicts a fact (contradiction). Alvia stores it as a new memory and links the two, so context can report both.
  • It relates to a fact, or to nothing. Alvia stores it as a new memory.

This happens once, when the fact forms, not on every read. Wording that says what changed gives the clearest result; see Memory formation.

In March, the Atlas team chooses PostgreSQL 16:

POST /v1/memories
{
"content": "The Atlas team chose PostgreSQL 16 as its primary database.",
"occurred_at": "2026-03-10T10:00:00-05:00",
"scope": { "organization_id": "acme", "application_id": "eng-assistant" }
}

Once it forms, a recall for “Which PostgreSQL version does Atlas use?” returns that memory:

Response
{
"status": "active",
"memory_id": "mem_01K6R8QJ2N4P6S8T0V2X4Z6B8D",
"content": "The Atlas team chose PostgreSQL 16 as its primary database (stated March 10, 2026).",
"category": "decision",
"is_favorited": false,
"sentiment": "neutral",
"occurred_at": "2026-03-10T10:00:00-05:00",
"ranked_score": 0.8914,
"scope": {
"organization_id": "acme",
"user_id": null,
"application_id": "eng-assistant",
"agent_id": null,
"team_id": null
},
"entities": { "project/Atlas": 0.52, "tool/PostgreSQL 16": 0.48 }
}

In September, the team upgrades:

POST /v1/memories
{
"content": "Atlas moved its primary database from PostgreSQL 16 to PostgreSQL 17.",
"occurred_at": "2026-09-29T09:00:00-04:00",
"scope": { "organization_id": "acme", "application_id": "eng-assistant" }
}

In this example, Alvia classifies the new fact as a supersession of the March memory, and the same recall returns the same memory, at its next version:

Response
{
"status": "active",
"memory_id": "mem_01K6R8QJ2N4P6S8T0V2X4Z6B8D",
"content": "Atlas uses PostgreSQL 17 as its primary database (stated September 29, 2026).",
"category": "decision",
"is_favorited": false,
"sentiment": "neutral",
"occurred_at": "2026-09-29T09:00:00-04:00",
"ranked_score": 0.9027,
"scope": {
"organization_id": "acme",
"user_id": null,
"application_id": "eng-assistant",
"agent_id": null,
"team_id": null
},
"entities": { "project/Atlas": 0.51, "tool/PostgreSQL 17": 0.49 }
}
  • The memory_id is unchanged. The memory has a new version, not a twin.
  • Search returns this memory once, with the PostgreSQL 17 text. There is no PostgreSQL 16 result for your application to rank against it.
  • occurred_at moved to the September statement. See When a memory changes.

Alvia normalizes the wording of what it stores, so your text can read a little differently from these examples. If the September text had contradicted the March one instead, without replacing it, both memories would stay and context would report both.

The lineage is kept. Inspect returns a memory’s versions, newest first, each with how Alvia classified it and why, and the memory’s relationships:

GET /v1/memories/{memory_id}/inspect
curl \
https://api.alvia.ai/v1/memories/mem_01K6R8QJ2N4P6S8T0V2X4Z6B8D/inspect \
-H "Authorization: Bearer $ALVIA_API_KEY"
Response
{
"memory_id": "mem_01K6R8QJ2N4P6S8T0V2X4Z6B8D",
"head_version": 2,
"versions": [
{
"version": 2,
"content": "Atlas uses PostgreSQL 17 as its primary database (stated September 29, 2026).",
"occurred_at": "2026-09-29T09:00:00-04:00",
"outcome": "supersession",
"explanation": "The new statement replaces PostgreSQL 16 with PostgreSQL 17 as the Atlas primary database."
},
{
"version": 1,
"content": "The Atlas team chose PostgreSQL 16 as its primary database (stated March 10, 2026).",
"occurred_at": "2026-03-10T10:00:00-05:00",
"outcome": "novel"
}
],
"relationships": []
}

The response is shortened here. See Inspect a memory in Read memories for every field.

The dashboard shows the same history. Open the project, then Memories, and open a memory:

  • History lists every version, newest first, with the outcome that produced it (such as Supersession or Refinement), the classifier’s explanation and when the version was stated and recorded.
  • Relationships lists the memories it is related to or contradicts.

Graph, in the project’s menu, shows the project’s memories and the relationships between them.

Every memory has an occurred_at time: when its fact was stated or observed.

  • On POST /v1/memories, occurred_at is optional. Without it, Alvia uses the time it received the request.
  • On POST /v1/memories/import, occurred_at is required. It is the date of the conversation.

Send occurred_at whenever the text is not from now: a message from last week, a record from an older system or a conversation you import later. With it, old information keeps its real age.

The value is an ISO 8601 date-time, such as 2026-09-23T14:00:00-04:00. Alvia keeps the offset you send and returns the same offset on reads. A value without an offset is read as UTC.

  • It must not be in the future. Alvia accepts a time up to five minutes ahead, for a client clock that runs fast.
  • A date with no time, such as 2026-09-23, is accepted once that day has started anywhere in the world.
  • It must not be earlier than 1970-01-01T00:00:00Z.

A value that breaks a rule gets a 400 response. See Errors.

Text often says “yesterday”, “last Friday” or “next quarter”. Alvia resolves these phrases with fixed calendar rules on the calendar day of occurred_at, in the offset you send, so the same phrase stated on the same day always names the same dates. Send the local time with its offset, not a UTC time that falls on another date.

Alvia writes the dates it resolved into the stored text:

  • A relative day gets its date, as in “yesterday (September 22, 2026)”.
  • A named period gets its range, as in “last summer (June through August 2025)”.
  • A memory of an event, plan, decision, task, issue or measurement whose text names no date ends with the day it was stated, as in “(stated September 23, 2026)”.

The date stays with the fact, so later reads do not depend on when the text was sent. Alvia dates each phrase that names a definite day or period, so for a date to be recorded, name one: “on September 18, 2026” rather than “a few days ago”, “in spring 2026” rather than “in the spring”.

When a fact revises a memory, the memory’s occurred_at becomes the later of two times: the memory’s own time and the revising fact’s time. It only moves forward: when an older fact sent after a newer one revises the memory, the memory keeps the newer time.

Where you can, send facts in the order they happened, each with the time it was stated.

occurred_at establishes the age of the information, not the date you uploaded it. This matters when you send old notes through a normal write or import a historical conversation. Importing a three-year-old conversation today does not make its facts new.

Ranking changes with age and the kind of memory. Each category carries a small ranking boost that fades with age: quickly for events, issues and metrics, slowly for preferences and identity. An old memory that matches the query well still ranks: decay only adjusts the order.

Preserve the original date on older information. For a current task, keep completion and scheduling state in your application, and ask Alvia for relevant context rather than treating a recalled task as proof that it is still open.

Search, recall and context accept two optional bounds:

  • occurred_after: only memories whose occurred_at is at or after this time
  • occurred_before: only memories whose occurred_at is before this time

Date bounds filter memories by when they were stated or occurred: each bound compares a memory’s current occurred_at (see When a memory changes).

On context, the bounds choose the memory that best matches, and context resolves it with all its related and contradicting memories. To get only memories inside the range, use search.

The bounds filter strictly: if no memory falls inside them, the read returns nothing. If you send both, occurred_before must be later than occurred_after.

POST /v1/memories/search
{
"query": "Which Atlas migration decisions are relevant?",
"scope": { "organization_id": "acme", "application_id": "eng-assistant" },
"occurred_after": "2026-04-01T00:00:00Z",
"occurred_before": "2026-07-01T00:00:00Z"
}

Use the bounds to limit a read to a period, when the date range is part of the question. Omit them when an older, still relevant preference should stay eligible. Time words inside the query, such as “last month”, guide which memories rank as relevant.