Files
Paul LütznerandClaude Opus 5 d0d18e3059 feat(ai): the setup wizard asks once, and takes no for an answer
The AI step, sitting with the other two admin steps because it configures
the install rather than a person's mailbox — and last of the three,
because it is the only one that can reasonably be answered with "no".
Mail credentials and integrations decide whether the app works at all.

SATISFIED MEANS ANSWERED, NOT ENABLED

"No thank you" is a complete answer and has to count as one, or the
wizard nags forever at every install that does not want this — which
will be most of them. So persist() writes the row even when every box is
empty, and isSatisfied() asks only whether a row exists.

That is deliberately weaker than the sibling steps, and it has to be: the
strongest available signal of "decided against" is indistinguishable from
"never opened" unless something is written down when they say no. The row
IS that record — the only way one appears is somebody pressing the button
here or in Admin -> AI.

FOUR PLACES, NOT ONE

Adding a step touches the enum case (the ordering), the icon match, the
requiresAdmin match, and OnboardingController::STEP_PATTERN. The last is
the one that does not update itself, and the comment above it is blunt
about the cost: a new case without it 404s AND takes the whole wizard
down with it, because the progress rail generates a URL for every
applicable step. The icon match is worse in a quieter way — non-exhaustive
is a fatal error, not a missing icon.

OnboardingStepCoverageTest exists to catch exactly this and passes.

The step renders the admin panel's own form, so there is one description
of these fields rather than two that drift. The aside spends half its
words on permission to skip, because that is the answer most installs
should give and a wizard that only explains how to say yes is a wizard
that pressures people into saying it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 17:54:59 +02:00
..
2026-07-30 18:48:19 +02:00
2026-07-30 18:48:19 +02:00