# Changelog 0.2

## v0.2.1 — 2026-10-05

0.2.0 was tagged but never published to npm, so 0.2.1 is the first public release and includes everything listed under 0.2.0.

### ♻️ Changed

- `bun run type-cost` prints a `query-200+batch` row (a `batch` of an insert, an update and a delete). It has no ceiling. The types of `batch` are unchanged: refusing `restore` and `expect` at compile time was measured and not built, and the known limits say why (D184).

### 🐛 Fixed

- The release workflow now publishes the tarball by explicit path (`npm publish ./packed/okmodel.tgz`). A bare `packed/okmodel.tgz` was read by npm as a `user/repo` git shorthand, and the publish step tried to clone `github.com/packed/okmodel.tgz`. A test now fails when any `npm publish` argument in a workflow does not start with `./` or `/`.

- `t.id()` on Postgres 15 to 17 with `schema({ requires })` not declared no longer fails with the raw `function uuidv7() does not exist`. `okm migrate apply` and `okm push` raise OKM1812 before they send any statement, so nothing is created, not even `okm_meta`. The message names the column, the migration and the server version, and the fix names both options: Postgres 18 with `schema({ requires })`, or `t.id({ default: "uuidv4" })`. A server that already has a `uuidv7()` function is not refused. Nothing changed in `connect()` or the runtime entry.

## v0.2.0 — 2026-10-05

### ✨ Added

#### contracts

- `Input` is the row a validated insert accepts. `OkmError` carries `issues` (`path` and a message key) when validation fails.

#### dialects

- `schema({ tenancy: columnTenancy({ key: "tenantId", type: "uuid" }) })` rewrites tenant tables once before compile. Each gains a guarded uuid key, a primary key of `(id, tenant key)`, and `UNIQUE` on those columns. `.unique()` becomes `UNIQUE (tenant key, column)` unless `.unique({ global: "reason" })`. Foreign keys between tenant tables include the tenant key. `global("reason")` opts a table out.

- A reference can list several target columns. `along` names the other local columns, so a composite foreign key is an ordinary constraint. Relations join on every column of the key.

- `t.id()` still defaults to database `uuidv7()`. `t.id({ default: "uuidv4" })` uses `gen_random_uuid()`. `t.id({ default: "none" })` has no default and is required on insert. A column choice wins over `schema({ defaults: { id } })`.

- `schema({ defaults: { id } })` sets the generator for every bare `t.id()`. `"uuidv4"` and `"uuidv7"` stay database defaults. A function is filled in the application.

- An OKID id is `text` with collation `C`. Collation is part of the catalog: create, diff, introspect, and plan.

- `.hidden()` stays out of default selects and includes, and is returned only when a root `select` names it. `.sensitive()` redacts the value in logs, errors, and `inspect()`.

- `filters()` is synchronous and throws OKM1123 when `allow`, `sort`, or `relations` names a hidden field. `parse()` loads the parser on first use.

- `schema({ traits })` applies those traits to every table. A table opts out with `omitDefaults` and a reason. A field the table already declares is OKM1012.

- `archivable()` turns a table's unique constraints into partial unique indexes (`WHERE archived_at IS NULL`). The primary key stays a full constraint. A tenant unique keeps the tenant key and the same predicate.

- `contains`, `containedBy`, `overlaps`, `hasKey`, `hasAnyKey`, `path`, and `matches` filter json, array, range, and tsvector columns. Text `contains` stays a substring. The value, key, or path is a parameter, and a regconfig name is checked before it is bound.

- `json.set`, `arr.append`, and `arr.remove` are namespace exports used inside `update`. Importing one does not pull the others.

- `.validate()` stores rules on a column. A table `validate` section stores rules by field, and `$row` stores cross-field rules. `schema({ validation })` and `table({ validation })` store the switch. `schema()` does not pack or run them.

- `schema({ validation: true })` marks that schema's insert body as `Input`. A boolean that is not the literal `true`, an object form, and a table-level `validation` switch do not change the type. The validate methods are added by importing `okmodel/validate` and are not part of the table type.

- Importing `okmodel/validate` types `insert.validate`, `insert.check`, `pick`, `omit`, `update.validate`, and the Standard Schema members on a table that validates, by module augmentation. A program that does not import it does not see those types and pays nothing for them. The `Input` mark follows the schema's `validation` option or the table's own, in the boolean and object forms.

- `manyThrough("labels", { through: "taskLabels" })` declares a to-many relation through a join table. Name `from` and `to` when the join table has more than one foreign key to a side. It works in `include`, in `has` / `none` / `every`, and in filters. The join rows and the targets carry the tenant and active-set predicates, so a row in another tenant or archived never shows. The relation carries its own resolver and emitter, so a schema without one does not ship them.

- `t.custom({ ..., accepts: ["Object"] })` names the object kinds a custom codec takes. Without it every object is refused for that column (OKM1121).

- `table({ presets })` and `trait(name, { fields, presets })` declare named, typed query refinements. A preset is `(q, ...args) => q`, its arguments are typed, and the builder has one method, `where`, which adds a predicate. No method removes or replaces one. A reserved or client-method name is a type error. `PresetQuery` is exported for presets written apart from the table.

- A trait's presets are merged into each table's set through the trait object. A schema-level trait's presets reach every table that keeps the defaults. A name defined by a table and a trait, or by two traits, is OKM1040, and its fix names both sources.

#### adapters

- `okmodel/pg/pg` connects with node-postgres, and `okmodel/pg/bun` connects with Bun.sql. Both are optional peers. The default stays postgres.js. Bun.sql loads only on Bun.

- node-postgres cancels an in-flight statement and returns notices. Bun.sql does not abort an in-flight statement and does not surface RAISE NOTICE. Neither describes a statement without running it. A plain pool of either exits after the last query.

- A postgres.js, node-postgres, or Bun.sql connection reserved for `tx()` that is lost is dropped and never written to again. A COMMIT cut by a lost connection is `outcome_unknown` (OKM1401), and no ROLLBACK follows it.

#### runtime

- `okmodel/tenancy` exports `columnTenancy` and `global`. `for({ tenantId })` puts the tenant predicate on every read, write, include, and `has` / `none` / `every` filter. `unscoped("reason")` omits the predicate and `inspect()` shows the reason. Insert still needs `for()`. The root client omits tenant tables. Calling one is OKM1701. Input cannot set the key, including `{ allow }` (OKM1190). Naming it in a filter or an update is OKM1704.

- An index on a tenant table that does not lead with the tenant key is OKM1706. A global table that references a tenant table is OKM1705. `set null` and `set default` are refused when the foreign key includes the tenant key.

- `registerTenancy()` records the tenancy check. A tenant key set by input is OKM1190. The package does not register the rule on import. An application that does not import `okmodel/tenancy` does not load that code.

- `connect()` refuses a Postgres server below 15 with OKM1803. `schema({ requires })` that names an older major keeps the OKM1802 check instead.

- `okmodel/ids` exports `uuidv4`, `uuidv7`, and `okid`. Pass one to `.default()` or `defaults.id`. A literal passed to `.default()` stays a database default. `.defaultSql()` stays a database expression. The generated value is not in the catalog, so changing a generator does not produce a migration. Insert fills each omitted value once, including each row of a batch, and the returned row carries it.

- `connect({ generators })` replaces a built-in generator by name.

- An operator that does not fit its column fails with OKM1124. The message names the column type and the operators that column accepts. Text search on a text column says it needs a tsvector column.

- A table records the file and line where it was defined. `inspect()` shows that location on the catalog rules, a schema error includes it, and `okm` prints it. The catalog hash does not.

- `okmodel/safety` is the registry later checks plug into. `registerRule` adds a rule, `verify` throws OKM1190 with every violation in stable order, and `safety.property` fails when rule order changes the verdict.

- `okmodel/traits` exports `timestamps()` and `trait()`. `timestamps()` adds `createdAt` and `updatedAt`. Insert leaves both to `now()`. Update sets `updatedAt` to `now()` and leaves `createdAt`. Input cannot set either, including `{ allow }`. An application that imports no trait does not load that code.

- `registerTimestamps()` records the timestamps check. A timestamp set by input is OKM1190. `{ allow }` does not clear it.

- `registerFieldExposure()` records the hidden, sensitive, and guarded checks. A hidden field shown, a sensitive value revealed, or a guarded field set without `{ allow }` is OKM1190.

- Unknown keys are dropped on insert and update. A guarded field is OKM1190 unless `{ allow }` names it. `allow` does not unlock a primary key on update.

- `okmodel/traits` exports `archivable()`. It adds `archivedAt` and `archiveId`. `archive()` returns `{ count, archiveId }` and hides the matching rows, including named children, in one statement. `restore()` returns `{ count }`, clears both columns, and brings back only rows that share that `archiveId`.

- Reads, updates, and deletes see the active set. `withArchived()` and `onlyArchived()` change that view. `delete()` stays permanent. Purge is `onlyArchived().delete(...)`. A dynamic call on a table that is not archivable throws OKM1052. `strategy: "table"` throws OKM1061.

- `registerArchive()` records the archive rules. An application that does not call `archivable()` does not load the archive statement.

- `okmodel/validate` exports `v`. Importing it adds `insert.validate`, `insert.check`, `pick`, `omit`, `update.validate`, and the Standard Schema surface. The engine loads on the first validated call. An application that does not import it does not load that code.

- A validated write runs transforms, then derived checks (length, required, integer range, precision, picklist, uuid, json), then the caller's rules. A failure is OKM1200. A value returned by `validate()` is frozen and is not checked again. `{ validate: false }` skips that call.

- A write that would validate throws OKM1201 before any statement, and before a transaction is opened, when `okmodel/validate` was not imported. The category is `input`. A schema that does not enable validation still writes. A failed engine import still rejects the call.

- `page({ orderBy, limit, after })` returns `{ items, next }`. It is a keyset on `orderBy` with the primary key appended, and it is stable while rows are inserted. The cursor holds the order and the values as the database wrote them, so a timestamp keeps its microseconds. A cursor used with another `orderBy`, or one `page()` did not return, is OKM1130. The tenant and active-set predicates apply to every page. The planner loads on the first `page()`.

- `aggregate({ where, groupBy, count, sum, avg, min, max, orderBy, limit })` returns grouped rows with the tenant and active-set predicates of any read. `groupBy` needs `limit` or `.all(reason)`. `orderBy` takes `groupBy` fields. There is no `having` and no `bucket`. A hidden field is refused. The planner loads on the first `aggregate()`.

- Aggregates decode with the source column's codec. `count` is a number, `min` and `max` follow the column, and `sum` and `avg` keep the value type of the column: an exact decimal string for `numeric` by default, a number for `numeric` with `as: "number"` and for integer and float columns. A column whose value is neither a string nor a number is OKM1124.

- `.required()` on `one()` and a `one()` without `orderBy` that matches more than one row (`not_unique`) have real-Postgres tests, with `inList([])`, `notIn([])`, `has` / `none` / `every`, null ordering, and literal escaping.

- `tasks.pending().ownedBy(userId).find({ limit: 20 })`: a preset is a method on the table handle that returns the handle, so calls chain. They apply to `find`, `one`, `count`, `exists`, `aggregate` and `page`, and narrow the rows `update`, `delete`, `archive` and `restore` act on, including each item of a list `update`. `insert` ignores them. The planner writes the tenant predicate, the active set, the caller's `where`, then each preset's predicates, joined with AND, so a preset cannot remove the tenant predicate or reach an archived row.

- A write through a preset still needs a `where` or `.all(reason)`. A preset does not stand in for one (OKM1102).

- `inspect()` lists one `preset` line per call with the preset name, the fields it filters, who defined it (`table tasks` or `trait flagged`) and the source location. Values stay parameters in `sql()`.

- A preset name that is a client method or reserved fails with OKM1040 in `trait()` when the trait is built and in `connect()` before the first query. A preset that does not return its builder is OKM1121 when the call is planned.

- `registerPresets()` in `okmodel/safety` registers the rule that a preset adds predicates and never removes or replaces one. A property test composes random chains over a tenant table and checks the statement text and the rows on every read and write.

- `db.tx(fn)` and `db.tx({ isolation, retry, timeout, signal }, fn)` run `fn` in a transaction on one reserved connection. The callback gets a client `t` with the same tables, presets, tenancy, and archive handling. The call commits when `fn` resolves and rolls back when it throws. It exists on drivers with `transactions: "interactive"`; on any other it is OKM1111. `timeout` is milliseconds and covers the whole transaction, retries included.

- A nested `tx()` is a savepoint on the same connection. It refuses options (OKM1121). If the callback swallows a statement error, the transaction fails with that error instead of committing: Postgres would turn the COMMIT into a silent ROLLBACK.

- `retry` runs the callback again, in a fresh transaction, after a serialization failure or a deadlock. `outcome_unknown` and `cancelled` are never retried. The `timeout` is one deadline for all attempts.

- `db.batch([...])` runs writes as one atomic unit on every driver, with results in order. A failure carries `batchIndex`, or `null` at commit. Inside `tx()` it is a savepoint, so the transaction survives a failure. `.replica()` is OKM1840 and a read in the list is OKM1121.

- `t.afterCommit(fn)` runs after the outermost commit, in order, and never on rollback. An error in one goes to `hookm.onError` and never changes the result.

- `find({ lock: "update" | "share", wait: "nowait" | "skip" })` inside `tx()` appends `FOR UPDATE` or `FOR SHARE`, with `NOWAIT` or `SKIP LOCKED`. Outside `tx()` it is OKM1830. `t.advisoryLock(key)` takes a transaction-level advisory lock; a string is hashed.

- Every read and write takes `{ signal, timeout }`. An abort cancels the statement where the driver can and fails as `cancelled`; a timeout fails as `timeout`. Cancelling inside `tx()` rolls it back. `connect({ timeouts })` sets `acquire`, `statement`, `transaction`, and `idleInTransaction` in milliseconds.

- `connect({ hookm })` takes observers. `onNotice` hears server notices, `onTransaction` hears `start`, `commit`, and `rollback` (savepoints at depth 1 and up), and `onError` hears errors with no caller. An error a hook throws is dropped.

- `tx`, `batch`, and locks load on first use. A program that calls none of them pays 215 bytes minified and 225 gzip at startup.

#### tooling

- `engines.node` is `>=22`. The checks that can run do so on Node, Bun, and Deno, and the runtime entry is imported as an edge bundle. A runtime that cannot run a check prints the reason.

- `okm check` reports OKM1030 when a field has rules on the column and in the table `validate` section, or when `validation.style` disagrees with where the rules sit.

- `okm check` reports OKM1201 when the schema would validate and the project never imports `okmodel/validate`.

- The 200-table query probe has gated rows for a project with `okmodel/validate` imported and for one that uses `page`, `aggregate`, and `manyThrough`, each at its measured value plus 3 percent (D176). `scripts/app-relations.ts` is a reported app that uses those features.

- `startCutProxy` in `@okmodel/harness` cuts the next COMMIT on a TCP connection, and `registerTxSuite` runs one conformance suite of 38 cases on postgres.js, PGlite, node-postgres, and Bun.sql: serializable conflict and retry, a real deadlock, `skip` and `nowait`, timeout and abort kills, a cut commit and a cut batch, pool reuse with no leaked state, and savepoints. A driver that cannot do a case shows it as a skip with the reason.

- The 200-table query probe has two more gated rows, `query-200+presets` (19,180 / 7,128, ceilings 19,755 / 7,341) and `query-200+tx` (24,283 / 6,948, ceilings 25,011 / 7,156), each at the measured size plus 3 percent (D176).

- The 0.2 gate runs four property tests on real Postgres with a fixed seed (`OKM_PROPERTY_SEED`, default 20261005) that is printed on failure: `isolation.property` (random compositions of tenancy, traits, presets, relations, `include`, `aggregate`, `page`, writes, `archive`, `restore`, `batch` and `tx` with savepoints and retry, over two tenants with colliding keys, a one-connection pool and a concurrent run), `safety.property` (every safety rule registered together), `statements.shape` (the statement count depends on the shape of the call and never on 0, 1, 10 or 1,000 rows), and `archive.correctness` (archive, restore, cascade, partial uniques and `archiveId` against a model, inside `tx`, savepoints and `batch`). Each is shown to fail when a predicate is dropped on the wire.

- `scripts/app-full.ts` is a reported app with tenancy, `archivable()`, `timestamps()`, validation, relations, presets, `tx` and `batch` together. The size script prints it beside the plain app. It is not a gate.

- `batch-refusals.test.ts` runs every write kind in a batch, shows a blocked restore and a wrong `expect` refused with nothing written, and shows both still work in `tx()`. `snapshot-reads.test.ts` shows every read shape is one statement inside `tx({ isolation: "read committed" })` and that a reader never sees half of a committed write. `raw-sql-paths.test.ts` fails when the client or a table gains a method that could take SQL text, or when the runtime layer starts to read the `sql` template. `codes-not-reached.test.ts` records that OKM1110, OKM1191 and OKM1702 are registered and not thrown in 0.2.

#### docs

- The README lists the key options. The quickstart stays on `t.identity()`. Schema says how ids are generated and how to change the default.

- The roadmap is a public board. Each release is a milestone, and a pull request ends with `Closes #N`.

- The README shows a jsonb `contains` filter and an array update with `arr.append`.

- The size page records the no-trait startup graph at 83,043 minified bytes and 27,666 gzip. The gates stay. An application that uses `timestamps()` measures 85,744 / 28,503, and that figure is not a gate.

- Spec section 9 uses `columnTenancy` and `global("reason")`. The size page records the no-tenancy app at 85,356 / 28,361. The gates are that measurement plus 3 percent (D160). Column tenancy measures 91,802 / 30,411 and is not a gate. The 91,000 / 30,000 cap stays.

- D170 accepts the featureless app at 87,148 / 28,938. The gates, the cap, and the 7,100 type probe stay. An application that uses `archivable()` measures 95,725 / 31,604 at startup and is not a gate. A unique a trait adds after the tenancy rewrite is still not widened.

- Validation is opt-in. OKM1030 is reported by `okm check`, not while `schema()` compiles. D172 records why the first build was rejected and where the engine lives.

- D174 moves the missing-import check to the first write. The typed surface arrives in P27 as a module augmentation in `okmodel/validate`.

- Spec sections 6.2, 10 and 12 describe `manyThrough`, `page`, `aggregate`, and the decode rule. `iStartsWith`, `iContains`, and `iEndsWith` are not in 0.2 (D176); use `ilike()` with an escaped pattern.

- Spec section 6.2.1 describes presets: order of predicates, which calls they reach, names, inspection, and the OKM1040 and OKM1121 cases. D180 records the P28 size and the choices made. Presets are out of the known limits.

- D176 records the first P27 build (+1,425 / +431 over the stop line), the one redesign in which the relation carries its own emitter, and the probe policy.

- Spec section 15 gives `timeout` in milliseconds and says a nested `tx()` takes no options. D181 records the P29 numbers and the choices.

- 0.2.0 "Safety" adds tagged operators, final safety verification, field exposure (`.hidden()`, `.guarded()`, `.sensitive()`), column tenancy, the `timestamps()` and `archivable()` traits with cascade to direct children, validation, `one`, `many` and `manyThrough` relations with `include`, `page` and `aggregate`, presets, `tx` with savepoints and retry, `batch`, row locks, advisory locks, and per-call `{ signal, timeout }`. Its limits are in `docs/known-limits.md`: a unique added by a trait after the tenancy rewrite is not widened; archive cascades to direct children only; `sum` and `avg` over a `text` column, and over a `bigint` read as `bigint`, are OKM1124; `iStartsWith`, `iContains` and `iEndsWith` are not in 0.2; a mixed-sign `interval`, a non-default `IntervalStyle` and a seconds offset on `timetz` are OKM1210 on read; `onRead` is stored and not applied; there is no JSON Schema export; `rls` tenancy is not built; on PGlite and Bun.sql a `tx` timeout or signal cannot kill a statement waiting on a lock; Bun.sql has no notices; batch-mode drivers (Neon, D1) have no adapter; `idleInTransaction` is a client-side timer; a table named `tx` or `batch` is shadowed; and an option or builder that is not in 0.2 throws OKM1061 and names its version. `restore` and any write with `expect` are not allowed in `batch` (OKM1121); use `tx()`.

- The known-limits page is rewritten in groups and says what happens for each limit. The size page and the README carry the feature-full app (113,705 / 37,506 at startup) and what the entry-chunk size pass could save (at most 31,173 / 11,081 on the plain app, not built).

### 💥 Breaking Changes

- `import { json }` is the write namespace (`json.set`). A json column is `t.json()`.

### ♻️ Changed

- A push to main runs check and attest. The Postgres suite and the tarball job stay on pull requests, the release workflow, and the weekly run.

- A declared server below 18 that uses `uuidv7()` fails with OKM1812, and the message names `defaults.id`.

- `okmodel/pg/pg` and `okmodel/pg/bun` connect entries are gated at the measured size plus 3 percent (D156).

- `t.id()` and `t.identity()` stay omitted from insert and update. A plain primary key is writable on insert and omitted from update. Changing it is OKM1190.

- A postgres.js pool opened with TLS still lingers about 30 s after the last query on Node and Bun. A plain pool exits on its own. `close()` or `await using` releases it.

- A release checks the tag, packs one tarball, tests that file on Postgres, and publishes that same file. The GitHub Release closes the milestone.

- The npm description is one sentence.

- App startup is gated at 85,100 minified and 28,000 gzip (D158). postgres.js connect is 40,800 / 14,100, `pg` is 41,000 / 14,250, and Bun.sql is 39,800 / 13,800. PGlite stays 38,146 / 13,505.

- Synchronous `filters()` takes the app startup graph to 84,907 / 28,017, past the gzip gate. The gates move to measured plus 3 percent (D160): app startup 87,454 / 28,857, postgres.js 41,519 / 14,352, PGlite 38,620 / 13,613, `pg` 41,677 / 14,543, and Bun.sql 40,545 / 14,096. The 91,000 / 30,000 cap stays.

- `filters()` returns the parser directly. The promise is on `parse()`, which is what a request handler awaits.

- A signal or timeout, a watched query, checkout, listen, stream, the server-version query, and the fix text for write and include errors load on first use. Error codes, messages, SQL, and catalog output stay the same. The no-trait startup graph goes from 85,568 / 28,329 to 83,043 / 27,666. The gates stay.

- P24 takes the no-tenancy app from 83,043 / 27,666 to 85,356 / 28,361. The gates move to measured plus 3 percent (D160): app startup 87,900 / 29,210, postgres.js 40,200 / 14,090, PGlite 37,800 / 13,450, `pg` 40,900 / 14,410, and Bun.sql 39,700 / 13,940. The 91,000 / 30,000 cap stays.

- P27 takes the no-tenancy app from 87,408 / 29,001 to 88,278 / 29,326. The gates move to measured plus 3 percent (D160), capped at 91,000 / 30,000: app startup 90,900 / 30,000, postgres.js 41,600 / 14,600, PGlite 39,300 / 13,980, `pg` 42,400 / 14,920, Bun.sql 41,200 / 14,470.

- P28 takes the no-tenancy app from 88,393 / 29,385 to 88,723 / 29,553 (+330 / +168), inside the stop line of +600 / +180. No gate or ceiling moves. `query-200` measures 17,062 / 6,259 (+143 / +40 over P27b) under 17,300 / 7,100.

- A `ReadBuild` receives the call it builds for, so `aggregate` writes the `where` of the call after presets were applied and not the one it was created with. The planner reads `ruleLines` for the archive set and for presets.

- `sum` and `avg` over a field typed `number` or `string` pass the types, and the result has the type of the field. A `bigint` field typed `bigint` is OKM1124.

- OKM1130 also covers a cursor that `page()` did not return.

- P27b takes the no-tenancy app from 88,278 / 29,325 to 88,393 / 29,385 (+115 / +60). No gate or ceiling moves. The gated 200-table probes do not move; `query-200-validate` is 24,888 (+3).

- In a `where`, a bare object is OKM1121 for every column and `eq(value)` is the equality form for any object value (Temporal, arrays, ranges, json). The comparison operators and `not` take the codec's objects as operands.

- P29 takes the no-tenancy app from 88,723 / 29,553 to 88,938 / 29,778 (+215 / +225), inside the stop line of +1,400 / +400. No gate or existing ceiling moves. `query-200` is 17,078 / 6,271 (was 17,062 / 6,259).

- A table named `tx` or `batch` is shadowed by the client method.

- `withTenantScope` is removed from `src/runtime/plan.ts`. Nothing called it since `withRowFilters` took its place. No behaviour changes.

### 🐛 Fixed

- `batch` refuses a `restore` and any write that carries `expect` with OKM1121, before any statement is sent, on every driver. Both are checked against the result of their statement, and a batch used to report that after it had committed, leaving its other writes in place (D183). The error names `tx()`.

- A read of an `interval` or `timetz` column no longer fails with OKM1210. `interval` decodes what Postgres sends (`01:30:00`, `1 year 2 mons 3 days 04:05:06.5`, `-2 days`) and an ISO-8601 duration. `timetz` decodes `01:02:03+03` and `±HH:MM`, and the offset is `±HH:MM`. A negative `Temporal.Duration` is written with a sign on each field because Postgres refuses a leading minus (D179). An interval that mixes signs and a `sql_standard` or `postgres_verbose` `IntervalStyle` stay in the known limits.

- `insert` and `update` `set` refused every object value with OKM1121, including the Temporal values of the default `timestamptz`, `timestamp`, `date`, `time` and `interval` codecs, json and jsonb values, arrays, bytes, ranges and points. A column now takes the objects its codec declares and OKM1121 stays for any other object, so an operator-looking object never reaches a scalar column. A `Date` is not an input of any default codec and is still refused.

- A range object without `empty` is OKM1210 before any statement. It was sent to the database as a malformed literal.

- `eq()`, `lt`, `gt`, `between`, `inList` and `not` on a Temporal, array, range or json column failed with OKM1121 for the same reason.

- `insert([...])` typed every key as required on each row, though the runtime already filled an omitted key with `DEFAULT` per row. Rows in one list may now omit different optional keys or pass `undefined`; `null` stays NULL.

- postgres.js crashed with an uncaught `socket.write` when a reserved connection was used after its socket closed, and put a closed connection back in its pool on `release()`.

- node-postgres raised an unhandled `error` event when a checked-out client lost its socket, and returned that client to the pool.

- Bun.sql parked a reserved connection that had been lost.
