DataTalksClub / DataTalksClub/website
Public course projection is stale: 2026 cohorts exist in the database but the site advertises 2025
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Groomed by the product manager. Raw intake preserved below under "Original report".
Summary
The public homepage advertises 2025 cohorts for Machine Learning, LLM and AI Dev Tools while the
database holds live 2026 cohorts for all three. The cause is a source-of-truth deviation, not stale
data: core/home_content.py and content/review_views.py read checked-in JSON projections for
course facts, while courses.Cohort is the specification-mandated owner of that data.
Owner direction: "We don't read projections. It must come from the database."
This is spec-conforming, not a spec amendment. No specification change is required and none is
in scope.
Normative basis
_docs/specs/README.mdFixed requirements: "Django serves the public site from validated,
versioned database read models."_docs/specs/01-platform-architecture.md"Data ownership": courses, cohorts, teaching teams,
course registrations, enrollments, assignments… are Database-owned and editable through
Studio/API. Courses appear nowhere in the GitHub-owned list._docs/specs/04-courses-and-cohorts.mdis entirely a database model specification. It never
mentions a course projection._docs/architecture/app-boundaries.md:coursesowns "database-owned courses, cohorts, and
learner workflows";contentowns "versioned GitHub-owned read models".
Reading content/public_projection/courses.json for public course display is therefore a deviation
from specifications 01 and 04.
Ownership classification (settles the #276 hand-off)
#276 explicitly declines to own this and asks for a classification: "The manifest must state which
course-owned surfaces, if any, are direct-sync content rather than course-domain reads."
Classification: every surface listed in Scope is course_domain. None of them is editorial
content and none of them is direct-sync content. The work is owned here. If the owner disagrees
with that classification, this issue closes as won't-fix and the surfaces return to #276.
Verified root cause
core/home_content.py:258 is the only runtime reader of public_projection()["courses"]:
latest = _latest_cohort_records(tuple(public_projection()["courses"]))
The artifact holds 12 cohorts pinned to
DataTalksClub/course-management-platform@98a235283904b4ef9ad29e196298540756cf1bcc (2026-08-04).
The current course dataset holds 15 cohorts across 7 course families.
Verified against the current 15-cohort dataset:
Family (DB Course.slug) |
Homepage link today (projection) | Homepage link required (database) |
|---|---|---|
de-zoomcamp |
/courses/de-zoomcamp/2026 |
/courses/de-zoomcamp/2026 |
ml-zoomcamp |
/courses/ml-zoomcamp/2025 |
/courses/ml-zoomcamp/2026 |
llm-zoomcamp |
/courses/llm-zoomcamp/2025 |
/courses/llm-zoomcamp/2026 |
mlops-zoomcamp |
/courses/mlops-zoomcamp/2025 |
/courses/mlops-zoomcamp/2025 |
sma-zoomcamp |
/courses/sma-zoomcamp/2025 |
/courses/sma-zoomcamp/2025 |
| AI Dev Tools | /courses/ai-dev-tools/2025 |
/courses/ai-dev-tools-zoomcamp/2026 |
The URL grammar does not change. Cohort.identifier is "2026", Course.slug is the family,
and reverse("course", args=[family.slug, cohort.identifier]) reproduces exactly the
/courses/<family>/<identifier> shape the projection already emits. No route, no redirect, and no
preserved path is touched.
Reuse, do not rewrite
courses/views/course_list.py:76 visible_course_list_queryset() already serves /courses from
the ORM with homework_count, project_count and learner_count annotations — exactly the fields
core/home_content.py:265-275 builds from JSON. Reuse it. templates/core/home.html:1058-1065
consumes only .label, .title, .cohort_label and .public_path, and
templates/core/home.html:1028-1029 consumes .homework_count and .project_count; all are
available from that queryset, so the catalogue template needs no structural change.
Scope
1. core/home_content.py course_catalog() — the live defect
Rebuild CatalogCourse construction on visible_course_list_queryset():
- one card per visible
Coursewhose newest visibleCohortis selected by
(year, start_date, id)descending; public_path=reverse("course", args=[course.slug, cohort.identifier]);cohort_label=f"{cohort.year} cohort"(unchanged copy);homework_count/project_countfrom the existing annotations.
COURSE_FAMILIES (core/home_content.py:18-49) stops being the list of courses that exist. It may
remain only as a presentation table supplying the short chip label and the preferred display
order. The database decides which families exist; a family the database holds but the table does
not must still render, using its Course.title as the chip label. An allowlist that silently drops
a new upstream family would reproduce this same staleness bug in a new place and is not acceptable.
core/home_content.py:263 currently raises ImproperlyConfigured when a family is missing. A
partially populated database would therefore return 500 on the homepage. It must instead render
an empty catalogue, matching the existing /courses empty state.
2. core/views.py:73 — the featured-cohort hero
review_projection()["course"] supplies the homepage hero's course identity, cohort title, start
date and description. Move the facts to the database; keep the editorial copy page-owned.
- From the database: course title, cohort title, cohort year, cohort start date, cohort public path,
homework/project counts. - Page-owned constants in
core/home_content.py(same pattern as the existing
FEATURED_BUILD_ITEMS,FEATURED_GROUP_NOTE,MEMBER_STORIES): the hero summary,format,
priceandnoticestrings.Cohorthas noformat,priceornoticefield, and
Cohort.descriptionfor the 2026 rows is generated boilerplate
("The 2026 live delivery of AI Dev Tools Zoomcamp.") whileCourse.descriptionfor
ai-dev-tools-zoomcampis raw README markup containing external image tags and
courses.datatalks.clublinks. Reading either into the hero would be a copy regression and a
markup-injection risk. Move today's exact strings into page constants; do not change a word. - Adding
format/price/noticemodel fields is a schema change and is a non-goal here. core/views.py:87-89usesnext(entry for entry in catalog if entry.family == FEATURED_FAMILY),
which raisesStopIteration(500) when that family is absent. It must fail soft.
3. content/review_views.py:379 course_cohort — the cohort page
Same split: cohort title, start date and the linked platform cohort come from courses.Cohort;
format, price and notice stay page-owned constants with today's exact strings.
content/review_views.py:382 currently does
Cohort.objects.filter(slug=cohort["legacy_platform_slug"]).first() with
legacy_platform_slug = "ai-dev-tools-2026". No cohort with that slug exists — the dataset holds
ai-dev-tools-2025 and ai-dev-tools-zoomcamp-2026 — so legacy_course is None and the
"Open the existing course workspace" call to action (templates/review/course_cohort.html:163-169)
is silently missing today. Resolving the cohort from the database fixes that link.
4. content/review_views.py:399 registration_preview
The cohort title in the heading and breadcrumb comes from the database.
5. content/public_views.py:1200 sitemap — verification only
The courses sitemap section already reads Cohort.objects.filter(visible=True, course__visible=True).
It is the proof that the pattern exists and needs no change. It must be covered by a regression
assertion that no course entry in the sitemap derives from courses.json.
6. Documentation corrections (in scope)
courses/services/local_course_seed.py:8-15claims "Specification 03 keeps public requests on
that projection." Specification 03 is GitHub content and people; it says nothing about the CMP
course catalogue. This over-claim is the most likely reason the deviation went unchallenged.
Correct it._docs/runbooks/local-course-content-review.md:193-195states "the public homepage does not read
this database at all… Refreshing the review database therefore cannot by itself change what the
homepage advertises." That becomes false with this change. Update it.
7. Tests that assert the current premise
These fail or become meaningless and must be rewritten, not deleted:
core/tests/test_homepage.py:46test_root_uses_the_shared_course_platform_shellrenders/
against an empty database and asserts"AI Dev Tools Zoomcamp","Starts August 31"and the
featured-cohort link. It must build cohorts in the test database.core/tests/test_homepage.py:104test_single_destination_cards_stretch_their_existing_semantic_links
asserts acourse-cardexists in the rendered homepage.playwright_tests/test_course_design_parity.py:677
test_no_database_course_catalog_uses_the_design_system_empty_stateasserts
not Cohort.objects.exists(). It currently covers only/courses; with the homepage now
database-backed, the same premise must also assert that/renders 200 with an empty catalogue
rather than 500.
Design rules
- Course facts and identity always come from
courses.Course/courses.Cohort. - Course editorial copy that has no database field stays a page-owned constant with today's
exact wording. It is never invented and never read from a projection. - Only
visible=Truecohorts ofvisible=Truecourses reach a public surface. - No public URL string changes.
/courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026and
/courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026/registration-preview/keep their exact
paths and URL names; only their data source changes. - Every converted surface degrades to an empty state, never to a 500.
Blast radius (verified, and deliberately small)
- No preserved path is touched.
/courses/<family>/<year>appears nowhere in the 2,937-row
_docs/compatibility/generated-path-baseline.jsonl(the only/courses/*legacy row is
/courses/2021-winter-ml-zoomcamp.html). - Content digests stay green because
content/public_projection/courses.jsonstays in the tree,
byte-identical.EXPECTED_COUNTS["courses"] = 12(content/public_data.py:65),
PROJECTION_TREE_SHA256andPROJECTION_MANIFEST_SHA256
(content_sync/dtc_content/contract.py:32-34) all keep validating the unchanged artifact.
Leaving the artifact in place is an explicit requirement of this issue, not an oversight. content/review_projection.jsonlikewise stays in the tree; thecontent/apps.pystartup check
andREQUIRED_PUBLIC_PATHSkeep passing unchanged.
The verification trap — read this before testing
courses/services/local_course_seed.py:68-75 seeds the local database from the same pinned
scripts/production_like_course_specs.json@98a2352 that built courses.json. On a freshly seeded
local database, switching the reader changes nothing visible: the seed writes the same 12 cohorts,
so Machine Learning and LLM still resolve to 2025 and AI Dev Tools still resolves to
ai-dev-tools-2025. A verifier who checks only a seeded database will wrongly conclude the fix does
nothing.
Verification must therefore use a database that actually contains ml-zoomcamp-2026,
llm-zoomcamp-2026 and ai-dev-tools-zoomcamp-2026. Two documented paths produce one:
_docs/runbooks/local-course-content-review.md(CMP snapshot import), plus_docs/runbooks/local-course-modules-preparation.md, which is explicitly the flow that supplies
the LLM / ML / AI Dev Tools 2026 cohorts the snapshot does not contain.
Focused Django tests must construct cohorts directly rather than going through the pinned seed, so
they are independent of the pin.
Non-goals
- Regenerating
content/public_projection/courses.json.scripts/build_public_projection.py:2435
emits"public_path": f"/courses/{slug}"→/courses/de-zoomcamp-2026, while the checked artifact
holds/courses/de-zoomcamp/2026. The builder cannot reproduce its own committed output, and
/courses/de-zoomcamp-2026resolves tocourse_family_viewwith no matching family — a 404.
Regeneration would rewrite every homepage course link into a 404. This is recorded here as
evidence for #253 and is an explicit non-goal. - Deleting
courses.json,review_projection.json, or any projection artifact. - Moving the CMP pin, touching the adoption ledger (#297), or any upstream code sync.
- The
ai-dev-toolsvsai-dev-tools-zoomcampURL/redirect question./courses/ai-dev-tools/2025
keeps resolving; whether it should redirect is a separate URL-identity issue. - Adding
format/price/noticefields toCohort, or any other schema change or migration. - Editorial, wiki, docs, FAQ, people and event-content conversion — owned by #276.
- Projection media (#301).
- Adding
/courses/<family>/<year>cohort pages to the sitemap. The sitemap lists family paths only;
that is pre-existing and unchanged here. - Deduplicating the two AI Dev Tools
Courserows in the database (see Owner decisions).
Dependencies
None blocking. #253, #297, #301 and #302 are related but supply no model, interface, decision or
infrastructure prerequisite for this reader change, and this change deliberately does not touch the
artifacts they are red on. #276 is not a dependency; it explicitly defers this classification here.
Owner decisions requested (do not block implementation)
These are escalated rather than decided. The stated default ships if no direction arrives, because
the site is currently advertising superseded cohorts and this is P0.
- Two visible AI Dev Tools
Courserows. The database holdsai-dev-tools(title
"AI Dev Tools Zoomcamp", cohortai-dev-tools-2025) andai-dev-tools-zoomcamp(same title,
cohortai-dev-tools-zoomcamp-2026), bothvisible = 1./coursesalready renders two
identically-titled family cards because of this. A repair (merging the families) is a data
migration with URL consequences and is out of scope here.
Default for this issue: the homepage renders exactly one AI Dev Tools card and one
featured hero, resolved to the newest cohort across the AI Dev Tools family slugs, via a small
reviewed alias entry in thecore/home_content.pypresentation table. This is presentation-only,
changes no URL and no row, and is reversible./courseskeeps showing both cards until the data
is repaired; that inconsistency is knowingly accepted for the duration and must be filed as its
own issue. - Upstream test rows on a public surface.
_docs/runbooks/local-course-content-review.md:186-188
records that the CMP snapshot containsfake-courseandfake-course-2withvisible = 1, each
becoming its own visible family.courses/views/course_list.py:130already carries a
title.lower().startswith("fake")guard, which is a smell. Once the homepage is
database-backed, a database carrying those rows would advertise them on the front page.
Default: the reader gains no name-based denylist; it renders onlyvisible=Truerows, and
hiding upstream test rows is an operator data fix that must be added to the apex-swap
data-freshness gate in_docs/runbooks/production-hosting-and-dns-migration.md. Confirm that the
cutover database has no visible fixture course.
Acceptance criteria
Source of truth
- [AUTO]
core/home_content.pyno longer imports or callspublic_projectionfor course data,
andpublic_projection()["courses"]has no runtime reader anywhere in the application. - [AUTO]
core/views.pyandcontent/review_views.pyno longer readreview_projection()["course"]. - [AUTO]
content/public_projection/courses.jsonandcontent/review_projection.jsonare
unchanged byte-for-byte;EXPECTED_COUNTS["courses"] == 12,PROJECTION_TREE_SHA256and
PROJECTION_MANIFEST_SHA256are unchanged and still validate. - [AUTO] A regression test asserts that no course entry in
production_sitemap()derives from
courses.json.
Correct cohort selection
- [AUTO] Against a database containing
ml-zoomcamp-2025andml-zoomcamp-2026, the
homepage catalogue links Machine Learning to/courses/ml-zoomcamp/2026and shows
"2026 cohort"; the 2025 link does not appear in the catalogue. - [AUTO] The same holds for LLM Zoomcamp (
/courses/llm-zoomcamp/2026). - [AUTO] MLOps and Stock Markets, whose newest visible cohorts are 2025, still link to
/courses/mlops-zoomcamp/2025and/courses/sma-zoomcamp/2025. - [AUTO] Exactly one AI Dev Tools card and exactly one
data-featured-courseelement render,
resolved toai-dev-tools-zoomcamp-2026at/courses/ai-dev-tools-zoomcamp/2026. - [AUTO] A
visible=Falsecohort, and any cohort of avisible=Falsecourse, never reaches the
homepage catalogue, the hero, the cohort page or the registration preview. - [AUTO] A course family present in the database but absent from the
core/home_content.py
presentation table still renders, labelled from itsCourse.title.
Copy and counts
- [AUTO] Hero and cohort-page copy that has no database field (
format,price,notice, the
hero summary,FEATURED_BUILD_ITEMS,FEATURED_GROUP_NOTE) renders the same strings as
before this change, now from page-owned constants. - [AUTO] No raw markdown, HTML tag, external image reference or
courses.datatalks.clublink
fromCourse.descriptionorCohort.descriptionreaches any rendered page. - [AUTO] The featured panel handles a zero count:
ai-dev-tools-zoomcamp-2026has 4 homework
assignments and 0 projects, and the page must not render "0 projects". The projects clause
is omitted when the count is zero, and singular/plural is correct for a count of one. - [AUTO]
course_family_countandcourse_family_wordare derived from the rendered catalogue,
so the "One free account. Six courses." line cannot contradict the cards above it.
Empty and failure states
- [AUTO] With an empty database,
/returns 200 with an empty course catalogue and no
featured hero, and raises noImproperlyConfigured,StopIterationorNoReverseMatch. - [AUTO] With a database holding some but not all families,
/returns 200 and renders only the
families that exist. - [AUTO] With no AI Dev Tools cohort at all,
/,
/courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026and its
/registration-preview/return a designed empty or 404 state, never a 500.
URLs and compatibility
- [AUTO]
/courses,/courses/<family>,/courses/<family>/<year>,
/courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026and
/courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026/registration-preview/keep their
exact paths, URL names and status codes. - [AUTO] The preserved-path baseline check and the redirect/compatibility suites pass unchanged.
- [AUTO] Every course link rendered on
/resolves to a 200 on the same database.
Documentation
- [AUTO] The
courses/services/local_course_seed.pydocstring no longer claims specification 03
keeps public requests on the course projection. - [AUTO]
_docs/runbooks/local-course-content-review.mdno longer states that the homepage does
not read the database.
Verification integrity
- [AUTO] Focused Django tests build cohorts directly, not through
courses.services.local_course_seed, so they do not silently depend on the 98a2352 pin. - [HUMAN] The tester confirms the browser evidence was captured against a database containing
ml-zoomcamp-2026,llm-zoomcamp-2026andai-dev-tools-zoomcamp-2026, and records how that
database was produced. Evidence from a freshly seeded pinned database does not satisfy any
criterion in this issue.
Django / integration scenarios
- Empty database →
GET /returns 200, no course cards, no featured hero, no exception. - One family with 2024/2025/2026 visible cohorts → the catalogue shows one card for 2026 only.
- 2026 cohort marked
visible=False→ the card falls back to 2025 and links to
/courses/<family>/2025. - Course marked
visible=False→ the family disappears from the catalogue entirely. - Two cohorts in the same year → selection is deterministic by
(year, start_date, id). - A family in the database that is absent from the presentation table renders with its
Course.title. - Cohort with zero projects → the featured panel omits the projects clause.
production_sitemap()course entries are unchanged by this issue and independent of
courses.json.core/views.pyandcontent/review_views.pyimport graphs contain noreview_projection
course read.
Browser scenarios (desktop 1280×800 and mobile 390×844, light and dark)
/with the real 2026 dataset. The Machine Learning card reads "2026 cohort" and links to
/courses/ml-zoomcamp/2026; LLM reads "2026 cohort"; the AI Dev Tools hero shows the 2026 cohort
title and start date; exactly one AI Dev Tools card is present; the "Six courses" line matches the
number of cards. Follow the Machine Learning link and confirm a 200 course page for the 2026
cohort./with an empty database. The page renders its design-system empty state, not an error or a
debug page./courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026. Title, start date, format, price
and notice render as before, and the "Open the existing course workspace" call to action now
resolves to a real cohort instead of being silently absent./courses/ai-dev-tools-zoomcamp/cohorts/ai-dev-tools-2026/registration-preview/. Heading and
breadcrumb carry the database cohort title./coursesis visually and factually consistent with/for every family except the known,
escalated AI Dev Tools duplicate.- No horizontal overflow and no broken layout at either width.
Repository / operations scenarios
git statusshows no change tocontent/public_projection/orcontent/review_projection.json.- Content digest, manifest, inventory and tree-reproducibility checks pass unchanged.
- Migration-drift check reports no migration (this issue adds none).
- The versioned verification plan in
_docs/ci/change-selective-ci.mdis generated with recorded
base/head, graph and plan digests, and everyrerun/reused/skipped/not_applicable
disposition is justified. Screenshot components may not be markednot_applicable: this change
has render impact on/and on both review routes.
Why P0
_docs/runbooks/production-hosting-and-dns-migration.md has this site replacing datatalks.club.
Cutting over while the front page advertises superseded cohorts would send real prospective learners
to the wrong cohort. This must be resolved before the apex swap, and it is now known that refreshing
the database alone cannot fix it.
Related
#253 (projection not reproducible — this issue supplies the concrete public_path divergence as
evidence), #276 (editorial direct-sync cutover; defers course surfaces here), #297 (CMP adoption
ledger), #301 (projection media), #302 (inventory validator).
Original report (raw intake, preserved)
Raw user intake — needs PM grooming. Reported by the owner while reviewing the running site: "for ml zoomcamp there is actually a 2026 cohort — did we not sync all the data to the local db? Let us make sure that ALL THE DATA is up to date."
Symptom
The homepage advertises 2025 cohorts for courses that have open 2026 cohorts. Prospective learners would see and click through to a stale cohort.
Root cause
The homepage reads the checked-in projection, not the database — core/home_content.py:258 calls _latest_cohort_records(tuple(public_projection()["courses"])), and core/home_content.py:12 imports public_projection from content.public_data.
content/public_projection/courses.json holds 12 entries; the local database holds 15. Comparison:
| Course | Projection (rendered) | Database |
|---|---|---|
| Data Engineering | de-zoomcamp-2026 |
de-zoomcamp-2026 |
| Machine Learning | ml-zoomcamp-2025 |
ml-zoomcamp-2026 |
| LLM | llm-zoomcamp-2025 |
llm-zoomcamp-2026 |
| AI Dev Tools | ai-dev-tools-2025 |
ai-dev-tools-zoomcamp-2026 |
| MLOps | mlops-zoomcamp-2025 |
mlops-zoomcamp-2025 |
| Stock Markets | sma-zoomcamp-2025 |
sma-zoomcamp-2025 |
The projection is built from DataTalksClub/course-management-platform@98a235283904b4ef9ad29e196298540756cf1bcc, pinned 2026-08-04. Cohorts created upstream after that date are absent.
Note the AI Dev Tools slug also differs (ai-dev-tools-2025 vs ai-dev-tools-zoomcamp-2026), so this is a URL/identity question, not only a display label.
What grooming must decide
-
Which source is authoritative for public course display — the projection or the database? This is the core question. The projection exists for reproducibility and is bound to content-authority digests; the database is current. Today the homepage uses the projection and the seed command uses
scripts/production_like_course_specs.jsonpinned to the same revision, so a DB-only refresh would not fix the site. -
How does course data stay fresh going forward? A pinned projection guarantees reproducibility but guarantees staleness. Cohorts are created continuously upstream. Whatever is chosen must answer: when a new cohort opens upstream, what makes it appear on the site, and how quickly?
-
Moving the pin has known costs. A CMP upstream audit found a forward sync to
origin/mainisstatus=blockedwith 3 fatal errors and 45 overlay conflicts, plus four new migrations. That audit recommended NOT moving the pin for code. The course catalogue may be separable from the code sync — determine whether the catalogue can be refreshed independently of the adoption ledger. -
Regeneration blast radius. Rebuilding
courses.jsonchangesmanifest.jsondigests,tree_sha256, andEXPECTED_COUNTS(content/public_data.py:66pins"courses": 12). It touchesscripts/production_like_course_specs.json,courses/services/local_course_seed.py, and the parity contracts incontent_sync/dtc_content/. This interacts with #253 (projection reproducibility) and #302 (inventory validator drift) — both currently red onmain. Sequencing matters. -
AI Dev Tools slug change — if the canonical slug becomes
ai-dev-tools-zoomcamp-2026, does/courses/ai-dev-tools-2025need a redirect? Check against the 2,937-row preservation baseline.
Why this is P0
The migration runbook (_docs/runbooks/production-hosting-and-dns-migration.md) has this site replacing datatalks.club. Cutting over while advertising superseded cohorts would send real prospective learners to the wrong place. This should be resolved before the apex swap, not after.
Related
#253 (projection not reproducible after face8e4), #302 (inventory validator red), #297 (CMP adoption ledger, broken sync script), #301 (media out of the projection), #276 (cut public reads over to direct-sync authority) — #276 may be the strategic answer to question 2.
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
Start with visible_course_list_queryset() in courses/views/course_list.py and the projection reads in core/home_content.py, core/views.py, and content/review_views.py. Run the focused homepage, review, and Playwright tests after constructing database cohorts directly; done means public course surfaces use visible database records, preserve URLs and editorial copy, render empty states without 500s, and the specified documentation is corrected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- backend, database, documentation, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100