$npx -y skills add tkellogg/open-strix --skill long-running-jobsRun shell commands in the background with file-backed output capture and a completion event that wakes the agent when the job exits. Use when a command might take more than ~30 seconds (builds, tests, deployments, agent jobs like acpx/codex exec) and you want to keep working whil
| 1 | # Long-Running Jobs |
| 2 | |
| 3 | Some commands take minutes or hours. Rather than blocking on them, spawn them with `async_mode=True`. The harness owns the process — captures stdout/stderr to files, surfaces the job in the web UI, and **fires a completion event back into the agent's queue when the subprocess exits.** |
| 4 | |
| 5 | The completion event is a fresh turn: journal, memory blocks, and recent messages get rebuilt from disk. There is no in-context state to preserve. Whatever you need to remember when the job finishes has to live somewhere durable (journal entry, state file, the command itself) **before** you spawn. |
| 6 | |
| 7 | ## When to Use This |
| 8 | |
| 9 | **USE when:** |
| 10 | - Build commands (`cargo build`, `npm run build`, `make`) |
| 11 | - Test suites that take more than ~30 seconds |
| 12 | - Data processing, model training, large downloads |
| 13 | - Deployments or migrations |
| 14 | - Sub-agent invocations (`acpx`, `codex exec`, etc.) |
| 15 | - Any command where you want to keep working while it runs |
| 16 | |
| 17 | **DON'T USE when:** |
| 18 | - Quick commands (< 30s) — just call shell normally and wait |
| 19 | - Commands you need the result of immediately (chain into next reasoning step) |
| 20 | - Interactive commands that need stdin |
| 21 | |
| 22 | ## The Canonical Pattern |
| 23 | |
| 24 | ``` |
| 25 | shell(command="cargo build --release", async_mode=True) |
| 26 | ``` |
| 27 | |
| 28 | That's it. The harness: |
| 29 | |
| 30 | 1. Spawns the command in its own process group, file-backs stdout/stderr |
| 31 | 2. Returns immediately with `Spawned async job j_abc123 (pid 12345)` |
| 32 | 3. Surfaces the job in the web UI with a live status indicator |
| 33 | 4. When the subprocess exits, fires a `shell_job_complete` event that wakes the agent with a prompt containing `job_id`, `command`, `exit_code`, `elapsed`, and the tail (~4KB) of each stream |
| 34 | |
| 35 | You keep working in the current turn. The completion event arrives as a separate wake-up. |
| 36 | |
| 37 | ## Inspecting Running Jobs |
| 38 | |
| 39 | While a job is running you have three options — and one of them isn't a tool call: |
| 40 | |
| 41 | - **Web UI** — the user can see running jobs at a glance with elapsed time and status. If the user's watching, they already know. |
| 42 | - `shell_jobs_list()` — every job the registry knows about (running + recently finished), with `job_id`, `pid`, `status`, `elapsed_seconds`, and `seconds_since_last_signal`. |
| 43 | - `shell_job_output(job_id, tail_lines=N, stream="stdout"|"stderr"|"both")` — tail current output without waiting for completion. Useful for sanity-checking that a build is making progress vs hung. |
| 44 | |
| 45 | To stop a running job, call shell again with `kill <pid>` (or `kill -9 <pid>` if it's stuck). Pick the signal level yourself. |
| 46 | |
| 47 | ## What the Wake-Up Prompt Looks Like |
| 48 | |
| 49 | When the job exits, you wake up to something like: |
| 50 | |
| 51 | ``` |
| 52 | Shell job j_abc123def4 complete (status=exited_ok, exit_code=0, elapsed=47.3s). |
| 53 | Command: cargo build --release |
| 54 | |
| 55 | --- stdout tail --- |
| 56 | Compiling foo v0.1.0 (/home/agent/foo) |
| 57 | Finished release [optimized] target(s) in 47.21s |
| 58 | |
| 59 | --- stderr tail --- |
| 60 | (empty) |
| 61 | ``` |
| 62 | |
| 63 | That's the entire context the wake-up gives you about the job itself. Your journal, memory blocks, and recent channel history come back automatically — but they only carry whatever you wrote to them before spawning. |
| 64 | |
| 65 | ## The Pre-Spawn Discipline |
| 66 | |
| 67 | Because the completion turn is fresh, the cost of a context-thin spawn is paid by your future self. The fix is to leave breadcrumbs **before** you call shell: |
| 68 | |
| 69 | - **Journal entry naming the job and the plan.** "Spawned `j_abc123` building release for PR #45. If green, run integration tests next; if red, suspect borrow checker on UserPagination." A good journal entry is the single highest-leverage thing you can do here. |
| 70 | - **State files for any multi-step plan.** `state/research/jobs-in-flight.md` or similar. The completion turn can read it. |
| 71 | - **Encode the next step in the command itself when feasible.** `make build && make test && touch /tmp/build-pipeline.done` collapses two steps into one job, which means one wake-up instead of two. |
| 72 | |
| 73 | The old version of this skill recommended composing a long callback message at spawn time. That's no longer the model — the harness writes the wake-up prompt for you, and it doesn't carry your prose. Put your prose in the journal. |
| 74 | |
| 75 | ### What to Journal Before Spawning |
| 76 | |
| 77 | A useful pre-spawn journal entry answers: |
| 78 | |
| 79 | 1. **What did you spawn?** — `job_id`, command, the bigger task it serves |
| 80 | 2. **What's the plan on success?** — concrete next step |
| 81 | 3. **What's the plan on failure?** — likely failure modes and where to look |
| 82 | |
| 83 | Example: |
| 84 | |
| 85 | ``` |
| 86 | [journal entry, before shell(... async_mode=True)] |
| 87 | Spawned j_abc123 (cargo build --release) as part of PR #45 prep. |
| 88 | On exit_code=0: run integration tests, then post merge-ready comment on PR. |
| 89 | On non-zero: most likely the new UserPagination struct lifetime issue. |
| 90 | Grep stderr for "borrow" or "lifetim |