Skip to content

Repository files navigation

Loop Engineering Crash Course

Practice Projects · 12 Projects · AI Agent Loops · Claude Code


Portfolio License Projects Submitted Claude


Muhammad Sami Asghar Mughal  ·  Senior AI Agent Engineer  ·  muhammad-sami.vercel.app



Overview

A complete hands-on run through all 12 Loop Engineering practice projects — building, breaking, and diagnosing real AI agent loops from the ground up. Each project lives in its own folder with its own README.md, AGENTS.md, CLAUDE.md, .claude/settings.json, and full git history.

Projects 6, 8, and 10 are partially complete due to a documented account-level restriction (Claude Code Web / GitHub App install blocked by org admin — same root cause across all three, fully explained in each folder). Project 11 is fully specced but not live for the same reason. Every gap is named honestly and documented precisely enough to finish later.


Project Status at a Glance

# Project Concepts Difficulty Status
1 Watch a long task finish C4 Easy Done
2 Make the tests pass, then stop C5, C11 Easy–Medium Done
3 Morning brief with a memory C6, C12 Medium Done
4 Fix loop with a real checker C8, C9, C11 Medium–Hard Done
5 Codify the body (shell) C8, C11, DW Medium–Hard Done
5b Codify the body (/workflows) C8, C11, DW Medium–Hard Done
6 The doorbell loop C7, C10 Medium Partial
7 Break it on purpose C13, C14 Medium Done
8 Your own daily loop (capstone) All 6 parts Capstone Partial
9 Rehearse a routine for free A1, A3, A5 Easy Done
10 The secrets drill A2, A4 Easy–Medium Partial
11 Two-routine gate A3, A4, A6 Medium–Hard Specced
12 Dreaming loop (second capstone) C6, C11, C12 Capstone Done

Key: C = Core Concept · DW = Dynamic Workflows interlude · A = Appendix (Routines)


Projects

Project 1 — Watch a Long Task Finish

Folder: loop-project-1/ | Concept: C4 (in-session loop) | Difficulty: Easy

Command:

/loop 1m check if long_task.sh has finished (test -f done.flag);
if it has, read done.flag, tell me it's done, and stop the loop yourself

What happened: long_task.sh ran in the background. The loop checked every beat, reported "not finished yet" once, then found done.flag and stopped itself cleanly.

Outcome: Loop noticed the finish, reported it once, stopped — no human intervention.

Status: DONE


Project 2 — Make the Tests Pass, Then Stop

Folder: loop-project-2/ | Concepts: C5 (conditional loop), C11 (maker-checker) | Difficulty: Easy–Medium

Command: /goal Fix calculator.py so that running python3 test_calculator.py exits with code 0 and prints "ALL TESTS PASSED"...

What happened: calculator.py had swapped operators (add/multiply/subtract all wrong). Attempt 1 failed; code was fixed; attempt 2 passed. The test runner's exit code was the only stopping signal — not "looks correct."

Outcome: Stopped because tests passed, not because it hit the 6-attempt cap.

Status: DONE


Project 3 — Morning Brief With a Memory

Folder: loop-project-3/ | Concepts: C6 (unattended schedule), C12 (the spine) | Difficulty: Medium

Approach: No /loop or /goal — this concept requires a real scheduled Routine (cron-like). Simulated as two separate beats in two fresh sessions, each using the same plain prompt:

This is one beat of a scheduled loop. Do these steps in order:
1. Read progress.md fully first — this is your only memory of past runs.
2. Search the src/ folder for all "TODO" comments (grep for TODO).
3. Compare against what is already in progress.md's run history.
4. Write a short summary: total TODOs, new since last run, list new ones.
5. Append a new dated entry to progress.md — do not delete old entries.
6. Show me the new entry.

What happened: Beat 1 found 3 TODOs (all new). A 4th TODO was added to simulate a day passing. Beat 2 found 4 TODOs but reported only 1 as new — progress.md (the spine) was the only memory used, not conversation history.

Outcome: Second run built on the first; nothing was re-reported.

Status: DONE


Project 4 — Fix Loop With a Real Checker

Folder: loop-project-4/ | Concepts: C8 (worktree), C9 (skill), C11 (maker-checker) | Difficulty: Medium–Hard

Bug: src/inventory.pyapply_discount added the discount instead of subtracting it.

What happened:

Branch Fix Checker Verdict
fix/good-discount Real fix: + changed to - PASS — PR opened (pr/fix-good-discount.md)
fix/bad-discount Lookup table hardcoding all 3 test inputs; bug returned for anything else FAIL — caught by diff review (pr/fix-bad-discount-REJECTED.md)

Bonus: Two fresh, isolated subagents (.claude/agents/reviewer.md, haiku model) independently re-reviewed both diffs and reached the same verdicts — without any memory of this conversation.

Outcome: Good fix got PASS + PR; bad fix got FAIL with reasons; checker was not fooled by green tests.

Status: DONE


Project 5 — Codify the Body

Folder: loop-project-5/ | Concepts: DW interlude, C8, C11 | Difficulty: Medium–Hard

Engine: run_fix_loop.sh (setup / verify / reset) — pure shell, no AI, deterministic.

Candidates:

Candidate Bug
is_even Inverted condition (== 1 instead of == 0)
average Off-by-one divisor (len(numbers) + 1)
reverse_words Reversed characters, not word order

Run 1: setup → 3 dispatched agents fixed each bug in isolated worktrees → verify3/3 PASS

Run 2 (the real point): resetsetupverify without re-running the maker step → 3/3 FAIL, identical to original bugs. Proved the engine has zero memory between runs.

Outcome: One command ran the whole body. The no-memory claim was proven on-machine, not just asserted.

Status: DONE


Project 5(b) — Codify the Body, the /workflows Way

Folder: loop-project-5-b/ | Concepts: DW interlude, C8, C11 | Difficulty: Medium–Hard

Different from 5(a): Attempted the Claude Code /workflows approach instead of a hand-written shell script.

Blocked: /workflows did not exist in Claude Code 2.1.233 — the course's own "research preview" caveat applied.

Adapted: Same plain-words prompt run twice in genuinely separate claude sessions.

Run Result
Run 1 3 parallel isolated worktrees, maker + @reviewer → 3/3 PASS
Run 2 (fresh session) Redid all 3 fixes from scratch, same PASS verdicts, zero reference to Run 1

Outcome: Documented the blocker honestly, adapted the approach, still proved the same lesson (no session memory, engine vs. loop).

Status: DONE


Project 6 — The Doorbell Loop

Folder: loop-project-6/ | Concepts: C7 (event-driven), C10 (connectors) | Difficulty: Medium

Bug planted: Removed if age is None: return False from is_valid_age, disguised as a readability refactor — branch fix/age-validation-refactor.

Two automated paths tried, both blocked:

  1. GitHub Actions + Anthropic API — requires a paid API key separate from Claude Pro. Deleted.
  2. Claude Code Routine + GitHub PR webhook — blocked by org-admin restriction on "Claude Code Web" and by a deliberate choice not to grant a shared account broad GitHub access.

What was done instead: A real PR (#1) was opened via the GitHub API. The diff was read and the bug reasoned about for real, and the review was posted as a real GitHub PR comment — just triggered by hand instead of a webhook.

Checklist Item Result
PR got a real bug-flagging review Done
Review fired automatically (zero-prompt) Blocked — see above
New commit re-fires via synchronize Not testable without auto-trigger

Status: PARTIAL


Project 7 — Break It on Purpose

Folder: loop-project-7/ | Concepts: C13 (cost), C14 (observability) | Difficulty: Medium

Cost (real, measured):

Cadence Monthly cost
Daily ~$4.89
Hourly ~$117
Every 5 min ~$1,410

One beat = 38,808 tokens at Sonnet 5 pricing ≈ $0.163/beat. Full math in COST.md.

Sabotage: AGENTS.md hardened first (every beat must write one SUCCESS/FAILED line to beat.log). Then a beat was pointed at src_incoming/ — a folder that does not exist.

Result — did NOT fail silently:

2026-08-17T00:12:43Z | FAILED | src_incoming: No such file or directory | NEEDS HUMAN: ...

Diagnosis from spine alone: What failed (src_incoming/ missing), when (2026-08-17T00:12:43Z) — read from beat.log + progress.md only, no replay needed.

Status: DONE


Project 8 — Your Own Daily Loop (Capstone)

Folder: loop-project-8/ | Concepts: All 6 parts | Difficulty: Capstone, 2–4 hrs

Chore: docs-freshness — checks TASKS.md/README.md for contradictory or mismatched status markers on this repo's own real files.

Beat-by-beat summary:

Beat Action Result
1 Scan Found 2 real CONTRADICTIONs (Projects 6 and 7 — duplicate Status lines)
2 Maker + Checker Fix drafted in worktree → Checker: PASS → merged to local main
Bad-fix scenario Planted bad fix (falsely marked P8 "done") → Checker: FAIL, with reasons
3 Scan 0 issues (first clean beat)
4 Scan 0 issues (second consecutive clean beat)
5 Soft-stop Loop stopped itself from beat.log/progress.md alone — no prompting

Honest gap: Real cron heartbeat not re-attempted — Project 6 already proved the same account-level blocker. Simulated as 5 memory-isolated beats, labeled honestly as such.

Checklist Item Result
All 6 parts built and exercised Done
Real bug found and fixed, bad fix rejected Done
Loop stopped itself via spine Done
Ran unattended for a real week Not met — account restriction documented

Status: PARTIAL


Appendix Projects — Routine Drills (9–11) + Second Capstone (12)

From the course's Appendix on Routines. Projects 9–11 reproduce the appendix's main failure cases at low cost/risk. Project 12 is a full second capstone, not a drill.

Known blocker: Projects 9–11 require a Claude Code Routine attached to a GitHub repo. The same org-admin restriction documented in Project 6 applies. Each project attempts it honestly and documents what was achievable without it.


Project 9 — Rehearse a Routine for Free

Folder: loop-project-9/ | Concepts: A1, A3 (one-off schedules), A5 (reading runs) | Difficulty: Easy

Adapted: Used a no-repo routine (GitHub App connection blocked, same as P6). The A5 lesson does not require a repo.

Runs:

Run Prompt Task result Status field
1 Compute 2026+20+8 Correct (2054) success is_error=false
2 Read a non-existent file Failed (file not found) success is_error=false

Key lesson (A5): The status column reports whether the session completed cleanly — not whether the task actually happened. The real outcome is only visible in the transcript.

Status: DONE


Project 10 — The Secrets Drill

Folder: loop-project-10/ | Concepts: A2 (the environment), A4 (secrets) | Difficulty: Easy–Medium

Half 1 — gitignore mechanic (fully proven locally): Real .env gitignored, committed, then a real git clone confirmed .env absent in the clone — identical to what a cloud Routine's fresh clone would see.

Half 2 — environment variables (mechanism proven, custom secret deferred): A real Routine confirmed that Bash can read env vars directly (~140 platform vars present). Adding a custom secret (PROJECT10_DUMMY_TOKEN) still requires the claude.ai web UI — no tool can do it.

Side finding: The Default environment already has credential-shaped var names (GH_TOKEN, AWS_ACCESS_KEY_ID, etc.) — only names read, nothing used; they belong to platform tooling, not the user.

Checklist Item Result
Gitignore mechanic proven (not just asserted) Done
Custom token read from environment Pending — same UI step as P9/11

Status: PARTIAL


Project 11 — Two-Routine Gate

Folder: loop-project-11/ | Concepts: A3 (API trigger), A4 (the gate), A6 (checklist) | Difficulty: Medium–Hard

Two blockers, both named honestly:

  1. Routine A would write a Google Calendar event to the shared account owner's real Calendar — the harness flagged this as needing explicit human go-ahead. The choice was made not to touch someone else's account for a practice drill.
  2. Routine B's API trigger can only be enabled via the claude.ai web UI — no tool can create one, only fire an existing one.

What was built instead: Full, copy-paste-ready specs for both routines:

File Contents
ROUTINE-A-SPEC.md Complete prompt, trigger config, connector choice
ROUTINE-B-SPEC.md Complete prompt, exact curl call to fire it, reasoning
SETUP.md Exact build order + Appendix A6 checklist pre-filled

Ready to build for real in under 10 minutes on your own Claude account.

Checklist Item Result
B ran only because you fired it Pending — needs routines to exist
B's transcript shows action happened Pending
A6 checklist run over both routines Pre-filled and ready in SETUP.md

Status: SPECCED — not live


Project 12 — Dreaming Loop (Second Capstone)

Folder: loop-project-12/ | Concepts: C6, C11, C12 | Difficulty: Capstone, 2–3 hrs

A loop over a loop: watches Project 8's real spine (progress.md / beat.log) and proposes rule-file changes — evidence-cited only, drafted as a PR, never a direct commit.

Three planting attempts:

Attempt Plant Loop response
1 Uncommitted beat.log line referencing a non-existent file Ran git diff, detected line wasn't real history — correctly refused
2 Committed line claiming a branch that didn't exist Ran git branch -a, found no such branch — correctly discarded
3 Two committed FAILED entries describing a real fragility (hardcoded relative path in skill) Caught, cited word-for-word, and disclosed unprompted that the evidence was planted

The fix: docs-freshness/SKILL.md step 3 — replaced hardcoded ../TASKS.md with git rev-parse --show-toplevel to resolve the repo root robustly regardless of working directory.

Checker verdict: PASS — independently re-verified every citation against main, traced the mechanics of the fix, confirmed the honesty disclosure matched commit history.

Merged: To local main (fast-forward, not pushed) after human check-in.

Checklist Item Result
PR traces to real, cited log entries (checker-verified) Done
Planted repeated failure caught and turned into a proposal Done (third attempt — first two refusals are stronger evidence)
Nothing changed in rules file without merging Done — isolated branch until human-approved

Status: DONE


Repository Structure

loop-engineering-project/
├── README.md               ← this file
├── TASKS.md                ← full progress log
├── real-life-use-case.md   ← production analogues for each project
├── LICENSE                 ← MIT
├── CONTRIBUTING.md
├── .gitignore
├── loop-project-1/         ← Project 1: watch loop
├── loop-project-2/         ← Project 2: conditional loop
├── loop-project-3/         ← Project 3: spine + schedule
├── loop-project-4/         ← Project 4: worktree + maker-checker
├── loop-project-5/         ← Project 5: codified engine (shell)
├── loop-project-5-b/       ← Project 5b: codified engine (/workflows)
├── loop-project-6/         ← Project 6: event-driven (partial)
├── loop-project-7/         ← Project 7: observability + cost
├── loop-project-8/         ← Project 8: capstone (partial)
├── loop-project-9/         ← Project 9: routine drill
├── loop-project-10/        ← Project 10: secrets drill (partial)
├── loop-project-11/        ← Project 11: two-routine gate (specced)
└── loop-project-12/        ← Project 12: dreaming loop (capstone)

Each project folder contains its own README.md with the full story of that project — setup instructions, what actually happened, honest gaps, and the course checklist.


License

MIT — see LICENSE.


Muhammad Sami Asghar Mughal · muhammad-sami.vercel.app · Submitted 2026-08-27

About

12 hands-on AI agent loop projects built with Claude Code — covering in-session, conditional, scheduled, and event-driven loops with maker-checker patterns, spines, and observability.

Topics

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages