Lesson 2.1 ended on a loop nobody could see. It kept running for three days after the terminal closed. So how do you stop one?
Four lessons went on starting things. This one is about ending them.
Teardown: three moves
There are three moves. You do all three, and none of them is enough on its own.
| # | Move | What it does | What it depends on |
|---|---|---|---|
| 01 | Clear the goal | Drops the active completion condition, and the session-scoped Stop hook it wraps. | The session. It covers this session only. |
| 02 | Delete the scheduled task | Removes the task, by id. | The listing being accurate. |
| 03 | Set the switch | Turns /loop off and stops every scheduled task. | Nothing above it. |
First, the goal:
/goal clear
Second, the schedule. Ask Claude:
list my scheduled tasks,
then delete the one I just made
Claude reaches CronList and CronDelete for this. Delete by id, and read the
list back.
Third, the switch. Set the environment variable:
CLAUDE_CODE_DISABLE_CRON=1
Do this as well as the delete, not instead of it. The switch stops things firing; it deletes nothing, so unset it and the task is still there. The delete is the move that removes the thing. The switch is the move that works when the delete didn't. Order matters less than doing both.
Why "check the task list" is not evidence
Here is a real listing, from a reported issue against Claude Code (anthropics/claude-code#64744):
> CronList
(no scheduled tasks)
> TaskList
(no running tasks)
Both empty. And it was still firing. That loop had:
- survived Ctrl-C;
- survived a closed terminal;
- run 864 iterations over 72 hours;
- cost about $300 nobody approved.
An empty listing is the absence of evidence, not evidence of absence. That's the whole argument for the third move: a switch whose effect doesn't depend on the thing you're trying to stop reporting itself honestly.
It's the same reasoning Lesson 1.2 used for the hook. A stop rule that asks the system to cooperate is a request. One that doesn't is a ceiling.
What /loop bounds for you already
/loop does bound some of this for you. Three numbers, for free:
| Bound | Value | What it means |
|---|---|---|
| Minimum interval | 1 minute | You can't ask for a tighter cadence than this. |
| Tasks per session | 50 | A loop that schedules loops runs out. |
| Expiry | 7 days | It fires one last time, then deletes itself. |
The habit
- Before you start: know which of the three moves you'll need. A
/loopin a session you're watching needs one. Anything scheduled needs all three. - After:
/goal clear, then delete the task by id, then setCLAUDE_CODE_DISABLE_CRON=1. Then read the listing back, and don't treat it as proof. - Always: teardown is part of the loop's definition, not a chore after it.
A loop you can't stop is not a loop. It's an outage with a cron expression.
After this lesson
You will be able to:
- tear down a loop with all three moves: clear the goal, delete the scheduled
task by id, and set
CLAUDE_CODE_DISABLE_CRON=1; - say what each move depends on, and why the switch goes in as well as the delete;
- explain why an empty
CronListorTaskListdoesn't prove a loop stopped; - name the three bounds
/loopgives you (1-minute minimum interval, 50 tasks per session, 7-day expiry) and why they're a damage cap, not a safety net.
What comes next
Lesson 2.6, "The Loop, Actually Running: A Real End-to-End Demo", puts every piece from Lesson 1.1 to this one into a single real session, teardown included.
After that, Module 3 picks up the next problem: a loop running unattended is also a loop making mistakes unattended. Stopping it is not the same as checking it.