Wednesday afternoon I hit a bug that killed the entire run after the first podcast completed. A counter increment in the wrong place. Forty minutes of debugging to find one line. By Saturday the generator was shipping three genuinely different podcasts per day, each one pulling from a freshly synthesized topic rather than a rotating static list. That is the week in one breath. But the path from Wednesday to Saturday is the more interesting story.
A Pipeline That Knows What Yesterday Looked Like
nblm-podcast-gen is a private tool I built for myself: a Shell pipeline that spins up three personalized NotebookLM podcasts every day, each drawing from twenty or more sources and angled toward indie developer goals. The idea is that I get a curated audio feed instead of aimlessly grazing RSS. The problem with the original version was that the topic pool was static. I had written a fixed list of themes, and the generator rotated through them on a schedule. It worked, but it was dumb. The same themes kept leading regardless of what I had actually been doing or thinking about that week.
The redesign I shipped this week changes that fundamentally. Instead of a static pool, the pipeline now runs a topic synthesis step at the start of each day. That step reads a living listener profile file, a history ledger that records which themes have led recently, an achievement collector that tracks what I shipped, and a metrics collector that gates topic selection on actual usability signals. From those inputs it synthesizes a fresh set of candidate themes specific to today, then caps how often any single theme may lead across a rolling window. The result is that the three podcasts I get on a Monday after a heavy architecture week sound genuinely different from the three I get on a Friday after a content and legal sprint. The system knows the difference because I told it what happened.
The history ledger was the piece I underestimated. I originally thought a simple “last used” timestamp per theme would be enough to prevent repetition. It was not. Themes were still clustering because the synthesis step kept arriving at the same conclusions from the same inputs. The cap on how often one theme may lead is a hard constraint that overrides the synthesis output when needed. That felt slightly inelegant to me, a rule bolted on top of a generative step, but in practice it produces much better variety than the soft probabilistic approach I tried first. Sometimes a hard cap is the right answer.
The hardest part of the week was the rendering wait. NotebookLM takes a variable amount of time to finish generating audio after you submit a notebook. My original pipeline was checking for completion too early and occasionally declaring a run successful when the audio file was not actually ready. The fix was to add an explicit wait that polls until rendering is genuinely done before moving on. Simple in principle. In practice I had to watch several runs fail in different ways before I understood the exact shape of the timing problem. The commit message says “Wait for NotebookLM to finish rendering before judging a run” and that is exactly what it does, but the word “judging” is doing some work there. The pipeline was confidently marking runs as complete and logging a success when the output was either partial or absent. A polite word for that is optimistic. A more accurate word is wrong.
One other thing I fixed: test runs were writing into the same log files and state files as production runs. That meant a test I ran to verify a change was leaving entries in the history ledger that affected the next real run. I separated them with a dedicated test path. Obvious in retrospect. I am slightly embarrassed it took this long.
The final state of the pipeline now has a cleaner separation of concerns than anything I built in the first version: synthesize topics freshly each day, collect context from multiple living files, apply hard diversity constraints, wait properly for external services, and keep test state well away from production state. That is a better architecture. It also took about sixteen commits to get there, which tells you something about how the actual shape of a system emerges through iteration rather than upfront design.
Side Notes: An LLC, Several Sites, and a Beauty Salon CI Fix
Monday was almost entirely a legal and positioning sprint across four sites. I moved the operating entity behind theapparchitect.com, marcelrgberger.com, and jasminelise-beauty.de from a German GbR structure to DigitalFreedom Global LLC, updated the governing law in the terms accordingly, and pushed the company address into every imprint and footer copyright line that referenced me personally. On the English surface of theapparchitect.com the pricing is now shown in USD; the German surface stays in EUR. These are not glamorous changes. They are the kind of changes you make once and really do not want to make twice, so getting them consistent across every repo in one sitting matters.
I also added a batch of new pages to theapparchitect.com: regional lead pages and several industry-specific pages, each making its own case rather than reusing the same copy with a city name swapped in. The positioning across all of these is explicitly remote-first. I removed the physical-presence promises that had crept into three of the regional pages, which were accurate but not actually a selling point for the clients those pages are meant to reach. If someone in Cape Town or Hamburg is considering custom app development, the relevant proof is what I have shipped, not whether I can show up to their office.
On jasminelise-beauty.de, the YouTube data pipeline that surfaces the salon’s video view counts was silently blocking CI instead of updating gracefully when the API quota hit a limit. I fixed it to fail loudly and continue, and separately corrected a rounding error in the listing display that was showing lower view counts than the actual numbers. Small things, but client sites get the same care as my own.
What This Looks Like From the Outside
A client who hires an independent developer is trusting one person to make sound architectural decisions, catch their own mistakes, and keep shipping even when the work is unglamorous. Sixteen commits to evolve a pipeline architecture, followed immediately by a Monday of legal housekeeping across four sites, is a fair portrait of what that actually looks like in practice. Not every week is a major release. Most weeks are this: one interesting technical problem solved well, and several other things maintained or corrected that nobody will ever see.
If you are weighing whether to have a custom app built and want to see more of how I work, the services page is the right starting point.
This week’s most active repository, nblm-podcast-gen, is private. But the pipeline architecture I described here, living listener profiles, a history ledger with hard diversity caps, and explicit external-service wait logic, is a pattern I apply in client work wherever a system needs to behave differently based on its own recent history.