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.
Why change needs handling
Section titled “Why change needs handling”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.
How a new fact joins what Alvia holds
Section titled “How a new fact joins what Alvia holds”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.
Example: a database upgrade
Section titled “Example: a database upgrade”In March, the Atlas team chooses PostgreSQL 16:
{ "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:
{ "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:
{ "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:
{ "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_idis 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_atmoved 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.
See the history
Section titled “See the history”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:
curl \ https://api.alvia.ai/v1/memories/mem_01K6R8QJ2N4P6S8T0V2X4Z6B8D/inspect \ -H "Authorization: Bearer $ALVIA_API_KEY"{ "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.
When a fact was stated
Section titled “When a fact was stated”Every memory has an occurred_at time: when its fact was stated or observed.
- On
POST /v1/memories,occurred_atis optional. Without it, Alvia uses the time it received the request. - On
POST /v1/memories/import,occurred_atis 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.
Rules for the time
Section titled “Rules for the time”- 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.
Dates in the stored text
Section titled “Dates in the stored text”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 memory changes
Section titled “When a memory changes”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.
Age and temporal decay
Section titled “Age and temporal decay”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.
Filter by date
Section titled “Filter by date”Search, recall and context accept two optional bounds:
occurred_after: only memories whoseoccurred_atis at or after this timeoccurred_before: only memories whoseoccurred_atis 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.
{ "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.