Files
geograsim/App/docs/sim-spec-2026-06-12.md
T
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00

20 KiB
Raw Blame History

GeoGraSim — Sim-Spec für externe KI-Entwicklung

Stand: 2026-06-12 · Ergänzung zu module-interface.md und integration-konzept.md.

Wichtig: Dieses Dokument fasst nur das Neue / Geänderte seit Juni 2026 zusammen und bündelt die für eine externe KI essentiellen Schnittstellen. Die historischen Files bleiben gültig für: Sprache (Lernarbeit statt Spiel · Leichte Sprache), iPad-Pattern, Persistenz/Resume, Datei-Struktur, Datenmodell-Grundlagen.


0. TL;DR — Was eine neue Sim aus Plattform-Sicht ist

App/sims/<sim-id>/game.html      ← deine HTML-Datei
App/sims/<sim-id>/engine.js      ← optional, separate JS-Logik
App/sims/<sim-id>/assets/        ← optional, sim-eigene Bilder/Sounds

App/pages/<sim-id>.php           ← PHP-Wrapper (Plattform-Standard, Atlas legt an)

Plus 1 Eintrag in der DB-Tabelle module_info (Title, Icon, Status, sort_order — Atlas macht das).

Die Sim selbst ist ein selbst-laufender Browser-Frontend-Block. Sie spricht mit der Plattform NUR über die 4 Window-Globals (siehe §2) und einen einzigen HTTP-Endpoint /api/progress.php zum Speichern (siehe §3).


1. Plattform-Conventions (Stand Juni 2026)

Drei Window-Globals + ein optionales Lib. Vollständige Doku im Header von /assets/js/live-client.js.

1.1 window.__GGS__ — Plattform setzt, Sim liest

Wird vom PHP-Wrapper VOR der Sim-Logik gesetzt:

window.__GGS__ = {
  sessionId:   'uuid',          // anonyme Session
  studentId:   12,              // optional, null bei Gast
  classId:     3,               // optional
  simId:       'kofferdetektiv',
  simName:     'Kofferdetektiv',
  level:       1,
  baseUrl:     '/geograsim/App', // oder '' auf Prod
  apiUrl:      '/geograsim/App/php/api',
  basePath:    '/geograsim/App',
  mode:        'free',          // 'free' | 'teacher_started' | 'locked'
  forcedLevel: null,            // wenn 'teacher_started': Pflicht-Level
  easy:        false,           // Leichte Sprache aktiv?
};

Wenn __GGS__ nicht da ist, läuft die Sim trotzdem (Dev/Direkt-URL) — sie sollte robust mit undefined umgehen.

1.2 window.GGS_LIVE_STATE — Sim setzt eine Funktion

Funktion, die einen Snapshot des aktuellen Sim-Zustands zurückgibt. Wird vom Plattform-Live-Client alle 4 s aufgerufen und ans Lehrer-Cockpit gestreamt.

window.GGS_LIVE_STATE = function () {
  if (!gameReady) return null;
  return {
    simId:       'kofferdetektiv',
    phase:       'fall-3-zoll',     // freier Text — was passiert gerade
    progressPct: 60,                // 0-100, didaktische Empfehlung
    score:       42,                // wenn schon scorebar
    health:      'good',            // good | ok | struggle (für Status-Punkt)
    // weitere sim-spezifische Felder — Cockpit picked sich, was es kann
  };
};

Fallstrick (echter Bug von Fluss am 2026-06-11): Nutze KEINE Property params auf einem Objekt, wenn params nicht direkt darauf existiert. if (!game.params) true → Snapshot null → Cockpit zeigt „meldet keine Live-Werte" obwohl die Sim läuft. Lies State direkt aus dem Computed-Result.

1.3 window.GGS_TUTORIAL — Sim registriert Replay-Hook

Damit der -Button im Header das Onboarding nochmal öffnet:

window.GGS_TUTORIAL = {
  simId:  'kofferdetektiv',
  title:  '🕵️ Kofferdetektiv',
  replay: function () { startTutorial(); }, // deine eigene Funktion
};

Der Button wird in der Sim selbst eingefügt (siehe §4.2 Header-Standard).

1.4 window.__GGS_LIVE__ — Plattform setzt, Sim liest

Read-only Proxy, von live-client.js bereitgestellt:

window.__GGS_LIVE__ = {
  activeMs:          number,    // aktive Spielzeit (gecappt bei 7_200_000 = 120 min)
  idleMs:            number,    // Zeit seit letzter Interaktion
  sessionStartedAt:  timestamp,
  lastInteractionAt: timestamp,
  capped:            boolean,
  MAX_SESSION_MS:    7200000,
  IDLE_THRESHOLD_MS: 90000,
};

Wann lesen: Beim Submit als duration_ms — bitte __GGS_LIVE__.activeMs statt Date.now() - start benutzen. Sonst landet auch Idle-Zeit (Tab vergessen) in der DB. Plattform-Cap zusätzlich im Backend defensiv.

1.5 Reservierte __-Heartbeat-Keys

Der Live-Client mischt automatisch in jeden GGS_LIVE_STATE-Snapshot:

state.__activeMs, state.__idleMs, state.__lastInteractionAt,
state.__sessionStartedAt, state.__capped

Niemals überschreiben — Cockpit liest diese aus.


2. Plattform-Bibliotheken (optional einbindbar)

2.1 Glossar-Tooltip

<script src="/assets/js/glossar-tooltip.js" defer></script>
<script>
  window.addEventListener('DOMContentLoaded', () => {
    GlossarTooltip.init({
      apiBase:  '/php/api',
      moduleId: 'kofferdetektiv',   // filtert DB auf sim-eigene Begriffe
    });
  });
</script>

Im Markup:

<span class="ggs-g" data-glossar="zollkontrolle">Zollkontrolle</span>

Wirkung: Tap/Klick → Popup mit Kurztext + Link zum vollen Glossar-Eintrag. Tooltip-CSS wird automatisch injiziert. Hat zentralen Style — nicht selbst CSS für .ggs-g schreiben.

Vorgehen für neue Glossar-Begriffe: in der DB-Tabelle glossar anlegen, module_id setzen. Atlas oder Glossar-Instanz macht das.

2.2 Quick-Intro (Mini-Onboarding-Modal)

Für Sims OHNE volles Tutorial. Bietet ein einheitliches Modal beim 1. Besuch + Replay-Funktion.

<script src="/assets/js/ggs-quick-intro.js" defer></script>
<script>
  window.addEventListener('DOMContentLoaded', () => {
    GgsQuickIntro.init({
      simId: 'kofferdetektiv',
      title: '🕵️ Kofferdetektiv',
      lead:  'Du untersuchst beschlagnahmte Koffer am Flughafen. Was darf rein, was nicht — und warum?',
      steps: [
        'Koffer öffnen — du siehst Inhalt + Herkunft + Zielland.',
        'Gegenstände prüfen — viele Vorschriften erschließen sich aus Geografie + Politik.',
        'Entscheidung treffen: durchlassen, beanstanden oder beschlagnahmen.',
        'Feedback bekommen — pro Entscheidung Lehrtext + Glossar.',
      ],
      goals: [
        'Zoll- und Einreisebestimmungen verstehen',
        'Herkunftsländer von Produkten geografisch einordnen',
        'Gesetzeskunde im Kontext (Artenschutz, Drogen, Lebensmittel)',
      ],
    });
  });
</script>

Verhalten: 1. Besuch → Modal auto-open. localStorage merkt sich „gesehen" (ggs.intro.<simId>). Library registriert automatisch window.GGS_TUTORIAL.replay(). -Header-Button öffnet das Modal wieder.

Du kannst alternativ ein volles, sim-spezifisches Tutorial bauen (mehrere Schritte mit Bildern + Glossar-Begriffen — siehe weltkueche/game.html als Referenz). Dann musst du nur window.GGS_TUTORIAL = {simId, title, replay: deineFunktion} selbst setzen.

2.3 Live-Client (live-client.js)

Wird vom PHP-Wrapper automatisch eingebunden — Sim muss nichts tun. Library trackt Idle (siehe §1.4), sendet Heartbeats an /api/live.php, pollt Pause-Flag.


3. Submit-Pfad — Wie die Sim ihre Daten persistiert

Drei Aktionen via POST /api/progress.php mit JSON-Body. Auth läuft automatisch über die Session-Cookie aus __GGS__.

3.1 Live-Fortschritt (optional, alle paar Aktionen)

fetch(__GGS__.apiUrl + '/progress.php', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  credentials: 'same-origin',
  body: JSON.stringify({
    sim_id:  'kofferdetektiv',
    action:  'update',
    data:    { phase: 'fall-3', score: 42 },
  }),
});

Selten nötig — GGS_LIVE_STATE deckt das eigentlich ab. Nur wenn du persistente Zwischenstände in der DB willst.

3.2 Reflexionsantwort (zwischen Phasen)

fetch(__GGS__.apiUrl + '/progress.php', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  credentials: 'same-origin',
  body: JSON.stringify({
    sim_id:    'kofferdetektiv',
    action:    'reflection',
    data: {
      level:    1,
      question: 'Was war dein schwierigster Fall?',
      answer:   'Der Koffer aus Kenia',
    },
  }),
});

3.3 Abschluss-Submit (Pflicht — am Ende einer Stufe)

fetch(__GGS__.apiUrl + '/progress.php', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  credentials: 'same-origin',
  body: JSON.stringify({
    sim_id: 'kofferdetektiv',
    action: 'submit_assessment',
    data: {
      level:       1,
      stars:       3,
      score:       72,
      duration_ms: (window.__GGS_LIVE__ && window.__GGS_LIVE__.activeMs) || 0,
      completed:   true,
      results: {
        // SIM-SPEZIFISCHE Felder. Die Plattform-Benchmark-Engine
        // erwartet von dir konkrete Keys (siehe §5) — bitte mit Atlas
        // abstimmen, bevor du das Schema festschreibst.
        cases_solved:       8,
        cases_total:        10,
        correct_first_try:  6,
        wrong_decisions:    1,
        glossar_consulted:  4,
      },
    },
  }),
});

Wichtig: results ist sim-spezifisches JSON. Es wird in assessments.results als String gespeichert und sowohl im Lehrer-Cockpit (live.php) als auch im Ergebnisse-Tab (results.php) ausgelesen. Konventionen pro Sim folgen aus der Benchmark-Formel — siehe §5.


4. Design-System (Pflicht)

CSS-Variablen + Layout-Klassen aus /assets/css/design-system.css.

4.1 Farb-Variablen

--ggs-fjord       /* Primärblau, Headlines */
--ggs-fjord-dark  /* Dunkler Akzent */
--ggs-moss        /* Erfolg/Grün */
--ggs-sand        /* Kosten/Neutral */
--ggs-coral       /* Gefahr/Rot */
--ggs-orange      /* Warnung */

UI-Farben sind NICHT die Bild-Palette (siehe §6).

4.2 Header-Standard (Plattform-konform)

<header class="ggs-header">
  <a href="/schueler" class="ggs-header-logo">
    <img src="/assets/img/bildLogo.png" alt="" class="ggs-header-logo-icon">
    <img src="/assets/img/textlogo_geograsim.svg" alt="GeoGraSim" class="ggs-header-logo-text">
  </a>
  <div class="ggs-header-separator"></div>
  <span class="ggs-header-module">Kofferdetektiv</span>
  <span class="ggs-header-badge">Stufe 1</span>

  <div class="ggs-header-spacer"></div>

  <div class="ggs-header-actions">
    <!-- Plattform-Standard:  als ERSTER Button vor 📚 -->
    <button class="ggs-btn-ghost" id="btn-info"
            title="Anleitung nochmal zeigen"
            onclick="if(window.GGS_TUTORIAL&&typeof window.GGS_TUTORIAL.replay==='function')window.GGS_TUTORIAL.replay();"></button>
    <a class="ggs-btn-ghost" id="ggs-lehrplan-link" href="#" target="_blank" title="Lehrplan-Bezug">📚</a>
    <button class="ggs-btn-ghost" id="btn-mute" title="Ton aus/an">🔊</button>
    <button class="ggs-btn-ghost" id="btn-reset" title="Fortschritt zurücksetzen">🔄</button>
    <a href="/schueler" class="ggs-btn-ghost" title="Zurück zum Cockpit">🏠</a>
  </div>
</header>

Regel: = Tutorial-Replay (Plattform-Convention). Wenn deine Sim einen anderen Info-Inhalt hat (z.B. Fachinfo/Glossar in Farmer), kommt der mit eigenem Icon — 📖 für Glossar, 📚 für Lehrplan.

4.3 Layout-Zonen

ggs-sim-layout 3-Spalten-Grid (Status links, Sim Mitte, Aktionen rechts). Details in module-interface.md §5.

4.4 Cockpit-Sichtbarkeit (LIVE_PRIMARY_FIELDS)

Damit deine Sim im Lehrer-Cockpit sinnvolle Spalten zeigt, muss Atlas in teacher.html einen LIVE_PRIMARY_FIELDS['kofferdetektiv']-Eintrag setzen. Beispiel:

'kofferdetektiv': [
  {k:'health',           label:'Status', align:'center', fmt: v => LIVE_VALUE_LABELS.health[v] || '—'},
  {k:'phase',            label:'Phase'},
  {k:'progressPct',      label:'Fortschritt', fmt: v => _liveBar(v, 0, 100, 'mid')},
  {k:'score',            label:'🏅 Score',     fmt: v => _liveBar(v, 0, 100, 'high')},
  {k:'cases_solved',     label:'Gelöst'},
  {k:'correct_first_try',label:'1.-Versuch ✓'},
],

Range-Bars: Helfer _liveBar(val, min, max, goodWhen). goodWhen: 'high' (grün ab 60% Range), 'low' (invers — niedrig=gut), 'mid' (neutral grau).

Vorgehen für externe KI: Liefere Atlas einfach die Liste deiner GGS_LIVE_STATE-Felder + welche davon Range-Bars wert sind. Atlas patcht teacher.html.


5. Benchmark-Engine — Sim braucht eine Scoring-Formel

Die Plattform berechnet pro Submission einen 0100-Modul-Score (für Lehrer-Cockpit Top 3 / Bottom 3 und Ergebnisse-Tab). Die Formel lebt in App/php/lib/Benchmark.php als private statische Methode.

Pro neuer Sim: du lieferst die Formel-Idee, Atlas integriert sie. Beispiel-Briefing (Kofferdetektiv):

scoreKofferdetektiv($results) {
  $solved   = $results['cases_solved']      ?? 0;
  $total    = $results['cases_total']       ?? 0;
  $firstTry = $results['correct_first_try'] ?? 0;
  $wrong    = $results['wrong_decisions']   ?? 0;
  if ($total === 0) return null;
  $base    = ($solved / $total) * 70;                  // 70 % Lösung
  $bonus   = ($firstTry / $total) * 30;                // 30 % Sauberkeit
  $malus   = min(20, $wrong * 5);                      // bis -20 für Fehlentscheidung
  return max(0, min(100, $base + $bonus - $malus));
}

Wichtig:

  • Wenn keine bewertbaren Daten → null (lieber „noch keine Bewertung" als geratene Zahl).
  • Pro Modul max. die 3 besten Sessions zählen — macht die Engine selbst.

6. Bildstil — Flat Scandinavian

Für ALLE Bilder in der Sim (Onboarding, Kategorie-Icons, Karten-Backgrounds, Charakter-Avatare):

Palette:

Background  #e8e4d8  (warmer Sand)
Primär      #1f4b37  (Forest)
Sekundär    #4a7c4e  (Moss)
Akzent      #e8c547  (Honey)
Detail      #b04a3e  (Coral, sparsam)

DALL-E-Prompt-Block (Standard):

Flat scandinavian illustration, geometric shapes, soft warm color palette
(#1f4b37 forest, #4a7c4e moss, #e8c547 honey, #e8e4d8 sand background,
#b04a3e coral accent sparingly). No people unless explicit, no text, no
photo-realism. Clean educational style for children 10-14, Sek I.
[hier deine Sim-Szene]. Square 1024x1024.

Auflösungen:

  • Onboarding-Karten: 1024×1024 → WebP, max 200 KB
  • Sim-eigene Bilder: 512×512 oder 1024×1024 je nach Verwendung
  • Glossar-Illustrationen: 512×512 (Atlas verwaltet das zentral)

Standorte: App/sims/<sim-id>/assets/img/ für sim-eigene; App/assets/img/glossar/ für Glossar.


7. Sprache (Pflicht — schon in module-interface.md §4a)

Kurz zur Erinnerung:

Nicht Sondern
spielen, Spiel arbeiten mit, Simulation, bearbeiten
Spieler:in Lernende, Bearbeiter:in
Game Over Ziel verfehlt / Durchgang beendet
Highscore Beste Leistung
Lehrkraft Lehrperson (österreichisches Pädagog:innen-Bildungsgesetz)

Begründung: Wygotski-basierte Lernarbeit-Sprache. Wenn die Sim für Schüler:innen sichtbar wird, gilt das strikt.

Plus: Sek-I-Tonalität (1014 Jahre). Nicht infantil, nicht erwachsen. Du-Form, aber höflich.


8. Persistenz, iPad-Pattern, Leichte Sprache

Alle drei sind in module-interface.md abgehandelt und gelten weiter:

  • Persistenz (§7b): localStorage Autosave + Server-Mirror, Reload führt nie zum Neustart, expliziter Reset-Button mit Bestätigung.
  • iPad (§4c): 1180×820 Referenz, Touch-Ziele 3640 px, kein Hover-Kleber, font-size: 16px für Inputs.
  • Leichte Sprache (§4b): Plattform-DB liefert Easy-Varianten von Glossar/Lehrplan automatisch, wenn Lehrperson das pro Schüler:in setzt.

9. Workflow für eine neue Sim (Pragmatisch)

  1. Konzept-Skizze an Atlas: Was lernen sie? Wie ist die Mechanik? Welche Stufen? Welche Lehrplan-Anbindung?
  2. Atlas legt an:
    • module_info-Eintrag (Title, Icon, Status='entwurf', sort_order)
    • App/pages/<sim-id>.php-Wrapper
    • Routing in .htaccess
    • Glossar-Begriffe einsammeln (an Glossar-Instanz weitergeben)
  3. Externe KI baut App/sims/<sim-id>/:
    • game.html mit Plattform-Header + GGS_LIVE_STATE + GGS_TUTORIAL/QuickIntro
    • Engine in JS (vanilla, kein Build-Step)
    • Submit-Pfad bei Phasen-Ende
    • localStorage-Autosave
  4. Atlas integriert:
    • LIVE_PRIMARY_FIELDS['<sim-id>'] in teacher.html
    • Benchmark::score<SimName>() in Benchmark.php
    • Playwright-Tests in .dontDeploy/playwright/tests/stufe1-smoke/
    • Deploy + Test
  5. Status auf aktiv schalten (über admin-modules.html oder direktes SQL).

10. Test-Pattern (Stufe 1 Smoke)

Pro Sim mindestens 1 Test in .dontDeploy/playwright/tests/stufe1-smoke/:

test('Sim kofferdetektiv · __GGS__ + live-client + GGS_LIVE_STATE', async ({ page }) => {
  await page.goto('https://geograsim.at/kofferdetektiv');
  await page.waitForTimeout(2500);
  const info = await page.evaluate(() => ({
    ggs:      typeof window.__GGS__,
    state:    typeof window.GGS_LIVE_STATE,
    tutorial: typeof window.GGS_TUTORIAL,
  }));
  expect(info.ggs).toBe('object');
  expect(info.state).toBe('function');
  expect(info.tutorial).toBe('object');
});

Existing Pattern in sim-platform-context.spec.js deckt alle Sims ab — Atlas ergänzt deinen Sim-Slug dort.


11. Referenz-Sims als Vorbild

Wenn die externe KI ein gutes Vorbild für die Code-Struktur braucht:

  • App/sims/weltkueche/game.html — full Tutorial-Pattern + Glossar-Tooltip in Tutorial-Texten + saubere Submit-Logik. Mittelgroß (~2000 LoC).
  • App/sims/busfahrt/game.html — Karten-basierte Sim mit Quick-Intro statt full Tutorial. Topbar mit eigenem Styling.
  • App/sims/eu-werkstatt/game.html — Drag-and-Drop / Card-Sorting-Pattern.
  • App/sims/sonnensystem/game.html — 3D-Canvas (Three.js) + state-driven Aufgaben.

Die externe KI sollte mindestens eine dieser Sims lesen, bevor sie ihre eigene baut — um Plattform-Header, Glossar-Anbindung und Submit-Pattern abzukucken.


12. Was die externe KI explizit NICHT machen soll

  • Keine eigenen URL-Routen / .htaccess-Eingriffe — Atlas macht das.
  • Keine Schema-Änderungen an DB-Tabellen ohne Rückfrage.
  • Keine API-Calls in Endpoints, die nicht in §3 stehen.
  • Keine eigenen Plattform-Bibliotheken in /assets/js/ ergänzen — wenn dir was zentral fehlt, melde es Atlas.
  • Keine Spiel-Sprache. Ernsthaft — siehe §7.
  • Keine echten KI-Calls IM Produkt (OpenAI/Claude/Anthropic-API). Auswertung ist parametrisch. (Tutorial-Inhalte mit KI vorgenerieren ist OK, im Produkt landet das als statischer Content.)

13. Erstkontakt-Briefing für die externe KI (Copy-Paste)

Du baust ein neues GeoGraSim-Sim-Modul namens "<sim-id>".
Plattform-Kontext + Conventions: docs/sim-spec-2026-06-12.md (dieses Doc) +
docs/module-interface.md.

Hard Constraints:
- Vanilla HTML/JS/CSS. Kein Build-Step. Kein React/TypeScript.
- Plattform-Header genau wie in §4.2.
- window.GGS_LIVE_STATE() liefern (§1.2).
- window.GGS_TUTORIAL setzen oder ggs-quick-intro.js nutzen (§1.3 / §2.2).
- Submit am Ende via POST /api/progress.php (§3.3).
- duration_ms aus __GGS_LIVE__.activeMs lesen (§1.4).
- Sprache: Lernarbeit, nicht Spiel (§7).
- iPad-tauglich (siehe module-interface.md §4c).
- Persistenz: Reload darf nie neu starten (siehe module-interface.md §7b).

Liefere am Ende:
- App/sims/<sim-id>/game.html (+ engine.js wenn separat)
- Liste deiner GGS_LIVE_STATE-Felder + welche Range-Bars Sinn machen
- Vorschlag für Scoring-Formel (Benchmark §5)
- Glossar-Begriffe, die du im Tutorial verwendest

Atlas integriert dann teacher.html, Benchmark.php, .htaccess, DB.

14. Ansprechpartner & Files-Map

Fragen zu Wer / Wo
Plattform-API / live-client / Cockpit Atlas, /assets/js/live-client.js, /teacher.html, /php/api/live.php
Submit-Endpoint, assessments-Schema Atlas, /php/api/progress.php, /php/api/assessment.php
Benchmark-Formel pro Sim Atlas, /php/lib/Benchmark.php
Glossar-Begriffe / Tooltip-Pattern Glossar-Instanz / Atlas, DB-Tabelle glossar + /assets/js/glossar-tooltip.js
Lehrplan-Anbindung / Kompetenz-Mapping Lehrplan-Instanz / Atlas, DB-Tabellen kompetenzen/kompetenz_modules/lehrplan_anchors
Design-System-Klassen /assets/css/design-system.css
Bildstil + DALL-E-Prompts Atlas, §6 hier + Memory reference_bildstil

Fragen? An Atlas (Plattform-Instanz). Diese Spec wird laufend gepflegt — bei jeder neuen Convention bitte hier ein Update einreichen statt Side-Doc.