Skip to content

feat(portal-v3): integrator golden path — wave 1 redesign - #2079

Draft
soamdesai-tfh wants to merge 32 commits into
mainfrom
feat/pv3-redesign-w1
Draft

feat(portal-v3): integrator golden path — wave 1 redesign#2079
soamdesai-tfh wants to merge 32 commits into
mainfrom
feat/pv3-redesign-w1

Conversation

@soamdesai-tfh

@soamdesai-tfh soamdesai-tfh commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What

Wave 1 of the v3 portal redesign: an integrator-first golden path for the developer adding World ID to their app (proof-of-personhood via World ID 4.0), distinct from the miniapp-builder flow. Entirely behind the per-email PORTAL_V3_EMAILS flag — v2 users see no change.

The path: sign up → auto-created personal team (no team ceremony) → onboarding ("Verify your first human") → create app (auto-registers the RP with a managed signer key, one save-your-key step) → create an action → embedded IDKit quickstart → watch the first verified proof arrive on the dashboard.

Pieces (8 tasks)

  • Capability-driven navuseAppCapabilities hook → sidebar shows Dashboard · Actions · Sign in · Configuration · Advanced; Mini App and Legacy actions appear only for apps that have them. The old "World ID" item is replaced by "Advanced".
  • Auto personal team on first v3 login — client orchestration over the existing /api/create-team (no backend change); fails loud to the manual create-team page.
  • Create-app auto-registers the RP (managed mode, auto-generated signer key) with a single "save your key" step; 6-step dialog collapsed to create → registering → key-ready → register-failed. Shared use-auto-register-rp hook.
  • Dashboard SetupStrip — RP recovery / first-action CTA / live "waiting for first verification → ✓" poll (the aha moment).
  • Verification-first dashboard — stats/tabs gated by capability; miniapp apps keep today's payments view.
  • Embedded IDKit v4 quickstart on the action page (real IDKitRequestWidget snippet, staging → Simulator link).
  • /create-team forked + flipped — the route login-callback sends brand-new users to; v3 users get the auto-team there (caught in manual walkthrough: the shell-mounted bootstrap was unreachable on the real signup path), second-team creation keeps the manual form.
  • Onboarding apps page forked + flipped into v3 with the new copy (graduated out of "straggler" status — see below).

Constraints held

  • Zero @/scenes/PortalV3 → @/scenes/Portal imports (decoupling invariant preserved).
  • v2 (scenes/Portal) untouched except one import-repoint from a GraphQL-doc drain (apps-page doc → scenes/common, R100 rename).
  • No new .graphql/codegen; web/graphql/graphql.ts untouched.
  • The one backend-shaped requirement (auto-team, auto-RP) is done as v3 client orchestration over existing endpoints — no server changes, flag is the kill switch.

Verification

  • npx tsc --noEmit → 0 · npx jest tests/unit/57 suites / 172 tests pass · pnpm prettier clean on all touched files.
  • Whole-branch review (in addition to per-task reviews) caught and fixed one cross-task defect: the onboarding "Start" button wrote a jotai atom that no v3 component read (v3 shell uses AppsDropdown, not v2's atom-reading Header) — the golden path's first click dead-ended. Now wired; a test pins the actual dialog mount.
  • All four branch-touched routes compile and execute server-side (verified via the running dev server; clean redirect to auth, no crashes).

Not yet done — needs a human

  • Interactive authenticated walkthrough. The golden path lives behind allow-listed Auth0 login, which can't be driven headlessly. Recommend one manual pass on a PORTAL_V3_EMAILS account before merge: fresh user → auto-team → onboarding → click Start → create app → save key → dashboard → create action → quickstart → (staging) produce a Simulator proof → strip flips to ✓.

Known follow-ups (not blockers)

  • The v3 WorldId40MigrationBanner is now orphaned (SetupStrip superseded it); its ?enableWorldId4 link is benign self-navigation. Left in place per surgical-change discipline; remove in a later wave.
  • Observability audit (PostHog funnel) was deliberately deferred. The 5 v3_* events are emitted by the components but not yet dashboarded/alerted.
  • Minor test-coverage and polish notes tracked in the branch's review ledger.

Rollout / rollback

Per-user via PORTAL_V3_EMAILS; nothing ships to non-allow-listed users. Rollback = revert the merge; v2 paths are untouched.

Moves the hardened keygen+registerRp sequence (once-per-app signer key,
30s AbortController timeout, already_registered treated as success,
posthog capture with no key leakage) into a standalone hook so the
dashboard setup strip can reuse it for existing-app RP recovery. The
dialog now consumes the hook via a status->step mapping; behavior is
unchanged (8/8 tests in pv3-w1-create-app-auto-rp.test.tsx pass
unmodified).
…A, live first-proof wait

Adds SetupStrip above VerificationStatusSection, replacing
WorldId40MigrationBanner in v3. Renders exactly one of: RP-missing
recovery (reusing use-auto-register-rp, with an admin-only gate and an
inline save-your-key panel), a create-first-action CTA once RP is
registered, a polling live-wait for the first verification once actions
exist, or nothing once real verification traffic shows up. Polling
(via startPolling/stopPolling) is scoped to the waiting state only, and
the first-proof posthog capture fires exactly once per live 0->1
crossing (a fresh mount that arrives already-verified does not
re-trigger it).
…ippet defines

The frontend snippet was posting to /api/v4/verify/${appId} on the developer's own origin, but the backend snippet defines the route at /api/verify-world-id. Developers pasting the two snippets together would get a 404. Changed the frontend fetch URL to match the backend route exactly.
…human"

This reverts commit 6330cf0ff8968a32bce47c5a1ef60ca42f986d92.
Move the apps welcome page's InitialApp query from scenes/Portal into
scenes/common so the v3 fork can share it without importing scenes/Portal.
…copy

Fork the /teams/[teamId]/apps welcome page into PortalV3 with new
verify-your-first-human copy, and flip the route to choose between v2
and v3 via pickPortalVersion, following the phase-1 migration recipe.
…te-team

login-callback sends brand-new users to /create-team before they ever reach
the portal shell, so the shell-mounted AutoTeamBootstrap was unreachable on
the actual first-login path (caught in manual walkthrough). The route now
chooses per user: v3 new users get the auto-created personal team; users who
already have a team (intentionally creating another) keep the manual form;
v2 unchanged.
…, email-aware team names

- deriveTeamName moved out of the 'use client' module (calling it from the
  create-team server page threw a client-reference error; the layout call
  site had the same latent bug, masked by unreachability)
- AutoTeamBootstrap recovers from lost create races (concurrent tab already
  made the team → re-read profile → proceed) instead of showing the error card
- email signups no longer get 'foo@bar.com's team' (name===email falls back
  to the local-part)
…next to the integration snippet

The live 'waiting → ✓ first proof received' indicator lived on the app
dashboard, disconnected from where the developer just did the work — a
context-free banner. Relocate it onto the action page beside the Quickstart:
it polls while the action has zero verifications and flips in place the
instant one lands, right next to the IDKit snippet / simulator link they used.
The dashboard SetupStrip is trimmed to pure setup guidance (RP recovery +
create-first-action); the funnel event moves with the moment (now keyed by
action_id).
The post-create soft navigation could read a stale (team-less) session and
bounce back to /create-team, re-firing the auto-create and colliding on the
duplicate-user constraint — surfacing an error card over an account that was
actually created. Hard-navigate (window.location) so the server sees the fresh
session cookie and no bounce occurs; on any create failure, refresh the session
and poll the profile before giving up, recovering into the existing team rather
than showing the error.
…t + Settings-tab tester

The left-column Verifications count duplicated the Verified-humans count beside
it, and the Settings tab's Try-it-out tester duplicated the QR tester now on the
Overview page — remove both. Settings becomes a single-column identifier/
description form; the Overview page owns the tester.
The action Overview page's Try-it-out tester and the Show-integration-
quickstart dropdown both rendered near-identical IDKit code. Add an
optional kioskOnly prop to TryAction that hides the code view and the
view-toggle when the QR kiosk is available, and pass it from the Overview
page so the quickstart dropdown is the single source of integration code.
The code view still shows as a fallback for app types without a kiosk
(e.g. on-chain), and all other TryAction usages are unchanged (default off).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant