Transfer Challenge: Reading List Builder
This is harder than Lesson 4. In Lesson 4, you built the search feature that Emma walked you through in Lessons 1-3. This time, you build something you have never seen before. If TDG works, it works on anything. Prove it.
Emma pulls up a fresh whiteboard and writes:
SmartNotes users want a reading list. Given their notes and a time budget in minutes, build a prioritized list of notes they can read within that budget. Shorter notes come first (so users finish more notes in less time). The function should include notes until adding the next one would exceed the budget. Return both the selected notes and the total estimated reading time in minutes.
She does not set a timer this time. She does not close her laptop. She just says: "This is a different algorithm from search. Search filters and sorts by relevance. This accumulates and sorts by size. Same TDG method. Different problem. Go."
The Problem
Read the problem statement above. That is your entire input. No hints about:
- The function name
- The parameter names or types
- The return type
- The reading time calculation formula
- The edge cases
You extract all of that yourself.
Your deliverables:
| File | Purpose |
|---|---|
smartnotes_reading_list.py | Function stub with types and docstring |
test_smartnotes_reading_list.py | 8+ tests covering happy paths and edge cases |
tdg_journal.md | Append to your journal from Lesson 4 |
Your TDG Cycle
You know the steps. You have done them twice (once guided in Lesson 3, once semi-independently in Lesson 4). Now do them from memory.
Some questions to consider during specification:
- How do you calculate reading time from word count? (You wrote
reading_timefunctions in Ch 50.) - What should happen when the notes list is empty?
- What should happen when the budget is zero?
- What should happen when a single note exceeds the entire budget?
- What should the return type be? A tuple? A dataclass? Think about what the caller needs.
Some questions to consider during testing:
- Does the sorting actually put shorter notes first?
- Does the accumulation stop at the right moment?
- Is the reading time calculation correct for known word counts?
- Does the function handle the boundary case where the next note fits exactly?
After You Finish
Compare this experience to Lesson 4. In Lesson 4, you rebuilt a feature you had seen before. This time, you designed from scratch: you chose the function name, the parameter types, the return structure, and the edge cases. If both exercises produced green test suites and clean journals, then TDG is genuinely yours. The method works on any problem, not just the one you practiced.
PRIMM-AI+ Practice: A New Algorithm
Predict [AI-FREE]
Press Shift+Tab to enter Plan Mode.
Your reading list function works. Without running the code, predict:
- If you have 5 notes with word counts [500, 100, 300, 50, 200] and a budget of 3 minutes (at 250 words per minute), which notes make the list? In what order?
- What is the total reading time of the selected notes?
Write your predictions.
Check your prediction
Sorted by word count (shortest first): [50, 100, 200, 300, 500].
Reading times at 250 wpm: 0.2, 0.4, 0.8, 1.2, 2.0 minutes.
Accumulating: 0.2 + 0.4 = 0.6, + 0.8 = 1.4, + 1.2 = 2.6, + 2.0 = 4.6 (exceeds 3.0).
Selected: the first 4 notes (word counts 50, 100, 200, 300). Total: 2.6 minutes.
If your function uses math.ceil for reading time or a different words-per-minute rate, your numbers will differ. The key is whether the accumulation logic stops at the right place.
Run
Press Shift+Tab to exit Plan Mode.
Run the prediction inputs through your function. Compare actual output to your prediction.
Investigate
Your reading list function and your search function both take list[Note] and return a subset. But the algorithms are different: search filters by content matching; reading list sorts by size and accumulates within a budget.
Write one sentence explaining which algorithm is harder to specify and why. If you want to go deeper, run /investigate @smartnotes_reading_list.py in Claude Code and ask: "What is the time complexity of this function? Could it be made more efficient with a different sorting approach?"
Modify
Change the priority order: instead of shortest-first, add a parameter strategy: str that accepts "shortest" or "longest". Update your stub, add tests for both strategies, and re-generate.
Make [Mastery Gate]
Write one more function from scratch: daily_digest(notes: list[Note], max_notes: int) -> str that returns a formatted string summarizing the top max_notes notes by word count (longest first), showing title and reading time for each. In Claude Code, type /tdg to guide you through the cycle.
Try With AI
If Claude Code is not already running, open your terminal, navigate to your SmartNotes project folder, and type claude. If you need a refresher, Chapter 44 covers the setup.
Prompt 1: Compare My Two Implementations
I built two SmartNotes features using TDG:
1. search_notes (keyword matching with tag filter)
2. [your reading list function name]
Compare the two implementations. Which has better test
coverage? Which has more edge cases? Which docstring
is more precise?
What you're learning: You are comparing your own work across two different problems. This reveals your specification habits: do you write better docstrings for familiar problems? Do you test more edge cases when the algorithm is simpler?
Prompt 2: What Would Break at Scale?
Here is my reading list function:
[paste implementation]
If SmartNotes had 100,000 notes, what performance problems
would appear? Suggest one optimization that keeps the same
interface and passes the same tests.
What you're learning: You are thinking beyond correctness to scalability. The AI can identify sorting bottlenecks or memory issues that matter at scale, even though your test suite only uses 3-5 notes.
Prompt 3: Rate My TDG Transfer
I completed two TDG cycles back to back:
Cycle 1 (search_notes): Rebuilt a feature I had been walked through.
- [describe: how many iterations, what failed, how long it took]
Cycle 2 (reading list): Built a feature I had never seen before.
- [describe: how many iterations, what failed, how long it took]
Compare the two cycles. Was my specification better or worse
on the unfamiliar problem? Did I write more or fewer tests?
What does that tell me about my TDG habits?
What you're learning: You are evaluating whether the TDG method truly transferred. If your second cycle was slower but produced a better specification, the method is working. If it was faster but with weaker tests, you may be skipping steps under time pressure.
James leans back. Two features. Two green test suites. Two different algorithms. The TDG cycle worked identically on both: stub, test, generate, verify, debug, read.
"The search was about filtering," he says. "The reading list was about accumulating. Different algorithms, different edge cases, different return types. But the process was the same every time."
Emma nods. "That is the point. The method does not care what you are building. It cares that you specify before you generate and verify before you ship."
James looks at his SmartNotes project. Five files now: the Note model, the search feature, the reading list, and two test suites. "It is starting to feel like a real project," he says. "But it is all functions. Functions that take notes, functions that return notes. There is no structure holding it together."
"That is exactly the problem Phase 5 solves," Emma says. She writes on the whiteboard:
You have been specifying functions. But SmartNotes is growing. A list of functions is not an architecture. You need objects that model your domain: Notes with behavior, Collections that manage them. That is Phase 5: the Python object model.
James stares at the whiteboard. "So I stop writing standalone functions and start building structures?"
"Exactly. The dataclass you have been using for Note is the simplest form of it. Phase 5 gives it behavior: methods that belong to the note, not floating beside it. Your TDG cycle does not change. Your building blocks do."