Skip to content

chore: migrate off the planning/ convention - #37

Merged
lesnik512 merged 2 commits into
mainfrom
chore/migrate-off-planning
Sep 6, 2026
Merged

chore: migrate off the planning/ convention#37
lesnik512 merged 2 commits into
mainfrom
chore/migrate-off-planning

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Migrates db-retry 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.

This branch also carries chore(context7): drop the stale planning/ exclusion — the context7.json entry naming planning/ only becomes dead once this migration deletes the directory, so the two land together.

planning/ held 21 files: the portable convention README, five templates, five
change files, one decision record, an empty deferred.md, index.py, and three
release notes. Nothing is lost. Git history is the record for the change files;
the prose in them that outlives a PR has moved — the decision record and one
rescued rejected alternative are now ADRs, the enforceable claims are now tests,
the vocabulary is CONTEXT.md. planning/releases/{0.4.3,0.5.0,0.5.1}.md were each
diffed against gh release view <tag> and are identical bar a trailing newline
GitHub adds, so the published Releases are the record.

architecture/ held eight pages: a README, a glossary, and six capability pages.
The capability prose is mechanism a reader gets from db_retry/ directly and is
deleted; glossary.md seeded CONTEXT.md.

Decisions

  • planning/decisions/2026-06-27-retry-default-stays-env-configured.md becomes
    docs/adr/0001-retry-default-stays-env-configured.md, frontmatter stripped.
  • Rescued from the non-goals of
    planning/changes/2026-06-26.01-retriable-error-seam.md:
    docs/adr/0002-retriable-taxonomy-is-asyncpg-classes.md — the taxonomy stays a
    tuple of asyncpg classes rather than SQLSTATE data. Its only home was
    architecture/retriable.md, which this commit deletes.

Invariants
Five enforceable claims became tests whose name is the claim. Each was verified
by breaking it in db_retry/, confirming red, restoring, confirming green, and
confirming a benign narrowing stays green:

  • test_a_statement_of_unknown_outcome_is_never_retriable — red when
    RETRIABLE_ASYNCPG_ERRORS widens to asyncpg.TransactionRollbackError; green when
    a sibling (DeadlockDetectedError) is added instead, and when
    PostgresConnectionError is narrowed to a subclass.
  • test_the_default_attempt_count_is_re_read_on_every_call — red when
    settings.get_retries_number() is hoisted into the decorator body.
  • test_a_retry_never_re_runs_the_body_immediately — red under wait_none(); green
    when the curve is retuned (initial, exp_base, jitter).
  • test_use_replica_overrides_a_target_session_attrs_already_in_the_dsn — red when
    the dict union in build_db_dsn is flipped; green under an equivalent
    {**query, ...} rewrite.
  • test_host_and_port_stay_paired_through_the_shuffle — 0/12 runs passed both when
    host and port are shuffled independently and when failover derives from a
    second shuffle; 12/12 green when the shuffle is dropped entirely. Six hosts
    with distinct ports, so an accidental in-step shuffle cannot mask a mis-pairing
    more than once in 720 runs.
    The pairing assertion moved out of test_build_connection_plan_multihost, which
    gave both hosts port 5432 — a swap there is unobservable.

architecture/retriable.md gave StatementCompletionUnknownError as SQLSTATE 40002.
It is 40003; 40002 is TransactionIntegrityConstraintViolationError. The invariant
test carries the correct code, and the same wrong class name in
tests/test_retry.py's parametrize comment is corrected.

Glossary audit
The single glossary entry, "Retriable error", rejected two synonyms.
"recoverable error" appears nowhere outside the glossary itself and is dropped.
"transient error" appears twice, both times in README.md and both user-visible
strings; both are reworded to "retriable", which is what earns the entry its
listing. Two terms are added on the subtlety clause: "Attempt" (retries=N is
stop_after_attempt(N), so retries=1 never retries) and "Primary host" (a position
in the two-stage connect, not a PostgreSQL replication role).

Release policy
.github/workflows/release.yml no longer reads planning/releases/.md. This
retires the curated-release-notes policy: a Release body is now always GitHub's
generated notes, edited after the fact with gh release edit --notes-file when a
release wants prose. The file is now identical to modern-di's, which dropped the
same gate in 1ea74ee (modern-python/modern-di#449).

justfile drops the index and check-planning recipes and the planning/index.py
line from lint-ci; .github/workflows/_checks.yml gains the offline lychee links
job that replaces it. Two unscheduled items from the change files' non-goals were
written up as draft issues outside the repo and not opened.

The sibling commit on this branch deletes planning/, so the exclusion now
names a directory that does not exist.

Dropping the key rather than leaving an empty list: Context7's schema
(https://context7.com/schema/context7.json) declares no required properties
and gives excludeFolders a default of [], so an absent key and an empty list
are equivalent. Absent matches the repos that never carried the line.

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.

planning/ held 21 files: the portable convention README, five templates, five
change files, one decision record, an empty deferred.md, index.py, and three
release notes. Nothing is lost. Git history is the record for the change files;
the prose in them that outlives a PR has moved — the decision record and one
rescued rejected alternative are now ADRs, the enforceable claims are now tests,
the vocabulary is CONTEXT.md. planning/releases/{0.4.3,0.5.0,0.5.1}.md were each
diffed against `gh release view <tag>` and are identical bar a trailing newline
GitHub adds, so the published Releases are the record.

architecture/ held eight pages: a README, a glossary, and six capability pages.
The capability prose is mechanism a reader gets from db_retry/ directly and is
deleted; glossary.md seeded CONTEXT.md.

Decisions
- planning/decisions/2026-06-27-retry-default-stays-env-configured.md becomes
  docs/adr/0001-retry-default-stays-env-configured.md, frontmatter stripped.
- Rescued from the non-goals of
  planning/changes/2026-06-26.01-retriable-error-seam.md:
  docs/adr/0002-retriable-taxonomy-is-asyncpg-classes.md — the taxonomy stays a
  tuple of asyncpg classes rather than SQLSTATE data. Its only home was
  architecture/retriable.md, which this commit deletes.

Invariants
Five enforceable claims became tests whose name is the claim. Each was verified
by breaking it in db_retry/, confirming red, restoring, confirming green, and
confirming a benign narrowing stays green:
- test_a_statement_of_unknown_outcome_is_never_retriable — red when
  RETRIABLE_ASYNCPG_ERRORS widens to asyncpg.TransactionRollbackError; green when
  a sibling (DeadlockDetectedError) is added instead, and when
  PostgresConnectionError is narrowed to a subclass.
- test_the_default_attempt_count_is_re_read_on_every_call — red when
  settings.get_retries_number() is hoisted into the decorator body.
- test_a_retry_never_re_runs_the_body_immediately — red under wait_none(); green
  when the curve is retuned (initial, exp_base, jitter).
- test_use_replica_overrides_a_target_session_attrs_already_in_the_dsn — red when
  the dict union in build_db_dsn is flipped; green under an equivalent
  {**query, ...} rewrite.
- test_host_and_port_stay_paired_through_the_shuffle — 0/12 runs passed both when
  host and port are shuffled independently and when failover derives from a
  second shuffle; 12/12 green when the shuffle is dropped entirely. Six hosts
  with distinct ports, so an accidental in-step shuffle cannot mask a mis-pairing
  more than once in 720 runs.
The pairing assertion moved out of test_build_connection_plan_multihost, which
gave both hosts port 5432 — a swap there is unobservable.

architecture/retriable.md gave StatementCompletionUnknownError as SQLSTATE 40002.
It is 40003; 40002 is TransactionIntegrityConstraintViolationError. The invariant
test carries the correct code, and the same wrong class name in
tests/test_retry.py's parametrize comment is corrected.

Glossary audit
The single glossary entry, "Retriable error", rejected two synonyms.
"recoverable error" appears nowhere outside the glossary itself and is dropped.
"transient error" appears twice, both times in README.md and both user-visible
strings; both are reworded to "retriable", which is what earns the entry its
listing. Two terms are added on the subtlety clause: "Attempt" (retries=N is
stop_after_attempt(N), so retries=1 never retries) and "Primary host" (a position
in the two-stage connect, not a PostgreSQL replication role).

Release policy
.github/workflows/release.yml no longer reads planning/releases/<tag>.md. This
retires the curated-release-notes policy: a Release body is now always GitHub's
generated notes, edited after the fact with `gh release edit --notes-file` when a
release wants prose. The file is now identical to modern-di's, which dropped the
same gate in 1ea74ee (modern-python/modern-di#449).

justfile drops the index and check-planning recipes and the planning/index.py
line from lint-ci; .github/workflows/_checks.yml gains the offline lychee links
job that replaces it. Two unscheduled items from the change files' non-goals were
written up as draft issues outside the repo and not opened.
The sibling commit on this branch deletes planning/, so the exclusion now
names a directory that does not exist.

Dropping the key rather than leaving an empty list: Context7's schema
(https://context7.com/schema/context7.json) declares no required properties
and gives excludeFolders a default of [], so an absent key and an empty list
are equivalent. Absent matches the repos that never carried the line.
@lesnik512
lesnik512 merged commit 5535738 into main Sep 6, 2026
6 checks passed
@lesnik512
lesnik512 deleted the chore/migrate-off-planning branch September 6, 2026 19:13
@lesnik512 lesnik512 changed the title chore(context7): drop the stale planning/ exclusion chore: migrate off the planning/ convention Sep 6, 2026
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