From e2940e185e37113ed2874cc2566ff644a6de7873 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sat, 17 Oct 2026 21:31:13 +0300 Subject: [PATCH] docs: plain-prose pass over agent and glossary docs --- AGENTS.md | 27 +++++++++---------- CONTEXT.md | 10 +++---- .../adr/0002-free-threading-is-compat-only.md | 4 +-- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3382753..d3a50cc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,18 +6,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co `faststream-outbox` is a FastStream broker whose transport is a Postgres table (transactional outbox pattern), Postgres-only at v0. [`CONTEXT.md`](CONTEXT.md) opens with what it does and owns -the vocabulary — read it before naming a concept in code, a test name, or an issue title. +the vocabulary. Read it before naming a concept in code, a test name, or an issue title. ## Commands -`just` (task runner) and `uv` (package manager). The [`justfile`](justfile) is the source of truth — -`just --list`, or read it. Every recipe carries its intent as a comment. The things it does not say: +`just` (task runner) and `uv` (package manager). The [`justfile`](justfile) is the source of truth: +run `just --list`, or read it. Every recipe carries its intent as a comment. The things it does not say: - `just test [args]` forwards args **unquoted**, so a spaced `-k` expression word-splits and fails. Run one keyword per invocation, or a substring matching all targets. - `tests/test_unit.py` + `tests/test_fake.py` need no Postgres and run directly under `uv run pytest`; `tests/test_integration.py` skips if `POSTGRES_DSN` is unreachable. The coverage gate is - on by default, so a partial run trips it — pass `--no-cov` while iterating. + on by default, so a partial run trips it; pass `--no-cov` while iterating. - Nothing validates Markdown links outside `docs/`. `just docs-build` runs `mkdocs --strict` over the site only; root Markdown, `.github/`, and `docs/agents/` are unchecked. @@ -28,18 +28,17 @@ will **not** tell you: - `subscriber/usecase.py` is load-bearing: **every terminal write filters on `acquired_token`**, so a stale writer finds `rowcount == 0` and is dropped. Any new fetch or terminal path must preserve - that — `tests/test_client_contract.py::test_delete_noop_on_token_mismatch` is the claim. + that; `tests/test_client_contract.py::test_delete_noop_on_token_mismatch` is the claim. - `client.py` and `testing.py` implement the same rules twice, in SQL and in Python, because one runs in the database and one in the process. They cannot share an implementation, so - `tests/test_client_contract.py` couples them by behaviour — a change to either adapter's insert, fetch, + `tests/test_client_contract.py` couples them by behaviour. A change to either adapter's insert, fetch, terminal, or DLQ semantics belongs in that suite. -- `schema.py` — the three partial indexes and the `_lease_ck` CHECK are load-bearing, not - decoration. -- `message.py` — the `DLQFailureReason` `Literal` is a **public contract**; operator queries key off +- `schema.py`: the three partial indexes and the `
_lease_ck` CHECK are load-bearing. +- `message.py`: the `DLQFailureReason` `Literal` is a **public contract**; operator queries key off those strings, so changing one is API-breaking. - `metrics/` (the recorder seam) and `prometheus/` + `opentelemetry/` (native middleware) are two - seams, deliberately. Each fires for events the other physically cannot observe — `fetched` has no - `StreamMessage`, `lease_lost` fires after `consume_scope` exits. **Don't collapse them.** + seams on purpose. Each fires for events the other cannot observe: `fetched` has no + `StreamMessage`, and `lease_lost` fires after `consume_scope` exits. **Don't collapse them.** `TestOutboxBroker` swaps in `FakeOutboxClient` and nothing else on the publish side: the real producer and publish middlewares run, and a thin wrapper dispatches what the fake stored once the producer @@ -57,11 +56,11 @@ what breaks it. ## Code Style -- **Never use local/inline imports** — tests included, no `if TYPE_CHECKING` exception. `ruff` +- **Never use local/inline imports**, tests included, with no `if TYPE_CHECKING` exception. `ruff` catches them; if `# noqa: PLC0415` looks like the fix, hoist the import instead. - Docstrings: public API documents the contract; internal helpers get a one-line contract, plus at - most 1–2 lines for a genuinely non-obvious constraint. Never narrate implementation or justify - code to a reviewer — cross-file rationale lives in an invariant test's docstring. + most one or two lines for a non-obvious constraint. Never narrate implementation or justify + code to a reviewer; cross-file rationale lives in an invariant test's docstring. - Lint suppressions are intentional and carry their reason at the site. The recurring cluster is everything downstream of `BrokerUsecase`'s invariance on its config type. diff --git a/CONTEXT.md b/CONTEXT.md index 4fdbb54..46f5eb1 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -11,7 +11,7 @@ docs must agree on it. General programming vocabulary does not belong here, howe project uses it. **Row**: -One outbox record — the unit the transport moves. There is no `state` column: a row exists until it +One outbox record: the unit the transport moves. There is no `state` column: a row exists until it is delivered or terminally fails. _Avoid_: job, task, event @@ -33,7 +33,7 @@ statement matches nothing. _Avoid_: lease id, owner id **Claim**: -Acquiring a lease on a row via the fetch CTE. Increments `deliveries_count` — including on an +Acquiring a lease on a row via the fetch CTE. Increments `deliveries_count`, including on an expired-lease reclaim. _Avoid_: fetch (that is the loop and the client method), dequeue @@ -50,7 +50,7 @@ The bounded wait on stop for in-flight rows to reach a terminal write. _Avoid_: graceful shutdown (that names the whole phase, not this wait) **Outcome**: -The single disjoint result `dispatch_one` matches on — `Ack`, `Retry(delay_seconds)`, or +The single disjoint result `dispatch_one` matches on: `Ack`, `Retry(delay_seconds)`, or `Terminal(reason)`. _Avoid_: status, result, state @@ -64,7 +64,7 @@ _Avoid_: schedule, delayed message **`timer_id`**: The dedup key for at most one *live* row per `(queue, timer_id)`. It is not a global once-ever -idempotency key — a delivered row's id is reusable. +idempotency key; a delivered row's id is reusable. _Avoid_: idempotency key, message key **Relay**: @@ -73,7 +73,7 @@ _Avoid_: bridge, forwarder **DLQ**: The opt-in sibling audit table terminal failures are archived to. Off by default. -_Avoid_: dead-letter queue as a transport — nothing consumes it +_Avoid_: dead-letter queue as a transport (nothing consumes it) **Recorder seam**: The `MetricsRecorder` callable the library invokes for events outside the FastStream bus. diff --git a/docs/adr/0002-free-threading-is-compat-only.md b/docs/adr/0002-free-threading-is-compat-only.md index 99e3dbc..3b7b18e 100644 --- a/docs/adr/0002-free-threading-is-compat-only.md +++ b/docs/adr/0002-free-threading-is-compat-only.md @@ -1,10 +1,10 @@ # Free-threading is a compatibility guarantee, not a parallelism redesign Free-threaded support means proving that `faststream-outbox` runs correctly on free-threaded CPython -3.14t with the GIL disabled; it does not mean rearchitecting the subscriber to use multiple cores. +3.14t with the GIL disabled. The subscriber is not rearchitected to use multiple cores. The package is pure-Python asyncio, one event loop with N worker tasks rather than OS threads, so free-threading changes none of its runtime semantics and the guarantee costs a CI job, a -`Free Threading :: 2 - Beta` classifier, and a docs note rather than a source change. Exploiting the +`Free Threading :: 2 - Beta` classifier, and a docs note, with no source change. Exploiting the parallelism was rejected because the two-loop subscriber, the lease-token invariant, and drain-on-stop all assume one loop, throughput is dominated by Postgres I/O, and scaling today means running more subscriber processes. The guarantee was bounded by SQLAlchemy until 2.0.54, whose