Commit Graph
4 Commits
Author SHA1 Message Date
m3ta-chironandClaude Fable 5.1 9e2aa506a6 feat(notify): Desktop-Benachrichtigung für den Ruhezeit-Toast
quiet_mode = "toast" war bisher nur eine Protokollzeile. Jetzt zeigt der
Daemon eine Desktop-Benachrichtigung — über die Bordmittel des Systems,
exec-basiert wie osbuiltin, ohne Bibliothek und ohne CGO: notify-send
(Linux), osascript display notification (macOS), PowerShell Toast
(Windows; Text base64, kein Quoting-Problem). Best effort: fehlt das
Werkzeug, bleibt es bei der Protokollzeile plus Start-Warnung; `talk
doctor` zeigt den Weg unter „Toast". Der Aufruf läuft asynchron, ein
hängender Notification-Daemon blockiert die Queue nie.

Queue: OnToast-Hook, gefeuert bei Toast-Verwurf beim Eintritt und im Worker
(nie unter dem Mutex). Tests: notify-Paket mit fake notify-send im PATH,
Prozessgrenze: Ruhezeit + toast → fake notify-send bekommt Titel und Text,
der Provider keinen Treffer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 10:37:46 +02:00
m3ta-chironandClaude Fable 5.1 df9a9b30cf feat(cli): doctor, test, voices, setup — Einrichtung und Diagnose ohne TUI
Usability-Schicht für Nutzer, die kein Terminal-Handbuch lesen wollen:

- `talk doctor`: eine Zeile je Prüfpunkt mit klarem Satz, was Sache ist und
  was zu tun wäre — Config (kaputt → ✘, die übrigen Prüfungen laufen mit
  Defaults weiter), Daemon (läuft / startet beim nächsten Sprechbefehl /
  fremder Dienst auf dem Port → ✘ mit Port-Hinweis), Audio (oto-Kontext
  wirklich initialisieren; null → ⚠), jede Provider-Stufe (Key-Env-Name,
  Endpunkt, Default-Stimme; OS-Stimme nennt den gefundenen Mechanismus),
  Mute (an → ⚠ mit unmute-Hinweis), Gesetze inkl. gerade aktiver Ruhezeit.
  --probe macht echte Synthese-Proben, --json liefert die Liste. Exit 0
  ohne ✘. Windows-Konsolen bekommen [OK]/[WARN]/[FAIL].
- `talk test`: Testsatz direkt über den Sprech-Kern (nicht über den
  Daemon), nennt die Stufe, die wirklich gesprochen hat, Synthese- und
  Wiedergabezeit; Mute/Ruhezeit gelten bewusst nicht, Mute wird genannt.
- `talk voices`: kuratierter Katalog mit Namen (ElevenLabs-Premade, OpenAI,
  Edge-Neural de/en-Locales) als Tabelle, „*“ = effektive Default-Stimme
  (Config vor Repo-Default), Config-IDs außerhalb des Katalogs als eigene
  Zeile, Filter --provider/--lang, --json.
- `talk setup`: wenige Fragen statt Bubble Tea — Keys nur als Hinweis
  (ohne Key läuft die Edge-Stimme), Stimme je Sprache aus dem Katalog mit
  Probehören, Ruhezeit; schreibt die config.toml zusammengeführt mit
  bestehenden Werten (.bak), spricht einen Testsatz. q bricht ab, bevor
  etwas geschrieben wird; ohne Terminal keine Fragen, nur die wirksame
  Konfiguration.

Unterbau: provider.Catalog/VoiceName/DefaultVoice, Chain.SpeakOutcome
(Stufe, Bytes, Zeiten) durch speak.Engine bis in Daemon-Log und test,
audio.Check (oto-Initialisierung ohne Ton), config.Path,
OSBuiltin.Describe. README: Abschnitt „Erste Schritte“.

Tests: voices-Zeilen (Default-Markierung, Config-Override, Filter, JSON),
doctor (ohne Daemon, Mute/Key via JSON, fremder Dienst → Exit 1, kaputte
Config → ✘ aber vollständiger Bericht), test (fake espeak), setup
(stdin-Antworten → config.toml-Inhalt, Merge mit .bak, Abbruch schreibt
nichts, Pipe stellt keine Fragen), Prozessgrenze: doctor/--probe/voices/
test gegen echten Daemon.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 10:08:44 +02:00
m3ta-chironandClaude Fable 5.1 97650ad0ec feat(queue): Gesetze max_text und max_age — gekürzte Langtexte, verfallene Stau-Ansagen
Zwei Löcher im Anti-Spam-Gesetz, die ein Prompt trivial nutzt: Ein Agent
kann 64 KB Text einreichen (ElevenLabs stellt die Rechnung, die Ansage
dauert Minuten), und geparkte normal-Ansagen spielen unbegrenzt spät —
„Build fertig" zweieinhalb Minuten nach dem Build ist keine Nachricht.

- queue.max_text (Default 400 Zeichen, Env TALK_MAX_TEXT): längere Texte
  werden beim Eintritt an einer Wortgrenze gekürzt und so gesprochen; die
  /speak-Antwort meldet truncated, die CLI warnt auf stderr, das MCP-
  Ergebnis sagt es im Text. Runen, nicht Bytes.
- queue.max_age (Default 2m, Env TALK_MAX_AGE): Items, die beim
  Fälligwerden länger gewartet haben, werden verworfen (discarded
  "expired", Protokoll nennt Wartezeit und Grenze). now:true ist
  ausgenommen. Rang: now > veraltet > Ruhezeit > Rate-Limit.
- /status trägt "limits": {max_text, max_age}; `talk status` zeigt sie.
- Negative Werte sind Config-Fehler; 0 = unbegrenzt.

Tests: truncateText-Tabelle (Wortgrenze, Satzzeichen, Runen), Enqueue-
Kürzung, decide-Vorrang inkl. now-Ausnahme, geparktes Item verfällt;
Config-Schichten; Prozessgrenze: Provider bekommt die gekürzte Fassung,
dritte von drei Stau-Ansagen wird nie gesprochen.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 09:54:40 +02:00
m3ta-chironandClaude Fable 5.1 bc26a73aeb refactor: ein Binary — talk startet den Daemon selbst, talkd und Degraded Mode entfallen
Zwei Binaries erzeugten den halben Komplexitätsballast: Degraded Mode mit
Lock-Datei und Stale-Lock-Race, „läuft talkd?", Autostart-Konfiguration je
OS, doppelte Chain-Konstruktion in cmd/talk und cmd/talkd — und der
Degraded Mode kannte weder Rate-Limit noch Ruhezeiten.

Jetzt:
- internal/speak: der eine Sprech-Kern (Config + Sink + Kette → Ansage);
  Daemon und Direkt-Pfad benutzen ihn, es gibt keine zweite
  Zusammensetzung der Kette mehr.
- internal/daemon: Run (der bisherige talkd-Kern), Spawn (detached per
  Setsid bzw. DETACHED_PROCESS, stdout/stderr → daemon.log), PID-Datei
  daemon.pid, Shutdown-Trigger.
- `talk daemon run|start|stop`: Vordergrund (systemd/launchd), expliziter
  Hintergrund-Start, Stop per POST /shutdown — stop wartet, bis HTTP weg
  und die PID-Datei gelöscht ist (Prozess wirklich beendet).
- Autostart: say/wrap starten den Daemon beim ersten Sprechbefehl selbst
  (daemon.autostart, Default an; Env TALK_AUTOSTART). Verliert ein
  paralleler Aufruf das Port-Rennen, erkennt er das am Kind-Ende und nutzt
  den Daemon des anderen — kein Timeout-Warten.
- Direkt-Pfad statt Degraded Mode: ohne erreichbaren oder startbaren
  Daemon spricht talk direkt (Mute respektiert, kein Lock, Hinweis +
  Grund + Log-Pfad auf stderr). Steuerbefehle starten den Daemon bewusst
  nicht; `talk status` zeigt „läuft nicht" (Exit 1).
- cmd/talkd und cmd/talk/degraded.go entfernt; README-Daemon-Abschnitt
  neu.

Tests: Harness baut nur noch talk und startet `talk daemon run`;
TALK_AUTOSTART=false als Grundausstattung aller Tests (kein Test
hinterlässt Daemons), autostart_test.go schaltet es bewusst an: Autostart
beim ersten say, Idempotenz, paralleler Start in EINE Queue, expliziter
start/stop, Port fremd belegt → Direkt-Pfad ohne Timeout. Cross-Compile
aller sechs Targets mit CGO_ENABLED=0 verifiziert.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 09:49:16 +02:00