561 Commits

Author SHA1 Message Date
chahinebrini
c39dfcbf8b fix(migration): screentime_passcode in korrektes Prisma-Migration-Format
Loose .sql Datei → 20260601_add_screentime_passcode/migration.sql
(prisma migrate deploy erwartet Verzeichnis-Format)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:34:34 +02:00
chahinebrini
d65ba84eb1 feat(binder): MDMClient, EnrollView improvements + supervise flow_backup
- MDMClient: error handling verbessert
- SuperviseRunner: robustere EOF-nach-Success Erkennung
- EnrollView: Enrollment-Status-Polling, Retry-Logik
- SuperviseView: UX-Verbesserungen
- ConfigureView: minor cleanup
- flow_backup.go: backup flow für supervise-magic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:28 +02:00
chahinebrini
db0aa6d24e feat(native): Protection Onboarding v2 + Devices + ProtectionSlide
- ProtectionOnboardingSheet: Android a11y 2-step flow mit tamper-arm nach Return
- ProtectionDetailsSheet: cleanup, iOS/Android split, locked-state logic
- ProtectionSlide: neuer Onboarding-Slide für Protection-Intro
- _layout.tsx: reconcileVpn on app-foreground (Android VPN self-heal)
- devices.tsx: Two-device approval flow
- useProtectionState: applyCooldownDisableIfElapsed, forceDisable on cooldown-end
- iOS module Info.plist: bundle version bumps
- app.config.ts: minor config updates
- tmp/.deploy-runtimes: build-time metrics aktualisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:20 +02:00
chahinebrini
adf0d33f1b feat(android): Protection Module v2 — VPN self-heal, boot-receiver, multilang
- RebreakProtectionModule: reconcileVpn, Samsung overlay guide, openPowerDialog,
  armTamperLock preconditions (VPN+a11y required), openAccessibilitySettings fallback chain
- RebreakVpnService: onRevoke auto-recover, blocklist self-heal on empty start,
  ACTION_RESTART für hard-reload nach blocklist-Änderung
- VpnBootReceiver (neu): startet VPN nach Geräte-Neustart wenn filter_enabled=true
- Strings: DE/EN/FR/AR a11y-Guide, Overlay-Permission, Hint-Steps
- RebreakProtectionModule.ts: reconcileVpn, armTamperLock, disarmTamperLock,
  openPowerDialog, openAccessibilitySettings, dismissAccessibilityHint exports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:30:07 +02:00
chahinebrini
ab4b9c48e5 feat(ios): Screen Time Passcode als Layer 3 (setup flow)
User generiert 4-stelligen Code in der App, setzt ihn manuell als
Screen Time Passcode → ReBreak speichert ihn auf dem Backend.
Damit kann niemand Screen Time deaktivieren → deny-removal bleibt
aktiv → App nicht deinstallierbar ohne den Passcode.

Backend:
- Profile.screentimePasscode Feld (Migration add_screentime_passcode)
- POST /api/protection/screentime-passcode — Code speichern
- GET /api/protection/screentime-passcode — Code abrufen (nach Cooldown)

iOS UI (blocker.tsx):
- ScreentimePasscodeCard erscheint wenn Layer 1 + 2 aktiv (iOS only)
- Code-Generierung → Einmal-Anzeige → Deep-Link zu Settings → Screen Time
- Bestätigung speichert Code auf Backend, Card zeigt Confirmed-State

Locales: DE/EN/FR/AR screentime_* Keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:19:43 +02:00
chahinebrini
59766f8530 fix(android): block Force Stop + App-Info auf Samsung OneUI
Samsung nutzt generische Klassen (SubSettings, FrameLayout) statt
InstalledAppDetails für die App-Info-Seite — classGuard schlug fehl,
Tamper-Lock griff nicht.

Fixes:
- classGuard für Uninstall-Pfad entfernt: Text-Kombination "rebreak"
  + Deinstall-Keyword reicht (App-Liste zeigt Buttons nie inline)
- Force-Stop-Bestätigungsdialog explizit erkannt via "stopp erzwingen"
  + "abbrechen" + "ok" (Dialog nennt App-Namen nie)
- Throttle-Reset bei TYPE_WINDOW_STATE_CHANGED: eliminiert 400ms-Fenster
  zwischen Activity-Wechsel und erstem CONTENT_CHANGED-Check
- ApplicationDetail (ohne 's') zu Pattern-Listen: Samsung OEM-Variante
- accessibility_service_summary korrigiert: "ReBreak — Schutz" statt
  "Sichert den Schutz gegen Abschalten ab" (HIGH_CONFIDENCE_KEYWORD muss matchen)

Getestet auf Samsung Galaxy A50 (One UI): App-Info-Seite wird sofort
per BACK-Action geblockt, Lyra-Toast erscheint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:11:01 +02:00
chahinebrini
2e056c7257 feat(devices): Apple-style two-device approval flow + email fallback
iCloud-Sign-In Pattern: wenn ein neues Gerät versucht sich anzumelden
und das Plan-Limit erreicht ist, kann der User auf einem bereits
angemeldeten Gerät bestätigen — Code wird auf BEIDEN Geräten gezeigt
für visuellen Vergleich (verhindert Code-Forwarding-Attacken).

Backend:
- New table device_approval_requests + supabase_realtime + RLS
- POST   /api/devices/approvals               — create (new device)
- GET    /api/devices/approvals               — list pending (existing devices)
- GET    /api/devices/approvals/:id           — status poll (new device)
- POST   /api/devices/approvals/:id/approve   — approve + atomic evict
- POST   /api/devices/approvals/:id/reject    — reject
- POST   /api/devices/approvals/:id/email     — trigger email fallback
- POST   /api/devices/approvals/email/:token  — magic-link approve (no auth)
- Email-Template via Resend (lyra-neutral, security-formal)
- 10min TTL, 6-digit numeric codes (crypto-random)

Frontend (rebreak-native):
- DeviceApprovalIncomingSheet — existing devices: code + device-picker + Allow/Reject
- DeviceApprovalPendingSheet  — new device: code + spinner + 'Send via email'
- useDeviceApprovalRealtime   — postgres_changes subscription
- DeviceLimitReachedSheet     — neues CTA 'Auf anderem Gerät bestätigen'
- i18n DE/EN/FR/AR

Migration läuft automatisch via prisma migrate deploy bei push.
2026-06-01 02:36:28 +02:00
chahinebrini
efca157969 fix(backend): device-mgmt cleanup + stats rejected fallback + realtime refresh
- devices: cleanupStaleDevices() purges phantoms >14d not bound; called from
  GET /api/devices + register limit-check. Deterministic sort
  (lastSeenAt, createdAt, id) so iPad+iPhone see identical order.
  Merge-cutoff tightened 30d -> 7d.
- stats: rejected aggregates from notifications(type='domain_rejected')
  via Math.max — admin reject cascade-deletes submission row.
- blocker.tsx: useDomainSubmissionRealtime triggers blockerStats.refresh()
  directly (not stale-check only) so info-sheet shows fresh rejected count.
2026-06-01 02:23:27 +02:00
chahinebrini
578abfe3bb chore(release): v0.3.13 build 46 / vc36 — DM scroll fix + chat timestamps weekday/days/weeks/months 2026-05-31 07:33:06 +02:00
chahinebrini
2715d2620b chore(release): ios buildNumber 44 → 45 (TestFlight + MDM deployed) 2026-05-31 03:42:23 +02:00
chahinebrini
acf14aaf11 chore(release): rebreak-native v0.3.13 build 44 (versionCode 35)
- Bump android versionCode 33 → 35 (build.gradle was out-of-sync with app.config.ts)
- Archive NEXT_RELEASE.md → CHANGELOG.md
- Released to Google Play Internal Track

Release notes (Build 44):
- DM-Chat: scrollt jetzt zuverlässig zur neuesten Nachricht
- Lyra-Sprachnachrichten: arabisch/türkisch antworten in richtiger Sprache (STT fix)
- DM Push-Notifications: Fehler-Logging hinzugefügt
2026-05-31 02:03:00 +02:00
chahinebrini
49e558902b fix(dm): always scroll to bottom on new messages and content-size changes
The smart isNearBottomRef gating was too restrictive — own sent messages,
image-loads, and incoming partner messages were sometimes not scrolled to.
Adopt the room-chat pattern: always scroll on messages.length change and
onContentSizeChange. Drop isNearBottomRef + firstContentSizeChangeRef +
onScroll handler.
2026-05-31 01:37:44 +02:00
chahinebrini
55e3cdfb26 fix(transcribe): pass language=ar/tr to nova-2-general so Lyra answers in correct language
Without explicit language param, nova-2-general falls back to multilingual
auto-detect and often misdetects arabic audio as english (phonetic transcript
'salam alaikum' instead of 'السلام عليكم'). detectLang() then sees only
latin chars and answers in english.

Confirmed via Deepgram docs: nova-2-general accepts language=ar and language=tr
(only nova-3 rejects them with HTTP 400).
2026-05-31 01:37:11 +02:00
chahinebrini
e0b4d9f530 fix(dm): add error handling and logging to DM push notifications 2026-05-31 01:32:34 +02:00
chahinebrini
3a4e1ecfba feat(coach): switch Lyra to Gemini 2.5 Flash Lite (Groq+OpenRouter quotas dead)
- Primary: gemini-2.5-flash-lite (~789ms TTFR, ~10x cheaper than Haiku, no reasoning overhead)
- Fallback 1: gemini-2.5-flash (smarter when Lite overloaded)
- Fallback 2: gpt-4o-mini (anchor on different provider)
- message.post.ts: candidates chain replaced
- sos-stream.get.ts: gemini-flash-lite default + auto-fallback to gpt-4o-mini if key missing
- nitro.config.ts: geminiApiKey runtimeConfig
- start-staging.sh: GEMINI_API_KEY export + NITRO_GEMINI_API_KEY

OpenRouter credits = 0, Groq TPD exhausted - users get 503 currently.
2026-05-31 01:07:10 +02:00
chahinebrini
487af4ede1 fix(coach): duplicate lastUserMsg declaration in message.post 2026-05-31 00:15:28 +02:00
chahinebrini
685782b538 fix(coach): dynamische Sprache (Text-Detection + App-Locale-Fallback)
LLM-Prompt (message.post + sos-stream):
- LANG_INSTRUCTIONS Map raus, ersetzt durch dynamische Instruktion
  'Reply in {detectedFromUser} ... fallback: {appLang}'
- Lyra matcht jetzt die Sprache der letzten User-Message (per
  detectLang Unicode-Detection); App-Locale ist nur noch Fallback
- Instruktion doppelt eingehängt (Anfang + Ende des System-Prompts)
  gegen recency bias bei langen deutschen Prompts

TTS (speak dispatcher + speak-cartesia + speak-elevenlabs):
- Kein 'de'-Default mehr für language. detectLang(text, locale) leitet
  Sprache primär aus dem Antwort-Text ab (Arabic/Cyrillic/CJK/Turkish-
  Letters), Locale als Fallback
- Cartesia + ElevenLabs: language/language_code nur senden wenn
  ableitbar, sonst Provider auto-detect statt erzwungenem 'de'
- speak-cartesia: sonic-2 → sonic-3 (Multi-Lang, war beim Dispatcher-
  Fix gestern vergessen worden)
- Google: en-US neutraler Fallback statt de-DE-Bias

Neu: server/utils/detect-lang.ts
2026-05-31 00:12:40 +02:00
chahinebrini
b956b3b1fc chore(ui): branded splash.png statt weißer icon.png-Box + Release-Notes
- app.config.ts splash: icon.png → splash.png (full-bleed Navy-Gradient, cover)
- NEXT_RELEASE.md: Blur-Menüs + neuer Splash ergänzt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:52:58 +02:00
chahinebrini
89775838bc fix(chat): gedrückte Bubble scharf über Blur + Emoji-Ring entfernt
- MessageActionMenu: scharfe Preview-Kopie der gedrückten Bubble am Anker
  (bleibt über dem Blur sichtbar, WhatsApp-Stil) statt mitgeblurrt
- Reaktions-Leiste: kein Ring/Hintergrund mehr, aktives Emoji nur leicht größer
- Reaction-Pills: plain Emoji + Count ohne Hintergrund/Border

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:44:41 +02:00
chahinebrini
f83a13ba60 chore(release): deploy.sh all-platform (MDM/TF/Android) + NEXT_RELEASE-Workflow
- deploy.sh: Android-Signing-Auto-Restore aus build-config/android-signing/
  (gitignored), getestet für MDM + TF + Android
- .gitignore: *.keystore + build-config/android-signing/
- NEXT_RELEASE.md wiederhergestellt mit Notes fürs WA-Chat-Popup + Comment-Likes
  (deploy.sh archiviert es in CHANGELOG + leert es danach)
- buildNumber 27→41, versionCode 18→31 (Release-State)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:34:57 +02:00
chahinebrini
2591b2a89c feat(chat): WhatsApp-Style Reaktions-/Aktions-Popup (DM) + Reaction-Pills
- MessageActionMenu: an der Bubble verankert (measureInWindow) statt zentriert,
  Blur-Backdrop, Emoji-Leiste oben (fremd) + Aktions-Liste unten (fremd:
  Antworten/Kopieren, eigen: Kopieren/Löschen)
- ChatBubble: Long-Press → measure + Menu, Reaction-Pills unter Bubble,
  Tombstone "Nachricht gelöscht"; ersetzt @expo-action-sheet
- dm.tsx: optimistisches Reaction-Toggle + Delete-Confirm + Realtime-Refetch
  (Reaction-Changes + Partner-Soft-Delete)
- useChatRealtime: DM-Hook lauscht zusätzlich auf reactions + message-UPDATE
- PostCommentsSheet: optimistisches Herz + Realtime-Subscription + größeres Icon
- i18n (de/en/fr/ar): chat.delete/message_deleted/delete_confirm_* + public_domain

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:18:51 +02:00
chahinebrini
69f01c5a0c feat(chat): DM-Reaktionen + Soft-Delete Backend + comment_likes realtime
- db/chat: getDmHistory liefert reactions + deletedAt; toggleDmReaction
  (WA-Toggle) + softDeleteDmMessage (nur eigene)
- Endpoints: /api/chat/reaction (Emoji-Toggle, 7er-Allowlist) +
  /api/chat/delete-message (Soft-Delete für alle)
- dm/[userId].get: aggregierte reactions + deleted im Response, Inhalt bei
  Tombstone geblankt
- Migration: comment_likes zur supabase_realtime-Publication (fixt
  Post-Kommentar-Like-Realtime, eskaliert von rebreak-native-ui)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 11:18:32 +02:00
chahinebrini
28887cfc49 feat(chat): schema + migration — Multi-Emoji-Reaktionen + Soft-Delete
- ChatMessageReaction + DirectMessageReaction (PK user+message, emoji-Spalte,
  1 Reaktion/User/Message = WhatsApp-Verhalten)
- deleted_at (Tombstone) auf chat_messages + direct_messages
- Realtime-Publication für beide Reaction-Tabellen
NICHT gepusht: Push triggert Auto-Migration auf Staging-DB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 10:56:39 +02:00
chahinebrini
4135e388ff ui(deploy): reorder progress bar so %, time stay visible on narrow terms
Old layout truncated the critical right-side (1m23s/~3m) on long labels.
New layout puts %, bar, and elapsed/eta on the LEFT, label+subtitle on
the RIGHT where truncation does no harm. Bar shrunk 20→15 chars.
2026-05-30 10:25:24 +02:00
chahinebrini
8f871611f1 feat(deploy): round ETA display (~8m statt ~8m00s)
Baseline aus tmp/.deploy-runtimes wird weiterhin per Run aktualisiert
(genauer Sekunden-Wert im Cache, nur Display gerundet).
2026-05-30 10:21:51 +02:00
chahinebrini
adc506291a fix(deploy): ERR-trap survives set -u (FUNCNAME may be unbound at top-level) 2026-05-30 10:16:11 +02:00
chahinebrini
6255006cad feat(deploy): clean Ctrl+C handler kills background xcodebuild
Without this, Ctrl+C would kill deploy.sh but leave xcodebuild
running as orphan, eating CPU and locking DerivedData.
2026-05-30 10:14:01 +02:00
chahinebrini
24a52a5bae fix(deploy): run_quiet works on bash 3.2 — drop subshell, toggle set -e around wait
- Background subshell '( cmd ) &' + 'wait' interacted badly with
  set -euo pipefail on macOS bash 3.2, killing the script silently
  before the progress-bar's error branch could run
- New approach: just 'cmd &' (no subshell), bracket the whole bg+wait
  region with 'set +e' / 'set -e', then check rc explicitly
- Also adds ERR-trap with call stack + RUN_QUIET_DEBUG=1 fallback
  (streams output directly via tee, useful for debugging build failures)
2026-05-30 10:12:10 +02:00
chahinebrini
9f8e99d287 fix(deploy): preserve rc under set -e (run_quiet swallowed errors silently)
'wait $pid' triggered 'set -e' before 'local rc=$?' could capture exit code,
so xcodebuild failures vanished without any log dump. Use 'wait $pid && rc=0 || rc=$?'
idiom to keep the script alive and let the error-dump branch run.
2026-05-30 10:05:10 +02:00
chahinebrini
061bd2d799 fix(deploy): allow flags before subcommand (./deploy.sh --skip-pods)
Treat $1 as subcommand only if it doesn't start with '-', else default to 'all'.
2026-05-30 10:03:11 +02:00
chahinebrini
77407f9d63 feat(deploy): --skip-pods flag + quiet clean-ios
- clean-ios.sh: new --skip-pods (or SKIP_PODS=1) skips prebuild+pod install
  (use when Pods are already fresh — saves ~30s)
- clean-ios.sh: new --quiet (or REBREAK_QUIET=1) suppresses end-of-run
  'Nächste Schritte' tips (cluttered deploy output)
- deploy.sh: new --skip-pods flag, auto-passes --quiet to clean-ios.sh
2026-05-30 10:01:41 +02:00
chahinebrini
b15ee42a85 fix(deploy): subtitle fallback + realistic runtime seeds
- subtitle now falls back to last non-empty log line when no Compile/Build
  action matched yet (so user sees activity during xcodebuild setup phase
  instead of empty bar at 0%)
- realistic seeds: 22min → 8min for xcarchive (typical RN archive)
2026-05-30 09:55:53 +02:00
chahinebrini
e6e1bab35a fix(deploy): single-line progress bar (no fragile cursor moves)
- previous 2-line render with \033[1A broke when xcodebuild crashed
  early — left mangled output and lost stderr
- new render_progress: ONE line, truncated to $COLUMNS, no cursor moves
- indeterminate mode (no baseline) shows ping-pong bar instead of spinner
- removes 2-line reserve + clear logic in run_quiet
2026-05-30 09:51:49 +02:00
chahinebrini
32d270ccad feat(deploy): brew-style time-based progress bar with runtime-cache
- new render_progress() draws ████████░░░░░░░░ 42% (1m23s / ~3m18s) bar
- runtime_lookup/save persist step durations in tmp/.deploy-runtimes
  (gitignored — auto-learns from successful runs)
- first run = spinner mode (no baseline yet), subsequent runs show real %
- still shows live xcodebuild action (Compiling X.swift) as subtitle
- format_duration helper: 45s / 1m23s readable output
2026-05-30 09:47:48 +02:00
chahinebrini
f48df2a968 chore(deploy): require ASC API-Key, drop app-specific-password fallback, brew-style spinner with live build action
- removes APPLE_APP_SPECIFIC_PASSWORD legacy branches (it never worked for xcodebuild -exportArchive anyway, only altool-upload)
- ASC API-Key now hard-required via require_asc_api_key preflight (fails fast with clear msg + path hint)
- run_quiet: spinner now tails the build log and shows current action (Compiling X.swift, Linking, CodeSign, etc.) as live subtitle — feels like brew/homebrew progress
- .env.deploy.local.example: drop unused fallback section
2026-05-30 09:46:38 +02:00
chahinebrini
b029c00413 chore(deploy): persist iOS auth via .env.deploy.local + ASC API-Key
- deploy.sh auto-sources apps/rebreak-native/.env.deploy.local (gitignored)
  and ~/.config/rebreak/deploy.env as fallback
- new helper xcodebuild_auth_args() injects -allowProvisioningUpdates +
  -authenticationKeyPath/ID/IssuerID into archive + both exportArchive calls
- ASC API-Key (free, .p8 from appstoreconnect.apple.com) is now the
  required path for exportArchive — app-specific-password no longer works
  for export since Xcode 14 (still used as altool-upload fallback)
- .env.deploy.local.example template added with one-time setup steps
- .gitignore: add *.p8 (.env*.local already covered)
2026-05-30 09:39:46 +02:00
chahinebrini
b31066a04c feat(chat): native action sheet + Insta-style heart for DM messages
- ChatBubble: useActionSheet replaces custom Modal (native iOS popup, Android bottom sheet)
- DM mode (isDM prop): hides like-count, shows Insta-style heart badge under bubble when liked
- Group chat unchanged
- Cleanup: remove unused Modal/Platform imports, sheet styles, actionsOpen state
- deploy.sh: auto-detect ANDROID_HOME + auto-create local.properties for local Gradle
- NEXT_RELEASE.md: DM reactions release note
- Includes other staged work across binder-mac, marketing, ops/mdm, ios/
2026-05-30 09:14:32 +02:00
chahinebrini
38df6fc79d feat(chat): push notifications for DMs + rooms
Backend:
- Prisma PushToken model + chat_push_enabled flag on profiles
- Migration 20260530_add_push_tokens (push_tokens table + profile flag)
- Service sendChatPush with expo-server-sdk (auto-disable invalid tokens)
- Fire-and-forget push trigger in sendDirectMessage + createRoomMessage
- API POST /users/me/push-token (upsert) + DELETE (soft-disable)

Client (rebreak-native):
- usePushTokenRegistration hook: permission, getExpoPushTokenAsync,
  Android channel 'chat', POST to backend; idempotent per session
- Notification tap deep-link: dm -> /dm?userId, room -> /room?roomId

Deploy:
- run_quiet spinner for silent altool/xcodebuild/gradle phases
- Release-notes pipeline (--notes flag / NEXT_RELEASE.md / interactive)
  archived to CHANGELOG.md, printed with ASC + Play Console links
- Default version bump ON (--no-bump opt-out), build cleanup
- NEXT_RELEASE.md with push-notification release note
2026-05-30 08:16:45 +02:00
chahinebrini
d31d5b3b83 fix(coach): use nova-2-general for ar/tr STT to avoid 400 errors 2026-05-30 02:14:16 +02:00
chahinebrini
6d59bfd62b feat(community): Bild-Upload-Limit 5MB → 10MB
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 01:09:47 +02:00
chahinebrini
38811820e6 feat(backend): Public-Domain-Guard + Mail-Detection (spins/%-Pattern)
Public-Domain-Guard (icloud.com/gmail.com etc. nie blockbar/veröffentlichbar):
- neue utils/public-email-domains.ts (shared Freemail-Liste)
- custom-domains/index.post + custom-domains/suggest + curated-domains/suggest
  lehnen Public-Domains mit 400 PUBLIC_DOMAIN ab (defense-in-depth)

Mail-Detection (mo): "spins" zu GAMBLING_KEYWORDS + Subject-%-Pattern (Score 10)
→ fängt "Spins + 400% Bonus"-Spam von Freemail-Absendern. 61/61 Tests grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 01:06:06 +02:00
chahinebrini
e5eff6778f fix(ci): expo-blur in package.json behalten — Lockfile-Mismatch beheben
CI (frozen-lockfile) brach seit 2 Commits: committed package.json hatte
expo-blur entfernt, pnpm-lock.yaml aber behalten → ERR_PNPM_OUTDATED_LOCKFILE,
Deploy blieb auf fd44687 (STT/TTS/Coach-Fixes nie live). expo-blur wird für
iOS-Vibrancy (SearchBarFloating) wieder gebraucht → package.json + Lockfile
konsistent mit expo-blur. Android nutzt platform-conditional Fallback.

Trägt zusätzlich den Version-Sync 0.3.6 → 0.3.13 (zied Versions-Schema).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 00:21:42 +02:00
chahinebrini
f9d44a6754 feat(coach): mehrsprachiges TTS — locale durchreichen + Cartesia Sonic-3
TTS-Sprache war provider-übergreifend hart auf "de" verdrahtet, locale aus dem
Request wurde ignoriert → arabischer Text wurde deutsch-phonetisch gesprochen.

- locale aus Body auslesen → Basis-Sprachcode an alle Provider
- Pro: Cartesia sonic-2 → sonic-3 (sonic-2 kann kein Arabisch; sonic-3 = 42 Sprachen)
- Legend: ElevenLabs language_code gesetzt (turbo_v2_5 multilingual, ar dabei)
- Google-Fallback: BCP-47-Map (ar→ar-XA etc.), de-Voice nur noch für de

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 23:57:12 +02:00
chahinebrini
b0315fd177 feat(coach): Lyra-Prompt-Update (Pricing/Beta/Geräte-Limits) + fr Sprach-Instruktion
- Prompt-Rewrite via Copilot: 2-Tier-Pricing (kein Free), Beta-Phase,
  Geräte-Limits, Mail-IDLE, RebReakBinder, Pricing-Disziplin (kein Proaktiv-Pitch)
- fr zu LANG_INSTRUCTIONS (message + sos-stream) — französische App-User
  bekamen sonst deutsche Lyra-Antworten

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 23:46:25 +02:00
chahinebrini
4f788e640e fix(coach): STT auf Deepgram nova-3 — fixt Arabisch/Türkisch-Transcription
nova-2 unterstützt kein ar/tr → Deepgram 400 "No such model/language/tier
combination" → leeres Transcript ("kein Text nach Speech"). nova-3 deckt alle
gelisteten Sprachen als diskrete Codes ab (de/en/tr/ar/fr/es/pt/it), ohne
Regression. Verifiziert gg. Deepgram models-languages-overview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 23:45:50 +02:00
chahinebrini
c3de7055a5 feat(mail): Sucht-Compound-Regel + Phase-1-Training-Foundation
Task B — linguistische FP-Fix:
- mail-classifier.ts: Subject-Keyword-Loop überspringt Keyword-Score wenn
  Subject das Keyword als Sucht-Compound enthält (z.B. "glücksspiel" in
  "Glücksspielsucht" → kein +50 Score). Globale linguistische Invariante
  Deutsch — Gambling-Marketer schreiben nie "Glücksspielsucht-Bonus".
- gambling-keywords.mjs: GAMBLING_WHITELIST erweitert um Stamm-Varianten
  (wettsucht, spielsucht, suchtberatung, suchthilfe) als Fallback für
  Compounds wo keyword ≠ exakter Stamm.
- 4 neue Tests: Forum Glücksspielsucht → PASS, Hilfe bei Spielsucht → PASS,
  Wettsucht-Selbsthilfe → PASS, Glücksspiel-Bonus 100€ → BLOCK.

Task C — Phase-1-Data-Foundation:
- mail-training-utils.ts: sanitizeSubjectForTraining() (PII-Stripping via
  Regex: EMAIL/URL/NUM/Greeting/ALL-CAPS) + detectSubjectLanguage() via
  franc (iso639-3). 26 Unit-Tests.
- franc@6.2.0 installiert (~50KB ESM).
- mail.ts insertMailClassificationSample(): ruft sanitizeSubjectForTraining()
  auf, schreibt detectedLang + subjectSanitized in features-JSON
  (Interim bis Schema-Migration).
- mail-retention-cron.ts: Subject-Nullification nach 30 Tagen (täglich) +
  Sample-Purge nach 12 Monaten (monatlich). DSGVO Art. 5 Abs. 1e.

105 Tests grün (58 classifier + 26 training-utils + 11 display-name + 10 gmail).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 08:14:57 +02:00
chahinebrini
fd446874e9 feat(backend): GlobalMailDisplayName — schema + migration + seed (30 brands)
Neue Tabelle `global_mail_display_names` für admin-kuratierte Glücksspiel-Marken.
Mo's mail-classifier try/catch-Fallback wird damit produktiv (Display-Name-Layer 2.5).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:09:58 +02:00
chahinebrini
807847381f fix(mail): Junk-Folder IDLE-Gap + Layer 2.6 global Display-Name-Patterns
Task 1 — Junk-Folder Fix:
- noopTimer (alle 2min) ruft jetzt triggerScan(conn) fire-and-forget auf
- Outlook/Hotmail-Mails die direkt in "Junk Email" landen werden damit
  innerhalb von max. 2min erfasst (IDLE hört nur INBOX, kein exists-Event)
- Consent-Guard analog exists-Event: nur wenn conn.consentAt gesetzt

Task 2 — Layer 2.6 global Display-Name-Patterns:
- getMailDisplayNamePatterns(userId) neu in db/domains.ts:
  lädt aus global_mail_display_names (admin-curated, pending Migration)
  + user_custom_domains type=mail_display_name (backward-compat)
  mit try/catch-Fallback bis Schema-Migration deployed ist
- getCustomMailDisplayNames() als @deprecated markiert (bleibt für Übergangszeitraum)
- scan-internal.post.ts: Import auf getMailDisplayNamePatterns umgestellt
- mail-classifier.ts: Layer 2.6 Kommentar von "dead code" auf "live v1.1" aktualisiert

Schema-Migration (global_mail_display_names) ist Aufgabe von rebreak-backend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 16:07:05 +02:00
chahinebrini
4a9601aadb fix(mail): Gmail OAuth — switch to iOS-typed Client (was Android Debug)
Bestehender OAuth Client `Rebreak Android Debug` mit Android-Type
verlangte zusätzlichen "Enable Custom URI scheme"-Toggle und passte
strukturell nicht zum iOS-only-Targeting. Neuer iOS-Client angelegt
mit Bundle-ID org.rebreak.app — Reverse-Client-ID-Redirect-URI
funktioniert out-of-the-box ohne Console-Toggle.

Infisical-Secret GOOGLE_OAUTH_CLIENT_ID wurde parallel auf neue
iOS-Client-ID aktualisiert (staging + prod).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 15:36:17 +02:00
chahinebrini
7cc9eb1d6d fix(mail): Google OAuth redirect_uri auf Reverse-Client-ID
Google iOS-OAuth-Client lehnt `rebreak://...`-Schemes mit
`invalid_request` ab. Reverse-Client-ID-Format ist required.
Empirisch verifiziert 2026-05-28 (siehe memory).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 15:23:16 +02:00