Skip to content

fix(onboarding): re-land #893, T&C acceptance on the Details step - #899

Merged
kavin-114 merged 2 commits into
developfrom
fix/893-reland-tnc-details
Aug 17, 2026
Merged

fix(onboarding): re-land #893, T&C acceptance on the Details step#899
kavin-114 merged 2 commits into
developfrom
fix/893-reland-tnc-details

Conversation

@kavin-114

Copy link
Copy Markdown
Contributor

Summary

Re-lands #893, which shows MERGED but never reached develop. Users accept the Terms and Conditions on the Details step instead of the Review and Pay step, exactly as #893 intended.

#893 was stacked on #888 and got merged into #888's feature branch 15 seconds after that branch was itself merged into develop, so its commits stranded on a dead side branch. This PR cherry-picks those same two commits (aa5978b, e0c1ead here; 3eacdce, 1d0f995 here) onto current develop. Clean pick, no conflicts. Carries its own frontend spec and backend validation tests.

How the strand happened

Merge timeline on 2026-08-16: #888 (base develop) merged at 16:50:45 as dc548ef. #893 (base fix/onboarding-contact-required, never retargeted) merged at 16:51:00 as 12fd555, whose commits are not ancestors of develop. GitHub only retargets a stacked PR when the base branch is deleted before the second merge, which did not happen here.

Pre-merge checklist

  • CI is green — the tests check on this PR passes (never merge on ❌)
  • Branch is up to date with main (cut from develop tip 078286c today)
  • New/changed behavior has tests (the coverage gate still passes)
  • I self-reviewed the diff

Move the required Terms & Conditions checkbox from Review & Pay to
Details, gating Details' Continue action instead of the Pay button.
Consent to be contacted (which the T&C acceptance carries per the
2026-08-14 owner decision) is now captured before the Plan-step lead
capture fires, closing the gap where a captured lead had no consent
until Pay. payDisabled and onPayClick's own termsAccepted guard stay
in place as invariants.

The reconnect side-branch inside onDetailsSubmit is exempt: a
returning customer reconnecting an existing paid account never went
through this checkbox before and makes no signup call that needs
terms_accepted/contact_consent, so the gate is checked only after the
reconnect branch has already returned.

Also adds light server-side email validation: start_signup rejects a
malformed email with a clean message before touching admin, and
capture_onboarding_lead (fire-and-forget) silently drops a malformed
non-blank email instead of forwarding it, without ever raising.

Rewrites the OnboardingView.spec.js "lead-capture + T&C (frozen
contract)" block for the new placement, and adds a regression test
proving the reconnect branch is not gated by termsAccepted.
Two leftover references to the checkbox's old Review & Pay location,
found by code review of #893: the getTermsUrl mount comment, and a
note on onDetailsSubmit's field-validation block explaining why terms
is checked separately from the other fields (after the reconnect
branch, not batched with them) rather than restructuring the gate.
@kavin-114

Copy link
Copy Markdown
Contributor Author

Code review

Found 3 issues (all verified against the diff; note these were present in the original #893 code, this re-land inherits them):

  1. Resume paths can land directly on the Pay step with termsAccepted reset to false, and the T&C checkbox no longer exists there: payDisabled then permanently disables Pay with no on-screen control to unblock it (reload on Review, or checkout-return, triggers it).

// answer proves an intent exists (Plan 01), so later billing edits route
// through the authenticated update_billing facade, never a fresh guest signup.
state.intentExists = true;

  1. start_signup's email guard uses validate_email_address, which parses address LISTS and is truthy if any one embedded address is valid, and the code then forwards the ORIGINAL string: "not-an-email, real@x.com" passes and is persisted as the account identity.

jarvis/jarvis/onboarding.py

Lines 1007 to 1009 in 1d0f995

require_jarvis_admin()
# Light shape check on the caller-typed email, before anything else touches
# it (onboarding_contract.update below, admin_client.signup) - a malformed

  1. capture_onboarding_lead now silently drops any lead whose email fails local validation instead of forwarding it to admin as before, so the attempt leaves no record anywhere (fire-and-forget, low blast radius).

jarvis/jarvis/onboarding.py

Lines 360 to 362 in 1d0f995

just never becomes a lead instead of surfacing anywhere. A blank/missing
email is left to admin_client as before (unchanged from prior behaviour).
"""

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@kavin-114
kavin-114 merged commit b42646e into develop Aug 17, 2026
8 checks passed
@kavin-114

Copy link
Copy Markdown
Contributor Author

All 3 review findings addressed in 8e4e42a (email validation + lead forwarding) and f13ff58 (T&C fallback checkbox on Pay for resumed sessions, stale comments fixed). Frontend suite green locally (1332 tests, Node 24); backend tests on CI.

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