Lesson 5 of 6 · 15 min · Checked on 10 October 2026
Final project: a system that works alone, with brakes
By the end of this lesson you will be able to:
- Build an agent that every morning compares a page with yesterday's and writes a summary
- Put four brakes in place: a box of its own, a spending cap, a log and a stop button
- Schedule and unschedule the agent with cron or schtasks
- Test each brake before leaving the system to run on its own
The junta de freguesia (parish council) publishes notices on a page: water cuts, the boat timetable, roadworks. Mr Manuel wants to know every morning whether there is news, without opening the page and comparing it in his head with yesterday's.
Today you build that watcher, with a test page of your own, which you change by hand to simulate the notices.
Before you start: what “done” means
The project is done when:
- the folder
experiencias/vigiaexists, with Git and a test page; - the watcher keeps one copy per day and compares it with the previous one;
- when there are changes, an agent writes a summary in
resumos/; - every run leaves one line in the log, with what it did and the estimated cost;
- the four brakes have been tested one by one;
- the watcher ran on a schedule and then you removed the scheduling.
The file and folder names stay in Portuguese, so the commands match the course: vigia (watcher), pagina (page), historico (history), resumos (summaries), registo.csv (log), PARAR (stop), and in config.json the keys tecto_execucao_usd (per-run cap), tecto_mes_usd (monthly cap) and max_voltas (maximum turns).
The four brakes:
- A box of its own: it only works in its own folder, with the agent having no tools and no network beyond the call to the model.
- Spending cap: one per run and another per month; each run may only spend what is left of the month, and two runs never overlap.
- Log: one line per day with what it did and how much it cost.
- Stop button: if a file called
PARARexists, it does not run.
Watch out
The watcher does not send emails or messages. If one day you want an alert on your phone, the agent writes the draft and a person decides whether it goes out.
This is how it looks on 6 October 2026; your screen may differ. Look for the button with the same name. Guides: Agent SDK, crontab, schtasks.
Step 1: the folder and the test page
Create experiencias/vigia, run git init and open the agent inside it:
Create pagina/index.html: a simple “Junta notices” page with three made-up notices,
each with a date and one sentence. Also create a .gitignore with .env, .venv/ and historico/.
Do not install anything.
Check: the page opens in the browser and the .gitignore has the three lines.
If it did not work: if the page does not open, check that it is called index.html and is inside pagina/.
Step 2: the watcher without a model
First, the predictable part, with code only:
Create vigia.py, in Python, with no extra packages. In this order:
1. If the file PARAR exists, write “stopped” in the log and exit.
2. If the file A_CORRER exists, write “already running” and exit; otherwise create it and delete it at the end, whatever happens.
3. Read config.json (tecto_execucao_usd, tecto_mes_usd, max_voltas). Add up the month's costs in registo.csv,
error lines included. What is left is tecto_mes_usd minus that sum; if it is zero or less,
write “monthly cap” and exit. This run's cap is the smaller of tecto_execucao_usd and what is left;
write it in the log.
4. Copy pagina/index.html to historico/YYYY-MM-DD.html.
5. Compare it with the previous copy. With no previous copy or no differences: write that in the log and exit.
6. With differences, for now, just print the diff.
Each line of registo.csv has: date, time, what it did, estimated cost.
Accept --data YYYY-MM-DD so I can test different days. Create config.json with low values that I will adjust.
Show me the plan before writing.
Check: run python vigia.py --data 2026-10-01, change a notice on the page, run it with --data 2026-10-02. The second time it prints the diff.
If it did not work: if the second run says “no differences”, check that you saved the page before running.
Step 3: the agent that summarises
Now you connect the model, only for what the code does not know how to do: explaining the changes in plain language.
Install claude-agent-sdk in a virtual environment .venv. In step 6 of vigia.py, call the Agent SDK
with the diff in the request and these options: cwd equal to the watcher's folder, permission_mode="dontAsk",
disallowed_tools=["*"], max_turns=max_voltas and max_budget_usd equal to this run's cap (the smaller of
tecto_execucao_usd and what is left of the month). Ask for a short summary in the same language as the page, inventing nothing.
vigia.py saves the summary in resumos/YYYY-MM-DD.md and the total_cost_usd in the log.
The script itself reads the key from .env and never shows it.
If the SDK stops with an error, log the reason and whatever cost comes in the answer, and exit. If no cost comes at all,
log “unknown cost” and create the file PARAR, so I review it before the next run.
Add --simular ok|tecto|indisponivel, which does not call the model: it returns a fixed summary with cost 0.01,
an end by error_max_budget_usd with a cost, or a connection error with no cost.
The key stays in .env, as in lesson 1/06. The "*" takes all tools away from the agent: it receives the diff, returns text, and your code is what saves. Without it, in dontAsk mode it could still read files in the folder, including the .env.
If it did not work: “Invalid API key” means the script is not reading the .env; check the name ANTHROPIC_API_KEY.
Tip
The cost in the log is an estimate, and the SDK caps are approximate too: they count estimated costs and are checked between turns. The real brake is the spending limit in the provider's console (lesson 1/06). Run once, look at the cost in the log and adjust the caps in config.json.
Step 4: testing the brakes
First ask for the prediction:
Without changing code, tell me what vigia.py does and which line it writes in the log in each case:
PARAR exists; the monthly cap was reached; the page did not change.
Then prove it:
- Create the file
PARARand run. The log says “stopped”, with no cost. - Delete
PARAR, settecto_mes_usdbelow what you have already spent and run. The log says “monthly cap”. - Put the value back and run on a day with no changes. There is no call to the model and no cost.
- On a day with changes, run with
--simular tecto. The log shows the end byerror_max_budget_usdand the cost. - Set
tecto_execucao_usdabove what is left of the month and run with--simular ok. The log shows this run's cap equal to what is left. - Run with
--simular indisponivel. The log says “unknown cost” andPARARappears; delete it. - Start two runs at the same time with
--simular ok. One runs; the other writes “already running”.
If it did not work: if the result does not match the prediction, show the agent the log line and ask it to fix only that point.
Step 5: scheduling
Show me how to run the watcher every day at 7:00 with this folder's .venv: on Linux or macOS,
a crontab line; on Windows, a correr.bat and the schtasks command. Do not schedule anything: I will do it.
On Linux or macOS, run crontab -e and add the line, with your own path:
0 7 * * * cd /caminho/para/experiencias/vigia && .venv/bin/python vigia.py >> cron.log 2>&1
The five fields are minute, hour, day, month and day of the week. crontab -l shows what is scheduled. Cron starts with a minimal environment, so the script has to read the .env by itself.
On Windows, with the correr.bat (“run.bat”) that the agent wrote:
schtasks /create /tn vigia /tr "C:\caminho\experiencias\vigia\correr.bat" /sc daily /st 07:00
schtasks /query /tn vigia
Tip
To unschedule: in cron, crontab -e and delete only that line; on Windows, schtasks /delete /tn vigia /f. Do not use crontab -r for this: it deletes all the lines, not just the watcher's.
If it did not work: if nothing happens at 7:00, look at cron.log; the most common mistake is a wrong path.
Exercise
How I know it worked there is one copy per day in historico/, a summary only on the days with changes, one line per day in the log with the cost, and crontab -l (or schtasks /query) no longer shows the watcher.
If it didn't work
- Summaries with things that are not on the page: reinforce in the request “summarise only what is in the diff” and check that the diff goes in the request.
- The log has two lines on the same day: the watcher ran twice; look at
crontab -l. - The month's cost does not match the console: the log's cost is an estimate; the console rules.
What you take from this project
A system that works on its own and that you know how to stop: the code does the predictable, the agent only what needs language, everything is logged and nothing goes out without a person.
Videos
Agent loops in practice with Claude Code and Codex: a manager loop that reads the backlog and a worker loop, with skills, verification and guardrails at the end.
Test what you learned
Corrections to this lesson (1)
- : After a second external review: each run may only spend what is left of the month, error lines count towards the sum, an unknown cost creates `PARAR`, and two runs never overlap; `PARAR` prevents the next runs, it does not interrupt one in progress (you stop the process); the tests gained four simulated cases, with no paid calls.