Skip to content

Resume a session once when its background work was killed - #59

Merged
JacobStephens2 merged 2 commits into
mainfrom
issue-58
Sep 26, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
issue-58

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

A session that ends its turn while waiting on a background Bash task is killed along with that task. The Run then used to fail with an unexplained no PR found. Now thirdshift prevents it where it can, gives the session one Resume when it happens anyway, and says why if it happens again.

 every prompt (fresh, continuation, conflict Repair, CI-fix Repair)
+  ends with: run long commands in the foreground; never end the turn
+  while a background task you depend on is still running

 run_session(kind, prompt)            # now session::Sessions::run
   run claude -p                      → Progress
+  if tasks were killed after the last result (and it wasn't an error result)
+    claude -p --resume <session_id> "your background work (…) was killed…"
+      logged and labelled as <kind>-resume
+  if tasks were killed after that one too
+    fail: "<kind> session ended while waiting on a background task (<description>), which was killed"

Progress records the session_id from init, each task's description from task_started, and tasks killed after the last result (task_updated with patch.status: "killed", or task_notification with status: "stopped"). A later result clears the list, so background sub-agents that resumed the session don't count. A Resume is not a Repair and doesn't count toward MAX_REPAIRS. CONTEXT.md and the README describe the Resume.

Seams under test: Progress (stream lines in, killed background work and session id out), and the Run binary driven by the fake claude/gh (prompts, the resume-once and fail-with-reason cases, the Failed run push). The fake claude now puts session_id: fake-session-<n> in its init line, and can emit lines after its closing result.

Evidence

  • Before: the session starts ./mvnw test in the background and ends its turn. The task is killed and the Run ends with thirdshift: no PR found.
    After: tests/background_work.rs:
    session 1: task_started b1 "./mvnw test …"; result; task_updated b1 killed
    session 2: commits, opens PR
    → exit 0; session 2 argv has --resume fake-session-1; logs *-implement.jsonl + *-implement-resume.jsonl
    both sessions kill b1
    → Failed run "implement session ended while waiting on a background task (./mvnw test -Dtest=GamesPageTest), which was killed"
      (not "no PR found"); uncommitted work pushed in the failure commit
    CI-fix Repair kills b1 → resumed as repair-1-resume, then goes green
    
  • Unit tests in progress.rs cover: killed after the last result; stopped notifications, deduplicated; no kills; sub-agents that resumed; a kill before a later result; kills after an error or max_turns result; init without a session id.
  • cargo test --no-fail-fast: everything passes except ci::waits_for_github_to_work_out_whether_the_pr_is_mergeable. That test is timing-sensitive (300 ms grace) and also fails 3 of 4 times on main at this machine's current load, so this change didn't cause it.

Merge Danger

Door: two-way

Blast Radius: sessions

Every prompt gets one more paragraph. A session that ends with killed background work now costs one extra claude --resume session before the Run goes on or fails.

Unaddressed findings

Standards

  • Progress now tracks task state as well as progress lines (Divergent Change): skipped. The issue names "Progress, or whatever condenses the stream" as the place for this, and both jobs read the same events in one pass.
  • Killed work as plain &str/String lists (Primitive Obsession): partly addressed. Removing the Ended struct got rid of the re-allocation. A dedicated type wasn't worth it for two join("; ") calls.
  • The HEADLESS wording is repeated in four test files: skipped. Those tests assert each whole prompt as a literal on purpose, so the expected values don't come from the code under test.
  • fresh has no blank line before the headless paragraph: skipped. That matches fresh's existing style, which has no blank lines between paragraphs.

Spec

  • The no-session_id path and "Resume opens no PR" have no Run-level tests: skipped. The first needs a fake claude that leaves out its session id, and no real stream does that. The second is the existing no PR found path, which is already tested. init without a session id is covered by a unit test.
  • README text goes beyond the CONTEXT.md glossary entry: kept. The README documents the log names and Failed run causes, and both change here.
  • A sub-agent killed as the session ends counts as killed background work: kept on purpose. It is work the session was waiting on, just like a Bash task.

Closes #58

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits September 25, 2026 22:24
Sessions run with claude -p, which exits when the agent ends its turn and
kills any background task still running. Every prompt now tells the agent
to run long commands in the foreground. When a session still ends with
background tasks killed after its last result, the Run resumes it once
with --resume, logged as <kind>-resume, and fails with the killed task's
description if it happens again, instead of an unexplained 'no PR found'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sessions bundles where a Run's sessions run and does the single Resume
itself, so run.rs only names the session kind. A session whose last
result was an error is not resumed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 89d0500 into main Sep 26, 2026
13 checks passed
@JacobStephens2 JacobStephens2 mentioned this pull request Sep 26, 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 ends its turn waiting on a background task is killed, and the Run fails with 'no PR found'

1 participant