Make new_suggestions and Send to Google Chat recoverable
MQ-167-01: a scheduled or manual batch now emits new_suggestions:{batchId}
in the transaction that first stores its Side quests, so the cards and
their notification commit together. A later round that fails, or a
failure recording the batch's end, no longer loses the event; a later
round's emit is a no-op by key; a notification that cannot be stored
fails that store, leaving no unannounced cards. Replenish batches still
never notify. finishBatch goes back to recording status only.
MQ-167-02: migration 0017 adds shopping_lists.chat_send_unconfirmed_at.
SendToChat records the send as begun before posting, clears it with
sent_to_chat_at once Google Chat accepts, and clears it alone when Chat
refuses. A post Chat does not answer, or one it accepts that cannot be
recorded, is ErrSendUnconfirmed (502 chat_unconfirmed, not retryable)
and leaves the marker, as does a crash mid-send; a repeat without resend
never reposts, and resend posts again. The Supply run screen re-reads
the list after a failed send, shows an unconfirmed send, offers no Retry
for it, and sends again only with resend.
Task: f0cd9d50-9c11-41be-a945-7bae9f5f28a4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,12 @@ var ErrItemNotFound = errors.New("no Supply run has that item")
|
||||
// the household's time zone.
|
||||
var ErrWeekNotEnded = errors.New("the week has not ended")
|
||||
|
||||
// ErrSendUnconfirmed is a send to Google Chat whose outcome is not known:
|
||||
// Google Chat did not answer, or accepted the post but recording it failed.
|
||||
// The post may have been made, so the Supply run keeps the send as
|
||||
// unconfirmed and is posted again only with resend.
|
||||
var ErrSendUnconfirmed = errors.New("the shopping list may have reached Google Chat, but that could not be confirmed; check the space before sending again")
|
||||
|
||||
// ErrInvalidPeriod is an Archive grouping other than ByWeek, ByMonth and
|
||||
// ByYear.
|
||||
var ErrInvalidPeriod = errors.New("by must be week, month or year")
|
||||
@@ -158,7 +164,11 @@ type List struct {
|
||||
// SentToChatAt is when the list was sent to Google Chat; nil until it
|
||||
// is.
|
||||
SentToChatAt *string `json:"sent_to_chat_at"`
|
||||
Groups []Group `json:"groups"`
|
||||
// ChatSendUnconfirmedAt is when a send to Google Chat whose outcome is
|
||||
// not known began: one under way, or one that may or may not have been
|
||||
// posted. nil when there is none.
|
||||
ChatSendUnconfirmedAt *string `json:"chat_send_unconfirmed_at"`
|
||||
Groups []Group `json:"groups"`
|
||||
}
|
||||
|
||||
// Counts are how many items of a Supply run are still needed and how many
|
||||
@@ -563,13 +573,14 @@ func frozen(list db.ShoppingList) bool {
|
||||
// listOf is list with groups.
|
||||
func listOf(list db.ShoppingList, groups []Group) List {
|
||||
return List{
|
||||
ID: list.ID,
|
||||
WeekStart: list.WeekStart,
|
||||
PlanID: list.PlanID,
|
||||
Title: nullString(list.Title),
|
||||
Status: list.Status,
|
||||
SentToChatAt: nullString(list.SentToChatAt),
|
||||
Groups: groups,
|
||||
ID: list.ID,
|
||||
WeekStart: list.WeekStart,
|
||||
PlanID: list.PlanID,
|
||||
Title: nullString(list.Title),
|
||||
Status: list.Status,
|
||||
SentToChatAt: nullString(list.SentToChatAt),
|
||||
ChatSendUnconfirmedAt: nullString(list.ChatSendUnconfirmedAt),
|
||||
Groups: groups,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user