Files
HomerandClaude Fable 5.1 2183040db8 Harness spec 2.0.0: background work is a job — commands and subagents alike, seen and steered
One registry per process, every job owned by a session: bash's background commands (and a command
still running at its timeout, which now goes to the background instead of being killed) and
subagents started with task background: true. job_list, job_output, job_stop (the former
bash_list, bash_output, bash_kill) and the new job_send and report; a finished job wakes the owning
session in every front end with its marked note. The TUI counts jobs, lists them (/jobs), opens a
command's output, enters a subagent and messages it, moves a command to the background (ctrl+b)
and asks before quitting with jobs running. The link gains the jobs capability. Subagents get a
session row of their own, the spec's preamble and agent texts, and their errors reach the report.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-20 16:17:00 +00:00

128 lines
5.5 KiB
JSON

{
"about": "The message a session is given about its background work (loop.json jobs.marks), composed from a job's record: `kind` command or subagent; `id`; for a command its `command`, `exit` (a number, or null when it was stopped by a signal), `seconds` run and `unread` output (the last note_tail_chars of it, an ellipsis before a cut); for a subagent its `agent`, `description`, `report` (empty: \"(the subagent returned no report)\"), `steps`, `tools`, `reason` (stop, stopped, steps, budget, or error with `error` text), `seconds`, and `worktree` (a summary line, or absent); for a message from a subagent (`kind` message) its `agent`, `description` and `text`; for one to a subagent (`kind` to_subagent) the `text`. The result is `text`: the mark on the first line, then the note. Durations as both projects write them (\"5s\", \"1m 05s\", \"2h 03m\"). LLeMbas: services/jobs.py note; CLI: jobs/note.ts jobNote.",
"cases": [
{
"kind": "command",
"id": "job3",
"command": "npm test",
"exit": 0,
"seconds": 5,
"unread": "ok 12 tests\n",
"expect": {
"text": "[A background job you started has finished:]\njob3 (npm test) exited with 0 after 5s.\nOutput you had not read:\nok 12 tests"
}
},
{
"kind": "command",
"id": "job1",
"command": "python -m http.server 8000",
"exit": null,
"seconds": 125,
"unread": "",
"expect": {
"text": "[A background job you started has finished:]\njob1 (python -m http.server 8000) stopped (SIGTERM) after 2m 05s. It printed nothing you had not read."
}
},
{
"kind": "command",
"id": "job2",
"command": "make build\nmake test",
"exit": 2,
"seconds": 7325,
"unread": "…rror: missing semicolon\nmake: *** [test] Error 2\n",
"expect": {
"text": "[A background job you started has finished:]\njob2 (make build) exited with 2 after 2h 02m.\nOutput you had not read:\n…rror: missing semicolon\nmake: *** [test] Error 2"
}
},
{
"kind": "subagent",
"id": "job4",
"agent": "explore",
"description": "find the config loader",
"report": "The loader is `src/config/load.ts:40`; it is called from `src/app.ts:120`.",
"steps": 6,
"tools": 9,
"reason": "stop",
"seconds": 48,
"expect": {
"text": "[A subagent you started has finished:]\njob4 (explore: find the config loader) finished after 48s.\nThe loader is `src/config/load.ts:40`; it is called from `src/app.ts:120`.\n\n[explore subagent · 6 steps · 9 tool calls]"
}
},
{
"kind": "subagent",
"id": "job5",
"agent": "general",
"description": "add the migration",
"report": "",
"steps": 50,
"tools": 70,
"reason": "steps",
"seconds": 600,
"expect": {
"text": "[A subagent you started has finished:]\njob5 (general: add the migration) stopped at its step limit after 10m 00s.\n(the subagent returned no report)\n\n[general subagent · 50 steps · 70 tool calls · ended: steps]"
}
},
{
"kind": "subagent",
"id": "job6",
"agent": "general",
"description": "rename the module",
"report": "Renamed in 12 files.",
"steps": 8,
"tools": 14,
"reason": "stop",
"seconds": 90,
"worktree": "Branch lembas/general-rename-a1b2c3: 1 commit, 12 files changed. Merge it with `git merge lembas/general-rename-a1b2c3`, or drop it with `git branch -D lembas/general-rename-a1b2c3`.",
"expect": {
"text": "[A subagent you started has finished:]\njob6 (general: rename the module) finished after 1m 30s.\nRenamed in 12 files.\n\n[general subagent · 8 steps · 14 tool calls]\n[worktree] Branch lembas/general-rename-a1b2c3: 1 commit, 12 files changed. Merge it with `git merge lembas/general-rename-a1b2c3`, or drop it with `git branch -D lembas/general-rename-a1b2c3`."
}
},
{
"kind": "subagent",
"id": "job7",
"agent": "explore",
"description": "read the logs",
"report": "Partial: the error begins at 12:03.",
"steps": 3,
"tools": 4,
"reason": "stopped",
"seconds": 20,
"expect": {
"text": "[A subagent you started has finished:]\njob7 (explore: read the logs) was stopped after 20s.\nPartial: the error begins at 12:03.\n\n[explore subagent · 3 steps · 4 tool calls · ended: stopped]"
}
},
{
"kind": "subagent",
"id": "job8",
"agent": "explore",
"description": "check the API",
"report": "",
"steps": 1,
"tools": 0,
"reason": "error",
"error": "connection refused",
"seconds": 2,
"expect": {
"text": "[A subagent you started has finished:]\njob8 (explore: check the API) failed after 2s.\n(the subagent returned no report)\n\n[explore subagent · 1 step · 0 tool calls · ended: error — connection refused]"
}
},
{
"kind": "message",
"id": "job4",
"agent": "explore",
"description": "find the config loader",
"text": "There are two loaders; which one — the YAML one or the env one?",
"expect": {
"text": "[A message from a subagent you started:]\njob4 (explore: find the config loader):\nThere are two loaders; which one — the YAML one or the env one?"
}
},
{
"kind": "to_subagent",
"text": "The YAML one. Ignore the env loader.",
"expect": {
"text": "[The main agent sent this while you were working — take it into account from here:]\nThe YAML one. Ignore the env loader."
}
}
]
}