Skip to content

release: v0.9.0#322

Merged
iainmcgin merged 6 commits into
mainfrom
release/v0.9.0
Jul 17, 2026
Merged

release: v0.9.0#322
iainmcgin merged 6 commits into
mainfrom
release/v0.9.0

Conversation

@iainmcgin

Copy link
Copy Markdown
Collaborator

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:

Four breaking changes, all on the trait surface or generated-code shape: WirePayload opaque, inline message fields, infallible OwnedView::to_owned_message, u64 size helpers. MSRV unchanged at 1.75.

Notes on the prep

Four crates pinned buffa by literal version rather than through the workspace tablebuffa-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 to workspace = true in a follow-up so the next bump cannot repeat it.

v0.8.1 is not an ancestor of main — it shipped from the v0.8.x maintenance branch, so git describe on 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

  • CI-parity locally per the pre-release checklist: 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-merge is idempotent, so check-changelog should pass.
  • The intro's claims are checked against the data rather than recalled: exactly 4 breaking bullets batched; 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.0 on the merge commit → the tag-triggered publish workflow ships all crates.

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
@github-actions

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

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
@iainmcgin
iainmcgin marked this pull request as ready for review July 17, 2026 18:36
@iainmcgin

Copy link
Copy Markdown
Collaborator Author

[claude code] Pushed 479c357 after a review pass over the prose. Four corrections, two of them substantive:

The intro counted four breaking changes; there are five. The encoders taking &mut impl EncodeSink instead of &mut impl BufMut (#284) labels itself Breaking: but was filed under Changed — so counting the Breaking section, which is exactly how I checked the number, could not see it. Moved into Breaking changes and the count corrected. Someone 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. I had written -24.66% — four significant figures against the ±5% reproducibility floor that benchmarks/history/README.md documents. The deltas are real and clear the floor comfortably, but the precision was fiction; they now state the effect and name the floor. Same for the view range, whose -7% end sat inside the band that document calls noise.

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 0.7. Only the buffa pins moved there — the neighbouring pbjson = "0.7" lines quote prost's real version and are untouched.

Marking ready. Post-merge: tag v0.9.0 on the merge commit and the tag-triggered workflow publishes.

…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
@iainmcgin
iainmcgin requested a review from rpb-ant July 17, 2026 20:14
@iainmcgin
iainmcgin enabled auto-merge July 17, 2026 20:14
@iainmcgin
iainmcgin added this pull request to the merge queue Jul 17, 2026
Merged via the queue into main with commit 07b3c2c Jul 17, 2026
9 checks passed
@iainmcgin
iainmcgin deleted the release/v0.9.0 branch July 17, 2026 20:36
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants