Every post in this series starts the same way: I open Notion on a Friday and there’s a tool waiting for me that I didn’t pick. That’s on purpose, and it took me building an actual automation to get there.
For a long time before this series existed, I was stuck in what I’d call zero learning mode. I like learning new things, genuinely, but I’d sit down with time set aside and just… not know what to point it at. Too many options, no filter, no momentum. I wasn’t short on curiosity, I was short on a starting point. So I built one: an n8n workflow that picks a tool for me every week, does the research, and hands me a scoped 30-minute exercise, no decision required on my end except whether to actually do it.
This post is the one about that machine instead of about whatever it picked this week.
What it actually does, end to end
Every Friday at 1 AM, a scheduled trigger kicks off a run. From there:
- It pulls my active Topics from a Notion database (more on what’s in there below), rotates through them by week number so the same topic doesn’t come up every time, and picks two fallback topics alongside the primary one.
- For the primary topic, it searches GitHub for repos matching that topic’s search query, pushed to recently (7-14 days back depending on the topic), and cross-checks every candidate against every tool I’ve already been given, going back to week one. If I’ve seen it before, it’s out. No repeats, ever.
- An AI Picker Agent looks at the list of fresh candidates across all three topics and picks one, against explicit criteria I’ll get into.
- It pulls that repo’s README, recent release notes, and a bit of web context.
- A second AI Writer Agent turns all of that into the actual weekly brief: a plain-English summary, a specific 30-minute hands-on challenge, a “Critics Corner” of real failure modes, and a blog angle.
- It writes the result into Notion and pings me in Slack with a one-line summary and a link.
If nothing fresh turns up across all three topics in a given week, it skips the write entirely and just tells me that in Slack instead, so I know to widen the search rather than getting a stale or forced pick.
How the topics got built
The Topics database isn’t static; it’s evolved. Early on it had three broad entries: Golang, Security, AI. All three are still sitting in there, switched off rather than deleted, next to the fourteen narrower topics that replaced them: things like Offensive Security, Code & Dependency Vulnerability, SOC & Alert Triage, Identity & Zero Trust, Cloud Security & Supply Chain. Broad categories turned out to surface too much noise (or too much of the same famous tool over and over); narrower ones with an explicit description of what “good” looks like for that lane surface better picks.
These aren’t random either. The topics track areas that interest me, informed by some of my own research plus AI helping shape the actual wording, and a good number of them line up directly with what I’m responsible for in my day job right now: vulnerability triage, cloud security posture, identity and zero trust, SOC tooling. The generator isn’t just feeding a hobby; a lot of weeks it’s feeding something I need to already understand better for work, and the tool just happens to be the vehicle.
Each topic carries more than a name. A Description steers the picker (what “good” looks like for this lane), Exclude Hints rules out things I’m already tired of seeing, GitHub Queries is one or more literal search strings for that topic, and Lookback Days controls how recent “recent” needs to be, 7 days for fast-moving lanes like Golang or container tooling, 14 for security topics where meaningful projects move slower.
Two agents, two jobs
The workflow uses two separate AI agent calls, not one that does everything, and that was deliberate: reading and picking is a different task from writing, and I wanted each one focused on just its job instead of doing both passably.
The Picker Agent’s entire job is choosing one repo out of a shortlist. Here’s the actual system prompt, close to verbatim:
You are selecting a GitHub repo for a senior security/DevOps engineer to explore for 30 minutes.
This week's topics:
[topic descriptions, exclude hints, and any trending context]
SELECT repos that are: niche, recently active, hands-on (produces visible output in 30 min), scriptable.
REJECT: awesome-lists, docs-only, famous widely-known tools (e.g. ollama, nmap), platforms needing hours of setup.
Respond ONLY with valid JSON (no markdown fences): {"selected_index": <1-based integer>, "reason": "<one sentence>"}
That REJECT line exists because those are exactly the kinds of picks that waste a week: a list of links instead of a tool, documentation with no code to run, or something so famous I already know it (or so heavy to set up that “30 minutes” becomes a lie). Constraining the picker to a strict, fence-free JSON response also means the next step in the pipeline can parse it without babysitting the model’s formatting.
The Writer Agent never sees the shortlist or the rejection logic at all; it only sees the one repo the picker already chose, plus real research about it (README, release notes, web context). Its job is synthesis, not selection, and its system prompt is shaped entirely around producing four specific fields as strict JSON: a technical summary of at least 150 words, a 30-minute hands-on challenge with exact commands (not vague suggestions), a “Critics Corner” that has to name three concrete ways the tool fails at scale, two unstated assumptions the creators are making, and one likely security vulnerability, all written as flat declarative statements (“this fails at X”), not hedged ones (“this may fail”). That last instruction matters more than it looks: hedged output is easy for an LLM to produce and useless to act on. Forcing declarative claims means the Critics Corner section actually has something concrete for me (or now, for the rabbit-hole-post skill that turns these into blog posts) to work with.
The technical chain
Laid out node by node, the pipeline looks like this:
Discovery: Schedule Trigger (Fridays, 1 AM) → Get Active Topics (Notion) → pick primary + 2 fallback topics by weekNumber % topicCount → optional SearxNG trend search per topic → build a GitHub search query (one of the topic’s configured queries, plus a pushed:> date filter) → GitHub Search API, sorted by most recently updated, 15 results per topic → flatten all three topics’ results into one candidate pool → pull every repo URL that’s ever appeared in the Learning Sandbox database → filter out anything already seen.
Selection: if there are zero fresh candidates left, post a Slack warning and stop. Otherwise, build the Picker Agent’s prompt from the surviving candidates and topic context, call the LLM, parse its JSON response, and resolve which actual repo it picked.
Research: fetch that repo’s README directly from GitHub, fetch its 3 most recent releases, run one more SearxNG search combining the repo name and topic for broader context, and combine all of it into one research bundle.
Writing: build the Writer Agent’s prompt from that bundle, call the LLM, parse its strict-JSON response, and assemble a Notion page-creation payload (long text fields get chunked to fit Notion’s per-block character limit).
Delivery: create the Notion page with Status: New, then post a Slack message with the week number, topic, repo, a link to the new Notion page, and the blog angle’s hook line.
Nowhere in this chain does anything actually run the tool. That’s the generator’s job done: hand me a real, unrepeated, scoped candidate with a plan. What happens next is mine.
Where this actually pays off
The generator gets me to a starting point every week; it doesn’t get me all the way to a finished post. Two recent weeks are the clean evidence for why that gap matters. The ThreatCaddy week’s Sandbox Challenge assumed the app would just render; running it for real surfaced a genuine StrictMode bug that silently broke first load, root-caused and fixed. The Tejolote week’s exercise didn’t even touch the real binary, it simulated the tool’s output with cosign alone; running the actual tool against a real GitHub Actions build surfaced two more real bugs neither the generator nor a simulated lab would ever have shown me.
None of that is a knock on the generator. It’s not supposed to run the tool for me, verify its own output, or catch bugs in software it’s never executed; it’s supposed to solve zero learning mode, and it does that completely. The rest, actually doing the thirty minutes, hitting whatever breaks, and writing down what I find, has to stay a human step. That’s the whole reason this is a two-part system: an automation that removes the “what do I even look at” problem, and a person who still has to sit down and do the tinkering.