Expose the Supply run title job and cover the replacement race
CI / ci (push) Successful in 8m1s
CI / ci (pull_request) Successful in 7m52s

POST /shopping/{weekStart}/generate now answers with title_job_id, and
Push to Calendar's supply_run carries it too, so a client can follow the
title_supply_run job through GET /jobs/{id}, including a title_failed
failure. It is null when the list already has its title or was not
generated.

Add a regression test that replaces the week's list after archive_title
answers but before the title is stored: the old job fails title_failed
and the replacement stays untitled.

Maestro task a55adc28-4d8b-4517-b1d7-dcff39ea5a09.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-11 07:41:10 -04:00
co-authored by Claude Opus 5.5
parent be41b56690
commit a2ae76b5d4
5 changed files with 134 additions and 22 deletions
+10 -5
View File
@@ -111,10 +111,13 @@ type pushedPlan struct {
// plan Calendar does not show and pushing again makes it; or
// supply_run_failed when it could not be made, and POST
// /shopping/{weekStart}/generate makes it again. error is null when status
// is generated.
// is generated. title_job_id is the job titling a generated Supply run,
// which GET /jobs/{id} reports on; null when it already has its title or
// was not generated.
type pushedSupplyRun struct {
Status string `json:"status"`
Error *apiError `json:"error"`
Status string `json:"status"`
Error *apiError `json:"error"`
TitleJobID *string `json:"title_job_id"`
}
// postPlanPush pushes the week's plan to the chosen Google calendar
@@ -143,7 +146,9 @@ func postPlanPush(logger *slog.Logger, svc *plan.Service, shop *shopping.Service
// gone.
ctx := context.WithoutCancel(r.Context())
run := pushedSupplyRun{Status: "generated"}
if _, err := shop.GeneratePushed(ctx, week, revision); errors.Is(err, shopping.ErrFrozen) {
if g, err := shop.GeneratePushed(ctx, week, revision); err == nil {
run.TitleJobID = g.TitleJobID
} else if errors.Is(err, shopping.ErrFrozen) {
logger.InfoContext(ctx, "Supply run in the Supply archive left as it was after Push to Calendar", "week", week.String())
run = pushedSupplyRun{Status: "archived", Error: &apiError{Code: "archived", Message: err.Error()}}
} else if errors.Is(err, shopping.ErrPlanChanged) {
@@ -153,7 +158,7 @@ func postPlanPush(logger *slog.Logger, svc *plan.Service, shop *shopping.Service
Message: "the plan is on Calendar as pushed, but it was edited before its Supply run was made, so no Supply run was made from it; push again",
Retryable: true,
}}
} else if err != nil {
} else {
logger.ErrorContext(ctx, "generate Supply run after Push to Calendar", "week", week.String(), "error", err)
run = pushedSupplyRun{Status: "failed", Error: &apiError{
Code: "supply_run_failed",