[{"data":1,"prerenderedAt":340},["ShallowReactive",2],{"tag-labels":3,"site-header":4,"article-python-debugging-en":72},{},{"featuredPost":5,"navigations":29},{"id":6,"title":7,"title_id":8,"slug":9,"excerpt":10,"excerpt_id":11,"has_translation":12,"published_at":13,"reading_time":14,"hidden_from_listing":15,"tags":16,"thumbnail_image":21,"og_image":22,"series":23,"authors":28},110,"When You Pick a Framework Just to Look Different","Ketika Pilih Framework Cuma Biar Kelihatan Beda","ketika-pilih-framework-cuma-biar-kelihatan-beda","The last time you chose a new framework or library, was it because your problem needed that tool, or because it sounded cool and few people around you were using it? That question is uncomfortable. Technology decisions are often made for reasons that never appear in any technical document.","Terakhir kali kamu milih framework atau library baru, itu karena masalahmu butuh alat itu, atau karena alatnya kedengaran keren dan belum banyak yang pakai? Pertanyaan ini nggak enak dijawab. Soalnya keputusan teknologi sering diambil dengan alasan yang nggak pernah ditulis di dokumen teknis mana pun.",true,"2026-10-06T15:25:21.000Z",5,false,[17,18,19,20],"karier","framework","opini","pengalaman",null,"\u002Fog\u002Fketika-pilih-framework-cuma-biar-kelihatan-beda.png",{"id":24,"title":25,"title_id":26,"slug":27},9,"Articles","Artikel","all",[],[30,46,67],{"collection":31,"item":32},"series",{"__typename":31,"title":25,"title_id":26,"slug":27,"children_series":33},[34,38,42],{"title":35,"title_id":36,"slug":37},"Artificial Intelligence","Kecerdasan Artifisial","kecerdasan-artifisial",{"title":39,"title_id":40,"slug":41},"Programming Fundamentals","Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"title":43,"title_id":44,"slug":45},"Algorithms and Data Structures","Algoritma dan Struktur Data","algoritma-struktur-data",{"collection":31,"item":47},{"__typename":31,"title":48,"title_id":48,"slug":49,"children_series":50},"Tutorial","tutorial",[51,55,59,63],{"title":52,"title_id":53,"slug":54},"Learning Python","Belajar Python","belajar-python",{"title":56,"title_id":57,"slug":58},"Learning Go","Belajar Golang","belajar-golang",{"title":60,"title_id":61,"slug":62},"Learning Java","Belajar Java","belajar-java",{"title":64,"title_id":65,"slug":66},"Learning Pascal","Belajar Pascal","belajar-pascal",{"collection":31,"item":68},{"__typename":31,"title":69,"title_id":69,"slug":70,"children_series":71},"Lab","lab",[],{"article":73,"renderedBody":221,"renderedTakeaway":222,"latestArticles":223,"toc":323},{"id":74,"title":75,"title_id":76,"slug":77,"excerpt":78,"excerpt_id":79,"has_translation":12,"published_at":80,"reading_time":81,"hidden_from_listing":15,"tags":82,"thumbnail_image":21,"og_image":85,"series":86,"authors":213,"seoTitle":21,"seoDescription":21,"body_locale":216,"available_locales":217,"body":219,"takeway":220,"prerequisite":21},144,"Python debugging: the script that exits 0 with the wrong total","Debugging Python: skrip keluar dengan kode 0, tapi totalnya salah","python-debugging","A totals script runs clean, prints a number, and the number is wrong. With no traceback to follow, you stop the program at the line that writes the value and read the dict from pdb.","Skrip penjumlah berjalan mulus, mencetak angka, dan angkanya salah. Tanpa traceback untuk diikuti, kamu menghentikan program di baris yang menulis nilai itu lalu membaca isi dict dari pdb.","2026-11-09T01:00:00.000Z",7,[83,84],"python","debugging","\u002Fog\u002Fpython-debugging.png",{"id":87,"title":88,"title_id":89,"slug":90,"description":91,"description_id":92,"articles":93,"children_series":182,"parent_series":183},18,"Working with Python Scripts","Menggunakan Script Python","python-pemrograman-dengan-script","Leave the interpreter and start writing real .py files, including how to take input and print output.","Keluar dari interpreter dan mulai nulis file .py sendiri, lengkap dengan cara mengambil input dan menampilkan output.",[94,115,131,146,162,177],{"id":95,"title":96,"title_id":97,"slug":98,"excerpt":99,"excerpt_id":100,"has_translation":12,"published_at":101,"reading_time":102,"hidden_from_listing":15,"tags":103,"thumbnail_image":21,"og_image":106,"series":107,"authors":108},90,"Writing and Running a Python Script","Menulis dan Menjalankan Script Python","menulis-dan-menjalankan-script-python","A Python script is a file holding Python code, and it usually carries the .py extension.","Script python merupakan file berisi kode dengan bahasa Python yang biasanya memiliki ekstensi .py","2021-03-21T14:49:24.000Z",4,[83,49,104,105],"tooling","cli","\u002Fog\u002Fmenulis-dan-menjalankan-script-python.png",{"id":87,"title":88,"title_id":89,"slug":90},[109],{"authors_id":110},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},1,"Daffa Izzuddin","izzudd","Writer",{"id":116,"title":117,"title_id":118,"slug":119,"excerpt":120,"excerpt_id":121,"has_translation":12,"published_at":122,"reading_time":102,"hidden_from_listing":15,"tags":123,"thumbnail_image":21,"og_image":126,"series":127,"authors":128},91,"Input and Output Systems in Python 3","Sistem Input dan Output pada Python 3","sistem-input-dan-output-pada-python-3","A few functions, print() and input() among them, are the standard tools most I\u002FO work in Python is built on.","Beberapa fungsi seperti print() dan input(), merupakan fungsi standar yang banyak digunakan untuk melakukan I\u002FO.","2021-05-19T14:57:25.000Z",[83,124,125],"input-output","dasar","\u002Fog\u002Fsistem-input-dan-output-pada-python-3.png",{"id":87,"title":88,"title_id":89,"slug":90},[129],{"authors_id":130},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":132,"title":133,"title_id":134,"slug":135,"excerpt":136,"excerpt_id":137,"has_translation":12,"published_at":138,"reading_time":81,"hidden_from_listing":15,"tags":139,"thumbnail_image":21,"og_image":141,"series":142,"authors":143},136,"Python Modules: the Import That Printed the Report","Modul Python: Import yang Mencetak Laporannya","python-modules","You pull one function out into its own file, import it, and the whole report runs again and eats your input. A module is a file, `import` runs it top to bottom, and one line decides which of the two jobs it is doing.","Kamu memindahkan satu fungsi ke file sendiri, meng-import-nya, dan seluruh laporan jalan lagi lalu memakan inputmu. Modul adalah file, `import` menjalankannya dari atas ke bawah, dan satu baris menentukan pekerjaan mana yang sedang dijalankan file itu.","2026-11-06T01:00:00.000Z",[83,140],"modules","\u002Fog\u002Fpython-modules.png",{"id":87,"title":88,"title_id":89,"slug":90},[144],{"authors_id":145},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":147,"title":148,"title_id":149,"slug":150,"excerpt":151,"excerpt_id":152,"has_translation":12,"published_at":153,"reading_time":14,"hidden_from_listing":15,"tags":154,"thumbnail_image":21,"og_image":157,"series":158,"authors":159},138,"Python venv: the Install That Went Into the Wrong Python","venv Python: Instalasi yang Masuk ke Python yang Salah","python-venv-pip","`pip install` printed Successfully installed, and the next run raised ModuleNotFoundError on the same machine. Two interpreters, one install, and the variable your script checks is the one the interpreter never sets.","`pip install` mencetak Successfully installed, lalu jalan berikutnya melempar ModuleNotFoundError di mesin yang sama. Dua interpreter, satu instalasi, dan variabel yang skripmu periksa justru tidak pernah dipasang oleh interpreter.","2026-11-07T12:00:00.000Z",[83,155,156],"venv","pip","\u002Fog\u002Fpython-venv-pip.png",{"id":87,"title":88,"title_id":89,"slug":90},[160],{"authors_id":161},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":163,"title":164,"title_id":165,"slug":166,"excerpt":167,"excerpt_id":168,"has_translation":12,"published_at":169,"reading_time":81,"hidden_from_listing":15,"tags":170,"thumbnail_image":21,"og_image":172,"series":173,"authors":174},143,"Python stdlib: the datetime json.dumps Refused to Write","Python stdlib: datetime yang Ditolak json.dumps","python-stdlib","A script that totals minutes per day dies on the line that writes the report. Four stdlib modules carry it, and one TypeError stands in the way.","Skrip yang menjumlahkan menit per hari mati tepat di baris yang menulis laporan. Empat modul stdlib membawanya, dan satu TypeError menghadang.","2026-11-08T01:00:00.000Z",[83,171,105],"stdlib","\u002Fog\u002Fpython-stdlib.png",{"id":87,"title":88,"title_id":89,"slug":90},[175],{"authors_id":176},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":74,"title":75,"title_id":76,"slug":77,"excerpt":78,"excerpt_id":79,"has_translation":12,"published_at":80,"reading_time":81,"hidden_from_listing":15,"tags":82,"thumbnail_image":21,"og_image":85,"series":178,"authors":179},{"id":87,"title":88,"title_id":89,"slug":90},[180],{"authors_id":181},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},[],{"id":184,"title":52,"title_id":53,"slug":54,"parent_series":185,"children_series":186},10,{"id":111,"title":48,"title_id":48,"slug":49},[187,192,197,198,203,208],{"id":188,"title":189,"title_id":190,"slug":191,"cover_image":21},16,"Getting Started with Python","Berkenalan dengan Bahasa Python","python-intro",{"id":193,"title":194,"title_id":195,"slug":196,"cover_image":21},17,"Variables, Types, and Operations","Variabel, Tipe Data, dan Operasi","python-variabel-tipe-data",{"id":87,"title":88,"title_id":89,"slug":90,"cover_image":21},{"id":199,"title":200,"title_id":201,"slug":202,"cover_image":21},19,"Control Structures","Struktur Kontrol","python-struktur-kontrol",{"id":204,"title":205,"title_id":206,"slug":207,"cover_image":21},20,"Functional Programming with Python","Pemrograman Fungsional dengan Python","python-pemrograman-fungsional",{"id":209,"title":210,"title_id":211,"slug":212,"cover_image":21},30,"Collections and Strings","Koleksi dan String","python-collections",[214],{"authors_id":215},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},"en",[216,218],"id","You run your script. It exits 0 and prints a total. The total is wrong.\n\nNothing raised and nothing printed a warning, so there is no line number to start from.\n\n## A wrong number with no traceback\n\nThe script adds up practice minutes per day. The data is three sessions, and two of them fall on the same day.\n\n```json\n[\n  {\"date\": \"2026-11-03\", \"minutes\": 20},\n  {\"date\": \"2026-11-03\", \"minutes\": 45},\n  {\"date\": \"2026-11-04\", \"minutes\": 30}\n]\n```\n\nThe script reads the file, builds a `dict` of minutes per day, and prints each day followed by a total.\n\n```python\n# totals.py\nimport json\nimport sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1])\nsessions = json.loads(path.read_text())\n\ntotals = {}\nfor session in sessions:\n    day = session[\"date\"]\n    totals[day] = session[\"minutes\"]\n\nfor day in sorted(totals):\n    print(f\"{day}  {totals[day]}\")\nprint(f\"total {sum(totals.values())}\")\n```\n\nThis is the real run, measured on Python 3.14.9 on Linux.\n\n```text\n$ python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\n```\n\nThe exit code is 0. Now check the arithmetic by hand. On 2026-11-03 you practiced 20 and 45 minutes, so that day should be 65. 2026-11-04 is 30. The total should be 95, not 75.\n\nNothing raised because the loop assigns where it should add. The line `totals[day] = session[\"minutes\"]` is legal Python. It stores a number under a key, and if the key already exists it replaces the old value. There is no `TypeError` to raise and no `traceback` to print.\n\n## Stop it where it goes wrong\n\nA `traceback` points at a line that failed. Here no line failed, so you stop the program at the line that writes the wrong value.\n\nPut `breakpoint()` inside the loop, on the line before the assignment, indented like the lines around it. Then run the script as usual. The call starts `pdb`, and the program waits at the `(Pdb)` prompt.\n\nPython 3.14 prints the working directory in front of the file name. It is cut to `totals.py` here so the lines fit.\n\n```text\n$ python3 totals.py sessions.json\n> totals.py(12)\u003Cmodule>()\n-> breakpoint()\n(Pdb) p totals\n{}\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 20}\n(Pdb) n\n> totals.py(13)\u003Cmodule>()\n-> totals[day] = session[\"minutes\"]\n(Pdb) n\n> totals.py(10)\u003Cmodule>()\n-> for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) c\n> totals.py(12)\u003Cmodule>()\n-> breakpoint()\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 45}\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) n\n> totals.py(13)\u003Cmodule>()\n-> totals[day] = session[\"minutes\"]\n(Pdb) n\n> totals.py(10)\u003Cmodule>()\n-> for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 45}\n(Pdb) q\nQuitting pdb will kill the process. Quit anyway? [y\u002Fn] y\n```\n\nYou do not need to type this exact session. Six commands matter, and each one answers a question:\n\n- `p totals` prints the `repr` of `totals`. It answers what the `dict` holds right now.\n- `p session` prints the item the loop is working on.\n- `n` runs the current line and stops at the next one.\n- `c` continues until the next `breakpoint()`, which here is the next pass of the loop.\n- `l` lists the source lines around where you are stopped.\n- `q` quits. It asks `Quitting pdb will kill the process. Quit anyway? [y\u002Fn]`, because quitting ends the program.\n\nThe two hits prove the cause. On the first hit `session` is the 20 minute entry and `totals` is empty. On the second hit `session` is 2026-11-03 again, this time with 45, and `totals` already holds 20 for that day. After `n` runs the assignment, `totals` holds 45. The same day arrives twice, and the second write replaces the first 20 with 45.\n\n## One line that adds instead of overwrites\n\nThe fix is one line:\n\n`totals[day] = totals.get(day, 0) + session[\"minutes\"]`\n\n`totals.get(day, 0)` gives the minutes stored so far for that day, or 0 when the day is new. Delete the `breakpoint()` line and the file looks like this.\n\n```python\n# totals.py\nimport json\nimport sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1])\nsessions = json.loads(path.read_text())\n\ntotals = {}\nfor session in sessions:\n    day = session[\"date\"]\n    totals[day] = totals.get(day, 0) + session[\"minutes\"]\n\nfor day in sorted(totals):\n    print(f\"{day}  {totals[day]}\")\nprint(f\"total {sum(totals.values())}\")\n```\n\nBoth sessions on 2026-11-03 now count, and the run exits 0.\n\n```text\n$ python3 totals.py sessions.json\n2026-11-03  65\n2026-11-04  30\ntotal 95\n```\n\n## The checker\n\nThe arithmetic you did by hand can become a script you rerun. This checker runs `totals.py` on the same three sessions and compares the output with your numbers.\n\n```python\n# check_totals.py\nimport json\nimport subprocess\nimport sys\nimport tempfile\nfrom pathlib import Path\n\nSESSIONS = [\n    {\"date\": \"2026-11-03\", \"minutes\": 20},\n    {\"date\": \"2026-11-03\", \"minutes\": 45},\n    {\"date\": \"2026-11-04\", \"minutes\": 30},\n]\nEXPECTED = \"2026-11-03  65\\n2026-11-04  30\\ntotal 95\"\n\n\ndef report(results):\n    for label, ok, detail in results:\n        print(f\"{'PASS' if ok else 'FAIL'}  {label}\" + (f\"  \u003C- {detail}\" if not ok and detail else \"\"))\n    passed = sum(1 for _, ok, _ in results if ok)\n    print(f\"\\n{passed} of {len(results)} checks passed\")\n    return passed == len(results)\n\n\ndef main():\n    folder = Path(tempfile.mkdtemp())\n    data = folder \u002F \"sessions.json\"\n    data.write_text(json.dumps(SESSIONS))\n    run = subprocess.run(\n        [sys.executable, \"totals.py\", str(data)],\n        capture_output=True, text=True, input=\"\", timeout=20,\n    )\n    results = [(\"the script exits 0\", run.returncode == 0, f\"exit {run.returncode}\")]\n    by_day = {}\n    for line in run.stdout.strip().splitlines()[:-1]:\n        day, minutes = line.split()\n        by_day[day] = int(minutes)\n    results.append((\"2026-11-03 adds up both sessions\", by_day.get(\"2026-11-03\") == 65, str(by_day.get(\"2026-11-03\"))))\n    results.append((\"2026-11-04 keeps its single session\", by_day.get(\"2026-11-04\") == 30, str(by_day.get(\"2026-11-04\"))))\n    results.append((\"the report prints the totals it computed\", run.stdout.strip() == EXPECTED, repr(run.stdout.strip())))\n    source = Path(\"totals.py\").read_text()\n    results.append((\"no breakpoint() left in the script\", \"breakpoint()\" not in source, \"remove it before shipping\"))\n    return 0 if report(results) else 1\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n```\n\nRun it against the starter script and 3 of 5 checks pass. The exit code is 1.\n\n```text\n$ python3 check_totals.py\nPASS  the script exits 0\nFAIL  2026-11-03 adds up both sessions  \u003C- 45\nPASS  2026-11-04 keeps its single session\nFAIL  the report prints the totals it computed  \u003C- '2026-11-03  45\\n2026-11-04  30\\ntotal 75'\nPASS  no breakpoint() left in the script\n\n3 of 5 checks passed\n```\n\nThe two FAIL lines are the same bug seen twice. Run it against the fixed script and all 5 pass. The exit code is 0.\n\n```text\n$ python3 check_totals.py\nPASS  the script exits 0\nPASS  2026-11-03 adds up both sessions\nPASS  2026-11-04 keeps its single session\nPASS  the report prints the totals it computed\nPASS  no breakpoint() left in the script\n\n5 of 5 checks passed\n```\n\nThe last check fails if `breakpoint()` is still in `totals.py`. A breakpoint left in a batch job waits on `stdin` forever, so you want to find it before you ship.\n\n## What pdb cannot tell you\n\n`p` shows the value at that moment, not its history. To learn how a value got there, you step with `n` and print again, as the transcript did.\n\nA breakpoint also stops a batch job. The program does not finish until someone answers at the prompt.\n\n`PYTHONBREAKPOINT=0` disables breakpoints without editing the file. On this script it only hides the debugger.\n\n```text\n$ PYTHONBREAKPOINT=0 python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\n```\n\nThe breakpoint is skipped, the script exits 0, and the bug stays.\n\nWhen a number is wrong and nothing raised, stop the program at the line that writes the value, because the traceback is not coming.\n","- A wrong answer with no traceback has no line to follow, so you stop the program where the value is written instead of reading an error.\n- `breakpoint()` starts `pdb` inside the loop, and `p` prints the value at that moment: the same day arrives twice and the second write replaces the first.\n- `totals[day] = totals.get(day, 0) + session[\"minutes\"]` adds instead of overwriting, which is the whole fix.\n- A breakpoint left in a batch job waits on stdin forever, so the checker fails when `breakpoint()` is still in the file.","\u003Cp>You run your script. It exits 0 and prints a total. The total is wrong.\u003C\u002Fp>\n\u003Cp>Nothing raised and nothing printed a warning, so there is no line number to start from.\u003C\u002Fp>\n\u003Ch2 id=\"a-wrong-number-with-no-traceback\">A wrong number with no traceback\u003C\u002Fh2>\n\u003Cp>The script adds up practice minutes per day. The data is three sessions, and two of them fall on the same day.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">JSON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%5B%0A%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2020%7D%2C%0A%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2045%7D%2C%0A%20%20%7B%22date%22%3A%20%222026-11-04%22%2C%20%22minutes%22%3A%2030%7D%0A%5D\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">[\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">20\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">45\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">30\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>The script reads the file, builds a \u003Ccode>dict\u003C\u002Fcode> of minutes per day, and prints each day followed by a total.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20totals.py%0Aimport%20json%0Aimport%20sys%0Afrom%20pathlib%20import%20Path%0A%0Apath%20%3D%20Path(sys.argv%5B1%5D)%0Asessions%20%3D%20json.loads(path.read_text())%0A%0Atotals%20%3D%20%7B%7D%0Afor%20session%20in%20sessions%3A%0A%20%20%20%20day%20%3D%20session%5B%22date%22%5D%0A%20%20%20%20totals%5Bday%5D%20%3D%20session%5B%22minutes%22%5D%0A%0Afor%20day%20in%20sorted(totals)%3A%0A%20%20%20%20print(f%22%7Bday%7D%20%20%7Btotals%5Bday%5D%7D%22)%0Aprint(f%22total%20%7Bsum(totals.values())%7D%22)\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># totals.py\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sys\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pathlib \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">path \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(sys.argv[\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">sessions \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json.loads(path.read_text())\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">totals \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sessions:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    totals[day] \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> sorted\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">day\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">totals[day]\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"total \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{sum\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals.values())\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>This is the real run, measured on Python 3.14.9 on Linux.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20totals.py%20sessions.json%0A2026-11-03%20%2045%0A2026-11-04%20%2030%0Atotal%2075\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>The exit code is 0. Now check the arithmetic by hand. On 2026-11-03 you practiced 20 and 45 minutes, so that day should be 65. 2026-11-04 is 30. The total should be 95, not 75.\u003C\u002Fp>\n\u003Cp>Nothing raised because the loop assigns where it should add. The line \u003Ccode>totals[day] = session[&quot;minutes&quot;]\u003C\u002Fcode> is legal Python. It stores a number under a key, and if the key already exists it replaces the old value. There is no \u003Ccode>TypeError\u003C\u002Fcode> to raise and no \u003Ccode>traceback\u003C\u002Fcode> to print.\u003C\u002Fp>\n\u003Ch2 id=\"stop-it-where-it-goes-wrong\">Stop it where it goes wrong\u003C\u002Fh2>\n\u003Cp>A \u003Ccode>traceback\u003C\u002Fcode> points at a line that failed. Here no line failed, so you stop the program at the line that writes the wrong value.\u003C\u002Fp>\n\u003Cp>Put \u003Ccode>breakpoint()\u003C\u002Fcode> inside the loop, on the line before the assignment, indented like the lines around it. Then run the script as usual. The call starts \u003Ccode>pdb\u003C\u002Fcode>, and the program waits at the \u003Ccode>(Pdb)\u003C\u002Fcode> prompt.\u003C\u002Fp>\n\u003Cp>Python 3.14 prints the working directory in front of the file name. It is cut to \u003Ccode>totals.py\u003C\u002Fcode> here so the lines fit.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20totals.py%20sessions.json%0A%3E%20totals.py(12)%3Cmodule%3E()%0A-%3E%20breakpoint()%0A(Pdb)%20p%20totals%0A%7B%7D%0A(Pdb)%20p%20session%0A%7B'date'%3A%20'2026-11-03'%2C%20'minutes'%3A%2020%7D%0A(Pdb)%20n%0A%3E%20totals.py(13)%3Cmodule%3E()%0A-%3E%20totals%5Bday%5D%20%3D%20session%5B%22minutes%22%5D%0A(Pdb)%20n%0A%3E%20totals.py(10)%3Cmodule%3E()%0A-%3E%20for%20session%20in%20sessions%3A%0A(Pdb)%20p%20totals%0A%7B'2026-11-03'%3A%2020%7D%0A(Pdb)%20c%0A%3E%20totals.py(12)%3Cmodule%3E()%0A-%3E%20breakpoint()%0A(Pdb)%20p%20session%0A%7B'date'%3A%20'2026-11-03'%2C%20'minutes'%3A%2045%7D%0A(Pdb)%20p%20totals%0A%7B'2026-11-03'%3A%2020%7D%0A(Pdb)%20n%0A%3E%20totals.py(13)%3Cmodule%3E()%0A-%3E%20totals%5Bday%5D%20%3D%20session%5B%22minutes%22%5D%0A(Pdb)%20n%0A%3E%20totals.py(10)%3Cmodule%3E()%0A-%3E%20for%20session%20in%20sessions%3A%0A(Pdb)%20p%20totals%0A%7B'2026-11-03'%3A%2045%7D%0A(Pdb)%20q%0AQuitting%20pdb%20will%20kill%20the%20process.%20Quit%20anyway%3F%20%5By%2Fn%5D%20y\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 totals.py sessions.json\n&gt; totals.py(12)&lt;module&gt;()\n-&gt; breakpoint()\n(Pdb) p totals\n{}\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 20}\n(Pdb) n\n&gt; totals.py(13)&lt;module&gt;()\n-&gt; totals[day] = session[\"minutes\"]\n(Pdb) n\n&gt; totals.py(10)&lt;module&gt;()\n-&gt; for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) c\n&gt; totals.py(12)&lt;module&gt;()\n-&gt; breakpoint()\n(Pdb) p session\n{'date': '2026-11-03', 'minutes': 45}\n(Pdb) p totals\n{'2026-11-03': 20}\n(Pdb) n\n&gt; totals.py(13)&lt;module&gt;()\n-&gt; totals[day] = session[\"minutes\"]\n(Pdb) n\n&gt; totals.py(10)&lt;module&gt;()\n-&gt; for session in sessions:\n(Pdb) p totals\n{'2026-11-03': 45}\n(Pdb) q\nQuitting pdb will kill the process. Quit anyway? [y\u002Fn] y\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>You do not need to type this exact session. Six commands matter, and each one answers a question:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>p totals\u003C\u002Fcode> prints the \u003Ccode>repr\u003C\u002Fcode> of \u003Ccode>totals\u003C\u002Fcode>. It answers what the \u003Ccode>dict\u003C\u002Fcode> holds right now.\u003C\u002Fli>\n\u003Cli>\u003Ccode>p session\u003C\u002Fcode> prints the item the loop is working on.\u003C\u002Fli>\n\u003Cli>\u003Ccode>n\u003C\u002Fcode> runs the current line and stops at the next one.\u003C\u002Fli>\n\u003Cli>\u003Ccode>c\u003C\u002Fcode> continues until the next \u003Ccode>breakpoint()\u003C\u002Fcode>, which here is the next pass of the loop.\u003C\u002Fli>\n\u003Cli>\u003Ccode>l\u003C\u002Fcode> lists the source lines around where you are stopped.\u003C\u002Fli>\n\u003Cli>\u003Ccode>q\u003C\u002Fcode> quits. It asks \u003Ccode>Quitting pdb will kill the process. Quit anyway? [y\u002Fn]\u003C\u002Fcode>, because quitting ends the program.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The two hits prove the cause. On the first hit \u003Ccode>session\u003C\u002Fcode> is the 20 minute entry and \u003Ccode>totals\u003C\u002Fcode> is empty. On the second hit \u003Ccode>session\u003C\u002Fcode> is 2026-11-03 again, this time with 45, and \u003Ccode>totals\u003C\u002Fcode> already holds 20 for that day. After \u003Ccode>n\u003C\u002Fcode> runs the assignment, \u003Ccode>totals\u003C\u002Fcode> holds 45. The same day arrives twice, and the second write replaces the first 20 with 45.\u003C\u002Fp>\n\u003Ch2 id=\"one-line-that-adds-instead-of-overwrites\">One line that adds instead of overwrites\u003C\u002Fh2>\n\u003Cp>The fix is one line:\u003C\u002Fp>\n\u003Cp>\u003Ccode>totals[day] = totals.get(day, 0) + session[&quot;minutes&quot;]\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Ccode>totals.get(day, 0)\u003C\u002Fcode> gives the minutes stored so far for that day, or 0 when the day is new. Delete the \u003Ccode>breakpoint()\u003C\u002Fcode> line and the file looks like this.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20totals.py%0Aimport%20json%0Aimport%20sys%0Afrom%20pathlib%20import%20Path%0A%0Apath%20%3D%20Path(sys.argv%5B1%5D)%0Asessions%20%3D%20json.loads(path.read_text())%0A%0Atotals%20%3D%20%7B%7D%0Afor%20session%20in%20sessions%3A%0A%20%20%20%20day%20%3D%20session%5B%22date%22%5D%0A%20%20%20%20totals%5Bday%5D%20%3D%20totals.get(day%2C%200)%20%2B%20session%5B%22minutes%22%5D%0A%0Afor%20day%20in%20sorted(totals)%3A%0A%20%20%20%20print(f%22%7Bday%7D%20%20%7Btotals%5Bday%5D%7D%22)%0Aprint(f%22total%20%7Bsum(totals.values())%7D%22)\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># totals.py\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sys\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pathlib \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">path \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(sys.argv[\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">sessions \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json.loads(path.read_text())\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">totals \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sessions:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    totals[day] \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> totals.get(day, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">+\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> session[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> sorted\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">day\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">totals[day]\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"total \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{sum\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(totals.values())\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Both sessions on 2026-11-03 now count, and the run exits 0.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20totals.py%20sessions.json%0A2026-11-03%20%2065%0A2026-11-04%20%2030%0Atotal%2095\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 totals.py sessions.json\n2026-11-03  65\n2026-11-04  30\ntotal 95\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Ch2 id=\"the-checker\">The checker\u003C\u002Fh2>\n\u003Cp>The arithmetic you did by hand can become a script you rerun. This checker runs \u003Ccode>totals.py\u003C\u002Fcode> on the same three sessions and compares the output with your numbers.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%23%20check_totals.py%0Aimport%20json%0Aimport%20subprocess%0Aimport%20sys%0Aimport%20tempfile%0Afrom%20pathlib%20import%20Path%0A%0ASESSIONS%20%3D%20%5B%0A%20%20%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2020%7D%2C%0A%20%20%20%20%7B%22date%22%3A%20%222026-11-03%22%2C%20%22minutes%22%3A%2045%7D%2C%0A%20%20%20%20%7B%22date%22%3A%20%222026-11-04%22%2C%20%22minutes%22%3A%2030%7D%2C%0A%5D%0AEXPECTED%20%3D%20%222026-11-03%20%2065%5Cn2026-11-04%20%2030%5Cntotal%2095%22%0A%0A%0Adef%20report(results)%3A%0A%20%20%20%20for%20label%2C%20ok%2C%20detail%20in%20results%3A%0A%20%20%20%20%20%20%20%20print(f%22%7B'PASS'%20if%20ok%20else%20'FAIL'%7D%20%20%7Blabel%7D%22%20%2B%20(f%22%20%20%3C-%20%7Bdetail%7D%22%20if%20not%20ok%20and%20detail%20else%20%22%22))%0A%20%20%20%20passed%20%3D%20sum(1%20for%20_%2C%20ok%2C%20_%20in%20results%20if%20ok)%0A%20%20%20%20print(f%22%5Cn%7Bpassed%7D%20of%20%7Blen(results)%7D%20checks%20passed%22)%0A%20%20%20%20return%20passed%20%3D%3D%20len(results)%0A%0A%0Adef%20main()%3A%0A%20%20%20%20folder%20%3D%20Path(tempfile.mkdtemp())%0A%20%20%20%20data%20%3D%20folder%20%2F%20%22sessions.json%22%0A%20%20%20%20data.write_text(json.dumps(SESSIONS))%0A%20%20%20%20run%20%3D%20subprocess.run(%0A%20%20%20%20%20%20%20%20%5Bsys.executable%2C%20%22totals.py%22%2C%20str(data)%5D%2C%0A%20%20%20%20%20%20%20%20capture_output%3DTrue%2C%20text%3DTrue%2C%20input%3D%22%22%2C%20timeout%3D20%2C%0A%20%20%20%20)%0A%20%20%20%20results%20%3D%20%5B(%22the%20script%20exits%200%22%2C%20run.returncode%20%3D%3D%200%2C%20f%22exit%20%7Brun.returncode%7D%22)%5D%0A%20%20%20%20by_day%20%3D%20%7B%7D%0A%20%20%20%20for%20line%20in%20run.stdout.strip().splitlines()%5B%3A-1%5D%3A%0A%20%20%20%20%20%20%20%20day%2C%20minutes%20%3D%20line.split()%0A%20%20%20%20%20%20%20%20by_day%5Bday%5D%20%3D%20int(minutes)%0A%20%20%20%20results.append((%222026-11-03%20adds%20up%20both%20sessions%22%2C%20by_day.get(%222026-11-03%22)%20%3D%3D%2065%2C%20str(by_day.get(%222026-11-03%22))))%0A%20%20%20%20results.append((%222026-11-04%20keeps%20its%20single%20session%22%2C%20by_day.get(%222026-11-04%22)%20%3D%3D%2030%2C%20str(by_day.get(%222026-11-04%22))))%0A%20%20%20%20results.append((%22the%20report%20prints%20the%20totals%20it%20computed%22%2C%20run.stdout.strip()%20%3D%3D%20EXPECTED%2C%20repr(run.stdout.strip())))%0A%20%20%20%20source%20%3D%20Path(%22totals.py%22).read_text()%0A%20%20%20%20results.append((%22no%20breakpoint()%20left%20in%20the%20script%22%2C%20%22breakpoint()%22%20not%20in%20source%2C%20%22remove%20it%20before%20shipping%22))%0A%20%20%20%20return%200%20if%20report(results)%20else%201%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20sys.exit(main())\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># check_totals.py\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> json\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> subprocess\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> sys\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> tempfile\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pathlib \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">SESSIONS\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> [\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">20\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">45\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"date\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"minutes\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">30\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">EXPECTED\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"2026-11-03  65\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">2026-11-04  30\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">total 95\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\"> report\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(results):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> label, ok, detail \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> results:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">        print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">'PASS'\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> ok \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> 'FAIL'\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">  {\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">label\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> +\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> (\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"  &#x3C;- \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">detail\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> not\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> ok \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">and\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> detail \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    passed \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> sum\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> _, ok, _ \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> results \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">if\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> ok)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    print\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">passed\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> of \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{len\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(results)\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> checks passed\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> passed \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> len\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(results)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">def\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\"> main\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">():\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    folder \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(tempfile.mkdtemp())\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    data \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> folder \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"sessions.json\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    data.write_text(json.dumps(\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">SESSIONS\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    run \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> subprocess.run(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        [sys.executable, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"totals.py\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(data)],\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">        capture_output\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">True\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">text\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">True\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">input\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">timeout\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">20\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> [(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"the script exits 0\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, run.returncode \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"exit \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">run.returncode\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    by_day \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> line \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> run.stdout.strip().splitlines()[:\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">-\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">1\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        day, minutes \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> line.split()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        by_day[day] \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> int\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(minutes)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03 adds up both sessions\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 65\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-03\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04 keeps its single session\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 30\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(by_day.get(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"2026-11-04\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"the report prints the totals it computed\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, run.stdout.strip() \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> EXPECTED\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">repr\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(run.stdout.strip())))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    source \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Path(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"totals.py\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">).read_text()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    results.append((\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"no breakpoint() left in the script\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"breakpoint()\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> not\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> source, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"remove it before shipping\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> report(results) \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\"> __name__\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> ==\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"__main__\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    sys.exit(main())\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Run it against the starter script and 3 of 5 checks pass. The exit code is 1.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20check_totals.py%0APASS%20%20the%20script%20exits%200%0AFAIL%20%202026-11-03%20adds%20up%20both%20sessions%20%20%3C-%2045%0APASS%20%202026-11-04%20keeps%20its%20single%20session%0AFAIL%20%20the%20report%20prints%20the%20totals%20it%20computed%20%20%3C-%20'2026-11-03%20%2045%5Cn2026-11-04%20%2030%5Cntotal%2075'%0APASS%20%20no%20breakpoint()%20left%20in%20the%20script%0A%0A3%20of%205%20checks%20passed\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 check_totals.py\nPASS  the script exits 0\nFAIL  2026-11-03 adds up both sessions  &lt;- 45\nPASS  2026-11-04 keeps its single session\nFAIL  the report prints the totals it computed  &lt;- '2026-11-03  45\\n2026-11-04  30\\ntotal 75'\nPASS  no breakpoint() left in the script\n\n3 of 5 checks passed\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>The two FAIL lines are the same bug seen twice. Run it against the fixed script and all 5 pass. The exit code is 0.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20python3%20check_totals.py%0APASS%20%20the%20script%20exits%200%0APASS%20%202026-11-03%20adds%20up%20both%20sessions%0APASS%20%202026-11-04%20keeps%20its%20single%20session%0APASS%20%20the%20report%20prints%20the%20totals%20it%20computed%0APASS%20%20no%20breakpoint()%20left%20in%20the%20script%0A%0A5%20of%205%20checks%20passed\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ python3 check_totals.py\nPASS  the script exits 0\nPASS  2026-11-03 adds up both sessions\nPASS  2026-11-04 keeps its single session\nPASS  the report prints the totals it computed\nPASS  no breakpoint() left in the script\n\n5 of 5 checks passed\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>The last check fails if \u003Ccode>breakpoint()\u003C\u002Fcode> is still in \u003Ccode>totals.py\u003C\u002Fcode>. A breakpoint left in a batch job waits on \u003Ccode>stdin\u003C\u002Fcode> forever, so you want to find it before you ship.\u003C\u002Fp>\n\u003Ch2 id=\"what-pdb-cannot-tell-you\">What pdb cannot tell you\u003C\u002Fh2>\n\u003Cp>\u003Ccode>p\u003C\u002Fcode> shows the value at that moment, not its history. To learn how a value got there, you step with \u003Ccode>n\u003C\u002Fcode> and print again, as the transcript did.\u003C\u002Fp>\n\u003Cp>A breakpoint also stops a batch job. The program does not finish until someone answers at the prompt.\u003C\u002Fp>\n\u003Cp>\u003Ccode>PYTHONBREAKPOINT=0\u003C\u002Fcode> disables breakpoints without editing the file. On this script it only hides the debugger.\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">TEXT\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"%24%20PYTHONBREAKPOINT%3D0%20python3%20totals.py%20sessions.json%0A2026-11-03%20%2045%0A2026-11-04%20%2030%0Atotal%2075\" title=\"Copy\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>$ PYTHONBREAKPOINT=0 python3 totals.py sessions.json\n2026-11-03  45\n2026-11-04  30\ntotal 75\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>The breakpoint is skipped, the script exits 0, and the bug stays.\u003C\u002Fp>\n\u003Cp>When a number is wrong and nothing raised, stop the program at the line that writes the value, because the traceback is not coming.\u003C\u002Fp>\n","\u003Cul>\n\u003Cli>A wrong answer with no traceback has no line to follow, so you stop the program where the value is written instead of reading an error.\u003C\u002Fli>\n\u003Cli>\u003Ccode>breakpoint()\u003C\u002Fcode> starts \u003Ccode>pdb\u003C\u002Fcode> inside the loop, and \u003Ccode>p\u003C\u002Fcode> prints the value at that moment: the same day arrives twice and the second write replaces the first.\u003C\u002Fli>\n\u003Cli>\u003Ccode>totals[day] = totals.get(day, 0) + session[&quot;minutes&quot;]\u003C\u002Fcode> adds instead of overwriting, which is the whole fix.\u003C\u002Fli>\n\u003Cli>A breakpoint left in a batch job waits on stdin forever, so the checker fails when \u003Ccode>breakpoint()\u003C\u002Fcode> is still in the file.\u003C\u002Fli>\n\u003C\u002Ful>\n",[224,246,265,282,287,306],{"id":225,"title":226,"title_id":227,"slug":228,"excerpt":229,"excerpt_id":230,"has_translation":12,"published_at":231,"reading_time":102,"hidden_from_listing":15,"tags":232,"thumbnail_image":21,"og_image":237,"series":238,"authors":243},48,"What a Linked List Actually Is","Tentang Struktur Data LinkedList","tentang-struktur-data-linked-list","A linked list is a linear chain of nodes, each holding data and a pointer to the next node. Here is what that chain looks like, what it costs next to an array, and the same minimal implementation in Java, Python, and JavaScript.","Linked list adalah rantai linear berisi node, dan setiap node menyimpan data plus pointer ke node berikutnya. Ini bentuk rantainya, ongkosnya dibanding array, dan implementasi paling sederhana dalam Java, Python, serta JavaScript.","2026-11-21T01:00:00.000Z",[233,234,235,236],"struktur-data","linked-list","pointer","konsep","\u002Fog\u002Ftentang-struktur-data-linked-list.png",{"id":239,"title":240,"title_id":241,"slug":242},28,"Linear Data Structures","Struktur Data Linear","asd-struktur-linear",[244],{"authors_id":245},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":247,"title":248,"title_id":249,"slug":250,"excerpt":251,"excerpt_id":252,"has_translation":12,"published_at":253,"reading_time":81,"hidden_from_listing":15,"tags":254,"thumbnail_image":21,"og_image":256,"series":257,"authors":262},47,"Why Understanding Algorithms and Data Structures Actually Matters","Pentingnya Memahami Algoritma dan Struktur Data","pentingnya-memahami-algoritma-dan-struktur-data","An algorithm is an ordered list of steps, and that order decides how expensive a problem is. The examples are on the back of an instant noodle packet, in a factorial, and behind ctrl+z.","Sebagai konsep fundamental, algoritma menentukan seberapa mahal sebuah masalah diselesaikan. Contohnya ada di bungkus mie instan, di bilangan faktorial, dan di tombol undo.","2026-11-19T01:00:00.000Z",[255,233,236,125],"algoritma","\u002Fog\u002Fpentingnya-memahami-algoritma-dan-struktur-data.png",{"id":258,"title":259,"title_id":260,"slug":261},27,"About Algorithms and Data Structures","Tentang Algoritma dan Struktur Data","asd-intro",[263],{"authors_id":264},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":266,"title":267,"title_id":268,"slug":269,"excerpt":270,"excerpt_id":271,"has_translation":12,"published_at":272,"reading_time":14,"hidden_from_listing":15,"tags":273,"thumbnail_image":21,"og_image":277,"series":278,"authors":279},108,"At 11 a.m. the AI Quota Runs Out, and the Work Stops There","Jam 11 siang kuota AI-nya habis, dan kerjaan berhenti di situ","jam-11-kuota-ai-habis-kerjaan-berhenti","The panel says the usage limit is reached and the work stops, not because it is finished. Three guesses explain it, and all three are wrong: the limit is shaped like a cliff, and the day is built on top of one tool.","Panel di layar bilang batas pemakaian tercapai, dan kerja berhenti di situ. Bukan karena selesai. Ada tiga tebakan kenapa ini terjadi, dan ketiganya salah: batasnya berbentuk tebing, dan harinya dibangun di atas satu alat.","2026-11-12T01:00:00.000Z",[274,104,275,276],"ai","rate-limit","produktivitas","\u002Fog\u002Fjam-11-kuota-ai-habis-kerjaan-berhenti.png",{"id":24,"title":25,"title_id":26,"slug":27},[280],{"authors_id":281},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":74,"title":75,"title_id":76,"slug":77,"excerpt":78,"excerpt_id":79,"has_translation":12,"published_at":80,"reading_time":81,"hidden_from_listing":15,"tags":82,"thumbnail_image":21,"og_image":85,"series":283,"authors":284},{"id":87,"title":88,"title_id":89,"slug":90},[285],{"authors_id":286},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":288,"title":289,"title_id":290,"slug":291,"excerpt":292,"excerpt_id":293,"has_translation":12,"published_at":294,"reading_time":295,"hidden_from_listing":15,"tags":296,"thumbnail_image":21,"og_image":300,"series":301,"authors":303},141,"A 6,000-Token Handbook and a 0% Cache Hit Rate","Buku Panduan 6.000 Token dan Hit Rate Cache 0%","cache-hit-rate-zero","Twenty-four turns, caching switched on, and a bill 21% higher than running with no cache at all. A support agent whose prompt looks static, and the three lines that quietly change it every turn.","Dua puluh empat giliran, caching menyala, dan tagihan 21% lebih tinggi daripada menjalankannya tanpa cache sama sekali. Agen support yang prompt-nya kelihatan statis, dan tiga baris yang diam-diam mengubahnya tiap giliran.","2026-11-08T01:20:00.000Z",3,[70,297,298,299],"llm","cache","api","\u002Fog\u002Fcache-hit-rate-zero.png",{"id":302,"title":69,"title_id":69,"slug":70},29,[304],{"authors_id":305},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},{"id":307,"title":308,"title_id":309,"slug":310,"excerpt":311,"excerpt_id":312,"has_translation":12,"published_at":313,"reading_time":314,"hidden_from_listing":15,"tags":315,"thumbnail_image":21,"og_image":317,"series":318,"authors":320},140,"What a Prompt Cache Actually Remembers","Apa yang Sebenarnya Diingat Prompt Cache","what-a-prompt-cache-remembers","Four providers cache the same bytes under four different rulebooks. The question that decides your bill: when the second request is longer than the first, does it reuse the whole prefix or only the part that was already written?","Empat provider meng-cache byte yang sama dengan empat buku aturan berbeda. Pertanyaan yang menentukan tagihanmu: saat permintaan kedua lebih panjang, apakah seluruh prefix dipakai ulang atau cuma bagian yang sudah pernah ditulis?","2026-11-08T01:10:00.000Z",6,[297,316,298,299],"ai-agent","\u002Fog\u002Fwhat-a-prompt-cache-remembers.png",{"id":319,"title":35,"title_id":36,"slug":37},8,[321],{"authors_id":322},{"id":111,"name":112,"slug":113,"role":114,"profile_picture":21},[324,328,331,334,337],{"text":325,"depth":326,"id":327},"A wrong number with no traceback",2,"a-wrong-number-with-no-traceback",{"text":329,"depth":326,"id":330},"Stop it where it goes wrong","stop-it-where-it-goes-wrong",{"text":332,"depth":326,"id":333},"One line that adds instead of overwrites","one-line-that-adds-instead-of-overwrites",{"text":335,"depth":326,"id":336},"The checker","the-checker",{"text":338,"depth":326,"id":339},"What pdb cannot tell you","what-pdb-cannot-tell-you",1794269487226]