{
 "model": "claude-haiku-5-5",
 "category": "background",
 "scenarios": [
  {
   "id": "d1-two-servers",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "Two python servers started on 8810 and 8811 and both answered curl with 200. TaskStop stopped only bg-29c07875 (exit 143); after that 8810 refused (curl exit 7) and 8811 still answered 200. TaskOutput reported 'stopped, exit code 143' and 'running (process group 75018)' with the live tail. Checked: exit code [0], .err empty, 8/8 tool pairs in order, leftovers empty, cost recomputed at 0.0019953 matches.",
   "notes": "The record shows the second server stopped with the process (endedWithProcess) when the session closed. Port and process checks are clean now. Cache read ratio 0.969. The model made one extra ls call, which is harmless. No background-end notice was due, because the model stopped the task itself."
  },
  {
   "id": "d2-bad-bg-command",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "The missing command settled failed with exit 127 and stderr shown, without hanging. The sh command settled failed with exit 3 after about 1 s, showing 'starting' and 'crashed'. TaskOutput gave 'failed, exit code 3' and 'failed, exit code 127', and tasks frames show status failed with the right exit codes. Checked: exit code [0], toolStates failed 2 and complete 2, pairs intact, leftovers empty, cost recomputed at 0.001160035 matches.",
   "notes": "The exit code appears three times in the model-facing result (known and kept) and reads correctly. The end was seen in the tool result, so no duplicate 'while you worked' notice appeared. The model correctly reported both failures."
  },
  {
   "id": "d3-unicode-bg",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "Chinese, emoji, the ZWJ sequence and raw ANSI sequences stayed intact in the Bash start output, every tasks frame tail, both TaskOutput results and the saved session. No U+FFFD appears in the logs or saved contexts. outputBytes reached 324, which is 6 x 54 bytes, the exact size of each UTF-8 line. Final state completed, exit 0. Checked: exit code [0], 3/3 pairs, leftovers empty, cost recomputed at 0.00123396 matches.",
   "notes": "Model and timing point: the start look returned 4 of 6 lines at the 2 s cap, and the task ended about 6 ms before the model's first TaskOutput, so both TaskOutput calls returned 'completed'. The model's claim that it read the output 'once while it was still running' really refers to the Bash start look. Reading while running is shown by tasks frames here and by TaskOutput in d1 and d4. The scenario's check concerns mojibake, and it holds."
  },
  {
   "id": "d4-watcher-left-running",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "node --watch started in the background and answered 'ok'. After the FileEdit the watcher logged 'Change detected' and 'Restarting', and curl returned 'updated'. TaskOutput showed it running with that tail. The final server.mjs contains 'updated'. After driver exit the record shows it stopped with the process, exit 0. Checked: result.json leftovers and listeners empty, no node, watchdog or listener left now, exit code [0], 7/7 pairs, cost recomputed at 0.00164837 matches.",
   "notes": "The watcher was left running at turn end as asked, and the process-group cleanup also ended the watcher's child. The start look gave 'No output yet' at the 2 s cap because this server prints nothing, which is correct."
  },
  {
   "id": "d5-max-bg",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "Eight sleeps started. Call #9 settled failed with 'Error: 8 background tasks are already running. Stop one with TaskStop first.' Identical call #10 settled refused by the repeat guard, with 'Not run: this exact Bash call failed…', and did not run. TaskList listed the 8 background shells as running, then as 'stopped, exit code 143' after 8 TaskStop calls. The tasks frame shows all 8 stopped. Checked: toolStates complete 19, failed 1, refused 1; 21/21 saved pairs; exit code [0]; cost recomputed at 0.00314755 matches.",
   "notes": "The refused call has a tool-result frame and no tool-use frame, which is by design, and the saved session pairs it. The runner's process check greps for the run path and would miss 'sleep 300', so I checked separately. The model's pgrep found none, the shells settled only after their groups were gone, and no 'sleep 300' or gcode-watchdog process exists now. BSD pgrep excludes its own ancestors, so the model's pgrep result is reliable."
  },
  {
   "id": "d6-interrupt-resume",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "The interrupt landed on the 6th call, a TaskStop. It settled cancelled and said once 'The turn was stopped before background task… It was sent SIGTERM, and gets SIGKILL 2 s later if it is still running.' The error frame read 'Stopped before this turn finished.' The saved turn is marked '[Stopped by user. This turn is incomplete…]' with all results recorded. The resumed session had no 400, was told bg-7645afd2 had been stopped with the process, did not repeat npm init or the file write, added /time, verified both routes, then stopped the new server (exit 143). Checked: exit codes [0,0], .err empty, 13/13 pairs in order, leftovers empty, cost recomputed at 0.004337305 matches. I ran the app on a scratch port: /health and /time both answered, then I stopped it and confirmed the port was free.",
   "notes": "The model used `node index.js … &` instead of run_in_background, and G-Code adopted that group as a task and pointed the model to run_in_background. The next turn's session-memory line repeats the first line of the TaskStop error as a separate context item, which is not a duplicate in the tool result. The old pid was dead and the port free before the restart. The model deleted its stale server.pid and server.log, which was its own choice and was disclosed. Cache read ratio 0.969 across the resume."
  },
  {
   "id": "d7-sigkill-orphan",
   "status": "pass",
   "product_defects": [],
   "task_outcome": "The CLI was killed with SIGKILL during the foreground sleep: result.json shows killed true and serverGoneMs 364, and the driver exited 137 as designed. The saved session is valid. The last Bash call has the synthetic result 'No result was recorded: the process ended before this call finished…', followed by the '[The process ended before this turn finished…]' note. The shell record stays 'running' from a dead process, which is how a later process learns it ended. Checked: result.json leftovers empty, no process or listener now, 3/3 pairs, cost recomputed at 0.00086373 matches.",
   "notes": "The runner's process check would not see the foreground 'sleep 200', so I probed it offline under a scratch HOME: snapshot runSubprocess with the same group and killOnExit options, then kill -9 of its parent. The sleep was gone within 0.1 s and no watchdog remained; the scratch folder was removed. The session .lock file stays after SIGKILL, as expected with no exit path, and this scenario does not resume. No DiagnosticReports read or 'log show' effect is visible, and the log, incident and DSH checks found nothing. Guillotine Mode was not exercised."
  }
 ]
}