Skip to content

Commit 6dbb841

Browse files
steveyeggeclaude
andcommitted
fix(daemon): nudge agents on state divergence instead of silent accept
When the daemon detects that an agent bead state doesn't match tmux (e.g., bead says stopped but Claude is running), it now: 1. Logs the divergence clearly with STATE DIVERGENCE prefix 2. Nudges the agent with an actionable command to fix its state 3. Still skips the restart (safety - don't kill healthy sessions) This prevents silent state drift where bead state diverges from reality. Applied to: Deacon, Witness, Refinery ensure functions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent d89aae5 commit 6dbb841

1 file changed

Lines changed: 22 additions & 5 deletions

File tree

internal/daemon/daemon.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,14 @@ func (d *Daemon) ensureDeaconRunning() {
325325
hasSession, sessionErr := d.tmux.HasSession(deaconSession)
326326
if sessionErr == nil && hasSession {
327327
if d.tmux.IsClaudeRunning(deaconSession) {
328-
d.logger.Println("Deacon session healthy (Claude running), skipping restart despite stale bead")
328+
// STATE DIVERGENCE: tmux shows running but bead disagrees.
329+
// Don't kill (safety), but nudge the agent to reconcile its state.
330+
// This prevents silent state drift where bead and reality diverge.
331+
d.logger.Printf("STATE DIVERGENCE: Deacon bead='%s' but Claude is running in tmux", beadState)
332+
nudgeMsg := "[DAEMON] State divergence detected: your agent bead shows '" + beadState + "' but you appear running. Please run: bd agent state " + deaconSession + " running"
333+
if err := d.tmux.NudgeSession(deaconSession, nudgeMsg); err != nil {
334+
d.logger.Printf("Warning: failed to nudge Deacon about state divergence: %v", err)
335+
}
329336
return
330337
}
331338
}
@@ -460,8 +467,13 @@ func (d *Daemon) ensureWitnessRunning(rigName string) {
460467

461468
if err := mgr.Start(false); err != nil {
462469
if err == witness.ErrAlreadyRunning {
463-
// Session is healthy (Claude running) - bead state was stale
464-
d.logger.Printf("Witness for %s session healthy (Claude running), skipping restart despite stale bead", rigName)
470+
// STATE DIVERGENCE: tmux shows running but bead disagrees.
471+
// Don't kill (safety), but nudge the agent to reconcile its state.
472+
d.logger.Printf("STATE DIVERGENCE: Witness for %s bead='%s' but Claude is running in tmux", rigName, beadState)
473+
nudgeMsg := "[DAEMON] State divergence detected: your agent bead shows '" + beadState + "' but you appear running. Please run: bd agent state " + agentID + " running"
474+
if err := d.tmux.NudgeSession(sessionName, nudgeMsg); err != nil {
475+
d.logger.Printf("Warning: failed to nudge Witness %s about state divergence: %v", rigName, err)
476+
}
465477
return
466478
}
467479
d.logger.Printf("Error starting witness for %s: %v", rigName, err)
@@ -522,8 +534,13 @@ func (d *Daemon) ensureRefineryRunning(rigName string) {
522534

523535
if err := mgr.Start(false); err != nil {
524536
if err == refinery.ErrAlreadyRunning {
525-
// Session is healthy (Claude running) - bead state was stale
526-
d.logger.Printf("Refinery for %s session healthy (Claude running), skipping restart despite stale bead", rigName)
537+
// STATE DIVERGENCE: tmux shows running but bead disagrees.
538+
// Don't kill (safety), but nudge the agent to reconcile its state.
539+
d.logger.Printf("STATE DIVERGENCE: Refinery for %s bead='%s' but Claude is running in tmux", rigName, beadState)
540+
nudgeMsg := "[DAEMON] State divergence detected: your agent bead shows '" + beadState + "' but you appear running. Please run: bd agent state " + agentID + " running"
541+
if err := d.tmux.NudgeSession(sessionName, nudgeMsg); err != nil {
542+
d.logger.Printf("Warning: failed to nudge Refinery %s about state divergence: %v", rigName, err)
543+
}
527544
return
528545
}
529546
d.logger.Printf("Error starting refinery for %s: %v", rigName, err)

0 commit comments

Comments
 (0)