fix(channel): use worker_handles as guard for WorkerComplete events#331
Conversation
active_workers (HashMap<WorkerId, Worker>) is never populated because the Worker struct is consumed by worker.run() when spawned. This caused every WorkerComplete event to be silently dropped — the guard check at handle_event always returned early since active_workers was empty. Switch to worker_handles (which IS populated by spawn_worker_task) as the source of truth for whether a worker is active. This matches how cancel_worker_with_reason already treats worker_handles as sufficient. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| // Use worker_handles as the source of truth for active workers. | ||
| // (active_workers is never populated because Worker is consumed by .run()) | ||
| if self.state.worker_handles.write().await.remove(worker_id).is_none() { | ||
| return Ok(()); | ||
| } | ||
| drop(workers); | ||
|
|
||
| run_logger.log_worker_completed(*worker_id, result, *success); | ||
|
|
||
| self.state.worker_handles.write().await.remove(worker_id); | ||
| self.state.active_workers.write().await.remove(worker_id); | ||
| self.state.worker_inputs.write().await.remove(worker_id); |
There was a problem hiding this comment.
Minor robustness tweak: the early return on missing worker_handles skips cleanup of worker_inputs (and any future active_workers usage) if we ever see duplicate/late WorkerComplete events. Consider always cleaning those up, and reword the comment to avoid the absolute “never populated” claim.
| // Use worker_handles as the source of truth for active workers. | |
| // (active_workers is never populated because Worker is consumed by .run()) | |
| if self.state.worker_handles.write().await.remove(worker_id).is_none() { | |
| return Ok(()); | |
| } | |
| drop(workers); | |
| run_logger.log_worker_completed(*worker_id, result, *success); | |
| self.state.worker_handles.write().await.remove(worker_id); | |
| self.state.active_workers.write().await.remove(worker_id); | |
| self.state.worker_inputs.write().await.remove(worker_id); | |
| // `worker_handles` is the source of truth for running workers. | |
| let removed_handle = self.state.worker_handles.write().await.remove(worker_id); | |
| self.state.active_workers.write().await.remove(worker_id); | |
| self.state.worker_inputs.write().await.remove(worker_id); | |
| if removed_handle.is_none() { | |
| return Ok(()); | |
| } | |
| run_logger.log_worker_completed(*worker_id, result, *success); |
…el_id propagation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughUse Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
jamiepine
left a comment
There was a problem hiding this comment.
I broke this on my last PR but my internet went out so I couldn't fix it before sleep, thank you!
Summary
WorkerCompleteevent handler inchannel.rsguarded onactive_workers.remove(), butactive_workers(HashMap<WorkerId, Worker>) was never populated — theWorkerstruct is consumed by.run()when spawned, so it can't be storedWorkerCompleteevent was silently dropped at the early returnworker_handles, which is the actual source of truth for running workers (populated byspawn_worker_task, also used bycancel_worker_with_reason)Test plan
cancel_workerstill works correctly🤖 Generated with Claude Code