Real-World Project: A Posting Collection & Analysis Agent from Start to Finish A developer has published a field guide chapter detailing a daily-running autonomous agent, built with Claude Code, that collects contest and grant postings, filters out risky clauses, and produces a one-page morning digest. The system enforces a three-part checklist before recommending anything to a human — whether entries are open, whether the user is eligible, and whether the terms are safe — after an earlier version reported an already-closed contest as a participation candidate. The developer argues that writing tests first narrows an AI's scope of work from 'make something reasonably good' to 'make these tests pass,' and that failures must be surfaced alongside successes for the report to be trusted. This is chapter 8 of my book Building Autonomous AI Agents with Claude Code — a field guide to turning Claude Code from a coding assistant into an agent that remembers, verifies its own work, and knows when to stop. Everything below is from a system I actually run every day on one Windows PC. "Automatically collect contest and grant-program postings every day, filter out risky clauses, and produce a one-page report to read in the morning." The input posting sites , processing parsing and filtering , output digest , and schedule every morning are all in one sentence. When you hand work to an AI, this sentence becomes both the work order and the completion criterion . If it's fuzzy, the AI will very quickly build "something plausible that isn't what I wanted." The first prompt handed to the agent looks like this. If you don't write the "completion criterion," the AI's definition of done is "I finished writing the code." The done we want is "it runs." This two-line difference is also why the auditor from Chapter 6 is needed. The AI will plausibly guess, "that site usually has this kind of structure." A parser built from that guess is always wrong. Enforce the order. curl -sL "https://example.org/contest/list" -o /tmp/list.html grep -o 'class=" a-z - "' /tmp/list.html | sort | uniq -c | sort -rn | head -20 Measuring reveals things you could never know from guessing. The third one is a failure we actually experienced. Without parsing the closed/open status, we assumed "it's on the list, so it's still open" and reported a contest that had already ended as a participation candidate. We were verifying the hard things like eligibility and terms while missing the most basic question: "is it accepting entries right now?" So this rule is now baked into the collector. Checklist before recommending anything to a human: ① Is it accepting entries? ② Are we eligible? ③ Are the terms safe? Only something that passes all three is a candidate. Freeze the HTML fragments confirmed during measurement directly into test fixtures. Even if the site changes later, these tests guard against regressions in the parser logic itself . tests/test collector.py SAMPLE = """