feat(compliance): ToS, privacy, delete-account, export, age-gate, AI disclaimer (#115) - #131
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
There was a problem hiding this comment.
Three clusters of dead i18n keys introduced in this PR — all confirmed by grep with zero callers:
deleteAccount.lastUpdated— added to both locale files but the delete-account page never renders it (the terms page useslastUpdatedas a subtitle; this one doesn't).dataExport.title,dataExport.description,dataExport.success— export UI only references.button,.preparing,.error,.shareTitle.ageGate.confirmLabelandageGate.error— age gate lands entirely in theauth.legalPrefixconsent-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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
…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>
|
There was a problem hiding this comment.
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).


Legal & Compliance umbrella — frontend + shared side of the paired PRs. Ships as DRAFT.
Children in this PR
/terms+ mobileterms.tsx(registered in_layout.tsx); the brokenauth.termslinks flipped from/about→/termson 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.lastUpdatedbumped./delete-accountexplaining 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.UserDataExportZod 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.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).All new strings added to both
en.jsonandpt-BR.json(1595 keys each, parity verified).Validation
npm run type-checkGREEN (shared + web + mobile, stacked on the type-check fix).npm run lintclean (web + mobile).npm test: shared 815, web 1448 (incl. i18n parity), mobile 361 — all pass.Deviations
MINIMUM_AGEconstant 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