DataMind Oda keeps every chat as a session log on your computer, and the log's format carries a version. This page records the rules that keep older logs readable as the format changes: which changes start a new version, how two versions are compared, and what Oda keeps from the versions behind it. It is for people who build, ship or debug Oda itself. For where a log lives and what it holds, see How Oda stores chats.
How the format is versioned
The format has been through five versions so far, numbered v0 to v4, and Oda now writes v4.
The version sits in the log's first line, so Oda can tell how to treat a file before reading the chat itself.
Once a version has shipped, it is frozen. Its definition is kept as a compatibility record, and it is never rewritten to match later releases. A breaking change cannot be made to a released version; it starts the next one.
Oda reads a log through the definition of the version that wrote it, and it never rewrites a shipped log in place: a newer generation is saved beside the old file. That journey is on Opening sessions from other versions.
The version numbers on this page name the log format, not a release of DataMind Oda. They count one series: a lower number in an older record or release note is an earlier step of this same series, not a second count.
Changes that stay in the same version
A change can stay in a released version only in three cases:
An optional property added within an event's body. A log that predates the addition still reads as before: the property is simply absent from it.
A new event payload version, where every earlier variant is kept and the new version number is higher than all the ones before it.
A source kind added only as an attribution, recording where a message came from without changing the rest of the source policy.
Each case has exact conditions. A change that is not one of them is breaking, and it starts a new version.
Changes that need a new version
Every other recorded kind of change starts a new version, for example:
adding or removing a kind of record a log can carry;
removing a property, or changing its type;
adding a required property, or changing an existing property's optionality;
changing a union's variants;
changing an index signature;
changing a tuple's length or the number of its elements;
changing the source compatibility policy.
Two rules show what that means for the records a log carries:
Reserved slots stay empty. When a field is retired, the format can keep it as a reserved slot that must never carry data again. The slot still counts in the fingerprints, so giving it data back is a type change and needs a new version, and a record that tries is refused by the current rules when the log is read.
Tool definitions are not inlined. A log records which tools a chat used; it does not embed their full definitions. A record that carries one inline is refused by the current rules, and letting it carry one would itself be a version change.
How versions are compared
Every type the format can reach is fingerprinted: the format's machine-readable inventory carries each type and its SHA-256 digest, covering every referenced field. Reviewing a change means comparing the fingerprints of the types before and after, not reading the format text.
Not counted: comments and documentation; where a type is declared; alias names; read-only markers and brands; harmless reordering of fields, unions and intersections; and whether a repeated sub-type is shared or written out again.
Counted: property names; value types; optionality; the order of tuple elements; and the variants and values of a union or enum.
A change that looks cosmetic, such as reordering tuple elements, can still count; renaming a type alias does not, because alias names are not part of a fingerprint.
What is kept for older versions
Frozen definitions. Each released version is kept as a compatibility record: it is checked on every change, never converted to the newest form, and never rewritten.
A complete archive. Every version from the first to the current one keeps a complete reference: all of its types, a machine-readable schema, and an entry in an index of versions.
A retained corpus. A small set of older-format logs is kept as the direct input for compatibility checks: the most recent superseded baseline is kept, and the first version and every adjacent version stay directly covered. The set is bounded: at most 11 retained historical roles across 8 scenarios, and the baseline plus the current version keep the majority of it. Coverage for a version must come from that version's own logs.
How changes are recorded
Every accepted change gets its own record: an identifier made of its date and a short name, the exact types before and after, and their fingerprints.
Records are checked, not trusted: an incomplete record, or one that no longer matches the archived types, is rejected.
Once a version is finalized, its records are frozen, and a mistake is corrected by a successor record, never by editing the old one.
The newest finalized version is the floor for the format: a breaking change needs a version after it, and Oda keeps its writer at or above it.
Questions, answered
What keeps an old log readable after the format changes?
Each released version stays frozen: Oda reads a log through the definition of the version that wrote it, shipped generations are kept as compatibility records and never converted in place, and a finalized version's records cannot be edited. Nothing about a log you already have changes when a newer version ships.
Which changes need a new format version?
Every change except the three that can stay in the same version. For example: adding or removing a kind of record, removing a property or changing its type, adding a required property, changing optionality, changing a union's variants, an index signature or a tuple's shape, or changing the source policy. Giving a reserved slot data again also needs a new version.
Which changes can stay in the same version?
Three: an optional property added within an event's body; a new event payload version where every earlier variant is kept and the new number is higher than all before it; and a source kind added only as an attribution. Each case has exact conditions; anything else is breaking.
What changes a type's fingerprint, and what does not?
Counted: property names, value types, optionality, the order of tuple elements, and a union's or enum's variants and values. Not counted: comments and documentation, where a type is declared, alias names, read-only markers and brands, harmless reordering of fields, unions and intersections, and whether a repeated sub-type is shared or written out again.
Can a retired field carry data again?
No. A retired field can stay as a reserved slot that must never carry data again. The slot still counts in the fingerprints, so giving it data back is a type change and needs a new version, and a record that tries is refused when the log is read.
Why are tool definitions not stored inline?
A log records which tools a chat used, not their full definitions. A record that carries a definition inline is refused, and letting a record carry one would be a breaking change to the format.
How does Oda test that old logs still work?
Through a retained corpus of older-format logs: the most recent superseded baseline is kept, the first version and every adjacent version stay directly covered, and coverage for a version must come from that version's own logs. The set is bounded at 11 retained historical roles across 8 scenarios. Released versions are also kept as frozen compatibility records and checked on every change.
How is a change to the format recorded?
Each accepted change gets a record: an identifier of the date plus a short name, the exact types before and after, and their fingerprints. Records are checked rather than trusted, and once a version is finalized its records are frozen: a mistake is fixed by a successor record, never by an edit.
What is the accepted baseline for a breaking change?
The newest finalized version. It is a floor for the format: Oda keeps its writer at or above it, and a breaking change needs a version after it.
Why do older records name a different format number?
The numbers count one series, the log format's own versions, which have moved one step at a time from v0 to v4. Not every step was carried by a published release, so an older record can name a lower number of this same series; a number in a message about a log is always this series.