Versioning

Core. Every port reads benzene-version and selects handlers by core-concepts §2 — including not-found for a version no handler serves — so §1–§4 apply to every port. What is optional (core-concepts.md §10) is registering versioned handlers and payload casting (§5); a port that offers them offers them in exactly this shape.

A topic's request and response shapes change over a service's life. Versioning lets a producer keep sending a shape the topic still accepts, and a service keep serving it, without releasing every producer and consumer together. A message names its version; the service either has a handler for that version or can cast the payload to one it has.

1. Version is metadata

The version travels beside the payload, never inside it. The payload is exactly what the handler's type serializes to, in whatever format was negotiated — a version field embedded in the body would work for one format only. So the version is read from the message's metadata, and versioning stays independent of serialization.

2. One header

The version is the benzene-version header (wire-contracts.md §2). It is one name, configurable in one place, and the same name is read inbound and written by outbound clients.

3. Absence

A message with no version goes to the topic's default handler — the unversioned handler, or else the one the application declared as the default (core-concepts.md §2) — and its payload is taken to be in that handler's version, so no cast applies. Declaring the oldest version a topic still accepts as its default is what lets producers that predate versioning keep working once versioning is turned on.

4. Dispatch

Several handlers MAY be registered for one topic, one per version. Selection is core-concepts.md §2's: the handler for exactly the message's version; otherwise a handler the version can be cast to (§5); otherwise not-found, naming the topic and the version. There is no "nearest" or "highest available" fallback by default.

5. Payload casting

A cast turns a payload of one version into another version's type. With casts registered, one handler can serve producers on several versions:

(Informative.) Casts compose: a port that can chain registered casts (v1→v2, v2→v3) can serve v1 without a direct v1→v3 cast, and a shorter registered chain is preferred over a longer one.

6. Absorbing a version difference (informative)

There are three ways to absorb a change, and a service may combine them: