docs: drop architecture/; invariants are tests, contracts are decisions - #424
Merged
Conversation
Adds INVARIANT: docstrings to the tests that already pin the override, navigation, positional-path, live-context, validation, scope-algebra and container-lifecycle claims formerly stated in architecture/. Converts leading # comments that carried the same rationale into the docstring; comments explaining a test's own scope or limitations are left in place.
… their real tests Two INVARIANT docstrings overclaimed what their test proves: the override guard test implied it exercises an otherwise-unwireable factory (it doesn't - test_unwireable_factory_override_short_circuits does), and the scope-hop test implied it catches a skipped-navigation regression (its own retained comment already said it doesn't - two sibling tests do). Reword both to state only what each test proves and cite the test that proves the rest.
…roves The "never gates whether validation may run" clause wasn't exercised by this test (it only shows a repeat validate() skips the re-walk) and read as contradicting is_validated(): return, which does gate the re-walk. Keep only the memoization claim here and attribute the never-auto- triggers half to the four test_container.py tests that actually prove it.
architecture/validation.md states that validate() is the only thing that walks the graph - construction, open(), add_providers and resolve() never do. That invariant was missing from the original map and would otherwise survive nowhere once the page is deleted. The four tests already enforce it; mark each with its own INVARIANT docstring naming only the entry point that test exercises.
Replaces every architecture/*.md pointer in modern_di/ and tests/ with the name of the test that actually enforces the claim, or a self-contained sentence where no test covers it. Widens test_invariant_census.py to scan docstrings as well as comments, and both modern_di/ and tests/, so the new citations (and the ~7 hand-verified ones from Task 2) are guarded against rename rot.
Applies the fix wave from the branch's final whole-branch review: gives the union-member-origin-degradation contract a home (INVARIANT docstring + docs correction), fixes two planning/decisions cross-references that misroute readers, restores the glossary charter's subtle-meaning clause in CLAUDE.md, closes three citation-path gaps in the invariant census (module paths, planning-record paths, and Markdown-cited test names), names decisions/ and INVARIANT: docstrings as accumulation surfaces that need their own subtraction habit, and applies four minor cleanups (deduplicated docstrings, a redundant clause, "since removed" phrasing, and the positional-fast-path measurement rationale).
…nvariant planning/README.md understated test_invariant_census.py after the final fix wave widened its citation scope beyond modern_di/ comments; the sentence now names all four sources it checks. The union-degradation contract lived as an INVARIANT docstring on test_signature_item_parser, a 12-row parametrized table whose name is not the claim -- editing rows :30/:32 could silently orphan it. Extracted a dedicated test_union_member_degrades_to_bare_origin asserting the claim directly and moved the docstring there; the two table rows stay, since the table's job is general parser regression coverage, not hosting this one invariant. Updated the mechanism comment in types_parser.py to cite the new test.
Keeps only the free-threaded Beta record, which is cited from tests/test_free_threading.py and guarded by the census. Fixes the three links that would have dangled, and corrects the two thinning-commit figures in planning/README.md, which still labelled gross deletions as net cuts (433 deletions against 110 insertions across six pages; 292 against 147 across four).
The Beta support level and the thread-safety boundary it stated now live at user altitude in docs/introduction/design-decisions.md; the four references repoint there. planning/decisions/ gains nothing from this branch.
…heck Nothing on this branch exercised it, and decisions/ took no records. decisions/ now holds rejected alternatives only, stated the same way in the four-homes table, the admission check, and the PR template.
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.
Why
architecture/was 1,470 lines across 11 pages that restated whatmodern_di/already says, and the restatement is what rots.It had been cut to invariants twice and regrew both times.
b2404c4(#282, 2026-07-07) removed 433 lines against 110 insertions across six pages, with the subject "trimarchitecture/to charter — invariants only". Twenty-two days later047b6ea(#395, 2026-07-29) removed another 292 against 147 insertions across four pages. Between them,6b980b2(#322) repaired a page that had gone stale against the code. By the time this branch started,resolution.mdwas back to 175 lines carrying full prose on entry points, wiring-plan buckets and thread safety — all derivable.Promotion discipline was never the failure. 48 of the 67 commits touching
modern_di/since mid-June also touchedarchitecture/(72%, measured ated9b00d). The checklist in.github/PULL_REQUEST_TEMPLATE.mdasked "did you update the page?" and never "should this paragraph exist?" — so every PR added and none subtracted. A page that exists accumulates.The churn confirms which half rots.
resolution.md+617/−442 over 23 commits on a 175-line file;containers.md+451/−253;validation.md+474/−290. Against that,glossary.mdis +93/−4 over 2 commits. Pages that restate mechanism churn; pages that fix vocabulary do not.Design
Every fact now routes to exactly one of four homes:
modern_di/planning/decisions/docs/The admission check, now in
planning/README.mdand the PR template:And the rule that was missing: prose about mechanism has no home. There is no file to add a paragraph to. The absence of the directory is the mechanism. The two failed thinnings are cited in
planning/README.mdso the next person to propose a capability page has the evidence in front of them.The invariants were already executable. The plan assumed ~13 new tests were needed; an audit found nearly all of them already existed and were already well-named —
test_arity_rung_front_guards_the_override, the fivetest_can_call_positionally_rejects_*,test_validate_is_free_when_already_validated,test_scope_algebra_answers_deeper_members_for_any_int_enum, and more. The work became marking, not writing: 31 tests now carry anINVARIANT:docstring, and only four tests are genuinely new (one-graph, compile-resolver-fails-loudly,scope.pyimport purity, union-member origin degradation).An
INVARIANT:docstring is line 1INVARIANT: <claim>., blank line, then a paragraph naming what breaks it — where the anti-refactor warnings now live:That paragraph states what breaks the invariant, not necessarily a regression that one test alone would catch — the unit of truth is the invariant plus the suite. The accepted cost is recorded in
planning/README.md: a reader cannot tell from one docstring whether that test or a sibling catches a given regression.tests/test_invariant_census.pyguards the scheme, on the existingtest_docs_slug_census.pyprecedent: everytest_*name cited in a comment or docstring undermodern_di/andtests/must resolve to a real test; everyINVARIANT:docstring must have a "what breaks it" paragraph; and everytests/….py/planning/….mdpath cited from code, plus every test name cited fromCLAUDE.mdandplanning/decisions|deferred/, must resolve.Code comments cite tests, not pages. The ~16 comments reading
see architecture/performance.mdnow readsee test_resolve_costs_exactly_one_resolver_frame_per_node. A pointer to a test is verifiable; a pointer to prose is not.Vocabulary moved to
CLAUDE.md, which is auto-loaded wherearchitecture/README.mdnever was. A term earns a place when it has a synonym to reject, or a meaning subtle enough that code and docs must agree on it.Where each page went
resolution.mdvalidation.md_validatedmemoizes; graph-walked-is-graph-resolvedcontainers.mdproviders.mdGroup/Factory/CacheSettingstablesperformance.mdbenchmarks/README.md)scopes.mdfind_containerstepsscope.pyimports onlyenumconcurrency.mddocs/integration-kit.mdtesting-and-overrides.mdmodern-di-pytest(already indocs/)tests/providers/test_factory.py:185glossary.mdCLAUDE.mdREADME.mdplanning/README.mdNon-goals
Not a code change. No behaviour in
modern_di/moves; only comments change there. The four new tests assert what already shipped.Not deleting
docs/. Thearchitecture/↔docs/altitude split ends because one side of it ends.Not reshaping
decisions/ordeferred/. Frontmatter, naming andindex.pyare unchanged.deferred/2026-07-29-upstream-lean-convention.mdis updated — its central finding rested onarchitecture/promotion working — but deliberately not resolved; its revisit trigger stands.Not adding any
planning/decisions/record. Six were written during this work — transient teardown order, rebinding an in-use provider, override values not being type-checked, the integration-kit per-adapter boundary, the free-threaded Beta support level, and a record of this change itself — and all six were then dropped as not worth their weight. The free-threaded contract survives at user altitude indocs/introduction/design-decisions.md; the rest now live only in the code and its tests, or nowhere.planning/decisions/is unchanged by this branch apart from four repointed links.Not migrating the 13 sibling integration repos.
docs/integrations/writing-integrations.mdnow tells adapter authors to keep invariants in named tests rather than a prose truth home; each repo decides its own layout on its own schedule. Nothing here tells them what to do with thearchitecture/directories they have today.Not editing published GitHub Release bodies. The committed
releases/*.mdlinks are repointed fromblob/main/toblob/<version>/, verified to resolve against the tree as it stood; the live Release pages keep their original text.The census is name and path integrity, not truth. It cannot check that a comment cites the right test, nor that a rationale paragraph is true. Semantic drift is caught by the invariant test failing.
Verification
just test-ci— 509 passed, 100.00% line coverage.just lint-ci— ruff,ty,planning/index.py --check,planning/links.py: all clean.just check-links—links: OK. This is the completeness proof: those links resolve whilearchitecture/exists and only break once it is gone, so a green run after the deletion means every reference was rewritten or dropped.just docs-build—mkdocs build --strict, clean.grep -rn "architecture/"returns only tag-pinned release URLs (verified withgit cat-file -e 2.28.0:architecture/integration-kit.mdand2.30.0:architecture/concurrency.md), past-tense historical prose inplanning/, andplanning/links.py's module docstring — deliberately untouched, since that file is vendored into consumer repos and its docstring describes the general convention.No performance claim; nothing on the resolve path changes.
Size: 17 commits, 49 files, +651 / −1,630 (net −979). The 1,470-line figure is the gross size of the deleted directory.
Deliberate deviations from the original plan
planning/links.pyuntouched — vendored into consumer repos; editing it widens the fork for no gain.docs/introduction/concurrency.mdand nomkdocs.ymlchange —design-decisions.md§2 already owned thread safety.test_container.py::test_*_never_validatestests marked that the plan's map had missed; without themarchitecture/validation.md:9would have survived nowhere.