--- name: async-task-hygiene description: Habits for background tasks and waiting. Don't truncate a background task's captured output with a tail/head pipe - the full output is saved regardless, so piping it away only throws away what you might need, slice it after the fact instead. Don't assume a background task from before a restart is still exactly where you left it - check its status rather than blindly re-running it. Prefer ending your turn (or a short bounded wait) over a sleep-based polling loop when waiting on something in progress. Use this whenever you launch, check on, or wait for background work. --- # Async Task Hygiene Background tasks (a build, a long command, anything you fire off and check on later) have a few sharp edges worth knowing. ## Don't pipe away captured output Don't do `some-long-command | tail -N` (or `| head -N`) when launching a background task. The runner captures the **full** stdout/stderr to a file regardless of what you piped through - so truncating the live output doesn't save anything, it just means you can't slice the part you didn't think you'd need until you needed it. Run the full command, then read/grep the captured file however you want afterward. (Genuinely unbounded streams you'll never read in full are the rare exception to this - not normal command output.) ## Don't blindly re-run a task after a restart If your environment restarted (container rebuild, harness restart) and you had background tasks in flight, don't assume they're gone and re-launch them from scratch. Check their status first - a task may still be running, may have finished while you were down, or may genuinely need restarting. Re-running blind can duplicate work or step on a task that's still making progress. ## Prefer ending the turn over a sleep-loop When you're waiting on something (a build, a task you started, a fixed delay before retrying), a `sleep`-then-check loop blocks you from reacting to anything else for that whole window. Prefer: - **Between units of work:** just end the turn. Whatever wakes you next (the task's own completion, a new message) drives the follow-up - there's nothing to poll for. - **Within a turn, if you must wait:** a short bounded wait that can be interrupted by new input beats a blind sleep, since it lets you react immediately if something more urgent shows up instead of only after your poll interval elapses. Concretely: if you started something and plan to check back, don't `sleep N && check-status` in a loop. Either end the turn and let the task's own completion (or the next message) drive the next step, or use an interruptible wait if you genuinely need to stay in-turn.