Skip to content

Commit 708b209

Browse files
authored
docs: tighten changelog and release-note guidance (#319)
1 parent 5fdbbcb commit 708b209

2 files changed

Lines changed: 51 additions & 34 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,22 @@ Pre-1.0 note: while `pg_durable` is in major version `0`, minor releases may inc
1010

1111
### Added
1212

13-
- **`df.http_multipart()` (#302):** send `multipart/form-data` requests. Parts are described as a JSONB array of objects with `name` and `data_b64`, optionally `filename` and `content_type`. Gated by the same `include_http => true` grant as `df.http()`.
14-
- **Binary HTTP response bodies:** `df.http()` and `df.http_multipart()` now capture non-textual responses instead of losing them to lossy UTF-8 conversion. The response envelope gained an `encoding` field — `text` when the body is the response as-is, `base64` when it holds the base64 of the raw bytes. A body is treated as text when the `Content-Type` is absent, `text/*`, a `+json`/`+xml`/`+yaml` suffix type, or a common textual `application/*` type; everything else is base64.
15-
- **Variable substitution in multipart `data_b64`:** a part's `data_b64` can now reference an earlier result, e.g. `'data_b64', '$pdf.body'`, letting a payload produced by one HTTP step be uploaded by the next with no intermediate table. The reference must be the *whole* value; mixing it with surrounding text fails the node rather than sending a corrupt part, because splicing into base64 cannot produce a valid encoding.
16-
- **`examples/audio-roundtrip/`:** a self-verifying example that sends text to Azure OpenAI text-to-speech and pipes the returned MP3 straight into a Whisper transcription upload, then checks the transcript against the original phrase.
17-
- **`transaction_mode` argument for `df.start()`:** selects which transaction the *start itself* runs in. `'caller'` (the default) is the historical behaviour — the start joins the caller's transaction and is rolled back with it. `'new'` persists and enqueues the work on a separate PostgreSQL session, so it commits independently and **survives a rollback of the caller's transaction**. This provides the same rollback-survival outcome as Oracle autonomous transactions or `REQUIRES_NEW` for asynchronously started work; it is not a synchronous autonomous routine. The returned ID confirms launch, while completion and execution errors are observed through monitoring APIs. Nothing about the started function changes; only the commit boundary of the start does. An unrecognised value raises rather than falling back to the default, and `'new'` is rejected inside a workflow, where a `df.sql()` node's single autocommitted statement already makes a plain `df.start()` independent of any caller transaction. Because the separate session sees only committed rows, the captured `df.vars` snapshot excludes variables set earlier in the caller's open transaction. Each call uses an extra backend, so per-row and other high-fan-out call sites should avoid this mode; target effects should be idempotent because a connection failure can make launch outcome uncertain. The three-argument `df.start()` is dropped in favour of the four-argument one, but its C symbol is retained, so the new `.so` stays backward compatible with all previous schemas in this provider line (see `docs/upgrade-testing.md`).
13+
- **`df.http_multipart()` (#302):** send `multipart/form-data` requests, gated by the same `include_http => true` grant as `df.http()`.
14+
- **Binary HTTP response bodies:** `df.http()` and `df.http_multipart()` preserve non-text responses as base64 and identify the body format in the response envelope's `encoding` field.
15+
- **Audio round-trip example:** demonstrates piping Azure OpenAI text-to-speech output directly into a Whisper transcription upload.
16+
- **Independent starts (#285):** `df.start(..., transaction_mode => 'new')` commits the start on a separate session, allowing asynchronously started work to survive a caller rollback. The default `'caller'` mode retains the existing transaction behavior.
1817

1918
### Fixed
2019

21-
- **Multipart parts larger than 57 bytes (#302):** `data_b64` was decoded with a strict base64 decoder that rejects embedded whitespace, but PostgreSQL's `encode(bytea, 'base64')` wraps its output at 76 columns. Any part whose source data exceeded 57 bytes therefore failed to decode. Whitespace in `data_b64` is now ignored, so `encode()` output can be used directly.
22-
- **Unix-socket worker connections (#292):** a Unix-socket `PGHOST` is now percent-encoded when pg_durable builds the worker's PostgreSQL connection URL, allowing sqlx to connect through socket directories. TCP addresses and hostnames are unchanged.
20+
- **Unix-socket worker connections (#292):** workers can now connect when `PGHOST` names a Unix-socket directory.
2321

2422
### Changed
2523

26-
- **Loop execution and replay determinism (#228):** non-root `df.loop()` nodes now run as dedicated child sub-orchestrations, replay-recorded result/variable maps serialize in canonical key order, and root/non-root loops share the same body and while-condition policy. This fixes non-root loop restarts and makes `df.break()` in a while-condition behave identically at either graph position. A loop is now hosted by the same `execute-subtree` orchestration that runs parallel branches, rather than a separate loop orchestration.
24+
- **Loop execution (#228):** root and nested loops now share consistent execution, restart, and `df.break()` behavior.
2725

28-
> ⚠️ **Replay-breaking for in-flight instances.** These changes alter recorded orchestration inputs and scheduling order, and duroxide validates both by exact equality on replay.
29-
>
30-
> - **Every in-flight JOIN (`&`) or RACE (`|`) branch fails**, unconditionally. The `execute-subtree` input envelope gained `instance_id`, `vars`, `label`, and `iteration` fields, so a branch scheduled under 0.2.4 replays with an input shape the 0.2.5 binary rejects. This is *not* limited to workflows carrying multiple variables or named results.
31-
> - **In-flight root and non-root `df.loop()` instances may fail** with a nondeterminism error, because loop scheduling and recorded inputs both changed.
32-
>
33-
> The engine fails closed on a mismatch, but `df.instances` may remain `pending` or `running` because the replay aborts before pg_durable can record its normal failure status. Operators that require in-flight continuity should quiesce and drain before upgrading; operators that accept failed/recreated work can upgrade directly and inspect or cancel stale instances afterward. See `docs/upgrade-testing.md` under "Loop and sub-orchestration replay compatibility".
26+
> **Upgrade warning:** in-flight JOIN/RACE branches will fail on replay after upgrading, and in-flight loops may fail. Drain in-flight work before upgrading when continuity is required; see `docs/upgrade-testing.md` for details.
3427
35-
- **HTTP envelope fields are addressable with dot notation:** `$resp.body`, `$resp.status`, `$resp.ok`, and `$resp.encoding` now resolve against an HTTP result. Previously dot notation only worked on SQL results (which carry a `rows` array), so reading an HTTP envelope required an intervening SQL node such as `SELECT ($resp::jsonb->>'ok')::boolean`. `rows` still takes precedence, so SQL results are unaffected.
28+
- **HTTP result substitution:** HTTP response fields such as `$resp.body`, `$resp.status`, `$resp.ok`, and `$resp.encoding` are now directly addressable with dot notation.
3629
- **Dependencies:** bumped `duroxide` to 0.1.30 (#305), `uuid` to 1.24.0 (#293), `serde_json` to 1.0.151, and `tokio` to 1.53.1 (#298).
3730

3831
### Documentation

prompts/pg_durable-release.md

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,24 @@ here (by the agent or human) and lands via a PR — it is **not** automated.
9595
version, following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
9696
(grouped Added / Changed / Fixed / Security / Documentation, plus a Breaking
9797
Changes callout when relevant).
98-
3. **If the section is missing or empty**, draft it:
98+
3. Review the section as user-facing release history, even if it is already
99+
complete. Propose edits when entries are wordy, implementation-focused, or
100+
more detailed than users need. Remove pure CI/infrastructure work, expected
101+
behavior of a newly added feature, and bugs introduced and fixed within the
102+
same unreleased cycle. Keep operationally important compatibility, migration,
103+
and security guidance, but link to detailed docs instead of reproducing them.
104+
4. **If the section is missing, empty, or needs the editorial fixes above**,
105+
draft or revise it:
99106
```bash
100107
# Merged, user-facing changes since the previous tag
101108
git log --oneline --no-merges vX.Y.<prev>..main
102109
# Resolve a squashed commit to its PR when the number isn't in the subject
103110
gh api repos/microsoft/pg_durable/commits/<sha>/pulls --jq '.[].number'
104111
```
105-
Curate into user-facing entries with PR references. **Exclude** pure CI/infra
106-
noise (e.g. adding a linter, Dependabot config) — that lives in git history,
107-
not the changelog. Verify dependency lines against `Cargo.toml` (don't claim a
108-
`duroxide`/`duroxide-pg` bump that didn't happen).
109-
4. Open a PR with the changelog (and any docs sweep from Step 2), get it merged
112+
Curate into user-facing entries with PR references. Verify dependency lines
113+
against `Cargo.toml` (don't claim a `duroxide`/`duroxide-pg` bump that didn't
114+
happen).
115+
5. Open a PR with the changelog (and any docs sweep from Step 2), get it merged
110116
to `main`. **Do not tag until the changelog for the release is on `main`.**
111117

112118
> **Update the tracking issue:** link the changelog PR and tick **Changelog
@@ -195,16 +201,33 @@ gh run watch "$(gh run list --workflow package-release.yml --limit 1 --json data
195201

196202
## Step 5: Fill release notes and publish
197203

198-
The Package Release run creates the draft with placeholder notes. Replace them
199-
with the curated changelog **plus** an **Acknowledgements** credit and GitHub's
200-
auto-generated **New Contributors** section, then publish. The release-body
201-
content already lives in the committed `CHANGELOG.md`, so extract this version's
202-
section on the fly into a throwaway temp file — **do not** create or commit a
203-
separate `release-notes-*.md`.
204+
The Package Release run creates the draft with placeholder notes. Write a brief,
205+
punchy release summary as a **separate editorial exercise** from the changelog,
206+
then add an **Acknowledgements** credit and GitHub's auto-generated **New
207+
Contributors** section. The changelog is the complete user-facing history; the
208+
GitHub Release should help readers scan the release's value and upgrade impact.
209+
210+
Use the committed changelog as source material, but do not copy it verbatim:
211+
212+
- Prefer one short sentence per item and combine closely related changes.
213+
- Lead with headline capabilities and meaningful behavior changes.
214+
- Keep breaking, migration, security, and operational warnings concise but
215+
prominent; link to detailed documentation.
216+
- Omit routine dependencies, documentation-only changes, internal refactors,
217+
and CI/infrastructure work unless they materially affect users.
218+
- Omit expected details of a newly introduced feature and bugs introduced and
219+
fixed within the same release cycle.
220+
- Aim for substantially fewer words than the corresponding changelog section.
221+
222+
Draft the summary in a throwaway temp file. Do **not** create or commit a
223+
separate `release-notes-*.md`; the durable detailed record remains
224+
`CHANGELOG.md`.
204225

205226
```bash
206-
# 1. Extract the "## [X.Y.Z]" block from CHANGELOG.md (stops at the next "## [" heading)
207-
awk '/^## \[X\.Y\.Z\]/{f=1;next} /^## \[/{f=0} f' CHANGELOG.md > /tmp/notes-X.Y.Z.md
227+
# 1. Extract the changelog section as source material and seed a separate draft.
228+
# Rewrite the draft editorially before continuing; do not leave it as a copy.
229+
awk '/^## \[X\.Y\.Z\]/{f=1;next} /^## \[/{f=0} f' CHANGELOG.md > /tmp/changelog-X.Y.Z.md
230+
cp /tmp/changelog-X.Y.Z.md /tmp/release-summary-X.Y.Z.md
208231

209232
# 2. Fetch GitHub's auto-generated notes ONCE. `gh release edit` has NO
210233
# --generate-notes flag (only `gh release create` does), so we generate the
@@ -232,21 +255,22 @@ awk '/^## What.s Changed/{skip=1; next} /^## /{skip=0} /^\*\*Full Changelog\*\*/
232255
contributors=$(grep -oE 'by @[A-Za-z0-9-]+' /tmp/gen-notes-X.Y.Z.md \
233256
| sed 's/by //' | sort -u | grep -viE '@(dependabot|github-actions)' | paste -sd ' ' -)
234257

235-
# 5. Assemble: curated changelog + Acknowledgements credit + trimmed auto notes,
258+
# 5. Assemble: concise release summary + Acknowledgements + trimmed auto notes,
236259
# then set the release body
237260
{
238-
cat /tmp/notes-X.Y.Z.md
261+
cat /tmp/release-summary-X.Y.Z.md
239262
printf '\n---\n\n## Acknowledgements\n\nThanks to everyone who contributed to this release: %s.\n\n' "$contributors"
240263
cat /tmp/auto-notes-X.Y.Z.md
241264
} > /tmp/release-body-X.Y.Z.md
242265
gh release edit vX.Y.Z --notes-file /tmp/release-body-X.Y.Z.md
243266
```
244267

245268
- The temp files are transient (e.g. under `/tmp`); they are **not** part of any
246-
PR and the Package Release workflow never reads them. The single source of
247-
truth for curated content is the committed `CHANGELOG.md`.
269+
PR and the Package Release workflow never reads them. `CHANGELOG.md` remains
270+
the durable detailed record; the GitHub Release is its concise editorial
271+
companion.
248272
- `--notes-file` sets **only the GitHub Release body** — it does not touch
249-
`CHANGELOG.md`. The curated text comes from the changelog you already merged.
273+
`CHANGELOG.md`.
250274
- The `releases/generate-notes` API returns a "## What's Changed" PR dump, a
251275
"## New Contributors" section, and a "Full Changelog" link. We **drop**
252276
"What's Changed" from the body (it re-lists the same PRs the curated changelog

0 commit comments

Comments
 (0)