Guided setup without plan; embedded GitHub
# Guided setup: no plan step, embedded GitHub connect (architecture child C4)
This is part of the epic "Multi-Step Signup Flow With Stripe Billing". The full settled inputs are the PRD (Task 8416) and the Architecture (Task 8417); the relevant parts are embedded below.
This task is architecture child **C4**. C0, which added `org_signups.plan_confirmed_at`, has merged. C1 sets that column at Sign up and runs in parallel with this task.
## Original requirement (verbatim)
> When user logs in the onboarding wizard does not have a plan selection and goes straight to other things according to the plan selected.
>
> Embed GitHub connection to the Guided setup.
PRD decision **D8 Plan step**: "Remove for all new signups". Guided setup never shows Choose plan for orgs created through the new signup. Existing orgs keep today's behaviour.
## PRD requirement R10 (verbatim)
- **AC10.1** For an org created by the new signup, `/welcome` has no Choose-plan step, and its next steps follow the chosen plan (for example, the plan's node step).
- **AC10.2** The Git/forge step in guided setup embeds the GitHub connection: a "Connect GitHub" button that runs the existing GitHub App install + user connect inside the wizard and returns to the same step marked done. It does not link away to `/profile?tab=git-accounts`.
- **AC10.3** Existing orgs keep the current wizard behaviour.
## Approach (Architecture §5 C4, verbatim)
- `apps/api/app/routes/org_setup.py:404-421`: omit `choose_plan` when `signup.plan_confirmed_at IS NOT NULL`. In `apps/web/src/lib/welcomeWizard.ts:131-138`, drop the plan step, and stop the node step waiting on the plan for those orgs.
- `apps/api/app/routes/git_connect.py`:
- `POST /start` accepts `return_to`, checks it against the allow-list `{"/welcome", "/profile"}`, and stores it in the flow cookie (`:232`).
- `_profile_redirect` (`:165-174`) becomes `_return_redirect(return_to)`.
- The GitHub App install hop (`:329,492-500`) and the Setup URL handler (`:503-524`) read `return_to` from a short-lived, path-scoped cookie set at `/start`. It has a 10-minute max age and the same allow-list. The cookie is used instead of GitHub echoing `state`.
- The default stays `/profile`, which keeps today's behaviour.
- `apps/web/src/components/onboarding/WelcomeWizard.tsx:180-187` passes `return_to:"/welcome"`. When the wizard comes back with `?git_connect=connected`, it refreshes the forge step.
- **Risk: open redirect.** Use only the two-path allow-list. Never follow a free-form `return_to`.
The embedded connect follows `docs/standards/ui-style-guide.md` and `docs/standards/ui-copy-standards.md`:
- The button is labelled "Connect GitHub".
- The step shows a done state with the connected account.
- A failed or cancelled connect returns to `/welcome` with an inline, legible error, not a blank step.
Credentials & reach:
| Oracle(s) | Credential / environment | Status |
|---|---|---|
| Embedded Connect GitHub (AC10.2) in a real browser | a GitHub account able to sign in interactively | deferred: the operator said to proceed without an answer; covered by server tests that mock the OAuth hop |
| Dev UI oracles AC10.x (log in as a newly approved user) | the signing-up user's own inbox | deferred: the dev walk-through is the operator's manual acceptance |
| Server tests, `./dc check full` | nothing beyond the worktree | verified |
Out of scope:
- Do NOT touch signup routes or pages (C1/C3), or approval and billing (C2).
- Do NOT add a migration.
- Do NOT change the existing orgs' guided setup (AC10.3).
- Do NOT touch the separate card-style onboarding guide (`apps/web/src/lib/onboarding.ts`) beyond what AC10.2 needs. Reusing `return_to` there is a deferred follow-up.
- Do NOT accept a free-form redirect target.
Done means:
- **AC10.1:** an org with `plan_confirmed_at` set has no `choose_plan` step, and the wizard starts at a non-plan step.
- `tests/test_org_setup*` gains a case: no `choose_plan` when it is set, still present when it is NULL.
- The `welcomeWizard.ts` unit tests gain a case.
- The dev check is deferred (inbox).
- **AC10.2:** Connect GitHub in `/welcome` runs the install and connect, and returns to `/welcome` with the forge step checked. The URL never shows `/profile`.
- `tests/test_git_connect*`: `return_to=/welcome` redirects back to `/welcome` through both the OAuth hop and the App-install Setup URL hop. An unlisted `return_to` falls back to `/profile`.
- A `WelcomeWizard` unit test: `?git_connect=connected` marks the forge step done.
- The real-browser GitHub check is deferred.
- **AC10.3:** an existing self-service org with no plan chosen still sees Choose plan, and `/profile` connect still returns to `/profile`.
- The test cases above.
- **Gate:** `./dc check quick` locally and `./dc check full` in the merge lane are green.
Outcome
Confirmed-plan sign-ups skip Choose plan; Connect GitHub in /welcome returns there via allow-listed return_to (default /profile). Quick gate + changed-area tests green; review lanes pass; visual check done. PR #6461 queued. Follow-up draft #8473. Memory: used mem-22518.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)