From f19a0ab5d6a9a7c65f3a08324ac10ad44d5b5762 Mon Sep 17 00:00:00 2001 From: max Date: Sat, 17 Jan 2026 03:41:06 -0800 Subject: [PATCH] fix(patrol): add idempotency check for digest command Checks if a 'Patrol Report YYYY-MM-DD' bead already exists before attempting to create a new one. This prevents confusing output when the patrol digest runs multiple times per day. Fixes: gt-budqv9 Co-Authored-By: Claude Opus 4.5 --- internal/cmd/patrol.go | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/internal/cmd/patrol.go b/internal/cmd/patrol.go index 3885ad4c..7f1cc612 100644 --- a/internal/cmd/patrol.go +++ b/internal/cmd/patrol.go @@ -103,6 +103,19 @@ func runPatrolDigest(cmd *cobra.Command, args []string) error { dateStr := targetDate.Format("2006-01-02") + // Idempotency check: see if digest already exists for this date + existingID, err := findExistingPatrolDigest(dateStr) + if err != nil { + // Non-fatal: continue with creation attempt + if patrolDigestVerbose { + fmt.Fprintf(os.Stderr, "[patrol] warning: failed to check existing digest: %v\n", err) + } + } else if existingID != "" { + fmt.Printf("%s Patrol digest already exists for %s (bead: %s)\n", + style.Dim.Render("○"), dateStr, existingID) + return nil + } + // Query ephemeral patrol digest beads for target date cycles, err := queryPatrolDigests(targetDate) if err != nil { @@ -305,6 +318,40 @@ func createPatrolDigestBead(digest PatrolDigest) (string, error) { return digestID, nil } +// findExistingPatrolDigest checks if a patrol digest already exists for the given date. +// Returns the bead ID if found, empty string if not found. +func findExistingPatrolDigest(dateStr string) (string, error) { + expectedTitle := fmt.Sprintf("Patrol Report %s", dateStr) + + // Query event beads with patrol.digest category + listCmd := exec.Command("bd", "list", + "--type=event", + "--json", + "--limit=50", // Recent events only + ) + listOutput, err := listCmd.Output() + if err != nil { + return "", err + } + + var events []struct { + ID string `json:"id"` + Title string `json:"title"` + } + + if err := json.Unmarshal(listOutput, &events); err != nil { + return "", err + } + + for _, evt := range events { + if evt.Title == expectedTitle { + return evt.ID, nil + } + } + + return "", nil +} + // deletePatrolDigests deletes ephemeral patrol digest beads for a target date. func deletePatrolDigests(targetDate time.Time) (int, error) { // Query patrol digests for the target date