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.
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.
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).
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
- 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>
- 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>
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.
- 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)
'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.
- 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)
- 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
- 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
- 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
- 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)
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>
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>
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>
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>
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>
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>
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>