Skip to content

feat(course): add mandatory Data Recall Checkpoint (L3→L4) - #69

Merged
michalmaj merged 2 commits into
mainfrom
feature/data-recall-checkpoint
Oct 2, 2026
Merged

michalmaj merged 2 commits into
mainfrom
feature/data-recall-checkpoint

Conversation

@michalmaj

Copy link
Copy Markdown
Owner

Summary

First element of the final NumPy/Pandas integration: a mandatory, standalone Data Recall Checkpoint placed between Level 3 and Level 4. Levels 1–7 remain completely untouched — no existing mission, project, or checker was modified.

  • Placement: after Level 3 (the first point where CSV + JSON + repository pattern + validation all exist together, in exactly the shape Pandas/NumPy consume).
  • Scope: 30–60 minutes, zero new concepts/libraries — pure retrieval + application of Level 1 NumPy/Pandas skills on Level 3's real combat log schema.
  • Four tasks: load_combat_log, win_rate_by_class (groupby is an explicit learning objective), damage_stats (NumPy mean/std/p75/boolean mask, attack-only), rank_monsters_by_difficulty.
  • Checker: behavioral, dual-fixture (primary + variant with a genuinely different win-rate ranking and damage distribution), ground truth computed independently with stdlib csv — never by re-running the student's pandas/numpy code. Two light technique gates (AST check for groupby, monkeypatch spy for np.percentile) catch the "library imported ceremonially, real work done in plain Python" class of fake, since those two APIs are explicit learning objectives here, not just a means to a number.
  • Fixtures: generated from real, Pydantic-validated CombatLogRow instances imported directly from the frozen Level 3 Mission 06 task.py — not hand-typed numbers.
  • Course flow: COURSE_MAP.md / README.md document it between Level 3 and Level 4; tools/course_status.py tracks its completion the same way as other mandatory units (new print_checkpoint() helper, since it's a flat unit with no missions/projects subdirectory); tools/author_check.py verifies its required files and is part of the existing doc-coverage sweep.

Explicitly not in this PR

The Final Analytics Epilogue (after Level 7) — separate, future batch. No dead links added for it; README only mentions in prose that data skills are tied together again later.

Test plan

  • Scaffold → FAILs cleanly (load_combat_log is not implemented yet)
  • Reference solution → PASS on both fixtures, all 4 tasks, both technique gates
  • Alternative-valid solution (different chaining: .query/.agg/named aggregation/np.asarray/np.greater/np.count_nonzero) → PASS
  • Fake: hardcoded answers (including one that exactly matches the primary fixture) → FAIL on variant fixture
  • Fake: groupby avoided via a per-class Python loop (numerically correct) → FAIL at the technique gate
  • Fake: np.array created ceremonially, real stats computed in plain Python (values coincidentally correct) → FAIL at the np.percentile spy
  • Fake: only the first half of the dataset analyzed → FAIL on values
  • Deterministic across 3 repeated runs
  • Data correctness: 42 battles each fixture, exactly one terminal row per battle, 3 hero classes, 4 monsters, both wins/losses, both attack/potion turns, variant genuinely flips the hardest/easiest monster ranking and damage mean/std
  • tools/author_check.py → 737 checks passed, 0 errors
  • tools/course_status.py → checkpoint shows between L3/L4, "next up" resolves correctly, flips to [✓] after a real passing run
  • git diff confirms zero changes under any level_1...level_7 directory
  • No __pycache__, no leftover .progress, no temporary fixture-generator script committed; working tree clean except the intended patch

🤖 Generated with Claude Code

New mandatory, standalone checkpoint between Level 3 and Level 4 —
30-60 minutes, no new concepts, reactivating NumPy/Pandas from Level 1 on
the real combat log schema Level 3 Mission 06 introduced
(CombatLogRow: battle_id, turn, hero_name, hero_class, monster, action,
damage_dealt, damage_taken, hero_hp, monster_hp, result).

Four tasks: load_combat_log (real pd.DataFrame), win_rate_by_class
(terminal rows grouped by hero_class — groupby is an explicit learning
objective here, not just a means to a number), damage_stats (NumPy
mean/std/p75/boolean-mask over attack-only damage, excluding potion-turn
zeros which aren't weak hits), and rank_monsters_by_difficulty (same
win-rate idea, grouped by monster, sorted).

check.py runs every function against two fixtures and compares values
against ground truth computed independently with stdlib csv + plain
Python (never by re-running the student's own pandas/numpy code). The
variant fixture has a genuinely different win-rate ranking and damage
distribution, so hardcoded answers that happen to match the primary
fixture fail on the variant. Because groupby and NumPy array work are
explicit learning objectives (not just "get the right number"), two
functions also get a light technique gate: an AST check that groupby is
actually called inside win_rate_by_class, and a monkeypatch spy
confirming np.percentile is actually called inside damage_stats — neither
cares about variable names, chaining style, or lambda usage.

Both fixtures are generated from real, Pydantic-validated CombatLogRow
instances (imported directly from the frozen Level 3 Mission 06 task.py),
not hand-typed CSV rows — 42 battles each, 3 hero classes, 4 monsters,
both wins and losses, both attack and potion turns.

Verified: reference solution and an alternative-valid solution (different
chaining, .query/.agg/np.asarray/np.greater/np.count_nonzero style) both
PASS; four fakes (hardcoded answers incl. one that exactly matches the
primary fixture, groupby avoided via a per-class loop, np.array created
ceremonially with real work done in plain Python, analysis of only the
first half of the dataset) all FAIL at the gate they're meant to trip;
scaffold FAILs cleanly; deterministic across 3 repeated runs.
- COURSE_MAP.md / README.md: document the new mandatory checkpoint
  between Level 3 and Level 4. No link to a Final Analytics Epilogue —
  that doesn't exist yet; README only notes in prose that data skills get
  tied together again later, after Level 7.
- tools/course_status.py: new print_checkpoint() helper (the checkpoint
  is flat — one README/task.py/check.py, no missions/projects
  subdirectory — so it doesn't fit print_level()'s path convention) wired
  in between Level 3 and Level 4. Reuses the existing "projects" progress
  bucket so completion is tracked the same way other mandatory units are;
  existing levels' print_level() calls and path semantics are untouched.
- tools/author_check.py: new check_data_checkpoint() verifies the
  checkpoint's required files and check.py hygiene, and it's added to the
  existing COURSE_MAP.md/README.md documentation-coverage sweep.

Verified end-to-end: course_status.py correctly shows the checkpoint
between Level 3 and Level 4, correctly resolves "next up" to it once L1-L3
are complete, and correctly flips to [✓] after a real passing check.py run
(confirmed via a temporary reference solution, then reverted to scaffold).
author_check.py passes with the new checks included (737 checks, 0
errors). No changes to any level_1...level_7 file.
@michalmaj
michalmaj merged commit 27e58a2 into main Oct 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant