# Changing a standard type's encoding

Upgrading Jadpo never makes your stored data unreadable. A Jadpo release
changes how a standard type is sent, stored or written as text only
together with a reader for the old form and a release note. A golden file
per type, built from its examples, pins every form, so a change can't pass
unnoticed.

A value outlives the release that wrote it wherever it is kept: in a
column, in an event waiting to be delivered, in a passkey, in a signed
session cookie. Each of those records which encoding wrote it. So a
release that changes a form reads what an earlier release kept with that
release's reader, and an upgrade never strands it.

The encodings each release writes, as its release notes list them:

| Encoding | Release | What it changed |
|---|---|---|
| 1 | 0.1.0 | The first: every standard type's forms as its golden file holds them |

- **What a golden file pins.** For each valid example of the type: its
  canonical text (`none` for a type with no single text form, or for a
  secret value), the JSON the route codec writes, and each storage
  column's value as it is written into a statement, which is the value
  the runtime binds. Above them it records each column's suffix,
  PostgreSQL type and comment. A secret value's JSON is the redaction the
  codec writes, and a secret value is stored `never`. `Feature` and
  `FeatureCollection` are read and written through a program whose
  properties are `{ name }`, as their examples' are, since their second
  type argument is always your app's own object. People's formats aren't
  pinned: CLDR's wording isn't an encoding, and no stored value holds it.
- **One encoding number for a whole release,** currently 1, not one per
  type. A row or a token holds values of many types, so one number says
  which reader each needs. A golden file records the encoding that last
  changed it. A form whose type's file records an encoding at or past the
  release's can't be rewritten, so changing a form means raising the
  encoding, and the release notes' row for that encoding must name every
  type whose file it changed. A new type, or an example added or dropped,
  changes no form. A type dropped from the release keeps failing the
  test, since a release keeps reading every type it wrote.
- **Where the encoding is recorded:** in a signed part's payload (`enc`);
  in `jadpo_outbox.encoding` for an event's payload, which a delivery
  reads it by; in `jadpo_batch_runs.encoding` for a batch run's saved
  values and `key:`; and in `jadpo_passkeys.encoding` for a passkey, which
  is stored as `WebAuthnCredential`'s contract JSON
  ([Encodings in system tables](system-tables.md#encodings-in-system-tables)).
  A sealed value keeps its own version byte, which says how it is sealed,
  not how what it holds is written. An idempotency key's stored response
  is the bytes that were sent, read back as they were, and holds no
  encoding.
- **What was kept before encodings were recorded** is read as it was
  written, by the reader it was kept with: a signed part without `enc`,
  whose members are encoding 1's, and a passkey whose `encoding` is
  `NULL`, whose count is a number or its digits.
- **An encoding this release doesn't know,** a later release's, is never
  guessed at. A signed part with one opens nothing and the record is
  read, a passkey is refused, an event's delivery is parked and a batch
  run ends `failed`.
- **An entity's own columns record no encoding.** A form that changes
  their columns' types shows in the migration snapshot, so `jadpo check`
  asks for a migration. A form that changes what a column of the same
  type holds needs the release to provide the migration that rewrites
  stored values. No release has needed one yet.
