Fix stale Regular grounding and enforce three Regulars for onboarding
CI / ci (push) Successful in 2m20s
CI / ci (pull_request) Successful in 2m21s

A background grounding result now applies only while the dinner is still
pending under the normalized name the job searched: GroundPendingDinner and
FailPendingDinnerGrounding match normalized_name, so a rename makes the old
job's success or failure a no-op. Renaming a dinner still pending enqueues
a fresh grounding job for the new name; if that cannot be enqueued, the
dinner's grounding is failed so it is not left pending with no job.

Completing onboarding (PATCH /api/v1/settings/household with
onboarding_completed true) is refused with 400 invalid_settings until the
household has at least three Regulars (status regular; archived and Quest
log dinners do not count), and onboarding_completed_at is set only then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 18:47:41 -04:00
co-authored by Claude Opus 5.5
parent 9baabad84c
commit 171c9c2d11
9 changed files with 355 additions and 55 deletions
+18 -2
View File
@@ -71,10 +71,15 @@ type SettingsPatch struct {
// OnboardingCompleted, when true, records that the household finished
// onboarding: onboarding_completed_at becomes the time of the update,
// unless it is already set, when the first time is kept. Onboarding is
// never undone, so false is invalid.
// never undone, so false is invalid, and it cannot first be completed
// with fewer than MinRegulars Regulars.
OnboardingCompleted *bool
}
// MinRegulars is how many Regulars the household must have to complete
// onboarding.
const MinRegulars = 3
// Problem is one invalid field of a SettingsPatch, named by its JSON key.
type Problem struct {
Field string
@@ -167,7 +172,8 @@ func (s *SettingsService) Get(ctx context.Context) (Settings, error) {
// Update validates patch, applies it, records updatedBy (the signed-in
// member's subject, which must not be empty) and publishes the new settings
// to every subscriber. An invalid patch is a *ValidationError and changes
// nothing.
// nothing, as does completing onboarding with fewer than MinRegulars
// Regulars.
func (s *SettingsService) Update(ctx context.Context, patch SettingsPatch, updatedBy string) (Settings, error) {
if updatedBy == "" {
return Settings{}, errors.New("household: update settings: no member to record as updated_by")
@@ -214,6 +220,16 @@ func (s *SettingsService) Update(ctx context.Context, patch SettingsPatch, updat
setDow(&p.ChatAutosendDow, patch.ChatAutosendDow)
setString(&p.ChatAutosendTime, patch.ChatAutosendTime)
if patch.OnboardingCompleted != nil && !p.OnboardingCompletedAt.Valid {
n, err := q.CountRegularDinners(ctx)
if err != nil {
return fmt.Errorf("household: count Regulars: %w", err)
}
if n < MinRegulars {
return &ValidationError{Problems: []Problem{{
Field: "onboarding_completed",
Reason: fmt.Sprintf("add at least %d Regulars first (have %d)", MinRegulars, n),
}}}
}
p.OnboardingCompletedAt = sql.NullString{String: p.UpdatedAt, Valid: true}
}
row, err = q.UpsertHouseholdSettings(ctx, p)