Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions planning/releases/0.12.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# faststream-outbox 0.12.0 — architecture-deepening pass (internal refactors, no production behavior change)

**Minor release.** Every change to the installed package's **production** runtime is an
**internal refactor** — the producer, subscriber, lease/terminal-write, timer, DLQ, and
validation behavior are identical to 0.11.0, verified by the unchanged suite at 100%
coverage across Python 3.11–3.14. The batch came out of an architecture review that
deepened shallow modules, named duplicated rules, and removed a dead field. The **one**
behavior change is test-broker-only (a tightened `session` argument, below), which is why
this is a minor rather than a patch. Drop-in for production code.

## Breaking changes

- **`TestOutboxBroker`: `cancel_timer` / `fetch_unprocessed` now require `session`.** These
two methods moved behind the `AbstractOutboxClient` seam (see refactors below). Under the
**test broker** they previously accepted an *optional* `session` (an internal patch
defaulted it to `None`); they now require `session=...`, matching the real broker's
signature and the documented public contract (the argument is still ignored under the
test broker). **Production is unaffected** — `broker.cancel_timer` / `broker.fetch_unprocessed`
always required `session`. Only test code that called these on a `TestOutboxBroker`
*without* `session` needs a one-token edit. ([#141](https://github.com/modern-python/faststream-outbox/pull/141))

## Internal refactors (no behavior change)

Each deepens the codebase — more behavior behind smaller, single-owner seams — without
changing observable production behavior.

- **Schema-drift validator split out of `client.py`.** The ~285 LOC of Alembic-diff +
`pg_catalog` probes moved to a new `schema_validation.py`; the transport client roughly
halved (796 → 471 LOC) and no longer imports the optional `alembic` dependency.
`broker.validate_schema()` / `client.validate_schema()` are unchanged.
([#139](https://github.com/modern-python/faststream-outbox/pull/139))
- **Dead `to_delete` intent flag removed.** `OutboxInnerMessage.to_delete` was set on every
ack/terminal path but read by zero production code (dispatch branched on other fields).
Removed; tests now assert on the fields the dispatcher actually reads.
([#140](https://github.com/modern-python/faststream-outbox/pull/140))
- **`cancel_timer` / `fetch_unprocessed` routed through the client seam.** Both built inline
SQL on the broker's table handle, bypassing the `OutboxClient` that already owns the
table. They are now methods on `AbstractOutboxClient` (real + fake); the broker delegates,
and the test-broker patch machinery for them is deleted. (Carries the test-broker `session`
change above.) ([#141](https://github.com/modern-python/faststream-outbox/pull/141))
- **Envelope-managed header keys named once.** `content-type` and `correlation_id` were bare
string literals across `envelope.py` (write), `parser.py` (read), and the relay
header-strip. Defined once in `message.py` as `CONTENT_TYPE_HEADER` /
`CORRELATION_ID_HEADER` / `ENVELOPE_MANAGED_HEADERS`.
([#143](https://github.com/modern-python/faststream-outbox/pull/143))
- **Lease-token guard named; contract coverage filled.** The `NULL = NULL` token guard the
fake repeated inline is now one `_lease_token_matches` helper. The cross-adapter contract
suite gained the missing `mark_pending` unleased-row scenario (the delete path already had
it) and an empty-queues symmetry check.
([#144](https://github.com/modern-python/faststream-outbox/pull/144))
- **Terminal intent deepened to a canonical `Outcome` sum type.** `OutboxInnerMessage` now
records one `Outcome` — `Ack` | `Retry(delay_seconds)` | `Terminal(reason)` — instead of
three loosely-coupled fields. Success is an explicit `Ack()`; "reason and delay both set"
is now unrepresentable. `terminal_failure_reason` / `pending_delay_seconds` / `state_set`
remain as **read-only property views**, so readers and the `DLQFailureReason` public
contract are unchanged. ([#145](https://github.com/modern-python/faststream-outbox/pull/145))

## Design records (no code change)

- **The `conn: AsyncConnection | None` union on the client seam is deliberate.** The review
flagged it as a "type lie"; on inspection it is a documented widening (the fake genuinely
has no connection, the subscriber genuinely produces `None` on the fake path). Recorded as
an accepted decision with a revisit trigger.
([#142](https://github.com/modern-python/faststream-outbox/pull/142))
- **The two observability seams overlap on the consume/publish series.** `architecture/metrics.md`
now documents that the recorder adapters and the native middleware emit the same-named
consume/publish metrics — one Prometheus registry raises `Duplicated timeseries`, live OTel
meters on both double-count — and why the library can't guard it at construction (separate
registries / middleware-as-source-of-truth stays an operator concern).
([#146](https://github.com/modern-python/faststream-outbox/pull/146))

## Migration

- **Production code:** none. No public-API change, no schema change, no config change.
- **Test suites using `TestOutboxBroker`:** if you call `.cancel_timer(...)` or
`.fetch_unprocessed(...)` on the test broker without a `session`, add `session=...` (any
session — it is ignored under the test broker). Everywhere else, no change.

## Touched surface

- **Package code (internal only):** `faststream_outbox/schema_validation.py` (new),
`client.py`, `broker.py`, `message.py`, `envelope.py`, `parser/parser.py`,
`subscriber/usecase.py`, `testing.py`.
- **Tests:** `tests/test_client_contract.py` (+2 scenarios), `tests/test_unit.py`,
`tests/test_fake.py`.
- **Living docs / decisions:** `CLAUDE.md`, `architecture/dlq.md`, `architecture/test-broker.md`,
`architecture/metrics.md`, `docs/usage/testing.md`, `planning/decisions/2026-07-17-conn-union-is-deliberate.md`.

## See also

- [#139](https://github.com/modern-python/faststream-outbox/pull/139) — split schema-drift validator.
- [#140](https://github.com/modern-python/faststream-outbox/pull/140) — remove dead `to_delete` flag.
- [#141](https://github.com/modern-python/faststream-outbox/pull/141) — table ops behind the client seam.
- [#142](https://github.com/modern-python/faststream-outbox/pull/142) — `conn` union deliberate (decision).
- [#143](https://github.com/modern-python/faststream-outbox/pull/143) — name envelope header keys.
- [#144](https://github.com/modern-python/faststream-outbox/pull/144) — name lease-token guard + contract coverage.
- [#145](https://github.com/modern-python/faststream-outbox/pull/145) — canonical `Outcome` sum type.
- [#146](https://github.com/modern-python/faststream-outbox/pull/146) — two-seam overlap hazard (docs).