ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Prüfungsreif in kleinen Schritten: Fragen aus Ausbildungsordnung und Rahmenlehrplan, mit Quelle. Demo-Zugang für Bildungsträger.
Translation pending
EducationTypeScript · JavaScriptGitHub @siinanXDOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Lern-App, die aus einem Schlagwort (Pilot: Maschinen- und Anlagenführer) einen Kurs aus amtlichen Quellen erzeugt und aktuell hält.Read the full README ↗
A learning app for German vocational training that turns a keyword such as a trade name into a course built from official sources, checks every question with an LLM judge, and publishes only what passes.
Live: content-agent-ashen-nu.vercel.app · sample data without an account: /lernpfad?demo=1
Apprentices preparing for their exams need material that follows the official framework curriculum, and generic AI text invents facts without saying where they come from. This app runs a pipeline of research, plan, generate (Claude) and evaluate (an OpenAI judge), and a content guard blocks publishing when a unit lacks a source with retrieval date or the AI label. The repo builds itself: an agent loop picks Linear issues, opens pull requests and merges them only after lint, tests, build, axe and Lighthouse pass, observed from the separate Leitstand.
Runtime and API details: docs/ARCHITECTURE.md. Pilot course: Maschinen- und Anlagenführer (MAF); a second course for Industriekaufleute is generated by the same pipeline.
git clone https://github.com/siinanXD/Content-Agent-Lernapp && cd Content-Agent-Lernapp
npm ci
npm run dev # http://127.0.0.1:43123, works without keys (in-memory mock store)
content_versions), so a bad run can be rolled back.No open-source license: all rights reserved (owner decision, SIN-752).
Lern-App, die aus einem Schlagwort (Pilot: Maschinen- und Anlagenführer) einen Kurs aus amtlichen Quellen erzeugt und aktuell hält. Die folgenden Abschnitte beschreiben Entwicklung und Betrieb.
| Layer | Choice |
|---|---|
| App | Next.js 16 + React 19 + TypeScript + Tailwind 4 |
| Hosting | Vercel |
| Persistence | Supabase EU (AP-17) with in-memory mock fallback |
| Quality / tracing | Langfuse Cloud EU — JS/TS SDK v5 / platform v4 OTEL (SIN-197) |
| Ops scaffold | Hermes weekly source check + Telegram (AP-10/AP-16) |
| Models | Claude for generate (Batch); OpenAI mini as judge (D-06/D-07) |
docs/ops/SIN-438-grants.md — Tabellenrechte prüfen, transaktionaler Migrationstest und Rückkehrverfahren.
Verbrauch von Bau-/Reparaturläufen wird privat in Linear gespeichert; öffentliche PR-Steckbriefe und Tages-Updates zeigen diese Beträge nicht. Der Wochenleser liest private Kommentare mit Seitenabruf und dedupliziert Laufkennungen; historische PR-Marker bleiben vorübergehend lesbar.
docs/PRODUCT.md — Konzept und Bauplan AP-00–AP-12
docs/DECISIONS.md — Architekturentscheidungen mit Links
docs/ARCHITECTURE.md — Runtime- und API-Überblick
AGENTS.md — Entscheidungs- und Stopp-Regeln
docs/LANDKARTE.md — Repo-Landkarte: wo liegt was, Befehle, Konventionen (SIN-320)
docs/autonomy/groessen.md — Größen der Issues, Modell und Runden je Größe, Bündeln, Verbrauch (SIN-320)
docs/content/README.md — Curriculum-Maps (AP-13): MAF in allen fünf Schwerpunkten und Industriekaufleute 2024
docs/content/DIDAKTIK.md — Didaktik-Vorgabe (AP-18): Schablone je Einheit, Fragestufen, Wiederholung, Prüfungsmodus, Bilder
npm install
npm run dev
Dev server: http://127.0.0.1:43123 (npm run dev → port 43123).
Playable path (Figma approved by Sinan 2026-10-02; Didaktik D-31):
/ Startseite für Bildungsträger (Scroll-Story), /willkommen App-Einstieg/start — Schlagwort + Lernvariante → Kurs erzeugen/lernpfad — Module/Blöcke, Wiederholung, Prüfungsmodus/einheit/unit-03 — sections + alle 5 Fragetypen (+ Bildfragen)/wiederholung — Leitner 1/3/7/14/pruefung — schriftliche Teile aus exam.gradedParts (MAF PT/PP/WiSo)/ergebnis — Punkte + Ampel je Gebiet/profil — Fortschritt, Stapelgröße, PrüfungsreifeBeispielmodus ohne Konto (SIN-408): /lernpfad?demo=1 schaltet für die Browser-Sitzung erfundene Beispieldaten ein (Ergebnis, Wiederholung, Profil, Serie). Es wird nichts gespeichert oder gesendet; ?demo=0 beendet ihn. /quellen zeigt die Quelle je Einheit mit Abrufdatum. Gruppenansicht: /ausbilder?demo=1 (SIN-385). Ausbilder (SIN-415): /ausbilder/zugang?code=… löst den Einladungslink ein, /ausbilder/gruppen zeigt mehrere Gruppen und die Zugänge „x von y vergeben“, /ausbilder/archiv die archivierten Gruppen (Archivieren statt Löschen). Organisation, Kontingent und Einladungslinks legt der Admin unter /admin an (SIN-416): Reiter Organisationen (Zugänge „x von y“, Status, Link erzeugen, neue Organisation), Anfragen (demo_requests) und Alle Kurse. Die Rolle admin steht in app_metadata und wird nur mit dem Service-Role-Key gesetzt (supabase.auth.admin.updateUserById(id, { app_metadata: { role: "admin" } })); der Server prüft sie bei jeder Anfrage.
Design source: Figma · tokens in docs/design/. Phase-A SVGs: npm run content:mermaid.
npm run check:fast # Lint, Typecheck, Unit-Tests ohne Keys (vor jedem Push)
npm test
npm run content:mermaid
npm run test:a11y
npm run test:lighthouse # app must be running on :43123 (or add -- --serve)
npm run hermes:dry-run # AP-10 scaffold (no live Telegram)
npm run pilot:maf # AP-11 seed/fixture pipeline (app on :43123)
npm run supabase:verify # when SUPABASE_* present
npm run build
npm run lint
A11y gates (axe über alle Routen, Tastatur, Lighthouse a11y ≥ 0.9) laufen in den CI-Jobs e2e und perf (.github/workflows/ci.yml, gemeldet über den Sammel-Check build) und dürfen nicht abgeschaltet werden.
Quelle: docs/diagramme/pipeline.mmd, Nutzerwege: docs/diagramme/nutzerwege.mmd. Der Block unten ist eine Kopie von pipeline.mmd (ein Test prüft das). FigJam wird in einer Claude-Sitzung aus den .mmd nachgezogen, das Tages-Update meldet „Diagramme geändert“.
%% Pipeline-Ablauf des Loops, Stand main 09.10. (SIN-376). Quelle für das FigJam-Board „Pipeline-Ablauf“.
%% Ändern sich .github/workflows/ oder scripts/autonomy/, diese Datei im selben PR anpassen.
flowchart TD
subgraph Planung
Radar["Trend-Radar (sonntags)"] --> Planer
Planer["Planer (planner.yml, sonntags; offene Fertig-Liste sperrt Ideen)"] --> Schlange["Linear-Schlange"]
end
Schlange --> Dispatcher["Dispatcher (dispatch.yml): dringend, dann v1, höchstens 1 Idee pro Woche; sofort nach Merge, höchstens 2 parallel"]
Dispatcher --> Worker["Worker (worker.yml, Claude; bei offenen Haken nur daran, derselbe Haken-Stand höchstens 2-mal, sonst needs-human, SIN-795)"]
Worker --> PR["Pull Request mit Steckbrief (Bereiche: Frontend, Backend, Datenbank, Infra, Deploy)"]
PR --> Anstoss["pruefer-anstoss.yml: Anstoß an den Leitstand-Prüfer; merge-gate verlangt Beleg für den Head (SIN-1001)"]
subgraph Prüfung
PR --> CI["CI: Lint, Typecheck, Tests, Build, axe, Layout-Test, Lighthouse, Leistungsbudget"]
PR -- "Frontend geändert" --> Sicht["KI-Sichtprüfung gegen Figma: Kommentar, kein Gate (sichtpruefung.yml, SIN-523)"]
PR -- "Vorschau fertig" --> PRC["PR-Crawler: nur geänderte Seiten, auch eingeloggt, Bild oder Video im PR (pr-crawler.yml, SIN-906, SIN-914)"]
PR -- "Prompt, Richter oder Modell geändert" --> Gold["Goldset-Prüfung: Basis gegen PR, über 3 Prozentpunkte schlechter = rot (goldset.yml, SIN-629)"]
PR --> CodeQL["CodeQL"]
PR --> Review["Review-Agent (zweites Modell)"]
CI --> Gate{"pr-gate: Risiko"}
CodeQL --> Gate
Review --> Gate
end
CI -- rot --> Repair["repair.yml: bis 3 Runden, ohne Commit startet ci neu, Claude-Fehler = sofort needs-human"]
Repair --> CI
Repair -- "nach Runde 3" --> Mensch["needs-human, Stopp"]
Gate -- "risk:high" --> Freigabe["Label freigegeben (Sinan)"]
Freigabe --> Merge
Gate -- "risk:medium (Aufbauphase: ohne Frist)" --> Merge
Gate -- "risk:low" --> Merge["Auto-Merge (Squash)"]
Merge --> Haken{"Alle Fertig-Kriterien belegt? (SIN-779)"}
Haken -- ja --> Done["Linear: Done"]
Haken -- nein --> Offen["Issue bleibt offen, Kommentar nennt offene Haken"]
Merge --> Dispatcher
Merge --> Migr["Migrationen anwenden: additiv automatisch, sonst Sinan-Aufgabe (SIN-374, SIN-451)"]
Merge --> Bruch{"gate-bruch-Erkennung"}
Bruch -- "CI auf main rot" --> Revert["revert-guard: Revert-PR nur wenn Schritt davor grün (SIN-502)"]
Revert -- "Revert-PR gemergt" --> LinearTodo["Linear: Issue auf Todo, 2. Rücknahme needs-human (SIN-492)"]
Migr --> Deploy["production-deploy (höchstens 1× pro Stunde)"]
Deploy --> Smoke["Smoke-Test"]
Smoke -- rot --> Revert
Smoke -- grün --> Live["Live-Check (nach-deploy.yml)"]
Live -. "nachts / montags" .-> Testnutzer["Testnutzer-Agent: Crawler (auch eingeloggt) und 3 Personas, Funde werden Issues (testnutzer.yml, SIN-640, SIN-914)"]
Deploy -- "grün, höchstens 1× pro Tag" --> Video["Tutorial-Video (video.yml): Freigabe nötig (SIN-483)"]
Deploy -- "nur von Hand, freigabe ja" --> Werbeclip["KI-Werbeclip (werbeclip.yml): bis 5 €, Freigabe nötig (SIN-520)"]
Kurs["Kurs veröffentlicht"] -- "von Hand" --> Marketing["Werbetexte (marketing.yml): Prüfung, dann Freigabe nötig (SIN-484)"]
Live --> Waechter["Wächter / Loop-Status (status.yml)"]
Waechter -- "neuer App-Code" --> Deploy
Backup["Sicherung (backup.yml) nachts, Restore-Test (restore-test.yml) monatlich in wegwerfbarer Supabase (SIN-337)"] --> Waechter
Waechter --> Update["Tages-Update (digest.yml, 10:00 und 20:00)"]
Waechter --> Leitstand["Leitstand: Start/Ende jedes Schritts und Schnappschuss in Supabase (SIN-303)"]
Worker -.-> Leitstand
subgraph Projektwaechter["Projekt-Wächter (Leitstand-Repo, waechter.yml, stündlich, alle Projekte In Progress mit GitHub-Link)"]
PW["Projekt-Wächter"] --> PWS["Schlange leer: Backlog rückt nach, sonst Planungs-Issue"]
PW --> PWH["Hänger: über 6 h In Progress ohne PR, nur Label claude: Todo, beim 2. Mal needs-human; Worker 2-mal ohne PR: needs-human; Claude-Sofortabbruch zählt als Limit-Pause; 3 Fehlläufe desselben Auftrags in 60 Min ohne PR: Pause 60 Min (SIN-548)"]
PW --> PWU["Projekt-Update in Linear, einmal am Tag"]
PW --> PWR["Roter PR, 30 Min ohne Commit: @claude, Runde zählt mit"]
end
PWS --> Schlange
PWR --> Repair
subgraph Inhalte
Fabrik["Content-Fabrik (content-grow.yml, alle 2 h solange Content-Punkte der Fertig-Liste offen sind (sonst Mo/Mi/Fr), Cron und Anstoß durch den Dispatcher (Ereignis fabrik-anstoss), jeder Lauf im Statusprotokoll, bei API-Limit pausiert und sinan-Issue, am Ende Kostenbericht)"] --> Bewertung["Bewertungslauf"]
Bewertung --> Update
Bewertung --> Freigabe["Kurs veröffentlichen (content-publish.yml, von Hand, Prüfung vor published, danach Live-Check)"]
end
| Was | Befehl / Workflow | Wirkung |
|---|---|---|
| Leistungsbudget | npm run build && npm run perf:budget -- --serve · Grenzen in performance-budget.json | LCP (höchstens 2,5 s), CLS, TBT und JS-Größe je Route (Handy, Median aus 5 Läufen; --details zeigt das LCP-Element, --shard 1/2 misst nur einen Teil der Routen; CI nutzt zwei Teile parallel). Überschreitung macht build rot und blockiert den Merge. |
| API-Tests (SIN-538) | npm run build && npm run api:test -- --serve | Newman führt docs/api/postman-collection.json gegen die App aus (Health, Pipeline-Kette, 400/404); jede Prüfung zu Status und Antwortform muss grün sein. |
| Bildvergleich | npm run visual -- --update-snapshots, danach npm run visual · Workflow visual.yml | Handy- und Desktop-Bilder aller Seiten gegen main. Nur Hinweis (Lauf-Bericht, Artefakt bildvergleich), kein Gate; die Referenz kommt immer frisch aus main. Läuft nur, wenn der PR src/app, src/components oder Styles ändert, sonst grün (SIN-648). |
| Layout-Test (Gate, SIN-523) | npx playwright test e2e/layout.spec.ts · läuft im CI-Job e2e (Teil von build) | Alle Seiten aus e2e/routes.ts auf Handy (390×844) und Desktop (1280×800): keine überlappenden Knöpfe oder Textblöcke, kein waagerechtes Scrollen, kein abgeschnittener Text, Klickziele ≥ 44 px, nichts außerhalb des Bildes oder hinter fixen Leisten. Fehler nennen Seite, Ansicht und beide Elemente; das Artefakt playwright-results enthält das Bild mit roten Kästen. Gewollte Überlagerung: data-layout-ok="Begründung" am Element. |
| Goldset-Prüfung (SIN-629) | Workflow goldset.yml · lokal npm run quality:regression -- messen --out m.json · braucht OPENAI_API_KEY | Bei PRs, die Generator-Prompt, Richter oder Modellwahl ändern, misst der Job Richter (Erstquote auf 30 festen Einträgen, Urteil wie erwartet auf 70) und feste Prüfsätze (Quelle passt zum Beruf, keine Prüfhinweise, Elektro-Hinweis) mit dem Stand von main und dem PR. Über 3 Prozentpunkte schlechter oder Prüfsatz verletzt = rot, Ergebnis als PR-Kommentar. Wenige Cent je Lauf (Deckel 2 €), Ledger goldset-regression, Trace in Langfuse. |
| Testnutzer-Agent (SIN-640) | Workflow (Crawler nachts, Personas montags, auch von Hand mit , , ) · |
Ops / pilot docs: docs/ops/HERMES.md · docs/pilot/MAF-PILOT.md · docs/learning/LOOP.md · docs/ops/SUPABASE.md.
First live course slice after the curriculum map: modules M0, LF1, LF2, PA (~280 units), generate with Claude Batch, judge with OpenAI mini, publish only through the quality gate. Tracked in SIN-193. Costs and scores should land in Langfuse Cloud EU — prefer completing the v4 cutover (SIN-197) before relying on dashboards for that run.
npm run ap15:phase-a:dry
npm run ap15:phase-a
Artifacts land in docs/ops/AP15-PHASE-A.md and docs/ops/ap15-runs/.
Vercel project pointed at this repo. Empty/scaffold build must succeed (AP-01).
Git deploys are off (git.deploymentEnabled: false in vercel.json, SIN-309): neither main nor PR branches create Vercel deployments, so nothing counts against the Hobby limit of 100/day. Production is deployed by the production-deploy workflow (vercel deploy --prod via CLI, SIN-332; needs secret VERCEL_TOKEN and variable VERCEL_PROJECT_ID, plus VERCEL_TEAM_ID for team accounts). The status workflow starts it: at most once per hour, only when app code changed, and only one attempt per commit (a CANCELED/ERROR attempt is reported as "Production hängt" instead of being retried). If Production cannot be read from Vercel, or is more than 3 h behind main, the Loop-Status shows a red item under "Braucht dich" and sends Telegram (TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, optional). The deploy hook from SIN-266 is retired (secret VERCEL_DEPLOY_HOOK_PROD is no longer used).
Functions run in Frankfurt (regions: ["fra1"] in vercel.json). Right after each Production deploy the same production-deploy run checks /api/health and /api/learner/phase-a for 200 and opens a revert PR otherwise (SIN-308). URL env vars (e.g. LANGFUSE_BASE_URL) are cleaned of quotes and validated in src/lib/env.ts; an invalid value disables the feature and logs a warning instead of crashing.
Live-Check (SIN-319). After each Production deploy (and nightly at 21:05 Berlin time) the workflow nach-deploy checks the live app against docs/ops/live-checkliste.md: API routes (scripts/autonomy/live-check.mjs), all pages on phone and desktop, the unit flow with all 5 question types, exam mode, offline mode, service worker, axe and a Lighthouse short run (live/live.spec.ts). It only reads or uses test data (write endpoints are stubbed, test id livecheck-<time>). Result: artifact live-check (checklist + screenshots) and the line "Live-Check hh:mm: n/n grün" on the status page and in the daily update. If it is red after a deploy, revert-guard opens a revert PR. Every new page or feature goes into the checklist (a unit test keeps list and checks in sync). Locally: npm run build && npm start -- --port 43123, then npm run live:local. Against another URL: PLAYWRIGHT_BASE_URL=<url> npm run live:browser and npm run live:api -- --base <url>, or run the workflow by hand with a URL.
Tutorial-Video (SIN-483). Der Workflow video nimmt den Klickweg Start → Beruf wählen → Lernpfad → erste Einheit → Ergebnis gegen die Production-URL auf (video/aufnahme.spec.ts, playwright.video.config.ts), schneidet ihn mit Revideo (video/schnitt/, 1920×1080: Titel, Handy-Aufnahme 390×844 im Rahmen links, Schritt-Titel und Text in der rechten Spalte, nie über der App, Logo, Hinweis „Lerninhalte mit KI erstellt“, Farben aus docs/design/tokens.json) und legt das MP4 in Supabase Storage (Bucket werbemittel) und als Zeile in werbemittel ab, Status wartet_auf_freigabe. Kein KI-Videomodell, 0 € API, Länge unter 90 Sekunden (sonst Fehler), ohne Ton. Auslöser: von Hand oder nach erfolgreichem production-deploy, höchstens 1 Lauf pro Tag. Schreibende Endpunkte sind abgefangen, es gibt keine Personendaten. Je Schritt entsteht ein Standbild aus dem fertigen MP4 (Artefakt video-standbilder, SIN-522). Hilfen: scripts/autonomy/video.mjs. Ohne SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY bleibt das Video ein Artefakt (tutorial-video).
KI-Werbeclips (SIN-485). node scripts/autonomy/werbeclip.mjs plan zeigt als Trockenlauf geplante Clips (3 Entwürfe, 1 Final) und Kostenschätzung ohne API-Aufruf. lauf (Workflow werbeclip, nur von Hand mit Eingabe freigabe: ja, Secrets HIGGSFIELD_KEY_ID und HIGGSFIELD_KEY_SECRET) erzeugt die Clips über die Higgsfield-API, fragt den Status ab (Backoff; failed/nsfw werden gemeldet), lädt das Ergebnis herunter, blendet „Mit KI erstellt“ ein (Revideo, video/schnitt/) und legt es in Storage ab. Vor jedem Aufruf wird der Deckel von 5 € pro Produkt geprüft; Modelle, Preise und Endpunkte stehen in docs/autonomy/higgsfield.json. Ergebnis: Zeile in werbemittel mit Kosten, KI-Kennzeichnung und Status wartet_auf_freigabe.
Werbetexte (SIN-484). Der Workflow marketing (von Hand, Eingabe Kurs-Stichwort, z. B. MAF) liest die Zahlen eines veröffentlichten Kurses aus der Datenbank (Einheiten, Minuten, Quellen, Fragen) und schreibt 3 Varianten: LinkedIn-Beitrag, Kurztext für die Website, E-Mail an Bildungsträger. Zielgruppe sind Bildungsträger und Ausbilder (FernUSG). Modell: claude-haiku-5-5, bei verworfenem Text einmal claude-sonnet-5-5, Deckel 1 € pro Lauf. scripts/autonomy/marketing.mjs prüft jeden Text: Hinweis „mit KI erstellt“ nötig, keine Garantien, keine IHK-Aufgaben oder -Logos, keine Zahl außerhalb der Datenbank. Durchgefallene Texte werden verworfen; bestehen nicht alle 3, wird nichts gespeichert. Ablage in werbemittel (Status wartet_auf_freigabe), veröffentlicht wird nie automatisch. Braucht ANTHROPIC_API_KEY, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY.
Set in Vercel / local .env.local (never commit): Anthropic, OpenAI, Langfuse (EU), Supabase (EU). Work without keys uses mocks.
Abo im Testmodus (SIN-771): /abo startet Stripe Checkout (Abo-Modus), /abo/bestaetigung zeigt den Stand. Braucht STRIPE_SECRET_KEY (sk_test_…) und STRIPE_PRICE_ID (Test-Preis); Live-Schlüssel lehnt src/lib/abo/abo.ts ab. Ohne beide zeigt /abo „noch nicht eingerichtet“.
Langfuse quality-gate tracing uses JS/TS SDK v5 / platform v4 OTEL ingestion (docs/quality/README.md). Course runs appear as one Langfuse session with one trace per unit (M3 · 02 Spannmittel), containing the generation and every judged question with answer, reason and scores (SIN-299, SIN-383); npm run langfuse:setup creates score configs, the safety-sample annotation queue, prompts and the dashboard (docs/ops/langfuse-dashboard.md).
supabase/migrations/ (additive SQL + RLS). Runbook: docs/ops/SUPABASE.md.scripts/verify-table-grants.sql im SQL-Editor oder per psql; meldet „ok“ oder nennt offene Rechte für anon und authenticated.SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY: API routes persist via Supabase (service role, server-only).COURSE_STORAGE=mock): in-memory mock-store — tests stay green.npm run supabase:verify.NEXT_PUBLIC_SENTRY_DSN, DSN-Host ingest.de.sentry.io) erfasst Fehler der App-Routen und der Pipeline (scripts/content-grow.ts). Ohne Personendaten (src/lib/sentry-privacy.ts); ohne DSN passiert nichts.SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT); ohne Token steht „nicht verfügbar“.docs/decisions/SIN-406-fabrik-durchsatz.md). Deckel und Qualitäts-Schwelle sind unverändert.STRUCTURED_OUTPUTS entfällt; Generierung, Richter, Plan- und Research-Agent nutzen ein Schema, docs/decisions/SIN-1036-structured-json.md). Eine Richter-Antwort, die das Schema verletzt, macht die Einheit „nicht bestanden“ mit Grund. Abgelehnte Batch-Anfragen stehen mit API-Fehlertext im Log und Bericht; 0 Einheiten oder über 50 % gescheiterte Teilaufträge machen content-grow und ap22-ab rot (Exit 1).nichtVersucht mit Grund (Kostendeckel, Zeitlimit, begonnene Module, andere Map). Die Kosten je Einheit rechnen ohne Reparatur, damit der Deckel nicht doppelt zählt (SIN-434, docs/decisions/SIN-434-fabrik-engpass.md).docs/decisions/SIN-431-indkfl-fabrik.md). Jeder Lauf bedient eine Map und wechselt zum nächsten Lauf; Kurs und amtliche Quellen für Industriekaufleute legt die Migration 20261012010000 an.MAP_COURSES, erzeugt werden sie erst mit CONTENT_GROW_SCHWERPUNKTE=1 und nach Metall und Industriekaufleuten. Gemeinsame Module erzeugt nur MAF Metall. Der Trockenlauf (npm run content:grow -- --dry-run) nennt je Map Ziel, vorhanden, geteilt, neu (SIN-479, docs/decisions/SIN-479-schwerpunkte.md).content-publish.yml, nur von Hand, Eingabe Kurs-Stichwort). Standard ist der Trockenlauf; er zeigt die Prüfungen (Quelle und Abrufdatum, Richter bestanden, Verbotsliste), schreibt nichts. Mit dry_run aus setzt er bei null Mängeln courses.status = published, schreibt das Ereignis publish und startet den Live-Check. Lokal: ().https://github.com/siinanXD/Content-Agent-Lernapp
Jeder PR wird automatisch geprüft (build, pr-title, merge-gate). In der Aufbauphase (SIN-855) mergen risk:low und risk:medium ohne Frist nach grünen Checks. Danach gilt: risk:low mergt ohne Frist, risk:medium (Workflow-Rechte, Gate-Änderungen) nach 30 Min Einspruchsfrist (Label einspruch:30m, Stopp mit no-automerge), risk:high wartet auf das Label freigegeben. Nach jedem Merge startet post-merge den Dispatcher sofort. Details: AGENTS.md, Abschnitt „Pull Requests und Merge“. Hat Cursor kein Guthaben, übernimmt Claude: @claude in einem Issue-Kommentar.
docs/content/MAF-CURRICULUM.md — Vorgängerversion v1 (nur Metall); runtime loader uses docs/content/maf-metall.json via AP-14 (PR #24)
testnutzer.ymlebeneurldry_runLIVE_URL=<url> node scripts/autonomy/crawler.mjsDer Crawler (ohne KI) geht alle Routen auf Handy und Desktop ab und prüft Fehler, tote Links, Seitwärts-Scrollen, LCP unter 2,5 s und axe. Die Personas (Azubi Metall, Azubi vor der Zwischenprüfung, Ausbilder) spielen feste Wege; claude-haiku-5-5 bewertet Bilder und Texte nach fester Liste (Deckel 0,50 USD). Funde werden Korrektur-Issues (Label frontend/content, Dubletten-Prüfung, höchstens 5 je Lauf), der Bericht ein Kommentar in SIN-640. Nur lesend, schreibende API-Aufrufe sind abgefangen. Braucht LINEAR_API_KEY, Personas ANTHROPIC_API_KEY. Eingeloggt (SIN-914): Testkonten agent-lernend, agent-ausbilder (Repo-Variablen AGENT_LERNEND_EMAIL, AGENT_AUSBILDER_EMAIL, optional AGENT_ORGANISATION_ID), Sitzung per scripts/agent-testnutzer/sitzung.mjs ohne Passwort, auch im PR-Crawler; Testdaten (agent-test-…) zählen nicht in Kennzahlen. Entscheidung: SIN-640, SIN-914. |
| PR-Crawler (SIN-906) | Workflow pr-crawler.yml (nach der Vercel-Vorschau, auch von Hand mit pr, url) · node scripts/pr-crawler/routen.mjs --dateien <liste> --quelle <checkout> | Leitet aus den geänderten Dateien des PRs die Seiten ab (Seite, Komponente, lib-Datei über Importe; höchstens 6) und prüft nur diese auf der Vorschau wie der Testnutzer (Fehler, tote Links, axe, Knöpfe, erste Frage). Beleg im PR-Kommentar <!-- pr-crawler -->: Bild je Route (Handy, Desktop), ab 2 Seiten ein Video (WebM, höchstens 10 MB, im Artefakt). Hat das verknüpfte Linear-Issue ein Planungsbild (Anhang oder Bild in der Beschreibung, LINEAR_API_KEY), steht es als Soll neben dem Ist-Bild (SIN-1008); ohne Bild nichts. Fund = roter Check, Grund in loop_events (Schritt pr-crawler); ohne Frontend-Änderung „Übersprungen“ mit Grund. Braucht VERCEL_AUTOMATION_BYPASS_SECRET, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY. Entscheidung: SIN-906. |
| KI-Sichtprüfung (Hinweis, SIN-523) | Workflow sichtpruefung.yml · node scripts/autonomy/figma.mjs --bild "02 Lernpfad" | Bei PRs an src/app oder src/components bekommt claude-haiku-5-5 Bilder der betroffenen Seiten (Handy, Desktop) und das Figma-Frame und schreibt einen Kommentar mit höchstens 5 Punkten und den Kosten. Blockiert nichts. Braucht ANTHROPIC_API_KEY, optional FIGMA_ACCESS_TOKEN. |
| Aufräum-Agent | npm run cleanup:scan · Workflow aufraeumen.yml (montags) | Ungenutzte Dateien, tote Pakete, große Dateien, Doppelungen, Doku-Abgleich: ein PR pro Woche. |
| Trend-Radar | Workflow trend-radar.yml (sonntags) · node scripts/autonomy/trend-radar.mjs --week | Claude recherchiert Trends (GitHub, Figma Riffs/Weave, Hugging Face, Changelogs, Skills/MCP), schreibt docs/research/trend-radar-<JJJJ-KW>.md (ein PR pro Woche) und legt pro Lauf genau ein Linear-Issue „Trend-Radar KW “ (Label research, alle Funde als Checkliste) an; bei dry_run entsteht nichts in Linear. Nichts wird automatisch eingebaut. Der Tages-Update nennt den Bericht montags. Entscheidung: SIN-313. |
| README-Erinnerung | npm run readme:check · Schritt im CI-Job pruefen (Teil von build) | Warnung, wenn ein PR Befehle, Seiten, Umgebungsvariablen oder Workflows ändert, ohne README.md anzufassen. |
| Changelog | npm run changelog (braucht volle Git-Historie) | CHANGELOG.md aus den Conventional-Commit-Titeln auf main, ohne KI. Erzeugt, nicht von Hand bearbeiten; der Aufräum-Lauf aktualisiert sie. |
| CodeQL | Workflow codeql.yml (PR, main, montags) · node scripts/autonomy/codeql-gate.mjs <Ordner> | JavaScript/TypeScript. Funde ab security-severity 7.0 machen den Lauf rot (SARIF als Artefakt codeql-sarif). |
| Paket-Updates | .github/dependabot.yml (Dependabot, montags) | npm und Actions: Minor/Patch als Bündel, Major einzeln. Sicherheits-Updates sofort (Repo-Einstellung). Entscheidung: SIN-295. |
| Review-Agent | Workflow review.yml · node scripts/autonomy/review.mjs --pr <Nr> · Secret OPENAI_API_KEY, Repo-Variable REVIEW_DAILY_CAP_USD (Standard 2) | Ein OpenAI-Modell liest jeden PR-Diff gegen den Auftrag und kommentiert mit Schwere. Schwer: Claude repariert im selben PR (zählt als Runde), leicht: nur Hinweis. Behauptet ein Fund rote Tests oder einen kaputten Build, obwohl build grün ist, gilt er als widerlegt; von der Reparatur begründet verworfene Funde („Fund geprüft, keine Änderung nötig“) zählen nicht als Runde und kommen nicht erneut als schwer (SIN-381). Kosten je Review im Kommentar, Tagesdeckel, Label no-review überspringt. Entscheidung: SIN-297. |
| Recht-und-Inhalt-Wächter | src/lib/review/ · läuft in POST /api/courses/{id}/publish | Blockiert die Veröffentlichung (422, reason: content_guard) bei fehlender Quelle oder Abrufdatum, fehlender KI-Kennzeichnung, Personendaten oder IHK-Aufgaben als Vorlage. Die Verbote aus AGENTS.md stehen als Liste in regeln.ts. |
| Fertig-Liste einsortieren (SIN-528) | node scripts/autonomy/einsortieren.mjs --projekt <Linear-Projekt> [--fertig docs/FERTIG.md] [--tabelle out.md] [--apply] · braucht LINEAR_API_KEY | Einmalig: offene Issues nach „v1 Grundgerüst“ oder „v2 Ideen“ (+ Label idee). Ohne --apply nur die Tabelle. Danach startet der Dispatcher dringend → v1 → höchstens eine Idee pro Woche; loop_snapshot bekommt fertig_v1, gesamt_v1, ideen_offen. Entscheidung: SIN-528. |
| Leitstand-Ereignisse | node scripts/autonomy/leitstand.mjs ereignis --schritt <dispatch|worker|pr-gate|planner|digest|status> --status <start|ok|fehler> · braucht SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY (vorhandene Secrets) | Jeder Schritt schreibt Start und Ende in Supabase loop_events (Projekt, Schritt, Issue, PR, Status, Dauer; der Worker zusätzlich Tokens und total_cost_usd), status.yml den Stand in loop_snapshot (Kontingente, Schlange, offene PRs). Lesen nur für Nutzer in leitstand_nutzer (RLS), keine Personendaten. Ohne Secrets nur eine Warnung. Für alle Repos gleich (LEITSTAND_PROJEKT überschreibt den Projektnamen). Entscheidung: SIN-303. |
| Aufgaben für Sinan | node scripts/autonomy/sinan.mjs sync|list|create (braucht LINEAR_API_KEY) | Was nur Sinan tun kann, steht als Linear-Issue mit Label sinan (Link, Minuten, Schritte, Prüfung), nicht im PR-Text. Tages-Update und Status-Seite zeigen sie unter „Braucht dich“; der Loop schließt sie selbst, wo er es lesen kann. Entscheidung: SIN-310. |
| Projekt-Starter (nur Plan) | node scripts/autonomy/starter.mjs plan --name N --idee I [--supabase geteilt|eigen|aus] [--ohne posthog,langfuse] [--json] · starter.mjs sql --schema S | Baut aus Name und Idee den Plan für ein neues Projekt (Schritte je Dienst, benötigte Konto-Tokens nur als Namen, Prüfung je Schritt, Start-Issues). Führt nichts aus; --live ist gesperrt, bis die Konto-Tokens in Infisical liegen und der Loop eine Woche stabil lief. Entscheidung: SIN-202. |
Tagesdeckel judge-backfill (SIN-894) | Workflow run-task.yml, Eingaben task=judge-backfill, optional kurs (z. B. Industriemechaniker) · Repo-Variable TAGESDECKEL_EUR (Standard 40) | Der Deckel zählt Euro des Tages aus judge_runs.cost_eur, nicht Läufe. Summe < Deckel: weiterer Lauf startet; Summe ≥ Deckel: Stopp, Grund als loop_events-Schritt tagesdeckel. Deckel je Lauf (19 €) bleibt. kurs bewertet nur diesen Kurs, leer = alle. Entscheidung: SIN-894. |
| Modellvergleich (SIN-437) | Aufgabe ab-alle-modelle in run-task (Workflow run-task.yml) · lokal npm run ap22:alle:dry (kostenlos: Kandidaten und Kostenschätzung), npm run ap22:alle · braucht ANTHROPIC_API_KEY, OPENAI_API_KEY | Haiku 5.5, Sonnet 5.5, gpt-6-luna, gpt-6.1-sol und chat-latest gegen dieselben 20 LF3-Einheiten (gleicher Prompt, normale Aufrufe, ein Reparatur-Durchgang). Zwei Richter (gpt-5.4-mini und Claude Haiku 5.5): eine Frage besteht nur mit beiden. Bericht docs/quality/runs/ab-alle-modelle-<Datum>.md mit Richter-Schnitten, Abweichungen, Kosten je Einheit (USD, EUR) und je 2 Beispielen im Volltext. Deckel 6 €, Abbruch bei Überschreitung. Wechselt kein Standardmodell. Entscheidung: SIN-437. |
| Prüfhinweise bereinigen (SIN-620) | npm run content:bereinigen [-- --apply] · COURSE_STORAGE=supabase, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY | Entfernt Sätze mit internen Prüfhinweisen (stichprobe, gegenzuprüfen, abgleich mit, Quelle: KMK) aus Einheiten und Fragen und ergänzt bei „spannungsfrei“, „verdrahten“, „anklemmen“ den Elektrofachkraft-Satz. Ohne --apply nur Bericht; Antworttexte meldet er als manuell. Danach npm run quality:judge-backfill. Der Wächter vor publish blockiert neue Treffer. Entscheidung: SIN-620. |
| Inhalte zurücksetzen (SIN-632) | npm run inhalte:zuruecksetzen -- liste --kurs <uuid> --einheit <id> · -- --lauf <run_id> [--apply] · -- --kurs <uuid> --einheit <id> --ab <Version> [--apply] · SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY | Jede Änderung an units/questions steht per Trigger in content_versions (alter und neuer Stand, Grund, Issue, Lauf). Das Skript kehrt Versionen eines Laufs oder einer Einheit um; ohne --apply nur Plan. Ablauf bei falschem Inhalt: docs/ops/NOTFALL.md. |
| Bestehensquote des Richters (SIN-507) | npm run quality:quote [-- --out docs/quality/SIN-507-quote.md] · braucht SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY (nur lesend) | Quote über vorhandene Fragen, je Frage die neueste Bewertung (beide Frage-ID-Schreibweisen vereint), aufgeschlüsselt nach Modul, Fragetyp, prompt_version, judge_model, Datum; Durchfaller nach Kriterium, Top 10 Begründungen. Keine API-Kosten. Bericht: SIN-507. |
| Goldset Industriekaufleute (SIN-447, SIN-449) | Aufgabe goldset-indkfl in run-task · lokal npm run quality:goldset-indkfl · braucht OPENAI_API_KEY, für den Datensatz LANGFUSE_* | 35 von Sinan geprüfte Fragen (docs/quality/indkfl-goldset.json, davon 3 Gegenproben). Legt den Langfuse-Datensatz indkfl-goldset an und lässt den Richter alle Fragen bewerten. Bericht docs/quality/runs/indkfl-goldset-check-<Datum>.md: Urteil wie erwartet, Gegenproben erkannt, abgelehnte gute Fragen. Kosten wenige Cent (09.10.: 0,015 $), darum ohne Tagesdeckel; nach jeder Änderung am Richter neu starten (SIN-456). |
node --import tsx scripts/content-publish.ts --keyword <Stichwort> --dry-rundocs/decisions/SIN-796-veroeffentlichen-workflow.mddocs/decisions/SIN-628-staging.md): Neue Einheiten der Fabrik tragen den Status entwurf, geprueft oder veroeffentlicht. Die App zeigt nur veroeffentlicht; Einheiten ohne Status (Bestand) gelten als veröffentlicht. Freigabe automatisch, wenn der Richter bestanden hat und die Proben grün sind, sonst bleibt die Einheit unsichtbar.docs/FERTIG.md offene Content-Punkte hat; leeres API-Guthaben pausiert sie bis zum manuellen Start und legt ein sinan-Issue an (SIN-797, docs/decisions/SIN-797-fabrik-durchlauf.md). Danach gilt wieder: Montag, Mittwoch und Freitag, höchstens einmal je Tag und unter 40 € je 7 Tage (ein Lauf startet nur, wenn sein Deckel von 20 € noch hineinpasst; SIN-645); MAF-Metall-Module ohne Einheiten (LF3 bis LF6) kommen zuerst (SIN-469, docs/decisions/SIN-469-fabrik-laeufe-pro-woche.md).content_factory_runs (Migrationen 20261007020000, 20261010010000), mit Start, Ende und Ergebnis. Auch ein Abbruch (fehlende Secrets, fehlender Kurs, Absturz) hinterlässt eine Zeile mit stop_reason. Der Planer leitet daraus „läuft wöchentlich“ und „hängt“ (2 Läufe ohne neues Modul) ab (scripts/autonomy/fabrik.mjs).stop_reason „fertig: alle Einheiten erzeugt“ (0 €); der Dispatcher-Anstoß ruht danach. Repariert wird eine Einheit höchstens 2 Runden. Kennzahl content_fabrik_kosten zeigt Kosten heute, 7 Tage und Einheiten je € (docs/decisions/SIN-901-fabrik-endet.md).stop_reason „pausiert: API-Limit am “; content_fabrik_status zeigt „pausiert“ statt „hängt“, der Planer legt kein Issue an, der nächste Lauf startet normal (docs/decisions/SIN-450-fabrik-pausiert-bei-api-limit.md).fabrik-haengt.