From 42ee29bcc2250dd235f42062647ef144a8d0b659 Mon Sep 17 00:00:00 2001 From: Raghav Sood Date: Thu, 12 Mar 2026 17:52:14 +0800 Subject: [PATCH] fix: send heartbeat on new orchestrator sessions Remove the early return in SendHeartbeat that was incorrectly skipping the heartbeat message when a new orchestrator session was created. This bug caused new tasks to never be picked up because the orchestrator never received the heartbeat instructions to check for new issues. This change also adds retry logic for failed heartbeats and only increments the heartbeat count for non-bootstrap heartbeats to avoid premature session rotation. Fixes #72 --- internal/agent/orchestrator.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/internal/agent/orchestrator.go b/internal/agent/orchestrator.go index 24a3ca2..3a93e3e 100644 --- a/internal/agent/orchestrator.go +++ b/internal/agent/orchestrator.go @@ -176,11 +176,10 @@ func (o *Orchestrator) SendHeartbeat(ctx context.Context) error { return fmt.Errorf("ensure session: %w", err) } - if justCreated { - log.Printf("agent: session just bootstrapped, skipping duplicate heartbeat") - return nil - } - + // Always send a heartbeat message, even if the session was just bootstrapped. + // The orchestrator needs to receive heartbeat instructions to write decision + // files (new_tasks.json, etc.). Without this, ProcessDecisions would run + // immediately but find no decision files to process. msg := o.composeHeartbeatMessage(ctx) if err := o.client.SendMessage(ctx, orch.SessionID, msg, ""); err != nil { @@ -190,10 +189,18 @@ func (o *Orchestrator) SendHeartbeat(ctx context.Context) error { if err != nil { return fmt.Errorf("recreate session after send failure: %w", err) } - return nil + // Retry sending heartbeat to the new session + msg = o.composeHeartbeatMessage(ctx) + if err := o.client.SendMessage(ctx, orch.SessionID, msg, ""); err != nil { + return fmt.Errorf("retry heartbeat after recreation: %w", err) + } } - o.queries.IncrementHeartbeatCount(ctx, orch.ID) + // Only increment heartbeat count for non-bootstrap heartbeats. + // When justCreated is true, this is the bootstrap heartbeat, not a regular one. + if !justCreated { + o.queries.IncrementHeartbeatCount(ctx, orch.ID) + } if err := o.waitForIdle(ctx, orch.SessionID); err != nil { return fmt.Errorf("wait for idle: %w", err)