Drei zusammenhängende Blöcke in einem Commit (Files überlappen sich
zwischen den Themen — sauberer Split nicht ohne Friktion möglich):
1. Wordeck-Text-Only-Cleanup
Image-Occlusion + Audio-Front-Code raus. Server ist seit Migration
0004_wordeck_text_only.sql text-only (in Prod waren 0 Karten der
Typen, 0 Media-Files). Native-Code war Build-11-Altlast.
- Gelöscht: MediaCache, MediaEnvironment, RemoteImage,
AudioPlayerButton, MaskEditorView, CardEditorMediaFields,
CardEditorPayload, Media.swift
- CardType-Enum auf 5 Werte: basic / basic-reverse / cloze /
typing / multiple-choice
- media_refs aus Card, CardCreateBody, CardUpdateBody, call-sites
- WordeckAPI.uploadMedia / .fetchMedia / .deleteMedia + Single-File-
makeMultipartBody gestrichen
- MarketplaceCardConverter ohne Media-Cases
- CardRenderer ohne imageOcclusionView / audioFrontView
2. AI-Media-Mode raus
/decks/from-image-Endpoint existiert serverseitig nicht (server
registriert nur /decks/generate für Text-Prompts). Native-Aufrufe
wären 404 — toter Code.
- aiMedia-Case aus DeckEditorView.CreateMode, ModePicker auf
3 Optionen (Leer / KI / CSV)
- AIMediaFormSections, MediaFileRow, mediaPickers, thumbnail,
ingestPhotoItems, handlePDFImport raus
- generateDeckFromMedia + makeFromImageMultipartBody raus
- GenerationMediaFile-Struct + PhotosUI-Import + PlatformImage-
typealias raus
- NSPhotoLibraryUsageDescription aus project.yml entfernt (es gibt
keinen Photo-Library-Zugriff mehr)
- maxMediaFiles/maxImageBytes/maxPDFBytes + inferImageMimeType +
imageExtension aus DeckEditorHelpers raus
3. ζ-1 Offline-Sync
Konzept in docs/OFFLINE_SYNC.md. Server-authoritative-FSRS bleibt —
kein lokales FSRS, nur Snapshot-Modell.
- Neue SwiftData-Models: CachedCard + CachedDueReview, beide mit
userId/deckId-Indizes
- ModelContainer um die zwei Models erweitert (additive Migration,
sollte automatisch laufen — vor TestFlight verifizieren)
- DueReview bekommt programmatischen init(review:card:) für die
Cache-Rekonstruktion
- DeckListStore.refresh() zieht Cards + Due-Reviews pro Deck
parallel in einer TaskGroup; applyToCache in drei Helpers
gesplittet (applyDecks / applyCards / applyDueReviews)
- Karten: Upsert mit Orphan-Cleanup
- Due-Reviews: voll ersetzt pro Refresh (Server-`due`-Zeiten
ändern sich, Merge wäre falsch)
- StudySession.start() fällt bei Netz-Fehler auf
CachedDueReview-Snapshot zurück, setzt isOfflineSession-Flag
- StudySessionView zeigt offline-Banner und am Ende der Session
einen Hinweis „Weitere Karten erst nach Verbindung verfügbar"
- AccountView.wipeLocalCache(): DSGVO-Wipe vor signOut() und nach
deleteAccount → CachedDeck + CachedCard + CachedDueReview +
PendingGrade werden gelöscht
Plus: Keychain-Test in WordeckNativeTests.swift fix — erwartete
"ev.mana.wordeck", muss seit Cross-App-SSO-Commit 19fee75
ManaSharedKeychainGroup nutzen. Auf Konstant-Reference umgestellt,
damit's nicht wieder driftet.
Verifikation:
- xcodebuild iOS-Simulator: BUILD SUCCEEDED
- swiftlint --strict: 0 violations in 68 files
- swiftformat: clean
- 37/37 Tests grün (inkl. fix-Keychain-Test)
- macOS-Build scheitert an pre-existing .topBarTrailing in
StudySessionView (iOS-only API seit 2026-05-13, nicht durch
diesen Commit verursacht)
Pflicht-Verifikation vor TestFlight (in PLAN.md verewigt):
- SwiftData-Migration auf Bestandsbuilder
- Offline-Endurance (50+ Karten Flugmodus)
- Logout-Wipe mit Account-Switch
- Cross-Check Web ↔ Native nach Offline-Grade
Diff: 35 files, +869 / -1622, netto ~−750 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
Plan — wordeck-native (SwiftUI Universal)
Stand: 2026-05-13 — TestFlight Build 11 (v0.9.4). Alle Phasen β-0 bis β-7 + Polish-Iterationen. 43 Unit-Tests + 1 UI-Test grün.
Wordeck-Web-Look übernommen (v0.9.0 ff.): Fan-Stack-Tiles (5:7 Aspect, 3 rotierte Background-Layer), CardSurface in md/lg/hero, RatingBar mit Good-Emphasis. Tap auf Tile = Study- Mode, Pencil-Icon unten rechts = Deck-Detail. Identische Tile- Optik in Decks- und Entdecken-Tab.
App-Store-Connect: Wordeck, App-ID 6769019526, Bundle
ev.mana.wordeck, Team QP3GLU8PH3. AASA + /privacy + /help
live unter wordeck.com.
Pflicht-Check für β-2: Endurance-Test auf realem Gerät (200+ Karten mit Flugmodus zwischendurch) steht aus.
SOT:
../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md. Dieses File ist die App-lokale Status-Spur, das Greenfield-Doc hat die ganze Architektur-Begründung.
Aktueller Stand
✅ β-0 — Setup (2026-05-12, Tag v0.1.0)
- Repo-Skelett unter
git.mana.how/till/wordeck-native project.ymlmit Bundle-IDev.mana.wordeck, ManaSwiftCore viapath: ../mana-swift-coreAppConfigalsManaAppConfig-Provider:- Auth:
https://auth.mana.how - API:
https://api.wordeck.com - Keychain-Service:
ev.mana.wordeck
- Auth:
WordeckTheme.swiftmit forest-Werten (lokal nachgebaut ausmana/packages/themes/src/variants/forest.css)LoginView(Email/PW gegen mana-auth)- 3 Unit-Tests (AppConfig)
✅ Post-β-7-Polish (2026-05-13, Tags v0.8.1 → v0.9.4)
Live-Fixes nach Apple-Submission und Real-Device-Tests:
v0.8.1Wordeck-Rebrand (Bundleev.mana.cards→ev.mana.wordeck, AASA + Docker-Compose-Env nachgezogen)v0.8.2Archive-Polish — Versions-Sync zwischen Targets, iPad- Orientationsv0.8.3Sendable + AppIcon-Asset-Cleanup, /privacy + /help Stubsv0.8.4PhotosPicker Sendable-Warning via Sub-View-Structv0.8.5ITMS-90129-Fix: DisplayName Cards → Wordeck, Build 2v0.8.6Wordeck-Rebrand User-facing Strings durchgängigv0.8.7PublicDeckOwner.pseudonym Bool statt String? (Decoder-Crash bei Marketplace-Deck-Open)v0.8.8Card-Liste in DeckDetailView + WordeckAPI.listCardsv0.8.9URL-Query-Bug-Fix in ManaCore.AuthenticatedTransport (URL.appending(path:) encoded?→ 404; gefixt via String-Concat, ManaCore v1.0.1). Behob alle "0-Karten"-Phänomene und das stille Schlucken von Query-Endpoints.v0.9.0Wordeck-Web-Design: Fan-Stack-Tiles, CardSurface in 3 Sizes, RatingBar mit Good-Emphasis, horizontale Scroll-Sectionsv0.9.1Multiple-Choice-Karten gerendert (Distractors via Server, Tap-Selektion, Reveal mit Korrekt/Falsch-Highlight)v0.9.2Typing-Karten gerendert (Levenshtein-Match 1:1 aus cards-domain portiert: correct/close/wrong, Aliases-Support, Diakritika-Normalisierung)v0.9.3DEBUG-Auto-Login analog memoro-native (ensureSignedIn()in #if DEBUG) — auch in manaspur-native nachgezogenv0.9.4Tile-Tap = Study-Mode direkt, Pencil-Edit-Icon unten rechts → DeckDetail, ExploreView mit gleichem Tile-Layout (5:7 Aspect, Kategorie-Icon oben rechts in primary)
Schema-Fix: cards-Repo Commit 4d905bb (0002_decks_archived_at)
gleicht Schema-Drift in der Production-DB aus.
✅ β-7 — App-Store-Vorbereitung (2026-05-13, Tag v0.8.0)
- App-Icon-Platzhalter:
scripts/make-appicon.swiftgeneriert 1024×1024 PNG aus CoreGraphics (forest-green + "C"-Letter). Asset-Catalog auf Single-Size-Pattern umgestellt. Vor App-Store-Submit durch Designer- Icon ersetzen (siehedocs/RELEASE_CHECKLIST.md). StudyCardsIntent+CardsAppShortcuts(App Intents Framework): Siri-Shortcut "Karten lernen mit Cards" / "Mit Cards lernen", öffnet die App, App-Shortcut-Provider macht ihn ohne Konfiguration sichtbar.CardsShareExtension-Target (app-extension): empfängt Text/URL aus Safari/Mail-Share-Sheets, SwiftUI-Mini-Editor, persistiertPendingSharein App-Group. Haupt-App zeigt Banner in DeckListView, Tap →PendingShareConsumeViewmit Deck-Picker + Front/Back-Felder, Submit →POST /cards, danachPendingShareStore.remove.PendingShare+PendingShareStoreshared in beiden Targets.NSPhotoLibraryUsageDescription+NSUserActivityTypesin Info.plist ergänzt für Image-Occlusion-Picker und Universal-Links.docs/RELEASE_CHECKLIST.md— externe Schritte: Apple-Developer- Portal-Konfiguration, AASA-Endpoint, TestFlight-Test-Plan, App-Store- Connect-Felder, Compliance-Verifikation.- UI-Test robuster gegen Keychain-State (akzeptiert sowohl Login als auch Decks/Entdecken als gestartete App).
✅ β-6 — Native-Polish (2026-05-13, Tag v0.7.0)
- Keyboard-Shortcuts in
StudySessionView: Space = flip, 1/2/3/4 = again/hard/good/easy (über hidden Buttons mit.keyboardShortcut(.space/KeyEquivalent), iPad-Magic-Keyboard- macOS-tauglich)
NotificationManager@Observable: Permission-Request, Authorization-State, täglicherUNCalendarNotificationTriggerzur konfigurierten Uhrzeit (UserDefaults-Persistierung)SettingsView(in AccountView verlinkt): Toggle + DatePicker für Reminder, "Über"-Section mit Server-URLsWidgetSnapshotCodable mittopDecks(Top-3 nach dueCount) undtotalDueCountWidgetSnapshotStoreschreibt in App-Group-Containergroup.ev.mana.wordeckDeckListStore.refreshruftupdateWidgetSnapshot()undWidgetCenter.shared.reloadAllTimelines()nach jedem PullWordeckWidgetExtension-Target (eigenes app-extension-Bundle):WordeckWidgetBundle+CardsDueWidgetmitStaticConfiguration, Support für systemSmall, systemMedium, accessoryCircular, accessoryInline, accessoryRectangularDueProvideralsTimelineProvider: liest Snapshot, plant Refresh alle 30 min (plus instant-Refresh via Haupt-App)DueWidgetViewmit Family-Switch, alle 5 Family-Layoutscom.apple.security.application-groups: group.ev.mana.wordeckim Haupt- und Widget-EntitlementWidgetSnapshot.swiftin beiden Targets via XcodeGen-source-array (single-source-of-truth)
Deferred auf β-7: Siri-Shortcuts (App Intents), Share-Extension für Save-as-Card. Niedrige Priorität — Keyboard + Notifications + Widget decken 90% des Native-Polish ab.
✅ β-5 — Marketplace (2026-05-13, Tag v0.6.0)
PublicDeckEntry,PublicDeck,PublicDeckVersion,PublicDeckOwner,PublicDeckDetail,ExploreResponse,BrowseResponse,SubscribeResponseCodable-DTOs mit snake_caseMarketplaceSortEnum (recent/popular/trending) mit deutschen LabelsWordeckAPI: explore(), browseMarketplace(query:sort:language:), publicDeck(slug:), subscribe(slug:), unsubscribe(slug:)MarketplaceStore@Observable mit Explore-State + Browse-StateExploreViewmit Featured + Trending Carousels, Browse-LinkBrowseViewmit Searchable + Sort-Picker + ListePublicDeckViewmit Header + Version + Owner + Subscribe-Button (Auto-Fork serverseitig, danach NavigationLink zum eigenen Deck)PublicDeckCard+BrowseRowKomponenten mit forest-ThemeRootView→ TabBar (Decks / Entdecken / Account) statt Single-View- Universal-Link-Handler in
RootView(onOpenURL + onContinueUserActivity):https://wordeck.com/d/<slug>undcards://d/<slug>→ Explore-Tab öffnetPublicDeckView associated-domains: applinks:wordeck.comim entitlement- 5 neue Marketplace-Decoding-Tests (35 Total grün)
Wichtig: Universal-Links funktionieren erst, wenn AASA-Endpoint
unter wordeck.com/.well-known/apple-app-site-association
ausgeliefert wird — heute 404. Web-seitige Aufgabe.
✅ β-4 — Media + Advanced Card-Types (2026-05-13, Tag v0.5.0)
MediaUploadResponseDTO +MediaKind-EnumMaskRegionCodable mit 0..1-Coordinates,MaskRegions.parse/encode- Helpers (1:1-Port auscards-domain/image-occlusion.ts— Sortierung nach ID lexikographisch)CardFieldsBuilder.imageOcclusion,.audioFrontmit korrektermask_regions-Serialisierung als stringified JSON-ArrayWordeckAPI.uploadMedia(data, filename, mimeType)mit Multipart (25 MiB max),.fetchMedia(id)für streamed bytesMediaCacheactor mit LRU 200 MB (sortiert nachcontentModificationDate)mediaCache-Environment-Key, im App-Entrypoint instantiiertRemoteImageView — authentifiziertes Image-Loading mit ProgressView- Failure-State
AudioPlayerButton— AVAudioPlayer-Wrapper mit Play/Pause-Toggle, AVAudioSession-Setup für iOSCardRenderer.imageOcclusionView: AsyncImage + opake Maske über aktiver Region (Frontside), Label-Reveal auf BacksideCardRenderer.audioFrontView: AudioPlayerButton + back-Text auf FlipMaskEditorView: Touch-Drag-to-Create-Rectangle, Liste mit Label-Edit- Delete, 0..1-Normalisierung beim Commit
CardEditorViewerweitert: PhotosPicker für Image, fileImporter für Audio, Magic-Byte-MIME-Detection (JPEG/PNG/GIF/WebP)- 6 neue Tests für MaskRegions-Parse/Encode + Field-Builder (30 Total)
✅ β-3 — Editor (2026-05-13, Tag v0.4.0)
DeckCreateBody,DeckUpdateBody,CardCreateBody,CardUpdateBodyEncodable-Structs (snake_case viaCodingKeys, nil-Felder werden weggelassen)CardFieldsBuildermit Type-spezifischen Pflicht-Feld-KonstruktorenWordeckAPI: createDeck/updateDeck/deleteDeck + createCard/updateCard/deleteCardDeckEditorViewfür Create + Edit in einer View (mode-switch), Color-Picker mit 8-Preset-Palette aus forest-Theme, Category-Picker (11 Kategorien mit deutschen Labels), Visibility-Segmented-ControlCardEditorViewmit Type-Picker (basic, basic-reverse, cloze, typing, multiple-choice) und dynamischen Feldern je Typ. Cloze-View zeigt Live-Cluster-Count und Hint-Syntax-Hinweis. image-occlusion und audio-front zeigen β-4-PlaceholderDeckDetailViewmit 4 Action-Buttons (Lernen, Karte hinzufügen, Bearbeiten, Löschen), Confirmation-Dialog für Delete- DeckListView: "+"-Button im Toolbar (Leading), Sheet für Create
- 7 zusätzliche Encoding-Tests (24 Unit-Tests total)
✅ β-2 — Study-Loop (2026-05-13, Tag v0.3.0)
Card,Review,DueReviewCodable-DTOs,CardType-Enum (alle 7 Typen)Rating-Enum:again | hard | good | easymit deutschen LabelsCloze-Helpers (extractClusterIds, subIndexCount, clusterId, renderPrompt, renderAnswer, hint) — 1:1-Port auscards/packages/cards-domain/src/cloze.tsWordeckAPI.dueReviews(deckId:),WordeckAPI.gradeReview(...)mit ISO8601-EncoderPendingGradeSwiftData-Model +GradeQueuefür Offline-Submit (FIFO-Drain, originaler reviewedAt-Timestamp bleibt erhalten)StudySessionals @Observable State-Machine (loading/studying/finished/failed)CardRenderer: basic, basic-reverse (sub-index-abhängig), cloze client-rendered. image-occlusion/audio-front/typing/multiple-choice zeigen Placeholder (β-3/β-4)RatingBarmit Haptic-Feedback (medium für again/hard/good, heavy für easy, soft beim Flip)StudySessionViewvollbild aus DeckListView per NavigationLink- 9 zusätzliche Tests (Cloze 8x, Review/DueReview-Decoding 3x)
✅ β-1 — Decks lesen (2026-05-13, Tag v0.2.0)
Deck-Codable-DTO mit snake_case-CodingKeys, plusDeckCategory,DeckVisibility,FsrsSettings- ISO8601-Date-Decoder mit Fractional-Seconds-Toleranz
WordeckAPI.listDecks(),cardCount(deckId:),dueCount(deckId:)CachedDeckals SwiftData-Model mitlastFetchedAt(Offline-Read)DeckListStoreorchestriert API + Cache, paralleles Counts-Fetching via TaskGroupDeckListViewmit Pull-to-Refresh, Card/Due-Counts, deck.color-Streifen, Inbox-Banner für Marketplace-ForksAccountViewmit Sign-out-Button- iOS-Simulator-Build + Tests grün (6 Unit-Tests, 1 UI-Test)
Phasen (Detail in Greenfield-Plan)
| Phase | Status | Inhalt |
|---|---|---|
| β-0 | ✅ 2026-05-12 | Setup, Login, API-Probe |
| β-1 | ✅ 2026-05-13 | Decks lesen, SwiftData-Cache, Pull-to-Refresh |
| β-2 | ✅ 2026-05-13 | Study-Loop, Offline-Grade-Queue (Endurance-Test offen) |
| β-3 | ✅ 2026-05-13 | Editor: Deck-CRUD + Card-Create (5 Types); Anki-Import auf β-3-ext verschoben |
| β-4 | ✅ 2026-05-13 | Media-Upload, image-occlusion (Touch-Mask-Editor), audio-front (AVAudioPlayer) |
| β-5 | ✅ 2026-05-13 | Marketplace (Explore/Browse/Subscribe) + TabBar + Universal-Link-Handler (AASA server-side pending) |
| β-6 | ✅ 2026-05-13 | Keyboard-Shortcuts + Daily-Reminders + WidgetKit (Siri/Share deferred auf β-7) |
| β-7 | ✅ 2026-05-13 | App-Icon-Platzhalter + Siri-Shortcut + Share-Extension + Release-Checklist (externe Apple-Schritte siehe docs/RELEASE_CHECKLIST.md) |
| Wordeck-Cleanup | ✅ 2026-05-18 | Image-Occlusion + Audio-Front-Code raus (Server seit Migration 0004_wordeck_text_only.sql text-only). Gelöscht: MediaCache, MediaEnvironment, RemoteImage, AudioPlayerButton, MaskEditorView, CardEditorMediaFields, CardEditorPayload, Media.swift. CardType-Enum auf 5 Werte reduziert, media_refs aus Card+CardCreateBody+CardUpdateBody+CardCreate-Call-Sites raus, WordeckAPI.uploadMedia/.fetchMedia/.deleteMedia raus, makeMultipartBody (Single-File) raus. |
| AI-Media-raus | ✅ 2026-05-18 | /decks/from-image-Endpoint existiert serverseitig gar nicht — gesamten Native-Code rausgenommen: aiMedia-Case + Sub-Sections in DeckEditorView, generateDeckFromMedia + makeFromImageMultipartBody, GenerationMediaFile-Struct, PhotosUI-Import, PlatformImage-typealias, NSPhotoLibraryUsageDescription aus project.yml. ModePicker auf 3 Optionen (Leer/KI/CSV). Auch Test fix: WordeckNativeTests nutzt jetzt ManaSharedKeychainGroup statt String-Literal. 37/37 Tests grün. |
| ζ-1 (Offline-Sync) | ✅ 2026-05-18 | CachedCard + CachedDueReview SwiftData-Models, DeckListStore.refresh() zieht Cards+Due-Reviews pro Deck parallel (TaskGroup) und ersetzt den Snapshot atomar. StudySession.start() fällt bei Netz-Fehler auf den Cache zurück, setzt isOfflineSession-Flag für UX-Banner. DueReview bekommt programmatischen init(review:card:) für die Rekonstruktion. ModelContainer um die zwei Models erweitert (additive Migration, sollte automatisch durchlaufen). DSGVO-Logout-Wipe in AccountView: vor jedem signOut() und nach deleteAccount werden CachedDeck+CachedCard+CachedDueReview+PendingGrade aus dem Context gelöscht. iOS-Build grün, swiftlint --strict clean, 37/37 Tests passen. |
Geplant: ζ-2..4
Konzept in docs/OFFLINE_SYNC.md.
| Phase | Inhalt | Aufwand |
|---|---|---|
| ζ-2 | Distractor-Pool für MC-Karten (pro MC-Karte 10 Distractors mit-cachen) | 0,5 Tag |
| ζ-3 | SettingsView-Cache-Footprint anzeigen + manueller Cache-Clear |
0,5 Tag |
| ζ-4 (optional) | BGAppRefreshTask, Wi-Fi-Only-Toggle |
0,5 Tag |
Server-authoritative-FSRS bleibt — kein lokales FSRS, nur Snapshot.
Pflicht-Verifikation für ζ-1 (Endurance auf realem Gerät)
- SwiftData-Migration: alte App von TestFlight installieren, dann über Xcode mit ζ-1-Build überschreiben — Cache muss durchlaufen, kein Crash. (Additive Schema-Change sollte automatisch gehen, aber unverifiziert.)
- Offline-Study: 50+ Karten lernen mit Flugmodus, App killen, neu öffnen, weiter lernen — alle Grades landen am Server nach Reconnect.
- Logout-Wipe: Abmelden, anderer Account anmelden — keine Karten/Decks des Vorgängers in der DeckListView sichtbar.
- Cross-Check mit Web: Karte offline gegradet → Web zeigt identischen Review-State nach Reload.
Nächste Schritte: TestFlight + App-Store
Alle remaining steps sind externe Aktionen außerhalb des Repos —
Apple-Developer-Portal, App-Store-Connect, Xcode-Archive, das
Cards-Web-Repo (AASA). Strukturierte Liste in
docs/RELEASE_CHECKLIST.md:
- Apple-Developer-Konfiguration (Team-ID, App-IDs, App-Group, Profiles)
- App-Icon-Platzhalter durch Designer-Icon ersetzen
- AASA-Endpoint auf
wordeck.com(Cards-Web-Repo) - Xcode-Archive + TestFlight-Upload
- Endurance- und Cross-Device-Tests im TestFlight-Beta
- App-Store-Connect-Listing (Description, Screenshots, Privacy)
- Submission
Notizen aus β-4
- PencilKit für Mask-Editor explizit nicht genutzt. Web macht
Image-Occlusion-Masks per Touch-Drag-Rechteck (kein Freihand). Server-
Schema (
MaskRegion = {id, x, y, w, h, label}) erlaubt nur Rechtecke, PencilKit-Strokes wären dafür übersteigert. Wenn später Polygon-Masks oder Freihand-Skizzen dazu kommen, kann PencilKit nachgereicht werden. - Apple-Pencil-Support trotzdem grundsätzlich da: SwiftUI's
DragGesturereagiert auf Pencil-Eingaben genauso wie auf Finger.
Verschoben auf β-3-Extension oder später
- Anki-Import (
.apkg-Parser): Web parsed client-side und ruftPOST /cardspro Karte. Native bräuchte eigenen Swift-Parser für Anki-Pakete (Plist/sqlite/.apkg) — eigener Brocken, nicht blockierend für andere Phasen. - Card-Edit (PATCH /cards/:id): Card-Create reicht für Web-Parität in v1, Edit kann später nachgereicht werden.
- Distractor-Vorschau für Multiple-Choice-Editor: Server liefert
Distractors zur Lernzeit (
/decks/:deckId/distractors), Editor zeigt sie nicht — Web macht das auch nicht.
Pflicht-Tests für β-2 (vor β-3-Start)
- Endurance-Test auf realem Gerät: 200+ Karten lernen, Flugmodus zwischendurch — alle Grades landen am Server nach Reconnect.
- Cross-Check mit Web: Karte gegrade in Native → Web zeigt identischen Review-State nach Reload.
Cross-Refs
../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md— Greenfield-Plan SOT../mana/docs/MANA_SWIFT.md— Plattform-SOT../cards/CLAUDE.md— Cards-Repo../cards/STATUS.md— Web-Phasenstand (Referenz)../mana-swift-core/CLAUDE.md— ManaCore-KonventionenCLAUDE.md— Repo-Konventionen