The Status Value That Shouldn't Exist Yet
A weeks-old file carried a status value the old reader never wrote. The vocabulary changed without a schema version bump. A lesson in data contracts.
TL;DR
A checker warning revealed a transcript file whose status value didn't exist in the old reader's vocabulary. The fix splits failures into unknown-status, where the reader lags, and legacy-enum-status, where a writer changed vocabulary without bumping the schema version. Lesson learned: treat enum changes as schema changes and bump versions in the same commit.
A transcript file that had been sitting untouched for weeks suddenly lit up a warning. I was running the ingest checker in my my-agent repo, and the uncomfortable part was the status value itself: transcribed, a value the old reader version never wrote. Old file, new vocabulary.
My first reflex was the lazy one. Probably a typo in the data, probably the checker misread something. The old instinct said: just add that value to the reader's enum, ten minutes and done. But the more I turned it over, the stranger it got. If the writer of that file was really an old version, where did it get a value that was only registered in version two?
The answer showed up in commit 113eb57 in my-agent. The cmd_check function now separates two failure classes that used to blur into one.
if sv > 1 and st not in EVENT_STATUSES:
warn("unknown-status")
elif sv < 2 and st in NEW_STATUSES:
warn("legacy-enum-status")
The first class is obvious: a versioned file carrying a value the current reader doesn't know, flagged as unknown-status. The second class is the slippery one: a file with a schema_version below 2 that carries one of the new values, or status done while holding a media object. It means some writer changed the status vocabulary without ever bumping the version. The file looks legacy, but its content already migrated in secret.
These two classes deserve different responses. When you hit unknown-status, the reader is the outdated one and needs catching up. When you hit legacy-enum-status, the reader is innocent; the writer sneaked something past: some commit swapped the vocabulary without swapping the version number. Hunting that commit down is far more useful than quietly appending values to an enum, because the problem was never a short list. The problem is a faked history.
The rules from Kubernetes and Postgres
The habit of "just add it to the enum" died for me the day I looked at how large systems play it. The Kubernetes API deprecation policy states that an API element, once it exists at a version, "can not be removed from that version or have its behavior significantly changed". The only way to change its meaning is to increment the API group version [1]. There is also the round-trip contract: write an object at one version, read it as another, convert it back, and the result is identical with no information lost [1]. Letting meaning drift in place tears up that contract.
Postgres has a rule I keep borrowing for thinking about name collisions: within one schema, two objects of the same type cannot share a name, yet the same name in different schemas is no conflict at all [2]. Status vocabularies scoped per schema version work the same way. The value transcribed in schema v1 and in schema v2 never collide, as long as the versions are honest.
Tolerant reader, disciplined writer
Fowler framed the Tolerant Reader principle with Postel's law: be conservative in what you do, be liberal in what you accept from others [3]. Providers must be able to evolve for new demands while causing minimal breakage to existing clients [3]. In this pipeline the principle runs both ways. The reader is liberal: an unknown value gets flagged, not a crash. The writer is disciplined: changing the vocabulary means bumping the version, in the same commit.
One honest admission: the checker only warns, it rejects nothing. What keeps this rule from being a footnote is review. A pull request that slips in a new value without a version bump is now visible, and visible is already enough to make someone think twice before merging.
From now on I treat enum changes as schema changes, no exceptions. There is no more "it's just one extra value" argument. Files don't lie. What lies is the assumption that a low version number means old content. And that checker warning from yesterday turned out to be no nuisance. It was the only one telling the truth about a contract that slipped.