Skip to content

chore: migrate off the planning/ convention - #12

Merged
lesnik512 merged 1 commit into
mainfrom
chore/migrate-off-planning
Sep 6, 2026
Merged

chore: migrate off the planning/ convention#12
lesnik512 merged 1 commit into
mainfrom
chore/migrate-off-planning

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Migrates modern-di-taskiq off the planning/ convention and onto PR-body-as-spec, with CONTEXT.md owning the vocabulary and docs/adr/ holding rejected alternatives. Part of modern-python/.github#67.

Replaces the two-axis planning/ + architecture/ convention with PR-body-as-spec,
CONTEXT.md for vocabulary, and docs/adr/ for rejected alternatives, per
modern-python/.github#67.

  • planning/ deleted in full. changes/ held three design docs whose reasoning is
    in git history and, where it was a rejected alternative, in the ADRs below.
    releases/ held four notes; all four were diffed against the published GitHub
    Release bodies with gh release view and are identical, so the Releases are
    the record. There was no deferred.md and no unscheduled repo-local work in the
    change files, so nothing needed drafting as an issue. decisions/ was empty.
  • architecture/ deleted. Both pages were prose about mechanism that main.py
    states more precisely. The four enforceable claims became INVARIANT tests: the
    four-symbol public surface (new tests/test_public_surface.py), reopening the
    root container on a second worker cycle, one per-task child shared by a task's
    parameters and isolated across tasks, and that child closing on the error path.
  • Every invariant was verified by breaking it. Growing all and adding an
    unprefixed public binding each fail the public-surface test, while renaming
    main.py to _main.py -- a narrowing -- does not. Dropping the WORKER_STARTUP
    handler fails the restart test. Moving the child's close after the yield
    without a try fails the error-path test. Hoisting the child to broker lifetime
    fails the per-task test.
  • The per-task test did not fail under the use_cache=False violation its own
    docstring named: its two parameters resolved the same provider, which taskiq
    collapses into one dependency node resolved once however many children exist.
    They now name two different providers (tests/dependencies.py gains
    request_singleton_holder, a REQUEST-scoped factory taking the cached
    request_singleton), so the shared-child assertion is load-bearing and the test
    goes red under both violations.
  • Two decisions become docs/adr/0001 (the per-task scope rides a generator
    TaskiqDepends, not a middleware) and 0002 (only the WORKER_* lifecycle pair is
    wired), rescued from the change files' non-goals, each with a revisit trigger.
  • CONTEXT.md defines only the two terms local to this integration; Container,
    Provider, Group, Scope, Resolution and Override stay modern-di's, and broker,
    worker, task, message, middleware and result backend stay taskiq's. Both
    Avoid entries were audited against real usage and both had live stragglers:
    "APP-scope container" in two rows of README.md's symbol table and "request
    child" in two test names and two comments, all moved to "root container" and
    "per-task child". None is a user-visible string. The internal parameter name
    request_container is left alone -- it is the shared spelling across the sibling
    integrations, not this repo's own vocabulary.
  • AGENTS.md gains Workflow and Where-a-fact-goes and loses the planning lanes;
    its Architecture section now points at the two ADRs, which the link gate checks.
  • docs/agents/domain.md was deliberately not written. This repo has no
    docs/agents/ and no Agent skills section in AGENTS.md to link one from, so a
    lone domain.md would be a half-rollout of a separate convention. Post-migration
    modern-di-grpc and modern-di-celery are in the same position.
  • justfile and lint-ci drop index/check-planning/check-links; the offline lychee
    gate replaces links.py (Add the offline link gate to the 18 green repos, and unpublish the ADRs in the two that have them .github#66) as a links job in
    _checks.yml. It reports 0 errors, and was itself checked by breaking an ADR
    link and confirming it errors.

Beyond the recipe, and the reason this is chore: and not docs: release.yml read
planning/releases/${GITHUB_REF_NAME}.md both as a mandatory gate for stable tags
and as the Release body source, so deleting planning/ would have broken every
future stable release. It is now modern-di's post-migration workflow verbatim
(which dropped the same gate in 1ea74ee, modern-python/modern-di#449): GitHub's
generated notes, with the prerelease flag the only resolved metadata. This
retires the mandatory-curated-notes policy for this repo; that is the precedent's
call, not a new one.

Verification

  • just lint-ci — clean (eof-fixer, ruff format, ruff check, ty).
  • Test suite green at the repo's 100% coverage gate.
  • Offline link gate (lychee --offline --no-progress '**/*.md') — 0 errors.
  • No planning/, architecture/, check-planning, check-links or convention-version reference remains.

Note

release.yml previously read planning/releases/<tag>.md, both as a hard gate for stable tags and as the Release body source; deleting planning/ without changing it would have broken the next stable release. It now uses GitHub's generated notes, matching modern-di post-modern-python/modern-di#449. This retires the mandatory-curated-notes policy.

Replaces the two-axis planning/ + architecture/ convention with PR-body-as-spec,
CONTEXT.md for vocabulary, and docs/adr/ for rejected alternatives, per
modern-python/.github#67.

- planning/ deleted in full. changes/ held three design docs whose reasoning is
  in git history and, where it was a rejected alternative, in the ADRs below.
  releases/ held four notes; all four were diffed against the published GitHub
  Release bodies with `gh release view` and are identical, so the Releases are
  the record. There was no deferred.md and no unscheduled repo-local work in the
  change files, so nothing needed drafting as an issue. decisions/ was empty.
- architecture/ deleted. Both pages were prose about mechanism that main.py
  states more precisely. The four enforceable claims became INVARIANT tests: the
  four-symbol public surface (new tests/test_public_surface.py), reopening the
  root container on a second worker cycle, one per-task child shared by a task's
  parameters and isolated across tasks, and that child closing on the error path.
- Every invariant was verified by breaking it. Growing __all__ and adding an
  unprefixed public binding each fail the public-surface test, while renaming
  main.py to _main.py -- a narrowing -- does not. Dropping the WORKER_STARTUP
  handler fails the restart test. Moving the child's close after the yield
  without a try fails the error-path test. Hoisting the child to broker lifetime
  fails the per-task test.
- The per-task test did not fail under the use_cache=False violation its own
  docstring named: its two parameters resolved the same provider, which taskiq
  collapses into one dependency node resolved once however many children exist.
  They now name two different providers (tests/dependencies.py gains
  request_singleton_holder, a REQUEST-scoped factory taking the cached
  request_singleton), so the shared-child assertion is load-bearing and the test
  goes red under both violations.
- Two decisions become docs/adr/0001 (the per-task scope rides a generator
  TaskiqDepends, not a middleware) and 0002 (only the WORKER_* lifecycle pair is
  wired), rescued from the change files' non-goals, each with a revisit trigger.
- CONTEXT.md defines only the two terms local to this integration; Container,
  Provider, Group, Scope, Resolution and Override stay modern-di's, and broker,
  worker, task, message, middleware and result backend stay taskiq's. Both
  _Avoid_ entries were audited against real usage and both had live stragglers:
  "APP-scope container" in two rows of README.md's symbol table and "request
  child" in two test names and two comments, all moved to "root container" and
  "per-task child". None is a user-visible string. The internal parameter name
  request_container is left alone -- it is the shared spelling across the sibling
  integrations, not this repo's own vocabulary.
- AGENTS.md gains Workflow and Where-a-fact-goes and loses the planning lanes;
  its Architecture section now points at the two ADRs, which the link gate checks.
- docs/agents/domain.md was deliberately not written. This repo has no
  docs/agents/ and no Agent skills section in AGENTS.md to link one from, so a
  lone domain.md would be a half-rollout of a separate convention. Post-migration
  modern-di-grpc and modern-di-celery are in the same position.
- justfile and lint-ci drop index/check-planning/check-links; the offline lychee
  gate replaces links.py (modern-python/.github#66) as a links job in
  _checks.yml. It reports 0 errors, and was itself checked by breaking an ADR
  link and confirming it errors.

Beyond the recipe, and the reason this is chore: and not docs: release.yml read
planning/releases/${GITHUB_REF_NAME}.md both as a mandatory gate for stable tags
and as the Release body source, so deleting planning/ would have broken every
future stable release. It is now modern-di's post-migration workflow verbatim
(which dropped the same gate in 1ea74ee, modern-python/modern-di#449): GitHub's
generated notes, with the prerelease flag the only resolved metadata. This
retires the mandatory-curated-notes policy for this repo; that is the precedent's
call, not a new one.
@lesnik512
lesnik512 merged commit b6f3a7f into main Sep 6, 2026
7 checks passed
@lesnik512
lesnik512 deleted the chore/migrate-off-planning branch September 6, 2026 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant