You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.jsonl contains no application path
for /courses/ai-dev-tools or /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 200 for /ai-dev-tools-2026/ and 404 for /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 emit source_stable_id=ai-dev-tools while retaining the repository
owner/name for provenance.
Resolve the repository-declared ai-dev-tools-zoomcamp course slug to the one
reviewed family and the ai-dev-tools-2026 cohort 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 CMP main or 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-tools family 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- to ai-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-course and fake-course-2 are 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 broad fake-* 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 /courses has
one AI Dev Tools family, that both 2025 and 2026 cohorts are under it, that /courses/ai-dev-tools/2026 is the canonical 200, and that the unrecorded ai-dev-tools-zoomcamp family path is a real 404 with 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 Course family (ai-dev-tools) with
the 2025 and 2026 cohorts under it, and cannot create the ai-dev-tools-zoomcamp
sibling or silently group an unmapped divergent source.
Course.slug, Cohort.slug, Cohort.identifier, and Cohort.year are 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-course and fake-course-2 fixture 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 -zoomcamp paths 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 the human label 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.
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 *-zoomcamp families; prove only the reviewed fixture rows are excluded
and existing canonical families are unchanged.
Verify /courses family grouping, family/cohort navigation, canonical metadata,
query preservation, loser-path 404 behavior, 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.
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.mdAccepted identity contract
The owner decision in the issue comment is authoritative:
Course.slugai-dev-toolsCohort.slugai-dev-tools-2026Cohort.identifier2026Course; it is not inferred from a slug at request timeCohort.year2026source_stable_idai-dev-toolsDataTalksClub/ai-dev-tools-zoomcampThe accepted canonical HTML route is
/courses/ai-dev-tools/2026(the acceptedno-
cohorts/syntax from #16). The legacy CMP routehttps://courses.datatalks.club/ai-dev-tools-2026/remains the observedcompatibility path. Existing
/docs/.../ai-dev-tools-zoomcamp/..., FAQ, and blogpaths belong to other source contracts and are out of scope.
For existing published families whose canonical slug really includes
-zoomcamp(for exampleml-zoomcamp), this issue does not rename them. The ruleis 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 pathfor
/courses/ai-dev-toolsor/courses/ai-dev-tools-zoomcamp; its AI Dev Toolsrows are docs, FAQ, blog, and media contracts.
200for/ai-dev-tools-2026/and404for/ai-dev-tools-zoomcamp-2026/oncourses.datatalks.club.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
only in the gitignored
.local/scratch manifest. A regenerated AI Dev Toolsmanifest must emit
source_stable_id=ai-dev-toolswhile retaining the repositoryowner/name for provenance.
ai-dev-tools-zoomcampcourse slug to the onereviewed family and the
ai-dev-tools-2026cohort slug. Preserve source contentIDs and checksums; do not make a source identity claim from a title, year, or
repository-name suffix alone.
same explicit mapping. A source/content identity collision, source-stable-ID
conflict, or unmapped divergent source rejects before any database mutation.
DataTalksClub/course-management-platform@98a235283904b4ef9ad29e196298540756cf1bcc.This issue is not permission to sync CMP
mainor port unrelated upstream code.Existing-database repair
Course/Cohort schema. The migration identity rule is frozen independently of
mutable runtime catalog code so historical replay remains deterministic.
ai-dev-toolsfamily is the survivor when both family rows exist. Movethe 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).
ai-dev-tools-zoomcamp-toai-dev-tools-for retained cohort slugs, includingthe 2026 row. Preserve unrelated legacy slugs and never derive an identifier from
a year during repair.
that row/primary key under the canonical slug rather than creating a second row.
A healthy one-family database is a no-op.
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.
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
fake-courseandfake-course-2are development/testdata, 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 broad
fake-*orsubstring filter.
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
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.
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.
/courseshasone AI Dev Tools family, that both 2025 and 2026 cohorts are under it, that
/courses/ai-dev-tools/2026is the canonical200, and that the unrecordedai-dev-tools-zoomcampfamily path is a real404with no redirect chain. Afailed check holds rollout and triggers re-freeze/reconciliation; it is not
waived as a pre-existing data condition.
Acceptance criteria
source_stable_id, family slug, cohort slug, and route identifier above on everyregeneration; the repository name remains provenance only.
Coursefamily (ai-dev-tools) withthe 2025 and 2026 cohorts under it, and cannot create the
ai-dev-tools-zoomcampsibling or silently group an unmapped divergent source.
Course.slug,Cohort.slug,Cohort.identifier, andCohort.yearare keptdistinct in importer, seed, URL, and compatibility tests; the canonical route
uses the family slug plus identifier and does not infer identity from the year.
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.
collisions fail atomically with bounded redacted diagnostics; a second invocation
after success is a no-op with the same safe aggregate result.
fake-courseandfake-course-2fixture rows cannot enter the adoptedpublic catalogue, while real/unclassified rows are not hidden by a broad pattern;
this behavior is covered by import and public-count tests.
application path absent from the preservation baseline, and the separate docs,
FAQ, blog, and media
-zoomcamppaths remain byte/route-compatible.regeneration, importer replay/conflict, route/404, FK-integrity, and PII/log
canary tests pass on one frozen candidate with no migration drift.
runs the graph-selected Playwright tier and captures/inspects desktop and mobile
screenshots for
/courses, the family page, and the 2025/2026 cohort paths.aggregate freshness/provenance and route checks above. A synthetic/local pass is
not substituted for this gate; the issue remains open with the
humanlabel untilit is recorded.
before the engineer commits; the orchestrator then performs the prescribed local
merge/push and on-call observes CI/deployment.
Dependencies and delivery status
removal of live
Cohort.save()regex inference; this issue must not duplicate orwiden that work.
regenerate
content/public_projection/courses.jsonor change homepage ownership.full-fidelity rehearsal. This issue supplies only the bounded family-identity
repair and its handoff; downstream candidates must consume its exact merge SHA.
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
ai-dev-tools-zoomcamp, regenerate itsmanifest, and prove the canonical source/family/cohort identity and repository
provenance are all retained separately.
with only the duplicate 2026 family, and a database already split across both;
replay each exact input and compare safe counts, IDs, and checksums.
campaign/registration, enrollment, submission/history, and source provenance;
repair it and prove every dependent PK/FK and cohort identifier survives.
unmapped-source, and injected-transaction-failure cases; assert no partial writes.
fake-course/fake-course-2, a real-looking unclassified course,and normal
*-zoomcampfamilies; prove only the reviewed fixture rows are excludedand existing canonical families are unchanged.
/coursesfamily grouping, family/cohort navigation, canonical metadata,query preservation, loser-path
404behavior, and absence of a redirect chain.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 resultingCI/deployment. No pull request is created.