Skip to main content

Twenty-one retrospective posts in one sprint, and the fact-checker that read every draft first

Share:XLinkedInHN
Pipeline diagram of parallel research subagents feeding parallel writer subagents, then a single fact-check subagent per batch reviewing every draft before commit.

Today I published twenty-one retrospective blog posts on this site in a single sprint. Before today the archive had about fifty-eight. Some of the new posts are years old in content: a Streamlit to-do app, a 2022 Data Mining lab, a Hotel Delivery System that predates my first job. Others are recent tools I never got around to writing up. Twenty-one posts in a day is not a rate I can hit by hand, and not one I would trust an AI writer to hit alone. What made me trust the output enough to publish is that a separate subagent read every draft before I committed and refused to let obvious fabrications through.

This is a post about that workflow.

The pipeline

Five batches of roughly four posts each. Each batch ran the same shape.

flowchart LR
    R1[Research: repo A] --> W1[Writer: repo A]
    R2[Research: repo B] --> W2[Writer: repo B]
    R3[Research: repo C] --> W3[Writer: repo C]
    R4[Research: repo D] --> W4[Writer: repo D]
    W1 --> FC[Fact-check subagent]
    W2 --> FC
    W3 --> FC
    W4 --> FC
    FC --> H[Kaushik applies corrections]
    H --> G[git commit]

Step by step:

  1. Research. One subagent per repo. Clones the repo, walks the code, returns a structured bundle of facts and an explicit list of unknowns. Prompt is blunt: facts only, every past batch caught one to six invented specifics.
  2. Writer. One subagent per repo, given the raw facts, the style guardrails, and the list of unknowns that must stay unknowns rather than get smoothed over.
  3. Fact-check. One subagent per batch. Re-clones every repo from a clean state. Reads each draft. Verifies every specific number, filename, function name, quoted commit message, and stated commit SHA against ground truth. Returns a report grouped by post.
  4. Human. I read the report, apply the corrections, then commit.

Five batches, five writer passes, five fact-check passes. Plus a bonus finding I will come back to.

What the fact-checker actually caught

Every batch surfaced real errors. Grouped by shape:

TypePostWhat the writer saidWhat the code said
Wrong countAarogyaVaani"8 supported languages" (inferred from a languages.py with 80+ ISO codes)The Vapi system prompt lists 3: Hindi, English, Kannada
Wrong quoteAarogyaVaani"BP ki dawai" as the village-vocab exampleThe prompt uses "BP badh gaya" instead of "hypertension"
Wrong commit-message quoteVitaScaleClaimed a message reads "clamp grader scores to (0.001, 0.999) strictly inside (0, 1)"The actual message uses an em-dash between those clauses. Paraphrased instead
Wrong countAntigravity-AutoAccept"five workspace settings"About a dozen
Wrong keybindingAntigravity-AutoAccept"Ctrl+Shift+Y toggles it"Ctrl+Shift+Y is bound to accept-now, not toggle
Wrong countBlitz-windows"twelve service files, north of 8,000 lines"Fourteen files, roughly 6,500 lines
Invented featureBlitz-windows"the companion app advertises itself with mDNS"No mDNS code in the tree. Clause deleted
Wrong countSAMgen"2,362 lines across 12 files"2,272 lines across 11 files
Wrong pathHackathon-explorerJSON at public/data/hackathons.jsonActually src/data/hackathons.json
Fabricated functionData Mining LabA def euclidean(...) helper in clustering.pyNo such function. Distance is inlined; math is not imported
Fabricated SQLData Mining LabA CREATE TABLE BASE (...) DDLNothing like that in the todo repo. Rewrote to describe shape
Wrong date and countData Mining Lab"Twelve commits, all landing on 2022-05-08"Eleven commits, all on 2022-10-05
Wrong function callStreamlit-DBMSDelivery-person picker is random.randint(1, 10)Actually random.randint(0, len(b) - 1) indexing a list

Two patterns are worth naming.

Most of these errors came from the writer smoothing over an unknown. A languages.py file with 80+ ISO codes is not the same as an assistant that speaks 80+ languages, but if you do not stop to notice the difference, you write the confident sentence. The research prompt asks for unknowns to be flagged and the writer prompt forbids inventing to fill gaps. It still happened a lot. The fact-checker is the load-bearing part of the pipeline for this failure mode, not the writer prompt.

The fabrications are also always plausible-adjacent. Nobody invented a keybinding out of nothing; they invented one that sat next to a real keybinding in the source. Nobody invented a function; they invented one you would expect to see. That is the shape a distracted reader misses and a fresh clone-and-grep catches.

What still slipped through

Two things I need to own.

The first batch of the second run shipped with four fabrications intact. The fact-check report was correct. My correction pass was not. Claude Code's Edit tool requires a Read of a file in the same session before editing, and my applier subagent had created those MDX files rather than reading them first, so half my edits silently no-op'd. I only noticed after the push, when a grep -n for the offending strings kept finding them. Fix commit an hour later. A fact-check that catches an error does not help if the correction step fails silently.

The fact-checker hallucinated once. It reported the LeetCode-Stats scraper post contained a raw MongoDB password. It did not; the post used <user>:<password> placeholders correctly. Cost me a re-read. I would rather have a fact-checker that occasionally flags a false positive than one that shrugs at a real fabrication, but the tradeoff is worth naming: it is not an oracle, and if you treat its report as gospel you will start "correcting" things that were already right.

One post also ships with real credentials still visible in the git history of the underlying repo. I rotated the Atlas user during the audit, but rewriting the history is not my call on somebody else's clone.

Where the costs sit

The writing itself is fast. A batch of four writer subagents finishes in about fifteen minutes of wall clock, because they run in parallel. The research pass is a similar shape.

The fact-check is where the wall clock goes. Each batch took several minutes because the fact-checker re-clones the repos, walks the code, and compares every claim. Fine. That is the whole point.

The part that does not compress is me. Reading the report, applying corrections, rotating credentials when the audit finds one, sanity-checking the false positive: that is human time and stays human time. For twenty-one posts it added up to most of a working day.

Why I would do this again

The alternative to this workflow, for twenty-one retrospective posts I have been carrying around for years, is one of:

  • One post every two months for the next year, which means I write two and quietly stop.
  • Publish writer output without a fact-check and hope nobody reads carefully enough to catch the fake function definitions and the wrong keybindings.

Fact-check subagents are a third option. Fast where I am slow, stubborn about specifics in a way I am not after post fourteen, and they catch their own writer's fabrications more often than they miss them. Enough to make the difference between "publish" and "keep in drafts forever."

Where I would not use it

The fact-checker catches invented specifics inside a well-scoped repo. It cannot catch invented framing, invented emotional beats, or an invented narrative arc that the writer built to make the post sound better than the code deserves. If the shape of the story matters more than the exact function names, this pipeline is not the right tool.

I would also not run it where the unknowns list is long and the reader is going to trust a confident-sounding sentence. Fact-check is good at "the file is called X" and bad at "and this is why it matters." The second one is on me.

The bonus finding

Running twenty-one repos through a fact-check pass surfaced three real credential leaks in old public repos of mine that I had forgotten about. Those get their own writeup at /blog/three-credential-leaks-in-my-old-repos. I did not go looking; the workflow found them on the way to something else, which is the kind of second-order value from an audit-shaped pipeline that I keep underestimating.

Cite as: Saravanan, K. (2026). Twenty-one retrospective posts in one sprint, and the fact-checker that read every draft first. Kaushik Saravanan. https://www.kaushik.cv/blog/factcheck-loop-twenty-one-posts