← Builds

    04Personal · Agent ops · Since Jun 2026

    Building a research agent that works the night shift.

    Got a research brief into my inbox on 77 of 93 nights by moving an overnight agent onto GitHub Actions, with scripted research and a deterministic validator deciding what gets sent.

    Role
    Design, build, operation
    Stack
    GitHub Actions · Node · Claude Code, headless · Resend
    Status
    Runs every night
    Code
    Code is private
    How night-ops turns research into a briefAt 01:00 UTC a deterministic script gathers and scores research. An agent writes the brief. A validator decides whether it is emailed; if anything fails, a failure alert is sent instead. The brief is committed only after it is sent.RESEARCH · NO MODELAGENT WRITESVALIDATOR✕ ALERTBRIEF BY MORNING01:00 UTC
    Cover: detail of Sahurs Meadows in Morning Sun, Alfred Sisley, 1894. The Met, Open Access.
    Problem
    A research brief written overnight by an agent, with nobody watching. A green run has to mean a real brief.
    What I built
    First a Hermes Agent cron job. Now scripted research, an agent that writes and a validator that decides, then email, on GitHub Actions.
    Result
    77 of the 93 nights from 20 July to 20 October 2026 ended with a brief in my inbox.
    How night-ops turns research into a briefAt 01:00 UTC a deterministic script gathers and scores research. An agent writes the brief. A validator decides whether it is emailed; if anything fails, a failure alert is sent instead. The brief is committed only after it is sent.RESEARCH · NO MODELAGENT WRITESVALIDATORALERTEMAILCOMMIT

    What it does

    At 01:00 UTC a GitHub Actions job starts. A script gathers candidates from 13 public sources, Hacker News, arXiv and GitHub among them, and scores them. No model is involved at this stage, and the run stops if it finds fewer than six candidates.

    Claude Code then runs headless, with at most 50 turns, and writes the brief: three sourced topics and some short drafts. A validator decides whether it’s sent. If it passes, the brief is emailed and then committed to the repository. If anything fails, I get a failure alert instead. The whole job has 30 minutes.

    A month on Hermes Agent

    It started in June on Hermes Agent, Nous Research’s open-source agent, running on a small server. Hermes keeps its own cron schedule and reports back in a chat, so the first version needed no pipeline. On 18 June it wrote a plan for the project. The next day it ran the research itself, with its own web search, and wrote the first brief.

    On 21 June it built the delivery around itself in one evening: nine commits in four and a half hours. Email through Resend, an HTML template, social cards that pair a painting from The Met with the headline, a style guide drawn from my own writing and a list of AI tells to strip out. Its cron was set for 01:00 UTC, the time the job still starts.

    Research never went into the repository. Each night Hermes searched and wrote with its own tools, and the scripts only sent what it wrote. Most nights a brief arrived. None of them was recorded: Hermes’s setup notes promised a push to GitHub after every run, and the repository has no brief and no commit between 22 June and 20 July.

    Two things moved me off it. The first was cost: Hermes needed the server and a paid model API, while Claude Code on GitHub Actions runs on the Claude subscription I already pay for. The second was visibility: with a cron on a server reporting into a chat, I couldn’t tell whether a night had worked. Hermes’s own plan had expected the move: start on its cron, then go to GitHub Actions and headless Claude Code once the prompts were proven.

    The move Hermes wrote

    On 20 July Hermes wrote the move itself. Its commit called the new seven-stage workflow “identical logic to Hermes cron”, and it looked complete. It wasn’t. The send step printed “Would send via Resend API”, made up an email id and returned “sent” without calling the email service. The image step wrote 20-byte placeholder files with a .png name. Research searched arXiv over a fixed window from July 2024, and every Hacker News item came back titled “HN:” and a number.

    The writing Hermes had done with its own judgement came across as scripts that printed the same text whatever the research found. The email sender it built in June had called the email service properly. The send step it wrote for the move didn’t.

    Nothing in the workflow checked what a stage produced. Working through it with Claude Code, I found the stubs the same afternoon, before the workflow ran a single scheduled night: its first run on GitHub stopped while compiling the brief, and 37 minutes after Hermes’s commit the stubs were gone. That evening the new pipeline delivered its first real brief. I rebuilt it around one rule:

    A green run has to mean a real brief.

    Rebuilt around gates

    A failed run at 01:00 is easy to miss. A missing email isn’t, which is why a failure sends its own alert.

    The gate

    The validator is a 652-line script between the agent and the inbox. It exits with an error on any hard failure, so a bad brief never reaches the email step. Its checks fall into these groups. Pick a brief to see what happens to it:

    Try it · what the gate does with a brief

    Pick a brief to validate
    • ✓Matches the brief's contractEvery required field is present, with three topics.
    • ✓Every topic cites its sourcesEach topic links what it's based on.
    • ✓No fabrication canariesNone of the boilerplate the first version made up.
    • ✓No AI-writing tellsChecked against the deslopify catalogue.
    • ✓Jargon explained where it's usedA technical term needs a plain gloss nearby.
    • ✓Lengths within boundsEach part has a word or character range.
    • ✓Openings rotate night to nightCompared with the last few briefs.
    • !Style warningssoftLogged for review, never block a send.
    Emailed, then committed

    Illustrative briefs. The check names are the real categories; the content is private.

    Social cards are made after validation, so the validator never sees the final email. Anything added downstream is unchecked by construction.

    Testing the gate

    The gate has its own test: one valid brief and 22 broken ones, each made from the valid one by breaking exactly one thing, plus two checks that openings rotate. That’s 25 assertions, and they run in under two seconds after any change to the validator.

    It’s the only test in the repository. The validator is all that stands between a bad refactor and a silent night. The suite has already caught a real bug: the rotation check could compare a brief with a later one when an older brief was validated again.

    Results

    77 / 93
    nights with a brief delivered
    652
    lines in the validator
    25
    assertions in the gate’s test
    13
    research sources
    20 July to 20 October 2026, counted from briefs committed after sending. One reader. I haven’t broken the 16 missed nights down by cause yet.

    What it doesn't check

    There is no factuality check. The prompt says every figure must trace to a fetched source, but nothing enforces it: a made-up figure reached a draft once, and I caught it by reading. The validator also never sees the final email, because the social cards are added after it runs.

    The next step is to keep a snapshot of the sources each night and check every number in the brief against them.

    Where the covers come from

    Each brief’s social cards pair a public-domain painting from The Met, picked by mood, with the headline. Hermes made the first ones on 21 June. That’s where the covers on these pages come from: details of bright, open landscapes from the same open-access collection. This one is a detail of Alfred Sisley’s Sahurs Meadows in Morning Sun (1894).