···991010## Build, Test, and Development Commands
1111- `go fmt ./...` then `go vet ./...`: Baseline formatting and static checks.
1212+- `golangci-lint run ./...`: For expanded go linters
1313+- `export ENABLE_MOCKS=1`: to test without kroger, openai credentials
1214- `go test ./...`: Run unit tests across all packages; add `-cover` when changing core logic.
1313-- `export ENABLE_MOCKS=1`: to test without kroger, openai credentials
1415- `go run ./cmd/careme -serve -addr :8080`: Start the web server (requires env vars below).
1516- `go run ./cmd/careme -zipcode 98101`: Helper to list Kroger location IDs by ZIP.
1617- `go build -o bin/careme ./cmd/careme`: Produce a local binary for manual runs.
+5-2
cmd/careme/mail.go
···132132 p.LastRecipes = append(p.LastRecipes, last.Title)
133133 }
134134 if err := rio.SaveParams(ctx, p); err != nil {
135135- if errors.Is(err, recipes.AlreadyExists) {
135135+ if errors.Is(err, recipes.ErrAlreadyExists) {
136136 slog.InfoContext(ctx, "params already exist, another process likely generated", "user", user.ID)
137137 return
138138 }
···154154 }
155155156156 var buf bytes.Buffer
157157- recipes.FormatMail(p, *shoppingList, &buf)
157157+ if err := recipes.FormatMail(p, *shoppingList, &buf); err != nil {
158158+ slog.ErrorContext(ctx, "failed to format mail", "error", err)
159159+ return
160160+ }
158161159162 from := mail.NewEmail("Chef", "chef@careme.cooking")
160163 subject := "Your new recipes are ready!"