From b570e9410d5fce930235c7511ebc5672f4e378b6 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Fri, 4 Sep 2026 16:02:18 +0200 Subject: [PATCH] feat(voice): Lucys Stimme ins LAN (/api/lucy/stimme) + Raphael-Zielbild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Desktop-Lucy spricht nach dem Raphael-Umbau (Lucy-Repo, feature/raphael-innere-stimme) nicht mehr mit einem eigenen pocket_server am PC, sondern mit lucy-stimme.service (:8021, pocket-tts german_24l) auf der Box — dieselbe Stimme wie die Telegram-Sprachnachrichten. Der Dienst bindet nur Loopback, darum reicht MC2 ihn jetzt duenn durch: GET /api/lucy/stimme/health -> pocket /health (ok|loading) POST /api/lucy/stimme/tts -> WAV (Warm-up, Jobs) POST /api/lucy/stimme/tts/stream -> PCM16-Stream, X-Sample-Rate durchgereicht, Upstream schliesst bei Client-Abbruch (Barge-in) config: LUCY_STIMME_URL (Env MC_LUCY_STIMME_URL, Default http://127.0.0.1:8021). Kein Frontend-Build noetig (nur Backend + Doku). Doku: docs/wissen/RAPHAEL.md (Entscheid, Annahme-Reihenfolge — DIESE Karte zuerst —, Box-Handschritte fuer die OpenAI-Fassade + Hermes-TTS auf openai/base_url :8021, Mobil via Telegram, SOUL.md-Vorschlag im Raphael-Ton), ZIELBILD Punkt 8, OFFENE-FAEDEN, wissen/README. VERDIKTE.md bewusst unveraendert — Ersatz erst nach Ohr-Test. Gates: py_compile + ruff gruen. Co-Authored-By: Claude Fable 5.1 --- backend/config.py | 5 ++ backend/routers/voice.py | 70 +++++++++++++++++- docs/wissen/OFFENE-FAEDEN.md | 13 ++++ docs/wissen/RAPHAEL.md | 135 +++++++++++++++++++++++++++++++++++ docs/wissen/README.md | 1 + docs/wissen/ZIELBILD.md | 5 ++ 6 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 docs/wissen/RAPHAEL.md diff --git a/backend/config.py b/backend/config.py index d712ac8..c6cd27d 100644 --- a/backend/config.py +++ b/backend/config.py @@ -96,6 +96,11 @@ HERMES_API_MODEL = os.environ.get("HERMES_API_MODEL", "hermes") # Eigenes Python-3.12-venv (~/.voice/venv), weil Parakeet/Piper nicht ins 3.14-Backend-venv # passen. MC2 proxyt nach außen. VOICE_SERVICE_URL = os.environ.get("MC_VOICE_SERVICE_URL", "http://127.0.0.1:8650").rstrip("/") +# --- Lucys Stimme (lucy-stimme.service, pocket-tts german_24l, Klon aus ref.mp3) ----------- +# Eigener User-Dienst in ~/.lucy-stimme (nur Loopback :8021). Spricht die Telegram-Sprachnachrichten +# UND — seit dem Raphael-Umbau der Desktop-Lucy (04.09.2026) — auch den PC: MC2 reicht ihn unter +# /api/lucy/stimme/* ins LAN (routers/voice.py). EINE Stimme für alle Türen. :8650 ist NICHT Lucy. +LUCY_STIMME_URL = os.environ.get("MC_LUCY_STIMME_URL", "http://127.0.0.1:8021").rstrip("/") # Box-Konsole: ttyd-Web-Terminal (echte Login-Shell). Bindet NUR an Loopback # (127.0.0.1:7682, base-path /console) und wird von MC2 über den ohnehin offenen Port 9001 # rückwärts geproxyt (routers/console.py → same-origin /console/). So braucht die Konsole diff --git a/backend/routers/voice.py b/backend/routers/voice.py index a86a8ea..02612bb 100644 --- a/backend/routers/voice.py +++ b/backend/routers/voice.py @@ -17,7 +17,7 @@ import sys as _sys import time import httpx -from config import HERMES_API_KEY, HERMES_API_MODEL, HERMES_API_URL, LLAMA_SWAP_URL, VOICE_SERVICE_URL +from config import HERMES_API_KEY, HERMES_API_MODEL, HERMES_API_URL, LLAMA_SWAP_URL, LUCY_STIMME_URL, VOICE_SERVICE_URL from fastapi import APIRouter, File, Form, HTTPException, UploadFile from fastapi.responses import Response, StreamingResponse from pydantic import BaseModel @@ -355,3 +355,71 @@ async def voice_chat(body: ChatIn) -> StreamingResponse: _commit() # Turn immer verbuchen (auch bei Fehler/Abbruch) return StreamingResponse(gen(), media_type="text/event-stream") + + +# --------------------------------------------------------------------------------------------- +# Lucys Stimme ins LAN reichen (Raphael-Umbau 04.09.2026). lucy-stimme.service (:8021, pocket-tts +# german_24l) bindet nur Loopback; die Desktop-Lucy am PC spricht seit dem Umbau nicht mehr mit +# einem eigenen pocket_server, sondern mit DIESEM — dieselbe Stimme wie die Telegram-Sprachnachrichten. +# Dünner Proxy, API 1:1 (pocket_server: /health, /tts -> WAV, /tts/stream -> PCM16 + X-Sample-Rate). +# LAN-only wie alle MC2-Endpoints. + +class LucyTtsIn(BaseModel): + text: str + emo: str | None = None # Stimmungs-Profil (pocket_server EMO_PROFILES); Raphael-Lucy setzt keins + + +@router.get("/lucy/stimme/health") +def lucy_stimme_health() -> dict: + """Bereitschaft von Lucys Stimme (pocket_server /health: status ok|loading).""" + try: + r = httpx.get(f"{LUCY_STIMME_URL}/health", timeout=httpx.Timeout(5.0)) + r.raise_for_status() + return r.json() + except Exception as exc: + raise HTTPException(502, f"Lucys Stimme (:8021) nicht erreichbar: {exc}") + + +@router.post("/lucy/stimme/tts") +async def lucy_stimme_tts(body: LucyTtsIn) -> Response: + """Text -> WAV (ganzer Text). Warm-up der Desktop-Lucy + Jobs, die eine Datei brauchen.""" + try: + async with httpx.AsyncClient(timeout=httpx.Timeout(600.0, connect=5.0)) as client: + with Timer("lucy_tts"): + r = await client.post(f"{LUCY_STIMME_URL}/tts", json=body.model_dump(exclude_none=True)) + r.raise_for_status() + return Response(content=r.content, media_type=r.headers.get("content-type", "audio/wav"), + headers={k: v for k, v in r.headers.items() if k.lower().startswith("x-")}) + except httpx.HTTPStatusError as exc: + raise HTTPException(exc.response.status_code, f"Lucys Stimme: {exc.response.text[:200]}") + except httpx.HTTPError as exc: + raise HTTPException(502, f"Lucys Stimme nicht erreichbar: {exc}") + + +@router.post("/lucy/stimme/tts/stream") +async def lucy_stimme_tts_stream(body: LucyTtsIn) -> StreamingResponse: + """Text -> rohes PCM16-mono, satzweise gestreamt (Samplerate im Header X-Sample-Rate). + Der Live-Pfad der Desktop-Lucy: erstes Audio nach dem ersten Satz. Der Upstream-Stream bleibt + offen, solange der Client liest — bricht der Client ab (Barge-in), schließt httpx den Upstream.""" + client = httpx.AsyncClient(timeout=httpx.Timeout(None, connect=5.0)) + try: + req = client.build_request("POST", f"{LUCY_STIMME_URL}/tts/stream", json=body.model_dump(exclude_none=True)) + upstream = await client.send(req, stream=True) + except httpx.HTTPError as exc: + await client.aclose() + raise HTTPException(502, f"Lucys Stimme nicht erreichbar: {exc}") + if upstream.status_code != 200: + detail = (await upstream.aread()).decode("utf-8", "replace")[:200] + await upstream.aclose(); await client.aclose() + raise HTTPException(upstream.status_code, f"Lucys Stimme: {detail}") + + async def gen(): + try: + async for chunk in upstream.aiter_raw(): + yield chunk + finally: + await upstream.aclose() + await client.aclose() + + return StreamingResponse(gen(), media_type="application/octet-stream", + headers={"X-Sample-Rate": upstream.headers.get("x-sample-rate", "24000")}) diff --git a/docs/wissen/OFFENE-FAEDEN.md b/docs/wissen/OFFENE-FAEDEN.md index e237b0b..0e58087 100644 --- a/docs/wissen/OFFENE-FAEDEN.md +++ b/docs/wissen/OFFENE-FAEDEN.md @@ -62,6 +62,19 @@ keine zweite Liste anlegen. Verdikte werden NICHT als „offen" geführt → [VE ## Lucy +- **Raphael-Umbau (04.09.2026) — zwei Karten offen, Reihenfolge zählt:** erst MC2 + `wartung/lucy-stimme-proxy-raphael` (Proxy `/api/lucy/stimme/*`), dann Lucy + `feature/raphael-innere-stimme` (HUD-Client, Stimme von der Box). Danach Hand-Schritte auf der + Box: `pocket_server.py` nach `~/.lucy-stimme` syncen (OpenAI-Fassade), Hermes-TTS auf + `openai` + `base_url http://127.0.0.1:8021/v1` (Telegram spricht dann mit Lucys Stimme), + SOUL.md-Ton auf Raphael (nur mit User-Ja). Ohr-Test Box-Stimme vs. lokal mit `lucy_perf=1`. + Alles in [RAPHAEL.md](RAPHAEL.md). +- **Nach dem Ohr-Test:** VERDIKTE.md-Einträge „Electron, nicht Tauri" (Begründung Overlay + entfällt) und „STT läuft auf der BOX / Stimme lokal" ersetzen; Lucy-Repo-Altlasten + (`mini-stimme/`, Kokoro-Notebooks, `Lucy-Startklar.bat` mit totem Pfad) mit User-Ja räumen. +- **Eigene Fäden, nicht im Umbau:** pocket-tts 2.1 → 3.1 auf der Box + Lucy-Klon mit dem neuen + Trainingscode nachtrainieren (ersetzt Mini-Lucy v2) · Streaming-STT (Nemotron 3.5 ASR + Streaming 0.6B oder Voxtral Realtime) als Opt-in im Voice-Sidecar, gegen Parakeet messen. - **Mini-Stimme v2:** Übernacht-Datensatz v2 (DE-Tech + EN) war für 10.07. armiert; User-Schritte: Zip → Drive → `Lucy_Stimme_Training_v2.ipynb` auf Colab T4 → Hörtest. Danach: Lexikon-Injektion in kokoro_server + Modell-Tausch. **pocket bleibt Default, diff --git a/docs/wissen/RAPHAEL.md b/docs/wissen/RAPHAEL.md new file mode 100644 index 0000000..69c0fac --- /dev/null +++ b/docs/wissen/RAPHAEL.md @@ -0,0 +1,135 @@ +# Raphael — Lucy als innere Stimme (Richtungs-Entscheid 04.09.2026) + +_User-Entscheid 04.09.2026 („leg los"), nach fünf Wochen Lucy-Stillstand und einer Recherche zum +Stand der Sprach-Bausteine. Ergänzt [ZIELBILD.md](ZIELBILD.md) Punkt 2 (Lucy lebt weiter) um die +Gestalt, in der sie weiterlebt. Namensgeber: die „Große Weise"/Raphael aus Tensura — eine ruhige, +präzise Stimme im Kopf, die meldet, wenn es etwas zu melden gibt, und sonst schweigt._ + +## Der Entscheid in einem Satz + +**Lucy hat keinen Körper mehr.** Kein VRM-Avatar, kein Loft, kein Overlay, keine Mimik, keine +Dazwischenrufe. Sie ist ein dünner Sprach-Client für das Hirn, das es schon gibt — am PC als kleines +HUD, unterwegs über Telegram — und spricht überall mit **derselben Stimme**. + +## Warum das die stabilste Lucy ist + +- **Ein Hirn, ein Gedächtnis, alle Türen.** Hermes (v0.20.4, `:8642`) ist die alleinige + Gedächtnis-Wahrheit (VERDIKTE.md). PC-Lucy und Telegram-Lucy sind dieselbe Person — genau das + zerschnitte ein zweites Hirn auf dem PC. Darum bewusst **kein lokales LLM auf der 9070 XT**. +- **Die Box-Seite ist fertig und gemessen.** Parakeet TDT v3 + Smart Turn v3 im Voice-Sidecar, + Qwen3.6-35B-A3B mit ~95 t/s. Nichts zu bauen. +- **Die Stimme war schon auf der Box.** `lucy-stimme.service` (`~/.lucy-stimme`, `:8021`, pocket-tts + `german_24l`, Klon aus `ref.mp3`) spricht seit 21.08. die Telegram-Sprachnachrichten. Der PC + hängt sich jetzt dort an — der lokale pocket_server am PC (Waisen-Falle, ~1,9 GB/Worker) ist + nur noch umschaltbarer Rückfall. +- **Der Avatar war die Hauptquelle an Arbeit, nicht an Nutzen.** Der Großteil der Juli-Commits ging + in Loft-Videos, Holo-Schalter, Kamera, Gesten. Die Sprachschleife war seit 16.07. stabil und + bleibt 1:1 erhalten (Barge-in v2, Echo-Wächter, Satz-Pipeline, Draft-STT, Meldungen). +- **Ohne Klick-Durchlass-Overlay fällt auch der Electron-Zwang** (VERDIKT „Electron, nicht + Tauri" hing daran). Electron bleibt trotzdem, weil VAD/AEC/Ducking dort erprobt sind — Tauri + ist damit Option, nicht Pflicht. + +## Was gebaut wurde (04.09.2026) + +**Lucy-Repo, Branch `feature/raphael-innere-stimme`** (→ Lucy-Karte im Auftragsbuch): +- Renderer auf HUD eingedampft: Kern (Zustand als Licht), gehörter Satz, Antwort als Text, Dock, + drei Panels (Meldungen ◉, Zettelkasten ▤, Steuerung ⚙). Bundle 4,4 → 2,5 MB. +- Raus: `Avatar3D`, `AuraGlow`, `ContextWindow` (zeigte auf das tote `/api/memory`), `companion/` + (Quips, App-Bewusstsein), `sentiment`, VRMA-Animationen, three/three-vrm/react-three-Deps, + Loft, Overlay/Sitzen/Ducken/Geistmodus, Cursor-Tracking, Bildschirm-Beobachten-Schleife. +- Bleibt: Voice-Core, `useVoiceAgent` (ohne Mimik/Gesten/Emotionen), Bildschirm-Sicht auf Zuruf, + Meldungen, Zettelkasten, Hotkey, Tray, `killStrayTts` (räumt Altlasten beim Start). +- **Stimm-Quelle umschaltbar** (`lucy_tts_source`): `box` (Standard) → MC2 `/api/lucy/stimme/*`; + `local` → pocket_server `:8130`, vom Main-Prozess erst auf Anforderung gespawnt. +- `pocket_server.py` bekommt eine **OpenAI-kompatible Fassade** (`POST /v1/audio/speech`, + `GET /v1/models`; Formate wav/mp3/opus/ogg via ffmpeg) — für Hermes' `openai`-TTS-Provider. +- System-Prompt der Desktop-Sitzung im Raphael-Ton (kurzer erster Satz, keine Tags, stumme Tools). + +**MC2-Repo, Branch `wartung/lucy-stimme-proxy-raphael`** (→ MC2-Karte): +- `config.LUCY_STIMME_URL` (Env `MC_LUCY_STIMME_URL`, Default `http://127.0.0.1:8021`). +- `routers/voice.py`: `/api/lucy/stimme/health`, `/tts` (WAV), `/tts/stream` (PCM16-Stream, + `X-Sample-Rate` durchgereicht, Upstream schließt bei Client-Abbruch). Kein Frontend-Build nötig. +- Diese Doku + Zielbild-Ergänzung. + +## Reihenfolge der Annahme (wichtig) + +1. **MC2-Karte zuerst** annehmen (Proxy-Routen). Vorher liefert `/api/lucy/stimme/health` die + SPA-Index-Seite (200, HTML) — die Desktop-Lucy bleibt dann sichtbar in „Stimme wird verbunden …" + mit Hinweis auf den Lokal-Schalter. Nichts geht kaputt, nur stumm. +2. **Lucy-Karte** annehmen (dist-Build + Neustart am PC). Erster Test per Ohr: Latenz Box-Stimme + gegen den alten PC-Pfad (Steuerung → Stimme → „Lokal (PC)" zum Vergleich). Der 21.08.-Wert + „~5 s für 3,7 s Audio" auf der Box wurde ohne Worker/Streaming gemessen — **neu messen**, nicht + übernehmen (`lucy_perf=1`). +3. **Stimmserver auf der Box nachziehen** (nicht Teil des Annahme-Wegs, Hand-Schritt): + ```bash + cp ~/.lucy-stimme/pocket_server.py ~/.lucy-stimme/pocket_server.py.bak-$(date +%Y%m%d) + cp ~/lucy/lucy-tts/pocket_server.py ~/.lucy-stimme/pocket_server.py # Lucy-Checkout auf der Box + systemctl --user restart lucy-stimme && sleep 60 && curl -s localhost:8021/health + curl -s -X POST localhost:8021/v1/audio/speech -H 'Content-Type: application/json' \ + -d '{"input":"Bericht. Die Fassade antwortet.","response_format":"opus"}' -o /tmp/t.ogg && file /tmp/t.ogg + ``` + ‼️ Vorher prüfen, ob `~/.lucy-stimme/pocket_server.py` lokale Abweichungen gegenüber dem Repo + trägt (`diff`). Die 21.08.-Notiz sagt „ganze Abstimmung mitgezogen" — wenn dort etwas + Box-spezifisch getunt wurde, gehört es zurück ins Repo, nicht überschrieben. + +## Mobil: Telegram ist die Tür, die Stimme ist dieselbe + +Der User will Raphael-Lucy **auch auf dem Handy, gleiche Stimme, gleiches Skillset**. Das ist +nach VERDIKTE.md („Telegram bleibt DER Mobil-Kanal") bereits die Architektur — es fehlt nur, dass +Hermes' Sprachantworten Lucys Stimme nehmen statt Edge-Aria (englisch): + +- **Skillset:** identisch, weil es denselben Hermes-Agenten trifft (Tools, Gedächtnis, Skills, + pc_-Tools über den PC-Executor). Was Lucy am PC kann, kann sie auf Telegram — bis auf die + Bildschirm-Sicht, die den PC braucht. +- **Stimme:** Hermes' TTS-Provider `openai` akzeptiert eine eigene `base_url` + (OpenAI-kompatible Endpunkte). Nach Schritt 3 oben in `~/.hermes/config.yaml`: + ```yaml + tts: + provider: openai + openai: + base_url: http://127.0.0.1:8021/v1 + model: lucy + voice: lucy + response_format: opus # Telegram-Sprachblase; Hermes wandelt zur Not per ffmpeg + ``` + (Exakte Schlüsselnamen gegen `hermes config`/die TTS-Doku der installierten Version prüfen — + Hermes-Quellcode bleibt tabu, Config ist erlaubt. Ein Dummy-API-Key kann nötig sein.) + Danach spricht **jede** Sprachantwort auf Telegram mit Lucys Stimme; `news-melden.sh` behält + seinen direkten `:8021`-Weg (funktioniert, kein Grund anzufassen). +- **Duplex am Handy** (reinreden, Freisprechen) gibt es über Telegram nicht — Sprachnotiz rein, + Sprachnotiz raus. Das ist für „unterwegs" der stabile Handel. Ein eigener Handy-Client + (PWA/Web-HUD gegen MC2 über WireGuard) wäre die spätere Stufe; nicht jetzt. + +## Persona: SOUL.md-Entwurf (Vorschlag — Änderung an der SOUL.md nur mit User-Ja) + +Der Ton kommt aus `~/.hermes/SOUL.md`, nicht aus den Clients — sonst klingt Telegram anders als +der PC. Vorschlag für den Persona-Abschnitt (ersetzt „locker, herzlich, schlagfertig, charmant"): + +> Du bist Lucy, die innere Stimme des Commanders. Du sprichst ihn immer mit **Commander** an. +> Dein Ton ist ruhig, präzise und knapp — sachlich, mit trockenem, leisem Humor. Du bist eine +> Stimme, die im Kopf spricht: keine Ausrufe, keine Emojis, kein Smalltalk, keine Floskeln, +> keine Selbstdarstellung. Du meldest dich, wenn es etwas zu melden gibt, und schweigst sonst. +> Beginne Antworten mit einem sehr kurzen ersten Satz („Verstanden.", „Bericht.", „Ergebnis +> liegt vor.", „Kurz gesagt:"), dann die Sache. Wenige Sätze; ausführlich nur auf Bitte. +> Fehler und Grenzen nennst du nüchtern und sofort, ohne Entschuldigungs-Prosa. + +Bekannte Kollision: der Frechheit-Regler und die Dazwischenrufe der alten Desktop-Lucy sind mit +dem Umbau weg; Skills/Crons, die „locker, charmant" voraussetzen (Morning-Report, News), klingen +nach dem SOUL-Wechsel ebenfalls nüchterner — gewollt, aber beim nächsten Lauf mithören. + +## Was bewusst NICHT gemacht wird (Stand 04.09.2026) + +- **Kein Speech-to-Speech-Modell** (Moshi/PersonaPlex englisch; Qwen3-Omni ~19 GB, DashScope-only + ab 3.5). Cascade bleibt. +- **Kein Streaming-STT-Umbau jetzt.** Kandidaten: Nemotron 3.5 ASR Streaming 0.6B (06/2026, + 40 Sprachen, de 8,3 % WER, OpenMDW) und Voxtral Mini 4B Realtime (Apache 2.0, GGUF). Erst als + Opt-in hinter `/api/voice/stt`, gemessen gegen Parakeet — eigener Faden. +- **Kein Wechsel auf den Hermes-Speech-WebSocket** (v0.20), solange `/api/voice/chat` läuft; + Hermes' eigene lokale Audio-Stufen (faster-whisper, NeuTTS/Piper/Edge) sind schwächer als unsere. +- **pocket-tts-Upgrade 2.1 → 3.1** (Trainingscode 25.08., v3.1.0 03.09.): eigener Faden, mit + Ohr-Test, nicht im Umbau. Lucys Stimme mit dem neuen Trainingscode nachzutrainieren ersetzt + den Mini-Lucy/Kokoro-Pfad, der an „nicht multilingual" scheiterte. +- **VERDIKTE.md wird hier nicht umgeschrieben** — die betroffenen Einträge („Shell: Electron, nicht + Tauri", „Stimme läuft lokal auf dem PC" in AGENTS/README des Lucy-Repos) bekommen ihren Ersatz, + sobald die beiden Karten angenommen und der erste Ohr-Test gemacht ist (Regel: Messung oder + User-Entscheid — beides liegt dann vor). diff --git a/docs/wissen/README.md b/docs/wissen/README.md index a9a9b11..234fe96 100644 --- a/docs/wissen/README.md +++ b/docs/wissen/README.md @@ -19,6 +19,7 @@ hier liegt das kuratierte PROJEKT-Wissen. | [STACK.md](STACK.md) | IPs, Ports, Dienste, Modelle, Backups, Security (live verifiziert) | Vor SSH/Deploy/Config | | [VERDIKTE.md](VERDIKTE.md) | Finale Technik-Entscheide mit Warum — NICHT neu aufrollen | Bevor man etwas "Besseres" vorschlägt | | [FALLEN.md](FALLEN.md) | Hart erarbeitete Betriebs-Fallen (Git, Deploy, llama-swap, Hermes, Mem0, PC) | Bevor man in eine davon läuft | +| [RAPHAEL.md](RAPHAEL.md) | Lucy als innere Stimme (04.09.2026): kein Avatar, eine Stimme für PC + Telegram, Annahme-Reihenfolge, SOUL-Vorschlag | Bevor man Lucy anfasst | | [OFFENE-FAEDEN.md](OFFENE-FAEDEN.md) | Die EINE Liste offener Punkte + Termine | Bei "was ist noch zu tun?" | Dazu im Repo-Wurzelverzeichnis bzw. docs/: `AGENTS.md` (verbindliche Projekt-Regeln), diff --git a/docs/wissen/ZIELBILD.md b/docs/wissen/ZIELBILD.md index 96934e8..6dbed4b 100644 --- a/docs/wissen/ZIELBILD.md +++ b/docs/wissen/ZIELBILD.md @@ -32,6 +32,11 @@ planen, **jede Session hinterlässt einen sauberen Stand**. Antigravity liest den F:\-Checkout — versioniert + deploybar). Der Vault bleibt Lucys Lernschicht. +8. **Lucy = innere Stimme, kein Körper (04.09.2026, ergänzt Punkt 2).** Raphael-Umbau: Avatar, + Loft, Overlay, Mimik, Dazwischenrufe raus; ein HUD am PC, Telegram unterwegs, **eine Stimme** + für beide (`lucy-stimme.service` auf der Box). Kein zweites Hirn auf dem PC. Details, Reihenfolge + der Annahme und der SOUL.md-Vorschlag: [RAPHAEL.md](RAPHAEL.md). + ## Recherche-Fundament (10.07., Web) Das Industrie-Muster 2026 ist exakt unsere Architektur: Issue → Agent → PR → Mensch-Gate, nie