Skip to content

feat(compliance): ToS, privacy, delete-account, export, age-gate, AI disclaimer (#115) - #131

Merged
thomasluizon merged 8 commits into
mainfrom
issue-115
Jun 5, 2026
Merged

feat(compliance): ToS, privacy, delete-account, export, age-gate, AI disclaimer (#115)#131
thomasluizon merged 8 commits into
mainfrom
issue-115

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Legal & Compliance umbrella — frontend + shared side of the paired PRs. Ships as DRAFT.

⚠️ DRAFT — AI-authored legal copy (ToS / privacy / age-gate / medical disclaimer) NEEDS LEGAL REVIEW by a Brazilian data-protection lawyer before un-drafting. Copy uses [LEGAL ENTITY NAME] LTDA + [CNPJ NUMBER] placeholders pending the compliance-ops step.

Children in this PR

  • Terms of Service — new web /terms + mobile terms.tsx (registered in _layout.tsx); the broken auth.terms links flipped from /about/terms on both platforms. Covers provider/LTDA, 13+ eligibility, license/acceptable-use, subscription + auto-renewal + cancellation + refunds, AI terms, not-medical-advice, warranty/liability, termination, governing law = Brazil, changes, contact.
  • Privacy hardening — extended the existing web + mobile privacy policy: data controller (LTDA+CNPJ), retention periods, Google OAuth scopes + calendar data, data residency / international-transfer basis, AI/automated-processing section, 13+ minors clause, right-to-export; lastUpdated bumped.
  • Public delete-account page — no-login web /delete-account explaining the in-app flow + email fallback + 7-day grace; linked from web Profile. (Mobile deletion already works in-app.) Public URL for the Play Data Safety form: https://app.useorbit.org/delete-account.
  • Data export — shared UserDataExport Zod type (shape matches the API response) + API.profile.export + web Server Action + mobile apiClient (writes JSON via expo-file-system, shares the file). 'Download my data' entry in web + mobile Profile.
  • Age gate (13+) — shared MINIMUM_AGE = 13; the signup consent line on both platforms now affirms the user is at least 13 (passwordless flow has no separate signup step, so the gate lives in the existing 'by continuing you agree' consent — see Deviations).
  • AI transparency + medical disclaimer — 'AI' label next to Astra + 'not medical/professional advice' disclaimer near AI output in chat, today summary, and retrospective (both platforms).

All new strings added to both en.json and pt-BR.json (1595 keys each, parity verified).

Validation

  • npm run type-check GREEN (shared + web + mobile, stacked on the type-check fix).
  • npm run lint clean (web + mobile).
  • npm test: shared 815, web 1448 (incl. i18n parity), mobile 361 — all pass.
  • Manual E2E pending (no browser E2E run here).

Deviations

  • Age gate is implemented as a 13+ affirmation in the existing passwordless consent line (no separate birthdate/checkbox), because the email-code flow has no distinct signup step and a hard checkbox would force the affirmation on every sign-in. Backed by the shared MINIMUM_AGE constant and consistent with the 13+ clause in ToS/Privacy.

Closes #115
Stacked on #128 (mobile type-check fix).

Paired API PR: thomasluizon/orbit-api#181

🤖 Generated with Claude Code

@thomasluizon thomasluizon added the needs-legal-review AI-authored legal copy pending lawyer review before merge label Jun 4, 2026
@vercel

vercel Bot commented Jun 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Jun 5, 2026 12:12am

Comment thread packages/shared/src/i18n/en.json Outdated
Comment thread packages/shared/src/i18n/en.json Outdated
Comment thread packages/shared/src/i18n/en.json Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three clusters of dead i18n keys introduced in this PR — all confirmed by grep with zero callers:

  1. deleteAccount.lastUpdated — added to both locale files but the delete-account page never renders it (the terms page uses lastUpdated as a subtitle; this one doesn't).
  2. dataExport.title, dataExport.description, dataExport.success — export UI only references .button, .preparing, .error, .shareTitle.
  3. ageGate.confirmLabel and ageGate.error — age gate lands entirely in the auth.legalPrefix consent-line change; no checkbox or validation message component exists.

Remove all six keys from both en.json and pt-BR.json. Everything else in the PR — the Terms/Privacy expansions, data export (web + mobile), delete-account public page, AI disclosure badges, cross-platform parity, and i18n parity between the two locales — is solid.

Comment thread packages/shared/src/validation/constants.ts Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One dead export to clean up before this merges.

MINIMUM_AGE (packages/shared/src/validation/constants.ts:17) — exported from @orbit/shared with zero runtime callers. The age affirmation lives in the i18n strings as a hardcoded literal; the constant doesn't drive or enforce any code path. Remove the constant, its re-export in validation/index.ts, and the value-only test case.

Everything else is solid: the prior dead i18n keys (deleteAccount.lastUpdated, the three dataExport keys, ageGate keys) are confirmed removed in the follow-up commit. Terms/Privacy expansions, data export (web Server Action + mobile expo-file-system share), delete-account public page, AI disclosure badges, and cross-platform parity all look correct. i18n parity between en.json and pt-BR.json is maintained at 1595 keys each.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both prior findings fully addressed: the dead i18n key cluster (deleteAccount.lastUpdated, three dataExport keys, two ageGate keys) was removed in e32b6c2, and the unused MINIMUM_AGE export was cleaned up in 37f5b7c. The PR is now clean.

What shipped: ToS screen on web + mobile, privacy policy hardening (data controller, retention, Google scopes, data residency, automated-processing, minors, export), public /delete-account page linked from web Profile, data export via Server Action (web blob download) + expo-file-system share (mobile), AI disclosure badge and medical-advice disclaimer in chat, today-summary, and retrospective on both platforms. All new strings in both en.json and pt-BR.json at parity. Zod schema and shared UserDataExport type live in packages/shared. Cross-platform parity is maintained across all features.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All prior findings (dead i18n keys, unused MINIMUM_AGE export) were addressed in earlier commits and confirmed clean. The final commit (cf565fa) is a pure text substitution — [LEGAL ENTITY NAME]TL SOFTWARE ENGINEERING LTDA in both locale files — with no logic changes. The [CNPJ NUMBER] placeholder remaining is intentional per the PR description. No new high-signal issues. Code is solid: Zod schema in shared, Server Action on web, expo-file-system share on mobile, AI disclosure badges and medical-advice disclaimer across all AI surfaces on both platforms, full i18n parity at 1595 keys each.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two new commits since last approval are clean placeholder fill-ins: [LEGAL ENTITY NAME] LTDA (CNPJ [CNPJ NUMBER])TL SOFTWARE ENGINEERING LTDA (CNPJ 58.429.979/0001-06) in both privacy.controller.body and terms.provider.body, applied symmetrically to en.json and pt-BR.json. No logic change, both locales consistent. The full PR (ToS screen web+mobile, privacy hardening, public /delete-account, data export via Server Action + expo-file-system share, AI disclosure badges + medical-disclaimer, age-gate consent line, shared UserDataExport Zod type) was verified clean in the prior pass. Ship it.

@thomasluizon
thomasluizon marked this pull request as ready for review June 4, 2026 20:46

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compliance umbrella lands cleanly. ToS, privacy hardening, public delete-account page, data export, age-gate affirmation, and AI disclaimers are all present on both platforms with full i18n parity (en + pt-BR). No any, no console.log, all user-facing strings routed through i18n, userDataExportSchema is Zod-typed and covered by a dedicated test. The web export (Blob + anchor download via Server Action) and mobile export (expo-file-system File + Share) are platform-appropriate adapters. The login Terms link fix (/about → /terms) is correctly applied on both web email-step.tsx and mobile login.tsx. The public /delete-account page being web-only is intentional — it provides the Play Data Safety form URL while mobile users already have the full deletion flow in-app.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final commit (dc996ae) is a clean parity fix — removes the redundant /delete-account navigation button that was added to the web Profile but absent on mobile (users are served by the existing in-app DeleteAccountModal). Nothing new to flag.

Full PR ships correctly: ToS screen (web + mobile), privacy hardening, public /delete-account page, data export via Server Action (web Blob download) + expo-file-system File + Share (mobile), AI disclosure badge and medical-advice disclaimer across all AI surfaces on both platforms, age-gate affirmation in the consent line (both platforms). Shared userDataExportSchema is Zod-typed and covered by a dedicated test. All user-facing strings routed through i18n with full en/pt-BR parity at 1595 keys each. No any, no console.log, 'use client' is justified on the new public pages (auth store + navigation hooks). Cross-platform parity is maintained across every feature in this PR.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid compliance umbrella. The implementation is consistent and thorough across all six deliverables.

What landed: ToS page (web + mobile), privacy policy hardening with LGPD-specific sections (controller, retention, Google scopes, data residency, automated processing, minors, export), public /delete-account page with Play-required no-login URL, data export flow (shared Zod type + endpoint constant + web Server Action + mobile expo-file-system write + Share), 13+ age affirmation in the passwordless consent line on both platforms, and AI label + medical disclaimer in chat/today summary/retrospective on both platforms.

Checks: No any, no console.log, i18n parity confirmed (136 keys added to both locales). The expo-file-system v2 File.create()/File.write() calls are synchronous so the Share call correctly follows a fully-written file. Public routes (/terms, /delete-account) properly added to the proxy allowlist with a test covering all three legal pages. 'use client' on the public pages is consistent with the existing privacy page pattern. The emptyState parent has alignItems: 'center' so the chat disclaimer's maxWidth is correctly centered. Tests added for the shared userDataExportSchema.

Base automatically changed from chore/fix-mobile-type-check to main June 5, 2026 00:05
thomasluizon and others added 8 commits June 4, 2026 21:10
…disclaimer (#115)

Legal & compliance umbrella (frontend + shared). Children:
- ToS: new web /terms + mobile terms.tsx (registered), auth.terms links flipped
  from /about to /terms on both platforms.
- Privacy hardening: controller (LTDA+CNPJ placeholders), retention, Google
  scopes + calendar data, data residency/transfer basis, AI/automated-processing,
  13+ minors clause, right-to-export; rendered on existing web + mobile screens.
- Public delete-account page: no-login web /delete-account explaining in-app +
  email fallback + 7-day grace; linked from web Profile.
- Data export: shared UserDataExport Zod type + API.profile.export + web Server
  Action + mobile apiClient (writes JSON, shares file); 'Download my data' in
  web + mobile Profile.
- Age gate (13+): shared MINIMUM_AGE constant; signup consent line on both
  platforms now affirms 13+.
- AI disclosure: 'AI' label on Astra + 'not medical/professional advice'
  disclaimer near AI output in chat, today summary, and retrospective (both
  platforms).

All new strings in en.json AND pt-BR.json (1595 keys each, parity verified).

Closes #115

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…TDA (#115)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
login.tsx pushed to an in-app /terms route that does not exist on mobile,
breaking the Terms link on the login screen and failing type-check. Match
openPrivacyPolicy and open the hosted page via Linking.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The profile already has the working in-app delete flow (opens
DeleteAccountModal). #115 added a second button that only navigated to the
public /delete-account info page, redundant for a logged-in user and absent
on mobile. Removed it so web matches mobile; the public page stays as the
Play-required no-login deletion URL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
proxy.ts PUBLIC_PATHS only allowlisted /privacy, so unauthenticated hits to
/terms (the login consent link) and /delete-account redirected to /login.
The latter also broke the Play-required no-login account-deletion URL. Added
both paths and broadened the proxy test to cover all three public legal pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thomasluizon
thomasluizon merged commit b3e9b72 into main Jun 5, 2026
6 checks passed
@thomasluizon
thomasluizon deleted the issue-115 branch June 5, 2026 00:12
@sonarqubecloud

sonarqubecloud Bot commented Jun 5, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
20.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid compliance umbrella. Here's what I checked:

Data export — shared UserDataExport Zod type with tests, API.profile.export constant, web Server Action, mobile expo-file-system write + Share, and the export button wired identically on both platforms. The web anchor-click download pattern and mobile synchronous file write are both correct.

Legal pages — terms and privacy land on both platforms with identical structure. Mobile uses useGoBackOrFallback + useAuthStore for the back action (same as the existing privacy screen). The 'use client' directives on the web public pages are justified by the router hooks.

Public route allowlist/terms and /delete-account added to PUBLIC_PATHS in proxy.ts, proxy test updated to cover all three legal paths.

AI disclosure — "AI" badge and "not medical advice" disclaimer placed consistently on the today summary, chat empty state, and retrospective on both platforms. showDisclaimer guard condition is identical in web and mobile.

i18n — 1595 keys in both en.json and pt-BR.json; common.backToProfile and common.goBack keys pre-exist and are used correctly. No hardcoded user-facing strings in the diff.

Cross-platform parity — all six feature areas (ToS, privacy hardening, delete-account, data export, age-gate, AI disclaimer) are present on both apps/web and apps/mobile, with acceptable platform-only differences (web uses Blob download, mobile uses expo-file-system + Share; delete-account is a public informational web page only since mobile deletion is already in-app).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-legal-review AI-authored legal copy pending lawyer review before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Legal & Compliance Hardening: privacy policy, ToS, AI disclosures, data rights (umbrella)

1 participant