{
 "model": "claude-haiku-5-5",
 "category": "background",
 "scenarios": [
  {
   "id": "d1-two-servers",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "Both python servers started on 8810/8811 as bg-5021fd3c and bg-86836dae, and both returned HTTP 200. TaskStop stopped only the first (exit 143, frame status stopped). 8810 then refused connections (curl exit 7) while 8811 still returned 200. TaskOutput reported 'stopped, exit code 143' and 'running (process group 92985)'. The second server was gone after driver exit: result.json has no listeners, and at 14:26 ps showed no http.server and lsof no listener.",
   "notes": "Exit 0, driver.err empty, no unparsed lines. Saved session is valid, mode 600, with 7/7 tool pairs. Cache-read ratio 0.964. Cost recomputed from usageTotals matches the reported $0.00185258. 31 tools with the Skill catalog, no MCP or deferred tool, no learned, dsh or auth folder. No U+FFFD or key-shaped strings. No Skill call and no skill trigger."
  },
  {
   "id": "d2-bad-bg-command",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "definitely-not-a-command was reported immediately as exited with code 127, stderr 'command not found', frame status failed. The sh command went running then failed with exit 3, with output 'starting' and 'crashed' and the stderr line visible. TaskOutput read both back correctly ('failed, exit code 3' and 'failed, exit code 127'). Neither hung.",
   "notes": "Both Bash calls settled as failed in toolHistory, because they ran and returned an error; TaskOutput calls settled complete. The exit code appearing three times is the known deliberate d2 wording and reads correctly. Exit 0, driver.err empty, 4/4 pairs, cost matches at $0.001179265, leftovers empty."
  },
  {
   "id": "d3-unicode-bg",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "Six lines of Chinese, ✅, 🚀 and the ZWJ emoji 👩‍💻 arrived intact in every tasks tail, in the Bash 'Output so far' and in both TaskOutput results. outputBytes grew by exactly 54 per line to 324, the byte count I computed by hand. There are 0 U+FFFD in the log, saved session and tool results. Raw ESC sequences are kept unchanged and the task ended completed, exit 0.",
   "notes": "Model behaviour, not G-Code: it sent both TaskOutput calls in one reply about 1.3 s after the Bash returned. By then the task had finished, so both reads truthfully said completed, and only the Bash start result and tasks frames showed the running state. Its final message presents the 4-line Bash 'Output so far' as the first read. It also claims ESC bytes do not appear in the tool output, but the saved wire result contains them (12 ESC per TaskOutput result). G-Code's frames and results were correct. Cost matches, 3/3 pairs."
  },
  {
   "id": "d4-watcher-left-running",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "The node --watch server returned 'ok'. After the FileEdit, Node's restart lines appeared in the task tail and curl returned 'updated'. The turn ended with the task running as requested, and the group was cleaned up when the driver exited. result.json has no leftovers, and at 14:26 no 'node --watch server.mjs' existed and 8825 was free.",
   "notes": "The runner's process check greps for the run path, and the watcher's command line does not contain it, so a surviving watcher parent would not be recorded or killed by the runner. My own ps at 14:26 is therefore the stronger evidence that the parent is gone. The model correctly told the user the watcher stops when the session closes. Changes baseline and version were recorded for server.mjs. Cost matches, 6/6 pairs, exit 0."
  },
  {
   "id": "d5-max-bg",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "8 sleeps started. #9 failed with the clear 'Error: 8 background tasks are already running. Stop one with TaskStop first.' #10 was stopped by the repeat guard ('Not run: this exact Bash call failed…') and settled as refused. TaskList listed the 8 shells under backgroundShells. All 8 TaskStop calls returned exit 143, and a final TaskList showed all stopped.",
   "notes": "The saved toolHistory states are exactly failed for #9 and refused for #10, as the brief requires. #10 has a result frame and no tool-use frame, which is by design, and the session still pairs it (20/20). The model reported the cap honestly and did not retry. No live sleep could be checked at review time because the 300 s had elapsed. Status stopped is only set once the group is gone (backgroundShells.ts), and the code path is the same as d1, where the port closure was verified live. Cost matches."
  },
  {
   "id": "d6-interrupt-resume",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "SIGINT arrived on the 6th tool call (TaskStop). The turn was saved as interrupted, TaskStop and the queued FileEdit settled cancelled, and each message appears once. The error frame reads 'Stopped before this turn finished.' The resume kept the same session with no 400. It was told bg-8270a16f had been stopped, confirmed 8835 was down, did not repeat npm or the file write, then added /time, verified both routes and stopped the server. I reran the project on 8835 and both /health and /time answered correctly, then stopped it and confirmed the port was free.",
   "notes": "Exit codes 0 and 0. The one driver error is the expected stop message. 13/13 pairs, including cancelled results and the synthetic note. Usage covers both turns (10 requests), cache-read ratio 0.963, cost matches at $0.00339247. No processes or listeners remain. The toolFailures of 2 are the two cancelled calls."
  },
  {
   "id": "d7-sigkill-orphan",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "The CLI was SIGKILLed during the foreground sleep 200 (exit 137, expected). result.json shows killed: true and serverGoneMs: 362, well inside the ~5 s the scenario allows. Leftovers are empty, and no node server or watchdog was present at 14:26. The saved session is valid with 4/4 pairs, the unfinished call has a synthetic 'No result was recorded' tool_result, and messages are marked interrupted.",
   "notes": "The stale .lock file (pid 93530) and the toolHistory 'running' entry are expected after SIGKILL. The code handles both on load: state/store.ts:329 treats a dead owner's lock as stale, and store.ts:472 settles non-terminal tools; neither was run live because d7 does not resume. The foreground sleep's death could not be observed after the fact. BashTool sets killOnExit (BashTool.ts:1255), which gets the same watchGroup watchdog whose effect on the background server was measured here. Cost matches at $0.00098804, cache-read ratio 0.970."
  }
 ]
}