Make the release guide and guards tell the truth before v0.15.0

Merged
JE
2mo ago
+281-58

The release surface carried nine catalogued defects under the standing rule "don't tag before fixing". This PR repairs the seven that live in the tree; the other two happen at tag time (# Unreleased → # v0.15.0 promotion with make bump; the version-specific release-body line). Doc + tooling only — no SDK behaviour. While it was in review, main moved by four merges (#804, #807, #809, #810); those are absorbed too.

What this fixes

  1. Two post-tag entries re-filed out of # v0.14.0 into # Unreleased: the #662 absent-expiry changes and the TimelineEventData retype (both landed in #703, after the tag — git show go/v0.14.0:MIGRATING.md contains neither heading).

  2. Five missing entries written (four from the original catalogue, one found by review):

    • #773 (PR #779): the Documents/ScheduleEntry read paths — Get/GetEntry and the composites built on them — return a transport failure verbatim instead of stamping it permanently malformed.
    • #737 (PR #754): four TS paginated methods now declare the ListResult they always returned.
    • #735 (PR #749): Swift public inits — recorded as not a break (verified: no existing initializer changed shape; updateMyPreferences was uncallable outright, updateGaugeNeedle callable only as the nil-payload {} bc3 400s).
    • 1919e77f7 (bare commit): maxPages enforced at runtime in TS/Ruby.
    • Ruby crash-to-ApiError class (bare commits 2f21c9de7, 328153020, 478514642, surfaced by Codex): mailto:/hostless server-supplied URLs in Link headers, redirects, and the download hop used to crash with raw URI/ArgumentError exceptions; all three now refuse with ApiError. One combined entry records the class and the rescue that stops matching.
  3. #650 recount re-derived from both schemas (two of the seven new keys conditional, plus position, which was already modeled).

  4. #604's Kotlin row now names decodeFailure as the discriminator, matching the KDoc.

  5. Trailer rewritten: verification commit and the true in-flight set (now fa15fc126; the event-feed stack and two drafts).

  6. make release guard gap closed, then hardened by review: the target originally grepped seven of the ten files scripts/bump-version.sh writes. Now: root and TS package.json read via jq field access; TS client constant exact; python/pyproject.toml parsed from its [project] table (awk section-scoped); both Gemfile.lock records (PATH spec + CHECKSUMS) exact whole-line matched in both lockfiles; TS package-lock.json both fields via jq. Every guard mutation-proven, including the literal bypasses (nested JSON "version" field; a version = assignment in a foreign TOML table) demonstrated against the old checks and refused by the new ones. Restore by copy, diff -q clean, real exit codes captured.

  7. Release bodies state the notes' blind spot: generated notes are built from merged PRs and cannot see bare commits; MIGRATING.md records consumer-visible changes regardless of how they merged.

  8. Main-merge absorption (#804/#807/#809/#810): #809/#810 wrote their own entries and carry breaking; #807 is CI-internal; #804 was breaking-labeled with no entry — its entry is added (address-policed discovery hop 2, the variadic NewDiscoverer compile note, remedies in policy order).

Labels

breaking applied to: #703, #716, #723, #725, #726, #754, #779, #782, #796 (joining #772; #804/#809/#810 arrived already labeled). Declined in writing: #749 (not a break), #781, #727, #751 (rationales in the thread history).

Verification

  • LC_ALL=C make doc-constants-check green (REAL_EXIT captured) after every MIGRATING edit.
  • Every # Unreleased entry cross-checked against git log go/v0.14.0..origin/main; #770/#771 ruled out as unentried breaks by inspection.
  • All guard proofs as above.

Not doing (declined in writing): a CHANGELOG — MIGRATING.md plus label-generated notes is the convention; a third surface is a third thing to drift.