diff --git a/internal/calendar/scheduler.go b/internal/calendar/scheduler.go index 2a82a79..f594130 100644 --- a/internal/calendar/scheduler.go +++ b/internal/calendar/scheduler.go @@ -158,6 +158,9 @@ func (s *Scheduler) tickForAgent(ctx context.Context, agent ReportableAgent, now chosen = slot } } + s.host.Log("info", "calendar slot selection", map[string]any{ + "agent": agent.ID, "available": len(resp.Slots), "chosen": chosen != nil, + }) if chosen != nil { s.dispatchSlot(ctx, agent.ID, *chosen) } @@ -173,14 +176,19 @@ func (s *Scheduler) tickForAgent(ctx context.Context, agent ReportableAgent, now // transition immediately. func (s *Scheduler) dispatchSlot(ctx context.Context, agentID string, slot Slot) { ident := slot.SlotIdent() + s.host.Log("info", "calendar dispatchSlot enter", map[string]any{ + "agent": agentID, "slot_ident": ident, + }) s.mu.Lock() if _, dup := s.activeBySlotIdent[ident]; dup { s.mu.Unlock() + s.host.Log("info", "calendar dispatchSlot skipped (already active)", map[string]any{"slot": ident}) return } if _, agentBusy := s.activeByAgentID[agentID]; agentBusy { // Don't pick up another slot until the current one resolves. s.mu.Unlock() + s.host.Log("info", "calendar dispatchSlot skipped (agent has active slot)", map[string]any{"agent": agentID}) return } now := time.Now().UTC() @@ -191,12 +199,21 @@ func (s *Scheduler) dispatchSlot(ctx context.Context, agentID string, slot Slot) message := buildWakeMessage(slot) source := "calendar:" + ident + s.host.Log("info", "calendar firing WakeAgent", map[string]any{ + "agent": agentID, "slot": ident, "source": source, "msg_len": len(message), + }) if err := s.host.WakeAgent(ctx, sdkplugin.WakeAgentRequest{ AgentID: agentID, Message: message, Source: source, }); err != nil { + s.host.Log("warn", "calendar WakeAgent failed", map[string]any{ + "agent": agentID, "err": err.Error(), + }) s.resolveLocally(ident, agentID, SlotAborted, "", "wake failed: "+err.Error()) return } + s.host.Log("info", "calendar WakeAgent enqueued ok", map[string]any{ + "agent": agentID, "slot": ident, + }) // Mark Ongoing on the backend. update := SlotAgentUpdate{ Status: SlotOngoing, StartedAt: now.Format("15:04:05"), diff --git a/internal/calendar/types.go b/internal/calendar/types.go index aa8669c..5b1848f 100644 --- a/internal/calendar/types.go +++ b/internal/calendar/types.go @@ -8,16 +8,18 @@ package calendar import "time" // SlotStatus enumerates the lifecycle. String values match backend's -// SlotStatus enum verbatim (camelCase as stored in DB). +// SlotStatus enum verbatim (snake_case — verified via heartbeat +// response shape against running harborforge-backend). type SlotStatus string const ( - SlotNotStarted SlotStatus = "NotStarted" - SlotOngoing SlotStatus = "Ongoing" - SlotFinished SlotStatus = "Finished" - SlotAborted SlotStatus = "Aborted" - SlotDeferred SlotStatus = "Deferred" - SlotPaused SlotStatus = "Paused" + SlotNotStarted SlotStatus = "not_started" + SlotOngoing SlotStatus = "ongoing" + SlotFinished SlotStatus = "finished" + SlotAborted SlotStatus = "aborted" + SlotDeferred SlotStatus = "deferred" + SlotPaused SlotStatus = "paused" + SlotSkipped SlotStatus = "skipped" ) // SlotType: work vs on_call. Affects whether the agent flips to busy.