Session Protocol
Run this at the start of every session, before new material
01
Paste your code
Open with "Day X — here's what I built" and paste the script. No code = no session.
02
Explain it back
I pick 2–3 lines and ask "what does this do?" Answer in plain English. If you can't, we stay there.
03
Modify it live
I give a small change task. You do it without help, paste the result.
04
Break and fix
I name an error to introduce. You make it, read the message, diagnose it, fix it.
If steps 02–04 don't pass: we debug the gap, not move forward. This is the only way the evaluation threshold actually builds.
How to open every session: "Day X. Yesterday I worked on [topic]. Here's what I built:" then paste your code. That single message triggers the full protocol.
Week 1 · 12–18 May
Variables, lists, loops, conditionals, dictionaries
The building blocks. By the weekend you can write a script that stores structured data, loops through it, applies logic, and prints formatted output without help.
Monday
12 May
2 hrs
Variables + first script
Naming values · f-strings · running Python
▼
Build
Test
Report
Read the Variables section of the Python Primer — 10 minutes. Then create
hello.py in ~/code/mollerbeck/. Write 5 variables: client name (string), employees (integer), industry (string), revenue (float), active (boolean). Print all five using f-strings. Run it. Then break it deliberately — delete a quote, mistype a variable name — read the error, fix it.✓ Script runs · 5 formatted lines print · broken and fixed once
Paste
hello.py at session open. I will ask:Explain it
Point to your f-string line. What is the f for? What do the curly braces do?
Answer in one sentence. "It lets me embed the variable X directly into the string" is sufficient.
Modify it
Add a sixth variable — a list of three contact names at the company. Print it.
You haven't formally learned lists yet. Figure it out. The point is whether you can extend code you've written.
Break and fix
Remove the closing quote from one string. What error? What line does it point to — and why might it be one line off?
SyntaxError. Python only discovers the problem when it runs out of string — which may be the line after.
Gate: if you can't explain what an f-string does in plain English, we stay on Day 1 material.
End-of-session report — paste this into chat
Day 1 done.
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Tuesday
13 May
2 hrs
Lists + loops
Collections · for loops · enumerate · filtering
▼
Build
Test
Report
Read the Lists and Loops section. Create
clients.py. Build a list of 5 client names. Write a for loop that prints each. Add enumerate so it prints "Client 1: Vestas" etc. Then filter: only print clients with names longer than 5 characters. Run after each change — don't write everything then run once.✓ Numbered list prints · filter works · ran after each addition
Paste
clients.py. I will ask:Explain it
What does
enumerate do? Why do you need it to print a number alongside each item?Without it you'd need a separate counter variable. Enumerate gives you index and value together.
Modify it
Change the loop so it only prints clients at even index positions (0, 2, 4).
You'll need the modulo operator
%. Figure it out.Break and fix
Remove the colon after your
for statement. What error do you get?SyntaxError. This is one of the most common errors in Python. Recognise it instantly.
Gate: can you write a for loop from memory without looking anything up? If not, repeat today before moving to conditionals.
End-of-session report
Day 2 done.
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Wednesday
14 May
2 hrs
Conditionals
if / elif / else · comparison operators · logic in loops
▼
Build
Test
Report
Read the Conditionals section. Extend
clients.py — add an employee count to each client entry. Loop through and print different output by size: under 200 = "Small", 200–500 = "Mid-size", over 500 = "Large". Then add a second condition: flag any client in the "Energy" industry with a note. You're writing logic that resembles real advisory segmentation.✓ All three size bands print correctly · industry flag works
Paste updated
clients.py. I will ask:Explain it
What is the difference between
== and =? Where does each appear in your code?One assigns, one compares. Confusing them is one of the most common beginner errors.
Modify it
Add a condition: if a client is both Large AND in Energy, print "Priority client".
You'll need the
and operator combining two conditions.Break and fix
Change one
== to = inside an if statement. What happens?SyntaxError — Python doesn't allow assignment inside a condition.
Gate: write an if/elif/else block from memory. If you have to look up the syntax, repeat today.
End-of-session report
Day 3 done.
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Thursday
15 May
2 hrs
Dictionaries
Key-value pairs · accessing keys · list of dicts
▼
Build
Test
Report
Read the Dictionaries section. Rewrite
clients.py so each client is a dictionary with keys: name, industry, employees, active. Store all clients in a list of dictionaries. Loop through and print a formatted summary for each. Apply the size and industry conditions from Day 3 — now reading from dictionary keys. This is the data structure your Phase 1 project runs on.✓ Each client dict prints a formatted summary with size and industry logic applied
Paste
clients.py. I will ask:Explain it
What happens if you try to access a key that doesn't exist in the dictionary? What error?
KeyError. This is the most common error you'll hit reading CSV data. Know it cold.
Modify it
Add a
revenue key to each dict. Print a different message if revenue is above or below 100.Break and fix
Misspell one key name —
client['Employees'] instead of client['employees']. What error? Why?KeyError. Python is case-sensitive. This will happen constantly with CSV column names later.
Gate: can you build a list of dictionaries and loop through it from memory? This is the foundation of the Phase 1 project. Don't leave it shaky.
End-of-session report
Day 4 done.
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Built: [what you made]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Error I hit: [paste if relevant]
Weekend
17–18 May
6 hrs
Functions + first CSV read
def · parameters · return · csv.DictReader
▼
Build
Test
Report
Part 1 (2 hrs) — Functions: Read the Functions section. Refactor
Part 2 (4 hrs) — CSV: Create
clients.py so formatting logic lives in a function called format_client(client) that takes a dict and returns a string. Call it from your loop. Output should be identical — you're reorganising structure, not changing behaviour.Part 2 (4 hrs) — CSV: Create
clients.csv with columns: Name, Industry, Employees, Active. Add 5 rows. Write summariser.py that reads it with csv.DictReader and calls format_client(row) for each row. This is the skeleton of the Phase 1 project.✓ CSV reads correctly · format_client called per row · formatted output for all 5 clients
Paste both files. I will ask:
Explain it
What does
return do in your function? What happens if you remove it?Without return, the function produces None. Printing None is a common confusing output.
Explain it
What is DictReader doing differently from a regular CSV read? Why does it make accessing columns easier?
It gives each row as a dict keyed by column header. You access by name, not position.
Modify it
Add a second function
is_priority(client) that returns True if employees > 500. Call it in the loop and print a flag next to priority clients.Weekend gate: biggest step of Week 1. If functions or CSV reading feel unclear, flag it explicitly in your report — don't paper over it. Week 2 builds directly on this.
End-of-weekend report
Weekend done.
Functions feel: [solid / shaky / unclear]
CSV reading feels: [solid / shaky / unclear]
Biggest sticking point: [be specific]
Functions feel: [solid / shaky / unclear]
CSV reading feels: [solid / shaky / unclear]
Biggest sticking point: [be specific]
Week 2 · 19–23 May
Writing files, error handling, and shipping the real project
You can read real data. This week you write to files, handle failures gracefully, and by Friday you have a working briefing summariser you'd actually use.
Monday
19 May
2 hrs
Writing to files
open() write mode · producing a real output file
▼
Build
Test
Report
Read the Files section. Extend
summariser.py: write output to briefing.txt instead of printing to the terminal. Open it in VS Code after running. Each client on its own section, with a separator between them.✓ briefing.txt exists · opens in VS Code · contains all 5 formatted summaries
Paste
summariser.py. I will ask:Explain it
What is the difference between opening a file with
"w" and "a"? What happens to existing content in each mode?"w" overwrites. "a" appends. Running a "w" script twice doesn't double the output — it replaces.
Modify it
Add a header line to briefing.txt — the date generated and the number of clients processed.
Count the rows and write the header before the loop.
Gate: open briefing.txt and show me. If the formatting looks wrong, fix it before moving on. The output file is the product.
End-of-session report
Day 6 done.
briefing.txt looks: [describe what's in it]
Solid: [what felt clear]
Shaky: [what felt uncertain]
briefing.txt looks: [describe what's in it]
Solid: [what felt clear]
Shaky: [what felt uncertain]
Tuesday
20 May
2 hrs
Error handling
try / except · .get() · graceful failure
▼
Build
Test
Report
Add
try/except to summariser.py so missing fields don't crash the script — print a warning and skip that client. Test it: deliberately remove a value from your CSV. Then add .get() with a fallback for optional fields.✓ Script runs on bad data without crashing · prints a clear warning for the affected row
Paste
summariser.py. I will ask:Explain it
What is the difference between
try/except and .get()? When would you use each?try/except catches errors after the fact. .get() prevents them by supplying a default. Use .get() for known-optional fields; try/except for the unexpected.
Modify it
Write failed rows to a separate
errors.txt instead of just printing a warning.Gate: introduce three different types of bad data. Does the script handle all three? Show me the output.
End-of-session report
Day 7 done.
Error types I tested: [list them]
Script handled: [all / most / struggled with X]
Shaky: [what felt uncertain]
Error types I tested: [list them]
Script handled: [all / most / struggled with X]
Shaky: [what felt uncertain]
Wednesday
21 May
2 hrs
Output quality
Formatting · conditional language · making it usable
▼
Build
Test
Report
The briefing output needs to be something you'd actually open before a client meeting. Add section headers, conditional language based on size (different phrasing for small vs large), clear priority flags, and a summary line at the top. Open
briefing.txt after every run and ask: would I use this?✓ You'd genuinely open briefing.txt before a client call
Paste the text of
briefing.txt — not the code. I will evaluate:Usefulness test
Is this something you'd actually use? What's missing that would make it genuinely useful for a pre-meeting brief?
Product decision as much as coding decision. The answer determines what we add.
Code test
Show me the conditional language section. How does the phrasing change by size? Walk me through the logic.
Gate: the output has to be genuinely useful, not just technically correct. If it reads like raw data, we spend more time here.
End-of-session report
Day 8 done.
Would I use briefing.txt: [yes / not yet — here's why]
What I added today: [describe]
What's still missing: [be honest]
Would I use briefing.txt: [yes / not yet — here's why]
What I added today: [describe]
What's still missing: [be honest]
Thursday
22 May
2 hrs
End-to-end with real data
Real client data · full pipeline · fix what breaks
▼
Build
Test
Report
Replace fictional CSV with real or realistic MøllerBeck client data — anonymised is fine. Run the full pipeline. Fix everything that breaks. Messy real data will break things clean fictional data didn't. That's the point. End of session: a script you'd actually run before an advisory meeting.
✓ Real data in · formatted briefing out · no crashes
Paste the full
summariser.py. Final project test:Full walkthrough
Walk me through the entire script top to bottom. Every function, every loop, every conditional — in plain English.
This is the evaluation threshold test. "I think it does X" doesn't pass. "It does X because Y" does.
Extend it
I'll name one new feature on the day. You implement it without help.
Phase 1 project gate: you must explain every line before we call this done.
End-of-session report
Day 9 done.
Can I explain every line: [yes / mostly / no — X is unclear]
Real data issues: [describe]
Project status: [done / needs one more session]
Can I explain every line: [yes / mostly / no — X is unclear]
Real data issues: [describe]
Project status: [done / needs one more session]
Friday
23 May
6 hrs
Consolidation + Git setup
Review everything · git init · first meaningful commits
▼
Build
Test
Report
Part 1 (2 hrs) — Consolidation: Open every file you've written over two weeks. Read each top to bottom. For any line you can't explain, write a comment above it in your own words. Active review, not passive reading.
Part 2 (4 hrs) — Git: Install Git. Run
Part 2 (4 hrs) — Git: Install Git. Run
git init in your mollerbeck folder. Make at least 5 commits with meaningful messages — not "update" but "add error handling for missing CSV fields". Push to GitHub. From here on, commit before every session.✓ All files commented · Git repo with 5+ meaningful commits · pushed to GitHub
Two-week checkpoint — the Phase 1 evaluation. I will ask:
Cold recall
Without looking at your code: write a function that takes a list of dictionaries and returns only those where employees > 300.
If you've genuinely built all of this, you can write this in under 2 minutes.
Explain a concept
What is the difference between a list and a dictionary? When would you use each?
Git test
Show me your git log. Are the commit messages meaningful? Do they tell the story of what you built?
Phase 2 gate: pass the cold recall test and explain both concepts clearly. If you can, Phase 2 begins next session. If not, one more week on Phase 1 with harder exercises — recalibration, not failure.
Two-week final report
Two weeks done.
Cold recall: [passed / struggled with X]
Concepts — rate each solid/shaky:
list / dict / loop / function / file / error handling
Git: [set up / commits made / pushed]
Ready for Phase 2: [yes / need one more week on X]
Cold recall: [passed / struggled with X]
Concepts — rate each solid/shaky:
list / dict / loop / function / file / error handling
Git: [set up / commits made / pushed]
Ready for Phase 2: [yes / need one more week on X]