From 63146cc863070e37659b65e4448f5dee9d299956 Mon Sep 17 00:00:00 2001 From: Vinta Chen Date: Sat, 15 Aug 2026 13:00:16 +0800 Subject: [PATCH] docs: capture shortlist-reform decisions on shortlist-reform branch Records the outcome of a grilling session with the maintainer that settled the redesign of awesome-python from a catalog into a curated shortlist of Obvious Choices per Use Case. Execution is held pending maintainer go-ahead, so these files let a fresh agent resume without re-litigating settled decisions: - CONTEXT.md: glossary of the editorial vocabulary (Use Case, Obvious Choice, Challenger, Displacement, Split, etc). - docs/adr/0001-shortlist-not-catalog.md: the ADR recording the decision, considered options, and consequences (status: proposed). - .gitignore: docs/ was wholesale-ignored; carve out docs/adr/ so the ADR can be tracked. Co-Authored-By: Claude --- .gitignore | 5 +-- CONTEXT.md | 45 ++++++++++++++++++++++++++ docs/adr/0001-shortlist-not-catalog.md | 26 +++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 CONTEXT.md create mode 100644 docs/adr/0001-shortlist-not-catalog.md diff --git a/.gitignore b/.gitignore index f081c833..5fbd39f1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,8 +13,9 @@ __pycache__/ website/output/ website/data/ -# docs -docs/ +# docs (except decision records) +docs/* +!docs/adr/ # agents .agents/ diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 00000000..5456ff32 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,45 @@ +# Awesome Python Curation + +The editorial domain of awesome-python: which projects the list carries, how they are admitted, and how they are displaced. The list is a curated shortlist of obvious choices, not a catalog of everything good. + +## Language + +### List structure + +**Entry**: +A single listed project: `- [pypi-name](url) - Description.` The unit that is admitted, displaced, or pruned. + +**Section**: +A `###` heading in README.md (e.g. "Testing", "AI and Agents"). Sections group entries and live under a Thematic Group. +_Avoid_: Category (overloaded — the TOC calls them categories, but rules bind to Use Cases, not Sections) + +**Subcategory**: +A named bullet inside a Section with indented entries under it (e.g. "Mocking" inside "Testing"). Where present, each Subcategory is one Use Case. + +**Use Case**: +The unit of "it" in "one obvious way to do it": one distinct job a reader needs done. Defined by the list's existing structure — each Subcategory is a Use Case; a flat Section is a single Use Case. Submitters cannot define Use Cases; only the list structure does. +_Avoid_: Niche, micro-category + +### Admission + +**Serves Python Developers**: +The scope test for what belongs on the list at all: Python developers use it in their Python work. Implementation language is irrelevant — uv and ty are Rust and belong; a pure-Python library nobody uses in Python work does not. +_Avoid_: Python-first, written-in-Python (old requirement — removed) + +**Obvious Choice**: +An entry an experienced Python developer would name unprompted when asked "what do I use for [Use Case]?". Certified by maintainer editorial judgment, informed primarily by PyPI download counts rather than GitHub stars. A standard-library module holds a slot only when it is itself the Obvious Choice for the Use Case, not merely relevant to it. +_Avoid_: Industry Standard (old lane name), awesome (unfalsifiable) + +**Cap**: +The per-Use-Case entry limit: up to 3 Obvious Choices plus up to 2 Challengers, hard maximum 5. A qualitative bar first, a numeric backstop second. + +**Displacement**: +The only admission path into a full Use Case: the PR names the entry it replaces and argues the newcomer does that entry's job better. One in, one out. +_Avoid_: One-in-one-out (informal alias) + +**Challenger**: +An entry (at most two per Use Case) that is not yet the Obvious Choice but is a credible successor to one. Its description must say so, and admission requires adoption-trajectory evidence, not popularity alone. +_Avoid_: Rising Star (old lane name), Hidden Gem (old lane name — concept removed entirely) + +**Split**: +Maintainer-only restructuring of an oversized Use Case into finer Use Cases (new Subcategories), chosen over trimming when the size reflects genuinely distinct jobs. A submitter's entry PR can never perform a Split. diff --git a/docs/adr/0001-shortlist-not-catalog.md b/docs/adr/0001-shortlist-not-catalog.md new file mode 100644 index 00000000..68d51580 --- /dev/null +++ b/docs/adr/0001-shortlist-not-catalog.md @@ -0,0 +1,26 @@ +--- +status: proposed +--- + +# awesome-python is a shortlist of obvious choices, not a catalog + +By mid-2026 the list held 576 entries across 75 sections, with entry inflow up 24x year over year (96 additions in the trailing 12 months vs 4 the year before), concentrated in sections like AI and Agents (41 entries). The old acceptance model — three lanes (Industry Standard, Rising Star, Hidden Gem), only the first capped — admitted any project good enough in isolation, so categories grew without bound and stopped answering the reader's actual question: "what do I use for X?" We decided to reposition the list as a curated shortlist, per the Zen of Python: there should be one — and preferably only one — obvious way to do it. + +## The decision + +Each Use Case (a subcategory, or a flat section) lists at most its Obvious Choices — capped at 3 (provisional, to be reviewed after the prune), including at most one marked Challenger. Admission is by maintainer editorial judgment, informed primarily by PyPI download counts rather than GitHub stars, and stated as final. Once a Use Case is at cap, the only way in is Displacement: the PR names the entry it replaces and argues the newcomer does that job better. Use Cases are defined by the list's existing structure; an entry PR can never create the subcategory it needs. Standard-library entries hold a slot only where the stdlib module is itself the obvious choice. The existing stock gets the same test retroactively: a staged, worst-first prune (per-section sweep commits), with removed entries deleted outright — git history is the archive. Resources sections (Newsletters, Podcasts, Websites) are out of scope for now. + +## Considered options + +- Keep the three-lane model with caps on every lane: rejected because the lanes answer the wrong question ("is this good enough to enter?") once admission is comparative; rising-star momentum becomes Displacement evidence rather than an admission ticket, and Hidden Gem is definitionally incompatible with "obvious". +- Rules-only, no retroactive prune: rejected because every rejection would face "but X is listed" precedent arguments, and readers would see no change. +- Archive removed entries in a separate file: rejected because it recreates the catalog one click away and dilutes the identity the change exists to restore. + +## Consequences + +- The steady-state list shrinks toward roughly 400 entries or fewer (Testing dry run: 23 → 14); most future PRs will be rejected for fullness, not badness. +- awesome-python.com loses long-tail search traffic for the hundreds of niche tool names it will no longer carry. Accepted deliberately: reader trust over search surface. +- Fast-moving domains (e.g. AI and Agents) list current leaders by usage and absorb churn through Displacement; an oversized Use Case is either trimmed or Split into finer Use Cases by the maintainer. +- Linked awesome-* lists (e.g. awesome-python-testing) remain as the escape valve for readers who want exhaustive catalogs. + +See CONTEXT.md for the vocabulary (Use Case, Obvious Choice, Challenger, Displacement, Split).