cpatpa adds a mandatory onboarding wizard and per-user custom instructions
Three commits build the post-signup data-capture flow: a redirect-enforced onboarding wizard, a custom instructions panel with a 4096-char server-side cap, and a practice profile panel for updating the same fields after signup. The org-specific content (Piper Alderman's AU jurisdictions and practice areas) is seeded directly in `org_settings`.
The wizard (afde5326) is gated by middleware: any authenticated user with onboarded === false is redirected to /onboarding for every non-public route except /onboarding itself and /api/session-token (needed to mint a bearer token for the backend submit). The wizard collects display name, office, jurisdictions, practice areas, sectors, an optional "other" area, and the AI-use disclaimer. Submit is atomic - all profile fields, onboarded_at, and ai_disclaimer_at land in a single transaction alongside an user.onboarding.complete audit event. On success the wizard calls useSession().update({ onboarded: true }) to flip the JWT flag without forcing a sign-out, then redirects to /assistant.
The backend exposes four /me routes: GET /me, GET /me/onboarding-options (active entries from org_settings sorted by order), POST /me/onboarding, and PATCH /me. Validation runs server-side against the canonical lists in org_settings - unknown ids are filtered out, and the submit fails closed if no valid ids remain for any required axis.
Custom instructions (PATCH /me + d2a8512b) accept up to 4096 characters and truncate server-side if the payload is longer. The account panel shows a live char counter that turns red past the cap, plus a Discard button when the field is dirty. The additive-only contract - org system prompt takes precedence - is explained in the UI copy and enforced server-side at prompt-assembly time (Phase 9 in this fork).
The practice profile panel (99046ce8) surfaces the same wizard fields on /account for post-signup edits. Changing office snaps the timezone to that office's default. Both panels share the /me/onboarding-options endpoint for their picklist data.
One detail worth pulling: the jwt callback in auth.ts handles trigger === "update" so the onboarding flag propagates to the session token without a sign-out cycle. It's a two-line pattern but easy to miss.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?