+180 XP

Automating recurring work

The same prompt you run by hand every Monday morning can run itself, generate a competitive brief, and land in your inbox before you finish your coffee. The gap between a one-off Claude task and a hands-off pipeline is smaller than it looks, and this lesson closes it.

You already know how to get a great result from Claude once. Now we make it repeatable, scheduled, and safe.

The three places automation lives

There are three rungs on the automation ladder, and you climb them based on how much control and infrastructure you want.

Rung 1: Inside the Claude apps. Projects, Connectors, and Skills let you do a lot without touching code. A Connector plugs Claude into an external system (Gmail, Google Drive, Linear, a database) using MCP, the Model Context Protocol, which is the open standard that lets a model call external tools and data sources. This is the no-code rung.

Rung 2: Claude Code and scheduled agents. Claude Code can run unattended in a terminal or container. Pair it with cron (the standard Unix job scheduler) and you have a recurring agent on your own machine or a small server.

Rung 3: CI like GitHub Actions. Your automation lives in a repo, runs on Anthropic's or GitHub's infrastructure, has logs, and triggers on a schedule or an event. This is the most auditable and the most "production."

We will build the Monday brief on rung 3, but I will show you where rungs 1 and 2 fit.

What "scheduled" actually means

A schedule is just a trigger. The work itself is a normal Claude agent: a prompt, some tools, and a model call. **The new ingredient is that *nobody is watching when it runs*. That changes how you design it.**

When you are in the chat window, you are the guardrail. You see a bad answer and rerun it. An unattended pipeline has no human in that loop, so the guardrails have to be written into the pipeline itself. Hold that thought; it drives every design decision below.

The concrete build: a Monday competitive brief

Here is the goal. Every Monday at 7am, Claude:

  1. Pulls recent news and pricing pages for three competitors.
  2. Compares them against last week's brief.
  3. Writes a one-page brief highlighting what changed.
  4. Emails it to the team, but only after a cost and sanity check passes.

Step 1: Make the task work once, by hand

Never automate something you have not run manually first. Build it as a Claude Code session or a Project until the output is genuinely good. This is your spec.

Capture the winning instructions as a reusable asset. A Skill is a packaged set of instructions and resources Claude loads on demand, like a competitive-analysis playbook with your brief template, tone rules, and source list. A Skill makes the same behavior portable from your desktop session into the automated one.

Step 2: Choose the engine

For a scheduled job, the Claude Agent SDK (formerly the Claude Code SDK) is the right tool. It gives you Claude Code's agent loop (planning, tool calls, file edits) as a programmable library, so your scheduled run behaves like the interactive session you already tuned.

Here is the core of the job. It is deliberately small: the intelligence lives in the prompt and the Skill, not in the Python.

python
import os
from claude_agent_sdk import query, ClaudeAgentOptions

async def generate_brief() -> str:
    options = ClaudeAgentOptions(
        system_prompt="You are a competitive intelligence analyst. "
                      "Use the competitive-brief skill. Be concise and cite sources.",
        allowed_tools=["WebFetch", "Read", "Write"],
        permission_mode="acceptEdits",
        max_turns=20,
    )

    prompt = (
        "Generate this week's competitive brief comparing Acme, Globex, and Initech. "
        "Compare against last_week.md. Output a one-page brief to brief.md. "
        "Flag only what materially changed."
    )

    result = []
    async for message in query(prompt=prompt, options=options):
        result.append(message)
    return open("brief.md").read()

Two things to notice. allowed_tools is a whitelist: the agent can fetch web pages and read and write files, and nothing else. max_turns caps how long the agent loop can run. Both are guardrails, and we are only getting started.

Step 3: schedule it

On your own machine or server, cron is enough. A crontab entry is five time fields plus a command. This runs the job every Monday at 7am:

bash
# m h dom mon dow  command
0 7 * * 1  cd /opt/briefs && /usr/bin/python run_brief.py >> brief.log 2>&1

The 1 in the fifth field is Monday. The redirect at the end keeps a log, which you will want the first time something breaks at 7am while you are asleep.

Cron is great for a personal server, but it dies when your laptop sleeps and it has no audit trail beyond a text file. For anything a team depends on, move to CI.

Step 4: promote it to GitHub actions

GitHub Actions runs your job on a schedule using the same cron syntax, but in a clean cloud container with logs, secrets management, and history. The official Claude GitHub integration is built for this.

yaml
name: weekly-competitive-brief
on:
  schedule:
    - cron: "0 7 * * 1"   # Mondays 07:00 UTC
  workflow_dispatch:        # lets you trigger a manual run too

jobs:
  brief:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install claude-agent-sdk
      - name: Generate brief
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: python run_brief.py

Your API key lives in GitHub's encrypted secrets, never in the code. workflow_dispatch is a small but important addition: it lets you run the pipeline on demand to test it, instead of waiting for Monday.

The guardrails that make it safe

This is the part people skip, and it is the part that matters. An unattended agent with a credit card and an email button is a liability until you constrain it.

Cost caps

Set a hard budget at the account level in the Anthropic Console, using usage limits and spend alerts on the API key this pipeline uses. Give the automation its own key so a runaway loop cannot drain your whole budget. The max_turns cap in the SDK is your second layer: it bounds a single run even if the prompt sends the agent in circles.

Pick the right model for the job, too. A weekly brief does not need your most expensive model for every sub-task. Cheaper models for fetching and summarizing, a stronger model for the final synthesis, keeps cost predictable.

A review step before anything irreversible

Sending email is irreversible. So split the pipeline: generate runs unattended, but send waits for a human nod, at least until you trust it.

The cleanest pattern in GitHub Actions is to post the draft brief as output and require a manual approval (an environment protection rule) before the send step runs. You get the email drafted automatically and a one-click "ship it."

Knowledge check

1. According to the lesson, what fundamentally changes when a Claude task runs on a schedule with nobody watching?

2. What is MCP (the Model Context Protocol) as described in the lesson?

3. Why does the lesson insist you run a task manually before automating it?

MULTIPLE CHOICE

4. Select ALL statements that correctly describe the three rungs of the automation ladder.

Select all the correct answers.

MULTIPLE CHOICE

5. Select ALL of the following that are genuine steps the Monday competitive brief automation is designed to perform.

Select all the correct answers.

Validate the output, not just the run

A job that "succeeds" can still produce garbage. Add a cheap validation gate before the send step: does brief.md exist, is it under a sane length, does it contain the three competitor names, does it cite at least one source URL? If any check fails, stop and alert instead of emailing nonsense.

python
def validate(brief: str) -> None:
    assert 500 < len(brief) < 8000, "brief length looks wrong"
    for name in ("Acme", "Globex", "Initech"):
        assert name in brief, f"missing competitor: {name}"
    assert "http" in brief, "no sources cited"

This is dumb code on purpose. Deterministic checks catch the failures that a model-based review might rationalize away.

Scope the tools tightly

Notice we never gave the agent a "send email" tool inside the SDK call. The send happens in a separate, dumb script *after* approval. Keep destructive or external-facing actions out of the agent's tool list and in plain code you control. The agent writes a file; your pipeline decides what to do with it.

Building Agents with the Claude Agent SDK

Watch on YouTube

Where the no-code rung fits

You do not always need a repo. If your recurring task is "summarize this week's Linear issues into a Slack post," a Connector plus a scheduled trigger inside an automation tool may be all you need, with no Python at all. The connector marketplace is growing fast, so check whether your data source already has one before you build a custom MCP server.

The decision rule: stay no-code while the task is simple and the blast radius is small. Move to the SDK and CI when you need version control, real logging, cost controls, and review gates. The Monday brief sits on the line, which is why it is a good teaching example.

Idempotency: the quiet requirement

One last concept that separates a toy from a pipeline. Idempotency means running the job twice produces one correct result, not two messes. If GitHub retries a run, or you trigger it manually to test, you do not want two emails or a corrupted history file.

Make the run safe to repeat: write to a dated file (brief-2026-01-19.md), check whether this week's brief already exists before sending, and treat "already done" as success. Cheap to add, painful to retrofit after the day you get two briefs and a confused team.

Key Takeaways

  • Get it working by hand first, then schedule it. Your tuned Claude Code or Project session is the spec; package the winning behavior as a Skill so it ports cleanly into the automated run.
  • Match the rung to the stakes. Connectors and the app for simple, low-risk tasks; the Claude Agent SDK plus cron for personal jobs; GitHub Actions when a team depends on logs, secrets, and history.
  • Write the guardrails into the pipeline, because no human is watching. Use a dedicated API key with usage limits, cap max_turns, whitelist tools, and run deterministic validation before any irreversible action.
  • Keep irreversible actions out of the agent. Let Claude generate the file; let plain code, gated by a human approval, do the sending.
  • Make every run idempotent. Dated outputs and "already done" checks mean a retry or a test run never produces duplicate emails or corrupted state.

What to do, from this lesson

These actions are compiled in the role's Playbook.

  • Make every scheduled run idempotent with dated outputs and done-checks
See the full action playbook →