chore: migrate off the planning/ convention - #65
Merged
Conversation
Replaces `planning/` and `architecture/` with `CONTEXT.md`, `docs/adr/`, and invariant tests, matching the layout rolled out in modern-python/.github#50 and across modern-di. Tracking issue: modern-python/.github#67. Nothing was lost with the deleted directories: - `planning/releases/*.md` are the published GitHub Release bodies. Checked all seven against `gh release view`: 0.6.2-0.6.5 are byte-identical but for a trailing newline, 0.6.0 differs only in a link to a pre-rename spec path. The Releases are the record. 0.6.1 is the one exception and is drafted as an issue rather than fixed here: its published body was hand-condensed at publish time and ends with a "Full notes" link to `blob/main/planning/releases/0.6.1.md`, which 404s once this lands. The content survives at the `0.6.1` tag; the fix is a one-off Release edit, not a repo change. - `planning/changes/*` are design and plan documents for shipped work. Git history is the record; the two rejected alternatives in them that outlive their PR were rescued (below). - `planning/deferred.md` held one open item, now drafted as an issue. - `planning/_templates/`, `planning/index.py`, `planning/.convention-version`, `planning/audits/`, `planning/retros/` are convention machinery with nothing left to serve. - `architecture/README.md` describes the promotion rule the convention imposed. The five capability pages are prose about mechanism that the modules state more precisely — this package's source is unusually densely commented, and every claim about dispatch, routing, prefix extraction, watermarks and the direct-ack guards was already pinned by a named test. The two claims that were not are now tests. Decisions. The three records become `docs/adr/0001-0003`, frontmatter stripped and revisit triggers kept: batch-subscribers-unsupported (0001), health-delegation-chain-not-deepened (0002), control-signals-not-honoured (0003). Two more are rescued from change files: - `docs/adr/0004` records that refusing a non-`MANUAL` subscriber was built and rejected — it breaks the broker-level registration contract by turning a legitimate mixed-subscriber app into a hard error on its `ACK` subscribers. From the guard-direct-ack change file. 0001 now scopes its refusal against it. - `docs/adr/0005` records that a `run()`-owning async `CommitLoop` was rejected twice: the wait-tasks carry no invariant, and moving them would force every scheduler test back onto the event loop. From the pending-commits and commit-scheduler change files. Invariants. Two architecture claims were enforceable and unpinned: - `tests/test_rebalance.py::test_the_rebalance_flush_default_stays_under_aiokafkas_max_poll_interval` reads aiokafka's own `max_poll_interval_ms` default rather than hardcoding 300 s, so it fires on a dependency bump as well as on our own default. Verified red at the boundary (flush default raised to 300.0) and red on a simulated upstream drop to 5 s; still green when the flush default is tightened to 5.0 and when the unrelated shutdown timeout is raised to 45.0. - `tests/test_commit_scheduler.py::test_the_commit_scheduler_reads_no_clock_and_touches_no_asyncio` pins the seam ADR-0005 defends. Verified red three ways — `import time` plus a `time.monotonic()` call, a `from asyncio import Event` collaborator, and an `async def` on an existing method — and still green when a new synchronous method and a `typing` import are added. Both modules restored byte-identically and the suite re-run: 182 passed, 100% coverage. CONTEXT.md is authored from scratch; there was no `architecture/glossary.md`. FastStream and aiokafka own most of the vocabulary and it says so rather than redefining `broker`, `subscriber`, `consumer group`, `partition` or `offset`. Six terms are local. The `_Avoid_` audit cut three of the four candidate entries because the rejected synonym appears nowhere: no "skip" is used for a pass-through, no "ignore" for a route, and "ready" never names the whole pending list. A fourth was reversed — "handler" for the user's subscriber function is not a straggler but the dominant usage, including inside the user-visible `_DIRECT_ACK_REASONS` strings, so the entry now disambiguates instead of legislating. The one surviving `_Avoid_`, "in-flight handler" for a user task, is proven and its six stragglers are fixed: four README lines, a test docstring and a test assertion message. None is a user-visible program string. One source edit was forced. `middleware.py`'s `nack` guard message carries a GitHub URL to `planning/decisions/2026-07-28-control-signals-not-honoured.md`, deliberately absolute so a PyPI user without a checkout can open it. That is a user-visible string and it now points at `docs/adr/0003-...`. README's two links to the same record were repointed with it. AGENTS.md gains Workflow and Where-a-fact-goes, loses the planning lanes and the architecture promotion rule, and sheds the tooling notes the justfile already states and the invariant list the tests already enforce. justfile and lint-ci drop `index` / `check-planning`; the offline lychee gate lands in `_checks.yml` (modern-python/.github#66). context7.json drops its `planning` excludeFolders entry. `docs/agents/domain.md` is deliberately not written: `origin/main` has no `docs/` and AGENTS.md had no "Agent skills" section, so authoring it alone would be a half-rollout of a separate convention with nothing to link it from. The link gate went from 4 errors to 0. Three lived only in `planning/` and are fixed by deleting it; the fourth was a dangling `docs/adr/0004` link left by an interrupted earlier attempt, fixed by writing the record it wanted. Beyond the recipe, and the reason this is chore: and not docs: release.yml read `planning/releases/<tag>.md` as both a mandatory gate for stable tags and the Release body source, so deleting `planning/` would have failed every future stable release. It now matches modern-di/.github/workflows/release.yml verbatim (generated notes, prerelease flag only), which dropped the same gate in 1ea74ee, modern-python/modern-di#449. This retires the mandatory-curated-notes policy for this repo; a release wanting prose is edited after the fact. Deferred work is drafted, unopened, in DRAFT-ISSUES-faststream-concurrent-aiokafka.md one directory up: the commit_all flush-urgency loss on a transient-error re-queue, the partition-scoped rebalance flush, and the 0.6.1 Release link.
21 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates
faststream-concurrent-aiokafkaoff theplanning/convention and onto PR-body-as-spec, withCONTEXT.mdowning the vocabulary anddocs/adr/holding rejected alternatives. Part of modern-python/.github#67.Replaces
planning/andarchitecture/withCONTEXT.md,docs/adr/, andinvariant tests, matching the layout rolled out in modern-python/.github#50 and
across modern-di. Tracking issue: modern-python/.github#67.
Nothing was lost with the deleted directories:
planning/releases/*.mdare the published GitHub Release bodies. Checked allseven against
gh release view: 0.6.2-0.6.5 are byte-identical but for atrailing newline, 0.6.0 differs only in a link to a pre-rename spec path. The
Releases are the record. 0.6.1 is the one exception and is drafted as an issue
rather than fixed here: its published body was hand-condensed at publish time
and ends with a "Full notes" link to
blob/main/planning/releases/0.6.1.md,which 404s once this lands. The content survives at the
0.6.1tag; the fix isa one-off Release edit, not a repo change.
planning/changes/*are design and plan documents for shipped work. Githistory is the record; the two rejected alternatives in them that outlive their
PR were rescued (below).
planning/deferred.mdheld one open item, now drafted as an issue.planning/_templates/,planning/index.py,planning/.convention-version,planning/audits/,planning/retros/are convention machinery with nothingleft to serve.
architecture/README.mddescribes the promotion rule the convention imposed.The five capability pages are prose about mechanism that the modules state more
precisely — this package's source is unusually densely commented, and every
claim about dispatch, routing, prefix extraction, watermarks and the direct-ack
guards was already pinned by a named test. The two claims that were not are now
tests.
Decisions. The three records become
docs/adr/0001-0003, frontmatter strippedand revisit triggers kept: batch-subscribers-unsupported (0001),
health-delegation-chain-not-deepened (0002), control-signals-not-honoured (0003).
Two more are rescued from change files:
docs/adr/0004records that refusing a non-MANUALsubscriber was built andrejected — it breaks the broker-level registration contract by turning a
legitimate mixed-subscriber app into a hard error on its
ACKsubscribers.From the guard-direct-ack change file. 0001 now scopes its refusal against it.
docs/adr/0005records that arun()-owning asyncCommitLoopwas rejectedtwice: the wait-tasks carry no invariant, and moving them would force every
scheduler test back onto the event loop. From the pending-commits and
commit-scheduler change files.
Invariants. Two architecture claims were enforceable and unpinned:
tests/test_rebalance.py::test_the_rebalance_flush_default_stays_under_aiokafkas_max_poll_intervalreads aiokafka's own
max_poll_interval_msdefault rather than hardcoding300 s, so it fires on a dependency bump as well as on our own default. Verified
red at the boundary (flush default raised to 300.0) and red on a simulated
upstream drop to 5 s; still green when the flush default is tightened to 5.0 and
when the unrelated shutdown timeout is raised to 45.0.
tests/test_commit_scheduler.py::test_the_commit_scheduler_reads_no_clock_and_touches_no_asynciopins the seam ADR-0005 defends. Verified red three ways —
import timeplus atime.monotonic()call, afrom asyncio import Eventcollaborator, and anasync defon an existing method — and still green when a new synchronousmethod and a
typingimport are added. Both modules restored byte-identicallyand the suite re-run: 182 passed, 100% coverage.
CONTEXT.md is authored from scratch; there was no
architecture/glossary.md.FastStream and aiokafka own most of the vocabulary and it says so rather than
redefining
broker,subscriber,consumer group,partitionoroffset. Sixterms are local. The
_Avoid_audit cut three of the four candidate entriesbecause the rejected synonym appears nowhere: no "skip" is used for a
pass-through, no "ignore" for a route, and "ready" never names the whole pending
list. A fourth was reversed — "handler" for the user's subscriber function is not
a straggler but the dominant usage, including inside the user-visible
_DIRECT_ACK_REASONSstrings, so the entry now disambiguates instead oflegislating. The one surviving
_Avoid_, "in-flight handler" for a user task, isproven and its six stragglers are fixed: four README lines, a test docstring and a
test assertion message. None is a user-visible program string.
One source edit was forced.
middleware.py'snackguard message carries aGitHub URL to
planning/decisions/2026-07-28-control-signals-not-honoured.md,deliberately absolute so a PyPI user without a checkout can open it. That is a
user-visible string and it now points at
docs/adr/0003-.... README's two linksto the same record were repointed with it.
AGENTS.md gains Workflow and Where-a-fact-goes, loses the planning lanes and the
architecture promotion rule, and sheds the tooling notes the justfile already
states and the invariant list the tests already enforce. justfile and lint-ci drop
index/check-planning; the offline lychee gate lands in_checks.yml(modern-python/.github#66). context7.json drops its
planningexcludeFoldersentry.
docs/agents/domain.mdis deliberately not written:origin/mainhas nodocs/and AGENTS.md had no "Agent skills" section, so authoring it alone wouldbe a half-rollout of a separate convention with nothing to link it from.
The link gate went from 4 errors to 0. Three lived only in
planning/and arefixed by deleting it; the fourth was a dangling
docs/adr/0004link left by aninterrupted earlier attempt, fixed by writing the record it wanted.
Beyond the recipe, and the reason this is chore: and not docs: release.yml read
planning/releases/<tag>.mdas both a mandatory gate for stable tags and theRelease body source, so deleting
planning/would have failed every future stablerelease. It now matches modern-di/.github/workflows/release.yml verbatim
(generated notes, prerelease flag only), which dropped the same gate in 1ea74ee,
modern-python/modern-di#449. This retires the mandatory-curated-notes policy for
this repo; a release wanting prose is edited after the fact.
Deferred work is drafted, unopened, in
DRAFT-ISSUES-faststream-concurrent-aiokafka.md one directory up: the commit_all
flush-urgency loss on a transient-error re-queue, the partition-scoped rebalance
flush, and the 0.6.1 Release link.
Verification
just lint-ci— clean (eof-fixer, ruff format, ruff check, ty).lychee --offline --no-progress '**/*.md') — 0 errors.planning/,architecture/,check-planning,check-linksorconvention-versionreference remains.Note
release.ymlpreviously readplanning/releases/<tag>.md, both as a hard gate for stable tags and as the Release body source; deletingplanning/without changing it would have broken the next stable release. It now uses GitHub's generated notes, matchingmodern-dipost-modern-python/modern-di#449. This retires the mandatory-curated-notes policy.