Store readiness: in-app account deletion, privacy policy, terms, listing copy

Closes the remaining store-compliance gaps before App Store / Play
submission:

- Account deletion (App Store guideline 5.1.1): DELETE /me removes the
  user row (usage cascades). The app exposes it through one quiet
  'Account' caption link under the chat, visible only when signed in,
  driving two native dialogs (Sign out / Delete account with a
  destructive confirm) — no new views, no menus, minimalism intact.
  Deletion signs out and resets the app; copy notes that store
  subscriptions are cancelled in App Store / Play settings.
- docs/store/privacy-policy.md: the complete data inventory (matching
  the actual schema), transient OpenAI processing with no training, no
  profiling or ads, GDPR legal bases and rights, in-app erasure.
- docs/store/terms-of-service.md: not-therapy positioning with crisis
  guidance, AI-generated-content caveat, 18+ eligibility,
  auto-renewal/cancellation terms, liability, Swedish governing law.
- docs/store/listing.md: App Store and Play copy written to the
  honest-claims rule (subtitle 'Think Beyond Thought', keywords,
  descriptions), plus App Privacy and Data safety questionnaire
  mappings.
- LAUNCH.md updated: host the policy/terms, set store URLs, use the
  prepared listing copy.
- Tests: 30 passing (adds DELETE /me coverage).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z
This commit is contained in:
Claude
2026-08-03 22:37:25 +00:00
parent bf5c4e5f00
commit e5b81f46b0
11 changed files with 314 additions and 8 deletions
+11 -1
View File
@@ -1,6 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('../src/db.js', () => ({
deleteUser: vi.fn(),
getOrCreateUser: vi.fn(),
getUsage: vi.fn(),
incrementUsage: vi.fn(),
@@ -14,7 +15,7 @@ vi.mock('../src/subscription/index.js', () => ({
}));
import { generateReply } from '../src/chat.js';
import { getOrCreateUser, getUsage, incrementUsage } from '../src/db.js';
import { deleteUser, getOrCreateUser, getUsage, incrementUsage } from '../src/db.js';
import { handler } from '../src/handler.js';
import { verifyAndApplyPurchase } from '../src/subscription/index.js';
@@ -101,6 +102,15 @@ describe('authorization', () => {
});
expect(getOrCreateUser).toHaveBeenCalledWith('sub-1', 'a@b.se', 'apple');
});
it('deletes the account on DELETE /me', async () => {
const { status, body } = await call('DELETE', '/me', {
claims: { sub: 'sub-1', email: 'a@b.se' },
});
expect(status).toBe(200);
expect(body.deleted).toBe(true);
expect(deleteUser).toHaveBeenCalledWith('user-1');
});
});
describe('chat and the intelligent paywall', () => {