# Vertrag 4: Telemetry-API (Pflicht) Was ein Modul **kontinuierlich** während der Sitzung an die Plattform pushen muss, damit das Lehrer-Live-Dashboard funktioniert. **Das ist Spec-Pflicht, kein Add-on.** Wer keine Telemetry liefert, wird nicht integriert. Live-Sicht für Lehrpersonen ist der pädagogische Kern-USP von GeoGraSim — Module müssen sie tragen. ## Drei Event-Typen ### 1. Heartbeat — alle 10–30 Sekunden bei Aktivität Sagt: „Schüler*in ist da, aktueller Stand ist X." ```http POST /v2beta/api/telemetry Authorization: Bearer Content-Type: application/json { "type": "heartbeat", "moduleSlug": "logistik", "moduleVersion": "2.0.0", "phase": "auftrag-bearbeiten", "phaseLabel": "Auftrag wird bearbeitet", "step": "3/7", "currentScore": 42, "scoreMax": 100, "timeInPhaseS": 252, "totalTimeS": 1023, "ts": "2026-05-15T14:23:45Z" } ``` | Feld | Pflicht | Beschreibung | |---|---|---| | `type` | ✓ | `"heartbeat"` | | `moduleSlug` | ✓ | aus Manifest | | `moduleVersion` | ✓ | aus Manifest | | `phase` | ✓ | Modul-interne Phase-ID (string, frei wählbar) | | `phaseLabel` | ✓ | Anzeigename der Phase für Lehrer-Dashboard | | `step` | – | „X/Y"-Format wenn Modul Schritt-Konzept hat, sonst weglassen | | `currentScore` | – | aktueller Score wenn vorhanden | | `scoreMax` | – | maximaler Score wenn vorhanden | | `timeInPhaseS` | ✓ | Sekunden in der aktuellen Phase | | `totalTimeS` | ✓ | Sekunden seit Modul-Start (ohne Pausen >5min) | | `ts` | ✓ | ISO-Timestamp | **Frequenz**: aus `/api/runtime-config` (Default 10–30s). Modul wählt innerhalb der Plattform-Grenzen, je nach Geschwindigkeit. Bei sehr schnellen Modulen (Klick-Spiel) eher 10s, bei Reflexions-Modulen 30s. **Bei Inaktivität (Schüler*in hat 60s nichts getan)**: Heartbeat pausieren bis wieder Aktivität. Plattform-Helper-Lib `telemetry-client.js` macht das transparent — Modul übergibt seine bevorzugte Frequenz, Lib clamped auf Plattform-Grenzen. ### 2. Milestone — bei definiertem Fortschritt Sagt: „Schüler*in hat etwas Konkretes geschafft." ```json { "type": "milestone", "moduleSlug": "logistik", "milestoneId": "first-order-accepted", "milestoneLabel": "Erster Auftrag angenommen", "scoreDelta": 15, "currentScore": 42, "phase": "auftrag-bearbeiten", "ts": "..." } ``` | Feld | Pflicht | Beschreibung | |---|---|---| | `type` | ✓ | `"milestone"` | | `milestoneId` | ✓ | Aus `manifest.telemetryMilestones[].id` | | `milestoneLabel` | ✓ | Anzeigetext | | `scoreDelta` | – | Punkte für diesen Milestone (kann negativ sein) | **Wichtig**: `milestoneId` muss vorab im Manifest deklariert sein. Plattform lehnt unbekannte IDs ab. Erweiterung erfordert Modul-Update. ### 3. Stuck — bei Inaktivität trotz aktivem Tab Sagt: „Schüler*in zögert / hängt fest. Lehrperson sollte hinschauen." ```json { "type": "stuck", "moduleSlug": "logistik", "phase": "auftrag-bearbeiten", "phaseLabel": "Auftrag wird bearbeitet", "step": "4/7", "idleS": 95, "lastActionLabel": "Container ausgewählt", "hint": "Möglicherweise unsicher bei Routenwahl", "ts": "..." } ``` | Feld | Pflicht | Beschreibung | |---|---|---| | `type` | ✓ | `"stuck"` | | `idleS` | ✓ | Sekunden seit letzter sinnvoller Eingabe | | `lastActionLabel` | – | Was war die letzte Aktion (Lehrperson-Hilfe) | | `hint` | – | Modul-Diagnose was wahrscheinlich los ist | **Wann auslösen**: bei `idleS >= 90` Sekunden ohne sinnvolle Eingabe (Mouse-Move zählt nicht, Klick/Tap/Drag schon). Sobald wieder Aktivität: einmaliger neuer Heartbeat, kein „unstuck"-Event. ### 4. Accessibility-Blocker — bei A11y-Hindernis Sagt: „Schüler*in mit A11y-Bedarf kann an dieser Stelle nicht weiter." ```json { "type": "accessibility-blocker", "moduleSlug": "logistik", "blockerType": "visual-required", "hint": "Karten-Tap zur Routenwahl ist nicht per Tastatur ersetzbar in dieser Modul-Version", "phase": "auftrag-bearbeiten", "ts": "..." } ``` | Feld | Pflicht | Beschreibung | |---|---|---| | `type` | ✓ | `"accessibility-blocker"` | | `blockerType` | ✓ | `"visual-required"` / `"audio-required"` / `"mouse-required"` / `"motor-required"` | | `hint` | ✓ | Was genau ist das Hindernis (für Lehrperson) | **Wann auslösen**: wenn das Modul aus den Profil-Settings sieht (`preferences.screenReader === true` o.ä.), dass eine Aufgabe für diese Schüler*in nicht bearbeitbar ist. Lehrer-Cockpit zeigt das prominent — Lehrperson kann ein anderes Modul anbieten. ## Idempotenz und Reliability - Plattform akzeptiert dasselbe Event mehrfach (z.B. nach Netz-Hiccup); Deduplizierung über `ts` + `type` + `moduleSlug` + `session_token` - Modul soll bei Netz-Fehler **maximal 1× retryen**, dann verwerfen (kein Endlos-Queue im Browser, sonst Memory-Leak) - Bei Tab-Close: ein finaler Heartbeat mit `type: "heartbeat"` und `phase: "tab-closed"` falls möglich (`navigator.sendBeacon`) ## Was die Plattform damit macht - **Tabelle `telemetry_events_v2`** — alle Events landen dort - **Live-Push an Lehrer-Dashboard** via Server-Sent-Events (`/v2beta/api/teacher/live-stream?classId=42`) - **Lehrer-Dashboard zeigt pro Schüler*in**: - Aktuelles Modul + Phase + Step - Score-Bar - Zeit in aktueller Phase (rot wenn >> erwartet) - Stuck-Indikator (orange Warning bei aktivem Stuck-Event) - Letzter Milestone ## Performance-Limits - Plattform nimmt **maximal 1 Event pro Sekunde pro Schüler*in** an. Mehr wird gedrosselt (HTTP 429). - Pro Sitzung **maximal 2000 Events**. Danach werden Heartbeats ignoriert (Milestones noch akzeptiert). - Payload-Max: 4 KB pro Event. ## Mock-Endpoint für Modul-Entwicklung Während der Modul-Entwicklung (vor Integration) liefert die Plattform einen Mock-Endpoint: ``` http://localhost/v2beta-mock/api/telemetry ``` Akzeptiert alle Events, gibt 200 OK zurück, loggt nach `v2-platform/logs/mock-telemetry.log`. Modul-Instanzen können damit ihren Code testen, bevor sie liefern. ## ✅ Geklärt mit Thomas (2026-05-17) - **Heartbeat-Frequenz**: Modul wählt selbst innerhalb der Plattform- Grenzen (`/api/runtime-config`, Default 10–30s). Admin-Board justierbar. - **Stuck-Schwellwert**: zentrale Plattform-Konfig (Default 90s). Module können im Manifest keinen eigenen Wert setzen — wenn ein Modul stark abweichende Werte braucht, im Inbox-Ping an Atlas begründen. - **Stuck-Events**: vorerst nur visuell im Lehrperson-Dashboard (Rot-blinkender Indikator), keine Push-Notifications. Push folgt in Phase 2 wenn Auto-Tuning + Alerting kommt. - **Speicherdauer**: 30 Tage Default, Admin-konfigurierbar bis 365 Tage. Für Bildungsforschungs-Auswertungen einzelne Klassen auf 90 Tage oder mehr hochsetzen.