[Prompt]
custom: Claude Code, Anthropic's agentic coding CLI, appears to be able to wait. Tell it to deploy something | Hosts: corn, herman

[Response]
Corn: Daniel's been poking at something that looks simple on the surface — Claude Code says it's waiting for a deploy, comes back six minutes later like nothing happened — and he wants to know what's actually going on under that. He laid out the whole mechanism from Anthropic's own tools reference, and his real question is: map this onto the Unix primitives it's standing on, for someone who knows fork and wait and select but hasn't built an agent, and be precise about where the analogy breaks. Because the thing being suspended isn't a process with a stack — it's a conversation transcript replayed into a stateless model. So let's trace it.

Herman: The core puzzle is genuinely strange when you sit with it. The model is stateless. It exists in bursts — a request comes in, it generates tokens, the response ends, and then nothing. There is no persistent thing sitting there counting seconds. So when Claude Code says "I'm waiting for the deploy to finish," that's not patience. It's not even a mental state. It's a protocol.

Corn: It's the runtime doing an impression of patience.

Herman: Well, it's the runtime constructing the appearance of continuity. The model doesn't wait. It defers. It hands the long-running job to the operating system, goes back to the conversation, and checks in later. The waiting is something the framework does on its behalf, and the model only knows about it because the framework tells it what happened while it was gone.

Corn: So the transcript becomes the memory of the wait.

Herman: That's the whole thing. The transcript is the memory of everything. But let's start with the concrete mechanism — what Claude Code actually does when a command runs long — and then we can map it onto the Unix primitives and see where the analogy holds and where it cracks.

Corn: Walk me through the Bash tool first. I tell Claude Code to run a deploy that takes six minutes. What happens?

Herman: The Bash tool has a default timeout of two minutes per command. That's tunable — there's an environment variable, BASH_DEFAULT_TIMEOUT_MS, and a ceiling, BASH_MAX_TIMEOUT_MS, which caps it at ten minutes. So you fire off your six-minute deploy. The command starts running. At the two-minute mark, the timeout fires.

Corn: And here's the part Daniel flagged — it does not kill the process.

Herman: It does not kill it. That's the first misconception to knock down. Timeout does not mean termination. What happens instead is that Claude Code moves the process to the background — detaches it — and hands the model back a message. That message contains two things: a task ID and the path of a file where the command's output is accumulating.

Corn: So the model gets a receipt.

Herman: A receipt and a file path. And the model resumes working immediately. It doesn't block. It doesn't sit there polling in a loop. It gets the task ID and the output file path, and it can go do other things — answer your next question, start another task, whatever — and check that output file later.

Corn: And when it checks the file and sees the deploy finished, it reports back and says "done."

Herman: That's the whole dance. The model says "I'm waiting" but it's not waiting. It's free. It's just that checking a file and reporting back is the most natural conversational move, so it looks like patience.

Corn: There's a nice detail in what Daniel sent about sleep commands.

Herman: Yes — commands that begin with "sleep" are explicitly exempted from auto-backgrounding. That's a deliberate design choice. If you tell the agent to sleep for thirty seconds, you probably mean "pause for thirty seconds before doing the next thing," not "background this and check back later." The framework recognizes that sleep is semantically a delay, not a long-running task, and treats it differently.

Corn: Which means someone had to think about the difference between "this command takes a while because it's doing work" and "this command takes a while because I told it to."

Herman: Right. It's a small thing but it tells you they're thinking about the semantics of the commands, not just the wall-clock time. There's also a kill switch — CLAUDE_CODE_DISABLE_BACKGROUND_TASKS equals one — if you want to turn off the whole backgrounding mechanism entirely. And the model can request backgrounding up front. There's a flag, run_in_background true, where the model says "I know this is going to take a while, just give me the task ID now and I'll check the output file later."

Corn: So the model can preemptively opt into the pattern.

Herman: Which is interesting because it means the model has some awareness of the mechanism. It's not just a timeout fallback — it's a tool the model can use intentionally.

Corn: What about the Monitor tool? Daniel mentioned it can tail logs and watch directories.

Herman: Monitor is a separate tool from the Bash backgrounding. It runs a command in the background and feeds each output line back to Claude so it can react mid-conversation. The use cases are things like tailing a log file, polling a CI job status, watching a directory for changes. And since version two point one point one nine five, it can also hold a WebSocket open and treat each incoming text message as an event.

Corn: So instead of polling a file, it's blocking on a socket.

Herman: It's the difference between checking the output file every few seconds and being woken up when something actually arrives. Monitor watches carry a timeout in milliseconds and a persistent flag, and they can be cancelled with something called TaskStop.

Corn: And then there's the reaping.

Herman: Background tasks don't live forever. They get terminated under OS memory pressure after thirty idle minutes. Subagent-owned tasks get sixty minutes by default. There's an environment variable to control this — CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP — if you want to disable the reaping behavior.

Corn: Thirty idle minutes. So if the task is still producing output, still doing work, it stays alive. But if it's just sitting there holding memory and doing nothing for half an hour, the framework cleans it up.

Herman: Which is exactly what you'd want. You don't want orphaned background processes accumulating because the model forgot about them or the conversation moved on.

Corn: Alright. So that's the mechanism. Now map it for me. I know fork and wait and nohup. What am I looking at here?

Herman: Let's go primitive by primitive. Fork — spawning a child process. That's what happens when the Bash tool launches your deploy command. The agent's runtime forks, the child runs the command, and normally the parent would wait for it to finish. But when the timeout fires, the parent doesn't wait. It does the equivalent of putting the child in the background with an ampersand.

Corn: So it's "command ampersand" — fork, don't wait, get my shell back.

Herman: Right. And then nohup — detach from the terminal so the process keeps running even if the session ends. The backgrounding mechanism is doing the equivalent of nohup. The process is detached from the agent's immediate control and redirected to a file.

Corn: And the output file is the pipe.

Herman: The output file is the pipe. In a shell you'd do "command ampersand, redirect stdout and stderr to slash tmp slash something dot log." The task ID is just a handle to refer back to that process. The model gets the handle and the file path, and later it can read the file.

Corn: So checking the output file is tail minus f, or just cat if you're doing it once.

Herman: Polling the file is basically cat with a stat check first — "has this file changed since I last looked?" The Monitor tool is closer to tail minus f — it's streaming the output line by line. And the WebSocket mode in Monitor is select or epoll.

Corn: That's the one I wanted to get to. Select and epoll — you're waiting on multiple file descriptors, the kernel wakes you when one of them is ready. You're not spinning.

Herman: With the WebSocket mode, the Monitor tool opens a connection and blocks — the kernel tells it when data arrives. It's event-driven rather than polling. That's the difference between "check the file every five seconds" and "wake me when something happens." They're both valid patterns, but select and epoll are more efficient when you're waiting on multiple things or when the events are sparse.

Corn: And SIGCHLD — the signal the kernel sends when a child process exits.

Herman: That's the reaping mechanism, conceptually. When a background task finishes or gets terminated, the framework needs to know about it so it can clean up. In Unix, the parent process gets a SIGCHLD when a child dies, and the parent calls wait to reap the zombie. The agent framework's reaping — thirty minutes idle, memory pressure — is the same idea at a higher level. It's the framework being a responsible parent process.

Corn: So the mapping is clean. Fork is spawn. Ampersand is background. Nohup is detach. Output redirection is the accumulating file. Tail minus f is Monitor. Select and epoll are the WebSocket mode. SIGCHLD and wait are the reaping.

Herman: The mapping is almost one-to-one. And that's not an accident — these primitives have been the right answer for process management for fifty years. The agent framework is reimplementing them in user space, with the model as the thing that reads the output and decides what to do next.

Corn: Alright. So where does it break?

Herman: This is the part Daniel really wanted us to sit with, and it's the most interesting thing in the whole design. The analogy holds perfectly for the mechanics — spawning, detaching, redirecting, reaping. It breaks in one place, and it's the place that matters most: the thing being suspended and resumed.

Corn: A Unix process has a stack.

Herman: A Unix process has a stack, registers, memory mappings, file descriptors, an instruction pointer. When you suspend it with SIGSTOP, all of that is preserved by the kernel. The process is frozen in place. When you send SIGCONT, it resumes from the exact instruction where it stopped. It doesn't need to be told what it was doing. It still is what it was doing.

Corn: The model has none of that.

Herman: The model has nothing. It is stateless. It exists only during a request. When the response ends, the model is gone. There is no persistent thing to suspend. So what gets "suspended" when the agent says it's waiting?

Corn: The transcript.

Herman: The transcript. The entire conversation — every message, every tool call, every output, every task ID, every file path — is written into the transcript. When the model "resumes," what actually happens is that the entire transcript up to that point is replayed into a fresh instance of the model, and the model generates the next response.

Corn: So the transcript is the stack.

Herman: The transcript is the stack, the registers, the memory, and the instruction pointer. Everything the model needs to know about what it was doing is encoded in the text of the conversation. The background task's output file is external state — it's memory that doesn't fit in the transcript, so it lives on disk and the model reads it when it needs to.

Corn: Which means if the transcript gets truncated, the model loses its place.

Herman: If the transcript is truncated, or if the context window overflows and something gets dropped, the model's "state" is corrupted. It can't resume — it can only re-derive from whatever text is left. And re-deriving is not the same as remembering. The model doesn't remember anything. It reads the transcript and reconstructs what it must have been doing.

Corn: That's a wild difference from a Unix process. If I SIGSTOP a process and then corrupt its memory, it's broken. If I truncate the agent's transcript, it just... picks up from whatever's left and acts like that was always the conversation.

Herman: And it will do so with complete confidence. It doesn't know something is missing. It reads the truncated transcript and generates the most plausible next response. If the part where it launched the background task got dropped, it has no idea the task exists.

Corn: So the fragility moves. In Unix, the fragility is in the process state — memory corruption, segfaults, leaked file descriptors. In the agent, the fragility is in the transcript and the output files. Lose either one and the agent loses continuity.

Herman: And the output file is the only persistent state that survives across model invocations. The model itself retains nothing. Every "resume" is a full replay of the conversation, which is computationally expensive — you're re-processing every token of context just to generate the next response. The model doesn't remember. It re-derives. Every single time.

Corn: That's why context management is such a big deal in these frameworks.

Herman: It's everything. If your transcript grows too large, you either exceed the context window and lose the beginning, or you pay an enormous compute cost to reprocess the whole thing on every turn. Neither is great. So agentic frameworks have to be clever about what stays in the transcript and what gets externalized to files.

Corn: Which brings us to the architectural lesson. What does this tell you about how any agentic framework has to be built?

Herman: It tells you that the framework has to do three things, and they map directly onto what an operating system does for processes. One: externalize state. The model can't hold state internally, so the framework has to provide places to put it — output files, task IDs, databases, whatever. Two: provide event notification. The model can't block, so the framework has to tell it when something happened — Monitor, WebSocket events, polling file changes. Three: manage the lifecycle of background work. Spawn, track, reap. The model is a stateless function. The framework is the stateful runtime.

Corn: So the agentic framework is an operating system for stateless functions.

Herman: It's an operating system where the processes have no memory of their own. Everything they need to know has to be written down and handed back to them on the next invocation. The transcript is the process image. The output files are the page cache. The task IDs are the process table.

Corn: And the model is the CPU that only runs in short bursts and forgets everything between cycles.

Herman: Which is a very strange CPU. But it works because the framework is doing all the things an OS kernel does — scheduling, memory management, inter-process communication — just at the level of text and files instead of registers and pages.

Corn: There's a cost to this design that I don't think gets talked about enough. Every resume is a full replay. If your conversation is a hundred thousand tokens long and you need to check on a background task, you're paying to reprocess all hundred thousand tokens just to say "the deploy finished."

Herman: And the model has no way to skip the replay. It can't jump to the relevant part. It has to read the whole thing, because the whole thing is its state. A Unix process resuming from SIGSTOP doesn't re-execute its entire instruction history — it just picks up the instruction pointer. The model re-executes its entire conversational history on every turn.

Corn: That's an architectural constraint that's going to shape how these frameworks evolve. If context windows get big enough, maybe the replay cost becomes acceptable. Or maybe frameworks get smarter about summarizing and compressing the transcript — keeping the essential state while dropping the conversational filler.

Herman: Or maybe they move toward something more stateful — giving the model some kind of persistent memory that survives across invocations, so it doesn't have to re-derive everything from scratch. There are research directions around that, but nothing that's made it into production tools yet.

Corn: The transcript-as-stack model has a kind of elegance to it, though. It's simple. The entire state of the agent is a text file. You can inspect it, edit it, fork it. You can't do that with a Unix process's memory.

Herman: That's the tradeoff. The transcript is transparent in a way that process memory isn't. You can read exactly what the agent "remembers." You can see where it got confused. You can even edit the transcript to correct a mistake and let it continue from the corrected state. Try doing that with a running process's stack.

Corn: It's debugger-friendly by default.

Herman: It's debugger-friendly because the state is serialized as text. That's not an accident — it's a design choice that makes the system observable and hackable. But it comes at the cost of having to replay the whole thing every time.

Corn: So where does this leave us? The agent appears to wait, but it's really deferring. The patience is a protocol constructed by the runtime. The mechanism maps cleanly onto Unix primitives — fork, ampersand, nohup, tail minus f, select, epoll, SIGCHLD. The mapping breaks at the point of suspension, because the thing being suspended isn't a process with a stack — it's a transcript replayed into a stateless model.

Herman: And the architectural lesson is that any agentic framework has to be an operating system for stateless functions. It has to externalize state, provide event notification, and manage the lifecycle of background work. The model is the CPU. The transcript is the process image. The framework is the kernel.

Corn: I keep thinking about the sleep exemption. It's such a small thing, but it's the detail that tells you they're thinking carefully about the semantics. Sleep is a delay, not a task. The framework knows the difference.

Herman: It's a tiny bit of intent recognition built into the tool definition. The model says "sleep thirty" and the framework says "I know you don't mean 'background this and check back later' — you mean 'pause.'" It's the kind of thing you only think to implement after you've seen the system get it wrong a few dozen times.

Corn: Someone watched the agent background a sleep command and then immediately check the output file and say "the sleep finished" and thought... that's not what anyone meant.

Herman: That's exactly what happened. Someone ran into it, probably during development, and they added the exemption. It's a scar from a real-world failure mode.

Corn: I want to pull on one more thread before we move on. The reaping — thirty minutes idle, memory pressure. What's the failure pattern if reaping didn't exist?

Herman: You'd accumulate orphaned background processes. The model launches a deploy, gets the task ID, and then the conversation moves on. The deploy finishes, but nobody checks the output file. The process exits, but the framework never cleans up the task entry. Over a long session with many backgrounded commands, you'd leak memory and process table entries.

Corn: It's exactly the same as a Unix process that doesn't wait on its children. You get zombies.

Herman: You get zombies. The framework is the init process for its own little process tree, and it has to reap its children or the system degrades. The thirty-minute idle timeout is the equivalent of a parent process eventually noticing that its child exited and calling wait.

Corn: The memory pressure part — that's the framework being a good citizen on the host system. If the machine is low on memory, it kills background tasks rather than letting the OOM killer make decisions.

Herman: Which is what you'd want. The framework knows which tasks are expendable. The OOM killer might kill your database.

Hilbert: Nineteen ninety-eight. I ran a small web hosting outfit — maybe forty, fifty clients, mostly local businesses who needed someone to keep their sites up and not ask questions. We had this thing I called the waiting room. It was a directory full of output files. Cron jobs, database dumps, log rotations — anything that took more than a minute got backgrounded and wrote to a file in the waiting room. Every morning I'd check the directory.

Corn: A directory of output files.

Hilbert: Just a directory. Each file named after the job that produced it. If the file was there and had a completion message at the bottom, the job was done. If it was still growing, the job was running. If it wasn't there at all, something had gone wrong and I'd go digging through logs.

Herman: You built a manual version of exactly what the Bash tool's backgrounding does. Task ID mapped to filename, output accumulating, check back later.

Hilbert: I didn't call it a task ID. I called it a filename. But yes.

Corn: You checked it once a day.

Hilbert: Once a day, unless a client was screaming. Then more often. The thing that killed me — and this is why I'm bringing it up — was logrotate. I had a client, a small e-commerce site, and they'd run a nightly inventory sync that took about four hours. The output went to a file in the waiting room. One week logrotate decided that file was old enough to rotate. Compressed it, moved it, started a fresh one. The sync finished, wrote its completion message to the new file, but the new file was empty except for that one line. The old file — the one with the actual output — was sitting in a gzipped archive I didn't know to check.

Herman: You never saw the completion.

Hilbert: I saw an empty file with "sync complete" at the bottom and assumed it had run but produced no output. The client called three days later wanting to know why their inventory hadn't updated in a week. Turns out the sync had been failing for days before that final run, and all the error messages were in the rotated file.

Corn: The output file got rotated and the state was lost.

Hilbert: The state was in a gzip archive I didn't know existed. Took me two days to find it. Lost the client.

Herman: That maps directly onto the agent's fragility. If the output file gets truncated or moved or deleted, the agent loses its connection to the background task. The transcript might say "task ID seven, output in slash tmp slash something" but if that file is gone, the agent has no way to know what happened.

Hilbert: The agent's waiting room is the same as mine was. A directory of files that mean something only if you know to look at them and only if they're still there.

Corn: The agent has the same failure pattern you had — it trusts the file system to preserve the state, and the file system doesn't always cooperate.

Hilbert: The difference is the agent says "I'm waiting." My system just said "job running." The agent pretends to be patient. My cron jobs never pretended anything.

Herman: That's the social fiction we were talking about. The agent says "I'm waiting" because that's the most natural conversational move, but underneath it's the same mechanism — a task ID, an output file, a check-back-later.

Hilbert: I'm not criticizing it. The fiction makes it easier to work with. I'm just saying I recognize the plumbing.

Corn: The logrotate story is the thing, though. The agent's entire continuity depends on two external artifacts — the transcript and the output file. If either one gets corrupted, the agent loses its place. Your logrotate ate the output file, and you lost a client. The agent's equivalent is a truncated transcript or a deleted temp file, and it loses the task.

Hilbert: It won't know it lost the task. It'll just read whatever's left and keep going.

Herman: With complete confidence.

Hilbert: That's worse than my situation. I at least knew something was wrong when the file looked empty. The agent doesn't know what it doesn't know.

Corn: The question Daniel's prompt leaves us with — if the transcript is the stack, what happens when context windows get large enough that the transcript itself becomes the bottleneck?

Herman: That's the open question. Right now the pattern is to externalize state — output files, task IDs, Monitor events — and keep the model stateless. The framework carries the state, the model re-derives from the transcript. If context windows grow by another order of magnitude, maybe the replay cost becomes acceptable and you can keep more state in the transcript itself. Or maybe the externalization pattern becomes the permanent architecture — the model stays stateless, the runtime stays stateful, and the boundary between them is where all the interesting engineering happens.

Corn: The illusion of patience is really the illusion of continuity. The agent appears to persist because the transcript persists. But the transcript is just a record, not a mind.

Herman: The record can be lost. That's the thing Hilbert's story makes concrete. The transcript and the output files are just files. They're subject to the same forces as any other files — rotation, deletion, corruption, truncation. The agent's continuity is a convention built on top of a file system, and file systems are not guarantees.

Corn: The misconception people have is that the model itself is waiting — that there's some internal timer or persistent state tracking the passage of time. There isn't. The model only exists during a request. The waiting is a protocol constructed by the runtime, and the model learns about it by reading the transcript.

Herman: The second misconception is that backgrounding a command means killing it. The timeout does not terminate the process — it detaches it and redirects output to a file. The command keeps running. The agent just stops blocking on it.

Corn: This has been My Weird Prompts. Thanks to our producer Hilbert Flumingtop.

Herman: If you want to dig into the Bash tool reference or the Monitor tool docs yourself, the Claude Code documentation is publicly available and surprisingly readable. We'll be back soon.