Commit Graph
8 Commits
Author SHA1 Message Date
m3ta-chironandClaude Fable 5.1 85fa64cfe6 refactor: switch the whole project to English
talk is going to be published. Code comments, CLI output, error messages,
daemon log lines, MCP descriptions and results, test names and assertions,
README, spec, skill, build scripts and flake are now English. Only the
spoken announcement texts stay bilingual: the wrap result and the test
sentence are German or English depending on the detected language.

Language fallback is now configurable: `lang = "en" | "de"` (env
TALK_LANG, default en) is used when detection cannot tell (numbers, single
words, ties) and for talk's own announcements. langdetect.Detect takes the
fallback as an argument instead of hard-coding German. `talk setup` asks for
it; `talk doctor` and `talk status` show it.

Vocabulary pinned across packages so the process-boundary tests observe
stable strings: chain log lines ("chain: tier X skipped (not available)",
"falling back to the next tier", "spoken via X"), queue lines ("discarded:
quiet hours ... active", "replaced by toast", "max_age"), CLI notes
("speaking directly", "daemon started at", "no daemon", "nothing to cancel"),
status block with aligned English labels, MCP messages, origin guard 403.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 11:15:35 +02:00
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 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 79314dfb0f fix(queue): fertige Ansagen nicht mehr als „abgebrochen" protokollieren
Der Worker rief cancel() auf seinem eigenen Kontext, BEVOR er ctx.Err()
prüfte — damit war jede normal beendete Ansage im Protokoll „abgebrochen",
auch bei Erfolg. Der Abbruch wird jetzt vor dem eigenen cancel() abgelesen.
Regressionstest: fertig heißt „fertig nach", ein echter Cancel heißt
„abgebrochen".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 09:50:08 +02:00
m3ta-chironandClaude Fable 5.1 b86632006e test(queue): TestLawState ohne Zeitbombe — dynamisch inaktives Ruhefenster
Das feste Fenster 18:00-08:00 mit Toast-Modus toastete die Testansage
zwischen 18 und 8 Uhr statt sie zu sprechen; der Test und damit CI waren
14 Stunden am Tag rot. Jetzt beginnt das Fenster zwei Stunden nach jetzt
und dauert eine Stunde; ein Sweep über alle 24 Stunden sichert ab, dass es
zu keiner Tageszeit aktiv ist. Assertions bleiben gleich stark und prüfen
zusätzlich QuietActive/QuietUntil im LawState.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 09:35:45 +02:00
m3ta-chiron 5385a4b87d feat(queue): Rate-Limit und Ruhezeiten als Gesetz im Worker-Loop
Das Anti-Spam-Gesetz wird an den Loop verwired (die Rate-Limiter-Struktur
aus Task 04): Jede gestartete Ansage bucht das Fenster — low über dem
Budget verfällt (beim Eintritt, Admission nennt den Grund für die
202-Antwort), normal parkt bis zum Slot (Slot-Wecker broadcastet die
Cond, geparkte bleiben in der Queue und in /status sichtbar), high darf
mit Burst-Recht verstoßen, now bricht bewusst aus Rate-Limit UND
Ruhezeit aus und bucht trotzdem ein neues Fenster. Ruhezeiten
(quiet.go: lokales Fenster HH:MM-HH:MM über Mitternacht) verwerfen still
oder ersetzen durch Toast — gilt auch für high. Rangordnung: mute (API)
> now > Ruhezeit > Rate-Limit. Enqueue liefert Admission statt nur ID;
LawState für /status. Lock-Ordnung Queue → RateLimiter bleibt gewahrt.
2026-10-18 17:40:41 +02:00
m3ta-chiron e492baddac feat(queue): serialisierte Ansage-Queue mit Prioritäten, now-Sprung, Mute-Store und Rate-Limit-Struktur
- Worker-Loop: eine Ansage gleichzeitig; now > high > normal > low, FIFO je Level
- Cancel bricht die laufende Ansage ab (ctx) und leert die Queue (Zahl als Rückgabe)
- Close verwirft wartende Items, bricht die laufende Ansage ab und wartet auf den Worker
- MuteStore: persistenter Mute-State als Datei mute im Config-Dir (atomar geschrieben)
- RateLimiter: Struktur + Semantik für Task 05, bewusst noch nicht angebunden
  (Task-04-Default unbegrenzt, dokumentierte Seam in rate.go)
2026-10-18 17:10:26 +02:00
m3ta-chiron 24dfe4d46c chore: bootstrap go module, license, readme and package skeleton 2026-10-18 15:14:18 +02:00