release: v0.9.0#322
Conversation
Bump the workspace to 0.9.0 and roll the 44 unreleased fragments into the changelog. Four crates pinned buffa by literal version rather than through the workspace table, so bumping the root manifest alone left buffa-codegen, buffa-build, protoc-gen-buffa and protoc-gen-buffa-packaging depending on ^0.8.0 — which does not resolve once the workspace is 0.9.0, and would have published the tooling against the previous runtime. The bsr-quickstart comment that tells downstream projects which crates.io version to depend on is bumped too. No-Verification-Needed: version bump and generated changelog only
|
All contributors have signed the CLA ✍️ ✅ |
The intro counted four breaking changes and there are five. The encoders taking `&mut impl EncodeSink` instead of `&mut impl BufMut` labelled itself "Breaking:" but sat under Changed, so counting the Breaking section — which is how the number was checked — could not see it. Moved to where it belongs and counted again. A reader porting a hand-written ViewEncode impl would have trusted the list of four and met a compile error. The performance figures claimed more than the measurements support. "-24.66%" is four significant figures against a reproducibility floor this project documents at ±5%, and the "-7%" end of the view range sits inside the band the same document calls noise. Both now state the effect and name the floor. The summary also called the plain-varint path a bulk pass, which its own entry contradicts: fixed-width decodes in one call, varint hoists the per-element dispatch out of a loop that remains. The migration guides still told new users to depend on 0.7, two releases stale. Only the buffa pins move — the pbjson lines quote prost's real version. No-Verification-Needed: changelog and docs prose only
|
[claude code] Pushed The intro counted four breaking changes; there are five. The encoders taking The performance figures claimed more than the measurements support. I had written Also: the intro described the plain-varint path as a bulk pass, which its own entry contradicts (fixed-width decodes in one call; varint hoists the per-element dispatch out of a loop that remains), and the two migration guides still told new users to depend on Marking ready. Post-merge: tag |
…eaks Iain's structure, with the claims checked against the code and the measurements. Six corrections: try_encode_bounded has no default — max_bytes is a required argument. The default that matches the specification is the 2 GiB ceiling every encode path now enforces, which is a different change; the two are now described separately. OwnedView::to_owned_message was not an unintentional regression. It became fallible in 0.8.0 as a deliberate part of the CVE-2026-55407 fix, which put view-to-owned conversion under the decode-time limit. 0.8.1's accounting fix made that error unreachable for wire-decoded views, and only then was the Result dead weight. Calling it an accident credits us with less than we did. The performance range undersold the work. Packed decode is -36% on the columnar batch and -16% on shorter arrays; view inlining is worth up to -32%. The old "25-32%" matched no measured pair. The build flags the notes promise to disclose are lto = true, codegen-units = 1, -Cllvm-args=-align-all-nofallthru-blocks=6 and -Cllvm-args=-align-loops=64. The layout swing is ~20%, not 25% — and the striking part is what the benchmarks/history README actually documents: byte-identical machine code, moved by a cache line. The +/-5% reproducibility floor is a separate number and is now stated as one. The worked example arrives at ~400 MiB, not >200 MiB. Headings are h3: at h2 they would have split the release, becoming siblings of [0.9.0] and [0.8.1]. The at-a-glance list points at the migration notes rather than competing with them. No-Verification-Needed: changelog prose only
A list that says "WirePayload is now opaque" tells a reader nothing they can act on. Each entry now carries the reason and, more usefully, the boundary between code that breaks and code that does not. WirePayload: a variant holding exactly the field's bytes cannot see the wire buffer around them, which is what the slack-aware UTF-8 validator needs for its fast path. Accessor callers are unaffected; constructors and matches move. Inline fields: reading and writing through the MessageField API is unchanged, so most code touching the structs needs no edit — what breaks is an explicit MessageField<Foo> annotation, which still names the boxed form. The memory tradeoff is stated too, since an unset inline field costs size_of::<T>() where a pointer used to, and box_type_in is the per-field way back. Size helpers: the u64 widening is what makes the 2 GiB ceiling enforceable at all, because sizes must accumulate in a type that cannot wrap before anything can check them. Literals still infer, so hand-written call sites are a small edit or none; checked-in generated code will not compile until regenerated, and that is the consequence worth leading with. No-Verification-Needed: changelog prose only
"primarily consists of a set of additional controls and fixes" spent four words on the work of one, weighted a headline feature and routine maintenance equally, hedged with "primarily" without saying what the remainder was, and left out the performance work — which is a third of the release and the part most readers will notice first. The replacement names what the release does in the order a reader cares about, and says "produce and allocate" rather than "resources" because those map onto the two controls exactly: encode produces bytes, decode allocates memory. No-Verification-Needed: changelog prose only
The 0.9.0 file mixed hard-wrapped and flowing entries because there was no rule, only a permission: CONTRIBUTING said hard-wrapped continuation lines "is fine". Both styles were therefore correct, so both appeared — 18 wrapped against 31 flowing. The example under that sentence made it worse by wrapping itself while describing the wrapping. Flowing wins on the merits and was already the majority. Hard-wrapped prose has to be re-flowed by hand on every later edit and produces diffs where a one-word change repaints a paragraph; MD013 is off, so nothing was asking for the wrapping in the first place. Reflowed the 0.9.0 section (170 continuation lines joined) and stated the rule in CONTRIBUTING, with an example that now demonstrates it rather than contradicting it. The reflow is checked to be whitespace-only: it aborts if a single word differs. Earlier releases are left alone — those notes are published, and rewriting them would be churn against a permanent record. No-Verification-Needed: changelog and contributor-doc prose only
Cuts v0.9.0: workspace version bump plus the 44 unreleased changelog fragments rolled into
CHANGELOG.md.Scope
53 merged PRs since v0.8.0, in four themes:
try_encode_boundedlets a caller express their own budget in one size pass (Add try_encode_bounded: budget-checked single-pass encode entry points #320).Tag::newand varint inlining (encoding: add #[inline] to Tag::new #259, encoding: force-inline varint decode in packed element loops #265), smoothutf8 0.2.3 (deps: bump smoothutf8 to 0.2.3 #246).buffa-remote-derive(Add buffa-remote-derive: ProtoString/ProtoBytes/ProtoList/ProtoBox/MapStorage for remote types #251),override_feature_in(codegen: path-scoped editions feature overrides (override_feature_in), first entry enum_type:OPEN #291),exclude_package(feat(codegen): add exclude_package to drop option-only imports #279), thejifffeature (buffa-types: addjifffeature (Timestamp/Duration conversions) #264),EncodeSink/Rope(encode: vectored (rope) output via an EncodeSink abstraction #284), package-rootFILE_DESCRIPTOR_SET_BYTES(codegen: root re-export of FILE_DESCRIPTOR_SET_BYTES, strip source_code_info #286).Four breaking changes, all on the trait surface or generated-code shape:
WirePayloadopaque, inline message fields, infallibleOwnedView::to_owned_message, u64 size helpers. MSRV unchanged at 1.75.Notes on the prep
Four crates pinned
buffaby literal version rather than through the workspace table —buffa-codegen,buffa-build,protoc-gen-buffa,protoc-gen-buffa-packaging. Bumping the root manifest alone left them on^0.8.0, which does not resolve against a 0.9.0 workspace; the local CI-parity run failed on it before CI saw it. Left unfixed, this would have published the tooling depending on the previous runtime. Worth converting these toworkspace = truein a follow-up so the next bump cannot repeat it.v0.8.1 is not an ancestor of
main— it shipped from thev0.8.xmaintenance branch, sogit describeon main reports v0.8.0. I checked whether 0.9.0 would regress it: it does not. v0.8.1 was a single release commit, and its fix (#266,6ac87ed) reached main independently and is documented in main's[0.8.1]section. 0.9.0 is a strict superset.Fragment audit. 44 fragments cover every user-visible merged PR. The ones without a fragment are CI (#230, #245, #288, #296), benchmarks (#243, #258, #318), and work already released in 0.8.1 (#252, #266) — none warrant release notes.
Verification
cargo clippy --workspace --all-targets --all-features -- -D warnings,cargo test --workspace --all-features(48 suites),RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps,cargo fmt --all --check,task lint-md— all clean.task changelog-mergeis idempotent, socheck-changelogshould pass.column_batch/decode−24.664% is from the metal run's criterion output; the view range is −7% to −32% across seven of eight benchmarks, the eighth being a dataset the change cannot touch.Post-merge
Tag
v0.9.0on the merge commit → the tag-triggered publish workflow ships all crates.