Repository navigation
Conversation
… wakes it Claude Code wakes itself when a background task finishes, even after the MonoCode turn has ended, but the adapter only tracks tasks for the turn it has open. A task left from an earlier turn therefore had no row under the answer Claude yielded with, and the work it woke Claude to do folded that answer away. Once Claude has yielded, a notification for a task with no row now adds a finished background row under the answer, so yieldedAt keeps it out of the fold.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
What changed
When Claude Code wakes itself for a background task that MonoCode is no longer tracking (the turn that started it is over), the task now gets a finished background row under the answer Claude yielded with, the same row it would have had if the turn had waited for it. The work Claude does after it wakes no longer folds that answer away.
Why
Claude Code wakes itself when a background task finishes, even after the MonoCode turn has ended. The adapter only follows tasks for the turn it has open:
sendClaudeTurnclearsbackgroundTasks/backgroundRowson every new message, andshowBackgroundRowsreturns early when!live.activeTurn. So a task started before the latest message, or one Claude backgrounds in a turn it woke itself for (for example a watcher it re-arms on every wake), never gets a row.noteTaskNotificationreturns atif (!live.activeTurn) return;, so the answer still gets no row.yieldedAt()looks for a background row right after prose, finds none, andfoldableWork()reads the answer as commentary on the work that follows and folds it.The fix is in
noteTaskNotification. Once Claude has yielded (turnResultSeen), a notification for a task that has no row adds one through the sametool.startedasshowBackgroundRows(now a small sharedshowBackgroundRowhelper) and settles it right away.yieldedAt()then stops the fold at the answer. This doesn't change:handleLinemutes everything, the same as before.How it shows up (hit in daily use in a downstream fork with the same adapter code, with a heartbeat watcher Claude re-arms on every wake):
Related: #888 handles a different case in a different place. There the user writes in while the turn is still open and waiting, and the second answer has no row under it, so #888 marks yielded answers in
core/apply.ts/transcriptActivity.tsfrombackground.updated. Here the MonoCode turn has already ended, sobackground.updatednever fires for the task, and #888's mark doesn't apply. This PR only touches the Claude adapter (claude.ts). The two don't overlap, and either can land first.UI
No chrome change. The transcript now keeps the yielded answer above a finished background row instead of folding it. No screenshot because reproducing it needs a live background task that outlives the turn. The unit test below covers the transcript result.
Validation
keeps the answer above what a task from an earlier turn wakes Claude to do(claudeLive.test.ts): ends a turn at "The answer.", then sends atask_notificationfor an untracked task, followed by a woken turn (Bash + reply). It checks the block order and that the fold doesn't take the answer. It fails onmain: there's no background row (['The answer.', '[tool]', 'Pushed back half an hour.']), and with that assertion removed,foldedBlockscontains'The answer.'. It passes with this change.adds no row for a task that finishes before Claude yields or after a stop: guards the mid-turn and post-stop paths. It passes onmainas well; removing the newturnResultSeencheck makes it fail.npm run check:web: vitest 483 files / 5365 tests passed (2 files / 13 tests skipped),tsc --noEmitclean. No Rust touched, so I didn't runcheck:rust.prettier --checkis clean on the changed hunks. The test file has some pre-existing unformatted lines that I left alone.Checklist
npm run check(check:web; no Rust files changed)Summary by CodeRabbit