chore: initial import

This commit is contained in:
2026-07-24 21:37:03 +02:00
commit 6b4ed9bc36
111 changed files with 95131 additions and 0 deletions
+280
View File
@@ -0,0 +1,280 @@
# API-Vertrag boehmitools Training
Stand: aus dem vollständigen boehmitools-Quellstand und den Plugins `trainingsplan`/`trainingstracker` 2.0 abgeleitet. Die Plugins werden durch die Host-App unter `/plugins/<plugin-id>` gemountet. Alle Pfade unten sind relativ zur konfigurierbaren Serverbasis, standardmäßig `https://tools.d-razz.de/`.
## Authentifizierung
Die Host-App schützt alle Pfade außer exakt `/api/health` optional mit HTTP Basic Auth. Ist `APP_PASSWORD` gesetzt, muss der Client `Authorization: Basic <base64(username:password)>` senden. Der Standard-Benutzername des Servers ist `admin`, sofern `APP_USERNAME` nicht gesetzt wurde.
Fehlerantwort bei fehlender oder falscher Anmeldung:
```json
{"detail":"Anmeldung erforderlich."}
```
Status: `401 Unauthorized`, Header `WWW-Authenticate: Basic realm="boehmitools"`.
## Host-Verbindungstest
### `GET /api/health`
Ohne Authentifizierung erreichbar. Antwortfelder laut Backend:
```json
{
"status": "ok",
"version": "1.0.0",
"plugins": {"trainingsplan":"ok","trainingstracker":"ok"},
"tandoor_configured": false,
"openai_configured": true,
"authentication_enabled": true,
"jobs_running": 0
}
```
Status: `200`. Die App prüft danach zusätzlich den authentifizierten Tracker-Health-Endpunkt.
## Trainingstracker
Basis: `/plugins/trainingstracker`
### `GET /api/health`
Liefert `ok`, Server-Datenpfade, Verfügbarkeit von OpenAI und die unterstützten Vertragsversionen:
- `plan_schema`
- `training_contract`
- `tracker_schema`
- `result_data`
- `prompt`
- `analysis_schema`
- `normalization`
Status: `200`, oder hostweit `401`.
### `GET /api/plans`
Antwort:
```json
{
"plans": [{
"id": "phase-1.json",
"filename": "phase-1.json",
"name": "Phase 1",
"title": "…",
"subtitle": "…",
"weeks": 8,
"days": 6,
"tracked": true,
"start_date": "",
"completed_sessions": 5,
"source_changed": false,
"plan_id": "phase-1",
"published_revision": 1,
"tracker_revision": 7
}],
"selected": "phase-1.json"
}
```
Status: `200`.
### `POST /api/plans/{plan_id}/select`
Kein Request-Body erforderlich. Antwort: `{"ok":true,"selected":"phase-1.json"}`.
Status: `200`; `404` mit `{"error":"Trainingsplan nicht gefunden"}`.
### `GET /api/plans/{plan_id}`
Antwort:
```json
{
"plan": { /* normalisierte veröffentlichte Planfassung */ },
"tracker": { /* Tracker-Schema 7 */ },
"analysis_catalog": { /* Wochen- und Gesamtstatus */ },
"equivalence_guide": { /* Variantencluster und Fallback-FAQ */ },
"capabilities": {"openai": true}
}
```
`plan` enthält ausschließlich vom Backend erzeugte Felder wie `source_file`, `source_hash`, `plan_id`, `schema_version`, `contract_version`, `compatibility`, `published_revision`, `published_at`, `name`, `title`, `subtitle`, `weeks`, `days`, `phases`, `phase_by_week`, `stages`, `exercise_catalog`, `front`, `training_format`.
`tracker` enthält mindestens `version`, `revision`, `source_file`, `source_hash`, `profile`, `progressions`, `sessions`, `week_statuses`, `created_at`, `updated_at`; für die GET-Antwort werden zusätzlich `source_changed`, `analysis_cache` und `analysis_state` zusammengesetzt.
Status: `200`; `404`; `400` bei nicht lesbarem Plan.
### `PUT /api/plans/{plan_id}/tracker`
Vollständiges Speichern, primär für Kompatibilität. Request entweder direkt Trackerobjekt oder:
```json
{"expected_revision":7,"tracker":{}}
```
Antwort:
```json
{
"ok": true,
"updated_at": "…",
"revision": 8,
"analysis_catalog": {},
"analysis_state": {}
}
```
Status: `200`; `400` für ungültige Nutzdaten; `409` bei Revisionskonflikt; `413` über 5 MiB.
### `PATCH /api/plans/{plan_id}/tracker/session`
Der von der App verwendete Autosave-Vertrag:
```json
{
"expected_revision": 7,
"profile": {"start_date":"","display_name":"","plan_notes":""},
"week_statuses": {"1":{"status":"open"}},
"session_key": "w01-d01",
"session": {
"status": "in_progress",
"plan_id": "phase-1",
"plan_revision": 1,
"items": {}
}
}
```
`profile`, `week_statuses` und `session` sind jeweils optional. Ein Patch ersetzt die betreffende Session vollständig, nicht einzelne Felder innerhalb der Session.
Erlaubte Sessionstatus: `planned`, `in_progress`, `stopped`, `completed`.
Erlaubte Übungsstatus: `planned`, `completed`, `partial`, `skipped`. Bei `skipped` kann `skip_reason` gespeichert werden. Neue strukturierte Ergebnisse nutzen `result_data` Version 2:
```json
{
"version": 2,
"mode": "reps|seconds|minutes",
"laterality": "bilateral|unilateral",
"sides_mode": "same|separate",
"sets": 3,
"weight_kg": 8.0,
"values": [8,7,6],
"left_values": [8,7,6],
"right_values": [8,7,6]
}
```
Das Backend akzeptiert maximal 20 Sätze, Werte von 0 bis 100000 und genau ein gemeinsames Gewicht. Alte Freitextfelder `result`, `note`, `progression` bleiben erhalten.
Erfolg wie beim PUT. Konfliktantwort:
```json
{
"error":"Die Session wurde in einem anderen Tab oder Gerät geändert.",
"code":"revision_conflict",
"current_revision":8
}
```
Status: `409`. Die App darf dann nicht automatisch gegen Revision 8 überschreiben.
### `POST /api/plans/{plan_id}/analysis`
Request Woche:
```json
{"scope":"week","week":1}
```
Request Gesamtanalyse:
```json
{"scope":"overall"}
```
Unveränderte, bereits aktuelle Analyse: `200` mit `cached:true`, `selection`, `analysis_state`, `analysis_cache`, `analysis_catalog`.
Neuer Hintergrundjob: `202 Accepted` mit `cached:false`, `job`, `analysis_state`, `analysis_catalog`.
Fehler: `400` bei fehlender OpenAI-Konfiguration, ungültiger Woche oder fehlenden Daten. `409` wenn bereits eine Analyse läuft. `404` bei unbekanntem Plan.
### `GET /api/plans/{plan_id}/analysis/status`
Antwort enthält `analysis_state`, `analysis_cache`, `analysis_catalog`, `updated_at`. Die App pollt nur bei `analysis_state.status == "running"`.
Status: `200`; `400`; `404`.
### `GET /api/plans/{plan_id}/tracker/export`
Liefert die Sessiondatei als `application/json`; erzeugt bei Bedarf einen leeren Tracker. Status `200` oder `404`.
## Trainingsplan-Editor
Basis: `/plugins/trainingsplan`
### `GET /api/plans`
Antwort: `plans` mit `id`, `name`, `active`, `revision`, `published_revision`, `has_unpublished_changes`, plus `active`.
### `POST /api/plans`
Request:
```json
{"name":"Neue Phase","from":"optionale-plan-id","kind":"training"}
```
`kind` kann technisch auch `recipe` sein; die Android-App erstellt ausschließlich Trainingspläne. Antwort ist der öffentliche Wrapper mit `id`, `plan_id`, `name`, `revision`, `published_revision`, `updated_at`, `published_at`, `has_unpublished_changes`, `tracked_sessions`, `config`.
### `GET /api/plans/{pid}`
Öffentlicher Wrapper des Entwurfs. Status `200` oder `404`.
### `POST /api/plans/{pid}/select`
Setzt den aktiven Plan. Status `200` oder `404`.
### `POST /api/plans/{pid}`
Speichert den Entwurf:
```json
{"config":{},"expected_revision":4}
```
Antwort: `ok`, öffentlicher Wrapper, `validation` mit `errors` und `warnings`.
Konflikt: `409` mit `code:"revision_conflict"` und `current_revision`.
### `POST /api/plans/{pid}/validate`
Request `{"config":{}}`. Antwort: `errors`, `warnings`, normalisierte `config`, `diff`. `diff` enthält Zähler und stabile-ID-basierte Änderungen für `days`, `rotations`, `exercises`, `progression_steps`, außerdem `changes`, `total_changes`, `training_format_changed`, `plan_meta_changed`, `tracked_sessions`.
### `POST /api/plans/{pid}/publish`
Request `{"expected_revision":4}`. Erfolgsantwort: `ok`, Wrapper, `diff`, `validation`.
Status `400` bei Validierungsfehlern; `409` bei Revisionskonflikt.
### `POST /api/plans/{pid}/rename`
Request `{"name":"…"}`. Antwort `id`, `name`, `revision`. Status `400` bei leerem Namen, `404` bei unbekanntem Plan.
### `DELETE /api/plans/{pid}`
Antwort `{"ok":true,"active":"…"}`. Status `400`, wenn der letzte Plan gelöscht werden soll; `404` bei unbekanntem Plan.
### `GET /api/plans/{pid}/proposals`
Liefert die Vorschlagsdatei unverändert oder `{}`. Vorschläge enthalten unter anderem `id`, `status`, `scope_key`, `analysis_type`, `week`, `target`, `target_label`, `target_exercise_id`, `target_progression_id`, `target_step_id`, `suggested_change`, `reason`, `condition`, `manual_step`, `analysis_created_at`.
### `POST /api/plans/{pid}/proposals/{proposal_id}`
Request `{"status":"open|accepted|rejected|reviewed"}`. Antwort `ok`, `proposals`. Ungültiger Status: `400`.
### Weitere vorhandene Endpunkte
`GET /api/config`, `POST /api/config`, `GET /api/default`, `POST /api/reset`, `GET /api/plans/{pid}/export`, `POST /api/plans/import`, `POST /api/pdf` existieren. Die native App verwendet für den Kernbetrieb die planbezogenen Endpunkte; sie bearbeitet niemals Serverdateien direkt.
+62
View File
@@ -0,0 +1,62 @@
# Domänenmodell
## Vertragsversionen
- Plan: `schema_version = 3`
- Trainingsvertrag: `contract_version = 2`
- Ergebnisschema: Version 2
- Tracker: `version = 7`
- Ergebnisdaten: `version = 2`
## Identitäten
Dauerhaft stabil sind `plan_id`, Phasen-ID, Tages-ID, Rotations-ID, Übungsplatzierungs-ID, Bewegungs-`exercise_id`, `progression_id`, Progressions-ID und Stufen-ID. `legacy_id` wie `d1-r0-e0` dient ausschließlich der Migration alter Sessions. Neue Sessionitems werden mit der stabilen Übungsplatzierungs-ID gespeichert.
## Planlebenszyklus
Eine Plandatei besitzt `config` als veröffentlichte Fassung und `draft` als bearbeitbaren Entwurf. `revision` schützt Bearbeitungen, `published_revision` kennzeichnet die vom Tracker gelesene Fassung. Entwurf speichern und veröffentlichen sind getrennte Operationen.
## Planstruktur
Plan → Phasen, Tage, Progressionen, Übungsbibliothek und Trainingsformat.
Tag → Warm-up, Rotationen, Cooldown, Stretch.
Rotation → stabile ID, Bezeichnung, Übungen.
Übungsplatzierung → `id`, `legacy_id`, `exercise_id`, `progression_id`, Name, Hinweise, Standard-`result_schema`.
Progression → stabile ID/Key, Name, geordnete Stufen.
Progressionsstufe → ID, Name, optionale Phase, Bewegungscluster, Analysefaktor und optional überschreibendes `result_schema`.
## Ergebnisschema
Auflösungsreihenfolge:
1. manuelle Sessionanpassung,
2. Schema der gewählten Progressionsstufe,
3. Standardschema der Übung,
4. automatische Fallback-Erkennung.
Felder: `mode` (`reps`, `seconds`, `minutes`, `none`, im Plan zusätzlich `auto`), `weight_mode` (`none`, `optional`, `required`), `laterality` (`bilateral`, `unilateral`), `sides_mode` (`same`, `separate`) sowie optional eine feste Satzanzahl.
## Tracker
Tracker → Profil, Sessions, Wochenstatus, Revision und Planbezug.
Session-Key: `wNN-dNN`. Sessionstatus: `planned`, `in_progress`, `stopped`, `completed`.
Sessionitemstatus: `planned`, `completed`, `partial`, `skipped`. `done` bleibt als Kompatibilitätsfeld erhalten und ist bei `completed` oder `partial` wahr.
`result_data` speichert eine Messart, gemeinsame oder getrennte Satzwerte und höchstens ein gemeinsames Gewicht. Unterschiedliche Gewichte pro Satz, RIR, RPE und Technikrating gehören bewusst nicht zum Vertrag.
## Analyse
`analysis_state` ist persistent und kann `idle`, `running`, `done` oder `error` sein. Der Cache enthält höchstens eine Analyse je Woche und genau eine Gesamtanalyse. Neuberechnung überschreibt dieselbe Datei. Es gibt keine Historie.
`analysis_catalog` kennzeichnet Wochen als `missing`, `stale` oder `current`; die Gesamtanalyse zusätzlich als `needs_weeks`.
## Vorschläge
Analysen erzeugen höchstens drei strukturierte Vorschläge. Ziel-IDs verbinden Vorschläge mit Übungen, Progressionen und Stufen. Die App darf Status ändern und zum Ziel navigieren, aber niemals automatisch den Plan mutieren.
+25
View File
@@ -0,0 +1,25 @@
# Implementierungsplan
## Meilenstein 0
Backendcode, Mounts, Authentifizierung, Datenmodelle, Migrationen, Beispielpläne, alte Sessions, Analysen, Vorschläge und Web-UIs analysieren. Vertrag und Konfliktmodell dokumentieren.
## Meilenstein 1
Single-Activity-App, Compose/Material 3, Navigation, Hilt, Retrofit/OkHttp, Kotlin Serialization, Room, DataStore, Android-Keystore-Verschlüsselung, WorkManager und Verbindungstest.
## Meilenstein 2
Trackerplan laden, UI-Zustand pro Plan persistieren, Legacy-IDs migrieren, Sessionstatus und Übungsstatus abbilden, Ergebnisfelder aus Stufe/Übung/Fallback bestimmen, debounced Autosave und Offline-Queue implementieren.
## Meilenstein 3
Lokale Fortschrittskarten, Variantencluster/FAQ, Wochenabschluss, Analyseauswahl, `200`/`202`, persistente Jobanzeige und Polling.
## Meilenstein 4
Entwurfs-CRUD, strukturierte Planansicht, Result-Schema-Editor, Bibliothek, Validierung, Diff, Publish und manuell bestätigte KI-Vorschläge.
## Meilenstein 5
Unit-, Repository-, MockWebServer- und Compose-Tests mit allen gelieferten Fixtures; 360-dp-Prüfung; Light/Dark; Gradle-Test-, Lint- und Assemble-Läufe; ZIP/APK/Build- und Testbericht.
+27
View File
@@ -0,0 +1,27 @@
# Offline- und Konfliktkonzept
## Cache
Room speichert die zuletzt erfolgreichen JSON-Antworten für Planlisten, normalisierte Trackerpläne, Editorentwürfe und Analysen. Der Cache ist lesbar, wenn das Netzwerk fehlt; er ist kein eigenständiger Serverdatenbestand.
## Ausstehende Mutationen
Sessionänderungen werden als vollständiger Session-Patch mit `expected_revision` gespeichert. Bei Transportfehlern wird exakt der gesendete JSON-Patch in einer FIFO-Queue abgelegt. WorkManager synchronisiert nur bei Netzwerkverbindung und in Erstellungsreihenfolge.
## Revisionskonflikt
HTTP `409` mit `code = revision_conflict` stoppt die Queue am konfliktbehafteten Eintrag. Der Eintrag bleibt erhalten und wird als Konflikt markiert. Die UI zeigt lokalen Patch und aktuelle Serverrevision. Es gibt kein automatisches Rebase und kein blindes Überschreiben.
Sichere Aktionen:
- Serverstand neu laden und lokale Änderung manuell erneut anwenden.
- Konflikt-Patch nach bewusster Benutzerentscheidung verwerfen.
- Keine automatische Änderung der `expected_revision`.
## Analysejobs
Ein laufender Job wird vom Server in `state.json` gespeichert. Die App verlässt sich nicht auf flüchtigen UI-Zustand, sondern liest nach Neustart `analysis_state` erneut. Solange `status == running`, bleibt der Startbutton deaktiviert und der Statusendpunkt wird gepollt.
## Serveränderungen
Die App schreibt nie direkt in Plan-, Session-, Analyse- oder Vorschlagsdateien. Sämtliche Mutationen laufen über die vorhandenen HTTP-Endpunkte.
+36
View File
@@ -0,0 +1,36 @@
# Screen Map
## App-Shell
- Start/Verbindung: Server-URL, Benutzername, Passwort, Verbindungstest.
- Hauptnavigation: Tracker, Planeditor, Einstellungen.
- Material-3-Theme mit Light/Dark/System.
## Tracker
- Planauswahl.
- Session-Ansicht mit Wochen- und Tageschips.
- Sessionstatus starten, stoppen, fortsetzen, wieder öffnen, abschließen, zurücksetzen.
- Warm-up, Rotationen, Übungen, Cooldown und Stretch.
- Übungsstatus offen, erledigt, teilweise, übersprungen.
- Progressionsauswahl und progressionsabhängiger Ergebnis-Editor.
- Fortschritt: lokale Sessionkennzahlen, Wochenstatus, Analyseauswahl, Cache/Jobstatus.
- Plan: veröffentlichte Struktur und Planrevision.
- FAQ: Trainingsformat, Variantencluster und Backend-Fallbackregeln.
## Planeditor
- Planliste, Auswahl, neuer Plan, Umbenennen, Löschen.
- Entwurfsübersicht und Metadaten.
- Tage, Rotationen und Übungen mit stabilen IDs.
- Ergebnisschema pro Übung.
- Progressionsstufen und überschreibende Ergebnisschemata.
- Übungsbibliothek.
- Prüfung mit Fehlern/Warnungen.
- Tracker-Vorschau und Änderungsübersicht.
- Veröffentlichen.
- Vorschlags-Postfach: prüfen, Ziel öffnen, angenommen/abgelehnt/geprüft markieren.
## Visuelle Referenz
In den gelieferten Archiven sind keine Bilddateien oder Screenshots enthalten. Als vorhandene visuelle Referenz wurden daher die mobile-first HTML/CSS-Oberflächen der beiden Plugins ausgewertet. Die native Umsetzung übernimmt Informationshierarchie und Funktionsgruppen, nicht DOM-Struktur oder Web-Komponenten.
+85
View File
@@ -0,0 +1,85 @@
# Vollständige Quellenanalyse
Stand: 24. Juli 2026
## Untersuchte Lieferobjekte
| Archiv | Originaldateien | SHA-256 |
|---|---:|---|
| `01-boehmitools-current(1).zip` | 93 | `167d0d1b4670cbe6bb7cab10d2b84c315fde6dadc824cf3f7b9d6ccce3d61d1e` |
| `02-training-tools-v2.0-bundle.zip` | 29 | `b2859ccb86a3915a86e3c64e157c9021f3b713b63b600360419e686f41c8dbee` |
| `03-training-app-samples.zip` | 8 | `1634f3b83e98d5e5df7d1c56719dede13ed4cf1ce1517a3745cfee0384f93991` |
Alle enthaltenen Dateien wurden inventarisiert. Die technische Analyse umfasste insbesondere Host-Mounts und Basic Auth, beide Plugin-Backends, Storage- und Vertragsmodule, Plugin-Metadaten, Tests, HTML-Oberflächen, Migrationsdokumentation, beide Planwrapper, beide Legacy-Tracker, Wochen- und Gesamtanalyse sowie die Vorschlagsdatei. Die vollständige Dateiliste liegt unter `build-logs/source-files.txt`; die maschinenlesbare Datenauswertung unter `build-logs/source-audit.json`.
## Abgleich aktueller Serverstand gegen v2.0-Bundle
Die Quelltexte von `plugins/trainingsplan` und `plugins/trainingstracker` sind in beiden Lieferungen inhaltlich identisch. Im entpackten aktuellen Stand kamen durch lokale Python-Testläufe lediglich `__pycache__`-Dateien hinzu. Die beiden Trainingsplan-Dateien `phase-1.json` und `calihoss-newbie-8-wochen.json` sind ebenfalls identisch. Der vollständige Serverstand enthält zusätzlich vier Rezeptplan-Dateien, die nicht zum Umfang dieser Android-App gehören.
Die Host-App mountet die Plugins unter:
- `/plugins/trainingsplan`
- `/plugins/trainingstracker`
Die App nutzt ausschließlich diese HTTP-Mounts. Es gibt keinen direkten Dateizugriff.
## Pläne und Vertragsversionen
Beide Planwrapper haben `revision = 1` und `published_revision = 1`. Die veröffentlichte Fassung liegt in `config`, der Entwurf in `draft`.
| Plan | Modus | Wochen | Tage | Blöcke | Platzierungen | Progressionen | Stufen | Katalog |
|---|---|---:|---:|---:|---:|---:|---:|---:|
| `phase-1` | `tabata` | 8 | 6 | 12 | 48 | 31 | 248 | 40 |
| `calihoss-newbie-8-wochen` | `sets_reps` | 8 | 6 | 12 | 48 | 31 | 248 | 27 |
Jede der 96 Übungsplatzierungen besitzt ein explizites `result_schema`; alle 496 Progressionsstufen ebenfalls. Tages-, Rotations-, Platzierungs-, Progressions- und Stufen-IDs sind innerhalb ihres jeweiligen Entitätstyps vollständig und eindeutig. Bewegungs-IDs werden absichtlich über mehrere Platzierungen hinweg wiederverwendet und dürfen daher nicht global eindeutig behandelt werden.
Die Beschreibung verwendet die Begriffe `plan_schema` und `training_contract`. In den Plan-JSONs heißen die tatsächlichen Felder `schema_version = 3` und `contract_version = 2`. Der Tracker-Health-Endpunkt meldet dieselben Verträge als `versions.plan_schema = 3` und `versions.training_contract = 2`. Die Android-Domäne bildet diese Namensdifferenz explizit ab, statt ein zusätzliches JSON-Feld zu erfinden.
## Legacy-Sessions und Migration
| Sessiondatei | gelieferte Version | Sessions | Items | positionsbasierte Übungskeys | Freitextergebnisse | `result_data` v2 |
|---|---:|---:|---:|---:|---:|---:|
| `phase-1.json` | 5 | 10 | 197 | 80 | 40 | 0 |
| `calihoss-newbie-8-wochen.json` | 1 | 6 | 88 | 48 | 0 | 0 |
Die Testdaten erzwingen damit beide Migrationspfade:
1. `dN-rN-eN` wird ausschließlich anhand des im Plan mitgelieferten `legacy_id` in die stabile Platzierungs-ID überführt.
2. Vorhandene Freitextergebnisse werden lokal in `result_data` Version 2 geparst, ohne den Originaltext vor einer Benutzeränderung zu verwerfen.
Warm-up-, Cooldown- und Stretch-Items besitzen eigene Sequenzschlüssel und werden nicht als Übungsplatzierungen migriert.
## Analyse und Vorschläge
Geliefert sind genau eine Wochenanalyse für Woche 1 und genau eine Gesamtanalyse. Beide besitzen `status = success` und die Antwortbereiche `headline`, `summary`, `data_quality`, `metrics`, `exercise_updates`, `plan_adjustments`, `warnings` und `conclusion`.
Die Vorschlagsdatei hat Version 2, gehört zu `phase-1`, basiert auf `published_revision = 1` und enthält zwei offene Vorschläge. Zielreferenzen verwenden stabile Bewegungs-, Progressions- und Stufen-IDs. Der Editor aktualisiert nur den Vorschlagsstatus und navigiert zum Ziel. Er überträgt keinen Vorschlag automatisch in den Entwurf.
## API- und Konfliktbefunde
- Hostweite Basic Auth wird vor den Plugin-Routen ausgewertet; nur der exakte Hostpfad `/api/health` ist ausgenommen.
- Der Session-Patch ersetzt eine komplette Session und prüft die globale Tracker-`revision` über `expected_revision`.
- Ein `409 revision_conflict` liefert `current_revision`; es existiert kein serverseitiger Merge-Endpunkt.
- Analyse-POST liefert `200` für einen aktuellen Cachetreffer und `202` für einen neu gestarteten Hintergrundjob.
- Der laufende Analysezustand ist serverseitig persistent und wird über den Statusendpunkt gelesen.
- Planentwurf und Veröffentlichung haben getrennte Revisionen und getrennte Endpunkte.
- Der Server erstellt beim Planlöschen ein Backup. Die Android-App verlässt sich dennoch ausschließlich auf die API und kennt keinen Backup-Dateipfad.
## Visuelle Referenz
Keines der drei Archive enthält PNG-, JPEG-, WebP- oder andere Screenshotdateien. Das ist ein Widerspruch zur Aufgabenbeschreibung, die UI-Screenshots als mitgelieferte Referenz nennt. Für die Umsetzung wurden deshalb die beiden mobilen HTML/CSS-Oberflächen vollständig als visuelle und funktionale Referenz ausgewertet. Die native App übernimmt Informationshierarchie, Begriffe und Arbeitsabläufe, nicht DOM-Struktur oder Web-Komponenten.
## Bewusst nicht übernommene Web-Funktionen
Der `phase-1`-Plan deklariert ein Tabata-Format. Entsprechend der App-Vorgabe wird dieser Vertrag angezeigt und für Ergebnisschemata berücksichtigt, aber es wird kein Tabata- oder Trainingstimer implementiert. Ebenso fehlen bewusst RIR, RPE, Technikrating, unterschiedliche Gewichte pro Satz, WebView und direkte OpenAI-Kommunikation.
## Verifikation des gelieferten Backends
Die vorhandenen Plugin-Testsets wurden lokal ausgeführt:
```text
11 passed, 8 skipped
```
Das vollständige Protokoll liegt unter `build-logs/backend-plugin-tests.log`.