// case file 03 · AI automation

Content Research Agent

One idea and a few sources in; a cited article plus LinkedIn, X and newsletter drafts out, behind an approval gate nothing skips.

RoleSolo · full-stack + AITeamSoloTimelineSep 2026StackNext.js + Node.js10criteria in every review scorecard
// 01 the problem

Turning a content idea into a researched, cited article and channel posts took hours, and AI drafts tend to invent sources and skip the human.

// architecture
Content Research Agentactive
  1. Idea + sources (human)
  2. Source type (decision)
  3. Firecrawl (step)
  4. Extract PDF·DOCX (step)
  5. n8n + Mistral OCR (trigger)
  6. Usable text? (decision)
  7. Excluded (thin / paywalled) (failure)
  8. Pick evidence (human)
  9. Plan + cited article: Sonnet (AI)
  10. 10-point scorecard (AI)
  11. Edit + revise (human)
  12. Channel drafts: Haiku · LinkedIn · X · newsletter (AI)
  13. Approval gate (decision)
  14. Newsletter via Resend (output)
  15. LinkedIn + X (simulated) (output)
  16. Supabase + RLS (store)
  17. Sentry (failure)
// 02 how it works
  1. Step 01

    An editor gives an idea, source URLs, or both; audience is the only required field.

  2. Step 02

    Firecrawl retrieves URLs; PDFs, images and DOCX extract directly, scans route through an n8n + Mistral OCR workflow.

  3. Step 03

    Thin, boilerplate or paywalled sources are marked failed and excluded, never bypassed.

  4. Step 04

    Sonnet plans and writes a cited article; a ten-criterion scorecard flags unsupported claims and missing citations, each linked to its quote.

  5. Step 05

    Haiku adapts the article to LinkedIn, X and newsletter, each checked against real platform limits.

  6. Step 06

    Approval needs a fresh passing evaluation plus all three drafts; the newsletter then sends for real via Resend.

// numbers I’d defend
10review criteria
3channel drafts per article
1approval gate, enforced in the database
< 40extracted chars routes a PDF to OCR
// 03 decisions that mattered
01

Approval is a real gate, not a UI state

Once granted it locks every action until an admin sends it back with a required, visible reason.

02

A recursive RLS policy silently broke every team check

Reproduced directly in Postgres (42P17: infinite recursion in policy) and fixed with a SECURITY DEFINER helper.

03

Two race conditions, one bug

Double-submits and generate-while-reloading both made duplicates. A fixed delay still produced three; an atomic database claim fixed both.

04

Fetching a URL is an SSRF risk

Source retrieval checks the resolved destination before it ever fetches.

// incident

A recursive RLS policy silently broke every team check

Team features failed quietly with no clear error in the app.

Reproducing the exact query directly against Postgres showed 42P17: infinite recursion detected in policy, a rule on team_members that checked team_members.

Fix: A SECURITY DEFINER helper breaks the recursive chain; team membership is now enforced below the UI and tested with a real non-admin account.

// 04 results
10criteria in the review scorecard
3channel drafts per article
1approval gate, enforced in the DB
// 05 takeaway

If a human has to approve it, the database has to know. A disabled button is not a control.

Next.jsNode.jsSonnetHaikuFirecrawln8nMistral OCRSupabaseResendSentry
// what I’d do differently
  • Map every state and who owns it (research, evaluation, editing, revision, approval, sending) before adding individual controls.
  • Run the full signed-in workflow with a real editor earlier, before polishing smaller parts.
  • Reserve testing and write-up time throughout the week, not at the end.
// artefacts
// contact

Let’s talk

Open to software engineering and AI automation roles, plus selective contract work. I read every message.

psst, click it
● DRAFT

Show me the copy-paste.
I’ll tell you if it should be a system.

01
02
How often?
03
Which tools?

Hiring? Tell me about the team.
I’ll tell you where I’d help first.

01
02
03
Timeline
04

You get a reply within 24 hours: whether it should be automated, what I’d use, and a rough timeline.

You get a reply within 24 hours.