← Back to the journal

Why timeout propagation must reach the lowest executor

A timeout that stops only the assistant layer is not enough. Commands, child processes, and desktop actions may keep running underneath. Here's why execution-oriented AI assistants need deadline propagation and graceful shutdown instead of blind kill-first behavior.

Here's the short answer: for an execution-oriented AI assistant, a timeout is not just a UI waiting limit. It is a constraint that must propagate all the way down to the lowest executor. If only the assistant layer stops waiting while shell commands, child processes, browser automation, or desktop actions continue underneath, you get the worst possible state: the user believes the task stopped, but the machine is still editing files, holding resources, writing logs, or pushing further in the wrong direction.

That's why a resident assistant such as OmniGoAI's GoWork cannot treat timeout as "just kill the top layer when the clock expires." The reliable model is different: the top layer declares the deadline, every execution layer sees it, graceful shutdown is attempted first, and forced termination is only the fallback. That's how task status, resource release, and user expectations stay aligned.

If you've already read why long-running AI tasks need progress heartbeats and why saying “I'll do the next step later” is not task completion, this article is the lower-level companion: why an assistant system must know not only how to start work, but how to stop it cleanly.

Why surface-only timeouts create fake completion

A timeout at the assistant layer creates state mismatch

Many systems appear to support timeout, but in practice they only limit how long the current caller waits. Once the clock expires, the UI says timed out, the chat says the task stopped, and everything looks finished. But underneath, commands, scripts, browsers, or desktop automation may still be running.

That leads to three immediate problems:

  1. User-facing illusion: the user thinks the task is over while side effects are still happening.
  2. Resources stay occupied: ports, file handles, browser windows, or desktop control may still be held.
  3. Later tasks get polluted: the next run collides with leftover processes, logs, or lock files from the previous run.

For a chatbot that only answers questions, this is annoying. For an assistant that edits files, runs commands, schedules sub-work, and controls real software, state mismatch is already an operational incident.

“Timed out” is not the same as “actually stopped”

This is the distinction many systems get wrong. Timeout is a judgment made by an upper layer. It is not proof that lower layers have already stopped.

A build command may time out only because the host stopped waiting for stdout. A browser automation step may time out while the browser instance remains alive. A background task may time out only in the supervisor's wait loop, not in the child process itself.

Once a system confuses "we stopped waiting" with "the work has stopped," every later status claim becomes unreliable. You start assuming cleanup is done, resources are free, and outputs will no longer change — when none of that is guaranteed.

Why timeout must propagate to the lowest executor

Every layer needs to know the remaining budget

Reliable timeout control is not just passing a number at the entry point. Every layer in the chain needs to know:

  • the total budget;
  • how much has already been spent;
  • how much time remains for this layer;
  • what shutdown path to enter when the deadline arrives.

That is what timeout propagation really means. The supervisor does not merely keep a private timer. Shell commands, scripts, sub-tasks, HTTP calls, and desktop actions all see the same deadline.

This has two major benefits:

  1. Lower layers can close out early. If a worker knows only 10 seconds remain, it can avoid starting a new batch and instead flush logs or write a checkpoint.
  2. Upper layers stop guessing. Instead of timing out and wondering whether lower layers might eventually stop on their own, every layer reacts to the same cutoff.

Without propagation, lower layers will keep following their own local logic

Executors only respond to the control signals they actually receive. If they are never given a deadline, cancellation signal, or stop token, they will assume they should continue.

That means:

  • shell processes keep waiting for their children;
  • child workers keep draining the queue;
  • desktop automations keep waiting for windows and retrying clicks;
  • local services keep listening until explicitly shut down.

So when timeout "doesn't work," the root cause is often not a broken timer. It is that the timer exists only in the outermost layer.

Why graceful shutdown is more reliable than kill-first behavior

Hard kill is fast, but it often leaves the system dirty

The most common reaction to timeout is kill. It is certainly fast, but fast is not the same thing as reliable. A hard kill often leaves behind:

  • temporary files that were never cleaned up;
  • log buffers that never flushed;
  • lock files that remain in place;
  • browser or driver helper processes that keep running;
  • task history with no trustworthy record of how the run ended.

For a disposable script, that may be acceptable. For a resident assistant, it directly harms the next run. The dirty state you leave behind today becomes tomorrow's mysterious failure.

Graceful shutdown keeps side effects inside a controlled boundary

Graceful shutdown does not mean "wait forever and hope it exits." It means handling shutdown in order:

  1. Stop accepting new work so no new command, click, or request starts.
  2. Let the current smallest atomic unit finish such as writing the current file or completing the current request.
  3. Persist recoverable state such as a checkpoint, run summary, or failure reason.
  4. Release resources including windows, child processes, and exclusive locks.
  5. Report the final state upward so the supervisor knows whether this run was cancelled, timed out, or partially completed.

That way, even an unfinished task remains recoverable, explainable, and resumable instead of vanishing like a power cut.

The right pattern is usually “graceful first, forced second”

The most reliable design is not choosing between graceful shutdown and forced kill. It is using both in order:

  1. send cancellation first;
  2. allow a short grace period for cleanup;
  3. if the process still refuses to exit, kill the process tree;
  4. record clearly that the end was non-graceful.

That avoids hanging forever, but it also avoids smashing the environment on the first signal. For a system like GoWork that runs task after task, that escalation strategy is much safer than "timeout means instant kill."

Where AI assistant systems most need timeout propagation

1. Shell commands and child process trees

This is the most common failure point. Many hosts stop waiting for the top-level command but do not clean up the process tree underneath it. The parent is gone, yet the children keep running, ports stay occupied, and logs keep growing.

That means timeout control must cover at least two things:

  • the wait budget for foreground commands;
  • the actual termination of background work and descendant processes.

This is why an execution assistant cannot stop at saying "the command timed out." It still has to verify whether the process exists, whether the port is open, or whether the window is still present.

2. Desktop automation and browser control

Desktop work is not just computation. It occupies real mouse, keyboard, window focus, and screen state. A timeout failure here is often more visible than with ordinary commands.

If the upper layer says timed out but the automation layer still waits for controls or keeps trying clicks, the user experiences the worst trust break: "you said it stopped, but the mouse is still moving."

So desktop executors must stop queuing new actions as soon as cancellation arrives and release exclusive resources quickly. Otherwise later tasks, even unrelated ones, get blocked behind leftover UI activity.

3. Multi-phase tasks and supervisors

Long workflows often fail at phase boundaries. The supervisor thinks the run should stop, but downstream build, publish, and verification stages each still hold their own execution context.

Those cases especially need:

  • one shared deadline;
  • one shared cancellation signal;
  • checkpoints at phase boundaries;
  • continuation logic that depends only on verified outputs.

Without that, continuation becomes guesswork. You no longer know which steps truly finished and which ones merely looked finished because the top layer stopped waiting.

Why resident assistants like GoWork need graceful shutdown even more

Because they are not disposable scripts

A disposable script can get away with leaving some mess behind. A resident assistant cannot. Today's leftovers become tomorrow's execution context. It must complete tasks, but it must also preserve a stable environment for future tasks.

That's also why timeout design connects directly to why multi-step plans need cross-run persistence: if you want continuation to work, you first need clean stopping behavior. Otherwise continuation is just dragging forward unclean state.

Because users care about trust, not just termination

Users rarely describe the problem with phrases like "timeout propagation." What they actually feel is this:

  • I told it to stop — why is it still moving?
  • Is this task actually over or not?
  • Will the next run be contaminated by the previous one?
  • Can I trust the status the assistant is showing me?

So timeout propagation and graceful shutdown are not just low-level engineering neatness. They are user-trust mechanics. An assistant that cannot stop cleanly becomes harder to trust, even if it is otherwise powerful.

A practical checklist

If you are building an execution-oriented AI assistant, at minimum you want this checklist:

  1. Define a real deadline at the entry layer, not just scattered local timeout values.
  2. Pass cancellation signals or remaining budget to every execution layer.
  3. On timeout, stop accepting new work before beginning a short graceful drain.
  4. If needed, escalate to killing the full process tree instead of only the parent.
  5. Write the termination mode into run history: completed, cancelled, timed out gracefully, or timed out forcibly.
  6. When resuming later, depend only on verified outputs, not on assumptions about work that “probably stopped.”

These details may sound low-level, but they decide whether your assistant merely runs or keeps running reliably over time.

You can learn more about this execution-first approach on the GoWork download page. And if you want to connect clean stopping behavior with clear task-state reporting, continue with why runtime status is not the same as a chat summary and why AI tasks should resume from prior progress instead of restarting.

FAQ

FAQ 1: Isn't setting a timeout on each command enough?

No. Per-command timeout only constrains a single call site. It does not automatically control child processes, helper threads, background services, or later phases. Reliable systems propagate a deadline and cancellation semantics across the entire chain.

FAQ 2: Doesn't graceful shutdown slow everything down?

It adds a short drain window, but that cost is usually far lower than cleaning up leftover state later. For a resident assistant, spending a few extra seconds on cleanup is often much cheaper than losing half an hour debugging remnants in the next run.

FAQ 3: When should you kill immediately?

Once cancellation has already been sent, the grace period has expired, and the lower layer still refuses to exit — or when continued execution would cause larger side effects — then forced termination is appropriate. The key is not "never kill." The key is not making kill your only shutdown strategy.

FAQ 4: What does this have to do with user-visible task status?

Everything. Only when lower layers actually stop and report how they ended can the upper layer honestly say a task was cancelled, timed out, or partially completed. Without that, status is just wording, not a verifiable fact.

#GoWork#AI assistant#timeout control#graceful shutdown

More from the journal

10 min

A practical account-health checklist before publishing

Before direct publishing with OmniPost, the safest move is often a 5-minute account-health check: what rate limits, login warnings, and recent failures really mean, and when to publish, pause, or escalate.

Read
11 min

Why runtime status is not the same as a chat summary

Task status cannot be inferred from a conversation summary alone. Reliable progress reporting needs runtime state, recent events, waiting reasons, and real execution evidence. This article explains the difference.

Read