DataTalksClub / DataTalksClub/website
AI Dev Tools renders as two separate course families, orphaning the 2026 cohort
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Product outcome
The course catalogue has one AI Dev Tools family. The 2025 and 2026 cohorts are
owned by that family, the 2026 curriculum import cannot recreate a sibling family,
and an already-split database can be repaired without recreating learner or
curriculum rows.
This issue is a bounded identity/data-repair slice. It does not redesign the
Course/Cohort model or move the public course reader to a new source of truth.
Normative authority
_docs/PROCESS.md- Specification 04 — Course-platform adoption and Course → Cohort
- Specification 02 — URL, link, SEO, and compatibility
- Specification 03 — GitHub content and provenance
- Specification 07 — security, privacy, and operations
- Specification 09 — migration and rollout
- Specification 10 — verification strategy
- Resolved owner decisions #14, #15, and #16
Accepted identity contract
The owner decision in the issue comment is authoritative:
| Field | AI Dev Tools value | Meaning |
|---|---|---|
Course.slug |
ai-dev-tools |
Reusable family/public catalogue identity |
Cohort.slug |
ai-dev-tools-2026 |
Globally unique retained legacy cohort slug; it is not the family slug |
Cohort.identifier |
2026 |
Stable cohort route segment scoped to Course; it is not inferred from a slug at request time |
Cohort.year |
2026 |
Schedule/display metadata, not the route identity |
source_stable_id |
ai-dev-tools |
Canonical source/import identity for this course family |
| repository | DataTalksClub/ai-dev-tools-zoomcamp |
Source provenance; the repository name is not the public family identity |
The accepted canonical HTML route is /courses/ai-dev-tools/2026 (the accepted
no-cohorts/ syntax from #16). The legacy CMP route
https://courses.datatalks.club/ai-dev-tools-2026/ remains the observed
compatibility path. Existing /docs/.../ai-dev-tools-zoomcamp/..., FAQ, and blog
paths belong to other source contracts and are out of scope.
For existing published families whose canonical slug really includes
-zoomcamp (for example ml-zoomcamp), this issue does not rename them. The rule
is an explicit reviewed repository-to-family mapping: a repository suffix must not
mint a second family when its reviewed canonical family already exists. Unknown or
new divergent identities require an explicit mapping; runtime regex/year stripping
or a blanket suffix rewrite is not an identity policy.
Current compatibility evidence
_docs/compatibility/generated-path-baseline.jsonlcontains no application path
for/courses/ai-dev-toolsor/courses/ai-dev-tools-zoomcamp; its AI Dev Tools
rows are docs, FAQ, blog, and media contracts.- A read-only production probe on 2026-09-02 returned
200for
/ai-dev-tools-2026/and404for/ai-dev-tools-zoomcamp-2026/on
courses.datatalks.club. - Therefore no redirect for the losing application family slug is owed or may be
invented. If a future fresh crawl proves a previously served alias, stop and
return that compatibility change to PM with its exact one-hop destination.
Scope
Source identity and recurrence prevention
- Put the reviewed mapping in a checked-in generator/configuration boundary, not
only in the gitignored.local/scratch manifest. A regenerated AI Dev Tools
manifest must emitsource_stable_id=ai-dev-toolswhile retaining the repository
owner/name for provenance. - Resolve the repository-declared
ai-dev-tools-zoomcampcourse slug to the one
reviewed family and theai-dev-tools-2026cohort slug. Preserve source content
IDs and checksums; do not make a source identity claim from a title, year, or
repository-name suffix alone. - The importer, local preparation path, and review/import adapter must use the
same explicit mapping. A source/content identity collision, source-stable-ID
conflict, or unmapped divergent source rejects before any database mutation. - Keep the existing source pin
DataTalksClub/course-management-platform@98a235283904b4ef9ad29e196298540756cf1bcc.
This issue is not permission to sync CMPmainor port unrelated upstream code.
Existing-database repair
- Add an idempotent repair migration or migration-owned helper against the current
Course/Cohort schema. The migration identity rule is frozen independently of
mutable runtime catalog code so historical replay remains deterministic. - The exact
ai-dev-toolsfamily is the survivor when both family rows exist. Move
the orphaned cohorts to it by updating their family FK; preserve every Cohort
primary key/UUID,identifier,year, and all downstream foreign keys (modules,
units, homework, projects, campaigns, registrations, enrollments, submissions,
reviews, scores, certificates, and other history). - Rewrite only the exact duplicate prefix
ai-dev-tools-zoomcamp-toai-dev-tools-for retained cohort slugs, including
the 2026 row. Preserve unrelated legacy slugs and never derive an identifier from
a year during repair. - If the canonical row is absent but the reviewed duplicate is the only row, reuse
that row/primary key under the canonical slug rather than creating a second row.
A healthy one-family database is a no-op. - Adopt source provenance onto the surviving family only under the reviewed identity
rule. Conflicting non-null source IDs, source content IDs, cohort identifiers,
years, destination slugs, or incompatible existing cohorts fail closed with a
bounded diagnostic. The transaction must leave Course/Cohort and all dependent
state unchanged on such a conflict (apart from a safe failure/audit record); it
must never discard or silently choose between learner/history records. - Delete a duplicate family only after all cohort references have moved and the
reference/integrity check passes. The repair must be safe to invoke/replay twice
and must report redacted aggregate counts and conflict reasons only.
Test-only rows
- Exact upstream fixture rows
fake-courseandfake-course-2are development/test
data, not public courses. If present in a review or production-like input, the
adopted public dataset must exclude them (or mark them non-public before the
catalogue query); the exclusion is explicit and exact, not a broadfake-*or
substring filter. - Keep those rows available to isolated characterization fixtures where useful, and
prove that they do not affect public family counts, routes, or migration totals.
Unclassified visible rows fail closed rather than being silently treated as test
data.
Freshness and provenance gate
- Every accepted import/repair report records the exact source repository, source
commit, manifest/checksum, migration leaf, and aggregate family/cohort counts.
No report, log, screenshot, or issue comment contains learner data, protected
exports, credentials, or raw source payloads. - Synthetic tests are not evidence that a deployed database is fresh. The eventual
operator gate must run against the actual rollout target, using aggregate-only
facts, and verify the course/cohort source was re-synced within the existing
migration runbook window (≤72 hours before the cutover gate), the accepted source
commit/provenance is current, and no failed/stale import is active. - The operator must verify after the exact release is deployed that
/courseshas
one AI Dev Tools family, that both 2025 and 2026 cohorts are under it, that
/courses/ai-dev-tools/2026is the canonical200, and that the unrecorded
ai-dev-tools-zoomcampfamily path is a real404with no redirect chain. A
failed check holds rollout and triggers re-freeze/reconciliation; it is not
waived as a pre-existing data condition.
Acceptance criteria
- The checked-in reviewed mapping/generator emits the canonical AI Dev Tools
source_stable_id, family slug, cohort slug, and route identifier above on every
regeneration; the repository name remains provenance only. - Fresh and repeated imports produce one
Coursefamily (ai-dev-tools) with
the 2025 and 2026 cohorts under it, and cannot create theai-dev-tools-zoomcamp
sibling or silently group an unmapped divergent source. -
Course.slug,Cohort.slug,Cohort.identifier, andCohort.yearare kept
distinct in importer, seed, URL, and compatibility tests; the canonical route
uses the family slug plus identifier and does not infer identity from the year. - The repair migration/helper handles healthy, canonical-only, duplicate-only,
and split-family databases idempotently; it preserves cohort UUID/PKs and every
downstream FK, re-prefixes only the reviewed duplicate cohort slug, and removes
no learner/history/curriculum row. - Source/provenance, cohort identifier/year/slug, and same-year/same-identifier
collisions fail atomically with bounded redacted diagnostics; a second invocation
after success is a no-op with the same safe aggregate result. - Exact
fake-courseandfake-course-2fixture rows cannot enter the adopted
public catalogue, while real/unclassified rows are not hidden by a broad pattern;
this behavior is covered by import and public-count tests. - The compatibility evidence remains unchanged: no redirect is added for an
application path absent from the preservation baseline, and the separate docs,
FAQ, blog, and media-zoomcamppaths remain byte/route-compatible. - Migration-forward, apply-twice, rollback/abort, backend-portability, source
regeneration, importer replay/conflict, route/404, FK-integrity, and PII/log
canary tests pass on one frozen candidate with no migration drift. - Because the catalogue and course pages visibly change, the independent tester
runs the graph-selected Playwright tier and captures/inspects desktop and mobile
screenshots for/courses, the family page, and the 2025/2026 cohort paths. - [HUMAN] After the exact release is deployed, the named operator records the
aggregate freshness/provenance and route checks above. A synthetic/local pass is
not substituted for this gate; the issue remains open with thehumanlabel until
it is recorded. - PM accepts the fresh independent tester report and the human operator evidence
before the engineer commits; the orchestrator then performs the prescribed local
merge/push and on-call observes CI/deployment.
Dependencies and delivery status
- #14, #15, and #16 are resolved inputs. #224 owns the separate pre-2024 mapping and
removal of liveCohort.save()regex inference; this issue must not duplicate or
widen that work. - #307 is the separate homepage projection/read-source defect. This issue does not
regeneratecontent/public_projection/courses.jsonor change homepage ownership. - #51/#53/#60 own the broader Course/Cohort migration, lifecycle/route platform, and
full-fidelity rehearsal. This issue supplies only the bounded family-identity
repair and its handoff; downstream candidates must consume its exact merge SHA. - Dispatch still waits for the release-recovery lane to provide one clean, green,
exact current-main base. Existing branch-local proposals are not engineer,
tester, or PM evidence and must be reconstructed from that frozen base.
Explicit non-goals
No CMP source-pin update; no full Course → Cohort structural migration; no change to
canonical route grammar; no DNS/redirect-Lambda activation; no redirect for a path not
present in the preservation evidence; no docs/FAQ/blog/content slug rewrite; no
production/protected-data import or live database mutation during engineering/testing;
no learner/history export; no removal of local test fixtures; no unrelated frontend
redesign; and no commit, merge, push, or deployment before the required lifecycle gates.
Required scenarios
- Parse a repository whose declared slug is
ai-dev-tools-zoomcamp, regenerate its
manifest, and prove the canonical source/family/cohort identity and repository
provenance are all retained separately. - Import into an empty database, a database with canonical 2025 data, a database
with only the duplicate 2026 family, and a database already split across both;
replay each exact input and compare safe counts, IDs, and checksums. - Seed a complex duplicate cohort with modules, units, homework, projects,
campaign/registration, enrollment, submission/history, and source provenance;
repair it and prove every dependent PK/FK and cohort identifier survives. - Exercise duplicate same-year/identifier/slug, source-identity, missing-canonical,
unmapped-source, and injected-transaction-failure cases; assert no partial writes. - Include exact
fake-course/fake-course-2, a real-looking unclassified course,
and normal*-zoomcampfamilies; prove only the reviewed fixture rows are excluded
and existing canonical families are unchanged. - Verify
/coursesfamily grouping, family/cohort navigation, canonical metadata,
query preservation, loser-path404behavior, and absence of a redirect chain. - Run the redacted source/migration/freshness report and the post-release human
aggregate route/provenance gate without accessing protected learner data.
Lifecycle
This is now groomed but remains an operationally held P0 lane until a clean current-main
reconstruction base exists. Engineering leaves an uncommitted frozen candidate, an
independent tester recomputes and verifies the complete plan, and PM accepts only a
tester PASS plus the required human evidence. Only then may the engineer commit with
Closes #308; the orchestrator locally merges/pushes and on-call observes the resulting
CI/deployment. No pull request is created.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read _docs/PROCESS.md and Specifications 04, 09, and 10 first, then locate the checked-in mapping/generator, Course/Cohort importer, and migration-owned repair entry point. Use compatibility/generated-path-baseline.jsonl as the route-compatibility reference. Done means idempotent identity repair, preserved foreign keys, fail-closed conflicts, passing migration/import/route/integrity tests, and the required rollout evidence.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, python
- Domain
- backend, databases, devops, testing-qa, web-dev
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100