Goal
Unblock the Django release gate: since #320 the catalogue template emits the canonical cohort routes, but three catalogue tests still expect the legacy generic two-segment shape. The django job has been unable to run to completion on main since #320 landed (every run died earlier at the quality gate, fixed by #341), so these stale expectations surfaced only now, as the only remaining red gate.
Evidence
Main run 34189057638 (87fd3f4): quality, container, playwright and screenshots all pass; django fails with 2 failures + 1 error, all in courses/tests/test_course_list_ordering.py:
test_catalogue_has_one_family_card_for_multiple_cohorts: asserts the literal /courses/catalogue-course/2026, but the card renders /courses/catalogue-course/cohorts/2026.
test_featured_course_is_an_exact_homepage_catalog_cohort: expects href="/courses/de-zoomcamp/2026", page renders the /cohorts/ shape.
test_active_and_finished_cards_link_to_their_cohort_routes: the course_card_html helper (courses/tests/course_list_base.py) locates the card via reverse("course", ...) and fails with ValueError: substring not found.
Root cause: #320 renamed the catalogue template's edition URL from {% url 'course' %} to {% url 'cohort' %}. Per the frozen route map (_docs/compatibility/shared-curriculum-route-aliases.json, decided 2026-09-07, owner PM #320), /courses/<family>/cohorts/<identifier> (route name cohort) is canonical; the generic two-segment shape remains only as a legacy alias. The product is aligned with the spec; the three tests plus the two course_list_base.py helpers still assert the legacy shape.
Scope
Non-goals
- No change to the route table, aliases, or any canonical route decision.
- No broader test rewrite beyond the stale route expectations.
Acceptance criteria
Goal
Unblock the Django release gate: since #320 the catalogue template emits the canonical cohort routes, but three catalogue tests still expect the legacy generic two-segment shape. The django job has been unable to run to completion on main since #320 landed (every run died earlier at the quality gate, fixed by #341), so these stale expectations surfaced only now, as the only remaining red gate.
Evidence
Main run 34189057638 (87fd3f4): quality, container, playwright and screenshots all pass; django fails with 2 failures + 1 error, all in
courses/tests/test_course_list_ordering.py:test_catalogue_has_one_family_card_for_multiple_cohorts: asserts the literal/courses/catalogue-course/2026, but the card renders/courses/catalogue-course/cohorts/2026.test_featured_course_is_an_exact_homepage_catalog_cohort: expectshref="/courses/de-zoomcamp/2026", page renders the/cohorts/shape.test_active_and_finished_cards_link_to_their_cohort_routes: thecourse_card_htmlhelper (courses/tests/course_list_base.py) locates the card viareverse("course", ...)and fails withValueError: substring not found.Root cause: #320 renamed the catalogue template's edition URL from
{% url 'course' %}to{% url 'cohort' %}. Per the frozen route map (_docs/compatibility/shared-curriculum-route-aliases.json, decided 2026-09-07, owner PM #320),/courses/<family>/cohorts/<identifier>(route namecohort) is canonical; the generic two-segment shape remains only as a legacy alias. The product is aligned with the spec; the three tests plus the twocourse_list_base.pyhelpers still assert the legacy shape.Scope
courses/tests/test_course_list_ordering.py/courses/tests/course_list_base.pyto the canonicalcohortroute (reverse withcohort_identifier, matching the file's post-Share current Zoomcamp curriculum across live and self-paced cohorts with GitHub archives #320 style).Non-goals
Acceptance criteria
uv run python manage.py test courses.tests.test_course_list_ordering --settings=website.settings.testpasses.make lint,make format-check,make typecheckstay green.