<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>SQLite |</title><link>https://aretascodes.dev/de/tags/sqlite/</link><atom:link href="https://aretascodes.dev/de/tags/sqlite/index.xml" rel="self" type="application/rss+xml"/><description>SQLite</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>de-DE</language><lastBuildDate>Fri, 12 Jun 2026 00:00:00 +0000</lastBuildDate><image><url>https://aretascodes.dev/media/icon_hu_2ab4f4763b27c75b.png</url><title>SQLite</title><link>https://aretascodes.dev/de/tags/sqlite/</link></image><item><title>CogniVault Backend erklärt, Teil 4 · Study Tools, Fortschritt und die Privacy-Belege</title><link>https://aretascodes.dev/de/blog/backend-explained-study-hub-privacy/</link><pubDate>Fri, 12 Jun 2026 00:00:00 +0000</pubDate><guid>https://aretascodes.dev/de/blog/backend-explained-study-hub-privacy/</guid><description>
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Alle Abkürzungen werden im Anhang am Ende der Seite vollständig erklärt.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In
haben wir eine Frage durch das hybride Retrieval und den Agenten-Loop bis hin zur zitierten Antwort verfolgt. In diesem letzten Teil wird dieselbe Maschinerie auf ein ganz anderes Ziel ausgerichtet: &lt;em&gt;Dir etwas beizubringen&lt;/em&gt; — und dann schließen wir die Serie ab, indem wir das zentrale Versprechen des Projekts überprüfen: Nichts verlässt deinen Rechner.&lt;/p&gt;
&lt;h2 id="ein-rezept-vier-lerntools"&gt;Ein Rezept, vier Lerntools&lt;/h2&gt;
&lt;p&gt;CogniVault generiert Quizzes, mehrteilige Workshops, Karteikartendecks und Mindmaps aus deinen Dokumenten. Vier verschiedene Outputs — aber unter der Haube steckt ein gemeinsames Fünf-Schritte-Rezept:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Abrufen (Retrieve).&lt;/strong&gt; Dieselbe hybride Suche wie aus Teil 3, aber statt deiner Frage lautet der Such-Prompt etwas Breitgefächertes wie &lt;em&gt;&amp;ldquo;Schlüsselkonzepte, Definitionen, wichtige Fakten, Hauptideen&amp;rdquo;&lt;/em&gt;, eingeschränkt auf die von dir ausgewählten Dokumente. Bis zu 15 repräsentative Chunks kommen zurück.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mit einem Template prompten.&lt;/strong&gt; Die Anweisungen an Gemma sind nicht tief in Python vergraben — es sind editierbare Markdown-Dateien im Ordner &lt;code&gt;backend/prompts/&lt;/code&gt; (&lt;code&gt;quiz.md&lt;/code&gt;, &lt;code&gt;flashcards.md&lt;/code&gt; usw.). Legst du eine modifizierte Kopie in &lt;code&gt;backend/prompts/custom/&lt;/code&gt; ab, überschreibt sie die mitgelieferte Version beim allerersten Request danach. Kein Neustart, keine Code-Änderung. Prompt Engineering als reine Konfiguration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Output erzwingen (Constrain).&lt;/strong&gt; Ein kleines lokales Modell zu bitten, &amp;ldquo;bitte gib JSON zurück&amp;rdquo;, klappt in den meisten Fällen — und &lt;em&gt;in den meisten Fällen&lt;/em&gt; bedeutet in Produktion schlichtweg einen Bug. CogniVault nutzt Ollamas grammatikgebundene Generierung (&lt;code&gt;format=&amp;quot;json&amp;quot;&lt;/code&gt;), was ungültiges JSON nicht nur unwahrscheinlich, sondern unmöglich macht, gepaart mit einer niedrigen Temperature für Konstanz. Die ganze Geschichte, wie man zuverlässige Strukturen aus einem 4-Milliarden-Parameter-Modell presst, findest du in
.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Defensiv validieren.&lt;/strong&gt; Jedes generierte Item wird Feld für Feld gecheckt. Fehlerhafte Items werden &lt;em&gt;verworfen&lt;/em&gt;, statt den kompletten Batch crashen zu lassen. Kleine Modelle verhauen manchmal eine von zehn Fragen; ein Produkt sollte deswegen nicht gleich zusammenbrechen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Persistieren.&lt;/strong&gt; Alles landet in SQLite. Quizzes sind später fortsetzbar, Workshop-Fortschritte überleben Neustarts und der Status von Karteikarten wird pro Deck gespeichert.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hier ist das Rezept für ein Quiz in Aktion:&lt;/p&gt;
&lt;div class="mermaid"&gt;%%{init: {'sequence': {'actorFontSize': 28, 'messageFontSize': 24, 'loopTextFontSize': 22, 'noteFontSize': 22}}}%%
sequenceDiagram
actor U as Du
participant F as Study Hub UI
participant B as FastAPI
participant V as VectorDB
participant O as Ollama (gemma4:e4b)
participant S as SQLite
U-&gt;&gt;F: Scope, Schwierigkeit, Fragenanzahl wählen
F-&gt;&gt;B: POST /api/study/quiz/generate
B-&gt;&gt;V: Hybride Suche, eingeschränkt auf deine Dokumente
V--&gt;&gt;B: Bis zu 15 repräsentative Chunks
B-&gt;&gt;B: Render das quiz.md Prompt-Template
B-&gt;&gt;O: chat(format="json", niedrige Temperature)
O--&gt;&gt;B: Grammatikgebundenes JSON
B-&gt;&gt;B: Jede Frage validieren, schlechte verwerfen
B-&gt;&gt;S: Quiz speichern (später fortsetzbar)
B--&gt;&gt;F: Typisierte Antwort
F--&gt;&gt;U: Spielen, einreichen, punkten — und vielleicht ein neues Badge
&lt;/div&gt;
&lt;p&gt;Die vier Tools unterscheiden sich nur in ihrem Template und ihrer Form: Quizzes produzieren Multiple-Choice- und Wahr/Falsch-Fragen mit Erklärungen; Workshops erstellen zuerst eine Gliederung und schreiben dann jede Lektion &lt;em&gt;on demand&lt;/em&gt;, wenn du sie öffnest; Karteikarten liefern Vorder-/Rückseiten-Paare; Mindmaps generieren einen Themenbaum, den das Frontend als interaktives Diagramm rendert. (Dieser Renderer war übrigens sein eigenes kleines Abenteuer:
.)&lt;/p&gt;
&lt;h2 id="sessions-die-sich-selbst-tracken"&gt;Sessions, die sich selbst tracken&lt;/h2&gt;
&lt;p&gt;Die meisten Lern-Apps zwingen dich, einen Start-Button zu drücken, und die meisten Leute vergessen es. CogniVault geht da einen anderen Weg: &lt;strong&gt;Lernsessions werden abgeleitet, nicht manuell gestartet&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Jede Chatnachricht verlängert entweder die aktuelle Session oder — nach einer 15-minütigen Pause — startet stillschweigend eine neue. Geh einen Kaffee holen, komm zurück, mach weiter: gleiche Session. Komm morgen wieder: neue Session. Keine Buttons, kein Vergessen.&lt;/p&gt;
&lt;p&gt;Jede Nachricht speichert außerdem ein winziges Event (Zeitstempel, ob du einen Scope-Filter oder Dateianhänge genutzt hast) in &lt;code&gt;progress.db&lt;/code&gt; — eine SQLite-Datenbank, also eine komplette relationale Datenbank in einer einzigen Datei. Elf Tabellen halten alles fest: Sessions, Nachrichten-Events, verdiente Badges, Quiz-Versuche und gespeicherte Quizzes, Workshops und Lektionen, Decks und Karten sowie Mindmaps.&lt;/p&gt;
&lt;p&gt;Eine kleine Engineering-Note, die sich abzugucken lohnt: Der Tracking-Call im Chat-Endpoint ist so verpackt, dass er den Chat &lt;em&gt;niemals&lt;/em&gt; blockieren oder abbrechen kann. Analytics müssen immer Beifahrer sein, niemals der Fahrer.&lt;/p&gt;
&lt;h2 id="25-badges-als-daten-definiert"&gt;25 Badges, als Daten definiert&lt;/h2&gt;
&lt;p&gt;Die Achievements sind nicht als &lt;code&gt;if&lt;/code&gt;-Statements im Code verstreut. Sie leben in einer einzigen JSON-Datei — 25 Einträge, jeder mit Code, Name, Icon, der Metrik, die er überwacht, und einem Zielwert. Nach jeder relevanten Aktion prüft ein Evaluator jede Definition gegen die Datenbank und speichert neu verdiente Badges. Einige Badges bilden Leitern und verweisen auf das nächste Level.&lt;/p&gt;
&lt;p&gt;Deklarativ schlägt hier imperativ aus einem einfachen Grund: Badge Nummer 26 hinzuzufügen bedeutet einen JSON-Eintrag zu ergänzen, nicht neue Logik zu schreiben. Das Design hinter den Streaks (Serien), der Pausen-Regel und der 90-Tage-Heatmap hat einen eigenen Artikel bekommen:
.&lt;/p&gt;
&lt;h2 id="spracheingabe-ganz-ohne-cloud-mikrofon"&gt;Spracheingabe, ganz ohne Cloud-Mikrofon&lt;/h2&gt;
&lt;p&gt;Der Mikrofon-Button wird von &lt;strong&gt;faster-whisper&lt;/strong&gt; befeuert — OpenAIs Whisper Spracherkennungsmodell, neu implementiert auf einer schnelleren Inference-Engine. Es läuft auf deiner CPU mit int8-Quantisierung (8-Bit-Zahlen statt 32-Bit: kleiner, schneller, genau genug). Kein Ton verlässt jemals deinen Rechner.&lt;/p&gt;
&lt;p&gt;Das Modell wird erst bei der ersten Transkription geladen (Lazy Loading), damit die App sofort startet. Und wenn faster-whisper gar nicht erst installiert ist, versteckt das Frontend den Mikrofon-Button einfach. Features sollten sanft degradieren, nicht explodieren.&lt;/p&gt;
&lt;h2 id="die-privacy-belege"&gt;Die Privacy-Belege&lt;/h2&gt;
&lt;p&gt;Die Serie begann mit einem Versprechen: &lt;em&gt;Nichts verlässt deinen Rechner.&lt;/em&gt; Versprechen sind billig — hier ist das Audit. Jedes Byte, das CogniVault speichert, und wo genau es lebt:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Daten&lt;/th&gt;
&lt;th&gt;Speicherort&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deine hochgeladenen Dateien&lt;/td&gt;
&lt;td&gt;Ordner &lt;code&gt;docs/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Die Originaldateien&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suchvektoren&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vector_store.faiss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FAISS Binär-Index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chunk-Text und Metadaten&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vector_store.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datei-zu-Kategorie-Zuweisung&lt;/td&gt;
&lt;td&gt;&lt;code&gt;categories.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chat-Sessions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chat_history.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sessions, Badges, Quizzes, Workshops, Decks, Mindmaps&lt;/td&gt;
&lt;td&gt;&lt;code&gt;progress.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ingestion Checkpoints&lt;/td&gt;
&lt;td&gt;PostgreSQL (lokales Docker-Volume)&lt;/td&gt;
&lt;td&gt;DBOS Systemtabellen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Die KI-Modelle selbst&lt;/td&gt;
&lt;td&gt;Ollamas lokaler Model-Store&lt;/td&gt;
&lt;td&gt;Modell-Gewichte&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Nichts aus dieser Tabelle liegt auf dem Computer von jemand anderem. Inference geht an &lt;code&gt;localhost&lt;/code&gt;. Embeddings gehen an &lt;code&gt;localhost&lt;/code&gt;. Der einzige ausgehende Request, den das Backend jemals macht, ist der URL-Import — und das nur auf deinen ausdrücklichen Wunsch und geschützt davor, private Adressen abzurufen. Die App zeigt diese Statistiken sogar live im &amp;ldquo;Privacy Vault Audit&amp;rdquo;-Panel an.&lt;/p&gt;
&lt;p&gt;Und weil Vertrauen mehr braucht als nur eine Tabelle: Das gesamte Backend ist von einer pytest-Suite abgedeckt, die du selbst ausführen kannst. Der Ansatz dazu ist in
dokumentiert.&lt;/p&gt;
&lt;h2 id="fazit-der-serie"&gt;Fazit der Serie&lt;/h2&gt;
&lt;p&gt;Vier Teile, eine Architektur:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;
&lt;/strong&gt; — Drei Prozesse, vier Schichten und ein Decoder-Ring für den Jargon&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;
&lt;/strong&gt; — Eine dauerhafte, formatbewusste Pipeline, die jedes Dokument in durchsuchbare Vektoren verwandelt&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;
&lt;/strong&gt; — Zwei Retriever, die gegenseitig ihre blinden Flecken abdecken, durch Rang fusioniert und von einem Agenten gesteuert werden&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teil 4&lt;/strong&gt; — Dieselbe Maschinerie, die Lernmaterialien generiert, Fortschritt ohne Buttons trackt, und eine Speichermap ohne Cloud-Abhängigkeiten&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Wenn es ein Hauptthema gibt, dann das: &lt;strong&gt;Langweilige, verifizierbare Entscheidungen im Dienste der Privatsphäre&lt;/strong&gt;. Exakte Suche statt Approximation. SQLite-Dateien statt gehosteter Datenbanken. Grammatikgebundenes JSON statt hoffnungsvollem Parsen. Soft-Deletes statt cleverer Index-Eingriffe. Jedes Puzzleteil ist etwas, das du öffnen, lesen und überprüfen kannst — und genau das ist der Punkt.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="anhang-abkürzungen-in-diesem-post"&gt;Anhang: Abkürzungen in diesem Post&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Abkürzung&lt;/th&gt;
&lt;th&gt;Volle Form&lt;/th&gt;
&lt;th&gt;Bedeutung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JSON&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JavaScript Object Notation&lt;/td&gt;
&lt;td&gt;Das strukturierte Format, das die Generatoren vom Modell erzwingen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQLite / SQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(SQL = Structured Query Language)&lt;/td&gt;
&lt;td&gt;Eine komplette relationale Datenbank, die in einer einzigen Datei lebt (&lt;code&gt;progress.db&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCQ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple-Choice Question&lt;/td&gt;
&lt;td&gt;Einer der beiden Quiz-Fragentypen (der andere ist Wahr/Falsch)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Central Processing Unit&lt;/td&gt;
&lt;td&gt;Hier läuft Whisper — keine Grafikkarte notwendig&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;int8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8-bit integer (Quantisierung)&lt;/td&gt;
&lt;td&gt;Modellgewichte als kleine Integer speichern: kleiner, schneller, genau genug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Künstliche Intelligenz (AI)&lt;/td&gt;
&lt;td&gt;Software, die Aufgaben ausführt, die normalerweise menschliche Intelligenz erfordern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application Programming Interface&lt;/td&gt;
&lt;td&gt;Die Endpunkte, die der Study Hub und das Dashboard aufrufen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FAISS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Facebook AI Similarity Search&lt;/td&gt;
&lt;td&gt;Der Vektor-Index in der Privacy-Tabelle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DBOS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Database-Oriented Operating System&lt;/td&gt;
&lt;td&gt;Die Durable-Workflow-Bibliothek, deren Checkpoints in PostgreSQL leben&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSRF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server-Side Request Forgery&lt;/td&gt;
&lt;td&gt;Die Art von Angriff, vor dem der URL-Importer schützt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PNG / PDF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Portable Network Graphics / Portable Document Format&lt;/td&gt;
&lt;td&gt;Zwei der Export-Formate für Mindmaps (neben Markdown)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SVG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Scalable Vector Graphics&lt;/td&gt;
&lt;td&gt;Das Zeichenformat im Browser, das hinter dem interaktiven Mindmap-Rendering steckt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Nächste Schritte:&lt;/strong&gt; Klone
und lies mit — die README skizziert die komplette Architektur, und jede Behauptung in dieser Serie kann direkt mit dem Code unter &lt;code&gt;backend/&lt;/code&gt; abgeglichen werden. Und falls du Lust auf die Deep-Dive-Versionen dieser Themen hast: Die
knüpft genau da an, wo dieser Rundgang endet.&lt;/p&gt;</description></item><item><title>Teil 7 · Gamifying Learning: 25 Badges, Idle-Gap Sessions und eine 90-Tage-Heatmap</title><link>https://aretascodes.dev/de/blog/gamifying-learning-badges-heatmap/</link><pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate><guid>https://aretascodes.dev/de/blog/gamifying-learning-badges-heatmap/</guid><description>
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Teil einer Serie über die Entwicklung von
. Zuvor:
.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;Alle Abkürzungen werden im Anhang unten auf der Seite vollständig erklärt.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ich habe acht Jahre lang ICT an weiterführenden Schulen unterrichtet, bevor ich zum Full-Stack-Developer gewechselt bin. Die zuverlässigste Lektion aus dieser Zeit war unangenehm einfach: &lt;strong&gt;Schüler, die beständig da waren, haben gelernt. Die anderen nicht.&lt;/strong&gt; Talent, Vorwissen, sogar die Motivation an einem bestimmten Tag – all das war der bloßen Anwesenheit nachgelagert.&lt;/p&gt;
&lt;p&gt;Der &amp;ldquo;Dashboard&amp;rdquo;-Tab in CogniVault ist ein kleiner Versuch, genau das zu fördern. Es ist keine Duolingo-Streak-Panikmaschine. Es sind drei Dinge:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hero-Statistiken&lt;/strong&gt; – gesamte Lernzeit, gesamte Sessions, aktueller Streak.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;25 Erfolgs-Badges&lt;/strong&gt; – automatisch erfasst über Chat, Quizzes, Workshops, Flashcards und Mindmaps hinweg.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eine 90-Tage-Aktivitäts-Heatmap&lt;/strong&gt; – im GitHub-Stil, mit fünf lila Intensitätsstufen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Das Ganze besteht nur aus einem kleinen Satz von SQLite-Tabellen und ein paar React-Komponenten. Der interessante Teil ist aber nicht der Code – es sind die Designentscheidungen.&lt;/p&gt;
&lt;h2 id="idle-gap-sessions"&gt;Idle-Gap Sessions&lt;/h2&gt;
&lt;p&gt;Die schwerste Frage klang eigentlich am einfachsten: &lt;strong&gt;Was zählt als eine Lern-Session?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Die naive Antwort ist: &amp;ldquo;Alles, was zwischen dem Öffnen und Schließen der App passiert.&amp;rdquo; Aber das ist falsch. Leute lassen Tabs offen. Leute gehen für eine Stunde weg und kommen dann wieder. Leute öffnen die App um 9 Uhr morgens, machen nichts und schauen um 14 Uhr wieder rein.&lt;/p&gt;
&lt;p&gt;Die Antwort, bei der ich gelandet bin: Eine Session endet, wenn du &lt;strong&gt;15 Minuten lang inaktiv&lt;/strong&gt; warst. Stellst du eine Frage und bist dann 16 Minuten inaktiv – das ist eine Session. Kommst du zurück und stellst eine weitere Frage – beginnt eine neue. Der Schwellenwert ist über &lt;code&gt;STUDY_SESSION_IDLE_GAP_SECONDS=900&lt;/code&gt; konfigurierbar.&lt;/p&gt;
&lt;p&gt;Die Uhr richtet sich nach den &lt;strong&gt;Chat-Nachrichten&lt;/strong&gt; – dem konversationellen Kern des Lernens in CogniVault. Jede Nachricht verlängert entweder die offene Session (indem sie den &lt;code&gt;ended_at&lt;/code&gt;-Zeitstempel und die Nachrichtenanzahl erhöht) oder, falls die Pause seit der letzten Aktivität den Schwellenwert überschreitet, schließt sie diese implizit ab und eröffnet eine neue:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Simplified from backend/services/progress_tracker.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idle_gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;most_recent_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ended_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;idle_gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ended_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# same session continues&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;open_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ended_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# new session begins&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Zwei Schreibvorgänge pro Nachricht. Die Dauer einer Session ist &lt;code&gt;ended_at - started_at&lt;/code&gt;, was bedeutet, dass &amp;ldquo;Gesamtzeit&amp;rdquo; die &lt;em&gt;aktive&lt;/em&gt; Zeit widerspiegelt, nicht &amp;ldquo;hatte einen Tab offen&amp;rdquo;. Das ist die einzige Zahl, die wirklich etwas aussagt. (Aktionen im Study Hub – Quizversuche, Karteikarten umdrehen, Mindmap-Exporte – werden als eigene Events erfasst und fließen in die Badge-Metriken unten ein; die Session-Uhr selbst bleibt nachrichtengetrieben und ehrlich.)&lt;/p&gt;
&lt;h2 id="25-badges-nicht-250"&gt;25 Badges, nicht 250&lt;/h2&gt;
&lt;p&gt;Die meisten gamifizierten Apps überfluten dich regelrecht mit Erfolgen. Es gibt einen Grund dafür: mehr Badges, mehr Dopamin, mehr täglich aktive Nutzer. Der Preis dafür ist, dass jedes Badge weniger bedeutet – irgendwann wird die ganze Schicht nur noch zur Tapete.&lt;/p&gt;
&lt;p&gt;Ich habe CogniVault auf &lt;strong&gt;25&lt;/strong&gt; limitiert, aufgeteilt auf die fünf Aktivitätsbereiche:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10 für &lt;strong&gt;Chat &amp;amp; Lerngewohnheiten&lt;/strong&gt; (erste Frage, 10 Nachrichten an einem Tag, 100 insgesamt, eine Stunde Gesamtlernzeit, 3- und 7-Tage-Streaks, eine 30-minütige Deep-Dive-Session, Night-Owl- und Early-Bird-Sessions, erste Nutzung des Scope-Filters)&lt;/li&gt;
&lt;li&gt;4 für &lt;strong&gt;Quizzes&lt;/strong&gt; (erstes Quiz, perfektes Ergebnis, Bestehen auf fortgeschrittenem Schwierigkeitsgrad, 10 Quizzes)&lt;/li&gt;
&lt;li&gt;4 für &lt;strong&gt;Workshops&lt;/strong&gt; (erste Outline, erste abgeschlossene Lektion, erster abgeschlossener Workshop, 5 abgeschlossen)&lt;/li&gt;
&lt;li&gt;4 für &lt;strong&gt;Flashcards&lt;/strong&gt; (erstes Deck, 50 umgedrehte Karten, ein Deck komplett gemeistert, 5 Decks)&lt;/li&gt;
&lt;li&gt;3 für &lt;strong&gt;Mindmaps&lt;/strong&gt; (erste Mindmap, erster Export, 5 Mindmaps)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jedes Badge hat ein einzeiliges Freischaltkriterium, das bei relevanten Events automatisch ausgewertet wird. Nichts Manuelles, nichts, was der Nutzer &amp;ldquo;einfordern&amp;rdquo; muss. Sie tauchen einfach auf.&lt;/p&gt;
&lt;p&gt;Und die Definitionen sind gar kein Code – sie sind &lt;strong&gt;Daten&lt;/strong&gt;. Alle 25 leben in einer JSON-Datei. Jeder Eintrag benennt die Metrik, die er beobachtet, und das zu erreichende Ziel:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card_reviewer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Card Reviewer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;icon&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;🃏&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;metric&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;total_card_flips&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ein einzelner Evaluator liest die aktuellen Statistiken aus, vergleicht jede Definition mit ihrer Metrik, gleicht sie mit bereits verdienten Badges ab und fügt neue Freischaltungen in die &lt;code&gt;progress.db&lt;/code&gt; ein. Ein 26. Badge hinzuzufügen, bedeutet, einen JSON-Eintrag hinzuzufügen, nicht neue Logik zu schreiben. Mehrere Badges bilden Leitern – jedes weiß, welches Badge das &amp;ldquo;nächste Level&amp;rdquo; ist, was den Schubs in der Detailansicht zum nächsten Ziel antreibt.&lt;/p&gt;
&lt;h2 id="die-heatmap"&gt;Die Heatmap&lt;/h2&gt;
&lt;p&gt;Auf die 90-Tage-Heatmap bin ich am meisten stolz, und sie ist gleichzeitig das Einfachste. Es ist ein 13×7-Raster aus Zellen, eine pro Tag, farblich markiert nach der Gesamtlernzeit an diesem Tag.&lt;/p&gt;
&lt;p&gt;Fünf Intensitätsstufen:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;level 0 — no activity
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;level 1 — under 15 minutes (a quick check-in)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;level 2 — 15-60 minutes (a focused session)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;level 3 — 1-3 hours (substantial study)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;level 4 — 3+ hours (a marathon)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Die Daten sind konzeptionell eine einzige Aggregation über die Sessions-Tabelle:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ended_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;study_sessions&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;now&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-90 days&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;day&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Das Backend füllt die fehlenden Tage mit Nullen auf, sodass das Frontend immer genau 90 Einträge erhält. Eine kleine clientseitige Funktion ordnet die Tagessummen in die fünf Level ein. Wenn du auf eine beliebige Zelle klickst, öffnet sich ein &lt;code&gt;DayDetailModal&lt;/code&gt; mit den Zahlen dieses Tages – Lernzeit, Sessions, Nachrichten – sowie allen Badges, die an diesem Tag verdient wurden.&lt;/p&gt;
&lt;p&gt;Der Grund, warum ich diese Komponente liebe: Sie macht die &lt;em&gt;Textur&lt;/em&gt; einer Lerngewohnheit sichtbar. Streaks sind toll, aber ein Streak ist nur eine Zahl. Eine Heatmap zeigt dir, dass du am Wochenende härter lernst, oder dass du den ganzen Monat über langsam abgebaut hast, oder dass die Lücke zwischen deinem letzten &amp;ldquo;Level 4-Tag&amp;rdquo; und heute größer ist, als du dachtest. Sie spiegelt etwas wider, worauf der Nutzer reagieren kann.&lt;/p&gt;
&lt;h2 id="was-ich-bewusst-weggelassen-habe"&gt;Was ich bewusst weggelassen habe&lt;/h2&gt;
&lt;p&gt;Drei Dinge, die du in den meisten gamifizierten Apps finden würdest, fehlen in CogniVault absichtlich:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Streak-Panik.&lt;/strong&gt; Kein &amp;ldquo;Dein Streak ist in Gefahr!&amp;quot;-Popup. Keine Regeln für Streak-Freezes. Keine gelben Ausrufezeichen. Der Streak wird angezeigt – das ist der gesamte Feedback-Loop. Wenn ein Nutzer seinen Streak bricht, dann bricht er ihn eben. Erwachsene brauchen keine Shaming-UX.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Leaderboards.&lt;/strong&gt; Das ist eine lokale Einzelnutzer-App. Es gibt keinen globalen Vergleich. (Und den sollte es auch nicht geben – Leaderboards optimieren beim Lernen das Falsche.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Konfetti, Fanfaren, Push-Benachrichtigungen.&lt;/strong&gt; Ein neu verdientes Badge taucht auf dem Quiz-Ergebnisbildschirm und im Dashboard-Raster auf. Das ist die ganze Feier. Alles, was größer ist, stiehlt dem Nutzer die Aufmerksamkeit zum Nutzen der App, nicht zu seinem eigenen.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Das allgemeine Prinzip: &lt;strong&gt;Miss, was wichtig ist, mach es sichtbar, aber ohne zu nerven.&lt;/strong&gt; Registriere, dass du wiedergekommen bist. Spiegele dir das wider. Tu nicht so, als ob es dich mehr interessiert, als es das tatsächlich tut.&lt;/p&gt;
&lt;h2 id="was-das-dashboard-nicht-zu-optimieren-versucht"&gt;Was das Dashboard &lt;em&gt;nicht&lt;/em&gt; zu optimieren versucht&lt;/h2&gt;
&lt;p&gt;Eine häufige Falle bei diesen Dashboards ist die umgekehrte Kausalität: Der Nutzer fängt an, die Metrik zu spielen, anstatt die eigentliche Sache zu tun. Ein täglicher Fragenzähler zum Beispiel führt dazu, dass Nutzer eine irrelevante Frage pro Tag stellen, um ihren Streak am Leben zu halten.&lt;/p&gt;
&lt;p&gt;Deshalb ist die Hürde absichtlich an genau einer Stelle sehr niedrig und überall sonst hoch. Es gibt &lt;em&gt;ein&lt;/em&gt; Badge, das keinen Aufwand erfordert – &amp;ldquo;Erste Frage&amp;rdquo;, das man für die allererste Nachricht bekommt –, weil jedes Spiel eine Einstiegsrampe braucht, die beweist, dass das System funktioniert. Danach werden die Metriken schwer zu manipulieren, ohne die eigentliche Arbeit zu tun:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Gesamtlernzeit&lt;/strong&gt; – sammelt sich nur während aktiver Nutzung an, mit Idle-Gap-Abschaltungen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sessions&lt;/strong&gt; – um mehr hinzuzufügen, muss man tatsächlich separate Arbeitsphasen starten.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Badges&lt;/strong&gt; – fast alle erfordern Tiefe (100 Nachrichten, ein Quiz meistern, ein Deck perfekt beherrschen, 5 Workshops abschließen), nicht nur oberflächliches Antippen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heatmap-Intensität&lt;/strong&gt; – erfordert anhaltendes Engagement an einem bestimmten Tag.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="implementierung-bewusst-klein"&gt;Implementierung: bewusst klein&lt;/h2&gt;
&lt;p&gt;Der Gamification-Kern besteht aus drei SQLite-Tabellen –&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;study_sessions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ended_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;message_events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sent_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;had_scope_filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;had_attachments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;achievements_earned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;earned_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;— plus die JSON-Badge-Definitionen, ein Evaluator-Modul und eine Handvoll React-Komponenten (&lt;code&gt;SummaryCards&lt;/code&gt;, &lt;code&gt;AchievementGrid&lt;/code&gt;, &lt;code&gt;ActivityHeatmap&lt;/code&gt;, &lt;code&gt;DayDetailModal&lt;/code&gt;). Die gleiche &lt;code&gt;progress.db&lt;/code&gt;-Datei hat mittlerweile weitere Tabellen für die gespeicherten Quizzes, Workshops, Decks und Mindmaps des Study Hubs bekommen – aber die Badge-und-Session-Maschinerie selbst ist nur ein paar hundert Zeilen lang geblieben.&lt;/p&gt;
&lt;p&gt;Daran ist nichts Ausgefallenes. Das Dashboard funktioniert, weil die &lt;em&gt;Designentscheidungen&lt;/em&gt; richtig sind, nicht weil die Implementierung raffiniert ist.&lt;/p&gt;
&lt;h2 id="fazit"&gt;Fazit&lt;/h2&gt;
&lt;p&gt;Wenn du ein Lern-Tool baust – oder ein beliebiges Tool, das von den Gewohnheiten der Nutzer lebt –, dann setze Gamification &lt;em&gt;bewusst&lt;/em&gt; ein. Wähle die Metriken, die das widerspiegeln, was du wirklich fördern willst. Begrenze die Anzahl der Erfolge. Verzichte auf die Streak-Panik-UX. Mach die Textur der Nutzung sichtbar, ohne dass die App verzweifelt wirkt.&lt;/p&gt;
&lt;p&gt;Oder direkter gesagt: Bau kein Duolingo. Bau ein Dashboard, auf das der Nutzer ab und zu schaut und das er dann wieder schließt, mit dem leichten Gefühl, weitermachen zu wollen. Das ist der ganze Job.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="anhang-abkürzungen-in-diesem-beitrag"&gt;Anhang: Abkürzungen in diesem Beitrag&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Abkürzung&lt;/th&gt;
&lt;th&gt;Vollform&lt;/th&gt;
&lt;th&gt;Bedeutung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UX&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User Experience&lt;/td&gt;
&lt;td&gt;Wie sich das Produkt anfühlt – genau das, was durch Streak-Panik-Mechaniken geopfert wird&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ICT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Information and Communications Technology&lt;/td&gt;
&lt;td&gt;Das Fach, das ich acht Jahre lang unterrichtet habe, bevor ich zum Full-Stack gewechselt bin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(SQL = Structured Query Language)&lt;/td&gt;
&lt;td&gt;Eine komplette relationale Datenbank in einer einzigen Datei, &lt;code&gt;progress.db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JSON&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JavaScript Object Notation&lt;/td&gt;
&lt;td&gt;Das Datenformat, in dem die 25 Badge-Definitionen liegen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User Interface&lt;/td&gt;
&lt;td&gt;Die Dashboard-Oberfläche: Statistiken, Raster, Heatmap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Als Nächstes:&lt;/strong&gt;
.&lt;/p&gt;</description></item></channel></rss>