Skip to content

A session that abandons a hung background task no longer fails a Run whose work is done - #305

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-302
Oct 2, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-302

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

A session that ends its turn with a background task still running gets its one Resume, as before. If the Resume ends the same way, the task may have been abandoned rather than awaited (a hung test run the agent could not stop), so the Run no longer fails there: thirdshift names the killed work in a progress line and carries on, and the steps that follow decide the outcome.

 Sessions::run(kind, prompt)
   start the session
   if background work was killed after its last result, and it has a session id
     Resume it, once
   if background work was killed again (or there was no session id to resume)
-    fail: "<kind> session ended while waiting on a background task (<task>), which was killed"
+    progress line: "<kind>: the Resume ended with a background task still running (<task>), which was killed; carrying on, …"
+    remember it on Sessions
+  return the final message (the Resume's, if there was one)

So that a session that really did stop short is not reduced to a bare no PR found, a later failure names the killed work ahead of its own cause, on stderr, in the Failed run commit and in the Run notification:

implement session ended with a background task still running (./mvnw test), which was killed, and a later step failed: no PR found

No caller has to ask for that. Sessions can now only be had through Sessions::within, which runs the steps it is given and adds the killed work to their failure:

 implement / review_and_deliver / architect review
-  let sessions = Sessions { logs, worktree, plugin_dir };
-  sessions.run(…)?; push; mark ready; deliver
+  Sessions::within(logs, worktree, plugin_dir, |sessions| {
+    sessions.run(…)?; push; mark ready; deliver      # Architect run: session, then conclude
+  })

Session prompts: the headless paragraph that ends every Session prompt, and the Resume prompt, now name TaskStop.

 … Never end your turn while a background task you depend on is still running: ending the turn kills it.
+Before ending your turn, stop every background task you no longer need with the `TaskStop` tool, by its task id:
+a task still running when your turn ends is taken as work you were waiting on.
 Re-run whatever you were waiting on in the foreground, then finish your job.
+If the re-run hangs or is moved to the background again, stop it with the `TaskStop` tool, by its task id,
+and say what could not be run, rather than leaving it running.

Also: prompts/ and the site's prompts page regenerated; CONTEXT.md's Resume entry, the README and the Progress doc comments say a task killed after the last result may have been awaited.

Two things a reviewer might not expect:

  • The Failed run commit's subject is now the whole cause as stderr prints it ({error:#}, first line), not only the outermost message. This is what lets it name both the killed work and the later failure, but it applies to every Failed run whose error carries context.
  • In an Architect run, the review's worktree now lasts until the review is concluded (the plan checked and labelled), not only until the session ends, so that a failure to conclude can name the killed work.

Evidence

  • Before: a Resume that ended with a killed background task failed the Run: implement session ended while waiting on a background task (./mvnw test -Dtest=GamesPageTest), which was killed, with the PR check, marking ready and delivery never reached.
    After: cargo test passes in full (30 test binaries, 0 failed), cargo clippy --all-targets is clean. New and changed tests:
tests/background_work.rs
  a_resume_that_ends_with_killed_background_work_is_warned_about_and_the_run_goes_on
    session 1 leaves a task running; its Resume opens a draft PR and leaves one running again
    → exit 0, 2 sessions (one Resume, never two), progress line names the task, PR marked ready
  a_session_with_no_id_to_resume_that_ends_with_killed_background_work_is_warned_about_too
    → exit 0, 1 session, progress line names the task
  a_run_that_fails_after_a_resume_ended_with_killed_background_work_names_that_work_too
    → exit 1; stderr and the Failed run commit: "implement session ended with … which was killed, and a later step failed: no PR found"
  a_merge_refused_after_killed_background_work_still_leaves_the_pr_ready_for_review
    → the Policy refusal is still recognised: PR stays ready, no failure commit
  a_session_that_ends_in_an_error_with_killed_background_work_is_not_resumed_or_warned_about
    → 1 session, "claude exited 3", nothing about background work
tests/spec_run.rs
  a_spec_review_whose_resume_ends_with_killed_background_work_still_delivers_the_spec_pr
    → Tickets checklist put back, Spec PR marked ready
tests/architect_run.rs
  a_review_whose_resume_ends_with_killed_background_work_is_read_by_the_resumes_final_message
  an_architect_run_that_fails_after_killed_background_work_names_that_work_too
tests/run_notification.rs
  a_run_that_fails_after_killed_background_work_names_that_work_in_its_notification

Unaddressed findings

Standards

  • run_to_final_message records killed work on Sessions through a RefCell, rather than returning it (codebase-design: return results, not side effects). Kept: returning it would make every caller carry it to the failure, which the issue rules out ("it must not depend on the caller remembering to ask"), and the issue names "kept on Sessions" as an option.
  • Primitive Obsession: Sessions keeps each ending as a formatted string, not kind plus tasks. Kept: the same wording goes to the progress line and the cause, and nothing reads the parts back.
  • review in src/architect.rs grew to 7 parameters and now also concludes. Kept: the session and conclude have to share one Sessions::within for a failure to conclude to name the killed work, and review owns the worktree and plugin directory that scope borrows.
  • Tests call the new line a warning, but it is printed with progress::step, without the warning: prefix. Kept: the issue asks for "a progress line naming the killed work", and progress::warn is for an error plus something to do by hand, which this is not.
  • The switch to {error:#} in src/failed_run.rs touches every Failed run with a context chain (possible Divergent Change). Kept, see Spec below.

Spec

  • The Failed run commit subject changes for every Failed run whose error has context, not only for killed work. Kept: the issue asks that the cause recorded in the Failed run commit name the killed work as well as the later failure, and the commit now carries the same cause stderr and the Run notification already print. Called out in the Summary.

Merge Danger

Door: two-way

Reverting restores the old failure. No data, config or stored state changes shape.

Blast Radius: Runs

Every session of every Run, Spec run and Architect run gets the longer headless paragraph. A Run whose session leaves a task running past its Resume now goes on to the PR check and CI instead of failing: if that session had in fact stopped short while its PR was already open and green, the PR can now be marked ready, or merged in a Merge run, where before the Run failed. Failed run commit subjects get longer where the error has context.

Closes #302

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits October 2, 2026 11:54
…n: the work may have been abandoned, so thirdshift names it and carries on, and a later failure's cause names it too; prompts tell the agent to stop tasks it no longer needs with TaskStop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it, a session with no id to resume covered, Sessions' doc comments covering that case too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 61fb7b6 into main Oct 2, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-302 branch October 2, 2026 16:30
@JacobStephens2 JacobStephens2 mentioned this pull request Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A session that abandons a hung background task fails a Run whose work is done

1 participant