docs(adr): migrate planning/decisions to native ADRs in docs/adr (29 → 26) - #433
Merged
Conversation
Move the 29 decision records to docs/adr/NNNN-slug.md, dropping two, merging one, re-anchoring three whose evidence was deleted with architecture/, and compressing the rest (1843 -> 794 lines). Delete the planning tooling the old format required: index.py, the decision and deferred templates, .convention-version, and the index/check-planning recipes. ADRs are built by MkDocs but kept out of the nav with not_in_nav, so relative .md links between them resolve on GitHub and on the site. Claude-Session: https://claude.ai/code/session_01FBic7HFGR6sdRfSW3cGEv2
Review found three numbers cut that the compression rule says to keep: 0015's peer baseline (292 ns warm hit against dishka ~245, wireup ~98, that-depends ~85, dependency-injector ~61) and the inconsistent dishka parenthetical it left behind, and 0016's RLock range plus the _next_deeper figure the remaining cost is measured against. Repoint the perf-readability audit harness at docs/adr/, and trim rationale out of the census docstring. Claude-Session: https://claude.ai/code/session_01FBic7HFGR6sdRfSW3cGEv2
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: f22d035 | Previous: fa77633 | Ratio |
|---|---|---|---|
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type |
3261886.812979408 iter/sec (stddev: 1.3918226581085048e-8) |
3942022.934212653 iter/sec (stddev: 2.309896453466658e-8) |
1.21 |
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry |
3321822.774497667 iter/sec (stddev: 9.207945296853416e-9) |
4112858.8201955766 iter/sec (stddev: 1.886225647098389e-8) |
1.24 |
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve |
24680.113639914918 iter/sec (stddev: 0.000002920347977768684) |
24739.894068814585 iter/sec (stddev: 0.0000038121083324387136) |
1.00 |
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached |
18940.995016473902 iter/sec (stddev: 0.0000023793833158448368) |
18764.294714550128 iter/sec (stddev: 0.000003916044858254767) |
0.99 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] |
451.1773176789072 iter/sec (stddev: 0.0000752061327658595) |
592.5861318844157 iter/sec (stddev: 0.00006296074278938853) |
1.31 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] |
420.7768496163303 iter/sec (stddev: 0.0000946635753964873) |
519.5251569691909 iter/sec (stddev: 0.00021040745177117575) |
1.23 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] |
399.5290310698681 iter/sec (stddev: 0.00005296048510436462) |
414.4855560888166 iter/sec (stddev: 0.0002448265343448415) |
1.04 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] |
2404.796992850763 iter/sec (stddev: 0.000024752181869674966) |
2329.574674603042 iter/sec (stddev: 0.0000290802385199261) |
0.97 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] |
1803.3398394831652 iter/sec (stddev: 0.0001720133562202577) |
1723.1921312835866 iter/sec (stddev: 0.00022062647213172504) |
0.96 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] |
1270.9805795230345 iter/sec (stddev: 0.000050555589927191454) |
1165.8087528642104 iter/sec (stddev: 0.00005994027387913142) |
0.92 |
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container |
781195.575690802 iter/sec (stddev: 4.1209975655412064e-8) |
804962.4648029776 iter/sec (stddev: 6.02847416669573e-8) |
1.03 |
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope |
715973.1222974593 iter/sec (stddev: 3.8639161457844086e-8) |
765504.631275264 iter/sec (stddev: 5.6068887888367584e-8) |
1.07 |
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch |
2525.186780444069 iter/sec (stddev: 0.000009106347758657456) |
2440.6134899406984 iter/sec (stddev: 0.00001651544678920396) |
0.97 |
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control |
72993.22821921961 iter/sec (stddev: 8.677039709060992e-7) |
77281.7756172458 iter/sec (stddev: 0.0000012988863747743572) |
1.06 |
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale |
50759.23813555624 iter/sec (stddev: 0.0000013177735223810996) |
48944.95290380992 iter/sec (stddev: 0.0000016124198528796291) |
0.96 |
benchmarks/test_guard_resolve.py::test_g1_transient_resolve |
2176983.299815358 iter/sec (stddev: 2.3152409209855863e-8) |
2664231.736467139 iter/sec (stddev: 3.28428551600116e-8) |
1.22 |
benchmarks/test_guard_resolve.py::test_g2_cached_resolve |
3655493.8325392148 iter/sec (stddev: 4.747250128479804e-9) |
4879073.147842481 iter/sec (stddev: 1.060605836657873e-8) |
1.33 |
benchmarks/test_guard_resolve.py::test_g3_deep_chain |
826515.4931256069 iter/sec (stddev: 3.402227905552501e-8) |
985461.6080776677 iter/sec (stddev: 5.084532450165497e-8) |
1.19 |
benchmarks/test_guard_resolve.py::test_g4_wide_resolve |
461254.6976485525 iter/sec (stddev: 7.147293863538686e-8) |
489556.6430625755 iter/sec (stddev: 9.67413862880382e-8) |
1.06 |
benchmarks/test_guard_resolve.py::test_g5_cross_scope |
1798344.3632518398 iter/sec (stddev: 5.2629156238679125e-8) |
2068208.0538772766 iter/sec (stddev: 3.100820300387718e-8) |
1.15 |
benchmarks/test_guard_resolve.py::test_g9_context_resolve |
942042.7731219333 iter/sec (stddev: 2.8784809725539843e-8) |
1033386.7361755 iter/sec (stddev: 4.2539899561253e-8) |
1.10 |
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve |
577845.1789213789 iter/sec (stddev: 5.601839133982075e-8) |
668808.5259711052 iter/sec (stddev: 7.036121748714989e-8) |
1.16 |
benchmarks/test_guard_resolve.py::test_g18_alias_hop |
2483019.252091839 iter/sec (stddev: 8.72030257067565e-9) |
3112637.770022544 iter/sec (stddev: 2.3720897620340684e-8) |
1.25 |
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain |
29323.420713842526 iter/sec (stddev: 0.0000035613303755741256) |
27672.02299243562 iter/sec (stddev: 0.000002642271864823158) |
0.94 |
benchmarks/test_guard_validate.py::test_g11_validate_wide |
17892.254382846197 iter/sec (stddev: 0.0000025111698978075787) |
16725.92308110814 iter/sec (stddev: 0.000004104413783651893) |
0.93 |
This comment was automatically generated by workflow using github-action-benchmark.
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.
Closes #430.
Why
29 decision records sat in
planning/decisions/behind a vendored validator and alocal deviation from an external planning convention. Three problems had
accumulated:
docs/adr/with sequentialNNNN-slug.mdnames. Dated slugs, custom frontmatter and a fixed four-sectionbody are understood by nothing outside this repo.
main. Oneanswered a question closed twice over. Three leaned on evidence deleted with
architecture/in docs: drop architecture/; invariants are tests, contracts are decisions #424. One was arithmetic over two neighbours.planning/README.mdalready named this as owed work: "nothing yet prunes arecord once its call is settled".
Design
29 records become 26 ADRs at
docs/adr/NNNN-slug.md, numbered chronologically bythe original's date, slug kept minus the date prefix,
summary:frontmatterdropped (its only consumer was the deleted renderer).
Compression rule. Keep the decision in one sentence, every measured quantity,
the specific reason each alternative was rejected, and the revisit trigger. Cut
prose restating the code, background re-derived from a linked doc, and narrative
transitions. Three representative records were compressed first and signed off
before the other 23. Result: 1843 → 794 lines. Nine records carry benchmark deltas
and keep their numbers verbatim; the two
-inherentrecords compress lightly,because their reasoning is the payload.
Two drops.
signatureitem-opacity-supersededhad no measurement and itsquestion closed twice over — the candidate's substance shipped as
wiring.py'sfind_dep_providerandabsent_dispositionin #308, which the record itselfstates. Its one durable claim, that
types_parsermust not importProvidersRegistry, is visible in that module's import block and stated inCLAUDE.md. Accepted cost: nothing enforces the rule now.contextprovider-resolver-inline-declinedwas verified false againstmain— itsDecision sentence says the resolver "keeps delegating to the bound
ContextProvider.resolve", a method #425 deleted. The concern behind the declineis structurally impossible now that
ProviderScopeFrozenErrorraises on themutation it feared.
One merge.
d1-d4-derived-inherentstates outright that it has no independentrevisit trigger; it is arithmetic over its two neighbours. Its D1 half lands in
0020, its D4 line-count argument in0021. Those two stay separate: each carriesa distinct per-integration table and a distinct trigger (a framework gaining a
lifecycle hook, versus a per-parameter provider hook).
scope-map-inline-declinedandresolve-provider-not-a-seamare deliberatelynot merged despite arguing one question. Both are measured,
container.pycitesthe latter by path, and the latter explicitly states it does not supersede the
former — merging would flatten two opposite answers into one.
Three re-anchors, all citing evidence deleted with
architecture/in #424.0011re-anchors the registry-symmetry argument onCLAUDE.md's registries entry;0015strikes the "concurrency.mdwas kept" claim;0003strikes a claimedenter_scope/CreateScopedocs mapping that grep confirms never existed (addingthat vocabulary row is out of scope).
MkDocs.
not_in_nav: | /adr/. ADRs build and their links are validated, butthey stay out of the site menu.
exclude_docswas rejected: an excluded file'soutgoing links get no validation at all. Residual accepted cost — the 26 pages get
real site URLs and enter
sitemap.xml, acceptable since the repo is public and therecords are already GitHub-indexable.
Links. Sibling ADRs link as bare
NNNN-slug.md; links formerly reaching../../docs/become../. No ADR links to a path outsidedocs/—modern_di/...andtests/...stay inline code.Teardown.
planning/index.py,_templates/decision.md,_templates/deferred.mdand
.convention-versionare deleted, along with theindexandcheck-planningrecipes and the validator line in
lint-ci.planning/README.mdis rewrittenaround what remains.
links.py,releases/,scripts/and_templates/release.mdstay.
tests/test_invariant_census.pykeeps theINVARIANT:shape check and losesthe two citation checks, with every helper and constant only they used.
Non-goals
0011's own trigger has arguably fired, since theframing it rested on was abandoned. Re-anchoring its prose is in scope;
re-litigating the call is not.
planning/deferred/. It stays as-is; Migrate deferred items to GitHub Issues and retire the deferred concept #431 migrates it to Issues and retiresthe concept. Its two links into
decisions/are repointed here socheck-linksstays green.
enter_scopevocabulary row is not written.INVARIANT:test for thetypes_parserlayering rule. Considered andrejected: it cuts against removing the census's citation checks in the same PR.
pyproject.toml'sSLF001comment. It names aplanning/deferred/file thatstill exists, so the reference resolves; Migrate deferred items to GitHub Issues and retire the deferred concept #431 repoints it.
One deviation from the issue's stated non-goals:
planning/scripts/perf-readability-audit.workflow.mjsdrove its agents at
planning/decisions/, so this PR repoints it atdocs/adr/rather than ship a harness aimed at a deleted directory.
Verification
No new test seams — a good test here checks that links resolve and the build is
clean, not an ADR's content.
just check-links— the primary gate, and exactly what this migration risks:every relative link and heading anchor in the repo, including trees MkDocs never
sees. OK.
just docs-build(mkdocs build --strict) — covers the 26 newly built pages.Clean, no warnings; all 26 present in
site/adr/.just test-ci— 512 passed, 100% line coverage. The census trim is the onlyPython change; the coverage risk was dead helpers, and none remain.
just lint-ci— clean, with the deleted validator line removed in the samecommit.
Three published Releases whose bodies linked into
planning/decisions/werere-pushed from their updated in-repo notes:
2.29.0and3.4.0repoint at0013,3.3.0gets an absolute permalink to0026(its relative link was already brokenin a Release body), and
3.2.0's inline-code mention becomes a link to0025.Neither dropped record is linked from a Release.
https://claude.ai/code/session_01FBic7HFGR6sdRfSW3cGEv2