Repository navigation
fix(bg-shell): prevent stale context crash after session replacement - #120
Conversation
Ignore completion side effects from reaped jobs while recording their exit state so delayed force-kill escalation is cancelled. Clear cached UI contexts during reaping and contain stale UI access. Add regression coverage for stale contexts, replacement-session UI updates, wake delivery, and unnecessary SIGKILL escalation. Fixes itayinbarr#119
Prevent stdout and stderr events that arrive after session reaping from scheduling or delivering wakes to the replacement session. Add a real-process regression test using a SIGTERM trap. Refs: itayinbarr#119
itayinbarr
left a comment
There was a problem hiding this comment.
Approved and merging. Thank you for filing the issue and the fix together, with a harness that reproduces a timing-dependent crash deterministically. That is the hard part of this bug.
Your root cause is right, and I want to record the part that is easy to miss on review: the one-line move of hasUI into the try is the crash fix, but on its own it would have left the worse half of the bug in place. A reaped job's late data and close events would still repaint and wake the replacement session, so /new would have silently leaked the old session's job output into the new one instead of crashing. The jobs.has(job.id) guards are what actually make reaping mean something.
The ordering inside finish() is the detail I checked hardest, because it is the one that would have bitten later:
job.exited = true; // before the map check
job.exitCode = code;
job.endedAt = Date.now();
if (!jobs.has(job.id)) return;Setting exited ahead of the early return keeps scheduleForceKill's hasExited() gate honest for a job that reapAll already removed. Returning first would have re-introduced #102's orphan-SIGKILL problem from the other direction, and your fourth test pins exactly that. Good catch.
Verified locally: full suite 677 passed, typecheck clean, branch not behind main. CI was sitting in action_required because you are a first-time contributor here; I have approved the workflow run.
I am folding this into a wider pass in the same release. This is the third instance of the same shape (#108 in the context watchdog, now this), and SubCoderTracker holds a ctx across async sub-coder updates the same way, so I am adding a shared guard rather than waiting for the fourth report.
- assets/banner.svg: aider polyglot 78.7% (35B-A3B), TB2.0 rank itayinbarr#120, drop the model line, accurate annotations, re-flowed prompt lines. - site/: static one-pager (HTML/CSS/vanilla JS) deployed to GitHub Pages. Content driven by the paper + the repo's real behavior: npm install, llama.cpp serving, the actual little-coder tool-use flow, Apache-2.0. Community section features real answered issues; stars/issues/version fetch live from the GitHub API with baked-in fallbacks. - .github/workflows/pages.yml: Actions deploy of site/ to Pages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
hasUIaccess during session replacement.SIGKILL.Root cause
reapAll()sendsSIGTERMasynchronously and clears the job map before child-processdataandcloseevents necessarily finish. Those late events could access the stale cached UI context or wake and repaint the replacement session.Testing
npm test— 677 passed, 14 skippednpm run typecheckCloses #119