From 06dc3aca2438076c3c0fd05b7f2ae62f4f6a873b Mon Sep 17 00:00:00 2001 From: Eric Andrechek Date: Wed, 21 Oct 2026 02:18:46 -0400 Subject: [PATCH 1/2] docs (v2): the Spans change compiles silently in Go; check each entry BatchResult.Spans became []*Span (Python: Span | None, TS: Span | null), nil for a row the export did not carry. In Go, code written for []Span that reads r.Spans[i].Off still compiles and panics at run time on a nil entry; Python and TS fail the same way at run time; Rust's Option makes the compiler catch it. The four CHANGELOG bullets and the bindings-v1.md row now say so. Co-Authored-By: Claude Opus 5.5 --- docs/reference/bindings-v1.md | 44 +++++++++++++++++------------------ go/CHANGELOG.md | 2 +- python/CHANGELOG.md | 2 +- rust/CHANGELOG.md | 2 +- ts/CHANGELOG.md | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/reference/bindings-v1.md b/docs/reference/bindings-v1.md index 3d23924d..441e8c3f 100644 --- a/docs/reference/bindings-v1.md +++ b/docs/reference/bindings-v1.md @@ -420,28 +420,28 @@ The transform entry keeps v0's shape (`column`, `input`, `stored`, `reason`, `ro ### `BatchResult`, from the `batch` document and the export buffer -| field | from | type | -| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -| `outcome` | `outcome`, final | `Outcome` | -| `err_code`, `err_msg` | `code`, `err` (rule 5) | int32, bytes | -| `reason` | `reason`: the catalog id of a declined batch, present exactly where `code` is `CHS_CODE_DECLINED` | optional ASCII text | -| `refused_at`, `refused_row` | `refused_at` (a `refusal_site` value) and `refused_row`: where a refusal or decline sits, present whenever the outcome is not `accepted` or `accepted_poisoned`; `refused_row` for `row`, below `rows_read` | optional `RefusalSite`, optional unsigned 64-bit | -| `rows` | `rows`, each decoded as a `RowResult` | list of `RowResult` | -| `rows_read` | `rows_read` | unsigned 64-bit | -| `rows_skipped` | `rows_skipped` | unsigned 64-bit | -| `transformed` | `transformed`: every transform in the batch with its `row`, storage transforms (TTL) included, as the library lists them | list of `Transform` | -| `unsupported_settings` | `unsupported_settings`, name objects (rule 5): the call's own settings the library declined (ABI v2) | list of bytes | -| `engine_rows` | `engine_rows`: each stored row after the engine's insert-time merge, a list of cells | optional list of rows, each a list of `EngineCell` | -| `at_merge` | `at_merge`: what an `OPTIMIZE TABLE ... FINAL` of the part would do to its rows (ABI v2) | list of `AtMergeEntry` | -| `payload` | the `out_export` buffer, present only for an export that ran (absent when `export_declined` is non-empty) | optional payload | -| `spans` | `row_spans`: one entry per `rows` entry, index-aligned, each exported row's place in `payload` and null for a row not exported | optional list of optional `Span` | -| `export_columns` | `export_columns`: the export's one column list, name objects (rule 5) | optional list of bytes | -| `export_declined` | `export_declined` (rule 5) | bytes | -| `export_declined_reason` | `export_declined_reason`: the catalog id of a declined export | optional ASCII text | -| `rows_passed`, `rows_cut` | `rows_passed`, `rows_cut`, with an attached filter | unsigned 64-bit | -| `partition_count` | `partition_count` | optional unsigned 64-bit | -| `unconsumed` | `unconsumed`: the byte ranges the reader's error recovery skipped | list of `Span` | -| `framing` | `framing`: what the reader decided about the body's framing | optional `Framing` | +| field | from | type | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | +| `outcome` | `outcome`, final | `Outcome` | +| `err_code`, `err_msg` | `code`, `err` (rule 5) | int32, bytes | +| `reason` | `reason`: the catalog id of a declined batch, present exactly where `code` is `CHS_CODE_DECLINED` | optional ASCII text | +| `refused_at`, `refused_row` | `refused_at` (a `refusal_site` value) and `refused_row`: where a refusal or decline sits, present whenever the outcome is not `accepted` or `accepted_poisoned`; `refused_row` for `row`, below `rows_read` | optional `RefusalSite`, optional unsigned 64-bit | +| `rows` | `rows`, each decoded as a `RowResult` | list of `RowResult` | +| `rows_read` | `rows_read` | unsigned 64-bit | +| `rows_skipped` | `rows_skipped` | unsigned 64-bit | +| `transformed` | `transformed`: every transform in the batch with its `row`, storage transforms (TTL) included, as the library lists them | list of `Transform` | +| `unsupported_settings` | `unsupported_settings`, name objects (rule 5): the call's own settings the library declined (ABI v2) | list of bytes | +| `engine_rows` | `engine_rows`: each stored row after the engine's insert-time merge, a list of cells | optional list of rows, each a list of `EngineCell` | +| `at_merge` | `at_merge`: what an `OPTIMIZE TABLE ... FINAL` of the part would do to its rows (ABI v2) | list of `AtMergeEntry` | +| `payload` | the `out_export` buffer, present only for an export that ran (absent when `export_declined` is non-empty) | optional payload | +| `spans` | `row_spans`: one entry per `rows` entry, index-aligned, each exported row's place in `payload` and null for a row not exported Check each entry before reading it: in Go, Python and TS, reading a field of a nil entry compiles but fails at run time. | optional list of optional `Span` | +| `export_columns` | `export_columns`: the export's one column list, name objects (rule 5) | optional list of bytes | +| `export_declined` | `export_declined` (rule 5) | bytes | +| `export_declined_reason` | `export_declined_reason`: the catalog id of a declined export | optional ASCII text | +| `rows_passed`, `rows_cut` | `rows_passed`, `rows_cut`, with an attached filter | unsigned 64-bit | +| `partition_count` | `partition_count` | optional unsigned 64-bit | +| `unconsumed` | `unconsumed`: the byte ranges the reader's error recovery skipped | list of `Span` | +| `framing` | `framing`: what the reader decided about the body's framing | optional `Framing` | | type | fields, each from the same-named key unless noted | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/go/CHANGELOG.md b/go/CHANGELOG.md index 47de7371..fe814d0a 100644 --- a/go/CHANGELOG.md +++ b/go/CHANGELOG.md @@ -74,7 +74,7 @@ The four bindings in this repository are released together and give one answer, - **Enforcement gate lifted** for `26.8` on `darwin-arm64`. - **Enforcement gate lifted** for `26.9` on `darwin-arm64`. - `docs/limitations.md` is re-checked against artifact build `20261012.142240` (v2-dev.13), the library half measured on that build: functions that need a server, in a column expression, and a body the server may read serially or in parallel under a non-zero error budget are listed under "Declined by design"; the known divergence for a column expression that calls a function needing a server is retired, since that build declines those schemas (on 26.8 and 26.9 the `AI*` and `ai*` functions compile, as the page says); and the `lossy` raw-NUL TSV gap is marked fixed on that build and still present in the 1.0 libraries. `docs/guides/filters.md` gains a section on subcolumn filters and a JSON value typed by its batch. -- **Breaking (ABI v2 dev): `BatchResult.Spans` is `[]*Span`**, one entry per `Rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut), which the entry reads as nil. It was `[]Span` of the exported rows only. +- **Breaking (ABI v2 dev): `BatchResult.Spans` is `[]*Span`**, one entry per `Rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut), which the entry reads as nil. It was `[]Span` of the exported rows only. **This change compiles silently:** code written for `[]Span` that reads `r.Spans[i].Off` still builds, because Go dereferences the pointer for you, and it panics at run time on a nil entry. Check each entry for nil before reading it. - **Breaking (ABI v2 dev): `AtMergeEntry.Row` is `AtMergeEntry.PartRow`**, the document's `part_row`. - A document's `code` can be `CodeDeclined` (-2) for a row, batch, filter result or verdict this build declined, and every decline names its `Reason`; a `Source` the description does not list reads the description's fallback (`skipped`), not stored. diff --git a/python/CHANGELOG.md b/python/CHANGELOG.md index 2128fdda..2e1b4a76 100644 --- a/python/CHANGELOG.md +++ b/python/CHANGELOG.md @@ -34,7 +34,7 @@ The four bindings in this repository are released together and give one answer, ### Changed -- **Breaking (ABI v2 dev): `BatchResult.spans` is a tuple of `Span | None`**, one entry per `rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut), which reads as `None`. It was the exported rows' spans only. +- **Breaking (ABI v2 dev): `BatchResult.spans` is a tuple of `Span | None`**, one entry per `rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut), which reads as `None`. It was the exported rows' spans only. Code that reads `span.off` from every entry raises `AttributeError` on a `None` entry: check each entry before reading it. - **Breaking (ABI v2 dev): `AtMergeEntry.row` is `AtMergeEntry.part_row`**, the document's `part_row`. - A document's `code` can be -2 (`CHS_CODE_DECLINED`) for a row, batch, filter result or verdict this build declined, and every decline names its `reason`. - The refusal of a settings, query-parameter, default or server-settings value that is not valid UTF-8 is gone (it is the `{"b64": ...}` form now); a key that is not valid UTF-8, a macro and a server timezone are still refused, as a `UsageError` naming the input. diff --git a/rust/CHANGELOG.md b/rust/CHANGELOG.md index 63c1f3f0..8d36438b 100644 --- a/rust/CHANGELOG.md +++ b/rust/CHANGELOG.md @@ -35,7 +35,7 @@ The four bindings in this repository are released together and give one answer, ### Changed -- **Breaking (ABI v2 dev): `BatchResult::spans` is `Option>>`**, one entry per `rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut), which the entry reads as `None`. It was `Option>` of the exported rows only. +- **Breaking (ABI v2 dev): `BatchResult::spans` is `Option>>`**, one entry per `rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut), which the entry reads as `None`. It was `Option>` of the exported rows only. The compiler makes you handle the `None` entry for a row not exported. - **Breaking (ABI v2 dev): `AtMergeEntry::row` is `AtMergeEntry::part_row`**, the document's `part_row`. - **Breaking (ABI v2 dev): `SchemaServer::settings` is `BTreeMap`** (was `BTreeMap`), because a server profile's setting value can be bytes that are not valid UTF-8. - `chtypes list` and the tag listing name the published lines only (two-part, numeric order), as Go's always did; `chtypes resolve ` prints the exact version a line resolves to (#613). diff --git a/ts/CHANGELOG.md b/ts/CHANGELOG.md index e3e6c4fe..c3b88556 100644 --- a/ts/CHANGELOG.md +++ b/ts/CHANGELOG.md @@ -81,7 +81,7 @@ The four bindings in this repository are released together and give one answer, - **Enforcement gate lifted** for `26.8` on `darwin-arm64`. - **Enforcement gate lifted** for `26.9` on `darwin-arm64`. - `docs/limitations.md` is re-checked against artifact build `20261012.142240` (v2-dev.13), the library half measured on that build: functions that need a server, in a column expression, and a body the server may read serially or in parallel under a non-zero error budget are listed under "Declined by design"; the known divergence for a column expression that calls a function needing a server is retired, since that build declines those schemas (on 26.8 and 26.9 the `AI*` and `ai*` functions compile, as the page says); and the `lossy` raw-NUL TSV gap is marked fixed on that build and still present in the 1.0 libraries. `docs/guides/filters.md` gains a section on subcolumn filters and a JSON value typed by its batch. -- **Breaking (ABI v2 dev): `BatchResult.spans` is `(Span | null)[]`**, one entry per `rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut). It was the spans of the exported rows only. +- **Breaking (ABI v2 dev): `BatchResult.spans` is `(Span | null)[]`**, one entry per `rows` entry and index-aligned with it: `row_spans` carries `null` for a row the export did not carry (a skipped row, or a row a filter cut). It was the spans of the exported rows only. Code that reads `span.off` from every entry throws a `TypeError` on a `null` entry: check each entry before reading it. The type system flags this only with `strictNullChecks`. - **Breaking (ABI v2 dev): `AtMergeEntry.row` is `AtMergeEntry.partRow`**, the document's `part_row`. - **Breaking (ABI v2 dev): `Settings` values and `sessionTimezone` take `string | Uint8Array`**, and a string holding a lone surrogate (no bytes) is still refused as a `UsageError`; the refusal of a value that is not UTF-8 is gone for a `Uint8Array`, while a key that is not valid UTF-8 is still refused. - A document's `code` can be -2 for a row, batch, filter result or verdict this build declined, and every decline names its `reason`; a `Source` the description does not list reads the description's fallback (`skipped`), not stored. From 30ef4eb89a2fd6038af91e63dd490bf298218b3a Mon Sep 17 00:00:00 2001 From: Eric Andrechek Date: Wed, 21 Oct 2026 02:23:26 -0400 Subject: [PATCH 2/2] docs (v2): how a column_defaults value is read, with measured examples The value is read in the body format's own field encoding, by the reader that reads that column's field: CSV quoting, TSV escapes, a JSON string or number. Measured by a downstream user on build 20261021.025722 (darwin-arm64). A value that does not read is refused at the call, before any body byte. Guide subsection plus one CHANGELOG bullet per binding; spec/abi-v2/ is untouched and the ABI v2 fingerprint is unchanged (d53239933966...41c2, measured before and after). Co-Authored-By: Claude Opus 5.5 --- docs/guides/batches.md | 14 ++++++++++++++ go/CHANGELOG.md | 1 + python/CHANGELOG.md | 1 + rust/CHANGELOG.md | 1 + ts/CHANGELOG.md | 1 + 5 files changed, 18 insertions(+) diff --git a/docs/guides/batches.md b/docs/guides/batches.md index 9421c4a2..ed59af3f 100644 --- a/docs/guides/batches.md +++ b/docs/guides/batches.md @@ -342,6 +342,20 @@ let batch = schema.rows(Format::JsonEachRow, body, &options)?; +### How a `value` is read + +The library reads a `value` with the same reader that reads that column's field in the body: **the body format's own field encoding**, including its quoting and escapes. Measured by a downstream user on build `20261021.025722` (darwin-arm64): + +| body format | `value` | result | +| -------------------------- | ------------------- | -------------------------------------------------------------------- | +| CSV | `"a,""b"` | read: the string `a,"b` | +| CSV | `a,b` (unquoted) | refused: ClickHouse error 27 (`CANNOT_PARSE_INPUT_ASSERTION_FAILED`) | +| TSV | `a\tb` (the escape) | read: `a`, a tab, `b` | +| TSV | `a`, a raw tab, `b` | refused: error 27 | +| JSONEachRow, into `UInt32` | `"42"` or `42` | both read as 42 | + +A value that doesn't read is refused when the call starts, as a usage error, before any byte of the body is read. So write each `value` exactly as it would appear in a field of the body you send. + ## Next - [`transformations.md`](transformations.md) — what the accepted rows were silently changed into. diff --git a/go/CHANGELOG.md b/go/CHANGELOG.md index fe814d0a..d631e437 100644 --- a/go/CHANGELOG.md +++ b/go/CHANGELOG.md @@ -9,6 +9,7 @@ The four bindings in this repository are released together and give one answer, ### Added - `column_defaults` is covered by unit tests (ABI v2): the exact `insert_options` JSON for a UTF-8 value, a non-UTF-8 value (`value_b64`), an empty value (sent as `""`) and two entries in order, and the decode of a `default_override` cell (`SourceDefaultOverride`, stored). The batches guide gains a usage section. No behavior changed. +- Docs: `docs/guides/batches.md` shows how a `column_defaults` value is read, in the body format's own field encoding (CSV quoting, TSV escapes, a JSON string or number), with a value that doesn't read refused at the call before any body byte. - `CallError.Reason` (ABI v2): the error's catalog id, read through `chs_error_reason` by the generated layer and passed through verbatim (ASCII, empty if none), on all four call classes. - A fetch-only call (#492): `(*Registry).Fetch(ctx, request) (Resolved, error)` resolves, fetches, verifies and installs the build a request names into the cache, as `chtypes fetch` does, and opens nothing: no library is loaded and the process setup is untouched, so it needs no `Setup`. It honors the registry's fetch options (offline, the cache, and under the production channel the bases, the trust and the lock), and a concurrent `Fetch` and open of one request share one fetch. The `Resolved` names the installed version, build, digests and library path; errors are the fetch codes, and a done `ctx` is `ctx.Err()`. - `chtypes resolve [--json]` (#493): what a line or version resolves to on every platform the registry offers, one `resolved ` line each, or with `--json` one JSON array of `{platform, version, build, manifest}` objects, each signed statement verified exactly as a fetch verifies it and no layer downloaded; `--offline` answers from the cache. The four CLIs print the same bytes, so one parser reads them (a CI cache key, for example). diff --git a/python/CHANGELOG.md b/python/CHANGELOG.md index 2e1b4a76..4f3e29e2 100644 --- a/python/CHANGELOG.md +++ b/python/CHANGELOG.md @@ -9,6 +9,7 @@ The four bindings in this repository are released together and give one answer, ### Added - `column_defaults` is covered by unit tests (ABI v2): the exact `insert_options` JSON for a UTF-8 value, a non-UTF-8 value (`value_b64`), an empty value (sent as `""`) and two entries in order, and the decode of a `default_override` cell (`Source.DEFAULT_OVERRIDE`, stored). The batches guide gains a usage section. No behavior changed. +- Docs: `docs/guides/batches.md` shows how a `column_defaults` value is read, in the body format's own field encoding (CSV quoting, TSV escapes, a JSON string or number), with a value that doesn't read refused at the call before any body byte. - `CODE_DECLINED` (ABI v2, -2): the code a declined row, outcome or verdict carries, generated from the description and exported from `chtypes`, as `bindings-v1.md` section 3 promises. - `CallError.reason` (ABI v2): the error's catalog id, read through `chs_error_reason` and passed through verbatim (ASCII, empty if none), on all four call classes. - Spec batch 2, the options documents (ABI v2; moves the dev fingerprint): keyword-only `insertable_columns`, `column_defaults` (a sequence of `ColumnDefault`), `verdict_basis` (`VerdictBasis`: `INPUT`, `EXPORT`) and `defer_defaults` (`DeferDefaults`: `NONE`, `SERVER`) build `insert_options` for `Schema.row`, `Schema.rows`, `Schema.parse_block` and `Filter.rows`; `param_encoding` (`ParamEncoding`: `ESCAPED`, `RAW`) builds `filter_options` for `Schema.compile_filter`; `posture` (`Posture`: `CREATE`, `LOAD`) builds `schema_options` for `Library.compile_table`. `insert_options_extra`, `filter_options_extra` and `schema_options_extra` pass a member the binding has no name for through as JSON text, so the library, which refuses an unknown key (rule r1), is the only judge. A document with no member set is not sent. `Filter.rows` takes `columns` (`chs_filter_eval_body` takes the column list). diff --git a/rust/CHANGELOG.md b/rust/CHANGELOG.md index 8d36438b..d0b29caa 100644 --- a/rust/CHANGELOG.md +++ b/rust/CHANGELOG.md @@ -9,6 +9,7 @@ The four bindings in this repository are released together and give one answer, ### Added - `column_defaults` is covered by unit tests (ABI v2): the exact `insert_options` JSON for a UTF-8 value, a non-UTF-8 value (`value_b64`), an empty value (sent as `""`) and two entries in order, and the decode of a `default_override` cell (`Source::DefaultOverride`, stored). The batches guide gains a usage section. No behavior changed. +- Docs: `docs/guides/batches.md` shows how a `column_defaults` value is read, in the body format's own field encoding (CSV quoting, TSV escapes, a JSON string or number), with a value that doesn't read refused at the call before any body byte. - `chtypes::CODE_DECLINED` (ABI v2, `i32`, -2): the code a declined row, outcome or verdict carries, generated from the description. It is at the crate root, not in `code`, which is the fetch-code table. - A fetch-only call (#492): `Registry::fetch(&self, request: &str) -> Result` resolves, fetches, verifies and installs the build a request names into the cache, as `chtypes fetch` does, and opens nothing: no library is loaded and the process setup is untouched, so it needs no `setup`. It honors the registry's fetch options (offline, the cache, and under the production channel the bases, the trust and the lock), whatever `autofetch` says, and a concurrent `fetch` and open of one request share one fetch. The `Resolved` names the installed version, build, digests and library path; errors are the fetch codes, and a refused spelling or pin is `Error::Usage`, as for an open. - `chtypes resolve [--json]` (#493): what a line or version resolves to on every platform the registry offers, one `resolved ` line each, or with `--json` one JSON array of `{platform, version, build, manifest}` objects, each signed statement verified exactly as a fetch verifies it and no layer downloaded; `--offline` answers from the cache. The four CLIs print the same bytes, so one parser reads them (a CI cache key, for example). diff --git a/ts/CHANGELOG.md b/ts/CHANGELOG.md index c3b88556..880c7cfc 100644 --- a/ts/CHANGELOG.md +++ b/ts/CHANGELOG.md @@ -30,6 +30,7 @@ The four bindings in this repository are released together and give one answer, - A 2.0.0-dev SDK prefers the newest build of its own fingerprint (#511). A version request resolves `--fp-` first, the alias the staging dev registry serves beside every dev tag, with the binding's own generated ABI fingerprint, and the tag itself only when every base answers the alias 404; any other failure of the alias request is that failure, with no fallback, and an alias whose index lacks the platform is `CHTYPES_ARTIFACT_UNPUBLISHED`. The dev channel's cache lookups (the installed-build lookup a registry open makes first, the offline answer, and the monotonic rule of a fetch) now see only records whose signed `abi_fingerprint` is the SDK's own, so a newer build of another fingerprint, installed by another dev SDK in the shared cache, is never returned or kept over this one's own, and is never removed or rewritten. So a dev build of a newer fingerprint no longer strands an older dev SDK until its consumers upgrade. Trust is unchanged (the signed predicate and the load checks decide), a listing never shows an alias, nothing records one, `list` and `where` are unchanged, and there is no option: both steps are the dev channel's alone, and the v1 contract and every production channel never take them. The rule is `docs/guides/fetch-v1.md` section 3, and the `dev-alias-*`, `resolve-installed-dev-alias-*` and `list-tags-dev-alias` conformance cases hold all four bindings to it. - `CallError.reason` (ABI v2): the error's catalog id, read through `chs_error_reason` beside the other `chs_error_*` fields and passed through verbatim (ASCII, empty if none), on all four call classes and on `CallErrorFields`. A misuse the binding detects itself carries the empty string. - Spec batch 2, the options documents (ABI v2; moves the dev fingerprint): `insertableColumns`, `columnDefaults` (a list of `ColumnDefault`), `verdictBasis` (`VerdictBasis.Input`, `VerdictBasis.Export`) and `deferDefaults` (`DeferDefaults.None`, `DeferDefaults.Server`) build `insert_options` for `schema.row`, `schema.rows`, `schema.parseBlock` and `filter.rows`; `paramEncoding` (`ParamEncoding.Escaped`, `ParamEncoding.Raw`) builds `filter_options` for `compileFilter`; `posture` (`Posture.Create`, `Posture.Load`) builds `schema_options` for `compileTable`. `insertOptionsExtra`, `filterOptionsExtra` and `schemaOptionsExtra` pass a member the binding has no name for through as JSON text, so the library, which refuses an unknown key (rule r1), is the only judge. A document with no member set is not sent. `columns` now applies to `filter.rows` too (`chs_filter_eval_body` takes the column list). +- Docs: `docs/guides/batches.md` shows how a `column_defaults` value is read, in the body format's own field encoding (CSV quoting, TSV escapes, a JSON string or number), with a value that doesn't read refused at the call before any body byte. - Spec batch 2, a value that is not UTF-8 (ABI v2): a settings value, a query-parameter value, a `setup` default and a server profile's settings value may be a `Uint8Array` (`SettingValue` is `string | Uint8Array`), and bytes that are not valid UTF-8 are sent as `{"b64": ...}` (the content `json_object_byte_values`), where the binding used to refuse the value; so may `sessionTimezone`. `SchemaServer.settings` decodes the same form back to a `Buffer`. - Spec batch 2, the decline and refusal members (ABI v2): `RowResult`, `BatchResult` and `FilterResult` carry `reason` (the catalog id, present where the code is -2, `CHS_CODE_DECLINED`), `refusedAt` (a `RefusalSite`: `Call`, `BodyRead`, `Row`, `BodyStep`) and `refusedRow`; `RowResult.verdictReason`; `FilterRowError.reason`; `BatchResult.exportColumns` and `BatchResult.exportDeclinedReason`. All are `undefined` when the document carries none. - Spec batch 2, the schema description (ABI v2): `SchemaDescription.posture`, and `Column.defaultSupport` (a `DefaultSupport`), `Column.defaultSupportReason` and `Column.reads`, reserved members a build reports when it implements them. `Source.DefaultDeferred`, `Source.DefaultOverride` (stored) and `DeclinedTier.ServerRefuses` join the vocabularies.