Skip to content

chore: migrate off the planning/ convention - #14

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

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

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Migrates modern-di-celery 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/ is what git history already records; the six
    releases/*.md files were verified byte-identical to the published GitHub Release
    bodies (checked 3.0.2 and 2.1.0 with gh release view; the only difference is a
    trailing newline), so the Releases are the record. decisions/ held nothing but a
    .gitkeep, and there is no deferred.md — no work was left unscheduled anywhere in
    the change files, so no draft issues were needed.
  • architecture/ carried one capability page. Its decisions were rescued into four
    ADRs, each keeping a revisit trigger: 0001 no connection provider for a task
    invocation (from the celery-di-integration design), 0002 no functools.wraps on
    the inject wrapper, 0003 rejecting *args/**kwargs alongside FromDI (with the two
    alternatives that were considered and refused), and 0004 connecting both worker
    signal pairs (from the open-root-all-pools change and the 3.0.1 release notes).
    The rest of the page was prose about mechanism that main.py already states.
  • Two claims became INVARIANT tests, both verified by breaking them. The public
    surface is exactly the five documented symbols: it fails on an unprefixed public
    binding in init and on a helper promoted into all, and does not fail when
    internals are reshuffled behind private names or module imports. A FromDI
    parameter is absent from the signature Celery binds against: deleting the
    signature assignment, or swapping the hand-copied dunders for
    functools.update_wrapper, both turn it red, while narrowing the visible signature
    (dropping its return annotation) or narrowing what inject accepts leaves it green.
    That first break is the reason the test exists — the whole suite passed with the
    signature rewrite deleted, because the wrapper re-binds arguments itself and only
    the caller-side arity check moves. The other enforceable claims already had
    tests: dropping weak=False fails three lifespan tests, and dropping DITask's
    header reset fails test_ditask_resolves_without_inject.
  • CONTEXT.md defines the three terms local to this package; Container, Provider,
    Group, Scope, Resolution and Override stay modern-di's. The glossary audit forced
    two source edits: README.md called the root container the "APP-scope container"
    twice, and main.py's comment called the visible signature the "stripped
    signature" — both brought to the spelling the rest of the repo already uses.
    Neither is a user-visible runtime string. A third avoid-entry ("request
    container") was cut: it appeared nowhere in the repo, so there was no synonym to
    reject.
  • AGENTS.md gains Workflow and Where-a-fact-goes and loses the planning lanes.
    docs/agents/domain.md was deliberately not authored: this repo has no docs/
    directory before this change and no Agent skills section to link one from, so
    writing it alone would be a half-rollout of a separate convention.
  • justfile and lint-ci drop index/check-planning/check-links; the offline lychee
    gate in _checks.yml 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) and reports
    0 errors over every .md.

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 failed every
future stable release. It is now modern-di/.github/workflows/release.yml verbatim
(only the PyPI project name differs): GitHub's generated notes, prerelease flag
only. This retires the mandatory-curated-notes policy, following modern-di's
1ea74ee (modern-python/modern-di#449); a release wanting prose is edited after the
fact with gh release edit <tag> --notes-file.

Verified: just lint-ci clean, just test-ci 16 passed at 100% coverage, lychee
--offline 0 errors, and no remaining reference to planning/, architecture/,
check-planning, check-links or convention-version anywhere in the repo.

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/ is what git history already records; the six
  releases/*.md files were verified byte-identical to the published GitHub Release
  bodies (checked 3.0.2 and 2.1.0 with `gh release view`; the only difference is a
  trailing newline), so the Releases are the record. decisions/ held nothing but a
  .gitkeep, and there is no deferred.md — no work was left unscheduled anywhere in
  the change files, so no draft issues were needed.
- architecture/ carried one capability page. Its decisions were rescued into four
  ADRs, each keeping a revisit trigger: 0001 no connection provider for a task
  invocation (from the celery-di-integration design), 0002 no functools.wraps on
  the inject wrapper, 0003 rejecting *args/**kwargs alongside FromDI (with the two
  alternatives that were considered and refused), and 0004 connecting both worker
  signal pairs (from the open-root-all-pools change and the 3.0.1 release notes).
  The rest of the page was prose about mechanism that main.py already states.
- Two claims became INVARIANT tests, both verified by breaking them. The public
  surface is exactly the five documented symbols: it fails on an unprefixed public
  binding in __init__ and on a helper promoted into __all__, and does not fail when
  internals are reshuffled behind private names or module imports. A FromDI
  parameter is absent from the signature Celery binds against: deleting the
  __signature__ assignment, or swapping the hand-copied dunders for
  functools.update_wrapper, both turn it red, while narrowing the visible signature
  (dropping its return annotation) or narrowing what inject accepts leaves it green.
  That first break is the reason the test exists — the whole suite passed with the
  signature rewrite deleted, because the wrapper re-binds arguments itself and only
  the caller-side arity check moves. The other enforceable claims already had
  tests: dropping weak=False fails three lifespan tests, and dropping DITask's
  __header__ reset fails test_ditask_resolves_without_inject.
- CONTEXT.md defines the three terms local to this package; Container, Provider,
  Group, Scope, Resolution and Override stay modern-di's. The glossary audit forced
  two source edits: README.md called the root container the "APP-scope container"
  twice, and main.py's comment called the visible signature the "stripped
  signature" — both brought to the spelling the rest of the repo already uses.
  Neither is a user-visible runtime string. A third avoid-entry ("request
  container") was cut: it appeared nowhere in the repo, so there was no synonym to
  reject.
- AGENTS.md gains Workflow and Where-a-fact-goes and loses the planning lanes.
  docs/agents/domain.md was deliberately not authored: this repo has no docs/
  directory before this change and no Agent skills section to link one from, so
  writing it alone would be a half-rollout of a separate convention.
- justfile and lint-ci drop index/check-planning/check-links; the offline lychee
  gate in _checks.yml replaces links.py (modern-python/.github#66) and reports
  0 errors over every .md.

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 failed every
future stable release. It is now modern-di/.github/workflows/release.yml verbatim
(only the PyPI project name differs): GitHub's generated notes, prerelease flag
only. This retires the mandatory-curated-notes policy, following modern-di's
1ea74ee (modern-python/modern-di#449); a release wanting prose is edited after the
fact with `gh release edit <tag> --notes-file`.

Verified: just lint-ci clean, just test-ci 16 passed at 100% coverage, lychee
--offline 0 errors, and no remaining reference to planning/, architecture/,
check-planning, check-links or convention-version anywhere in the repo.
@lesnik512
lesnik512 merged commit deccbe2 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