diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 08ebfa0..043e049 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,6 +23,10 @@ jobs:
run: ruff check src/ tests/
- name: Ruff format check
run: ruff format --check src/ tests/
+ - name: Marketing version sync
+ # Fails if README/landing/OG-card versions drift from pyproject —
+ # the served social card once sat 3 minors behind silently.
+ run: python scripts/sync_marketing_version.py --check
test:
runs-on: ubuntu-latest
diff --git a/README.md b/README.md
index 6af2c50..b87cb61 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ An open-source project from **[NichevLabs](https://nichevlabs.com)**.
**Multi-agent orchestration in plain Python.** Build agent graphs, compose pipelines with `|`, deploy with one command. No DSL, no compile step, no paid debugger. Works with OpenAI, Anthropic, Gemini, and Ollama.
-> 🎉 **selectools is 1.0 — stable.** The public API is frozen; `@stable` symbols carry a 2-minor compatibility promise. Python 3.10+. Latest: [v1.2](#whats-new-in-v12) — four-stage guardrails, native structured output, and observability events.
+> 🎉 **selectools is 1.0 — stable.** The public API is frozen; `@stable` symbols carry a 2-minor compatibility promise. Python 3.10+. Latest: [v1.3](#whats-new-in-v13) — tested public streaming contracts for structured-output chat agents.
### 3 Ways to Build
@@ -32,6 +32,17 @@ result = AgentGraph.chain(planner, writer, reviewer).run("Write a blog post")
# selectools serve agent.yaml
```
+## What's New in v1.3
+
+### v1.3.0 — Streaming Contracts
+
+Makes `final_turn_only` structured output safe to rely on in streaming chat agents. Behaviors that integrators previously had to learn from the source are now **documented, tested public contracts** — enforced on every consumption surface: `astream()` chunks, the `stream_handler` callback, and the `selectools.serve` SSE stream.
+
+- **The synthesis turn never streams** — its JSON arrives only on the terminal `AgentResult` (`.content` / `.parsed`), never as chunks or through `stream_handler`. `StreamChunk(event="structured_synthesis_start")` fires once when the synthesis turn begins so clients can show a pending state.
+- **`single_pass` no longer streams the structured answer** (*Changed*, `@beta` surface) — content is suppressed since it's the JSON envelope by construction; tool-call chunks still stream for activity display. Read the answer from `AgentResult.parsed`.
+- **New `StreamChunk(event="structured_reuse")`** — when the converged loop answer validates and is reused, the just-streamed chunks WERE the answer; this signal lets a chat surface convert or retract the bubble. The serve SSE endpoint forwards event chunks as `{"type": "event", ...}`.
+- **`should_finalize` message contract** — the predicate's `messages` view is documented: TOOL messages always carry `tool_name`, and `tool_result` holds the result text for successful runs while staying `None` for errors/denials (the supported failure discriminator).
+
## What's New in v1.2
### v1.2.0 — Tool-Boundary Guardrails Complete & Structured Synthesis Follow-ups
diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md
index a30af61..01e8f1f 100644
--- a/docs/CLAUDE.md
+++ b/docs/CLAUDE.md
@@ -49,4 +49,5 @@ Every feature MUST update all of these:
- CHANGELOG.md lives at repo root — must be copied into `docs/` before build
- Examples gallery (`landing/examples/index.html`) must be regenerated when examples are added or edited: `python scripts/build_examples_gallery.py > landing/examples/index.html`
- Hardcoded counts (model count, test count, example count) appear across many files — audit all
+- **Marketing version strings** (README banner, landing status bar/footer, OG-card badges) are synced from `pyproject.toml` by `scripts/sync_marketing_version.py`. CI runs it with `--check` and fails on drift; the release flow (`release.py`) runs the write mode. After it bumps the OG **SVG**, re-render the PNG: `uv run --with playwright python scripts/render_og_image.py`.
- `docs/ARCHITECTURE.md` must be updated when adding new components
diff --git a/docs/assets/og-card.html b/docs/assets/og-card.html
index 1c8a015..5dd9fdf 100644
--- a/docs/assets/og-card.html
+++ b/docs/assets/og-card.html
@@ -152,7 +152,7 @@